Python 随机数生成的原理与局限

14次阅读

python random 模块基于 mt19937 伪随机算法,种子相同则序列确定,适用于模拟、测试等非安全场景;密码学用途须用 secrets 模块,其依赖系统熵源且不可重现。

Python 随机数生成的原理与局限

Python 的 random 模块不是真随机

它用的是 Mersenne Twister(MT19937)算法,确定性伪随机——只要种子一样,生成的序列就完全相同。这不是 bug,是设计使然,但很多人误以为 random.random() 能满足密码学或高并发唯一 ID 场景。

  • 适合模拟、测试、游戏逻辑等对“不可预测性”无硬要求的场景
  • 不适合生成密钥、token、session id——得用 secrets 模块
  • random.seed() 默认用系统时间,但多线程中若在同毫秒调用,可能撞种子;显式传入 os.urandom(8) 更稳

random.choice()random.choices() 别混用

前者从序列里挑一个,不放回;后者默认可重复抽样,且支持权重。用错会导致结果偏差,尤其在抽样统计或 A/B 测试中。

  • random.choice(['a','b','c']):每次返回单个元素,等概率
  • random.choices(['a','b','c'], weights=[0.1,0.8,0.1], k=5):返回长度为 5 的列表,b 出现概率高,且允许重复
  • 想“不放回抽多个”?用 random.sample(),它会报 ValueError 如果 k > len(population),而 choices() 不会

random.shuffle() 改原列表,别指望返回新列表

它直接在原 list 上操作,返回 None。写成 new_list = random.shuffle(old_list) 会让 new_list 变成 None,后续调用 appendlen 就崩了。

  • 正确做法:random.shuffle(my_list),然后直接用 my_list
  • 要保留原列表?先复制:shuffled = my_list.copy(); random.shuffle(shuffled)
  • 对 tuple、str 等不可变类型,必须转成 list 再 shuffle,否则报 TypeError: 'tuple' object does not support item assignment

secrets 模块才是安全随机数的起点

secrets 用操作系统级熵源(如 Linux 的 /dev/urandom),不依赖种子,也不可重现。但很多人只在生成 token 时想起来用,却忽略它不支持浮点分布、没有 shuffle、也没有加权抽样。

立即学习 Python 免费学习笔记(深入)”;

  • 生成密码:secrets.token_urlsafe(16)
  • 选一个选项:secrets.choice(['yes','no'])(注意:不支持 weights)
  • 生成整数范围:secrets.randbelow(100)(等价于 [0, 99]),不是 randint(0,100)
  • 想用 secrets 做正态分布?不行——得自己用 Box-Muller 等算法基于 secrets.randbits() 构造,成本高,多数情况没必要

真正难的不是选哪个函数,而是判断当前场景到底需不需要“不可预测”。加密、权限、防刷,必须用 secrets;模拟抛硬币、打乱播放列表、生成测试数据,random 完全够用,还更快更可控。

text=ZqhQzanResources