【问题标题】:Looking for an algorithm to spit out a sequence of numbers in a (pseudo) random order寻找一种算法以(伪)随机顺序吐出一系列数字
【发布时间】:2010-10-18 11:26:18
【问题描述】:

假设我有一个数字序列: {n, n+1, n+2, ... n + m}

在不提前存储数字的情况下,我想创建一个函数 f(),它给定序列 {1,2,3,...m} 将随机(或至少伪随机)顺序。

例如假设我的序列是 {10, 11, 12, 13, 14, 15, 16, 17}

f(1) 可以产生 14 f(2) 可以产生 17 f(3) 可以产生 13 f(4) 可以产生 10 f(5) 可以产生 16 f(6) 可以产生 15 f(7) 可以产生 11 f(8) 可以产生 12

在过去的某个时刻,一位同事向我展示了一种能够做到这一点的数学算法,但从那以后,我几乎忘记了它存在的所有内容。我记得你必须事先有序列,并从函数中使用的序列中生成一些常量。对于那些想知道的人,我很遗憾与那位同事失去了联系。

这个question's 的答案看起来接近我想要的,但我不确定这些答案是否允许我提前将输出限制为特定序列。


编辑:

为了澄清一点,我不想存储原始序列或打乱的序列。我想从原始序列中生成一个函数 f()。

令人沮丧的是我已经看到了这个,我只是记不住它,无法用谷歌再次找到它。

Fisher-Yates 算法非常适合置换或洗牌,但这不是我想要的。

【问题讨论】:

  • 您能否澄清“不提前存储数字”的意思?如果集合没有存储在某处,函数如何知道有效集合?您的意思是您的集合将始终由起始编号和计数定义吗?还是不应该提前存储随机输出?
  • 对“m”有什么限制吗? (例如 2 的幂?素数?)
  • (或保证 m 为“小”如 65536)
  • 看看这个维基百科条目:en.wikipedia.org/wiki/Linear_congruential_generator。注意关于生成给你一个完整周期的数字的部分。请注意,对于 m 的某个值(您的序列大小),数学并不适合您生成正确的 LCG。
  • @Jason S:m的大小没有限制。

标签: algorithm language-agnostic math sequence


【解决方案1】:

有一个简单的函数可以为给定的m 生成[0..m-1] 的排列。只需选择一个数字k,与m 互质,然后让f(i)=(k*i) mod m。这总是会产生一个排列(0<=i<m 上没有重复)。如果k 大于m,效果会更好。

例如m=20,设k=137(Python代码,%表示取模):

 >>> [(137*i) % 20 for i in range(20)]
 [0, 17, 14, 11, 8, 5, 2, 19, 16, 13, 10, 7, 4, 1, 18, 15, 12, 9, 6, 3]

这是一个非常简单的 PRNG,不能保证它的统计特性。

【讨论】:

  • k 和 m 需要互质。考虑一下如果 m=4 和 k=8 会发生什么...为 k 选择一个严格的素数将确保这一点。
  • Ekhm,这不是我写的吗? “选择一个数 k,与 m 互质”。
  • 这根本不是随机的。你的模式和上面的“减 3 (mod 20)”是一样的。其实 k 和 k mod m 是全等的,所以在上面,k ~= -3。
  • 这些年来这对我非常有用。我希望我能再次投票。
【解决方案2】:

这个问题类似于洗一副 (m + 1) 张牌,编号为 [n, ..., n + m]。请注意编号(因此n)并不重要;重要的是我们可以将卡片区分开来。 (如果需要,您可以稍后简单地添加 n。)

要做你想做的事,你可以执行Fisher-Yates shufflejust keep track of which indices have been selected 进行到目前为止的洗牌。这将允许您避免根据要求存储值本身的另一个副本。

【讨论】:

    【解决方案3】:

    您的问题有点令人困惑,因为听起来您想取回所有原始序列,但是您将 4 和 8 都映射到 10,而没有映射到 12。

    如果您实际上是想进行 1:1 映射,那么您正在寻找的是原始集合的随机排列。有一些方法可以做到这一点,无论是否先收集集合(但您需要生成它的东西,或者跟踪您所在的位置)。

    另外,请注意 n 并不重要。您始终可以使用 0,1,2,...m,然后根据需要将 n 添加到所有内容中。

    假设我已经正确解释了这一点,并且您实际上正在寻找一种洗牌算法(即随机排列,类似于洗牌一副牌称为洗牌),请查看Fisher-Yates

    [编辑] 好的,根据您的更新,您面临的问题是:您不想显式编码排列,但您必须以某种方式对其进行编码才能构造 f.最简单的方法是将排列后的索引实际存储在一个数组中,但如果您出于某种原因(例如太大)不想这样做,您可以以各种方式对其进行编码。但是,没有免费的午餐,因为信息理论限制了这有多简单。无论如何,您可以通过查找有关“编码排列”的工作获得一些想法,例如 this paper

    【讨论】:

    • 我确实打算进行一对一映射。我编辑了这个问题以反映这一点。很抱歉造成混乱。
    【解决方案4】:

    这是我自己编造的一些伪代码:

    function f(array a)
        array newArray
        while a.size() == 0
            int position = randomNumber(1 to a.size())
            int removedNumber = a[position]
            a.remove(position)
            newArray.insertAtEnd(removedNumber)
        end while
        return newArray
    

    【讨论】:

    • 他说他不想存储数字——传入包含数字的数组是作弊,不是吗?
    【解决方案5】:

    将初始值添加到列表中。
    然后,使用随机数在列表当前大小的范围内选择一个新的索引值。
    使用该索引来选择然后从列表中删除该数字。

    正如有人已经指出的那样,这类似于拥有一副牌,然后一次随机取出一张牌。

    【讨论】:

      【解决方案6】:

      如果您想要 1:1 映射,请使用其他答案中提到的 Fisher-Yates。

      如果您不关心 1:1 映射,并且您只需要所有结果值都来自给定序列(可能有重复),那么您可以使用具有指定范围的随机函数。

      例如,在 C++ 中,您可以通过以下方式使用 rand() -

      result = rand() % (m+1) + n
      

      所以对于你的例子,

      result = rand() % 8 + 10
      

      将产生一个介于 10 和 17 之间的整数。

      【讨论】:

        【解决方案7】:

        你可以fit a polynomial到选中的序列;我猜这就是你的同事给你看的。不过,与仅记住排列相比,它不会节省空间。

        【讨论】:

        • 最终函数相对简单,不需要长多项式(至少我记得是这样)。我们在序列中有大约一百万个数字,我认为函数中只有几个项。
        【解决方案8】:

        您可以按照my previous answer 使用分组密码和异或折叠来生成前 n 个整数的排列。

        【讨论】:

          【解决方案9】:

          如果不将原始函数的结果存储在某处,就不可能返回值。推理:

          您的随机数生成器告诉您从原始序列中返回这些值:第 5、第 11、第 3。

          所以你跳过前四个值,返回第 5 个,跳过另一个 5,返回第 11 个……现在如何返回第 3 个而不将其保存在某个地方?

          最接近您可以逃脱的事情是创建一个列表并附加您跳过的所有值,但这听起来很尴尬并且可能不值得努力。此外,在混洗算法返回一个非常大然后非常小的值的情况下,它会非常慢(在这种情况下,您首先会有效地将大多数值复制到列表中,这是您想要避免的)。

          我休息一下。

          【讨论】:

            【解决方案10】:

            您的输入由f(x) => x + 9 描述,或更一般的f(x) => n - 1 + x 描述为x 从1 开始。

            您链接到 another question,它描述了一个函数 r(x),它将 x 映射到一个随机值 0 <= r(x) <= m

            所以f(r(x) + 1)(r(x) + n)应该给你你想要的价值。

            对于小的m,您还应该能够通过跟踪和错误找到标准随机数生成器的种子,然后在使用 mod m+1 时生成 m+1 不同的值,如果您不想编码您的自己的发电机。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-03-11
              • 2011-11-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多