随机数生成器,C++

5

我知道在C++中生成随机数有一些限制(可能是不均匀的)。那么如何生成1到14620之间的数字呢?

谢谢。


你现在是否查过如何在 C++ 中使用随机数?同时,你也没有说明是需要一个比内置的 rand 函数更好的解决方案还是只是需要被告知如何使用 rand 函数。 - thecoshman
你能否澄清一下你所说的“C++中随机数生成的限制”是什么意思?更或者说,均匀的序列来自于好或者不好的生成器。 - Francesco
如果rand()是(可以是非均匀的),那么它就没有太多用处。 - Martin York
@thecoshman:是的,我查了一下,但我无法弄清楚如何使它们在均匀分布的意义上真正随机。 - notrockstar
@notroackstar,也许你的问题应该说明你想要真正随机的数字,或者至少是非均匀分布的。 - thecoshman
我认为他想要一个均匀分布。我的建议是查阅《C++数值计算方法》这本书。 - morishuz
8个回答

20

如果您有一个c++0x环境,那么与boost库非常接近的衍生标准现已成为标准:

#include <random>
#include <iostream>

int main()
{
    std::uniform_int_distribution<> d(1, 14620);
    std::mt19937 gen;
    std::cout << d(gen) << '\n';
}

这将会快速、简便且高质量。

你没有明确说明,但是如果你想要浮点数,只需使用以下代码:

std::uniform_real_distribution<> d(1, 14620);

如果你需要非均匀分布,你可以很容易地构建自己的分段常数分布或分段线性分布。


请注意,uniform int和uniform real分布之间有一个重要的区别。uniform int分布返回闭区间[1,14620]内的数字,而uniform real分布返回半开区间[1,14620)内的数字。 - Blastfurnace
1
可以使用类似于C++0x的种子生成方式,std::random_device r; std::mt19937 gen(r()); - Cubbi
如果你没有C++0x,那么boost::random可能是你的标准,除非你真的只需要一些快速而肮脏的东西... - Chinasaur

19

常用的方法是使用std::rand()函数并进行取模操作:

#include<cstdlib>
#include<ctime>

// ...
std::srand(std::time(0));  // needed once per program run
int r = std::rand() % 14620 + 1;

然而,正如@tenfour在他的答案中提到的那样,模运算符可能会破坏std::rand()返回值的一致性。这是因为模运算会将它丢弃的值转换成有效值,并且这种转换可能不是一致的。例如,对于[0, 10)中的n,值n%99转换为0,因此您可以通过真正的零或将9转换为零来获得零。其他值只有一次机会产生。

另一种方法是将std::rand()生成的随机数转换为范围在[0,1)之间的浮点数,然后将其转换和移位到所需的范围内。

int r = static_cast<double>(std::rand()) / RAND_MAX * 14620) + 1;

12

srand() / rand()是你需要的函数,正如其他人所回答的一样。

使用%运算符存在的问题是结果是明显不均匀的。为了说明这一点,想象一下rand()返回0-3的范围。以下是假设调用它4000次的结果:

0 - 1000 times
1 - 1000 times
2 - 1000 times
3 - 1000 times

现在,如果您对 (rand() % 3) 进行相同的采样,您会注意到结果会像这样:

0 - 2000 times
1 - 1000 times
2 - 1000 times

哎呀!更加统一的解决方案是:

int n = (int)(((((double)std::rand()) / RAND_MAX) * 14620) + 1);

很抱歉代码写得有些粗糙,但是核心思想是使用浮点数运算将随机数缩放到所需的区间,并转换为整数。


'(((double)std::rand()) / RAND_MAX)' - 你能解释一下这段代码吗?是先生成随机数,然后再将其缩放到RAND_MAX的范围内吗? - notrockstar
1
通过除以 RAND_MAX,您可以生成介于 0.0 和 1.0 之间的均匀随机数。这使得它很容易缩放到您想要的任何范围。在您的情况下,是 1-14620。 - tenfour
我不明白为什么你想要消除中间的“double”;最终结果是一个像你想要的“int”。除了使用其他人建议的“%”之外,我不知道还有什么只使用整数的答案。 - tenfour
1
很好的使用了强制类型转换来保持分辨率尽可能高,不过希望能看到使用C++强制类型转换。 - thecoshman

4
使用 rand 函数。
( rand() % 100 ) is in the range 0 to 99
( rand() % 100 + 1 ) is in the range 1 to 100
( rand() % 30 + 1985 ) is in the range 1985 to 2014

( rand() % 14620 + 1 ) is in the range 1 to 14620

编辑:

如链接中所述,随机数生成器在使用之前应使用 srand 进行初始化。常用的区分值是调用time函数的结果。


3
你漏掉了一个重要的补充:需要注意的是,这个取模运算并不能在整个范围内生成均匀分布的随机数 - user229044
没错,需要先进行种子文件的下载。链接底部提到了种子文件的下载。我会更新并包含这个信息。 - James
@James,@meagar,我怎样才能生成真正的均匀分布的随机数?我需要在算法的一个循环内生成30000个这样的随机数。 - notrockstar
@meager 我必须承认我之前不知道这件事,而且必须承认我目前还不理解为什么。 - sstn
@sstn:http://en.wikipedia.org/wiki/Pseudorandom_number_generator 上有一些关于为什么需要种子的信息。 - James
显示剩余2条评论

2
如前所述,您可以使用rand()。例如: int n = rand()%14620 + 1; 可以完成工作,但不是均匀的。 这意味着一些值(低值)会稍微更频繁地出现。这是因为rand()产生0到RAND_MAX范围内的值,而RAND_MAX通常不能被14620整除。例如,如果RAND_MAX == 15000,则数字1将比数字1000多出两倍,因为rand() == 0rand() == 14620都产生n == 1,但只有rand() == 999才会使n == 1000成立。
然而,如果14620远小于RAND_MAX,则此效果是可以忽略的。在我的电脑上,RAND_MAX等于2147483647。如果rand()在0到RAND_MAX之间产生均匀样本,则由于2147483647%14620 = 10327且2147483647 / 14620 = 146886,n平均会在1到10328之间出现146887次,而在10329和14620之间的数字平均会出现146886次,如果您绘制了2147483647个样本。
如果您问我,这并没有太大的区别。
但是,如果RAND_MAX == 15000,它将会有所不同,如上所述。 在这种情况下,一些早期的帖子建议使用: int n = (int)(((((double)std::rand()) / RAND_MAX) * 14620) + 1); 使其“更均匀”。 请注意,这仅更改更频繁出现的数字,因为rand()仍然返回“仅”RAND_MAX个不同的值。 要使其真正均匀,您必须拒绝任何在14620 * int(RAND_MAX / 14620)和RAND_MAX之间的整数形式rand()并再次调用rand()。 在RAND_MAX == 15000的示例中,您将拒绝在14620和15000之间的任何rand()值,并再次绘制。 对于大多数应用程序,这是不必要的。我会更担心rand()的随机性。

1

rand()函数并不是最好的随机生成器,更好的方法是使用CryptGenRandom()。

以下示例应该可以解决问题:

#include <Windows.h>

// Random-Generator
HCRYPTPROV hProv;
INT Random() {
    if (hProv == NULL) {
        if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_SILENT | CRYPT_VERIFYCONTEXT))
            ExitProcess(EXIT_FAILURE);
    }

    int out;
    CryptGenRandom(hProv, sizeof(out), (BYTE *)(&out));
    return out & 0x7fffffff;
}

int main() {
    int ri = Random() % 14620 + 1;
}


0

模数运算符是最重要的,您可以使用此模数应用限制,看看这个:

// random numbers generation in C++ using builtin functions
#include <iostream>

using namespace std;

#include <iomanip>

using std::setw;

#include <cstdlib>   // contains function prototype for rand

int main()
{
// loop 20 times
for ( int counter = 1; counter <= 20; counter++ ) {

    // pick random number from 1 to 6 and output it
    cout << setw( 10 ) << ( 1 + rand() % 6 );

    // if counter divisible by 5, begin new line of output
    if ( counter % 5 == 0 )
        cout << endl;

}

return 0;  // indicates successful termination

} // end main

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接