この記事は「Excelのサイコロは偏る?VBAで1万回振って検証!」
「Excelで100万回サイコロ検証!VBAと大数の法則」の続編です!
まだの方は下のリンクからどうぞ!
Excelの動きが止まる!VBAで「高速データ書き込み術」をマスターせよ!
本記事では、Excel 2021を使用して検証および数式の作成を行っています。
これまでの記事で、私たちはExcelとVBAを使い、
サイコロを振る壮大なシミュレーションに挑戦してきました。
そして、試行回数を100万回にまで増やした時、乱数が示す「大数の法則」の圧倒的な姿を目撃しました。
しかし、同時に多くの方が体験したはずです。
Excelが「うなり」を上げ、計算に長い時間がかかってしまうという現実を。
「長い待ち時間」はもう終わりにしよう!
今回の目的は、あの「うなり声」の根本原因である「大量データのセルへの書き込み」問題を、
VBAの強力なテクニックで解決することです。
これから紹介する方法をマスターすれば、あの長い待ち時間は過去のものになります。
もしかしたら、100万行のデータ書き込みが、文字通り「一瞬」で終わるかもしれません。
VBAの配列書き込みで高速化
今回、私たちが手にするのは、VBAにおけるパフォーマンスチューニングの最終奥義とも言える「配列書き込み術」です。
これは単なる小手先の技ではありません。
ExcelとVBAが連携する仕組みの核心に触れる、非常に重要で強力なテクニックです。
このシリーズの締めくくりとして、
ExcelのVBAプログラミングの真髄に触れる「暇つぶし」を始めましょう。

STEP1:「セル一つずつ書き込み」の弱点!
VBAの魔法を解き明かす前に、まずは「なぜ遅かったのか?」という原因を正しく理解することが重要です。
問題の根本を知ることで、これから学ぶテクニックの価値がより一層理解できるはずです。
前回のVBAコードを思い出してください。
Sub RollDice_10万回()
Const NumRolls As Long = 100000 ' ★★★ 試行回数を10万回に設定 ★★★
Dim ws As Worksheet
Dim i As Long
Set ws = ThisWorkbook.Sheets("10万回") ' 結果を出力するシート名
ws.Cells.ClearContents ' シートをクリア
ws.Range("A1").Value = "サイコロ振りシミュレーション結果"
ws.Range("A2").Value = "試行回数:" & NumRolls & "回"
ws.Range("A4").Value = "出目" ' 見出し
' --- 高速化設定 ---
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
For i = 1 To NumRolls
ws.Cells(i + 4, 1).Value = Int(Rnd() * 6) + 1
Next i
' --- 設定を元に戻す ---
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
MsgBox NumRolls & "回のサイコロ振りシミュレーションが完了しました!"
End Sub
前回は、ループ処理の中で ws.Cells(i + 4, 1).Value = ...
のように、
セルに一つずつ値を書き込んでいました。
この「1回計算して、1回セルに書く」という行為の繰り返しこそが、
処理が遅くなる最大の原因です。
人間にとっては自然な手順ですが、コンピュータ、特にExcelにとっては非常に非効率な作業なのです。
前回の記事で紹介した、
Application.ScreenUpdating = False
(画面描画停止) Application.Calculation = xlCalculationManual
(自動計算停止)
といった高速化コードは、
この非効率な作業の負担を軽減するための、いわば「応急処置」でした。
もちろんこれらも非常に重要ですが、問題の根本を解決するものではありませんでした。
今回は、この根本原因にメスを入れていきます。

STEP2:配列にデータを一括格納し、一回でシートに書き出す!
それでは、Excelを劇的に高速化させる「配列書き込み」の仕組みを解き明かしていきましょう。
このステップの主役は「配列」という、VBAプログラミングにおける非常に強力な武器です。
「配列」とは何か?
まず、「配列」とは何かを簡単に説明します。
VBAにおける配列とは、複数のデータをまとめて格納できる、メモリ上の「仮想的な箱(コンテナ)」のようなものです。

Excelシートに一つずつデータを書き込むのではなく、
まずVBAのメモリ上にあるこの「箱」に、計算結果を高速でどんどん溜め込んでいきます。
そして、全ての計算が終わった後、溜め込んだデータが入った箱を、
Excelシートに「一回で」丸ごと渡します。
Excelとの通信を最小限に抑えるこの方法こそが、
劇的な高速化を実現する原理なのです。
VBAコードの作成(配列書き込み版)
配列を使ったコードは、以下の4つのステップで構成されます。
配列変数の宣言
まず、Dim ary() As Variant
のように、データを格納するための「箱」を用意します。
Variant
型は様々な種類のデータを格納できる便利な型で、今回は数値を入れるために使います。
カッコ()
は、これが配列であることを示しています。

配列のリサイズ
次に、ReDim ary(1 To NumRolls, 1 To 1)
のように、用意した箱を必要なサイズに変更します。
今回は100万個のデータを入れたいので、「100万行 × 1列」のサイズの箱を用意しています。

配列へのデータ格納
続いて、ループ処理の中で ary(i, 1) = Int(Rnd() * 6) + 1
のように、計算結果をセルではなく、配列の指定した場所(i番目の行, 1番目の列)に格納していきます。

この処理はExcelシートを介さず、メモリ上で行われるため非常に高速です。
シートへの一括書き込み
最後に、ws.Range("A5").Resize(NumRolls, 1).Value = ary
という命令で、配列に溜め込んだ全データを、指定したセル範囲に一括で書き出します。

これが高速化の心臓部です。
全てのデータをメモリ上で処理し、Excelシートへの書き込みをたった一度の操作で完了させるため、劇的な速度向上を実現します。
STEP3:究極のスピード対決!「100万回サイコロ」で旧コードvs新コード!
理論を学んだら、次はいよいよ実践です。
従来の「セル一つずつ書き込み」方式と、新しい「配列一括書き込み」方式で、一体どれほどの速度差が生まれるのか。
衝撃的な結果を、あなた自身の目で確かめてみましょう。
準備:比較用のシートとボタン
まず、実験の準備をします。
「コントロール」シートに、「100万回(従来)」「100万回(高速化)」という2つの実行ボタンを新たに用意してください。

そして、結果を出力するためのシートとして、「100万回_従来」と「100万回_高速」という名前のシートを2枚作成します。
是非、お手元にストップウォッチを用意し、それぞれのマクロの実行時間を計測してみてください。
旧コード(従来方式)で「100万回」再体験
はじめに、前回も使用した従来方式のマクロ(RollDice_100万回_従来という名前に変更)を再度実行します。
これは前回記事の100万回コードと同じものです。
Sub RollDice_100万回_従来()
Const NumRolls As Long = 1000000 ' ★★★ 試行回数を100万回に設定 ★★★
Dim ws As Worksheet
Dim i As Long
Set ws = ThisWorkbook.Sheets("100万回") ' 結果を出力するシート名
ws.Cells.ClearContents ' シートをクリア
ws.Range("A1").Value = "サイコロ振りシミュレーション結果"
ws.Range("A2").Value = "試行回数:" & NumRolls & "回"
ws.Range("A4").Value = "出目" ' 見出し
' --- 高速化設定 ---
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
For i = 1 To NumRolls
ws.Cells(i + 4, 1).Value = Int(Rnd() * 6) + 1
Next i
' --- 設定を元に戻す ---
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
MsgBox NumRolls & "回のサイコロ振りシミュレーションが完了しました!"
End Sub
実行ボタンを押し、あの「長い待ち時間」をもう一度体験してみましょう。
PCがうなりを上げ、Excelが一生懸命に仕事をしている時間を感じてください。
ストップウォッチでの計測も忘れずに。

新コード(高速化版)で「100万回」
さて、いよいよ本番です。
「100万回(高速化)」ボタンに、以下の新しいコードを登録してください。
Sub RollDice_100万回_高速版()
Const NumRolls As Long = 1000000 ' 試行回数
Dim ws As Worksheet
Dim i As Long
Dim ary() As Variant ' ★★★ 配列を宣言 ★★★
' ★★★ 配列をリサイズ(行数, 列数) ★★★
ReDim ary(1 To NumRolls, 1 To 1)
Set ws = ThisWorkbook.Sheets("100万回_高速") ' 結果を出力するシート名
ws.Cells.ClearContents ' シートをクリア
ws.Range("A1").Value = "サイコロ振りシミュレーション結果"
ws.Range("A2").Value = "試行回数:" & NumRolls & "回"
ws.Range("A4").Value = "出目" ' 見出し
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' --- まず、結果を高速で配列に格納 ---
For i = 1 To NumRolls
ary(i, 1) = Int(Rnd() * 6) + 1
Next i
' ★★★ 配列を一括でシートに書き出す!これが高速化の肝 ★★★
ws.Range("A5").Resize(NumRolls, 1).Value = ary
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
MsgBox NumRolls & "回のサイコロ振りシミュレーションが完了しました!"
End Sub
心の準備はよろしいですか?
ストップウォッチをリセットし、実行ボタンをクリックしてください。
…どうでしょうか?
おそらく、クリックした次の瞬間には、完了メッセージが表示されたはずです。
この「一瞬で終わる衝撃」こそ、配列書き込みの真価です。
最後に、ストップウォッチで測った時間を比較してみてください。
多くの場合、処理速度が数十倍、あるいは百倍以上になったはずです。

STEP4:VBAの「真の力」!なぜ配列書き込みはこんなに速いのか?
この圧倒的な速度差は、一体どこから生まれるのでしょうか。
最後に、VBAとExcelの内部的な仕組みに少しだけ触れ、その秘密を解き明かします。
「API呼び出し」のオーバーヘッドを削減する配列術!
実は、VBAからExcelシートのセルを操作するたびに、
プログラムの内部では「API呼び出し」という命令のやり取りが発生しています。
これは、VBAがExcelに対して「このセルの値をこれに変えてください」と一つ一つお願いするようなものです。
従来のセル一つずつの書き込みでは、このお願いがデータの数だけ、
つまり100万回も発生していました。
この通信にかかる時間や手間(オーバーヘッド)が積み重なり、
大きな遅延となっていたのです。
一方で、配列書き込みはどうでしょうか。
この方法は、API呼び出しを「たった1回」に集約します。
例えば、宅配便が100万個の荷物を一つずつ届けるか、
それとも全ての荷物を一台の大型トラックに積み込んで一括で届けるか、の違いです。
どちらが効率的かは、言うまでもありませんね。
この「Excelとの通信回数を最小限に抑える」という考え方こそ、
VBAで大量データを扱う上で最も重要な高速化テクニックの一つなのです。
まとめ:VBA最終奥義をマスターせよ!
3回にわたる「暇つぶし」の旅も、いよいよこれが最終回です。
今回の「VBA配列書き込み術」によって、100万回のサイコロシミュレーションが劇的に高速化されることを体験しました。
RAND
関数による乱数の検証から始まり、大数の法則の証明、
そしてVBAの高速化テクニックまで、Excelが持つ奥深さと可能性を探求したこのシリーズも、これにて集大成です。
もう、Excelの動作が重いと嘆く必要はありません。
ぜひ、今回の配列書き込み術を、あなた自身のExcel作業に応用してみてくださいね。
次なる探求のテーマを見つけ、Excelをもっともっと遊び倒しましょう!