web-dev-qa-db-ja.com

ランダムウォークのシミュレーション

Xnは、0.5の確率でそれぞれ-1または1の値を取ることができます。そして、Sn = Sn-1 + Xn Sn = X1 + X2 + ::: + Xnで与えられる時間nで観測された部分和をどのように計算できますか。ここでランダムウォークをシミュレートしようとしています。私は次のことをしましたが、それが正しいかどうかは正確にはわかりません。

rw <- function(n){
    x=numeric(n)
    xdir=c(TRUE, FALSE)
    step=c(1,-1)
    for (i in 2:n)
    if (sample(xdir,1)) {
        x[i]=x[i-1]+sample(step,1)
    } else {
        x[i]=x[i-1]
    }
    list(x=x)
}

助けてください!

10
user3347124

この答えは、コードが機能しなかった理由を説明するためのものです。 @ jake-burkheadは、実際にコードを書く方法を示しました。

このコードでは、半分の時間だけステップを実行します。これは、移動するかどうかを決定するためにxdirからサンプリングしているためです。代わりに、ループ内で次のことをお勧めします。

_for(i in 2:n){
  x[i] <- x[i - 1] + sample(step, 1)
}
_

sample(step, 1)呼び出しは、ウォークが_1_または_-1_のどちらを移動するかを決定します。

部分和を計算するには、xを生成した後にcumsum()を使用できます。結果は、ウォークの特定のポイントでの部分和のベクトルになります。

4

cumsumを使用して、これを非常に簡潔かつ効率的に行うこともできます。

set.seed(1)

n <- 1000
x <- cumsum(sample(c(-1, 1), n, TRUE))

enter image description here

42
Jake Burkhead

この投稿では、この計算のためのさまざまなベースRメソッドのタイミングについて説明します。この投稿は、 この投稿 へのコメントと、JakeBurkheadによって投稿された最速のメソッドへの投稿の@josilberのコメントに触発されています。

以下では、ランダムウォークの計算にさまざまな方法が使用されています。これを実現するために、各関数は、以下のfncで定義されているように、1または-1のいずれかの1000個の値をプルします。タイミングテストでは、メソッドごとに1000回のレプリケーションでmicrobenchmarkを使用します。

_fnc <- function(n) sample(c(1L, -1L), n, replace=TRUE)
library(microbenchmark)
microbenchmark(all=cumsum(fnc(1000L)),
      reduce=Reduce("+", fnc(1000L), accumulate=TRUE),
      laplyRpCln=cumsum(unlist(lapply(rep.int(1L, 1000L), fnc))),
      laplyRpAn=cumsum(unlist(lapply(rep.int(1L, 1000L), function(x) fnc(1L)))),
      laplySqAn=cumsum(unlist(lapply(seq_len(1000L), function(x) fnc(1L)))),
      saplyRpCln=cumsum(sapply(rep.int(1L, 1000L), fnc)),
      saplyRpAn=cumsum(sapply(rep.int(1L, 1000L), function(x) fnc(1L))),
      saplySqAn=cumsum(sapply(seq_len(1000L), function(x) fnc(1L))),
      vaplyRpCln=cumsum(vapply(rep.int(1L, 1000L), fnc, FUN.VALUE=0)),
      vaplyRpAn=cumsum(vapply(rep.int(1L, 1000L), function(x) fnc(1L), FUN.VALUE=0)),
      vaplySqAn=cumsum(vapply(seq_len(1000L), function(x) fnc(1L), FUN.VALUE=0)),
      replicate=cumsum(replicate(1000L, fnc(1L))),
      forPre={vals <- numeric(1000L); for(i in seq_along(vals)) vals[i] <- fnc(1L); cumsum(vals)},
      forNoPre={vals <- numeric(0L); for(i in seq_len(1000L)) vals <- c(vals, fnc(1L)); cumsum(vals)},
      times=1000)
_

ここに、

  • 「all」は、Jake Burkhead、cumsumの提案を使用し、サンプルを一度にプルします。
  • 「reduce」はサンプルを一度にプルしますが、Reduceを使用して合計を実行します。
  • laplyRpClnは、lapplyunlistを使用してベクトルを返し、1の1000インスタンスを反復処理して、名前で関数を直接呼び出します。
  • laplyRpAnは、無名関数の使用が異なります。
  • laplySqAnは無名関数を使用し、seqではなくrepを使用して反復変数を作成します。
  • saplyRpCln、laplyRpAn、laplySqAnは、sapply/lapplyの代わりにunlistが呼び出されることを除いて、laplyRpClnなどと同じです。
  • vaplyRpClnなどはlaplyRpClnなどと同じですが、vapply/lapplyの代わりにunlistが使用される点が異なります。
  • レプリケートはreplicateの呼び出しであり、デフォルトはsimplify = TRUEです。
  • forPreは、ベクトルを事前に割り当ててそれを埋めるforループを使用します。
  • forNoPreは、空のnumeric(0)ベクトルを作成するforループを使用し、次にcを使用してそのベクトルに連結します。

これは

_Unit: microseconds
       expr      min         lq        mean     median         uq      max neval     cld
        all   25.634    31.0705    85.66495    33.6890    35.3400 49240.30  1000 a      
     reduce  542.073   646.7720   780.13592   696.4775   750.2025 51685.44  1000  b     
 laplyRpCln 4349.384  5026.4015  6433.60754  5409.2485  7209.3405 58494.44  1000   c e  
  laplyRpAn 4600.200  5281.6190  6513.58733  5682.0570  7488.0865 55239.04  1000   c e  
  laplySqAn 4616.986  5251.4685  6514.09770  5634.9065  7488.1560 54263.04  1000   c e  
 saplyRpCln 4362.324  5080.3970  6325.66531  5506.5330  7294.6225 59075.02  1000   cd   
  saplyRpAn 4701.140  5386.1350  6781.95655  5786.6905  7587.8525 55429.02  1000     e  
  saplySqAn 4651.682  5342.5390  6551.35939  5735.0610  7525.4725 55148.32  1000   c e  
 vaplyRpCln 4366.322  5046.0625  6270.66501  5482.8565  7208.0680 63756.83  1000   c    
  vaplyRpAn 4657.256  5347.2190  6724.35226  5818.5225  7580.3695 64513.37  1000    de  
  vaplySqAn 4623.897  5325.6230  6475.97938  5769.8130  7541.3895 14614.67  1000   c e  
  replicate 4722.540  5395.1420  6653.90306  5777.3045  7638.8085 59376.89  1000   c e  
     forPre 5911.107  6823.3040  8172.41411  7226.7820  9038.9550 56119.11  1000      f 
   forNoPre 8431.855 10584.6535 11401.64190 10910.0480 11267.5605 58566.27  1000       g
_

最初の方法が明らかに最速であることに注意してください。これに続いて、サンプル全体を一度にプルし、Reduceを使用して合計を実行します。 _*apply_関数の中で、関数の名前を直接使用する「クリーン」バージョンではパフォーマンスがわずかに向上しているようで、lapplyバージョンはvapplyと同等のようですが、値の範囲を考えると、結論は完全に単純ではありません。 sapplyは最も遅いように見えますが、関数呼び出しのメソッドが_*apply_関数のタイプを支配しています。

2つのforループのパフォーマンスが最悪で、事前割り当てのforループがforループを上回り、cとともに成長しました。

ここでは、openSuse 42.1で3.4.1のパッチバージョン(2017年8月23日頃にパッチが適用されています)を実行しています。

間違いがありましたらお知らせください。できるだけ早く修正します。最終的な機能をさらに調査するように勧めてくれたBenBolkerに感謝します。そこで、いくつかのバグが見つかりました。

3
lmo

ここにそれを行う1つの方法があります。

GenerateRandomWalk <- function(k = 250,initial.value = 0) {
  # Add a bionomial at each step
  samples = rbinom(k,1,0.5)
  samples[samples==0] = -1
  initial.value + c(0, cumsum(samples))
}
0
shahram