【问题标题】:C# Random Numbers aren't being "random"C# 随机数不是“随机的”
【发布时间】:2011-11-07 06:42:29
【问题描述】:

我知道 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函数是什么样的?

标签: c# random numbers


【解决方案1】:

我的猜测是 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)。这并没有解决可测试性,而是一步一步......

【讨论】:

  • 如何修改它以使用问题中的最小值和最大值?
【解决方案2】:

您没有向我们展示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 为例。但是,如果在很短的时间内连续调用它,它更有可能几乎总是产生相同的值...... ;-) “默认种子值来自系统时钟并且具有有限的分辨率。 "
  • @pst,不,它不会产生相同的值。 seed(初始值)取决于时钟,但是当你使用它时,它的内部状态会发生变化。
  • @J-16 SDiZ:我认为 pst 的意思是,如果您有一个方法正在实例化 Random 的新实例并返回单个随机值,并在循环,你会看到相同的值重复了几次,因为种子没有改变,然后另一个值重复了几次,因为时钟改变了种子,等等。
  • @jason,这正是发生在我身上的事情。我很困惑,但cmets完美地解释了它。我得到了 2-3 次重复,而且看起来并不随机。
【解决方案3】:

randomNumber 是什么?

通常会播种一个伪随机数生成器(与时间相关的事物,或随机的事物,例如两次按键或网络数据包之间的时间或其他事物)。

您没有说明您使用的是什么生成器,也没有说明它是如何播种的。

【讨论】:

  • 我在原帖中添加了我使用的随机数类
【解决方案4】:

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

了解随机不是really random 也很重要。

【讨论】:

    【解决方案5】:

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

    Thread.Sleep(10);
    

    因此系统将休眠 10 m 秒。你会得到新的新鲜随机数。 它是一个有保证的解决方案。但这也会影响系统的性能。

    【讨论】:

    • 在我的困惑中,当我不断得到 2-3 个重复值并且感到困惑时,使用睡眠是我粗略的解决方法。顺便说一句,我仍然在(100)处重复。在(500)处,它看起来是随机的。但是,是的,性能明显很糟糕。 @Jason 上面的回答是一个很好的解决方法。
    猜你喜欢
    • 1970-01-01
    • 2014-04-27
    • 2015-09-10
    • 1970-01-01
    • 2012-10-04
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 2016-11-07
    相关资源
    最近更新 更多