选择满足某些属性的随机数组元素

时间:2009-06-08 17:57:53

标签: algorithm arrays statistics probability

假设我有一个名为elements的列表,每个列表都满足或不满足某些布尔属性p。我想通过随机均匀分布选择满足p的元素之一。我不知道有多少项目满足此属性p

以下代码是否会这样做?:

pickRandElement(elements, p)
     randElement = null
     count = 0
     foreach element in elements
          if (p(element))
               count = count + 1
               if (randInt(count) == 0)
                    randElement = element

     return randElement

randInt(n)返回带有k的随机int 0 <= k < n。)

5 个答案:

答案 0 :(得分:14)

它以数学方式工作。可以通过归纳证明。

显然适用于满足p。

的n = 1个元素

对于n + 1个元素,我们将选择概率为1 /(n + 1)的元素n + 1,因此其概率为OK。但是,这会如何影响选择前n个元素之一的最终概率?

先前的n中的每一个都有可能以1 / n的概率被选中,直到我们找到元素n + 1。现在,在找到n + 1之后,存在1 /(n + 1)的机会,即元素n + 1被选择,因此存在先前选择的元素保持被选择的n /(n + 1)机会。这意味着它在n + 1找到之后被选择的最终概率是1 / n *(n / n + 1)= 1 / n + 1 - 这是我们想要所有n + 1个元素进行均匀分布的概率。

如果它适用于n = 1,并且它适用于给定n的n + 1,那么它适用于所有n。

答案 1 :(得分:6)

是的,我相信。

第一次遇到匹配的元素时,你肯定会选择它。下一次,您以1/2的概率选择新值,因此两个元素中的每一个都有相同的机会。下一次,您以1/3的概率选择新值,同时保留其他每个元素的概率为1/2 * 2/3 = 1/3。

我正在尝试查找有关此策略的维基百科文章,但到目前为止失败了......

请注意,更一般地说,您只是从未知长度的序列中挑选一个随机样本。您的序列恰好通过采用初始序列并对其进行过滤来生成,但算法根本不需要该部分。

我以为我在MoreLINQ中有一个LINQ运算符来执行此操作,但我无法在存储库中找到它...编辑:幸运的是,它仍然存在于this answer

public static T RandomElement<T>(this IEnumerable<T> source,
                                 Random rng)
{
    T current = default(T);
    int count = 0;
    foreach (T element in source)
    {
        count++;
        if (rng.Next(count) == 0)
        {
            current = element;
        }            
    }
    if (count == 0)
    {
        throw new InvalidOperationException("Sequence was empty");
    }
    return current;
}

答案 2 :(得分:3)

编程实践,pg。 70,(马尔可夫链算法)有一个类似的算法:

[...]
  nmatch = 0;
  for ( /* iterate list */ )
    if (rand() % ++nmatch == 0) /* prob = 1/nmatch */
      w = suf->word;
[...]
  

“注意选择一个的算法   我们不知道如何随机的项目   有很多项目。变量   nmatch将匹配数计为   列表被扫描。表达式

rand() % ++nmatch == 0
     

增加nmatch然后为真   概率为1 / nmatch。“

答案 3 :(得分:1)

decowboy有一个很好的证据证明这适用于TopCoder

答案 4 :(得分:0)

为了清楚起见,我会尝试:

pickRandElement(elements, p)
     OrderedCollection coll = new OrderedCollection
     foreach element in elements
          if (p(element))
               coll.add(element)
     if (coll.size == 0) return null
     else return coll.get(randInt(coll.size))

对我来说,这使得你想要做的更清楚,并且是自我记录的。最重要的是,它更简单,更优雅,现在很明显,每个都会以均匀的分布进行挑选。