用 Python 标准库处理随机:random(选择、采样、洗牌、可复现性)

系列 04 – 随机数生成与数据随机化

“随便挑一个”, “打乱顺序”, “不重复地挑几个”, “给不同概率的选项分配权重”。 这些功能在游戏、测试数据生成、简单仿真、数据拆分等场景中经常出现。

魔法师的赌场把戏

Python 标准库中的 random 模块可以用简短代码完成这些需求。 但需要注意的是,random 并不适合安全用途。


1. random 产生的“随机”到底是什么?

random 生成的是伪随机数(PRNG)。它不是完全不可预测的真随机数,而是基于内部状态生成“看似随机”的数列。

  • 同一初始状态(种子)会得到相同的结果。
  • 这使得调试和实验复现变得容易。
  • 但在攻击者可以推断其内部状态的安全场景中并不合适(后面会介绍 secrets)。

2. 基础随机数:数值生成

2.1 0 ≤ x < 1 的浮点数:random()

import random

x = random.random()
print(x)  # 0.0 <= x < 1.0

2.2 整数范围:randint(a, b) / randrange()

import random

print(random.randint(1, 6))   # 1~6(两端都包含)
print(random.randrange(0, 10, 2))  # 0,2,4,6,8 中的一个
  • randint(a, b) 包含 a 与 b。
  • randrange(start, stop, step) 与 range 规则相同(stop 不包含)。

2.3 浮点数范围:uniform(a, b)

import random

print(random.uniform(1.5, 3.5))

3. 在集合中挑选:选择、采样、洗牌

3.1 随机挑一个:choice()

import random

items = ["rock", "paper", "scissors"]
print(random.choice(items))

3.2 随机挑多个(不重复):sample()

import random

nums = list(range(1, 46))
picked = random.sample(nums, k=6)  # 不重复地挑 6 个
print(picked)
  • sample() 不修改原始列表
  • 结果 不重复

3.3 列表洗牌:shuffle()(原地修改)

import random

deck = list(range(10))
random.shuffle(deck)
print(deck)
  • shuffle() 直接 原地 修改列表。
  • 若想保留原始列表,可用 sample() 生成一个打乱后的副本。
import random

deck = list(range(10))
shuffled = random.sample(deck, k=len(deck))
print(deck)
print(shuffled)

4. 根据不同权重进行概率选择:choices()

当你想实现“常见、稀有、传奇”之类的选择时非常有用。

import random

items = ["common", "rare", "legendary"]
weights = [85, 14, 1]  # 也可以直接使用 0~1 的概率

result = random.choices(items, weights=weights, k=10)
print(result)
  • choices() 默认 允许重复
  • weights 可以是概率(0~1)或比例。

5. 结果的可复现性:seed()

给定相同的种子,结果会完全一致,尤其在示例或测试中非常实用。

import random

random.seed(42)
print(random.randint(1, 100))
print(random.randint(1, 100))

即使调用了多种函数,只要其调用顺序保持一致,结果也会相同。

提示:seed() 影响全局状态。若不想干扰全局,可单独创建 random.Random() 实例。


6. 本地随机数生成器:random.Random()

不想改动全局状态时,可以使用本地 RNG。

import random

rng = random.Random(42)  # 本地 RNG
print(rng.randint(1, 100))
print(rng.randint(1, 100))

此模式在库代码或多个模块共享随机数时尤为有用。


7. 安全随机数:使用 secrets 而非 random

random 是伪随机,存在可预测性。对于令牌、验证码、密码重置链接等安全用途,推荐使用 secrets

import secrets

token = secrets.token_urlsafe(16)
print(token)
  • secrets 采用系统安全随机源。

8. 常用模式示例

8.1 从数据中随机挑选一个或多个元素

import random

users = ["u1", "u2", "u3", "u4", "u5"]
print(random.choice(users))        # 1 人
print(random.sample(users, k=2))   # 2 人(不重复)

8.2 随机打乱后获取前部元素

import random

data = list(range(100))
random.shuffle(data)
train = data[:80]
test = data[80:]

8.3 简单的“加权轮盘”

import random

prizes = ["A", "B", "C", "D"]
weights = [1, 3, 10, 86]  # D 最常出现
print(random.choices(prizes, weights=weights, k=1)[0])

9. 小结

random 是一个通过简洁的 API 满足“随机选择”这一广泛需求的模块。

  • 单个挑选:choice
  • 不重复挑选:sample
  • 列表洗牌:shuffle
  • 加权挑选:choices
  • 可复现:seedrandom.Random()
  • 安全随机:secrets

下一期将继续探讨 statisticsmath 等数值处理的标准库。


相关文章: