rand

义项:随机数函数
rand

rand是C语言标准库中用于生成伪随机整数的函数,声明在stdlib.h头文件中,原型为int rand(void),每次调用返回0到RAND_MAX之间的一个整数[1]。它由一个可设置的种子值驱动,配合srand函数使用,广泛用于教学和一般程序的随机取值,因为实现简单、可移植性好而成为入门编程中最常见的随机数接口[2]。

目录
  1. 定义
  2. 原理
  3. 发展历程
  4. 应用
  5. 局限
  6. 参见

定义

rand是C标准库中声明于stdlib.h的一个无参函数,函数原型写作int rand(void),作用是在每次调用时按顺序取出一串伪随机整数中的一个[1]。它的返回值落在0到宏RAND_MAX之间,包含两端,RAND_MAX的具体数值由实现决定,微软的C运行库中为32767,GNU C库中则为2147483647[2]。POSIX标准还规定,rand生成的序列周期至少为2的32次方,并要求RAND_MAX不小于32767[3]。所谓伪随机,是指数值来自确定性的算法递推,只要起点相同,每次运行得到的数列就完全一致[4]。

与rand配套的还有srand函数,原型为void srand(unsigned seed),用来为后续的rand调用设定新序列的种子[5]。若在调用rand之前没有调用过srand,程序相当于先用种子值1初始化,因此同一个程序重复运行会得到相同的数列[2]。

原理

rand内部维护一个隐藏的状态量,通常称为种子。每次调用时,函数把当前种子代入固定的递推公式,得到下一个状态,再截取其中若干位作为返回值,同时用新状态覆盖旧种子[4]。POSIX文档给出了一种常见的实现写法,其核心递推可写为 $next = (next \times 1103515245 + 12345) \bmod 2^{31}$,返回值为 $(next \div 65536) \bmod 32768$,其中next即为种子[3]。这种线性同余形式的算法只依赖整数乘加与取模,因此运算开销很小。

srand的作用是把种子重新赋成参数给出的值,从而使后续rand产生另一条序列;用相同的种子再次调用srand,之后产生的数列会与上一次完全相同[5]。为了让每次运行得到不同的序列,常见做法是把当前时间作为种子,例如在程序开始处调用srand(time(NULL))[1]。由于种子只决定序列的起点,srand一般只在程序开始时调用一次,重复调用反而会削弱随机性[6]。

若需要把输出限制在指定区间内,通常用取模或缩放的办法。生成闭区间[a, b]内的整数可写作 $a + rand() \bmod (b-a+1)$;若希望覆盖到接近RAND_MAX的大范围,则更适合先把rand的返回值换算成浮点数再缩放,以免低位分布出现偏差[7]。下图为一次典型调用的流程。


flowchart LR

A[设定种子 srand] --> B[内部状态 next]

B --> C[线性同余递推]

C --> D[截取高位得到整数]

D --> E[返回 0 到 RAND_MAX]

D --> B

发展历程

rand与srand最初随C语言的标准化进入规范。ISO C标准把它们列为标准库中的伪随机序列生成函数,并要求使用前包含stdlib.h,此后各平台的C运行库都以这一接口为共同约定[2]。这也意味着不同实现可以采用不同算法,只要满足返回范围的要求即可,序列本身并不保证跨平台一致。

POSIX随后在C标准之上做了扩展,增加了带显式种子指针的rand_r,使多线程程序可以各自持有独立的序列[3]。不过标准文档也指出,这一扩展能携带的状态太少,难以满足对伪随机数质量要求较高的场合,并在后续版本中把它列为可能被移除的过时接口[5]。

C++沿用了C运行库中的rand,同时在标准库中另行提供了功能更完整的随机数设施,包括多种引擎与分布类型;微软的文档明确建议,需要高质量或密码学用途的随机数时,应改用这些新设施或rand_s,而不是继续使用rand[1]。

应用

rand最常见的用途是在教学与入门程序中演示随机行为。教材通常用它生成若干整数并观察输出,再通过srand改变种子来说明“确定性算法如何模拟随机”[4]。由于接口简单、无需额外依赖,它也被大量用于课后练习,例如猜数字和抽签类小游戏。

在一般应用软件中,rand可用于抽取样本、生成测试数据、随机排列数组元素,以及为模拟程序提供初值。游戏开发中常以它决定敌人生成位置、掉落物品或洗牌顺序;在网络程序中,它也能用于生成会话标识一类的临时值,但这类用途需要额外的安全性考量。在需要可复现结果的场合,固定种子的做法反而成为优点:调试时只要种子不变,程序每次运行的随机分支就完全一致[8]。

局限

首要局限是随机性质量有限。rand由确定性的递推产生,输出序列可以被完整复现,微软的文档明确指出它生成的数字不属于加密安全级别,不适合用于密钥、口令、令牌等安全场景[1]。同一文档还提醒,rand的输出范围较小,在某些平台上仅为0到32767,直接用于大范围取值会带来明显的取模偏差。

其次是可移植性方面的隐患。标准只规定返回值的范围,没有统一算法,因此同一段代码在不同编译器或不同C库下会得到不同的数列,依赖特定序列的程序在换平台后可能失效[2]。此外,低位的随机性通常弱于高位,用rand() % n取模时,若n不能整除RAND_MAX+1,部分取值出现的概率会略高[7]。

并发使用也存在问题。标准并未要求rand可重入,它依赖一份进程级的共享状态,多线程同时调用可能相互干扰,因此POSIX提供了rand_r,并建议在要求较高的场合改用drand48或random等更完善的生成器[3]。POSIX文档还直接指出,当程序有超出简单演示的需求时,应当避免使用这一组函数[5]。

参见

  • srand —— 为rand设定种子、决定其伪随机序列起点的配套函数

  • RAND_MAX —— 规定rand返回值上限的标准宏

  • 伪随机数生成器 —— rand所依赖的算法类别

  • 线性同余法 —— rand常见实现采用的递推方法

  • Mersenne Twister —— 质量更高、被现代标准库广泛采用的随机数引擎

词条评价
词条统计

浏览次数:2 次

阅读量:0 次 · 阅读完成量:0 次

最近更新:2026-10-28T18:45:57Z

历史版本

本条目引用的词条
srand RAND_MAX 伪随机数生成器 线性同余法 Mersenne Twister