问题陈述如下,我们有非常多的项目遍历迭代器模式(动态地构造或提取)所请求的项目。
由于项目数量较大而无法保存在内存中(例如列表)。
为了生成a,迭代器遵循的过程是什么? 每次调用迭代器时项的随机顺序。 独一无二 随机顺序意味着最终所有项目只遍历一次 但是以随机顺序返回。
如果项目数量相对较少,可以按如下方式解决此问题:
对于这个问题,人们可以假设迭代器可以索引项目(或排名/取消它们)。因此迭代器可以获取项目范围内所有索引i的第i项。
注意随机顺序应均匀分布在项目的所有排序集中,换句话说无偏见。这种情况省去了逐块方案中随机化项目列表的解决方案(例如,为了让一些项目在内存中并且只随机化它们,然后是下一个项目块等等)
答案 0 :(得分:3)
加密是可逆的,因此加密是从集合到其自身的一对一映射。
选择一个具有足够大的块大小的块密码来覆盖您拥有的项目数。
加密数字0,1,2,3,4 ......这将为您提供一个非重复的有序数字列表,最多可达2 ^(块大小)。
如果加密的数字太大,请忽略它。如果加密的数字在项目列表的大小范围内,则选择该项目。重复你需要的许多物品。
具有可变块大小的密码(如Hasty Pudding cypher)将减少未命中数。
答案 1 :(得分:0)
我将提供解决此问题的计划如下:
关键在于编码(后继)索引以产生随机顺序。
由于mentioned(可逆)加密就是这样一种方案。
让我们看看这些计划的概要:
假设迭代器使用后继函数来获取下一个索引/项。对于通常的情况,后继函数将是:
def succ(index):
return index+1
这将返回下一个索引。如果被视为二元操作,index+1
代码会在当前索引模式中创建一个新的索引模式(缺少更好的单词)。
据推测,可以推广这种模式(在二进制操作中,即logN)来创建一个返回下一个索引但以混洗或随机唯一顺序遍历的连续函数。
例如,加密例程可以看作是这样的方案。
更简单的方案是使用模拟算法,类似于伪随机数发生器,具有合适的参数,即:
def succ_rand_modulo(index):
return (seed*index+step) mod numItems
适当选择种子和步骤(例如相对素数且足够大)数字,以便生成具有统一属性的随机排序(即无偏见)
如上所述的线性映射可能不会产生所有排序,可以假设多项式映射具有更多可供参数调整,可以产生所有排列/排序(参见:Permutation Polynomials (over finite fields))
实际上,所有这些方法都是重新编码索引的方法(即加密,模运算,伪随机生成,二进制操作等)。
我想说最有效的方法是(多项式)模(即伪随机数生成),前提是参数适当选择,以便能够在所有随机排序上产生均匀/无偏分布< / strong>(见上文)。
更新:
另一种只需要numItems
位存储在内存中(但需要更多时间)的方法是基于拒绝的方法。可以随机生成或获取一个索引/项,并将位向量上的相关位设置为1(项目取出),然后生成另一个随机索引,如果设置了关联位(已经使用了索引),则拒绝它并重新生成另一个随机索引等等。平均而言,这将是O(numItems)
的运行时间,但最坏的情况可能很长。
UPDATE2:
另一种有希望的方法,在各种情况下也是最佳的,method by I. STOJMENOVIC可以统一有效地排列/取消组合对象,而不需要计算大整数。
该方法适用于这种情况如下,随机排序只是所有排序中的排列。因此,为了生成任何随机排序,人们试图生成N!
项的随机排列。能够均匀地(即无偏见地)产生随机排列而不处理大整数是一个优点。 STOJMENOVIC的方法仅使用[0,1]
中的一个随机数,并将其作为生成特定排列的概率。为了在这种情况下调整方法,可以使用{{1}中的单个随机数的初始种子来更改代码以一次返回一个排列元素(保持生成器在任何给定时间的某个状态) }}
Lecture notes on Lexicographic generation (touching upon random generation as well)
对相同问题/类似方法的引用:
基于独特随机数字生成的方案