【问题标题】:Random selection随机选择
【发布时间】:2011-03-24 08:34:32
【问题描述】:

给定两个整数 N 和 n (N >= n > 0),我如何生成长度 = n 的 [0, N) 的随机选择(不重复!)? 例如。给定 N = 5,n = 3 可能的解决方案是 (3,0,2) 或 (2,4,1) 等。

有一个限制阻止使用幼稚的方法:内存使用量必须是 O(n),而不是 O(N)。

/* 在幼稚的方法下,我的意思是使用 size=N 的临时数组,该数组最初按顺序填充数字 0..N-1。从这个数组中随机选择所需的 n 个项目。 */

【问题讨论】:

标签: algorithm random


【解决方案1】:

遍历从0到N的所有数字m,决定是否将m包含在遇到的集合中。您需要根据已处理的数字更新包含下一个数字的概率。

让我们将这个想法应用到给出的示例中,n=3 和 N=5。首先考虑m=0。剩下 3 个数字和 5 种可能性,所以 0 在集合中的概率为 3/5。使用随机数生成器来决定是否包含该数字。现在考虑 m=1。如果您在集合中包含 0,那么您还有 2 个数字和 4 个可能性,因此应该以 2/4 的概率包含它,但如果不包含 0,那么您还有 3 个数字和 4 个可能性,因此应该包含 1概率为 3/4。这将一直持续到所需的 3 个数字包含在集合中。

这是 Python 中的一个实现:

from __future__ import division
import random

def rand_set(n, N):
    nums_included=set()
    for m in range(N):
        prob = (n-len(nums_included)) / (N-m)
        if random.random() < prob:
            nums_included.add(m)
    return nums_included

您可以(并且可能应该)添加一个测试,以查看您的集合中何时有足够的数字,并尽早跳出循环。

数字存储在一个集合中,大小从 0 到 n 不等,所以使用的存储是O(n)。其他所有内容都使用恒定空间,因此总体上是O(n)。

编辑实际上,您可以使用这种方法走得更远一些,这样它就会占用恒定的空间。在 Python 中,只需根据上述内容制作一个生成器即可:

def rand_set_iter(n, N):
    num_remaining = n
    m = 0
    while num_remaining > 0:
        prob = num_remaining / (N-m)
        if random.random() < prob:
            num_remaining -= 1
            yield m
        m += 1

在这里,我继续使用 while 循环而不是 for 循环。要存储结果,您当然需要使用O(n) 空间。但是,如果您需要做的只是遍历数字,生成器版本会在 O(1) 中完成。

对于没有生成器的语言,您可以滚动自己的生成器,重复调用函数并更新静态或全局变量。

【讨论】:

  • 优秀。绝对是要走的路。
  • 我相信这叫做水库采样。
  • @Moron 我从来不知道这种方法的名称。谢谢!
  • 玛丽阿姨的回答。在各个方面都几乎完美。
【解决方案2】:

简单(但可能非常低效)的解决方案只是通过重复选择所需范围内的值来构建一个列表,并检查您是否已经选择了它。这有一个无限的最大时间,因为你可能总是不小心捡到你已经捡到的东西。

我对 O(n2) 解决方案有一个模糊的概念,该解决方案在每次迭代中都会选择 [0, N - i) 范围内的一个值,其中 i 是您已经获得的元素数量... 接着 将新值映射到[0, N) 范围内,方法是遍历现有的选取元素,如果您发现已经获得的值小于或等于您选取的值,则添加 1。您需要仔细考虑,但这实际上是我要研究的方法。

【讨论】:

  • 即使您的简单解决方案也很不错。如果 n
【解决方案3】:

在 python 中,这真的很容易:

selection = random.shuffle(range(N))[:n]

这是内存中的 O(N),因为有效值列表是首先生成的,然后在原地打乱,所以它不符合您的要求:(

你可以试试这样的:

N = 5
n = 3
selection = set()
while len(selection) < n:
    selection += pick_random_int(0, N)

这基本上是 Jon Skeet 提出的。这适用于 n

【讨论】:

    【解决方案4】:

    将区间 [0,N] 划分为 n 个区间。从每个间隔中选择一个随机数,然后随机化结果。问题是在这种情况下分布不均匀。

    【讨论】:

      猜你喜欢
      • 2019-07-30
      • 1970-01-01
      • 2017-07-14
      • 2011-06-30
      • 2011-04-16
      • 1970-01-01
      • 1970-01-01
      • 2023-01-04
      • 1970-01-01
      相关资源
      最近更新 更多