c语言随机数怎么生成(通俗易懂,附实例)
在 C 语言里,所谓随机数其实是“伪随机数”。计算机根据一个种子值,通过固定公式推导出看起来杂乱的数字序列。只要种子相同,每次运行得到的序列完全一致。这种确定性对调试有利,却让初学者误以为结果真随机。
理解“伪随机”本质后,才能正确运用随机数函数。
伪随机数的可重现性演示:
#include <stdio.h> #include <stdlib.h> int main(void) { /* 不设置种子,默认种子为 1 */ for (int i = 0; i < 5; ++i) printf("%d ", rand()); /* 多次运行结果完全相同 */ return 0; }
运行结果(任意机器):
41 18467 6334 26500 19169
rand()与srand()的基本用法
stdlib.h 提供两个核心函数:rand() 负责生成 0~RAND_MAX 之间的整数;srand(unsigned seed) 负责设置种子。若希望每次运行结果不同,必须把“时刻在变的值”设为种子,常用 time(NULL) 返回的秒数。注意 srand() 在整个程序中只需调用一次,重复调用反而可能削弱随机性。
设置时间种子并生成 10 个随机数:
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { srand((unsigned)time(NULL)); /* 只需调用一次 */ for (int i = 0; i < 10; ++i) printf("%d ", rand()); return 0; } /* 某次运行结果: 31529 15850 13549 21351 29358 30434 28276 21666 25608 26947 */
把随机数映射到任意区间
rand() 返回范围是 0~RAND_MAX,通常需要转换到更实用的区间 [min, max]。通用公式:rand() % (max - min + 1) + min。取模运算简单直观,但低位随机性可能不足;若对质量要求高,可用 (int)((double)rand() / RAND_MAX * (max - min + 1)) + min 来利用高位。
生成 [10, 99] 区间的随机两位数:#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { srand((unsigned)time(NULL)); for (int i = 0; i < 8; ++i) { int n = rand() % 90 + 10; /* 90 = 99-10+1 */ printf("%d ", n); } return 0; } /* 某次运行结果: 63 57 18 90 44 81 76 35 */
生成浮点随机数
游戏或仿真常需要 0.0~1.0 之间的浮点随机数。最简单写法:double r = (double)rand() / RAND_MAX; 若需区间 [a, b],再线性变换:a + r * (b - a)。注意 RAND_MAX 通常是整数上限,强制转 double 可避免整型除法精度损失。
输出 5 个 [0, 1) 的浮点随机数#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { srand((unsigned)time(NULL)); for (int i = 0; i < 5; ++i) { double r = (double)rand() / RAND_MAX; printf("%.6f ", r); } return 0; } /* 某次运行结果: 0.534711 0.902348 0.198074 0.956483 0.003011 */
随机数质量与可移植性
rand() 的实现依赖编译器,RAND_MAX 最小值仅 32767,周期也较短。若需加密或高质量仿真,应改用 POSIX 的 random() 函数或 C11 的 <stdlib.h> 新接口 rand_s()(Windows)/ arc4random()(BSD)。
另外,C++ 的 <random> 提供梅森旋转算法,跨平台且周期长,是现代 C 程序可以借鉴的方案。
使用 random() 提升随机质量(GNU/Linux):#define _DEFAULT_SOURCE #include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { srandom(time(NULL)); for (int i = 0; i < 5; ++i) printf("%ld ", random()); return 0; } /* 某次运行结果: 1234567890 987654321 555555555 111111111 202312345 */
常见误区与调试技巧
1. 每次 rand() 前都 srand(time(NULL))——时间精度为秒,导致连续调用得到相同种子。
2. 用 rand() % 10000 生成验证码却忽略 RAND_MAX 只有 32767,造成某些数字概率偏高。
3. 在多线程中共享 rand() 状态出现竞争。
解决:主线程只调用一次 srand();或改用线程安全的 erand48();或给每个线程独立种子。
【线程安全示例】独立种子:#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <time.h> #define THREADS 2 void* work(void* arg) { unsigned seed = time(NULL) + *(int*)arg; /* 各线程种子不同 */ for (int i = 0; i < 3; ++i) printf("T%d: %d\n", *(int*)arg, rand_r(&seed)); return NULL; } int main(void) { pthread_t t[THREADS]; int id[THREADS] = {1, 2}; for (int i = 0; i < THREADS; ++i) pthread_create(&t[i], NULL, work, &id[i]); for (int i = 0; i < THREADS; ++i) pthread_join(t[i], NULL); return 0; } /* 某次运行结果: T1: 1481765933 T1: 1020355381 T1: 1457077689 T2: 1805894523 T2: 1209059285 T2: 957064183 */
完整示例:猜数字小游戏
综合本节知识,写一段“猜 1~100 整数”的极简游戏。程序自动产生答案,用户键盘输入猜测,程序提示大了或小了,直到猜中。代码展示随机数生成、区间映射、输入输出与循环控制,适合初学者一次看懂。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { srand((unsigned)time(NULL)); int goal = rand() % 100 + 1; /* 1~100 */ int guess, cnt = 0; printf("Guess the number (1-100):\n"); do { printf("Your guess: "); scanf("%d", &guess); ++cnt; if (guess < goal) puts("Too small!"); else if (guess > goal) puts("Too big!"); else printf("Bingo! %d attempts.\n", cnt); } while (guess != goal); return 0; } /* 运行交互示例: Guess the number (1-100): Your guess: 50 Too big! Your guess: 25 Too small! Your guess: 37 Bingo! 3 attempts. */

浙公网安备 33010602011771号