SRAND 不帮助我创建随机数

srand doesn't help me creating random numbers

本文关键字:创建 随机数 帮助 SRAND      更新时间:2023-10-16

我正在试着洗牌。我随机创建两个数字,并将它们的值相互分配100次。但它并不是随机产生的。最奇怪的是,我在"counter++;"行放了一个断点,观察程序是否真的每次都选择不同的卡。当我调试时,如果我点击继续按钮太快,它每次都会创建相同的index1和index2(但index1不等于index2)。我想可能是时间原因。(当我点击继续按钮的速度太快,或者根本没有设置断点时,由于时间相同,它会创建相同的数字。)但如果是这样的话,为什么程序会为index1和index2创建不同的值?它们的行之间没有断点。所以时间一定很短。不过,它会创建不同的索引。我很困惑。如何摆脱?

编辑:解决方案主要是使用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);
    }

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

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

time(NULL)每秒只会改变一次,所以你会经历这样的行为
此外,正如@LightnessRacesinOrbit在一些评论中提到的那样,您应该只调用srand一次。

从C++11开始,就有了<random>标头改为使用它

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

注意:

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

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

顺便说一句,你为什么不使用C++11随机数生成器,它会产生均匀分布在闭区间[a,b]上的随机整数值i,,也就是说,根据离散概率函数分布。看见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';
}

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

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

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

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

正如其他人所说,问题的一部分是对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;
    }
}

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

但这仍然不是洗牌的好方法;它通常会使甲板的大部分保持不变。这是一个众所周知的问题。阅读费雪-耶茨洗牌:

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);
}

注意,我已经假设这里的stringchar或类似于char的东西的一些时髦的typedef,而不是std::string,这些代码都不能正常工作。