假设我有一个名为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
。)
答案 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))
对我来说,这使得你想要做的更清楚,并且是自我记录的。最重要的是,它更简单,更优雅,现在很明显,每个都会以均匀的分布进行挑选。