整体说明
- 随机种子(
seed)是初始化随机数生成器的状态,相同种子产生完全相同的随机序列(可复现性)- 不设置种子时,使用系统时间(
time.time())作为种子,每次运行结果不同 - 种子可以是整数、字符串、字节等(内部会转换为整数)
- 不设置种子时,使用系统时间(
- 随机状态(
state)本质是生成器的内部状态(包含所有必要信息,确保后续随机数可预测)random.getstate():保存当前状态(返回一个元组)random.setstate(state):恢复到之前保存的状态
- 全局
random模块在所有地方共享一个状态,不是线程安全的(多线程会竞争状态)- 建议多使用 局部
random.Random实例而不是全局random模块
- 建议多使用 局部
random 模块示例
- 以下是一个原生的 Python
random模块使用示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83import random
print("=" * 50)
print("随机种子的可复现性演示")
print("=" * 50)
print("\n【不设置种子(全局随机)】")
random.seed() # 重置为默认状态(系统时间种子)
print(f"第一次随机整数序列: {[random.randint(1, 100) for _ in range(3)]}")
print(f"第二次随机整数序列: {[random.randint(1, 100) for _ in range(3)]}")
print("\n【设置固定种子】")
random.seed(42) # 固定种子,注意 seed 是一个函数,不要写成 random.seed = 42,这样会导致 seed 失效
print(f"种子42-序列: {[random.randint(1, 100) for _ in range(3)]}") # 结果为固定值,跨平台跨进程可复现
random.seed(42) # 固定种子,重新设置相同种子,会重置随机序列到初始状态
print(f"种子42-序列: {[random.randint(1, 100) for _ in range(3)]}") # 结果复现上面的结果
print("\n" + "=" * 50)
print("随机状态的保存与恢复")
print("=" * 50)
random.seed(100) # 先设置种子,确保初始状态一致
print(f"\n初始状态-随机数序列: {[random.randint(1, 100) for _ in range(3)]}")
state = random.getstate() # 保存当前随机状态(返回一个不可变对象,包含完整的随机生成器状态)
print(f"状态改变后-随机数序列: {[random.randint(1, 100) for _ in range(3)]}") # 继续生成几个随机数
random.setstate(state)# 恢复之前保存的状态,从当时状态开始继续除数,就像是 seed(100) 后已经输出过多个 随机数一样
print(f"恢复状态后-随机数序列: {[random.randint(1, 100) for _ in range(3)]}") # 继续生成几个随机数,得到的结论是跟上面一致的
print("\n" + "=" * 50)
print("全局随机 vs 局部随机实例")
print("=" * 50)
# 全局 random:所有模块共享同一个状态
print("\n【全局随机(共享状态)】")
random.seed(200)
print(f"全局-随机数1: {random.uniform(0, 10)}")
print(f"全局-随机数2: {random.uniform(0, 10)}")
# 局部 Random 实例:独立状态,不影响全局
local_rand1 = random.Random(300) # 局部实例1,种子300
local_rand2 = random.Random(300) # 局部实例2,种子300
local_rand3 = random.Random(400) # 局部实例2,种子400
print("\n【局部随机实例(独立状态)】")
print(f"局部1-随机数(seed=300): {local_rand1.randint(1, 10)}")
print(f"局部2-随机数(seed=300): {local_rand2.randint(1, 10)}")
print(f"局部3-随机数(seed=400): {local_rand2.randint(1, 10)}")
# ==================================================
# 随机种子的可复现性演示
# ==================================================
#
# 【不设置种子(全局随机)】
# 第一次随机整数序列: [100, 29, 44]
# 第二次随机整数序列: [16, 34, 5]
#
# 【设置固定种子】
# 种子42-序列: [82, 15, 4]
# 种子42-序列: [82, 15, 4]
#
# ==================================================
# 随机状态的保存与恢复
# ==================================================
#
# 初始状态-随机数序列: [19, 59, 59]
# 状态改变后-随机数序列: [99, 23, 91]
# 恢复状态后-随机数序列: [99, 23, 91]
#
# ==================================================
# 全局随机 vs 局部随机实例
# ==================================================
#
# 【全局随机(共享状态)】
# 全局-随机数1: 0.4560930208539393
# 全局-随机数2: 2.0344697486239927
#
# 【局部随机实例(独立状态)】
# 局部1-随机数(seed=300): 10
# 局部2-随机数(seed=300): 10
# 局部3-随机数(seed=400): 6
附录:特别说明
random.seed是一个函数而不是一个数字或字符串,需要使用random.seed(1)而不是random.seed=1来设置种子- 这里容易理解错误
- 所有设置了固定种子的部分(如
seed=42、local_rand1 = random.Random(300)),每次运行结果完全一致 - 全局随机在未设置种子时,每次运行结果不同;但恢复状态后,会延续之前的序列
- 局部实例的操作不会影响全局或其他局部实例的状态,实现了完全隔离
random.shuffle(input)是个 in-place 修改方法,会直接修改input对象的值
附录:random 隔离实用技巧
- 临时固定状态:保存原始状态 -> 设置临时种子 -> 执行操作 -> 恢复原始状态(避免污染全局)
- 独立随机流:为不同任务/模块分配独立种子(比如
task_seed = base_seed + task_id),确保并行任务结果可复现 - 局部状态持久化:保存局部实例的状态(如
local_rand.getstate()),后续可在其他地方恢复继续使用 - 禁止全局污染:封装局部实例,强制代码使用独立随机生成器(避免误改全局状态)