随机数概念及算法

1.随机数的概念

随机数 是指在某个数值范围内,每个数出现的概率均等且无法被事先预测的数。生成随机数的方法称为随机数生成算法,根据其真实随机性来源,分为两类:


1. 真随机数生成器(TRNG,True Random Number Generator)

  • 原理:利用物理过程的不可预测性,例如:

    • 电子元件的热噪声(电阻噪声)

    • 放射性衰变的时间间隔

    • 量子效应(如光子偏振方向)

    • 大气噪声(无线电台干扰)

  • 特点

    • 完全不可重现(无法通过相同的输入得到相同序列)

    • 无周期性

    • 生成速度较慢,通常需要专用硬件

  • 应用:密码学密钥生成、彩票开奖、安全令牌等对不可预测性要求极高的场景。


2. 伪随机数生成器(PRNG,Pseudo-Random Number Generator)

  • 原理 :采用确定性的数学公式或算法,从一个初始值(种子)开始迭代计算,产生一个长周期、统计上均匀的数列。

  • 特点

    • 种子相同 → 生成的随机数序列完全相同(可重现)

    • 周期有限(但可以设计得非常长,如 MT19937 周期为 219937−1219937−1)

    • 生成速度极快,无需硬件支持

  • 常见算法

    • 线性同余生成器(LCG) :Xn+1=(a⋅Xn+c)mod  mXn+1​=(a⋅Xn​+c)modm(如 C 语言的 rand() 常基于此)

    • 梅森旋转算法(Mersenne Twister,MT19937):周期长、分布均匀,广泛用于科学计算和游戏

    • Xorshift:基于异或和移位操作,速度极快

    • PCG(Permuted Congruential Generator):LCG 的改进版,统计质量高

  • 应用:蒙特卡洛模拟、游戏随机事件、程序测试、非加密场景。


3. 密码学安全伪随机数生成器(CSPRNG,Cryptographically Secure PRNG)

  • 性质:在 PRNG 基础上增加安全要求:

    • 即使攻击者获得连续多个输出,也无法推算出之前或之后的状态(前向/后向不可预测性

    • 种子足够大且不可猜测

  • 常见实现

    • ChaCha20(流密码结构)

    • 基于哈希的 DRBG (如 HMAC_DRBG,用于 Linux 内核的 /dev/urandom

    • Yarrow(FreeBSD 等系统使用)

  • 应用:生成会话密钥、随机数用于 TLS/SSL、身份验证挑战值等。


总结表

类型 随机性来源 是否可重现 典型算法 主要用途
真随机(TRNG) 物理现象 热噪声采样、量子RNG 高安全性、抽奖、密钥生成
伪随机(PRNG) 数学公式 是(种子相同) LCG, MT19937, Xorshift 模拟、游戏、一般编程
密码学安全伪随机(CSPRNG) 数学公式+安全约束 是(种子保密) ChaCha20, HMAC_DRBG 加密协议、安全令牌

注意 :日常编程中用 srand(time(NULL)) + rand() 生成的是伪随机数,因速度快、可重现便于调试。而对安全性有要求的场景(如重置密码链接的 token)必须使用 CSPRNG。

2.伪随机的应用

1. 什么是"伪随机"?

伪随机数 是指通过确定性的数学算法生成的、看起来像随机数的数列。

  • "伪" 的意思是"假的、模拟的"------它不是真正不可预测的随机数,而是由固定算法计算出来的。

  • 但因为数列能通过常见的随机性统计检验(如分布均匀、前后无关联),所以在实际工程中把它当作随机数使用。

真正的随机数(如热噪声、放射性衰变)是无法通过公式重复生成的,而伪随机数本质上是一个周期很长、分布均匀的数列


2. 只要种子相同,生成的随机数序列就相同吗?

是的,一定相同

伪随机数生成器(PRNG)是一个确定性的函数:

  • 你给一个初始值(种子 ,比如 srand(seed) 中的 seed),它就会从该状态出发,按照固定公式产生第一个随机数,然后更新内部状态,再产生第二个,依次类推。

  • 如果两次运行的种子相同,整个数列就会完全一致。

例如:

c

复制代码
srand(123);
printf("%d ", rand());  // 假设输出 41
printf("%d ", rand());  // 假设输出 18467

只要种子是 123,在任何机器、任何时间运行,前两个 rand() 输出总是 41, 18467(具体数值依赖标准库实现,但对同一实现是固定的)。


3. 为什么用时间戳作种子?

为了让每次程序运行时种子不同,从而得到不同的随机数序列。

  • time(NULL) 返回当前秒数,每秒变化一次。

  • 这样你在不同时刻运行程序,种子不同,序列也就不同,达到"每次运行结果不一样"的直观效果。

但注意:如果程序在同一秒内启动两次,种子相同,两次的随机数序列也会相同。


4. 伪随机 vs 真随机

特性 伪随机(PRNG) 真随机(硬件随机数)
确定性 是(种子决定全部)
可重复性 种子相同则序列可重现 不可重现
周期 有限长(但极长,如2^19937-1) 无周期
来源 数学公式 物理现象(热噪声、量子效应)
用途 模拟、游戏、非安全场景 密码学、抽奖等要求不可预测的场景

5. 补充:伪随机并不"假"

虽然它是确定的,但质量高的 PRNG(如梅森旋转算法)生成的数列在统计上与真随机几乎无法区分。

对于绝大多数应用(如游戏、蒙特卡洛模拟),伪随机数完全够用,并且可重现的优点是调试和复现实验结果的好帮手。

3.伪随机的算法(根据时间戳生成指定范围内的随机数)

在 C 语言中,根据时间戳生成指定范围内的随机数通常分两步:

  1. time(NULL) 获取当前时间戳作为种子,调用 srand() 初始化随机数生成器。

  2. rand() 生成随机数,并通过算术运算限制到 [min, max] 区间。

下面是完整示例代码:

c

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/**
 * 生成 [min, max] 范围内的随机整数
 * @param min 最小值
 * @param min 最大值
 * @return 随机整数
 */
int random_in_range(int min, int max) {
    if (min > max) {
        // 交换,保证参数合法
        int temp = min;
        min = max;
        max = temp;
    }
    // 方法1:取模运算(适用于范围不大时)
    // return rand() % (max - min + 1) + min;

    // 方法2:浮点数缩放(分布更均匀,推荐)
    return (int)((double)rand() / (RAND_MAX + 1.0) * (max - min + 1) + min);
}

int main() {
    // 用当前时间戳(秒)设置随机种子
    srand((unsigned int)time(NULL));

    // 示例:生成 10 个 [5, 20] 范围内的随机数
    int min = 5, max = 20;
    for (int i = 0; i < 10; i++) {
        int r = random_in_range(min, max);
        printf("%d ", r);
    }
    printf("\n");

    return 0;
}

关键点说明:

  1. 时间戳time(NULL) 返回从 1970‑01‑01 至今的秒数,每次运行程序通常不同,保证种子随机性。

  2. 种子设置srand() 只需调用一次,一般在程序开头。

  3. 范围限制

    • 取模法 rand() % (max - min + 1) + min 简单直接,但若 max-min+1 不能整除 RAND_MAX+1,会导致低值略高的偏差(一般可接受)。

    • 浮点缩放法更均匀,推荐使用。

  4. 头文件<stdlib.h> 提供 rand/srand<time.h> 提供 time

注意事项:

  • 如果程序在 同一秒内多次运行 (例如循环中反复调用),time(NULL) 返回相同值,导致相同随机序列。此时可改用纳秒级时间(如 clock_gettime)或混合其他熵源。

  • 多线程环境下,rand() 不是线程安全的,建议使用 rand_r 或 C11 的 rand_s

如果需要更高质量的随机数(如加密场景),应使用 getrandom(Linux)或 rand_s(Windows)。但对于一般应用,上述方法足够。

相关推荐
阿Y加油吧2 小时前
算法实战笔记:LeetCode 169 多数元素 & 75 颜色分类
笔记·算法·leetcode
不要秃头的小孩2 小时前
力扣刷题——509. 斐波那契数
python·算法·leetcode·动态规划
We་ct3 小时前
LeetCode 120. 三角形最小路径和:动态规划详解
前端·javascript·算法·leetcode·typescript·动态规划
py有趣3 小时前
力扣热门100题之和为K的子数组
数据结构·算法·leetcode
hipolymers3 小时前
C语言怎么样?难学吗?
c语言·数据结构·学习·算法·编程
workflower5 小时前
机器人应用-楼宇室内巡逻
大数据·人工智能·算法·microsoft·机器人·动态规划·享元模式
ZPC82105 小时前
fanuc 机器人通过PR寄存器实现轨迹控制
人工智能·算法·计算机视觉·机器人
py有趣5 小时前
力扣热门100题之编辑距离
数据结构·算法·leetcode