C 或 C++ 中,rand() 函数生成的是伪随机数,但每次运行程序时,它通常会产生相同的随机数序列。这是因为随机数生成器使用了一个初始值,称为 种子,这个种子决定了随机数的序列。默认情况下,rand() 使用相同的种子值,每次程序启动时都会使用这个固定的种子(通常是 1 或 0)。因此,每次运行程序时,rand() 生成的随机数序列都是相同的。没有变化的种子,随机序列就会变得可预测。

1、使用 srand() 设置种子

要解决这个问题,你需要为每次程序运行提供一个 不同的种子。可以通过 srand() 函数设置随机数生成器的种子。常见的做法是使用 当前时间 作为种子,因为它每次都不同。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    // 根据当前时间设置种子
    srand(time(NULL));

    // 生成并打印随机数
    for (int i = 0; i < 5; i++) {
        printf("%d\n", rand());
    }

    return 0;
}

2、使用 rand() 生成随机数并模运算

可以通过模运算来限制生成的随机数范围,并确保每次运行时,种子不同。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    // 设置种子
    srand(time(NULL));

    // 生成范围在 0 到 99 之间的随机数
    printf("%d\n", rand() % 100);
    
    return 0;
}

3、使用更好的随机数生成器(random())

在一些系统中,random() 函数比 rand() 更加高效和随机性更好。它是一个更为先进的随机数生成器,且不依赖于种子(在多数情况下会自动使用系统时间等变动因素)。

#include <stdio.h>
#include <stdlib.h>

int main() {
    // 生成随机数
    printf("%ld\n", random());
    
    return 0;
}

4、使用更复杂的时间戳

如希望种子值更加不可预测,可以将更精细的时间信息作为种子,例如使用毫秒级别的时间戳。

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

int main() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    srand((unsigned int)(tv.tv_sec * 1000 + tv.tv_usec / 1000));

    printf("Random number: %d\n", rand());
    return 0;
}

5、使用 C11 标准库的 rand() 替代方法(random())

在一些支持 C11 标准的编译器中,可以使用 random() 函数代替 rand()random() 提供了比 rand() 更好的随机性,但仍然依赖于种子。可以通过 srandom() 设置种子。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    // 设置种子
    srandom((unsigned int)time(NULL));

    // 生成随机数
    printf("Random number: %ld\n", random());
    return 0;
}

6、使用 rand() 自定义生成器

可以通过自定义的伪随机数生成算法来替代 rand()srand()。例如,可以使用线性同余生成器(LCG)来生成随机数序列。

#include <stdio.h>

unsigned int my_rand(unsigned int *seed) {
    // 线性同余生成器(LCG)参数
    *seed = *seed * 1664525 + 1013904223;
    return (*seed) >> 16;
}

int main() {
    unsigned int seed = 12345; // 初始种子值

    // 生成随机数
    printf("Random number: %u\n", my_rand(&seed));
    return 0;
}

推荐文档