为啥我的随机数生成器在 C# 中看起来不是随机的?

Posted

技术标签:

【中文标题】为啥我的随机数生成器在 C# 中看起来不是随机的?【英文标题】:Why does it appear that my random number generator isn't random in C#?为什么我的随机数生成器在 C# 中看起来不是随机的? 【发布时间】:2010-10-30 06:31:00 【问题描述】:

我正在使用 Microsoft Visual C# 2008 Express。

我找到了这个sn-p的代码:

    public static int RandomNumber(int min, int max)
    
        Random random = new Random();

        return random.Next(min, max);
    

问题是我已经运行了超过 100 次,当我的 min = 0 和 max = 1 时,它总是给我相同的答案。我每次都得到 0。 (我创建了一个测试函数来运行它——真的——我每次都得到 0)。我很难相信这是一个巧合......我还能做些什么来检查或测试这个吗? (我确实用 min = 0 和 max = 10 重新运行了测试,前 50 次,结果总是“5”,第二次 50 次,结果总是“9”。

??我需要一些更一致的随机...

-阿迪娜

【问题讨论】:

出于某种原因,我认为“始终如一地随机”真的很有趣。 我想补充一点,根据您的应用程序的目的,使用伪随机将始终(至少在理论上)使您的应用程序可破解。 【参考方案1】:

您的范围不正确。 minValue 包含在范围内,而 maxValue 在范围内不包含(意味着它不会包含在范围内)。所以这就是它只返回 0 的原因。

另一个有用的注意事项:在方法中创建一个 Random 实例并不理想,因为它可能会在调用时获得相同的种子。所以我会说使用:

static Random gen = new Random();

public static int RandomNumber(int minValue, int maxValue)
    return gen.Next(minValue, maxValue);

【讨论】:

【参考方案2】:

您误解了“random.Next(min, max)”这一行。 “min”代替允许随机生成的最小数字。虽然“max”是在不允许生成的最小数字的位置,但它不是在允许绘制的最大数字的位置。所以当这条线是 random.Next(0, 1) 你基本上只允许绘制 0。

【讨论】:

【参考方案3】:

我找到了一种非常简单但有效的方法来生成随机数,只需获取当前日期时间毫秒的最后两位数:

   int seed = Convert.ToInt32(DateTime.Now.Millisecond.ToString().Substring(1, 2));
   int cnr = new Random(seed).Next(100);

它很粗糙,但它有效! :-)

当然,它会在统计上每一百次生成相同的数字。或者,您可以采用所有三位数字或与其他日期时间值(例如秒左右)连接。

【讨论】:

【参考方案4】:

你总是得到 0,因为 Random.Next 返回整数。您需要调用Random.NextDouble,它将返回一个介于 0 和 1 之间的数字。此外,您应该重用 Random 实例,如下所示:

[ThreadStatic]
static Random random;
public static Random Random  
    get 
        if (random == null) random = new Random();
        return random;
    

public static int RandomInteger(int min, int max)

    return Random.Next(min, max);

public static double RandomDouble() //Between 0 and 1
 
    return Random.NextDouble();
 

如果您想要加密安全的随机数,请使用 RNGCryptoServiceProvider 类;见this article

编辑:线程安全

【讨论】:

-1 因为这段代码是危险的错误。 Random 不是线程安全的,您正在访问共享实例而不提供任何锁定或每个线程实例。【参考方案5】:

几位张贴者声明 Random() 使用基于系统时钟上当前秒的种子,并且在同一秒内创建的任何其他 Random 实例将具有相同的种子。这是不正确的。 Random 的无参数构造函数的种子基于滴答计数或自启动时间以来的毫秒数。此值在大多数系统上大约每 15 毫秒更新一次,但可能会因硬件和系统设置而异。

【讨论】:

【参考方案6】:

这是任何答案的附录,因为这个特定问题的答案是边界应该是 (0, 2) 而不是 (0, 1)。

但是,如果您想使用静态包装方法,那么您必须记住Random 不是线程安全的,因此您需要提供自己的同步机制或提供每个线程的实例。这是一个很大程度上非阻塞的实现,它使用一个生成器来为每个线程生成器播种:

public static class ThreadSafeRandom

    private static readonly Random seed = new Random();

    [ThreadStatic]
    private static Random random;

    public static int Next(int min, int max)
    
        if (random == null)
        
            lock (seed)
            
                random = new Random(seed.Next());
            
        

        return random.Next(min, max);
    

    // etc. for other members

【讨论】:

Environment.TickCount (默认种子)对于种子来说已经足够了;制作单独的种子实例没有意义。 @SLaks - 如果你依赖 Environment.TickCount 那么你很可能会为每个线程实例获得相同的种子,这将导致它们产生相同的伪随机值序列。我非常怀疑这是你想要的。通过给他们一个明确不同的种子,你可以防止这种行为模式的发生。【参考方案7】:
random = new Random();

这会以当前时间(以秒为单位)启动随机数生成器。当您在系统时钟更改之前多次调用您的函数时,随机数生成器会使用相同的值启动,因此它会返回相同的值序列。

【讨论】:

Random() 构造函数的文档几乎一字不差地描述了这个问题:msdn.microsoft.com/en-us/library/h343ddh9.aspx【参考方案8】:

正如其他人所提到的,每秒构建多次的 Random 使用与种子相同的秒数,所以我将 Random 构造函数放在循环之外,并将其作为参数传递,如下所示:

public static int RandomNumber(Random random, int min, int max)

    return random.Next(min, max);

正如其他人所提到的,最大值是独占的,所以如果你想要一个 0 或 1,你应该使用 [0,2] 作为你的 [min,max],或者更大的最大值,然后使用二进制与1.

public static int RandomOneOrZero(Random random)

    return random.Next(0, int.MaxValue) & 1;

【讨论】:

Random对象也可以声明为静态变量,不需要每次都传递给方法。 我喜欢 RandomOneOrZero 方法。您使用它是因为您知道它比使用 [0,2] 更随机还是出于其他原因? @Matt:无论何时,随机性都是一样的。我只是把它从我的头顶上掀了起来。 @Whatsit:好点。我不确定她的用例是什么,所以我只是猜测了一下。静态或实例变量可能更合适。【参考方案9】:

除了其他答案中已经提到的 0-1 问题之外,当您正在寻找 0-10 范围并连续 50 次获得相同结果时,您的问题是真正的问题。

new Random() 应该返回一个随机数,种子从计时器初始化(当前秒),但显然你每秒调用此代码 50 次。 MSDN 建议:“为了提高性能,创建一个 Random 以随着时间的推移生成许多随机数,而不是重复创建一个新的 Random 以生成一个随机数。”。如果您在方法之外创建了随机生成器,那应该可以解决您的“非随机性”问题并提高性能。

如果您需要“更高质量”的伪随机数,还可以考虑使用this post 以获得比系统提供的更好的伪随机数生成器。

【讨论】:

【参考方案10】:

不要为 Next 创建包装方法。它浪费了创建 Random 类的新实例的周期。就用同一个吧!

Random myRand = new Random();

for(int i = 0; i < 10; i++)

    Console.WriteLine(myRand.Next(0, 10).ToString());

这应该给你十个随机值。

如前所述——Random 是伪随机的(就像所有实现一样),如果您使用相同的种子创建 100 个实例,您将获得 100 个具有相同结果的实例。确保您正在重用该类。

另外,正如人们所说,要注意 MinValue 是包容性的,而 MaxValue 是排斥性的。对于你想要的,执行 myRand.Next(0, 2)。

【讨论】:

做 10 次或更多次只是测试......我没有意识到“max”是排他性的。 有时你会想要一个 Random 的包装器方法(或者更可能是一个包装器类),以便你可以模拟它。 @adeena, BlackWasp:如果您出于任何原因需要重载/包装 Next,那就去做吧。但是,如果您只想获得另一个随机值,请使用 Next 并使用相同的 Random 类。这样一来,您就不会冒太多风险获得一堆具有相同种子的 Random。【参考方案11】:

Next() 的重载返回:

大于等于 minValue 且小于 maxValue 的 32 位有符号整数;即返回值的范围包括minValue,但不包括MaxValue。如果 minValue 等于 maxValue,则返回 minValue。

0 是它返回的唯一可能值。也许你想要 random.NextDouble(),它会返回一个介于 0 和 1 之间的双精度数。

【讨论】:

【参考方案12】:

min = 0 和 max = 1 的问题是 min 是包含的,而 max 是排除的。因此,该组合的唯一可能值是 0。

【讨论】:

为了让事情更加混乱,文档指出 maxValue 必须大于或等于 minValue。因此,您可以传入 random.Next(0,0),它的功能与 random.Next(0,1) 完全相同,即使没有与指定输入参数匹配的数字。【参考方案13】:

最小值包含在内,但最大值不包含在内。 Check out the API

【讨论】:

【参考方案14】:

在 VB 中,我总是从 Randomize() 函数开始。只需调用 Randomize() 然后运行您的随机函数。我还执行以下操作:

Function RandomInt(ByVal lower As Integer, ByVal upper As Integer) As Integer
    Return CInt(Int((upper - lower + 1) * Rnd() + lower))
End Function

希望这会有所帮助! :)

【讨论】:

你是真的吗?你知道我有多少次得到 VB 问题的 C# 答案并且能够弄清楚我做错了什么吗?这是概念,伙计,不是语法...天哪... 这些函数中的每一个仅在 VB 中可用(除非您引用 Microsoft.VisualBasic.dll) System.Random 类完全不同。

以上是关于为啥我的随机数生成器在 C# 中看起来不是随机的?的主要内容,如果未能解决你的问题,请参考以下文章

C#中如何产生随机字符串

sqlserver 中rand()是产生随机数,为啥还要设置种子?

为啥我的 SVG 图像在设备 API 16 和 API 27 中看起来很模糊

为啥我的设计元素在所有浏览器中看起来都不同?更新[关闭]

为啥我的随机数生成器不产生输出? [关闭]

c#中0-1000之间的随机数生成器[关闭]