【问题标题】:Creating a true random [duplicate]创建一个真正的随机[重复]
【发布时间】:2012-01-08 16:17:41
【问题描述】:

可能重复:
Why does it appear that my random number generator isn't random in C#?
How can I generate truly (not pseudo) random numbers with C#?

我创建了一个骰子游戏,骰子基于百分位数 1-100。

public static void Roll()
{
    Random rand = new Random((int)DateTime.Now.Ticks);
    return rand.Next(1, 100);
}

但我不觉得这是基于当前时间的真正随机。

如果我这样做了

for (int i = 0; i < 5; i++)
{
   Console.WriteLine("#" + i + " " + Roll());
}

它们都是相同的值,因为 DateTime.Now.Ticks 没有改变,它播种了相同的数字。

我想如果种子由于当前时间相同,我可以生成一个新的随机种子,但感觉不像是诚实的“重新滚动”

我应该怎么做才能尝试复制接近真实/诚实的掷骰子?我应该改用RNGCryptoServiceProvider 类来生成卷吗?

【问题讨论】:

  • 关于这个问题有很多变体。这是其中之一……Why does it appear that my random number generator isn't random in C#?……标记为骗子。 (您的 Random 应该只有一个静态实例,而不是每个 Roll 都有一个新的 Random)
  • 在引用代码时,通常最好复制粘贴。我假设您必须重新输入,因为您无法从 void 函数中返回值,但 Roll 被声明为 public static void Roll()
  • @rapsalands:真正的随机将是一个真正的随机序列。 Random 不会为 OP 这样做,我已经在我的回答中指出了这一点。 :-)
  • @spender:除了 OP 说他想要一个 true 随机数。现在,他可能不会,他可能只是在问Random 的问题,但是...... :-)

标签: c# random


【解决方案1】:

DateTime.Now.Ticks 的分辨率仅为approximately 16ms,因此如果您在 16 毫秒“槽”内多次创建具有该重载的 Random,它们都将使用相同的值播种,因此您将获得相同的值顺序。

在循环外初始化 Random,以便生成单个 Random 序列,而不是每次在循环内创建它,这可能导致 Randoms 被播种相同的值,从而产生相同的序列.

更新

我之前的观点是,默认构造函数使用 CPU 计时初始化 Random 是不正确的,默认构造函数实际上使用了 Environment.TickCount,即:

一个 32 位有符号整数,包含自上次启动计算机以来经过的时间量(以毫秒为单位)。

仍然具有低分辨率。如果您快速连续创建多个Random 实例,它们可以很容易地在同一时间段内创建,因此具有相同的种子值,并创建相同的序列。创建Random 的单个实例并使用它。

更新

对于您的 cmets,如果您希望跨多个线程生成随机序列,请参阅以下 Jon Skeet 文章,该文章讨论了线程安全包装器:

https://codeblog.jonskeet.uk/2009/11/04/revisiting-randomness

【讨论】:

  • 默认的 random() 种子是当前 CPU 滴答数?
  • @Kyle 我已经更新了我的答案。
  • 谢谢。 ^.^ 我不知道我是否还应该使用随机,因为我使用线程。我读过随机不是线程安全的。如果我在函数之外初始化 random() 我认为它会导致问题。
  • @Kyle 请参阅 Jon Skeet 的以下文章,其中讨论了使用 Random 的线程安全方法:msmvps.com/blogs/jon_skeet/archive/2009/11/04/…
【解决方案2】:

Random 这样的伪随机数生成器应该只播种一次,所以:

private static Random _rand = new Random();
public static int Roll()
{
    return _rand.Next(1, 100);
}

(注意我的返回值是int 而不是void;问题中引用的Roll 函数会导致语法错误。)

但你的标题说“创造一个真正的随机”Random 不会为你这样做,它是一个 pseudo-random 数字生成器,这意味着它是确定性的,如果你不知道种子就很难预测。通常这对于大多数用途来说已经足够了,但如果您需要 真实 随机性,则需要熵源。 http://random.org 是一种流行的。

【讨论】:

  • 感谢您提及这一点。我正在根据内存在线程中重写它,这是一个语法错误。感谢您的帮助。
【解决方案3】:

使用随机数生成器的常用方法是播种一次,保存它们并在整个程序中反复调用它们。只要您在开始时从合适的值播种,您就应该获得可接受的随机性 - 假设您使用的生成器正在使用一个函数返回适合您的目的的随机值。因此,将您的 Random 实例保存在 Roll() 函数之外,在第一次使用时为其播种,然后在每次需要另一个数字时对其调用 Next()。

当您深入了解时,计算机上没有真正的随机数生成,只有基于种子的伪随机序列。然而,人类在识别随机性方面很糟糕,所以通常没问题。

【讨论】:

  • lol@humans 识别随机性。谢谢您的帮助。 ^^
【解决方案4】:

我应该使用 RNGCryptoServiceProvider 类来生成卷吗?

如果这是一场涉及金钱的严肃游戏,那么:是的。

【讨论】:

  • 钱没有危险。我想复制逼真的卷。所以当人们玩的时候感觉就像他们真的在掷骰子。
  • 随机将绰绰有余,但请阅读有关播种的其他答案。
  • 我将使用它,因为 random() 不是线程安全的。不过,我今天学到了更多关于 random() 的东西。
【解决方案5】:

我假设您调用Roll() 方法的速度如此之快以至于Now.Ticks 是一样的?

解决此问题的最简单方法是每次调用Roll() 时都创建一个新的Random() 实例,而是创建一个静态变量来保存Random() 的单个实例。

【讨论】:

  • 是的,这就是问题所在。感谢您的帮助。
【解决方案6】:

您应该只在 Roll 函数之外创建一次 Random 类,并为其设置一个唯一值。

每次调用 Roll 时都会重新创建 Random,这会导致“非随机数”。

【讨论】:

  • 感谢您指出这一点。 ^^
猜你喜欢
  • 2012-09-29
  • 2013-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多