使用真正的随机数作为种子会产生真正的随机数吗?

Does using a true random number as seed result in truely random numbers?

假设我们使用自动抛出的 6 面骰子来生成 1 到 100 之间的整数。在 Python(或任何其他编程语言中),这个 [1, 6] 整数将用作种子值。生成的整数真的是随机的吗?如果不是,从这个随机源生成这样一个数字的最佳方法是什么?

没有。 (伪)随机数生成器生成确定性的值循环。种子所做的唯一一件事就是确定您开始在循环中的哪个位置检索值;它不会改变循环本身。

它不会是真正随机的,因为它是由算法生成的。但是,它的随机性足以模拟赔率之类的东西,或者为了制作游戏。

如果您想要一个好的 python 随机数生成模块,请使用 secrets 模块。它专为密码学而设计。例如:

import secrets
secrets.randbelow(10)

没有什么是真正“随机的”,但是您可以对随机数生成器进行一些有用的测量,例如从中生成一堆数字并查看它们是否分布相当均匀。如果你生成 1-100 的数字,并且你这样做 100*100 次,你应该期望在最终结果中看到每个单独的数字大约 100 次,并且你预计最频繁滚动的数字只会出现一点点超过100次。

>>> import random
>>> from collections import Counter
>>> c = Counter()
>>> for _ in range(10000):
...     c[random.randint(1, 100)] += 1
...
>>> max(c.values()
123

完全非随机数字生成器会是什么样子?让我们应用相同的过程,但这次我们每次都使用相同的数字而不是 random.randint(1, 100)

>>> c = Counter()
>>> for _ in range(10000):
...     c[42] += 1
...
>>> max(c.values())
10000
>>> c
Counter({42: 10000})

所以非常粗略地说,最大值越高,我们的随机数生成器的随机性就越小。

现在让我们试试每次使用 1-6 的随机数为 RNG 播种的方法:

>>> c = Counter()
>>> for _ in range(10000):
...     random.seed(random.randint(1, 6))
...     c[random.randint(1, 100)] += 1
...
>>> max(c.values())
5000
>>> c
Counter({31: 5000, 80: 5000})

不太好!每次调用 random.seed 时,您都会将 RNG 重置为特定状态,从这一点开始,如果您对其进行相同的调用,它将始终 return 相同的值序列。绝对最好的情况是,如果你只是在之前立即给它 6 个可能的不同种子之一,那么你应该只期望从那个 random.randint(1, 100) 调用中得到 6 个不同的答案,这将使你的最小最大计数为 1667。它变成实际 随机性更差,因为我们甚至没有得到 6 个可能的种子(因为 random.randint(1, 6) 调用落入了相同的确定性陷阱)。

tl;dr 是,如果你想要一个实际的伪随机数字序列,你不应该重新播种 RNG。

(edit) 假设您 被要求 仅使用 6 面骰子的输出为 RNG 播种——您是否可以多次掷骰子?考虑:

>>> c = Counter()
>>> for _ in range(10000):
...     random.seed(sum(random.randint(1, 6) * 6**i for i in range(100)))
...     c[random.randint(1, 100)] += 1
...
>>> max(c.values())
128

使用random模块时,可以使用the seed comes from the system time by default, but random.randint设置种子。请注意,要以这种方式设置种子,用于对 random.randint 的任何初始调用的种子是系统时间。

你的问题的答案,真正的随机性是一个无法实现的理想,一种热力学效率。为大多数目的生成 PRN 的最佳方法是回退到 random 的默认行为。设置和管理种子通常用于处理与生成的 PRN 序列的可重复性相关的问题。