3

我通过做一个简单的基于控制台的测验应用程序来练习时注意到了这一点。当我使用rand()它时,它会连续多次给我相同的值。数字范围越小,问题越大。

例如

for (i=0; i<10; i++) {
    x = rand() % 20 + 1;
    cout << x << ", ";
}

会给我1, 1, 1, 2, 1, 1, 1, 1, 14,- 肯定太多了,对吧?我通常从无到4个奇数(其余的都一样,也可以11, 11, 11, 4, 11 ...

难道我做错了什么?还是rand()不是我想的那么随机?
(或者这只是我不知道的 C#/Java 的一些习惯?它也经常发生在我身上……)

4

4 回答 4

4

如果我多次运行该代码,我会得到不同的输出。当然,不像我想要的那样多样化,但似乎不是确定性的(虽然它当然是,因为rand()只给出伪随机数......)。

但是,您处理数字的方式不会给您提供 [1,20] 上的均匀分布,我猜这就是您所期望的。实现这一点相当复杂,但绝不是不可能的。例如,请查看cplusplus.com上的文档<random>- 在底部有一个展示程序,可以在 [0,1) 上生成均匀分布。要使其达到 [1,20),您只需将输入参数更改为生成器 - 它可以在您喜欢的任何范围内为您提供均匀分布。

我做了一个快速测试,并调用rand()了一百万次。正如您在下面的输出中看到的那样,即使在非常大的样本量下,分布中也存在一些不均匀性。随着样本数量趋于无穷大,这条线(可能)会变平,使用类似的东西rand() % 20 + 1会给你一个需要长时间才能完成的分布。如果您采用其他方法(如上面的示例),即使对于非常小的样本量,您也更有可能实现均匀分布。

100 万次调用 rand()

编辑:
我看到其他几个人在使用srand()之前发布了关于使用随机数生成器播种的信息。这是一个很好的建议,但在这种情况下它不会解决您的问题。我再说一遍:在这种情况下,播种不是问题。

种子主要用于控制程序输出的可重复性。如果你用一个常数值(例如 0)为你的随机数播种,程序每次都会给出相同的输出,这对于测试一切是否正常工作很有用。通过使用非恒定值(当前时间是一个流行的选择)播种,您可以确保结果在程序的不同运行之间有所不同。

根据 C++ 标准,根本不调用srand()与调用相同。srand(1)因此,每次运行程序都会得到相同的结果,但每次运行都会得到一系列完全有效的伪随机数。

于 2013-03-31T20:27:49.417 回答
3

听起来你正在打模偏差

通过使用将随机数缩放到一个范围%并不是一个好主意。如果您将其减小到 2 的幂的范围,这几乎是可以接受的,但仍然很差。它主要受较小位的影响,这些较小的位在许多算法(rand()尤其是)中通常不太随机,并且它以非均匀的方式收缩到较小的范围,因为您减少到的范围不会平均划分您的随机范围数发生器。要减小范围,您应该使用除法和循环,如下所示:

// generate a number from 0 to range-1
int divisor = MAX_RAND/(range+1);
int result;
do
{
    result = rand()/divisor;
} while (result >= range);

这并不像看起来那么低效,因为循环几乎总是只通过一次。此外,如果您打算将生成器用于接近的数字,MAX_RAND您将需要一个更复杂的方程式divisor,我无法立即记住。

此外,rand()它是一个非常差的随机数生成器,如果您关心结果的质量,请考虑使用 Mersenne Twister 之类的东西。

于 2013-03-31T21:14:40.197 回答
2

您需要先调用srand()并给它时间参数以获得更好的伪随机值。

例子:

#include <iostream>
#include <string>
#include <vector>
#include "stdlib.h"
#include "time.h"

using namespace std;

int main()
{
    srand(time(0));
    int x,i;
    for (i=0; i<10; i++) {
        x = rand() % 20 + 1;
        cout << x << ", ";
    }
    system("pause");
    return 0;
}

如果您不希望任何生成的数字重复并且内存不是问题,您可以使用一个vector整数,随机打乱它,然后获取前 N 个整数的值。

例子:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
    //Get 5 random numbers between 1 and 20
    vector<int> v;
    for(int i=1; i<=20; i++)
        v.push_back(i);
    random_shuffle(v.begin(),v.end());
    for(int i=0; i<5; i++)
        cout << v[i] << endl;
    system("pause");
    return 0;
}
于 2013-03-31T20:24:44.293 回答
0

可能的问题是您每次都使用相同的“随机”数字,并且任何intmod 1 都是零。换句话说(myInt % 1 == 0) 总是正确的。而不是%1,使用% theBiggestNumberDesired.

此外,将随机数播种为srand. 使用常量种子来验证您是否获得了良好的结果。然后更换种子以确保您仍然获得良好的结果。然后使用像时钟这样更随机的种子来进一步吸奶。使用随机种子释放。

于 2013-03-31T20:23:19.227 回答