如何实现一个类似random.choice()的函数以返回可重复的结果?

4 投票
3 回答
2412 浏览
提问于 2025-04-17 00:30

我想给一个函数传递一个固定的种子(字符串),然后让它从一个列表中随机选择一个项目。不过,如果使用相同的种子,它应该总是选到同一个项目!显然,这并不是真正的随机,但它看起来应该是随机的,并且分布应该大致均匀。而且,它的速度也必须很快。

为了演示,这就是随机的工作方式。

>>> 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

撰写回答