C#随机数不是“随机的”

时间:2011-08-31 01:36:45

标签: c# random numbers

我知道C#Random类不会产生“真正的随机”数字,但我想出了这段代码的问题:

    public void autoAttack(enemy theEnemy)
    {
        //Gets the random number
        float damage = randomNumber((int)(strength * 1.5), (int)(strength * 2.5));

        //Reduces the damage by the enemy's armor
        damage *= (100 / (100 + theEnemy.armor));

        //Tells the user how much damage they did
        Console.WriteLine("You attack the enemy for {0} damage", (int)damage);

        //Deals the actual damage
        theEnemy.health -= (int)damage;

        //Tells the user how much health the enemy has left
        Console.WriteLine("The enemy has {0} health left", theEnemy.health);
    }

然后我在这里调用函数(为了检查数字是否是随机的,我调用了5次):

        if (thePlayer.input == "fight")
        {
            Console.WriteLine("you want to fight");
            thePlayer.autoAttack(enemy1);
            thePlayer.autoAttack(enemy1);
            thePlayer.autoAttack(enemy1);
        }

然而,当我检查输出时,我得到每个3个函数调用的完全相同的数字。但是,每次运行程序时,我都会得到一个不同的数字(重复3次):

 You attack the enemy for 30 damage.
 The enemy has 70 health left.

 You attack the enemy for 30 damage.
 The enemy has 40 health left.

 You attack the enemy for 30 damage.
 The enemy has 10 health left.

然后我将重新编译/调试/再次运行程序,并获得一个不同的数字而不是30,但它将重复所有3次。

我的问题是:每次调用此函数时,如何确保获得不同的随机数?我一遍又一遍地得到相同的“随机”数字。

这是我使用的随机类调用:

    private int randomNumber(int min, int max)
    {
        Random random = new Random();
        return random.Next(min, max);
    }

5 个答案:

答案 0 :(得分:29)

我的猜测是randomNumber每次都会创建一个Random的新实例......这反过来会根据当前时间创建一个新的伪随机数生成器...像你想象的那样经常改变。

不要那样做。重复使用相同的Random实例...但不要通过创建静态Random变量来“修复”它。从长远来看,这不会很好,因为Random不是线程安全的。它在测试中看起来都很好,然后在你碰巧遇到并发后,你会神秘地得到所有的零:(

幸运的是,使用线程本地工作并不是很难,特别是如果你使用的是.NET 4。你最终会得到一个每个线程Random的新实例。

我写了一篇你可能觉得有用的article on this very topic,包括这段代码:

using System;
using System.Threading;

public static class RandomProvider
{    
    private static int seed = Environment.TickCount;

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    public static Random GetThreadRandom()
    {
        return randomWrapper.Value;
    }
}

如果您将new Random()调用更改为RandomProvider.GetThreadRandom(),可能会执行您需要的所有操作(再次假设为.NET 4)。这不涉及可测试性,而是一次一步......

答案 1 :(得分:7)

您没有向我们展示randomNumber的代码。如果它看起来像

private int randomNumber(int m, int n) {
    Random rg = new Random();
    int y = rg.Next();
    int z = // some calculations using m and n
    return z;
}

那么,那就是你的问题。如果你不断创建Random的新实例,它们有时可能会有相同的种子(默认种子是系统时钟,它具有有限的精度;创建它们足够快并且它们获得相同的种子)然后此生成器生成的序列将始终相同。

要解决此问题,您必须实例化一次Random的实例:

private readonly Random rg = new Random();
private int randomNumber(int m, int n) {
    int y = this.rg.Next();
    int z = // some calculations using m and n
    return z;
}

为了澄清另一点,即使你这样做,Random的输出仍然不是“真实的”随机。它只是伪随机。

答案 2 :(得分:0)

什么是randomNumber

通常,伪随机数生成器是种子(带有时间相关的东西,或者是随机的,比如两个按键或网络数据包之间的时间)。

您没有说明您使用的是哪种发电机,也没有说明它是如何播种的。

答案 3 :(得分:0)

如果你在循环中生成随机数,它可能不会是随机的。因为随机数基本上是在当前系统时间内部创建的。 所以将这段代码放在循环中:

Thread.Sleep(10);

因此系统将进入睡眠状态10毫秒。你会得到一个新的随机数。 它是一个有保障的解但这也会影响系统的性能。

答案 4 :(得分:0)

在方法外部随机实例化对象。 (Random random = new Random();应该在方法之前编写)

您必须了解随机不是really random