srand 不能帮助我创建随机数

srand doesn't help me creating random numbers

我正在尝试洗牌。我随机创建两个数字并将它们的值分配给彼此 100 次。但它不是随机创建的。最奇怪的是,我在行 "counter++;" 下了一个断点来观察程序是否真的每次都选择了不同的卡片。当我调试时,如果我太快地点击继续按钮,它每次都会创建相同的 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);

    }

如果您使用 srand 设置特定的种子,rand 将生成特定的 pseudo-random 整数序列.
来自 man rand:

The srand() function sets its argument as the seed for a new sequence of pseudo-random integers to be returned by rand(). These sequences are repeatable by calling srand() with the same seed value.

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

自 C++11 起,您拥有 <random> header。 Use that instead.

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

Two different initializations with the same seed will generate the same succession of results in subsequent calls to rand.

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

解决方法是不调用srand或者只调用一次,比如程序启动的时候,或者少调用。

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

Notes:

Generally speaking, the pseudo-random number generator should only be seeded once, before any calls to rand(), and the start of the program. It should not be repeatedly seeded, or reseeded every time you wish to generate a new batch of pseudo-random numbers.

Standard practice is to use the result of a call to time(0) as the seed. However, time() returns a time_t value, and time_t is not guaranteed to be an integral type. In practice, though, every major implementation defines time_t to be an integral type, and this is also what POSIX requires.

顺便问一下,你为什么不使用 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';
}

正如其他人告诉您的那样,部分问题在于对 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 的一些时髦的 typedef,而不是 std::string,其中 none此代码可以正常工作。