2011-08-31 102 views
5

我知道C#Random类不会使“真正的随机”的数字,但我想出一个问题与此代码:C#随机数是不是“随机”

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); 
    } 
+4

你的'randomNumber'函数是什么样的? – Nija

+0

你可能想看看[这篇文章](http://www.codeducky.org/random-numbers-c-net-primer/),它讨论了这个问题以及.NET Random类的其他陷阱 – ChaseMedallion

回答

26

我的猜测是,randomNumber创建的每次Random一个新的实例......这反过来又创造基于新的伪随机数发生器目前的时间...不会像你想象的那么频繁地改变。

不要这样做。重复使用Random的同一个实例...但是不需要通过创建静态Random变量来“修复”它。这不会在长期内也无论是工作,因为Random是不是线程安全的。它会在测试中看起来很好,然后你会神奇地得到所有的零后,你碰巧得到不幸的并发:(

幸运的是,使用线程本地机制来工作并不难,特别是如果你在.NET 4你最终的Random每个线程的新实例

我写了一个article on this very topic这可能对你有用,包括下面的代码:如果你改变new Random()通话

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; 
    } 
} 

RandomProvider.GetThreadRandom()这可能会做一切你甲肾上腺素编辑(再次,假设.NET 4)。这并没有解决可测试性,但一步一个脚印的时间...

+0

这怎么可以修改为使用问题中的最小值和最大值? – Julien

0

什么是randomNumber

典型地,伪随机数发生器被接种(与时间有关的事,或者一些随机像两个按键或网络分组或东西之间的时间)。

您不会指出您使用的是什么发生器,也不会说明它是如何接种的。

+0

我在示例中添加了我用于原始文章 – Mento

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输出仍然没有“真”随机的。这只是假冒。

+1

+1的随机数类。但是,如果在非常短的时间内调用,它几乎总是会产生相同的值,这更有可能...... ;-)“默认的种子值来源于系统时钟并具有有限的分辨率。” – 2011-08-31 01:42:58

+0

@pst,不,它不会产生同样的价值。 'seed'(初始值)取决于时钟,但是它的内部状态在使用时会改变。 –

+0

@ J-16 SDiZ:我认为pst的含义是,如果你有一个实例化Random类的新实例并返回一个随机值并在循环中连续调用该方法的方法,你将看到相同的值重复了几次,因为种子没有变化,然后随着种子从时钟变化变为另一种值,重复了几次,等等。 – jason

0

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

Thread.Sleep(10); 

那么系统将进入睡眠状态,10分钟秒。你会得到新的随机数。 它是一个有保证的解决方案。但这也会影响系统的性能。

+0

在我困惑的时候,使用睡眠是我粗暴的解决方法,当我不断得到2-3个重复的值并且感到困惑时。顺便说一句,我仍然在(100)重复。在(500)处,它看起来是随机的。但是,表现很糟糕。 @贾森上面的答案是一个很好的解决办法。 – nanonerd

0

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

您也了解随机不是really random也是至关重要的。