如何以不可预测的顺序遍历整数范围?

时间:2009-06-05 13:56:35

标签: algorithm language-agnostic

如何以难以预测的顺序循环固定的整数范围(比如100000-999999)?

  • 假设范围可能足够大,以至于在数组中存储每个可能的整数是不切实际的,或者保留到目前为止找到的每个元素的数组。
  • 你必须只打一次范围内的每一个数字,并且能够告诉你什么时候结束,即没有数字

即。我想要一些比A更优雅的东西A)选择一个随机数,然后B)检查它是否已经被使用过,如果是,请回到步骤A,原因如下(除非你能说服我):

  • 当你开始用尽数字时,这真的很糟糕
  • 告诉你是否用完了未使用的号码可能会非常昂贵
  • 如果你有很多客户端或线程尝试在同一范围内同时执行此方法,这种方法也可能会出现并发问题

7 个答案:

答案 0 :(得分:9)

线性同余随机数发生器(在Knuth第2卷中以极其详细的细节覆盖)将以不易重复的方式逐步遍历给定范围内的每个值而不重复。基本陈述是

v = k * v + l mod m

其中m是集合的大小,k和l是m的相对素数(我相信这足以保证它按照需要工作),而v是正在使用的值。以某种或多或少的随机方式选择初始值,然后从那里开始。

一个优点是编写起来相当快,假设你可以避免溢出(通过限制k和m,或者使用任意精度的算术例程)。

答案 1 :(得分:4)

您应该查看使用linear feedback shift register。 “最大长度”LFSR将击中范围内的每个数字(2 ^ n -1),除了0。您可以存储第一个数字,这样您就可以知道它何时回到开始,或者您可以计算样本。问题是它是确定性的,所以从技术上讲,如果你知道算法就可以预测它。此外,给定序列中的任何数字,从该点开始,序列始终相同。

你可以拥有一堆LFSR算法的列表,并在开始之前随机选择一个,这样从运行到运行就不太可预测了。但是你用来选择算法的方法也是可以预测的......

如果您需要以真正的随机性进行此操作,那么您需要一个硬件随机数生成器(算法方法始终是可预测的)并且您必须维护该范围内所有数字的列表,然后使用随机数生成器从列表中选择一个数字,并同时从列表中删除它,这样就不会再次选择它。

答案 2 :(得分:3)

如果您需要不明显但不太安全,请为范围N选择M的步长,使N和M相对素数并执行算术模M

答案 3 :(得分:3)

如果您需要以加密安全的方式执行此操作,可能需要查看我在Cryptographically Secure Permutations with Block Ciphers上的文章。简而言之,选择一个分组密码,使用一种称为XOR折叠的技术将其降低到大于所需范围的2的最小功率,然后使用以下技术仅生成所需范围内的数字:

def permute(index, max):
  index = E(index)
  if index > max:
    return permute(index, max)

也就是说,只需“重新加密”您生成的超出所需范围的任何数字。生成整个序列所需的工作量受限于源序列中的项目数。生成单个元素的最坏情况是1 + unused_range,但这种可能性非常小。

您可以将其应用于生成1:1映射的任何内容,当然,不仅仅是分组密码示例。如果您正在处理不同类型的RNG - 例如LFSR,而不是重新应用该函数,只需跳过该元素。

答案 4 :(得分:1)

如果订单不必是真正随机的(只是不可预测的),并且如果我们允许存储小范围的值(比如N),那么:

  1. 将前N个值存储在子列表
  2. 从子列表返回随机值,直到N / 2为止,值仍为
  3. 将主要列表中的N / 2个值添加到子列表中。
  4. 重复2-3,直到完成所有值。
  5. 步骤2-3是必要的,否则每个第N个返回值都是可预测的。 改变子列表大小(N)和重载阈值(N / 2),以便在内存使用,重载频率和“随机性”之间找到一个很好的折衷方案。

答案 5 :(得分:1)

使用线性同余随机数生成器并丢弃范围之外的任何整数。

答案 6 :(得分:0)

让数据库生成一个包含数字1到N的表格。

让数据库为每个N值生成一个随机数。

返回按随机数排序的N个值。