【发布时间】:2018-10-02 01:41:51
【问题描述】:
我正在尝试创建一个生成器,它返回给定范围内的数字,这些数字通过函数foo 给出的特定测试。但是,我希望以随机顺序测试这些数字。下面的代码将实现这一点:
from random import shuffle
def MyGenerator(foo, num):
order = list(range(num))
shuffle(order)
for i in order:
if foo(i):
yield i
问题
这个解决方案的问题是有时范围会很大(num 可能是10**8 和更高的顺序)。这个函数可能会变得很慢,在内存中有这么大的列表。我已尝试使用以下代码避免此问题:
from random import randint
def MyGenerator(foo, num):
tried = set()
while len(tried) <= num - 1:
i = randint(0, num-1)
if i in tried:
continue
tried.add(i)
if foo(i):
yield i
这在大多数情况下运行良好,因为在大多数情况下num 会很大,foo 将传递合理数量的数字,并且调用 __next__ 方法的总次数将相对小(比如说,最多 200 个通常要小得多)。因此,我们偶然发现一个通过foo 测试的值并且tried 的大小永远不会变大是合理的。 (即使它只通过了 10% 的时间,我们也不希望 tried 大致超过 2000 左右。)
但是,当num 很小(接近__next__ 方法被调用的次数,或者foo 大部分时间都失败时,上述解决方案变得非常低效——随机猜测数字直到它猜到不在tried 中的一个。
我尝试的解决方案...
我希望使用某种函数将数字0,1,2,..., n 以大致随机的方式映射到它们自身。 (这没有用于任何安全目的,因此如果它不是世界上最“随机”的功能也没关系)。这里的函数 (Create a random bijective function which has same domain and range) 将带符号的 32 位整数映射到自身,但我不确定如何将映射调整到更小的范围。给定num,我什至不需要0,1,..num 上的双射,只需一个大于n 的值并“接近”num(使用您认为合适的任何关闭定义)。然后我可以执行以下操作:
def mix_function_factory(num):
# something here???
def foo(index):
# something else here??
return foo
def MyGenerator(foo, num):
mix_function = mix_function_factory(num):
for i in range(num):
index = mix_function(i)
if index <= num:
if foo(index):
yield index
(只要双射不在一组比num 大得多的数字上,index <= num 不是 True 的次数就会很小)。
我的问题
你能想到以下一种吗:
-
mix_function_factory的潜在解决方案,甚至是mix_function的一些其他潜在功能,我可以尝试将其推广到num的不同值? - 解决原始问题的更好方法?
提前非常感谢....
【问题讨论】:
-
也许您可以根据
num的大小执行方法 1 或 2:如果较小,请在预先计算的列表上使用 shuffle,如果较大则使用set方法 -
其他需要考虑的事情:如果生成器重复一个数字,真的有多糟糕?如果您可以摆脱偶尔重复的数字(可能在您的代码的另一部分进行一些更改),这会带来更多的可能性,如果
num真的很大,那么发生的机会可能会非常小。
标签: python performance generator shuffle