【问题标题】:Better random algorithm?更好的随机算法?
【发布时间】:2010-12-27 02:23:43
【问题描述】:

我正在用 C++ 制作游戏,它涉及用随机布尔值(是或否)填充图块,是或否由 rand() % 1 决定。感觉不是很随意。

我在启动时使用srandctime,但似乎出现了相同的模式。

是否有任何算法可以创建非常随机的数字?或者关于如何改进rand() 的任何建议?

【问题讨论】:

  • 只是出于好奇,您的阵列有多“大”?如果它很小,您可能看不到太多随机性。
  • 你能显示代码吗?您播种的方式可能有些问题,这就是为什么它似乎具有一致的模式。
  • "rand() % 2" 会产生更好的结果。
  • rand() % 1 通常为零,因为 1 的值足够小。
  • 人类在检测随机性方面是出了名的差。如果它很重要,那么不要猜测:一旦你修复了 %1 / %2 错误,捕获大量结果(1000 秒,而不是 10 秒),将它们粘贴到 Excel 中,然后计算平均值。

标签: c++ algorithm random seed


【解决方案1】:

【讨论】:

    【解决方案2】:

    “是”或“否”的完美方式是随机切换它们。你可能不需要随机函数。

    【讨论】:

    • 不是,OP说他需要随机值。
    • +1,@LorenVS- 在一个完全确定的宇宙中,任何事物都可能是随机的。
    • 好的,我可以删除这个,但我想,“OP 感觉不是很随意。”,我认为他得到了类似“Yes Yes Yes Yes Yes No”的东西,他/她可能认为它不是随机的
    • int random() { return 4; } // Completely random chosen number差不多。
    • ... 公平地掷出 D6。我从我收集的 D% 骰子中获得了更多的随机性,而且他们有一个奇怪的诀窍来掷出相当一致的 88%。
    【解决方案3】:

    标准随机数生成器的最低位不是很随机,这是一个众所周知的问题。

    我会调查boost random number library

    【讨论】:

      【解决方案4】:

      真正的随机性通常看起来不是很随机。确实希望看到奇怪的运行。

      但是,您至少可以立即做的一件事是避免只使用最低位。引用 C 中的数值配方:

      如果要生成 1 到 10 之间的随机整数,则应始终使用高位来实现,如
      j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));
      
      从来没有类似的东西
      j = 1 + (rand() % 10);
      
      (使用低位)。

      另外,您可以考虑使用具有更好属性的不同 RNG。 Xorshift 算法是一个不错的选择。它在几行 C 代码中既快速又紧凑,并且在统计上应该足以胜任几乎所有游戏。

      【讨论】:

      • 避免低位在很大程度上取决于生成器。一些 PRNG 会生成较弱的 high-order 位。
      【解决方案5】:

      许多伪随机数生成器都存在循环低位,尤其是 linear congruential 算法,这通常是最常见的实现。有些人建议移出最低有效位来解决这个问题。

      【讨论】:

        【解决方案6】:

        此外,如果您重新播种的速度过快,那么您将获得完全相同的数字。就我个人而言,我使用一个仅在时间改变时才更新种子的类。

        【讨论】:

        • 如果不重新播种,随机性会降低。
        • 如果你重新播种,它不是随机的。
        • rand() 会经过一个设计为随机(有点)的序列,只要您继续前进。如果您重新播种,那么您将开始一个新的序列。不能保证这两个序列的关系。
        • 如果你用相同的号码重新播种马丁然后你。我说的是随着时间的不同而重新播种。
        • @high6:不,这正是我要说的。您在应用程序启动时播种一次,然后您不再重新播种。否则,你就违背了种子的目的。开始一个随机序列。
        【解决方案7】:

        每次if(rand() % 50==0) 条件为真时,让您的数字感觉更加随机的快速方法是重新播种生成器。

        【讨论】:

        • 什么...这种情况告诉您需要重新播种?
        • 根据生成的数字范围和数字生成器,它会(应该)自动为每 50 个(或其他)生成器 1 重新播种生成的数字
        • 请注意,“感觉更随机”并不等于更好的统计随机性属性。 PRNG 是变化无常的东西,尤其是在处理不当或没有非常精确的知道一个人在做什么的情况下(即使这样,它们也会爆炸回到你的脸上)。
        【解决方案8】:

        低位不是很随机。
        通过使用 %2,您只检查随机数的底部位。

        假设您不需要加密强度随机性。
        那么下面的应该就OK了。

        bool  tile = rand() > (RAND_MAX / 2);
        

        【讨论】:

        • 实际上,通过使用 %1 他们甚至没有使用底部位。 :)
        • 您的解决方案与原始解决方案存在相同的问题:仅使用 rand() 的返回值的一位。 OP 仅使用最低位,您的解决方案仅使用最高位。更好的解决方案是使用所有位。
        • @sbk:如果我认真思考,是的,你是对的。我只是在简化 'rand()/(RAND_MAX + 1.0) * RANGE' 范围是 2。
        【解决方案9】:

        人们说低位不是随机的。所以从中间尝试一些东西。这将为您提供第 28 位:

        (rand() >> 13) % 2
        

        【讨论】:

        • 享受微软 CRT 带来的乐趣。一个不错的,无穷无尽的零流:-)
        • 是的,在这种情况下最好使用 13
        【解决方案10】:

        Knuth 建议通过减法生成随机数。它被认为是相当随机的。有关 Scheme 语言的示例实现,请参阅here

        【讨论】:

        • TAoCp 是一本好书,但它已经过时了,并且在过去 20 年的 PRNG 研究中发生了很多。实际上,减法方法并不比 LCG 好多少。
        【解决方案11】:

        我已经成功使用 Mersenne Twister 随机数生成器多年。其源代码可从广岛大学数学系here 获得。 (直接链接,这样你就不用看日文了!)

        这个算法的优点在于:

        1. 它的“随机性”非常好
        2. 它的状态向量是一个无符号整数和一个索引的向量,因此很容易保存它的状态,重新加载它的状态,并从它停止的地方恢复一个伪随机过程。

        我建议你看看你的游戏。

        【讨论】:

        • 显示第二个引用优势成立的任何 PRNG。实际上,这几乎是 PRNG 的标准功能。
        【解决方案12】:

        除了编写另一个 PRNG 或使用库之外,您可以做的最简单的事情就是使用一次调用 rand() 给您的 all 位。大多数随机数生成器可以分解为具有一定随机性和统计特性的比特流。在该流上均匀分布的各个位不需要具有相同的属性。本质上,您在这里丢弃了 14 到 31 位的伪随机性。

        您可以只缓存调用rand() 生成的数字并使用它的每一位(当然,这取决于rand() 给您的位数,这取决于RAND_MAX)。因此,如果您的 RAND_MAX 是 32768,您可以按顺序使用该数字的最低 15 位。尤其是如果RAND_MAX 这么小,您就没有处理生成器的低位,因此从高端获取位不会给您带来太多好处。例如,Microsoft CRT 使用等式生成随机数

        xn + 1 = xn子> · 214013 + 2531011

        然后移开该结果的最低 16 位并将其限制为 15 位。所以那里的生成器没有低位。这在很大程度上适用于 RAND_MAX 高达 231 但有时你不能指望它的生成器(因此可能将自己限制为 16 或 24 位,取自高阶结束)。

        因此,一般来说,只需缓存对 rand() 的调用结果,并按顺序为您的应用程序使用该数字的位,而不是 rand() % 2

        【讨论】:

          【解决方案13】:

          为了获得好的结果,您确实需要一个生成器来组合多个生成器的结果。仅仅丢弃底部是一个非常愚蠢的答案。

          带有进位的乘法实现起来很简单,并且单独使用时效果很好,如果您有多个乘法并将结果结合起来,您将获得非常好的结果。它也不需要太多内存,而且速度非常快。

          【讨论】:

          • 你不需要组合生成器来获得好的结果,你只需要使用一个好的生成器。此外,在不知道自己在做什么的情况下组合生成器可能会产生糟糕的结果。
          【解决方案14】:

          C++11 具有以下实现 Mersenne tittie twister 算法的方式。来自cppreference.com

          #include <random>
          #include <iostream>
          
          int main()
          {
              std::random_device rd;
              std::mt19937 gen(rd());
              std::uniform_int_distribution<> dis(1, 6);
          
              for (int n=0; n<10; ++n)
                  std::cout << dis(gen) << ' ';
              std::cout << '\n';
          }
          

          这会产生适合模拟的随机数,而没有许多其他随机数生成器的缺点。它不适合密码学;但加密随机数生成器的计算量更大。

          还有Well equidistributed long-period linear算法;有许多示例实现。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-03-26
            • 1970-01-01
            • 1970-01-01
            • 2014-07-15
            • 2010-09-05
            相关资源
            最近更新 更多