避免蒙特卡罗模拟中的基本rand()偏差?

时间:2015-03-31 13:22:39

标签: c vba dll random arc4random

我正在用C语言重写C中的蒙特卡罗模拟,以便在VBA / Excel的dll中使用。计算中的“引擎”是创建0到10001之间的随机数,并与5000-7000邻域中的变量进行比较。每次迭代使用4-800次,我使用100000次迭代。所以每次运行大约有50.000.000代随机数。

在Objective C中,测试表明没有偏见,我对C代码有很大的问题。 Objective C是C的超集,因此95%的代码都是复制粘贴,很难搞砸。我昨天和今天整天经历了很多次,我没有发现任何问题。

我使用srand()留下了arc4random_uniform()和rand()之间的区别,特别是因为偏向0到10000的较低数字。我所进行的测试与这样的偏差是一致的。对于大约5000以下的数字,.5到2%。任何其他解释是我的代码避免重复,我想它不会重复。

代码非常简单(“spiller1evne”和“spiller2evne”是5500到6500之间的数字):

srand((unsigned)time(NULL));
for (j=0;j<antala;++j){
[..]
        for (i=1;i<450;i++){
            chance = (rand() % 10001);

[..]

             if (grey==1) {


                 if (chance < spiller1evnea) vinder = 1;
                 else vinder = 2;
            }
            else{
                if (chance < spiller2evnea) vinder = 2;
                else vinder = 1;
            }

现在我不需要真正的随机性,伪随机性非常好。我只需要在累积的基础上近似均匀分布(如果5555的可能性是5556的两倍并不重要。如果5500-5599的可能性为5500-5699并且可能性为5如果对0-4000有明显的0.5-2%偏差而不是6000-9999。

首先,rand()是我的问题听起来似乎有道理吗?是否有一个简单的实现满足我的低需求?

编辑:如果我的怀疑是合理的,我可以使用任何一个:

http://www.azillionmonkeys.com/qed/random.html

我是否可以将其作为替代品复制粘贴(我在C中编写并使用Visual Studio,真的是新手)?:

#include <stdlib.h>

#define RS_SCALE (1.0 / (1.0 + RAND_MAX))

double drand (void) {
    double d;
    do {
       d = (((rand () * RS_SCALE) + rand ()) * RS_SCALE + rand ()) * RS_SCALE;
    } while (d >= 1); /* Round off */
    return d;
}

#define irand(x) ((unsigned int) ((x) * drand ()))

EDIT2:很明显上面的代码在没有相同偏见的情况下工作,所以我建议任何拥有与上述相同的“中间路线”的人。它确实带来了惩罚,因为它调用rand()三次。所以我仍然在寻找更快的解决方案。

1 个答案:

答案 0 :(得分:0)

rand()函数会在[0,int]范围内生成RAND_MAX。如果通过模数运算符(%)将其转换为不同的范围,就像原始代码那样,那么除非目标范围的大小恰好均分RAND_MAX + 1,否则会引入非均匀性。这听起来就像你看到的那样。

您有多种选择,但如果您想坚持使用基于rand()的内容,那么我建议您对原始方法进行修改:

/*
 * Returns a pseudo-random int selected from the uniform distribution
 * over the half-open interval [0, limit), provided that limit does not
 * exceed RAND_MAX.
 */
int range_rand(int limit) {
    int rand_bound = (RAND_MAX / limit) * limit;
    int r;
    while ((r = rand()) >= rand_bound) { /* empty */ }
    return r % limit;
}

虽然原则上每次调用该函数的rand()次调用的数量都是无限制的,但实际上,对于相对较小的limit值,平均调用次数仅略大于1,并且每limit个值的平均值小于2。它通过从[0,RAND_MAX]的子集中选择初始随机数来消除我之前描述的非均匀性,其大小均匀地除以limit