CC++ & Algorithm

C++随机数 rand

中等30
语言版本:C++Python
概述:像抽奖摇骰子一样,rand函数能帮我们在程序里产生随机数字。

让程序变得不可预测: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:范围计算错误

  • 想生成 16,写成 rand() % 6 会得到 05,少了 6。
  • 想生成 1020,写成 rand() % 10 + 10 会得到 1019(因为 %10 范围 0~9),漏了 20。正确是 rand() % 11 + 10

记住公式rand() % (最大值 - 最小值 + 1) + 最小值

错误4:忘记包含头文件

randsrand 需要 <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 完全足够,考试也只会考到这里。

现在你已经掌握了随机数的基本用法,快去试着给程序添加一点“不确定性”吧!比如做一个石头剪刀布的游戏,或者一个随机出数学题的口算练习器。编程本来就很有趣,加上随机数就更有意思了。

例题精讲

1单选题

在C++中,使用rand()函数生成随机数时,该函数返回的整数范围是?

A0 到 RAND_MAX (含两端)
B1 到 RAND_MAX (含两端)
C0 到 32767 (含两端)
D-32768 到 32767 (含两端)
2单选题

若想生成[10, 20]闭区间内的随机整数(包括10和20),正确的表达式是?

Arand() % 11 + 10
Brand() % 10 + 10
Crand() % 11 + 11
Drand() % 10 + 11
3判断题

如果不调用srand()函数设置随机数种子,每次运行程序,rand()函数生成的随机数序列都是相同的。

4判断题

在C++中使用rand()函数前,必须包含头文件<random>。

5填空题
以下程序试图生成一个1到6之间的随机整数(模拟骰子),请补全括号内的代码。

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
    srand(time(0));
    int dice = ___;
    cout << dice;
    return 0;
}