ライフゲーム続き
改善点
- 速度向上
- インデックスのテーブル と 行列はベクトルであること を利用して outer を消した。そのおかげで apply の回数が 縦セル×横セル から 縦セル になった.。
- 状態の継続
- 前回は f() を実行するたびに初期状態から始まっていたが、2回目以降の f() は前回の終了状態から継続するようにした。
- 自動リセット
- おなじ状態を繰り返すようになったら、ランダムなセルを「生」にして変化がおきるようにした。
> summaryRprof(tmp) life_game2 <- function(x=40, y=40, a=2) { m <- matrix(sample(c(rep(0,9),1),x*y,TRUE), y, x) om <- matrix(0, y * x, a) #インデックスの行列作成 make_idx <- function(v) { rbind( c(tail(v, 1), head(v,-1)), v, c(tail(v,-1), head(v, 1)) ) } xidx <- make_idx(1:x) yidx <- make_idx(1:y) nextstep <- function(m) { livings <- t(sapply( 1:y, function(i){ # y×x の行列から 3×3x の行列を作って、9×x の行列にしたあと列ごとの和をとる row_livings <- colSums( matrix(m[yidx[,i], as.vector(xidx)],ncol=x) ) })) ifelse(livings==3, 1, ifelse(livings==4, m, 0)) } function(n=100) { for (i in 1:n) { # 過去 a 回と比較して、どれかと同じならランダムにセルを生にする if (any(apply(om == as.vector(m), 2, all))) { m <<- ( m+matrix( sample(c(rep(0,16),1),x*y,TRUE),y ) ) %% 2 } # 過去 a 回の状態を保存 om <<- cbind(as.vector(m), om[,seq(a)]) m <<- nextstep(m) image(m) } } } f <- life_game2() f() f()
スピードは約2倍になった。
> system.time(life_game()()) ユーザ システム 経過 6.75 3.26 10.57 > system.time(life_game2()()) ユーザ システム 経過 2.41 2.97 5.75
apply を呼ぶ回数を x×y 回から y 回 に減らしたから、もっと速くなると思ったけど、意外と速くならないな。
もう少し速くできないか、と思ってプロファイラーで調べてみると、
> summaryRprof() $by.self self.time self.pct total.time total.pct image.default 3.28 63.3 4.24 81.9 axis 0.46 8.9 0.50 9.7 colSums 0.26 5.0 0.64 12.4 plot.new 0.24 4.6 0.24 4.6
描画の image で 80% 以上時間を使っていた、これ以上の最適化は意味なさそうなのでこれで終わり。