【问题标题】:Exhaustive random number generator详尽的随机数生成器
【发布时间】:2012-01-21 13:44:11
【问题描述】:

在我的一个项目中,我遇到了在给定范围内生成一组数字的需求,这些数字将是:

  • 详尽无遗,这意味着它将涵盖给定的大部分内容 范围没有任何重复。

  • 它将保证确定性(每次序列都是 相同的)。这可以通过固定种子来实现。

  • 它将是随机的(我不是很精通随机数理论,但我猜有一堆描述随机性的规则。从角度来看,像 0,1,2..N 不是随机的) .

我所说的范围可以是整数范围,也可以是实数范围。

例如,如果我使用标准 C# 随机生成器在 [0, 9] 范围内生成 10 个数字,我会得到:

0 0 1 2 0 1 5 6 2 6  

如您所见,给定范围的很大一部分仍然“未探索”并且有很多重复。

当然,输入空间可能非常大,因此记住以前选择的值不是一种选择。

解决这个问题的正确方法是什么?

谢谢。

在cmets之后: 好的,我同意随机不是正确的词,但我希望你理解我想要实现的目标。我想探索可能很大的给定范围,因此在内存列表中不是一个选项。如果一个范围是 (0, 10) 并且我想要三个数字,我想保证这些数字会不同并且它们将“描述范围”(即它们不会都在下半部分等)。

确定性部分意味着我想使用带有固定种子的标准 rng 之类的东西,所以我可以完全控制序列。

我希望我把事情说得更清楚了。

谢谢。

【问题讨论】:

  • 随机就是随机,不想重复就不要随机
  • 对于从 1 到 10 的十个 真正 随机数,您预计大约有三个重复数和大约三个缺失数。如果您不想允许重复,则不需要真正的随机数。谷歌的生日悖论。
  • determinismrandom 不能一起使用。随机意味着你无法预测它,而确定性意味着知道完整的前一个“周期”你可以预测它。
  • 您可以轻松地生成“不重复的随机数”,其数组长度为n 和“惰性随机播放”(即从“not-数组的“已看到”部分与“未看到”部分的最后一个元素,将“未看到”部分缩小一)
  • @ranty 好的,但伪随机和确定性可以一起使用 :)

标签: algorithm random


【解决方案1】:

以下是三种权衡不同的选项:

  1. 提前生成数字列表,并使用fisher-yates shuffle 将它们随机排列。根据需要从列表中选择。 O(n) 总内存,每个元素 O(1) 时间。随机性与您用来进行随机播放的 PRNG 一样好。也是三种选择中最简单的一种。
  2. 使用Linear Feedback Shift Register,它将在其序列中的每个值在重复之前恰好生成一次。 O(log n) 总内存,每个元素 O(1) 时间。但是,根据现值很容易确定未来值,并且 LFSR 最容易构建为 2 个周期的幂(但您可以选择 2 的下一个最大幂,并跳过任何超出范围的值)。
  3. 使用secure permutation based on a block cipher。可用于 2 周期的任何幂,并带有一点额外的技巧,任意周期。 O(log n) 总空间和每个元素 O(1) 时间,随机性与分组密码一样好。三者中最复杂的实现。

【讨论】:

    【解决方案2】:

    如果你只是需要一些东西,那么这样的东西呢?

    maxint = 16
    step = 7
    sequence = 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9, 0
    

    如果选择正确的步骤,它将在重复之前生成整个间隔。您可以使用不同的 step 值来获得“看起来”不错的东西。这里的“种子”是您在序列中开始的地方。

    这是随机的吗?当然不是。根据随机性的统计测试,它看起来是随机的吗?这可能取决于步骤,但很可能这在统计上看起来根本不是随机的。但是,它肯定会选择范围内的数字,而不是按原始顺序,并且没有任何记忆到目前为止所选择的数字。

    事实上,您可以通过列出一系列因素来使这个看起来更好 - 例如 [1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16] - 并使用它们的改组版本来计算 step * factor (mod maxint)。假设我们对示例因子列表进行了洗牌,例如 [3, 2, 4, 5, 1], [6, 8, 9, 10, 7], [13, 16, 12, 11, 14, 15]。然后我们会得到序列

    5, 14, 12, 3, 7, 10, 8, 15, 6, 1, 11, 0,  4, 13, 2, 9
    

    因素列表的大小是完全可调的,因此您可以存储任意数量的内存。更大的因素列表,更多的随机性。无论因子列表大小如何,都不会重复。当您用尽一个因子列表时,生成一个新的因子列表就像计数和改组一样简单。

    【讨论】:

    • “正确”步长是列表长度的相对质数。如果您要选择一个更大的素数并随机选择它,它可能是相当随机的。
    【解决方案3】:

    我的印象是,您正在寻找的是一个随机排序的数字列表,而不是一个随机的数字列表。您应该能够使用以下伪代码获得此信息。更好的数学家可能会告诉我这是否真的不是随机的:

    list = [ 1 .. 100 ]
    for item,index in list:
      location = random_integer_below(list.length - index)
      list.switch(index,location+index)
    

    基本上,浏览列表并从列表的其余部分中选择一个随机项目以用于您所在的位置。这应该随机排列列表中的项目。如果您每次都需要重现相同的随机顺序,请考虑保存数组,或以某种方式确保 random_integer_below 在给定一些种子的情况下始终以相同的顺序返回数字。

    【讨论】:

    • 范围可能很大,因此在内存列表中不是一个选项。我希望得到一些可以使用恒定内存的东西。
    • 仅供参考,此算法称为Fisher–Yates shuffle 或 Knuth shuffle。
    • @SINTER,我不相信在这种情况下你可以吃蛋糕也可以吃 - 随机数序列在短时间内两次命中相同数字的原因之一是因为它们对序列中的其他数字一无所知。
    • @SINTER 您希望您的号码了解他们周围的情况,因此您必须使用更详细的方案修改我的原始 list=[1..100] 。假设您想要 1..100000 范围内的 100 个项目,然后您选择 100 个平均为 1000 的数字到列表 I 中,并制作一个简单地添加它们的列表 (I[0],I[0]+I[1 ],I[0]+I[1]+I[2])。这应该会为您提供 1 到 100000 之间的随机、不同、大约均匀间隔的数字,然后您可以对该列表进行 Knuth 随机播​​放。
    【解决方案4】:

    按顺序生成包含范围的数组。所以数组包含[0, 1, 2, 3, 4, 5, ... N]。然后使用Fisher-Yates Shuffle 打乱数组。然后,您可以遍历数组以获取随机数。

    如果您需要可重复性,请在随机播放开始时为您的随机数生成器播种相同的值。

    【讨论】:

      【解决方案5】:

      不要使用随机数生成器来选择范围内的数字。最终会发生的是,您还有一个数字要填充,而您的随机数生成器将重复循环,直到它选择该数字。取决于随机数生成器,不能保证会发生。

      您应该做的是在所需范围内生成一个数字列表,然后使用随机数生成器来打乱该列表。洗牌被称为 Fisher-Yates 洗牌,有时也被称为 Knuth 洗牌。下面是一个伪代码,用于对包含 n 个元素的数组 x 进行洗牌,索引从 0 到 n-1:

      对于 in-1 到 1
      j = 随机整数,满足 0 ≤ ji
      交换 x[i] 和 x[j]

      【讨论】:

        猜你喜欢
        • 2023-01-03
        • 1970-01-01
        • 2015-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-18
        • 2017-04-05
        • 2011-01-24
        相关资源
        最近更新 更多