C语言生成不重复随机数的两种常用方法
在C语言开发中,生成唯一性随机数据(如抽奖、测试用例)是常见需求。本文将讲解如何利用标准库中的 srand 和 rand 函数,结合种子值与算法逻辑,实现1至N范围内不重复随机数的生成,涵盖基础原理及两种实用的代码实现方式。
理解C语言的随机数机制
C语言标准库提供的 rand() 函数并非真随机数,而是伪随机数,它依据一个内部的“种子”数值,通过递推公式计算出一系列看起来随机的数列。如果种子固定,生成的数列也将完全一致。
要实现真正的随机性,必须在调用 rand() 前使用 srand(unsigned int seed) 设置种子。通常采用当前时间作为种子,以确保程序每次运行时种子值不同:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void) {
// 以当前时间为种子,保证每次运行结果不同
srand((unsigned)time(NULL));
// 生成0到RAND_MAX之间的随机整数
int random_num = rand();
return 0;
}
第1步:获取指定范围的随机数
直接使用 rand() 得到的范围过大且从0开始。若需要生成 X 到 Y 之间(包含X和Y)的随机数,可使用取模运算转换:
公式:k = rand() % (Y - X + 1) + X;
- 目的:将巨大的随机范围压缩并平移至目标区间。
- 操作:对
(Y-X+1)取模得到 0 到 (Y-X) 的值,再加 X 偏移量。 - 示例:生成 1-100 的随机数为
rand() % 100 + 1。
生成不重复随机数的核心难点
直接生成随机数并检查是否已存在,在数据量接近总数时效率极低(“猴子抛硬币”效应)。下面介绍两种高效或直观的实现思路。
方法一:标记数组法(适合小范围密集数据)
创建一个布尔型或整型数组作为“哈希表”,记录每个数字是否已被选中。循环直到凑齐所需数量,每生成一个数都检查标记。
- 准备数组:初始化一个大小为
N+1的数组,全部置为0(表示未使用)。 - 生成与检查:生成目标范围内的随机数,若数组对应位置为0,则存入结果并标记为1;若为1,则重新生成。
- 终止条件:当收集的随机数达到预期数量时停止。
方法二:数组洗牌法(Fisher-Yates Shuffle,推荐)
此方法先生成一个包含所有可能值的有序数组,然后通过随机交换元素位置打乱顺序。取出前 N 个即为不重复随机序列,时间复杂度稳定为 O(N)。
复制代码 代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 交换两个整数的指针
void swap(int *pm, int *pn) {
int temp = *pm;
*pm = *pn;
*pn = temp;
}
int main(void) {
int a[513]; // 用于存储1-512
int i;
srand((unsigned)time(NULL));
// 1. 初始化数组,填入顺序数值 1-512
for(i = 1; i <= 512; i++) {
a[i] = i;
}
// 2. 洗牌算法:从后往前,将当前位置与[1,当前位]之间的随机位置交换
for(i = 512; i >= 1; i--) {
// rand() % i + 1 生成 1 到 i 的随机索引
swap(&a[i], &a[rand() % i + 1]);
}
// 3. 输出前10个作为不重复随机数示例
for(i = 1; i <= 10; i++) {
printf("%4d", a[i]);
}
printf("\n");
return 0;
}
注意事项与排错
- 头文件依赖:必须包含
<stdlib.h>和<time.h>。 - 种子设置:如果在同一秒内多次快速调用程序,未使用
srand(time(NULL))可能导致相同的随机序列。 - 非ANSI函数:避免使用
random()等非标准函数,它在GCC等现代编译器中不可用,应统一使用rand()。 - 内存管理:对于极大的范围(如1-10000000),一次性创建数组可能消耗过多内存,此时可考虑使用位图或外部存储来辅助去重判断。
以上就是C语言生成不重复随机数的详细内容,更多关于C语言随机数算法的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













