我认为基本任务是从数组中随机选择“事件”(对象)os
,其频率由它们各自weight
的 s 定义。该方法是将随机数(具有均匀分布)映射(即搜索)到阶梯累积概率分布函数上。
使用正权重,它们的累积和从 0 增加到 1。您给我们的代码只是从 0 端开始搜索。为了最大限度地提高重复调用的速度,预先计算总和,并对事件进行排序,以便最大的权重在前。
无论您使用迭代(循环)还是递归进行搜索都无关紧要。递归在试图“纯函数式”但无助于理解潜在数学问题的语言中很好。它并不能帮助您将任务打包成一个干净的函数。函数是打包迭代的underscore
另一种方式,但不会更改基本功能。只有在找到目标时才any
提前all
退出。
对于小os
数组,这个简单的搜索就足够了。但是对于大数组,二分查找会更快。看着underscore
我发现sortedIndex
使用这种策略。从Lo-Dash
(一个underscore
dropin),“使用二分搜索来确定应该将值插入数组的最小索引,以保持排序数组的排序顺序”
的基本用途sortedIndex
是:
os = [{name:'one',weight:.7},
{name:'two',weight:.25},
{name:'three',weight:.05}]
t=0; cumweights = (t+=o.weight for o in os)
i = _.sortedIndex(cumweights, R)
os[i]
您可以使用嵌套函数隐藏累积和计算,例如:
osEventGen = (os)->
t=0; xw = (t+=y.weight for y in os)
return (R) ->
i = __.sortedIndex(xw, R)
return os[i]
osEvent = osEventGen(os)
osEvent(.3)
# { name: 'one', weight: 0.7 }
osEvent(.8)
# { name: 'two', weight: 0.25 }
osEvent(.99)
# { name: 'three', weight: 0.05 }
在 coffeescript 中,Jed Clinger 的递归搜索可以这样写:
foo = (x, r, t=0)->
[y, x...] = x
t += y
return [y, t] if x.length==0 or t>r
return foo(x, r, t)
使用相同基本思想的循环版本是:
foo=(x,r)->
t=0
while x.length and t<=r
[y,x...]=x # the [first, rest] split
t+=y
y
对 jsPerf http://jsperf.com/sortedindex的测试
表明,在 1000 左右sortedIndex
时更快os.length
,但在长度更像 30 时比简单循环慢。