如何实现一个类似random.choice()的函数以返回可重复的结果?
我想给一个函数传递一个固定的种子(字符串),然后让它从一个列表中随机选择一个项目。不过,如果使用相同的种子,它应该总是选到同一个项目!显然,这并不是真正的随机,但它看起来应该是随机的,并且分布应该大致均匀。而且,它的速度也必须很快。
为了演示,这就是随机的工作方式。
>>> random.seed('Python')
>>> random.choice([1,2,3,4,5,6,7,8,9,0])
3
>>> random.choice([1,2,3,4,5,6,7,8,9,0])
6
>>> random.choice([1,2,3,4,5,6,7,8,9,0])
2
我想要的是这样的效果。
>>> notrandom([1,2,3,4,5,6,7,8,9,0],seed='Python')
4
>>> notrandom([1,2,3,4,5,6,7,8,9,0],seed='Python')
4
>>> notrandom([1,2,3,4,5,6,7,8,9,0],seed='Python')
4
只有在使用相同的列表和相同的种子字符串时,它的结果才需要是可重复的。
3 个回答
0
下面讨论了Trefex解决方案中概率分布不均匀的问题。
import sys
def notrandom(choices, seed):
n = len(choices)
h_values = 2**sys.hash_info.width
too_big = n * (h_values // n)
depth = 1
while True:
h = hash(f'{depth}:{seed}')
h = h % h_values # map negative to positive
if h < too_big:
return choices[h%n]
depth += 1
如果换用hashlib
来实现更强的哈希函数,就需要更多的工作来支持不同类型的种子:
import hashlib
def notrandom(choices, seed):
if type(seed) is int:
seed = '%x' %seed
if type(seed) is str:
seed = seed.encode('utf-8')
if type(seed) is not bytes:
raise ValueError('seed must be string, integer or bytes')
n = len(choices)
h_values = 2**256
too_big = n * (h_values // n)
depth = 1
while True:
h = hashlib.sha256(b'%d:%s' %(depth, seed))
h = int(h.hexdigest(), 16)
if h < too_big:
return choices[h%n]
depth += 1
0
第一次的时候,从列表中随机选一个数字,然后把这个数字进行哈希处理,最后把这个数字和哈希值的组合存到一个数组里。
之后,再对这个数字进行哈希处理,并把它当作你数组中的一个键来使用。
这显然不是个好办法,但我觉得在这里可能还算有效。
补充一下:我刚看到这个是针对同一个输入列表的。所以也要对这个列表进行哈希处理,并把这个哈希值也保存下来。
3
根据Python的随机模块文档,我觉得这就是你想要的内容:
这个模块提供的函数其实是一个隐藏的随机类(random.Random)的实例中的绑定方法。你可以自己创建随机类的实例,这样就能得到不共享状态的生成器。
比如说,
> r = random.Random()
> r.seed('Hi')
> r.random()
0.3787897089299177
> r.seed('Hi')
> r.random()
0.3787897089299177