更新时间:作者:小小条
学编程为什么一定要学随机数?
游戏中爆金币爆装备需要随机数,用户登录验证码需要随机数,各种抽奖活动也需要随机数。可以说,随机数造成了程序功能的不确定性,这正是很多有趣功能的核心所在。
今天我们就来彻底搞懂C语言和C++中的随机数使用,从基础概念到实战应用,给你一条龙服务。

学生:"老师,什么是随机数啊?感觉很神秘的样子。"
老师:"其实很简单。编程中的随机数就是调用某些函数后,每次得到一个不确定的整数。就好比我让你在1到100之间随便喊一个数,你每次喊的都不一样。"
学生:"那计算机怎么做到'随便'的呢?"
老师:"这就涉及到一个重要概念了——伪随机。计算机其实无法产生真正的随机数,它只能通过数学算法模拟随机性。"
让我们先看一个简单的C语言例子:
#include <stdio.h>#include <stdlib.h>int main() { // 打印5个随机数 for(int i = 0; i < 5; i++) { printf("%d ", rand()); } printf("\n"); return 0;}
重要发现:多次运行这个程序,你会发现每次输出的数字序列都完全相同!
第一次运行:41 18467 6334 26500 19169第二次运行:41 18467 6334 26500 19169第三次运行:41 18467 6334 26500 19169
这就是伪随机的特点:看起来随机,但实际上是可预测的。
rand()函数的随机序列由一个叫做"随机种子"的初始值决定:
#include <stdio.h>#include <stdlib.h>int main() { // 设置种子为1 srand(1); printf("种子=1: "); for(int i = 0; i < 3; i++) { printf("%d ", rand()); } printf("\n"); // 设置种子为2 srand(2); printf("种子=2: "); for(int i = 0; i < 3; i++) { printf("%d ", rand()); } printf("\n"); return 0;}
输出结果:
种子=1: 1804289383 846930886 1681692777种子=2: 1505335290 1738766719 190686788
关键理解:相同的种子产生相同的随机序列,不同的种子产生不同的随机序列。
要让每次运行程序都产生不同的随机序列,我们需要用时间作为种子:
#include <stdio.h>#include <stdlib.h>#include <time.h>int main() { // 用当前时间作为随机种子 srand(time(NULL)); printf("真随机序列: "); for(int i = 0; i < 5; i++) { printf("%d ", rand()); } printf("\n"); return 0;}
**time(NULL)**返回当前时间戳(从1970年1月1日到现在的秒数),这个值每秒都在变化,所以可以作为很好的随机种子。
#include <stdio.h>#include <stdlib.h>#include <time.h>void generateCode() { char code[7]; // 6位验证码 + 结束符 srand(time(NULL)); for(int i = 0; i < 6; i++) { code[i] = rand() % 10 + '0'; // 生成0-9的数字字符 } code[6] = '\0'; // 字符串结束符 printf("验证码: %s\n", code);}int main() { generateCode(); return 0;}
代码解析:
rand() % 10:得到0-9的随机数+ '0':转换为对应的字符(ASCII码转换)#include <stdio.h>#include <stdlib.h>#include <time.h>// 判断某个概率是否发生int checkProbability(float percent) { int threshold = (int)(percent * 100); // 转换为整数 int randomNum = rand() % 10000; // 0-9999的随机数 return randomNum < threshold ? 1 : 0;}int main() { srand(time(NULL)); float dropRate = 12.5; // 12.5%的爆率 printf("打怪10次,看看能爆几次装备:\n"); int dropCount = 0; for(int i = 1; i <= 10; i++) { if(checkProbability(dropRate)) { printf("第%d次:爆了!获得大宝剑!\n", i); dropCount++; } else { printf("第%d次:什么都没有...\n", i); } } printf("总共爆了%d次装备\n", dropCount); return 0;}
#include <stdio.h>#include <stdlib.h>#include <time.h>int main() { srand(time(NULL)); char* tasks[] = { "今天你洗碗", "今天你拖地", "今天你倒垃圾", "今天你买饭" }; int taskCount = sizeof(tasks) / sizeof(tasks[0]); int randomIndex = rand() % taskCount; printf("抽签结果:%s\n", tasks[randomIndex]); // 想作弊?改成这样: // printf("抽签结果:%s\n", tasks[0]); // 总是第一个 return 0;}
C++11引入了更强大的随机数库:
#include <iostream>#include <random>int main() { // 创建随机数生成器 std::random_device rd; // 硬件随机数种子 std::mt19937 gen(rd()); // 梅森旋转算法 std::uniform_int_distribution<> dis(1, 100); // 1-100均匀分布 std::cout << "C++现代随机数:"; for(int i = 0; i < 5; i++) { std::cout << dis(gen) << " "; } std::cout << std::endl; return 0;}
优势对比:
特性 | C语言rand() | C++现代随机数 |
随机质量 | 一般 | 优秀 |
分布控制 | 手动取余 | 多种分布类型 |
线程安全 | 否 | 是 |
可重现性 | 通过srand | 通过种子构造 |
随机数生成过程:种子值 ──→ [算法处理] ──→ 随机序列 ↓时间戳1640995200 ──→ [梅森旋转] ──→ 41, 18467, 6334...内存布局:┌─────────────┐│ 种子: 4字节 │ ← srand()设置├─────────────┤│ 状态: 624字节│ ← 算法内部状态├─────────────┤ │ 输出: 4字节 │ ← rand()返回值└─────────────┘
// 错误做法int main() { for(int i = 0; i < 5; i++) { printf("%d ", rand()); // 每次运行结果相同 } return 0;}// 正确做法int main() { srand(time(NULL)); // 设置随机种子 for(int i = 0; i < 5; i++) { printf("%d ", rand()); } return 0;}
// 错误做法for(int i = 0; i < 5; i++) { srand(time(NULL)); // 每次循环都设置种子 printf("%d ", rand());}// 正确做法srand(time(NULL)); // 只在程序开始时设置一次for(int i = 0; i < 5; i++) { printf("%d ", rand());}
// 想要1-6的随机数// 错误做法int dice = rand() % 6; // 结果:0-5// 正确做法 int dice = rand() % 6 + 1; // 结果:1-6
#include <chrono>#include <iostream>void testPerformance() { const int COUNT = 1000000; // 测试C语言rand() auto start = std::chrono::high_resolution_clock::now(); for(int i = 0; i < COUNT; i++) { rand(); } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start); std::cout << "C rand(): " << duration.count() << " 微秒\n"; // 测试C++随机数 std::mt19937 gen; start = std::chrono::high_resolution_clock::now(); for(int i = 0; i < COUNT; i++) { gen(); } end = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start); std::cout << "C++ mt19937: " << duration.count() << " 微秒\n";}
"种子决定序列,时间保证随机,取余控制范围,一次设置终生受用。"
下一篇预告:《文件操作详解:让程序读写天下文件》
本文是C++大白话系列第40篇,专注用最简单的语言讲解最核心的概念。
版权声明:本文转载于今日头条,版权归作者所有,如果侵权,请联系本站编辑删除