如何用自己的生成器覆盖Python的随机数?
在Python 的文档中random,它指出可以通过覆盖该random()方法来“使用您自己的基本生成器” 。假设我有一个自定义生成器,它在半开放范围 [0.0, 1.0) 内均匀地输出随机浮点数,即就像random().
- 我将如何用自己的 Python 替换 Python?
此外,假设替换是一个真正的随机生成器,它建立在一个自然的熵源之上。
- 这是否意味着我可以像
random.choice()在给定数组上一样使用类的其他部分,并声称所选元素是真正随机的?
回答
Python 的random模块实际上是围绕random.Random类型构建的。各种“随机函数”,例如random.random仅使用该类型的默认实例。
要创建自己的随机数生成器,只需子类化random.Random并替换其random 方法即可。虽然对基本功能没有严格要求,但强烈建议调整seed,getstate和setstate方法。
class RangeRandom(Random):
"""
Example "random" number generator that provides numbers from a uniform sequence
"""
def __init__(self, resolution=100):
self._pos = 0
self._resolution = resolution
def random(self) -> float:
self._pos = (self._pos + 1) % self._resolution
return self._pos / self._resolution
def getstate(self):
return self._pos, self._resolution
def setstate(self, state) -> None:
self._pos,self._resolution = state
def seed(self, a) -> None:
self._pos = int(a) % self._resolution
值得注意的是,定义random方法足以使所有其他方法使用相同的潜在“随机性”。例如,使用choice或randint将基于以下random方法绘制的随机整数:
>>> rand = RangeRandom()
>>> vals = [1, 2, 3, 4]
>>> [rand.choice([1, 2, 3, 4]) for _ in range(5)]
[2, 4, 2, 4, 2, 4]
值得指出的是,虽然实现random就足够了,random.Random然后使用内部助手将随机浮点数转换为随机整数;这本质上是有损的,因为 float 的精度有限。如果随机数生成器允许,建议也实现_randbelow或至少getrandbits.
random.getrandbits(k)
返回具有 k 个随机位的非负 Python 整数。此方法随 MersenneTwister 生成器提供,其他一些生成器也可以将其作为 API 的可选部分提供。当可用时, getrandbits() 使 randrange() 能够处理任意大的范围。
未记录的方法_randbelow必须接受一个整数n并返回一个区间内的随机整数[0, n)。
class ArbitraryRangeRandom(RangeRandom):
"""
Example "random" number generator for floats/ints from a uniform sequence
"""
def _randbelow(self, n: int) -> int:
self.random() # advance the RNG
if n == 0:
return 0
elif n <= self._resolution:
return self._pos % n
else:
return self._pos * (n // self._resolution)
>>> rand = RangeRandom()
>>> vals = [1, 2, 3, 4]
>>> # explicit int support provides better resolution/spacing
>>> print([rand.choice([1, 2, 3, 4]) for _ in range(5)])
[2, 3, 4, 1, 2]