srand并没有帮我创建随机数

时间:2015-12-12 14:39:48

标签: c++ random srand

我试图在牌组中洗牌。我随机创建两个数字并将它们的值分配给彼此100次。但它不是随机创造的。最奇怪的部分就是这个,我把断点放到了行#&c; ++;"观察程序是否每次都选择不同的卡片。当我调试时,如果我太快点击“继续”按钮,它每次都会创建相同的index1和相同的index2(但index1不等于index2)。我以为可能是因为时间问题。 (当我在继续按钮上点击太快或者我根本没有设置断点时,它会因同一时间而创建相同的数字。) 但如果是这样的话,为什么程序会为index1和index2创建不同的值?他们的线之间没有断点。所以时间必须非常小。它创建了不同的索引。我很困惑。如何摆脱?

编辑:解决方案是在main中使用srand一次。

void mix(string *a){
        srand(time(NULL));
        if (finisher == 100){
            return;
        }

        string b;
        int pos1;
        pos1 = rand() % 52;
        b = a[pos1];
        int pos2;
        pos2 = rand() % 52;
        cout << a[pos1] << "  " << a[pos2] << endl; //These lines are only for testing 
        a[pos1] = a[pos2]; 
        a[pos2] = b; 
        cout << a[pos1] << "  " << a[pos2] << endl; //These lines are only for testing 
        counter++;
        srand(time(NULL));
        mix(a);

    }

4 个答案:

答案 0 :(得分:4)

如果您使用srand设置特定种子,rand将生成某个伪随机整数序列
来自man rand

  

srand()函数将其参数设置为新序列的种子   由rand()返回的伪随机整数。 这些序列   可以通过使用相同的种子值调用 srand() 来重复。

time(NULL)每秒只会更改一次,因此您会遇到这样的行为 此外,正如@LightnessRacesinOrbit在某些评论中提到的那样,您应该只在一次时调用srand

从C ++ 11开始,您拥有<random>标头。 Use that instead.

答案 1 :(得分:3)

根据http://en.cppreference.com/w/cpp/numeric/random/srand

  

备注:

     

一般来说,伪随机数生成器只能在调用rand()和程序启动之前播种一次。每次希望生成一批新的伪随机数时,不应重复播种或重新播种。

     

标准做法是使用对时间(0)的调用结果作为种子。但是,time()返回time_t值,并且time_t不保证是整数类型。但实际上,每个主要实现都将time_t定义为一个整数类型,这也是POSIX所需要的。

顺便说一下,为什么不使用C ++ 11随机数生成器生成随机整数值i,均匀分布在闭区间[a,b] ,即,根据离散概率函数分布。见http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution

提供的示例代码是cppreference页面。

#include <random>
#include <iostream>

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

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

答案 2 :(得分:2)

您可能遇到调用srand的问题。 您的程序在一秒钟内多次运行shuffle_cards函数。 这意味着您在srand(time(NULL));中为srand提供的时间戳在多次调用中是相同的。

  

使用相同种子的两个不同初始化将在随后的rand调用中生成相同的连续结果。

因此,rand()调用的两次都将生成与shuffle_cards函数的第一次调用中相同的index1和index2数字。

解决方案是不要调用srand或调用一次,例如程序启动时,或者不经常调用它。

答案 3 :(得分:1)

正如其他人告诉你的那样,部分问题是对srand的所有调用。只打电话一次。

这不是你问的问题,但代码并不是改组牌组的好方法。首先,shuffle应该用循环编写而不是自己调用:

void shuffle(string* a) {
    for (int i = 0; i < 100; ++i) {
        int index1 = std::rand() % 52;
        int index2 = std::rand() % 52;
        string temp = a[index1];
        a[index1] = a[index2];
        a[index2] = temp;
    }
}

请注意,此代码使用初始化,而不是定义未初始化的变量,然后为其指定值。此外,我重新安排了顺序,以便更清楚地发生了什么。

但这仍然不是洗牌的好方法;它通常会使大部分甲板保持不变。这是一个很好理解的问题。阅读Fisher-Yates shuffle

void shuffle(string* a) {
    for (int i = 0; i < 51; ++i) {
        int idx = std::rand() % (52 - i) + i;
        string temp = a[idx];
        a[idx] = a[i];
        a[i] = temp;
    }
}

这可以通过使用标准库的swap函数来改进:

void shuffle(string* a) {
    for (int i = 0; i < 51; ++i) {
        int idx = std::rand() % (52 - i) + i;
        std::swap(a[i], a[idx];
    }
}

而且,除非你把它作为一个学习练习,否则你可以摆脱所有代码:

void shuffle(string* a) {
    std::random_shuffle(a, a + 52);
}

请注意,我假设string这里有char或某些类似char的类型,而不是std::string,这些代码都不会合理地工作。