os
基になるタスクは、それぞれweight
の sで定義された頻度で配列から「イベント」(オブジェクト) をランダムに選択していると思います。このアプローチは、乱数 (一様分布) を階段状累積確率分布関数にマップ (検索) することです。
正の重みを使用すると、それらの累積合計は 0 から 1 に増加します。提供されたコードは、単純に 0 の終わりから検索を開始します。繰り返し呼び出しで速度を最大化するには、事前に合計を計算し、最大の重みが最初になるようにイベントを並べ替えます。
反復 (ループ) で検索するか、再帰で検索するかは、実際には問題ではありません。再帰は、「純粋に機能的」であろうとする言語では優れていますが、根底にある数学的問題の理解には役立ちません。また、タスクをクリーンな関数にパッケージ化するのにも役立ちません。関数は反復をパッケージ化する別のunderscore
方法ですが、基本的な機能は変更しません。ターゲットが見つかった場合にのみany
、all
早期に終了します。
小さなos
配列の場合、この単純な検索で十分です。ただし、配列が大きい場合は、バイナリ検索の方が高速になります。調べてみると、この戦略underscore
を使用していることがわかります。(ドロップイン)sortedIndex
から、「バイナリ検索を使用して、ソートされた配列のソート順を維持するために、配列に値を挿入する必要がある最小のインデックスを決定します」Lo-Dash
underscore
の基本的な使用法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 }
Jed Clinger の再帰的検索は、coffeescript では次のように記述できます。
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/sortedindexsortedIndex
でのテストでは、1000 前後の場合は高速os.length
ですが、長さが 30 に近い場合は単純なループよりも遅くなる ことが
示唆されています。