关于c#:多个随机数是相同的

Multiple random numbers are the same

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Random number generator only generating one random number

初学者的问题。我有一个非常简单的程序,它画了一条线,我想随机化位置,但是每次我创建一个新的随机实例,它都会返回相同的值。问题出在哪里?谢谢您。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void Draw()
{
    Random random1 = new Random();
    int randomNumber1 = random1.Next(0, 300);
    Random random2 = new Random();
    int randomNumber2 = random2.Next(0, 300);
    Random random3 = new Random();
    int randomNumber3 = random3.Next(0, 300);
    Random random4 = new Random();
    int randomNumber4 = random4.Next(0, 300);
    System.Drawing.Graphics g = this.CreateGraphics();
    Pen green = new Pen(Color.Green, 5);
    g.DrawLine(green, new Point(randomNumber1, randomNumber2),
                      new Point(randomNumber3, randomNumber4));
}

private void btndraw1_Click(object sender, EventArgs e)
{
    Draw();
}

发生这种情况的原因是,每次执行新的Random时,都会使用时钟对其进行初始化。所以在一个紧密的循环中(或者一个接一个的多次调用),由于所有这些随机变量都是用相同的种子初始化的,所以您会多次得到相同的值。

要解决这个问题:只创建一个随机变量,最好是在函数外部,并且只使用那个实例。

1
2
3
4
5
6
7
8
9
10
11
Random random1 = new Random();
private void Draw()
{
    int randomNumber1 = random1.Next(0, 300);
    int randomNumber2 = random1.Next(0, 300);
    int randomNumber3 = random1.Next(0, 300);
    int randomNumber4 = random1.Next(0, 300);
    System.Drawing.Graphics g = this.CreateGraphics();
    Pen green = new Pen(Color.Green, 5);
    g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4));
}


只需使用相同的实例:

1
2
3
4
Random random = new Random();
int randomNumber1 = random.Next(0, 300);
int randomNumber2 = random.Next(0, 300);
//...

编程中的随机数并不是真正的随机数;它们是基于一些独特的种子,这些种子被获取和操作以生成看起来是一组随机数的东西。使用相同的种子将产生相同的数字集。

Random类的默认构造函数使用自系统作为种子启动以来经过的毫秒数,因此实际发生的情况是使用了相同的种子。

实际上没有理由创建不止一次的Random实例;单个实例将在每次执行代码时生成随机数集。

为了证明我的上述默认种子声明,我使用了反射:

1
2
3
4
5
// System.Random
/// <summary>Initializes a new instance of the <see cref="T:System.Random" /> class, using a time-dependent default seed value.</summary>
public Random() : this(Environment.TickCount)
{
}

以及Environment.TickCount

1
2
3
4
5
6
7
8
9
10
// System.Environment
/// <summary>Gets the number of milliseconds elapsed since the system started.</summary>
/// <returns>A 32-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.</returns>
/// <filterpriority>1</filterpriority>
public static extern int TickCount
{
    [SecuritySafeCritical]
    [MethodImpl(MethodImplOptions.InternalCall)]
    get;
}


随机数生成器(RNG)实际上并不生成随机数。相反,它使用一种算法来定义一个随机的数字序列。此序列取决于创建RNG时通过所述算法运行的seed

默认情况下,RNG是使用系统的时钟作为种子创建的,因为时钟通常在程序每次运行时都会变化,这使得预测"随机"序列非常困难。

在您的例子中,很可能时钟在创建一个随机对象和另一个随机对象之间没有变化;可能是由于CPU内部对指令的重新排序。

正如blachshma所说,最好只创建一个随机对象并只使用它。

1
2
3
4
5
6
7
public static Random MyRNG = new Random(); // create a single static random object, that you can use across all classes
private void Draw()
{
    randomNumber1 = MyRNG.Next(0, 300);
    randomNumber2 = MyRNG.Next(0, 300);
    // and so forth
}

请记住,System.Random的任何实例都不能保证是线程安全的,这意味着如果计划让多个线程共享同一个随机对象,则必须将其锁定。

1
2
3
4
lock (MyRNG)
{
    randomNumber = MyRNG.Next(0, 300);
}

如果不这样做,可能会破坏随机对象,从而导致后续调用仅返回0。


您不应该为每个数字创建一个新的Random对象。相反,使用相同的对象:

1
2
3
4
5
6
7
8
9
10
11
12
Random r = new Random();

private void Draw()
{
    // Create 4 random numbers
    int[] numbers = Enumerable.Range(0, 4).Select(x => r.Next(0, 300)).ToArray();

    System.Drawing.Graphics g = this.CreateGraphics();
    Pen green = new Pen(Color.Green, 5);
    g.DrawLine(green, new Point(numbers[0], numbers[1]),
                      new Point(numbers[2], numbers[3]));
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    private static readonly Random Random1 = new Random();

    private void Draw()
    {

        int randomNumber1 = Random1.Next(0, 300);
        int randomNumber2 = Random1.Next(0, 300);
        int randomNumber3 = Random1.Next(0, 300);
        int randomNumber4 = Random1.Next(0, 300);
        System.Drawing.Graphics g = this.CreateGraphics();
        Pen green = new Pen(Color.Green, 5);
        g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4));
    }


    private void btndraw1_Click(object sender, EventArgs e)
    {
        Draw();
    }

您只需要随机类的一个实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void Draw()
    {
        Random random1 = new Random();
        int randomNumber1 = random1.Next(0, 300);

        int randomNumber2 = random1.Next(0, 300);

        int randomNumber3 = random1.Next(0, 300);

        int randomNumber4 = random1.Next(0, 300);

        System.Drawing.Graphics g = this.CreateGraphics();
        Pen green = new Pen(Color.Green, 5);
        g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4));
    }


    private void btndraw1_Click(object sender, EventArgs e)
    {
        Draw();
    }


.NET的随机类需要的是一个种子值,您可以使用一个日期值作为种子,它将起作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void Draw()
    {
        Random random1 = new Random(unchecked((int)DateTime.Now.Ticks << (int)100));
        int randomNumber1 = random1.Next(0, 300);
        Random random2 = new Random(unchecked((int)DateTime.Now.Ticks << (int)200));
        int randomNumber2 = random2.Next(0, 300);
        Random random3 = new Random(unchecked((int)DateTime.Now.Ticks << (int)300));
        int randomNumber3 = random3.Next(0, 300);
        Random random4 = new Random(unchecked((int)DateTime.Now.Ticks << (int)400));
        int randomNumber4 = random4.Next(0, 300);
        System.Drawing.Graphics g = this.CreateGraphics();
        Pen green = new Pen(Color.Green, 5);
        g.DrawLine(green, new Point(randomNumber1, randomNumber2), new Point(randomNumber3, randomNumber4));
    }


private void btndraw1_Click(object sender, EventArgs e)
{
    Draw();
}