假设您有三个"选项",A
,B
和C
。
您的算法必须选择并返回一个随机的算法。为此,将它们放入数组{A,B,C}
并生成一个随机数(0,1或2)非常简单,该数字将是要返回的数组中元素的索引。
现在,此算法有一个变体:假设A
被选中的几率为40%,B
为20%,C
为40%。如果是这种情况,您可以采用类似的方法:生成数组{A,A,B,C,C}
并使用随机数(0,1,2,3,4)来选择要返回的元素。
有效。但是,我觉得这是非常低效的。想象一下,将此算法用于大量数量的选项。您将创建一个有点大的数组,可能有100个元素,每个元素代表1%。现在,这仍然不是很大,但假设你的算法每秒使用很多次,这可能很麻烦。
我考虑过制作一个名为Slot
的课程,该课程有两个属性:.value
和.size
。为每个选项创建一个插槽,其中.value
属性是选项的值,.size
一个等于数组中此类选项的出现次数。然后生成一个从0到总发生次数的随机数,并检查该数字落在哪个插槽上。
我更关心这个算法,但这是我对Ruby的尝试:
class Slot
attr_accessor :value
attr_accessor :size
def initialize(value,size)
@value = value
@size = size
end
end
def picker(options)
slots = []
totalSize = 0
options.each do |value,size|
slots << Slot.new(value,size)
totalSize += size
end
pick = rand(totalSize)
currentStack = 0
slots.each do |slot|
if (pick <= currentStack + slot.size)
return slot.value
else
currentStack += slot.size
end
end
return nil
end
50.times do
print picker({"A" => 40, "B" => 20, "C" => 40})
end
哪个输出:
CCCCACCCCAAACABAAACACACCCAABACABABACBAAACACCBACAAB
是否有更有效的方法来实现选择随机选项的算法,其中每个选项都有不同的被选择概率?
答案 0 :(得分:12)
最简单的方法可能是写一个案例陈述:
def get_random()
case rand(100) + 1
when 1..50 then 'A'
when 50..75 then 'B'
when 75..100 then 'C'
end
end
问题在于你无法传递任何选项,因此如果你想让它能够选择,你可以编写这样的函数。下面的内容非常类似于您编写的内容,但有点短:
def picker(options)
current, max = 0, options.values.inject(:+)
random_value = rand(max) + 1
options.each do |key,val|
current += val
return key if random_value <= current
end
end
# A with 25% prob, B with 75%.
50.times do
print picker({"A" => 1, "B" => 3})
end
# => BBBBBBBBBABBABABBBBBBBBABBBBABBBBBABBBBBBABBBBBBBA
# If you add upp to 100, the number represent percentage.
50.times do
print picker({"A" => 40, "T" => 30, "C" => 20, "G" => 10})
end
# => GAAAATATTGTACCTCAATCCAGATACCTTAAGACCATTAAATCTTTACT
答案 1 :(得分:6)
虽然这不是一个直接的答案,但我会向您展示一个帮助您概述此问题的来源:http://www.av8n.com/physics/arbitrary-probability.htm。
修改强>
刚刚在红宝石中找到了一个很好的来源,pickup gem。
require 'pickup'
headings = {
A: 40,
B: 20,
C: 40,
}
pickup = Pickup.new(headings)
pickup.pick
#=> A
pickup.pick
#=> B
pickup.pick
#=> A
pickup.pick
#=> C
pickup.pick
#=> C
答案 2 :(得分:6)
作为更高效算法的第一个近似值,如果计算累积分布函数(这只是分布函数的一次传递,计算运行总和),那么您可以使用a找到随机选择的整数的位置二进制搜索而不是线性搜索。如果您有很多选项,这将有所帮助,因为它将搜索时间从O(#options)减少到O(log #options)。
但是有一个O(1)解决方案。这是基本的大纲。
假设我们有N个选项,1...N
,权重为ω1...ωN
,其中所有ω值至少为0.为简单起见,我们缩放权重,使其均值为{{1}或者换句话说,他们的总和是1
。 (我们只是将它们乘以N
。我们实际上不必这样做,但是如果没有MathJax,它会使下一段更容易输入。)
现在,创建一个N/Σω
元素的向量,其中每个元素都有两个选项标识符(N
和lo
)和一个截止hi
。选项标识符只是整数p
,而1...N
将计算为p
范围内的实数。
我们继续按如下方式填写向量。对于每个元素(0, 1.0)
依次:
如果某些i
正好是ωj
,那么我们设置:
1.0
loi = j
hii = j
我们会从权重列表中删除 pi = 1.0
。
否则,必须有一些ωj
和一些ωj < 1.0
。 (那是因为平均重量是1.0,并且没有一个具有平均值。其中一些必须少一些,一些更多,因为所有元素都不可能大于平均值或所有元素都少于比平均值。)现在,我们设置:
ωk > 1.0
loi = j
hii = k
pi = ωj
我们再次从权重中移除 ωk = ωk - (1 - ωj)
。
请注意,在这两种情况下,我们都删除了一个重量,并且我们将权重之和减少了1.0。所以平均重量仍然是1.0。
我们继续这种方式,直到填满整个矢量。 (最后一个元素将有ωj
)。
鉴于此向量,我们可以选择加权随机选项,如下所示:
p = 1.0
范围内生成随机整数i
,在1...N
范围内生成随机浮点值r
。如果(0, 1.0]
,我们会选择选项r < pi
;否则,我们选择选项loi
。应该清楚为什么这可以从矢量的构造起作用。每个高于平均权重的选项的权重分布在各种向量元素中,而每个低于平均权重的选项被分配给某个向量元素的一部分,并具有相应的选择概率。
在实际实现中,我们将权重范围映射到整数值,并使总权重接近最大整数(它必须是hii
的倍数,因此会有一些晃动。然后我们可以选择一个槽并从一个随机整数中选择槽内的权重。实际上,我们可以修改算法以通过添加一些0加权选项来强制插槽的数量为2的幂来避免除法。因为整数运算不能很好地完成,所以需要稍微摆弄一下,但最终结果可以统计正确,模数正在使用的PRNG的特性,并且它将执行几乎与N
选项的简单未加权选择一样快(一个班次和几个比较额外),代价是占用少于N
个存储元素的向量(计算必须的可能性)几乎是插槽数量的两倍。