Javascript 如果我不知道集合的大小,我可以从集合中选择一个随机元素吗?

Javascript 如果我不知道集合的大小,我可以从集合中选择一个随机元素吗?,javascript,algorithm,random,graph-theory,Javascript,Algorithm,Random,Graph Theory,我正在编写一段JavaScript代码,如果项目满足某些要求,它应该从画布中选择一个随机项目。有不同种类的项目圆,三角形,正方形等,通常有不同数量的项目为每一种。这些项目是按层次排列的,因此一个正方形可以包含几个圆,一个圆可以包含其他圆,等等——它们都可以嵌套 现在,我选择随机项目的基本方法是: 递归地遍历画布并构建一个可能巨大的!项目清单 洗牌 从前面迭代洗牌列表,直到找到满足一些额外要求的项。 问题是它不能很好地扩展。我经常遇到内存问题,因为递归深度太高或者项目的总列表太大 我正在考虑重写这

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

现在,我选择随机项目的基本方法是:

递归地遍历画布并构建一个可能巨大的!项目清单 洗牌 从前面迭代洗牌列表,直到找到满足一些额外要求的项。 问题是它不能很好地扩展。我经常遇到内存问题,因为递归深度太高或者项目的总列表太大

我正在考虑重写这段代码,以便考虑在遍历画布时选择元素——但我不知道如何随机选择元素,如果我不知道总共有多少元素。


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

你可以递归地这样做:

遍历当前树级别,创建列表 使用从0到列表的随机数从中选择一个随机节点。长度 重复此操作,直到到达叶节点 这将使小子树中的项目被选中的概率更高


或者,您不需要构建列表,只需要跟踪项目的数量。这意味着要第二次遍历树以访问所选项目,但列表不需要额外的内存。

您无需首先创建列表即可完成此操作。对不起,我的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
这是正确答案。

从max\u r=-1和rand\u node=null开始。遍历树。对于满足条件的每个节点:

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

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

您想要真正的随机性,还是足够的随机元素?否。如果你真的做到了,那么你应该在最后知道集合的大小:@Alexandre:它确实需要足够的随机性-我不需要一个完美的分布。@Alexandre C.为什么要在不需要的时候创建一个列表呢?每个人都在描述的是所谓的水库采样:维基百科的文章不是很好,但我可能是误读了您的伪代码,但这不总是选择与条件匹配的第一个元素吗?在第一个匹配元素上,index变为1,所以rand变为[11]范围内的随机值-即1,否?是,但之后继续迭代,这意味着所选的可以被另一个元素替换。参见概率分析。马可的回答可能也是正确的,但分析起来并不容易。啊,我明白了!非常好的答案-我可以看到如何和为什么!这是有效的-此代码是否保证提供统一的分布?我的直觉告诉我会的,但我在分析它时遇到了困难,因为该算法继承了上一次迭代的状态。@Andreas它相当于随机映射N个不同的值碰撞的几率最小为N个元素,然后选择最大的一个。也许这样更容易看出来?是的,这是一个很好的解释!但是,如果几个元素映射到同一个伪随机值,这会影响分布吗?@Andreas它会通过选择第一个元素来影响分布,但是这种冲突的可能性非常小,以至于一个好的RNG更重要。+1:我喜欢这个解决方案有多短。这对我的案子来说已经足够好了。