これが私が見つけた別のアプローチです。これは高速ですが、単純に for ループで sample を何度も呼び出すほど高速ではありません。最初はとても良いと思っていましたが、benchmark() の使い方が間違っていました。
luke2 = function(probs) { # takes a matrix of probability vectors, each in its own row
probs <- probs/rowSums(probs)
probs <- t(apply(probs,1,cumsum))
answer <- rowSums(probs - runif(nrow(probs)) < 0) + 1
return(answer) }
確率を、0 から 1 までの数直線上に配置されたさまざまな長さの線として想像してください。大きな確率は、小さなものよりも数直線の多くを占めます。次に、数直線上のランダムなポイントを選択して結果を選択できます。確率が大きいほど、選択される可能性が高くなります。このアプローチの利点は、関数 luke、roman、roman2 のように sample を何度も呼び出す代わりに、runif() の 1 回の呼び出しで必要なすべての乱数をロールできることです。ただし、余分なデータ処理により速度が低下し、コストがこの利点を相殺する以上のようです.
library(rbenchmark)
probs <- matrix(runif(2000), ncol = 10)
answers <- numeric(200)
benchmark(replications = 1000,
luke = for(i in 1:20) answers[i] <- sample(10,1,prob=probs[i,]),
luke2 = luke2(probs),
roman = apply(probs, MARGIN = 1, FUN = function(x) sample(10, 1, prob = x)),
roman2 = replicate(20, sample(10, 1, prob = runif(10))))
roman = apply(probs, MARGIN = 1, FUN = function(x) sample(10, 1, prob = x)),
roman2 = replicate(20, sample(10, 1, prob = runif(10))))
test replications elapsed relative user.self sys.self user.child sys.child
1 luke 1000 0.171 1.000 0.166 0.005 0 0
2 luke2 1000 0.529 3.094 0.518 0.012 0 0
3 roman 1000 1.564 9.146 1.513 0.052 0 0
4 roman2 1000 0.225 1.316 0.213 0.012 0 0
何らかの理由で、さらに行を追加すると、apply() の動作が非常に悪くなります。for() のラッパーだと思っていたので、roman() は luke() と同じように動作するはずなので、理由がわかりません。