【问题标题】:Can I choose a random element from a set if I don't know the size of the set?如果我不知道集合的大小,我可以从集合中选择一个随机元素吗?
【发布时间】:2011-06-08 11:05:02
【问题描述】:

我正在编写一些 JavaScript 代码,如果该项目满足某些要求,它应该从画布中选择一个随机项目。有不同种类的物品(圆形、三角形、正方形等),每种物品的数量通常不一样。这些项目按层次排列(因此一个正方形可以包含几个圆圈,一个圆圈可以包含其他圆圈等等 - 它们都可以嵌套)。

现在,我选择随机项目的(原始)方法是:

  1. 递归遍历画布并构建一个(可能很大!)项目列表
  2. 随机播放列表
  3. 从前面迭代打乱的列表,直到找到满足一些额外要求的项目。

问题在于它不能很好地扩展。我经常遇到内存问题,因为递归深度太高或项目的总列表变得太大。

我正在考虑重写此代码,以便在遍历画布时考虑选择元素 - 但如果我不知道总共有多少元素,我不知道如何“随机”选择一个元素.

有人知道如何解决这个问题吗?

【问题讨论】:

  • 你想要“真正的”随机性,还是“足够随机”的元素?
  • 不。如果你确实管理了,那么你应该知道最后集合的大小:)
  • @Alexandre:它只需要足够随机——我不需要完美的分布。
  • @Alexandre C. 为什么不需要创建列表?
  • 大家描述的叫做水库采样:en.wikipedia.org/wiki/Reservoir_sampling(虽然维基百科的文章不太好)

标签: javascript algorithm random graph-theory


【解决方案1】:

max_r = -1rand_node = null 开头。遍历树。对于每个满足条件的节点:

r = random()
if r > max_r:
  rand_node = node
  max_r = r

最后rand_node 将是一个随机选择的节点,只需要一次迭代。

【讨论】:

  • 这段代码能保证均匀分布吗?我的直觉说它会,但我无法分析它,因为该算法正在继承上一次迭代的状态。
  • @Andreas 这相当于将 N 个不同的值(冲突的可能性很小)随机映射到 N 个元素,然后选择最大的一个。也许这样更容易看到它?
  • 是的,这是一个很好的解释!但是如果多个元素映射到同一个(伪)随机值,会影响分布吗?
  • @Andreas 它会通过选择最先出现的元素来影响它,但是这种冲突的可能性是如此之小,以至于一个好的 RNG 更为重要。
  • +1:我喜欢这个解决方案有多短。它适用于我的情况。
【解决方案2】:

您无需先创建列表即可执行此操作(对不起,我的 C 伪代码)

int index = 0;
foreach (element)
{
    if (element matches criteria)
    {
        index++;
        int rand = random value in [1 index] range
        if (1 == rand)
        {
            chosen = element;
        }
    }
}

数学计算出来,假设您有一个列表,其中 3 个元素符合条件,第一个元素被选中的概率是:

1 * (1 - 1 / 2) * (1 - 1 / 3) = 1 * (1 / 2) * (2 / 3) = 1 / 3

第二个被选中的概率是:

(1 / 2) * (1 - 1 / 3) = (1 / 2) * (2 / 3) = 1 / 3

最后是第三个元素

1 / 3

哪个是正确的答案。

【讨论】:

  • 我可能误读了您的伪代码,但这不会总是选择符合条件的第一个元素吗?在第一个匹配元素上,index 变为 1,因此 rand 变为 random value in [1 1] range - 这将是 1,不是吗?
  • 是的,但是之后你继续迭代,这意味着chosen 可以被另一个元素替换。见概率分析。 marcog 的回答也可能是正确的,但分析起来并不容易。
  • 啊,我明白了!非常好的答案 - 我可以看到(以及为什么!)这是如何工作的。 :-)
【解决方案3】:

你可以递归地这样做:

  1. 遍历当前树层,创建列表
  2. 从中选择一个随机节点(使用从0到list.Length的随机数)
  3. 重复直到到达叶节点

这将使小子树中的项目被选中的概率更高。

或者,您不需要建立一个列表,您只需要跟踪项目的数量。这意味着第二次遍历树以到达所选项目,但您不需要为列表提供额外的内存。

【讨论】:

    猜你喜欢
    • 2010-09-12
    • 2013-06-01
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    • 2012-08-28
    • 1970-01-01
    • 2013-03-28
    • 1970-01-01
    相关资源
    最近更新 更多