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

本文关键字:随机的 随机 随机数 | 更新日期: 2023-09-27 18:07:00

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

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

我猜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<Random> randomWrapper = new ThreadLocal<Random>
        (() => 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 ?

通常伪随机数生成器是种子的(与时间相关的东西,或随机的东西,如两次按键之间的时间或网络数据包或其他东西)。

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

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

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

Thread.Sleep(10);

所以系统会休眠10 m秒,你会得到一个新的随机数。这是一个保证的解决方案。但是这也会影响系统的性能。