滝の音

滝の音

名こそ流れてなお聞こえけれ

VBAでパフォーマンスの良いライフゲームをつくろう その5 Ver7と8.1

  • はじめに

このコーナーも第5回。
飽きたか飽きてないかでいうと
飽きてきました笑

  • Ver7は配列

Ver7はめちゃ速くなります。
このアイディアは思い浮かんではいたものの
実装するとすごいめんどくさくなりそうだなー
と思って放置していたのですが
いいやり方を思いついたのでようやく実装できます。

シートへのアクセスを減らして速度を上げることが一番簡単な手法。
今回はまずシートの情報を配列に書いて
配列の中身を書き換えて
最後に配列をシートに貼り付けます。

今まではシートの情報を取得して
計算の結果を配列に書いて
最後に配列を貼り付ける
というやり方でした。

ちょっと中途半端なやり方でしたが
シート情報を配列に入れてしまうと配列のサイズを決められてしまいます。
ふつうはそれは便利な特徴なのですが
そうなってしまうと配列の中に「隅」や「端」ができてしまいます。
それらの周囲は8マスではなくなってしまうので
計算するのに特別なやり方が必要になってしまいます。

これはまぁ書けばいいのですがちょっと大変そうだなぁ
と思って書いていませんでした。

ただ今回思いつきました。
読み込むシート情報を本来必要なものより一回り大きく読み込めばいいんです。

たとえばRange(cells(2,2),cells(9,9))の88マスを扱いたい場合は
Range(cells(1,1),cells(10,10))の10
10マスを読み込んで
実際に処理するのは8*8マス分にすれば解決します。

配列で説明すると
10*10のシート情報をStockに書き込むと
Stock(1 to 10, 1 to 10)として書き込まれます。

そしてcell_jdgでは

For i = 2 to 9
    For j = 2 to 9
        ‘処理をする
    Next
Next

みたいにすれば大丈夫です。

ただ処理の仕方も今までとは少し違うのでcell_jdgのちゃんとしたコードを載せますね。

Function cell_jdg(f_size, stock, q) As Variant()
 Dim p1 As Variant
 Dim trgt  As Range
 Dim cnt As Integer
 Dim ans() As Variant
 
 ReDim ans(1 To f_size + 2, 1 To f_size + 2)
 
'------------------------------------------------
'field内の各セルに対して
'自身と周囲の計9マスの生きているセルを数えて
'それによって生死を判定します
'生きている場合はそのセルに対応するans()に1を入れる
'------------------------------------------------
 For i = 2 To f_size + 1
    For j = 2 To f_size + 1
    
        cnt = stock(i - 1, j - 1) + stock(i - 1, j) + stock(i - 1, j + 1) + _
            stock(i, j - 1) + stock(i, j) + stock(i, j + 1) + _
            stock(i + 1, j - 1) + stock(i + 1, j) + stock(i + 1, j + 1)
            
        Select Case stock(i, j)
            Case 1
                If cnt = 3 Or cnt = 4 Then
                    ans(i, j) = 1
                End If
            Case Else
                If cnt = 3 Then
                    ans(i, j) = 1
                End If
        End Select
    
    Next j
 Next i

 cell_jdg = ans
 
End Function

これで速度がどうなるかというと。

f_size\Version Ver6_1 Ver7
30 調査終了 0.078125 調査終了0.0078125
更新終了 0.0625 更新終了0.0546875
50 調査終了 0.21875 調査終了0.0078125
更新終了 0.109375 更新終了0.109375
70 調査終了0.4208984 調査終了0.015625
更新終了0.21875 更新終了0.15625

調査時間が30倍くらい?に上がりました。
更新時間はCell_dscをいじっていないので特に変わらず。

このコーナーの初めのほうで決めた目標が100*100のセルに対して1秒間隔で画面を更新していくことでした

なんだか行けそうなので試してみますか。

調査終了0.046875
更新終了0.328125

余裕ですね笑

これにてチャレンジクリア!

。。。

というのもあっけないですね。
Ver7にケチをつけるなら
更新速度の遅さ。

7070 つまり4900マスで0.15秒
100
100 10000マスで0.32秒

これは遠からず壁が来ますね。
更新速度にこだわったVer8を作ってみましょう。

  • 更新速度を上げよう

これはシンプルなやり方がありますよね。

書き込むセル数が肝なら
それを減らせばよい。

1行の内容を1つのセルに入れてみましょう。

ただしこれを行うと欠点もあります。

セルの条件付き書式が使えなくなることと
数値以外のものを扱わねばならいことと
1つのセルに対するデータ量が増えてしまうこと。

ちなみに初期画面はこんな感じです。

“■”が生きているセル
”□”が死んでいるセル

という風に表します。

速度を測るぞ~
いまさら100*100に耐えられない仕様は論外なので
F_sizeは100で計測します。

じゃん。

調査終了 0.1411133
更新終了 0.21875

ってこれだけじゃよくわからないですね。

先ほどのVer7と比較してみると

Ver7 Ver8
調査終了0.046875 調査終了 0.1411133
更新終了0.328125 更新終了 0.21875

んー。これは。。
更新速度は速くなりましたが
調査速度が遅くなりました。
トータルではいい勝負?
ちなみにf_sizeを150にしてみると。

Ver7 Ver8_1
調査終了0.078125 調査終了0.3276367
更新終了0.722168 更新終了0.4853516

んー。

両者のいいところ悪いところがはっきり出ていますね。
これは正式にはバージョンアップできないなぁ。

  • まとめ

Ver7はVer6の意思を引き継がない形となってしまいました。
sheetの中身を配列に移すにはVariant型でないといけないらしいんですよね。

Ver8はうまいこといかなかったー
かといって悪いこともない。

Ver8_1としておきますか。

Ver7もVer8_1もいいところと悪いところがそれぞれあり
これらのいい点を統合出来たらバージョンアップということにしましょう。
ただちょっとやり方が思いつかないので
今回のライフゲームのコーナーはここまでにしましょうか。

当初の100*100はクリアしたしねっ