C++随机数 rand
中等30让程序变得不可预测:C++随机数rand的详细讲解
为什么我们需要随机数?
你有没有玩过猜拳游戏、掷骰子、或者抽奖抓娃娃?每次结果都不一样,这种“不确定”的感觉让游戏变得有趣又有期待。在编程里,我们也经常需要这种“不确定”——比如做一个猜数字游戏,让电脑随机想一个数,请你来猜;或者做一个抽奖程序,随机从全班名单里抽一个人;又或者让电子游戏里的怪物随机掉装备。这些场景的核心就是 随机数。
C++ 提供给我们的 rand 函数,就是用来生成这种“看起来随机”的数字的。虽然它实际上是“伪随机数”(通过一定算法算出来的,不是真正的随机),但对于我们平时做游戏、练习题已经够用了。
认识 rand 函数
rand 函数生活在 <cstdlib> 头文件里。每次调用它,它就会返回一个 0 到 RAND_MAX 之间的整数。RAND_MAX 通常是一个比较大的数,在多数编译器里是 32767。
#include <iostream>
#include <cstdlib> // 一定要包含这个头文件
using namespace std;
int main() {
int num = rand(); // num 会得到一个 0~32767 之间的整数
cout << "随机数: " << num << endl;
return 0;
}
但是这段代码有一个问题:每次运行程序,产生的随机数序列都是相同的。比如第一次运行输出 41,第二次运行还是 41,第三次还是同一个序列……这是因为 rand 背后有一个“默认种子”,每次程序启动时种子都一样。想让它每次不同,就要用到 srand 来设置种子。
给随机数“种”一个种子 —— srand
srand 函数负责给随机数生成器设置一个起始值,叫“种子”。种子不同,后面 rand 生成的序列就不同。最常用的种子就是 当前的时间(从1970年1月1日到现在的秒数),因为时间每秒钟都在变化,所以每次运行种子都不一样。
#include <iostream>
#include <cstdlib>
#include <ctime> // 为了使用 time 函数
using namespace std;
int main() {
srand(time(0)); // 用当前时间作为种子,只调用一次
int num = rand();
cout << "随机数: " << num << endl;
return 0;
}
重要提醒:srand 在整个程序里只调用一次就够了,通常放在 main 的开头。如果在循环里反复调用,反而会让随机数质量变差。
生成你想要的数字范围
rand() 直接返回 0~32767,但我们经常需要特定范围,比如:
- 骰子点数:1~6
- 考试成绩模拟:0~100
- 抽奖编号:1~50
- 百分制分数:0~100
怎么实现呢?用 取模运算符 % 配合 加法偏移。
公式:rand() % 范围大小 + 起始值
- 生成 a 到 b 之间的整数(包括 a 和 b):
rand() % (b - a + 1) + a
来看几个生活例子:
例1:掷骰子(1~6)
int dice = rand() % 6 + 1; // 范围大小 = 6,起始值 = 1
例2:抽奖编号(50~100)
int draw = rand() % 51 + 50; // 范围大小 = 100-50+1 = 51,起始值 = 50
例3:模拟考试分数(0~100)
int score = rand() % 101; // 范围 0~100,起始值 = 0,所以 +0 可以省略
例4:随机小数(0.0~1.0)
有时你需要小数,比如游戏里的掉落率。可以把 rand() 的结果强制转换成 double,再除以 RAND_MAX:
double ratio = (double)rand() / RAND_MAX; // 结果在 0.0 到 1.0 之间
常见错误(新手最容易犯的坑)
错误1:忘记调用 srand
如果不调用 srand,每次运行程序产生的随机数都一样,游戏就变成“猜固定数字”了,毫无乐趣。
// 错误示例:没有 srand
int main() {
for (int i = 0; i < 3; i++) {
cout << rand() % 10 << " "; // 每次运行都是同样的三个数
}
return 0;
}
错误2:在循环里多次调用 srand
有人想在每次取随机数前都 srand(time(0))。但 time 每秒才变化一次,循环执行很快,导致同一秒内多次调用得到的种子相同,结果可能都一样。
// 错误示例:循环内反复设置种子
for (int i = 0; i < 5; i++) {
srand(time(0)); // 同一秒内种子相同
cout << rand() % 10 << " "; // 输出可能全是同一个数字
}
正确做法:srand 只放在程序开头一次。
错误3:范围计算错误
- 想生成 1
6,写成5,少了 6。rand() % 6会得到 0 - 想生成 10
20,写成19(因为rand() % 10 + 10会得到 10%10范围 0~9),漏了 20。正确是rand() % 11 + 10。
记住公式:rand() % (最大值 - 最小值 + 1) + 最小值
错误4:忘记包含头文件
rand 和 srand 需要 <cstdlib>,time 需要 <ctime>。漏掉一个就会编译报错。
完整示例:猜数字游戏
下面是一个完整的猜数字游戏,电脑随机想一个 1~100 之间的数字,你来猜,电脑会提示“大了”或“小了”,直到猜对为止。
#include <iostream>
#include <cstdlib> // 引入 rand 和 srand
#include <ctime> // 引入 time
using namespace std;
int main() {
// 用当前时间作为随机种子
srand(time(0));
int secret = rand() % 100 + 1; // 秘密数字:1~100
int guess = 0; // 用户猜的数字,先初始化为0
int attempts = 0; // 尝试次数
cout << "我已经想好了一个1~100之间的数字,你猜猜看!" << endl;
// 循环,直到猜对
while (guess != secret) {
cout << "请输入你的猜测:";
cin >> guess;
attempts++; // 每猜一次加1
if (guess > secret) {
cout << "猜大了,再小一点。" << endl;
} else if (guess < secret) {
cout << "猜小了,再大一点。" << endl;
} else {
cout << "恭喜你!猜对了!就是 " << secret << endl;
cout << "你一共猜了 " << attempts << " 次。" << endl;
}
}
return 0;
}
你可以把这个代码复制到你的编译器里运行,每次运行的秘密数字都不一样哦!
让随机数更好玩:其他生活应用
- 随机点名器:把全班同学的编号放到数组里,用
rand() % 总人数挑一个。 - 模拟掷两个骰子:两个
rand() % 6 + 1相加。 - 抽奖程序:先设定中奖概率,比如中奖概率 10%,可以用小数判断
if ((double)rand()/RAND_MAX < 0.1)。
相关指引
rand 函数虽然方便,但它有一些缺点:
- 随机数质量不够“随机”(有周期,比如每过一段时间会重复)
- 对某些高级应用(如密码、复杂模拟)不够安全
如果你以后深入学习,还可以了解 C++11 标准里提供的 <random> 库,里面包含更强大的随机数生成器(比如 mt19937)和更灵活的概率分布。但在 GESP 二级考试中,rand + srand 完全足够,考试也只会考到这里。
现在你已经掌握了随机数的基本用法,快去试着给程序添加一点“不确定性”吧!比如做一个石头剪刀布的游戏,或者一个随机出数学题的口算练习器。编程本来就很有趣,加上随机数就更有意思了。
例题精讲
在C++中,使用rand()函数生成随机数时,该函数返回的整数范围是?
若想生成[10, 20]闭区间内的随机整数(包括10和20),正确的表达式是?
如果不调用srand()函数设置随机数种子,每次运行程序,rand()函数生成的随机数序列都是相同的。
在C++中使用rand()函数前,必须包含头文件<random>。
以下程序试图生成一个1到6之间的随机整数(模拟骰子),请补全括号内的代码。
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
srand(time(0));
int dice = ___;
cout << dice;
return 0;
}