【问题标题】:How can you walk through an integer range in an unpredictable order?如何以不可预知的顺序遍历整数范围?
【发布时间】:2009-06-05 13:56:35
【问题描述】:

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

  • 假设范围可能足够大,以至于将所有可能的整数存储在一个数组中或保留一个包含您目前找到的每个元素的数组是不切实际的。
  • 您必须点击范围内的每个数字一次且仅一次,并且能够判断您何时完成,即没有剩余数字

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

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

【问题讨论】:

  • 可以存储位掩码吗? 100.000 个整数可能太大而无法存储,但 100.000 位可能会少 32 倍。
  • 可以是双链表吗?
  • 可以存入数据库表吗?
  • 我详细阐述了一点,希望它更清楚

标签: algorithm language-agnostic


【解决方案1】:

线性同余随机数生成器(在 Knuth 的第 2 卷中详细介绍)将逐步遍历给定范围内的每个值而不重复,其方式不易预测。基本语句是

v = k * v + l mod m

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

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

【讨论】:

  • 与 m 互质是什么意思?那么,对于 100,我可以使用 19 和 13 吗?另外我假设操作顺序是 ((k * v) + l) mod m),对吧?
  • 相对素数表示没有公因数。素数相对于任何其他数来说都是素数。 4 与 9 互质,尽管两者都不是质数,因为 2 不能整除 9,3 也不能整除 4。5 与两者互质。
  • 我试过这个,发现它并没有在重复数字之前耗尽范围,这取决于我选择的常量值。根据维基百科条目tinyurl.com/2qx2xb,如果您希望序列详尽无遗,则有额外的限制:k-1 必须能被 m 的所有素因子整除,如果 m 能被 4 整除,k-1 必须能被 4 整除。因此,对于 m=100 k 可能是 21、41、61 或 81。似乎非常有限。 (即使 m 本身是素数也有可能?)文章还说 l 必须受 m 约束,我尝试违反并发现它似乎仍然有效。
【解决方案2】:

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

您可以列出一堆 LFSR 算法,然后在开始之前随机选择一个,这样每次运行的可预测性就会降低。但是你用来选择算法的方法可能也是可以预测的......

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

【讨论】:

    【解决方案3】:

    如果您需要不明显但不是很安全,请为范围 M 选择一个 N 的步长,这样 N 和 M 是相对质数,并进行算术 mod M。

    【讨论】:

    • 如何选择相对素数?
    • 最简单的就是选择一个大素数(所以相对于所有东西都是相对素数),但是有一些有效的方法......
    • 如果您可以使用素数生成器,选择一个大素数就可以了。否则,M-1 和 M+1 保证与 M 互质。使用其中任何一个都会产生非常明显的结果,因为 mod M 它们等价于 -1 和 1。另一种方法是生成一些前 n 个质数,测试它们中的每一个是否能整除 M,然后将不能相乘的质数相乘。昂贵且笨拙,但仅在初始化时需要。
    【解决方案4】:

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

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

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

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

    【讨论】:

      【解决方案5】:

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

      1. 将前 N 个值存储在子列表中
      2. 从子列表返回随机值,直到保留 N/2 个值
      3. 将主列表中的 N/2 个值添加到子列表中。
      4. 重复 2-3 直到完成所有值。

      步骤 2-3 是必要的,否则每个第 N 个返回值都是可预测的。 改变子列表大小 (N) 和重新加载阈值 (N/2) 以在内存使用量、重新加载频率和“随机性”之间找到良好的折衷。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

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

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

          【讨论】:

          • 回顾这个答案,我意识到我没有回答被问到的问题。我离开它是因为它可以用来以一种相当直接的方式解决这类问题。
          猜你喜欢
          • 1970-01-01
          • 2015-11-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-09
          • 2021-04-08
          • 1970-01-01
          • 2012-12-14
          相关资源
          最近更新 更多