【问题标题】:Coin flip simulation never exceeding a streak of 15 heads硬币翻转模拟从未超过 15 次正面
【发布时间】:2013-11-06 04:59:35
【问题描述】:

我正在模拟St Petersburg Paradox,当时我意识到我的掷硬币代码从未记录到连续超过 15 个正面的条纹。我运行了 100,000,000 次模拟,这应该会导致 平均 1526 条正面 16 长

(0.5^16) x 100,000,000 = 1526

很明显,出了点问题。

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

int main(int argc, char const *argv[])
{
srand(time(0));

int i, lim = 100000000, streak = 0, maxstreak = 0;
for (i = 0; i < lim; ++i)
{
    if (rand()%2) {
        streak++;
        if (streak > maxstreak) maxstreak = streak;
    }
    else streak = 0;
}

printf("Ran %d times, longest streak of %d\n", lim, maxstreak);
return 0;
}

每次都返回以下内容:

Ran 100000000 times, longest streak of 15

感谢您的帮助!

编辑:在 Windows 7 x64 上运行 GCC 版本 4.6.2。对一般编程有点陌生。

编辑2:感谢大家的帮助!有人留下来,我想知道当前的实现会限制 15 个头吗? rand() 函数怎么会被如此有趣地破坏以产生这个问题?

【问题讨论】:

  • 我在不同的机器上运行你的程序,我通常得到25、26、27左右的结果。
  • 运行并收到 24、26、24。怀疑您的 rand() 存在偏见。
  • 跑了 4 次,得到了 25、26、23、28。你用的是什么平台和编译器?
  • RNG 的类型很多,有些比其他的更“随机”。
  • 奇怪的是 RAND_MAX,根据定义必须至少为 32767。15 位 嗯。想知道 OP 会用 `if ((rand()%2) == 0)` 得到什么?

标签: c random coin-flipping


【解决方案1】:

尝试为您的随机数生成器选择不同的种子值。尽管 rand() 是一个非常好的随机数生成器,但它确实是一个伪随机数生成器。您可能想阅读 rand (man -s3 rand) 的手册页,其中明确指出(对于某些实现)您应该使用高阶位而不是低阶位...

NOTES
   The versions of rand() and srand() in the Linux C Library use the  same
   random number generator as random(3) and srandom(3), so the lower-order
   bits should be as random as the higher-order bits.  However,  on  older
   rand()  implementations,  and  on  current implementations on different
   systems, the lower-order bits are much less  random  than  the  higher-
   order  bits.   Do  not use this function in applications intended to be
   portable when good randomness is needed.  (Use random(3) instead.)

如果不了解您正在运行程序的系统的更多信息,我们无法知道这是否是您的问题。但是请尝试更改您的代码以使用与 2^0 位不同的位。

运行你的版本对我有用,

/coinflipsim 
Ran 100000000 times
head 50006650, streak 27
tail 49993350, streak 25

这是适用于我的代码,使用的位与位 0 不同,

int main(int argc, char const *argv[])
{
    srand(time(0));

    int i, lim = 100000000;
    int head=0, tail=0;
    int hstreak=0, tstreak=0;
    int hstreakmax=0, tstreakmax=0;
    for (i = 0; i < lim; ++i)
    {
        //if (rand()%2)
        if( rand() & (1<<13) ) //pick a bit, try different bits
        {
            head++;
            if( ++hstreak>hstreakmax) hstreakmax=hstreak;
            tstreak=0;
        }
        else {
            tail++;
            if( ++tstreak>tstreakmax) tstreakmax=tstreak;
            hstreak=0;
        }
    }
    printf("Ran %d times\n",lim);
    printf("head %d, streak %d\n",head,hstreakmax);
    printf("tail %d, streak %d\n",tail,tstreakmax);
    return 0;
}

将 rand()%2 行更改为此并重新运行,

        if( rand() & (1<<13) ) //pick a bit, try different bits

不同的结果,

./coinflipsim 
Ran 100000000 times
head 50001852, streak 25
tail 49998148, streak 28

【讨论】:

  • 谢谢!我还不太清楚使用位移,但我会弄乱它,看看它是如何工作的,干杯。我非常愿意去了解这部分是如何工作的,但是当然,这里的任何详细说明都将不胜感激。 rand() &amp; (1&lt;&lt;13)
  • 表达式 (1
【解决方案2】:

您的代码很好 - 您的 C 库的 rand() 实现显然低于标准。可能输出的低位之间存在相关性,或者内部状态可能非常小(因此您的 100,000,000 次试验实际上多次覆盖了生成器的整个输出序列)。

在第一种情况下(相关输出位),您可以对生成器的输出进行后处理以“白化”它,但在第二种情况下,您需要插入更好的实现,例如 Mersenne Twister。

【讨论】:

    【解决方案3】:

    令 X(i) 是第 i 次翻转有正面的事件。让 E(i) = union { X(j) | i

    您的分析假设事件 E(i) 是独立的。这是不正确的。如果 E(i) 不发生,则大大降低直接在前的 E(i-1)、E(i-2) 等发生的概率。

    说 E(i) 和 E(j) 独立当且仅当 |i - j| 是正确的 >= 16。

    可能是您的随机数生成器不是很好。由于rand() 最终会产生确定性模式,因此随机生成器可能会产生一个永远不会连续给出 16 个偶数(或奇数)数的模式。

    【讨论】:

    • 分析错了,但正确答案离0条纹还很远。
    • @n.m.是的,这就是为什么我说“当其他人运行此程序时,他们会获得大约 26 条条纹。”这意味着对条纹数量的预期约为 26。
    • 谢谢,这很有见地。如果您有任何相关资源,我会更深入地研究数学。
    • 程序输出的不是长度为16的条数,而是记录的最长条数。
    • 不,26 是最长的连胜纪录。 16连胜的数量(非常粗略)大约是150个左右。
    猜你喜欢
    • 2019-01-07
    • 2018-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-14
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多