如何缩小rand()的数字?

Max*_*xpm 34 c++ random generator mersenne-twister

以下代码每秒输出一个随机数:

int main ()
{
    srand(time(NULL)); // Seeds number generator with execution time.

    while (true)
    {
        int rawRand = rand();

        std::cout << rawRand << std::endl;

        sleep(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

我如何调整这些数字的大小,使它们总是在0-100的范围内?

Bla*_*ace 77

如果您正在使用C++并且关注良好的分发,则可以使用TR1 C++ 11 <random>.

#include <random>

std::random_device rseed;
std::mt19937 rgen(rseed()); // mersenne_twister
std::uniform_int_distribution<int> idist(0,100); // [0,100]

std::cout << idist(rgen) << std::endl;
Run Code Online (Sandbox Code Playgroud)

  • 虽然这是获得随机数均匀分布的正确方法,但这并不能解决MaxPM的问题,即无法获得良好的分布,但询问"如何缩小rand()中的数字". (20认同)

Kon*_*lph 30

到目前为止发布的所有示例实际上都给出了分布不均的结果 经常执行代码并创建统计信息以查看值是如何变形的.

在任何范围[0,N ]中生成实数均匀随机数分布的更好方法如下(假设实际上遵循均匀分布,这是非常明显的):rand

unsigned result;
do {
    result = rand();
} while (result > N);
Run Code Online (Sandbox Code Playgroud)

当然,这种方法很慢但确实产生了良好的分布.一种稍微聪明的方法是找到小于的N的最大倍数RAND_MAX并将其用作上限.在那之后,人们可以安全地接受result % (N + 1).

为了解释为什么天真模量方法是坏的以及为什么上述更好,请参考Julienne关于使用rand的优秀文章.

  • 虽然这绝对是正确的; 效果很轻微.RAND_MAX至少为32677,在我的机器上为2,147,483,647.对于最小RAND_MAX,这意味着0-77范围内的每个数字出现327次,而78-99中的数字仅出现326次,使得它们减少0.3%.对于我的机器的RAND_MAX,差异是数字0-47比数字48-99更可能是0.000 005%.对于大多数需求(例如,在严重的蒙特卡罗建模之外),简单模数将正常工作. (3认同)
  • 通过执行`while(结果>(RAND_MAX - RAND_MAX%N))`然后除以`RAND_MAX/N`,您可以轻松做得更好.对于小N,你丢弃的数字要少得多,但保持均匀分布. (2认同)

Jus*_*ner 26

int rawRand = rand() % 101;
Run Code Online (Sandbox Code Playgroud)

请参阅(了解更多详情):

rand - C++参考

其他人也指出,这不会给你最好的随机数分布.如果您的代码中的这类内容很重要,则必须执行以下操作:

int rawRand = (rand() * 1.0 / RAND_MAX) * 100;
Run Code Online (Sandbox Code Playgroud)

编辑

三年过去了,我正在编辑.正如其他人提到的,rand()有很多问题.显然,当有更好的替代方案时,我不能推荐它的使用.您可以在此处阅读有关详情和建议的所有信息:

rand()认为有害| GoingNative 2013

  • 请不要在实践中使用这种方法 - 这很糟糕. (20认同)
  • 就像我之前说的那样,使用模数法并不是完全随机的.100个数字,uint有648个完整范围0-100,一个范围0-87.因此,从0到87的数字比88-100的数字有更好的机会. (4认同)
  • -1.你仍然会得到一个不均匀的分布.`rand()`有RAND_MAX + 1个值; 除非它是101的倍数(它可能不是),否则没有办法将它们分配给101个桶而没有其中一个更大. (3认同)
  • 对于需要随机数的人,他们不会使用rand来生成它们.模数和重新缩放范围调整引入的失真仅在您实际上首先具有随机分布时才有意义. (2认同)

dr *_*bob 6

你可以做

cout << rawRand % 100 << endl; // Outputs between 0 and 99

cout << rawRand % 101 << endl; // outputs between 0 and 100
Run Code Online (Sandbox Code Playgroud)

为人民投降;注意最初发布后一分钟,我留下了评论:

来自http://www.cplusplus.com/reference/clibrary/cstdlib/rand “请注意,尽管这种模运算不会在跨度中生成真正均匀分布的随机数(因为在大多数情况下,较小的数字更有可能出现),但通常对于短跨度是一个很好的近似值。”

使用64位int并使用100个数字作为输出时,数字0-16用数字的1.00000000000000000455%表示(相同精度的相对精度为1%乘以10 -18),而数字17-99表示用数字的0.99999999999999999913%。是的,分布不是很完美,但是对于小跨度来说是一个很好的近似值。

还要注意,OP在哪里要求分配相同的数字?就我们所知,这些用于微小偏差无关紧要的用途(例如,除密码学以外的任何事物);如果他们使用密码学数字,那么这个问题对于他们来说太天真了,以至于他们无法编写自己的密码学)。

编辑 -对于真正关心随机数均匀分布的人,以下代码有效。请注意,这不一定是最佳的,因为对于64位随机整数,它将需要两次调用,rand()每10 ^ 18次调用一次。

unsigned N = 100; // want numbers 0-99
unsigned long randTruncation = (RAND_MAX / N) * N; 
// include every number the N times by ensuring rawRand is between 0 and randTruncation - 1 or regenerate.
unsigned long rawRand = rand();

while (rawRand >= randTruncation) {
    rawRand = rand();  
// with 64-bit int and range of 0-99 will need to generate two random numbers
// about 1 in every (2^63)/16 ~ 10^18 times (1 million million times)

// with 32-bit int and range of 0-99 will need to generate two random numbers 
// once every 46 million times.

}
cout << rawRand % N << stdl::endl;
Run Code Online (Sandbox Code Playgroud)

  • 来自http://www.cplusplus.com/reference/clibrary/cstdlib/rand/“请注意,尽管此模运算不会在跨度中生成真正均匀分布的随机数(因为在大多数情况下,较小的数字更有可能出现) ,但通常是短跨度的一个很好的近似值。” (2认同)