随机数
概述
随机数在计算机科学和编程中扮演着重要角色,广泛应用于密码学、模拟、游戏、统计采样等领域。
随机数的类型
1. 伪随机数(Pseudo-Random Numbers)
- 由确定性算法生成
- 给定相同的种子,会产生相同的序列
- 周期性重复
- 速度快,但可预测
2. 真随机数(True Random Numbers)
- 基于物理现象(如热噪声、放射性衰变)
- 不可预测
- 无周期性
- 生成速度较慢
常见随机数生成算法
线性同余生成器(LCG)
X(n+1) = (a * X(n) + c) mod m
- 参数:乘数 a、增量 c、模数 m
- 优点:简单、快速
- 缺点:周期有限、统计特性一般
梅森旋转算法(Mersenne Twister)
- 周期:2^19937 - 1
- 优点:周期长、分布均匀
- 缺点:状态空间大(2.5KB)
- 应用:Python 的 random 模块默认算法
xorshift 系列
- 基于位移和异或操作
- 优点:速度快、状态空间小
- 缺点:某些变体统计特性不佳
加密安全随机数生成器(CSPRNG)
- 如:/dev/urandom、CryptGenRandom
- 用于密码学场景
- 不可预测性至关重要
编程语言中的随机数
Python
import random
# 伪随机数
random.random() # [0.0, 1.0) 浮点数
random.randint(1, 10) # [1, 10] 整数
random.choice(['a', 'b', 'c']) # 随机选择
# 加密安全随机数
import secrets
secrets.token_hex(16) # 随机十六进制字符串
secrets.randbelow(100) # [0, 100) 安全随机整数JavaScript
// 伪随机数
Math.random() // [0, 1) 浮点数
// 加密安全随机数
crypto.getRandomValues(new Uint32Array(1))Java
import java.util.Random;
import java.security.SecureRandom;
// 伪随机数
Random rand = new Random();
rand.nextInt(10); // [0, 10) 整数
// 加密安全随机数
SecureRandom secureRand = new SecureRandom();
secureRand.nextInt(100);随机数种子
种子(Seed)用于初始化随机数生成器的状态:
import random
random.seed(42) # 设置种子
print(random.random()) # 每次运行结果相同用途:
- 可重现的实验结果
- 调试和测试
- 确定性模拟
应用场景
| 场景 | 推荐类型 | 示例 |
|---|---|---|
| 游戏 | 伪随机 | 随机地图生成、掉落概率 |
| 模拟 | 伪随机 | 蒙特卡洛模拟 |
| 密码学 | 真随机/CSPRNG | 密钥生成、会话令牌 |
| 抽奖 | 真随机 | 公平性要求高 |
| 测试 | 伪随机(固定种子) | 可重现的测试用例 |
注意事项
- 安全性:密码学应用必须使用加密安全的随机数
- 种子选择:避免使用可预测的种子(如时间戳)
- 周期性:伪随机数有周期,大量使用时需注意
- 分布:了解生成器的分布特性
Linux 系统生成随机数
Linux 内核提供了两种随机数生成机制,通过特殊的字符设备文件供用户空间访问。
/dev/random 和 /dev/urandom
| 特性 | /dev/random | /dev/urandom |
|---|---|---|
| 阻塞行为 | 熵池不足时阻塞 | 不阻塞 |
| 安全性 | 理论上更高 | 足够安全 |
| 性能 | 较慢 | 较快 |
| 适用场景 | 密钥生成(极少量) | 一般加密用途 |
| 推荐程度 | 不推荐 | 推荐使用 |
注意:现代 Linux(内核 4.8+)中,两者安全性差异已很小。大多数情况下推荐使用
/dev/urandom。
从命令行获取随机数
# 读取随机字节(十六进制)
xxd -l 16 -p /dev/urandom
# 生成随机密码
openssl rand -base64 12
# 使用 head 读取
head -c 16 /dev/urandom | xxd -p
# 生成随机数字(0-99)
od -An -N2 -tu2 /dev/urandom | awk '{print $1 % 100}'
# 使用 shuf 生成随机数
shuf -i 1-100 -n 1
# 生成 UUID
cat /proc/sys/kernel/random/uuidShell 脚本中使用随机数
#!/bin/bash
# 使用 $RANDOM(0-32767)
echo $RANDOM
# 生成指定范围的随机数(1-100)
echo $((RANDOM % 100 + 1))
# 从 /dev/urandom 读取
random_num=$(od -An -N2 -tu2 /dev/urandom | tr -d ' ')
echo $random_num
# 生成随机字符串
random_str=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 16)
echo $random_str系统调用 getrandom()
Linux 3.17+ 提供了 getrandom() 系统调用:
#include <linux/random.h>
#include <sys/syscall.h>
#include <unistd.h>
int main() {
unsigned char buf[16];
// 使用 getrandom 系统调用
ssize_t ret = syscall(SYS_getrandom, buf, sizeof(buf), 0);
// 或使用 glibc 封装(glibc 2.25+)
// ssize_t ret = getrandom(buf, sizeof(buf), 0);
return 0;
}getrandom() 标志:
0:默认行为,熵池未初始化时阻塞GRND_NONBLOCK:非阻塞模式,熵池未初始化时返回 -1GRND_RANDOM:使用/dev/random的熵池GRND_INSECURE(Linux 5.6+):获取未初始化的熵(仅用于引导早期)
/proc/sys/kernel/random/ 目录
查看随机数生成器状态:
# 可用熵位数
cat /proc/sys/kernel/random/entropy_avail
# 熵池容量
cat /proc/sys/kernel/random/poolsize
# 自上次启动以来的熵估计
cat /proc/sys/kernel/random/entropy_count
# 生成 UUID
cat /proc/sys/kernel/random/uuid
# 生成随机 UUID
cat /proc/sys/kernel/random/uuid使用 OpenSSL 生成随机数
# 生成 16 字节随机数(十六进制)
openssl rand -hex 16
# 生成 16 字节随机数(Base64)
openssl rand -base64 16
# 生成随机密码(字母数字)
openssl rand -base64 12 | tr -d '/+=' | head -c 12
# 写入文件
openssl rand -out random.bin 32熵源与 haveged
虚拟机或容器中可能熵不足,可使用 haveged 增加熵:
# 安装 haveged
sudo apt install haveged
# 查看熵可用性
cat /proc/sys/kernel/random/entropy_avail
# haveged 会持续监控并补充熵
systemctl status haveged在 C 程序中读取 /dev/urandom
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp = fopen("/dev/urandom", "rb");
if (!fp) {
perror("fopen");
return 1;
}
unsigned char buf[16];
size_t n = fread(buf, 1, sizeof(buf), fp);
fclose(fp);
// 打印十六进制
for (int i = 0; i < n; i++) {
printf("%02x", buf[i]);
}
printf("\n");
return 0;
}Linux 随机数架构图
┌─────────────────────────────────────────────────────┐
│ 硒源(Entropy Sources) │
├─────────────────────────────────────────────────────┤
│ 键盘中断 │ 鼠标中断 │ 磁盘IO │ 硬件RNG │ ... │
└─────┬─────┴─────┬─────┴────┬────┴─────┬─────┴──────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────┐
│ Linux 内核熵池(Entropy Pool) │
│ (SHA-1 混合) │
└─────────────────────┬───────────────────────────────┘
│
┌───────────┴───────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ /dev/random │ │ /dev/urandom │
│ (阻塞式) │ │ (非阻塞) │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────┐
│ 用户空间应用程序(通过 read/getrandom) │
└─────────────────────────────────────────────────────┘
最佳实践
- 优先使用
/dev/urandom或getrandom() - 避免自己实现随机数生成算法
- 对于加密密钥生成,使用专门的库(如 OpenSSL)
- 注意虚拟化环境中的熵不足问题
- 生产环境考虑部署 haveged 或 rngd 服务