C语言:随机数

1. 随机数概述

在编程中,随机数有着广泛的应用:游戏开发、密码学、模拟实验、抽奖系统等。C语言提供了生成伪随机数的函数库。

1.1 什么是伪随机数?

计算机无法产生真正的随机数,而是通过算法生成看似随机的数列,称为伪随机数。给定相同的种子,每次生成的随机数序列是相同的。

专业术语:

· 随机数种子:生成随机数序列的起始值

· 伪随机数:由算法生成的、具有统计随机性的数列

2. 核心函数

C语言通过 <stdlib.h> 头文件提供随机数函数:

函数 作用 返回值

rand() 生成随机数 0 到 RAND_MAX 之间的整数

srand() 设置随机数种子 void

2.1 RAND_MAX 常量

RAND_MAX 是 <stdlib.h> 中定义的常量,表示 rand() 能返回的最大值。通常为 32767(2^15-1),但在不同平台可能不同。

复制代码
#include <stdio.h>

#include <stdlib.h>



int main() {

    printf("RAND_MAX = %d\n", RAND_MAX);

    return 0;

}

3. 基本使用方法

3.1 最简单的随机数

复制代码
#include <stdio.h>

#include <stdlib.h>



int main() {

    // 直接调用 rand()

    printf("随机数1: %d\n", rand());

    printf("随机数2: %d\n", rand());

    printf("随机数3: %d\n", rand());

    

    return 0;

}

问题:每次运行程序,生成的随机数序列完全相同!

3.2 设置随机数种子

复制代码
#include <stdio.h>

#include <stdlib.h>

#include <time.h> // time() 函数需要此头文件



int main() {

    // 使用当前时间作为种子

    srand((unsigned int)time(NULL));

    

    printf("随机数1: %d\n", rand());

    printf("随机数2: %d\n", rand());

    printf("随机数3: %d\n", rand());

    

    return 0;

}

关键点:

· time(NULL) 返回从1970年1月1日至今的秒数

· 每次运行程序,种子不同,因此随机数序列不同

· 通常只在程序开始时调用一次 srand()

4. 生成指定范围的随机数

4.1 生成 [0, n) 范围的随机数

复制代码
int n = 100;

int randomNum = rand() % n; // 范围:0 到 99

4.2 生成 [a, b] 范围的随机数

通用公式:

int randomNum = a + rand() % (b - a + 1);

示例:

复制代码
// 生成 1 到 100 的随机数

int num = 1 + rand() % 100;



// 生成 50 到 100 的随机数

int num = 50 + rand() % 51;



// 生成 -10 到 10 的随机数

int num = -10 + rand() % 21;

4.3 生成 [a, b] 范围的随机浮点数

复制代码
// 生成 0.0 到 1.0 的随机浮点数

double randomDouble = (double)rand() / RAND_MAX;



// 生成 a 到 b 的随机浮点数

double randomInRange = a + (double)rand() / RAND_MAX * (b - a);

5. 完整代码示例

5.1 基础示例

复制代码
#include <stdio.h>

#include <stdlib.h>

#include <time.h>



int main() {

    // 初始化随机数种子

    srand((unsigned int)time(NULL));

    

    printf("========== 随机数生成示例 ==========\n\n");

    

    // 1. 生成 0-99 的随机数

    printf("1. 0-99 范围的随机数:\n");

    for (int i = 0; i < 5; i++) {

        printf(" %d\n", rand() % 100);

    }

    

    // 2. 生成 1-100 的随机数

    printf("\n2. 1-100 范围的随机数:\n");

    for (int i = 0; i < 5; i++) {

        printf(" %d\n", 1 + rand() % 100);

    }

    

    // 3. 生成 0.0-1.0 的随机浮点数

    printf("\n3. 0.0-1.0 范围的随机浮点数:\n");

    for (int i = 0; i < 5; i++) {

        printf(" %.4f\n", (double)rand() / RAND_MAX);

    }

    

    // 4. 生成 10.0-20.0 的随机浮点数

    printf("\n4. 10.0-20.0 范围的随机浮点数:\n");

    for (int i = 0; i < 5; i++) {

        double num = 10.0 + (double)rand() / RAND_MAX * 10.0;

        printf(" %.2f\n", num);

    }

    

    return 0;

}

5.2 实用示例:抽奖程序

复制代码
#include <stdio.h>

#include <stdlib.h>

#include <time.h>



int main() {

    srand((unsigned int)time(NULL));

    

    int lotteryNum = 1 + rand() % 100; // 1-100 的中奖号码

    int guess;

    int attempts = 0;

    

    printf("===== 幸运抽奖游戏 =====\n");

    printf("猜一个 1-100 之间的数字:\n");

    

    do {

        printf("请输入你的猜测:");

        scanf("%d", &guess);

        attempts++;

        

        if (guess > lotteryNum) {

            printf("猜大了!\n");

        } else if (guess < lotteryNum) {

            printf("猜小了!\n");

        } else {

            printf("恭喜!你猜对了!\n");

            printf("总共猜测了 %d 次\n", attempts);

        }

    } while (guess != lotteryNum);

    

    return 0;

}

5.3 实用示例:随机密码生成器

复制代码
#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <string.h>



// 生成随机密码

void generatePassword(char *password, int length) {

    // 密码字符集:大写字母 + 小写字母 + 数字

    char charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

                     "abcdefghijklmnopqrstuvwxyz"

                     "0123456789";

    int charsetSize = strlen(charset);

    

    for (int i = 0; i < length; i++) {

        int index = rand() % charsetSize;

        password[i] = charset[index];

    }

    password[length] = '\0';

}



int main() {

    srand((unsigned int)time(NULL));

    

    int length;

    printf("请输入密码长度:");

    scanf("%d", &length);

    

    char password[length + 1];

    generatePassword(password, length);

    

    printf("生成的随机密码:%s\n", password);

    

    return 0;

}

5.4 实用示例:模拟掷骰子

复制代码
#include <stdio.h>

#include <stdlib.h>

#include <time.h>



// 掷一个骰子

int rollDie() {

    return 1 + rand() % 6;

}



// 掷两个骰子

void rollTwoDice() {

    int die1 = rollDie();

    int die2 = rollDie();

    printf("骰子1:%d 骰子2:%d 总和:%d\n", die1, die2, die1 + die2);

}



// 统计每个点数出现的次数

void diceStatistics(int rolls) {

    int count[7] = {0}; // 索引 1-6 使用

    

    for (int i = 0; i < rolls; i++) {

        int result = rollDie();

        count[result]++;

    }

    

    printf("\n%d 次掷骰子统计:\n", rolls);

    for (int i = 1; i <= 6; i++) {

        printf("点数 %d:%d 次 (%.2f%%)\n", 

               i, count[i], (double)count[i] / rolls * 100);

    }

}



int main() {

    srand((unsigned int)time(NULL));

    

    printf("===== 模拟掷骰子 =====\n\n");

    

    printf("单次掷骰子:%d\n", rollDie());

    printf("\n掷两个骰子:\n");

    rollTwoDice();

    

    printf("\n");

    diceStatistics(100);

    

    return 0;

}

6. 进阶知识

6.1 更好的随机数生成器

标准库的 rand() 存在一些局限性:

· 周期较短(通常为 2^31)

· 随机性不够好
更好的选择:

· C11 标准:rand_s()(Windows)

· POSIX:random()、srandom()

· Linux:/dev/urandom 设备文件

6.2 使用 random() 函数

复制代码
#define _XOPEN_SOURCE

#include <stdlib.h>



int main() {

    srandom((unsigned int)time(NULL));

    

    // random() 返回 0 到 2^31-1 的值

    long randomNum = random();

    

    // 生成 1-100 的随机数

    int num = 1 + random() % 100;

    

    return 0;

}

6.3 洗牌算法(Fisher-Yates)

复制代码
#include <stdio.h>

#include <stdlib.h>

#include <time.h>



// 随机交换数组中的元素(洗牌)

void shuffle(int arr[], int n) {

    for (int i = n - 1; i > 0; i--) {

        // 生成 0 到 i 的随机索引

        int j = rand() % (i + 1);

        

        // 交换 arr[i] 和 arr[j]

        int temp = arr[i];

        arr[i] = arr[j];

        arr[j] = temp;

    }

}



int main() {

    srand((unsigned int)time(NULL));

    

    int cards[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    int size = sizeof(cards) / sizeof(cards[0]);

    

    printf("原始顺序:");

    for (int i = 0; i < size; i++) {

        printf("%d ", cards[i]);

    }

    

    shuffle(cards, size);

    

    printf("\n洗牌后:");

    for (int i = 0; i < size; i++) {

        printf("%d ", cards[i]);

    }

    printf("\n");

    

    return 0;

}

7. 常见陷阱与注意事项

7.1 多次调用 srand()

复制代码
// ❌ 错误示例:在循环中调用 srand()

for (int i = 0; i < 5; i++) {

    srand((unsigned int)time(NULL)); // 错误!

    printf("%d ", rand() % 100);

}

// 可能输出:41 41 41 41 41(因为 time() 秒数未变)



// ✅ 正确示例:只调用一次

srand((unsigned int)time(NULL));

for (int i = 0; i < 5; i++) {

    printf("%d ", rand() % 100);

}

7.2 取模的均匀性问题

// 当 RAND_MAX+1 不能被 n 整除时,rand() % n 不是完全均匀的

// 例如 RAND_MAX=32767,n=10000,某些数字出现的概率略高

// 改进方法:拒绝采样
int getRandom(int n) {

int limit = RAND_MAX - (RAND_MAX + 1) % n;

int r;

do {

r = rand();

} while (r > limit);

return r % n;

}

7.3 随机浮点数的精度

// 方法1:直接除法(推荐)

double r1 = (double)rand() / RAND_MAX; // 范围 [0, 1]

// 方法2:避免整数除法

double r2 = rand() / (RAND_MAX + 1.0); // 范围 [0, 1)

8. 练习题

基础练习

  1. 生成 10 个 1-50 的随机数,并找出最大值和最小值

  2. 模拟掷硬币 1000 次,统计正面和反面的次数

  3. 生成 20 个 0-100 的随机分数,计算平均分

进阶练习

  1. 实现一个简单的彩票系统:从 1-35 中随机抽取 5 个不重复的数字

  2. 编写一个程序,模拟两个玩家掷骰子,点数大者获胜,统计胜率

  3. 实现一个随机数组排序(随机打乱)函数

挑战练习

  1. 实现蒙特卡洛方法计算圆周率 π

  2. 编写一个程序,生成随机验证码(包含大写字母、小写字母、数字)

  3. 实现一个简单的老虎机游戏

9. 蒙特卡洛方法计算 π

复制代码
#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <math.h>



double estimatePi(int points) {

    int inside = 0;

    

    for (int i = 0; i < points; i++) {

        double x = (double)rand() / RAND_MAX; // 0-1 的随机数

        double y = (double)rand() / RAND_MAX;

        

        // 检查点是否在单位圆内

        if (x * x + y * y <= 1.0) {

            inside++;

        }

    }

    

    // π ≈ 4 × (圆内点数 / 总点数)

    return 4.0 * inside / points;

}



int main() {

    srand((unsigned int)time(NULL));

    

    int points[] = {100, 1000, 10000, 100000, 1000000};

    

    printf("蒙特卡洛方法估算 π 值:\n");

    printf("点数\t\t估算值\t\t误差\n");

    printf("----------------------------------------\n");

    

    for (int i = 0; i < 5; i++) {

        double pi = estimatePi(points[i]);

        printf("%d\t\t%.6f\t%.6f\n", 

               points[i], pi, fabs(pi - 3.1415926535));

    }

    

    return 0;

}

10. 知识点总结

知识点 说明

rand() 生成伪随机数,范围 0 到 RAND_MAX

srand() 设置随机数种子,通常使用 time(NULL)

time(NULL) 获取当前时间戳(秒),作为种子

rand() % n 生成 0 到 n-1 的随机数

a + rand() % (b-a+1) 生成 a 到 b 的随机整数

(double)rand() / RAND_MAX 生成 0 到 1 的随机浮点数

种子只设置一次 多次调用 srand() 会导致随机性变差

11. 课后作业

  1. 抽奖程序:设计一个抽奖程序,从 10 个参与者中随机抽取 3 个中奖者(不重复)

  2. 随机点名器:编写一个程序,从班级名单(可硬编码)中随机点名

  3. 猜数字游戏增强版:添加难度选择(不同范围),记录最佳成绩

  4. 密码生成器增强版:允许用户自定义字符集(是否包含特殊符号等)

记住:随机数是许多有趣程序的基础。掌握了随机数的使用,你就能创造出各种游戏、模拟程序和实用工具。多动手实践,才能真正理解随机数的奥秘!

相关推荐
風清掦2 小时前
【江科大STM32学习笔记-09】USART串口协议 - 9.1 STM32 USART串口外设
笔记·stm32·单片机·嵌入式硬件·学习
fengfuyao9852 小时前
CH552多功能音量调节旋钮设计与实现
c语言·开发语言
xushichao19892 小时前
实时数据压缩库
开发语言·c++·算法
minji...2 小时前
Linux 文件系统 (三) 软连接和硬链接
linux·运维·服务器·c++·算法
liurunlin8882 小时前
Go环境搭建(vscode调试)
开发语言·vscode·golang
优化控制仿真模型2 小时前
【计算机二级MSoffice题库软件】小黑课堂下载安装教程(2026年3月最新版)
经验分享
故事和你912 小时前
sdut-python-实验四-python序列结构(21-27)
大数据·开发语言·数据结构·python·算法
memcpy02 小时前
LeetCode 1456. 定长子串中元音的最大数目【定长滑窗模板题】中等
算法·leetcode·职场和发展
SuperEugene2 小时前
TypeScript+Vue 实战:告别 any 滥用,统一接口 / Props / 表单类型,实现类型安全|编码语法规范篇
开发语言·前端·javascript·vue.js·安全·typescript