C ++,某些组合永远不会出现使用rand()

时间:2014-10-13 01:01:28

标签: c++ random

我使用rand()进行两次整数,介于0和2之间。看来第一个int从不为0而第二个int是2.这是我的测试代码 -

#include <iostream>
#include <time.h>

int main()
{
    srand(time(NULL));
    int number1, number2;
    number1 = rand() % 3;
    number2 = rand() % 3;

    printf("%i, %i", number1, number2);
    return 0;
}

输出-25次尝试

2, 2
2, 2
2, 1
1, 2
0, 1
2, 2
1, 2
1, 0
2, 1
1, 0
0, 0
1, 2
2, 2
0, 0
2, 1
1, 0
2, 2
1, 0
2, 1
1, 0
0, 1
1, 2
1, 0
0, 0
2, 2

正如你所看到的,在25次尝试中,组合从未为0,2。这是我应该转移到的标志吗?随机&gt;?此外,永远不会有2,0。

4 个答案:

答案 0 :(得分:3)

不,这将发生在所有种子的9 *(1-1 / 9)^ 25 = 0.4736,或大约50%的时间。也就是说,你的前25个结果中将缺少{0,1,2}中带有数字的两位数序列,大约是运行程序的一半。

再次运行它,看看会发生什么。

答案 1 :(得分:2)

  1. 您一定要使用<random>。你越早忘记rand的存在,你就越幸福。

  2. rand有时会使用linear congruential generator(LCG)来实施。 LCG遭受许多缺陷,最相关的是顺序生成的数字的低阶位高度相关。因此,您不应使用rand() % k生成[0, k)范围内的数字。还有其他原因。实际上,从限制范围生成无偏随机整数涉及一些细微之处,<random>为您处理。

  3. srand(time(NULL))将随机数生成器播种到当前时间(以秒为单位),这意味着如果您按顺序多次运行程序,则种子将相同或相似。如果种子相同,则随机数序列也将相同。如果种子相似,则随机数序列也可以是相似的。除非在长期运行的程序中,否则不要这样做。为伪随机数生成器找到一个好种子可能很棘手。 <random>实施将有更好的默认行为,因此您通常不必担心这一点。

答案 2 :(得分:1)

是的。这段代码在第一次运行时给了我0.2对:

for( int i = 0; i < 20; ++i) {
  number1 = rand() % 3;
  number2 = rand() % 3;
  printf("%i, %i\n", number1, number2);
}

从均匀分布生成真正的随机数并不能保证给定(可能)的数字会出现在有限数量的试验中。良好PRNG的四个BSI criteria中的K2是

  

K2 - 一系列与'真实无法区分的数字   根据指定的统计检验随机'数字。

因此,生成伪随机数的趋势往往与从真正的随机分布中采样的方式相同 - 尽管由于限制,任何(可能的)数字将出现在某个点(时间小于或等于其周期)。

http://ideone.com/c5oRQL


使用std :: uniform_int_distribution

除了以上rand()不是最好的发电机。只要模运算中的除数不均匀地划分PRNG的范围,它就会始终引入偏差。运算符%使得以这种方式产生的概率分布倾斜,因为作为rand()的最大值的RAND_MAX可能不等于k * 3 + 2.如果除数不均匀地划分范围,则分布将偏斜并且偏差增加与除数。您可以阅读here更多内容。总结一下:在C ++中你应该使用<random>库:

#include <iostream>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937 gen( rd());
    std::uniform_int_distribution<> dis( 0, 2);

    for ( int n = 0; n < 25; ++n)
        std::cout << dis(gen) << ' ';
    std::cout << '\n';
}

答案 3 :(得分:1)

取%3不依赖于低阶位。

我运行下面的程序使用VC ++模拟运行OP的程序一千万次,在调用之间一秒钟。它没有偏见。

start = 1413167398
(0, 0) 1110545
(0, 1) 1111285
(0, 2) 1111611
(1, 0) 1111317
(1, 1) 1111666
(1, 2) 1110451
(2, 0) 1111580
(2, 1) 1110491
(2, 2) 1111054

#include <cstdlib>
#include <ctime>
#include <iostream>
#include <map>
#include <utility>

int main()
{
    std::map<std::pair<int, int>, int> counter;

    unsigned int start = static_cast<unsigned int>(std::time(nullptr));
    std::cout << "start = " << start << std::endl;
    unsigned int finish = start + 10000000;
    for (unsigned int seed = start; seed != finish; ++seed)
    {
        std::srand(seed);
        int x = rand() % 3;
        int y = rand() % 3;

        ++counter[std::make_pair(x, y)];
    }

    for (auto iter = counter.cbegin(); iter != counter.cend(); ++iter)
    {
        std::cout << "(" << iter->first.first << ", " << iter->first.second << ") ";
        std::cout << iter->second << std::endl;
    }

    return 0;
}