【问题标题】:Generate a number is range (1,n) but not in a list (i,j)生成一个范围为 (1,n) 但不在列表 (i,j) 中的数字
【发布时间】:2013-07-12 17:50:11
【问题描述】:

如何生成在(1,n) 范围内但不在特定列表(i,j) 中的随机数?

示例:范围为(1,500),列表为[1,3,4,45,199,212,344]

注意:列表可能没有排序

【问题讨论】:

  • 我假设您希望它高效而不是只生成数字直到它不在您的列表中?
  • “列表可能未排序”——我正在阅读此“未排序”而不是“您可能未对列表进行排序”。指定可能的实现语言可能会有所帮助。 Python 和 C++ 设置了有用的功能,例如
  • 如果 n 很小,您可以创建一个元素列表,例如,数组将是 {2,5,6,7..44,46,..500} (您明白了),然后在您的示例中生成一个随机索引,如 rand(493) 。并从 array[rand(493)] 中获取元素。

标签: algorithm math random


【解决方案1】:

Rejection Sampling

一种方法是拒绝抽样:

  1. 在 (1, 500) 范围内生成一个数字 x
  2. x 在您的不允许值列表中吗? (可以使用哈希集进行此检查。)
    • 如果是,返回步骤 1
    • 如果不是,x 是你的随机值,完成

如果您的允许值集明显大于您的不允许值集,这将正常工作:
如果有 G 可能的好值和 B 可能的坏值,那么您的预期次数'必须从G + B 值中抽取x,直到你得到一个好的值是(G + B) / G(相关几何分布的期望值)。 (您可以感觉到检查。当G 趋于无穷时,期望趋于1。当B 趋于无穷时,期望趋于无穷。)

对列表进行抽样

另一种方法是列出所有允许值的L,然后采样L[rand(L.count)]

【讨论】:

  • 蒂莫西列出了两个正确的标准答案。当范围远大于不允许的值列表(并且不方便存储在内存中)时,拒绝采样是正常的解决方案。当允许值的列表很容易存储在内存中时,对列表进行采样的方法是最佳的。
【解决方案2】:

当列表长度为 1 时,我通常使用的技术是生成随机 [1,n-1] 中的整数 r,如果 r 大于或等于该单个非法 值然后递增r

这可以概括为长度为k 的列表为小k 但需要 对该列表进行排序(您不能以随机顺序进行比较和增量)。如果列表的长度适中,那么在排序之后您可以从 bsearch 开始,并将跳过的值的数量添加到 r,然后递归到列表的其余部分。

对于长度为k 的列表,不包含大于或等于n-k 的值,您 可以做更直接的替换:在[1,n-k]中生成随机r,和 然后遍历列表测试r 是否等于list[i]。如果是 然后将r 设置为n-k+i(假设list 是从零开始的)并退出。

如果某些列表元素位于 [n-k,n] 中,则第二种方法会失败。

在这一点上,我可以尝试投资一些聪明的东西,但我目前所拥有的 对于k 的值远小于的均匀分布似乎足够了 n...

  1. 创建两个列表 - 一个低于 n-k 的非法值,另一个是其余的(可以就地完成)。
  2. [1,n-k]中生成随机r
  3. 对第一个列表应用直接替换方法(如果 rlist[i] 则将 r 设置为 n-k+i 并转到步骤 5)。
  4. 如果r 在第 3 步中没有更改,那么我们就完成了。
  5. 对较大值的列表进行排序并使用比较和递增方法。

观察:

  • 如果所有值都在下方列表中,则不会进行排序,因为没有要排序的内容。
  • 如果所有值都在上面的列表中,则不会进行排序,因为没有将r 移动到危险区域的情况。
  • 随着k 接近n,上层(已排序)列表的最大大小会增加。
  • 对于给定的k,如果上列表中出现的值越多(排序越大),在下列表中获得命中的机会就会减小,从而降低需要进行排序的可能性。

细化: 显然,对于大型k,事情变得非常复杂,但在这种情况下,列表中允许r 解决的漏洞相对较少。这肯定会被利用。

如果许多随机值相同,我可能会提出不同的建议 需要清单和限制。我希望非法值列表不是 先前调用此函数的结果列表,因为如果是,那么您 不会想要任何这些——相反,你会想要一个 Fisher-Yates 洗牌。

【讨论】:

    【解决方案3】:

    如前所述,如果可能,拒绝抽样将是最简单的。但是,如果您不想使用它,您可以将范围和不允许的值转换为集合并找出差异。然后,您可以从中选择一个随机值。

    假设您希望范围在 [1,n] 但不在 [i,j] 中,并且您希望它们均匀分布。

    在 Python 中

    total = range(1,n+1)
    disallowed = range(i,j+1)
    allowed = list( set(total) - set(disallowed) )
    
    return allowed[random.randrange(len(allowed))]
    

    (请注意,这并不完全一致,因为很可能,max_rand%len(allowed) != 0 但在大多数实际应用中这将非常接近)

    【讨论】:

      【解决方案4】:

      我假设您知道如何在 [1, n) 中生成随机数,并且您的列表也像上面的示例一样排序。

      假设您有一个包含 k 个元素的列表。制作一个 map(O(logn)) 结构,如果 k 更高,它将确保速度。将列表中的所有元素放入映射中,其中元素值将是键,“好”值将是值。稍后我将解释“好”的价值。因此,当我们拥有地图时,只需在 [1, n - k - p) 中找到一个随机数(稍后我将解释什么是 p),如果该数字在地图中,则将其替换为“好”值。

      “GOOD”值 -> 让我们从第 k 个元素开始。它的好值是它自己的值 + 1,因为下一个元素对我们来说是“好”的。现在让我们看看第 (k-1) 个元素。我们假设它的好值再次是它自己的值 + 1。如果这个值等于第 k 个元素,那么第 (k-1) 个元素的“好”值是第 k 个“好”值 + 1。还有您将不得不存储最大的“好”值。如果最大值超过 n,则 p(从上面)将为 p = 最大 - n。

      当然,只有当 k 是大数时,我才建议你这样做,否则 @Timothy Shields 的方法是完美的。

      【讨论】:

        猜你喜欢
        • 2022-01-13
        • 1970-01-01
        • 2018-07-18
        • 1970-01-01
        • 2011-10-20
        • 1970-01-01
        • 1970-01-01
        • 2011-05-16
        • 1970-01-01
        相关资源
        最近更新 更多