从具有加权概率的列表中随机选择

Posted

技术标签:

【中文标题】从具有加权概率的列表中随机选择【英文标题】:Randomly choosing from a list with weighted probabilities 【发布时间】:2013-06-19 11:45:05 【问题描述】:

我有一个由 N 个元素组成的数组(代表给定字母表的 N 个字母),并且该数组的每个单元格都保存一个整数值,该整数值表示该字母在给定文本中出现的次数。现在我想根据他在给定约束条件下出现的次数从字母表中的所有字母中随机选择一个字母:

如果字母有一个正值(非零),那么算法总是可以选择它(当然概率更大或更小)。

如果字母 A 的值高于字母 B,则算法必须更可能选择它。

现在,考虑到这一点,我想出了一个可以完成这项工作的简单算法,但我只是想知道是否有更好的方法可以做。这似乎是非常基本的,我认为为了更有效地完成这一点,可能需要做更多聪明的事情。这是我想的算法:

将数组中的所有频率相加。将其存储在 SUM 中 从 0 到 SUM 中选择一个随机值。将其存储在 RAN 中 [While] RAN > 0,从第一个开始,依次访问数组中的每个单元格,并从RAN中减去该单元格的值 最后访问的单元格是选择的单元格

那么,还有比这更好的事情吗?我错过了什么吗?

我知道大多数现代计算机的计算速度如此之快,如果我的算法效率低下,我什至不会注意到,所以这更多是一个理论问题,而不是一个实际问题。

我更喜欢解释算法,而不仅仅是代码,但如果您更愿意用代码提供答案,我对此没有任何问题。

【问题讨论】:

【参考方案1】:

想法:

遍历所有元素并将每个元素的值设置为迄今为止的累积频率。 生成一个介于 1 和所有频率之和之间的随机数 对该数字的值执行binary search(查找大于或等于该数字的第一个值)。

示例:

Element    A B C D
Frequency  1 4 3 2
Cumulative 1 5 8 10

生成一个1-10范围内的随机数(1+4+3+2 = 10,与累积列表最后一个值相同),进行二分查找,返回值如下:

Number   Element returned
1        A
2        B
3        B
4        B
5        B
6        C
7        C
8        C
9        D
10       D

【讨论】:

顺便说一下,这个方法叫做逆变换采样【参考方案2】:

Alias Method 为每个生成的值摊销了 O(1) 时间,但每次查找需要两个制服。基本上,您创建一个表,其中每列包含要生成的值之一、称为别名的第二个值以及在值及其别名之间进行选择的条件概率。使用您的第一个制服选择任何具有相同可能性的列。然后根据您的第二个制服在主要值和别名之间进行选择。最初为 n 个值建立一个有效的表需要 O(n log n) 的工作,但在表的构建后生成值是恒定的时间。可以下载this Ruby gem查看实际实现。

Marsaglia 等人的另外两种非常快速的方法。被描述为here。他们提供了C implementations。

【讨论】:

+1 Vose Alias 的插图,这里由 Kent Beck facebook.com/note.php?note_id=323786247654246 @aka.nice 在 Smalltalk 中也是如此。可爱! +1。感谢分享这个 - 我有一个项目,我使用了上面描述的二进制搜索方法,你的方法看起来是一个实质性的改进。阅读那篇 Marsaglia 论文,我是否正确地认为 Marsaglia 方法 II 本质上只是 Alias 方法?我很惊讶我的方法更快。我不认为你可以分享任何关于为什么会这样的直觉? 添加到 aka.nice 的评论中,Keith Schwarz 在keithschwarz.com/darts-dice-coins 有一个视觉解释(链接到 facebook 注释)这真的很酷! @pjs 我看到您将时间要求从 O(n) 更改为 O(n log n)。然而,飞镖、骰子和硬币声称 Vose Alias 在 O(n) 中运行,并假设 O(1) 为从集合中添加和移除而摊销,我同意。我是不是误会了?

以上是关于从具有加权概率的列表中随机选择的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Julia 的加权数组中选择一个随机项?

Python中的加权随机样本问题,怎么解决

生成具有概率的随机整数

随机优先与权重——非平均概率的选择工具

用于在随机加权选择之间进行选择的惯用 Clojure

MATLAB-如何随机选择具有更高概率的较小值?