随机数生成器每次仅返回一个数字

2024-05-06

Python 是否有一个随机数生成器,每次只返回一个随机整数next()函数被调用?数字不应该重复并且生成器应返回区间内的随机整数[1, 1 000 000]这是独一无二的。

我需要生成超过一百万个不同的数字,这听起来好像非常消耗内存,以防所有数字同时生成并存储在列表中。


您正在寻找一个线性同余发生器 https://en.wikipedia.org/wiki/Linear_congruential_generator具有完整的周期。这将使您能够获得目标数字范围内的非重复数字的伪随机序列。

实现 LCG 实际上非常简单,如下所示:

def lcg(a, c, m, seed = None):
    num = seed or 0
    while True:
        num = (a * num + c) % m
        yield num

然后,只需选择正确的值a, c, and m以保证 LCG 将生成完整的周期(这是您获得不重复数字的唯一保证)。正如维基百科文章所解释的,需要满足以下三个条件:

  1. m and c需要是相对素数。
  2. a - 1可以被所有素因数整除m
  3. a - 1能被 4 整除,如果m也能被4整除。

第一个很容易通过简单地选择素数来保证c。另外,这是最后可以选择的值,这最终将使我们能够稍微混淆序列。

之间的关系a - 1 and m不过更复杂。在全周期 LCG 中,m是周期的长度。或者换句话说,它是您的数字来自的数字范围。所以这就是你通常首先选择的。在你的情况下,你想要m在身边1000000。准确选择你的最大数量可能很困难,因为这限制了你很多(在你的选择中)a并且c),因此您也可以选择大于该数字的数字,然后跳过范围之外的所有数字。

我们来选择一下m = 1000000但现在。主要因素为m are 2 and 5。显然它也可以整除4。因此对于a - 1,我们需要一个数的倍数2 * 2 * 5满足条件2和3。让我们选择a - 1 = 160, so a = 161.

For c,我们使用一个介于我们范围之间的随机素数:c = 506903

将其放入我们的 LCG 中即可得到我们想要的序列。我们可以从范围(0 <= seed <= m)作为我们序列的起点。

那么让我们尝试一下并验证我们的想法是否确实有效。为此,我们只是从一组生成器中收集所有数字,直到找到重复的数字。到那时,我们应该有m = 1000000集合中的数字:

>>> g = lcg(161, 506903, 1000000)
>>> numbers = set()
>>> for n in g:
        if n in numbers:
            raise Exception('Number {} already encountered before!'.format(n))
        numbers.add(n)

Traceback (most recent call last):
  File "<pyshell#5>", line 3, in <module>
    raise Exception('Number {} already encountered before!'.format(n))
Exception: Number 506903 already encountered before!
>>> len(numbers)
1000000

这是正确的!所以我们确实创建了一个伪随机数字序列,使我们能够从我们的范围中获取不重复的数字m。当然,根据设计,这个序列将始终相同,因此当您选择这些数字时它只会随机一次。您可以切换以下值a and c但是,只要保持上述属性即可获得不同的序列。


这种方法的最大好处当然是您不需要存储所有先前生成的数字。它是一个恒定空间算法,因为它只需要记住初始配置和之前生成的值。

当您进一步了解序列时,它也不会恶化。这是一个普遍的问题,解决方案只是不断生成随机数,直到发现一个以前从未遇到过的新随机数。这是因为生成的数字列表越长,使用均匀分布的随机算法命中不在该列表中的数字的可能性就越小。因此,获得第 1000000 个数字可能需要很长时间才能使用基于内存的随机生成器生成。

但是,当然,这种简单的算法只执行一些乘法和一些加法,看起来并不是很随机。但您必须记住,这实际上是大多数伪随机数生成器的基础。所以random.random()在内部使用类似的东西。只是那m is 大很多,所以你不会注意到它在那里。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

随机数生成器每次仅返回一个数字 的相关文章

随机推荐