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

我知道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); } 

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

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

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

我写了一篇关于这个主题的文章 ,你可能觉得它很有用,包括这段代码:

 using System; using System.Threading; public static class RandomProvider { private static int seed = Environment.TickCount; private static ThreadLocal randomWrapper = new ThreadLocal (() => new Random(Interlocked.Increment(ref seed))); public static Random GetThreadRandom() { return randomWrapper.Value; } } 

如果您将new Random()调用更改为RandomProvider.GetThreadRandom() ,它可能会执行您需要的所有操作(同样,假设为.NET 4)。 这并不能解决可测性问题,而是一步一步……

您没有向我们展示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的输出仍然不是“真实的”随机。 它只是伪随机的。

什么是randomNumber

通常,伪随机数生成器被播种(具有与时间相关的事物,或者诸如两个按键或网络分组之间的时间的随机事物)。

您没有指出您正在使用的生成器,也没有指示它是如何播种的。

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

 Thread.Sleep(10); 

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

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

同样重要的是要了解随机不是随机的 。