こんにちは!マクロの記録を卒業して、そろそろ「自分だけのスマートな自動化ツールを作りたい」と意気込んでいる頃ですね。素晴らしいステップアップです。
Excel VBAを書いていると、「あれ、なんだか処理が重いな……」と頭を抱える瞬間がやってきます。特に、複雑な計算やデータの総当たり、階層構造のデータを扱うときに現れるのが「再帰関数(さいきかんすう)」という強力なテクニックです。
今回は、この再帰関数を`Static`(スタティック)変数の力で極限まで高速化する、プロの現場でも使われる秘密のテクニックを伝授しましょう。ここをクリアすれば、あなたのVBAスキルは確実に一段上のステージへ駆け上がりますよ。
—
1. 「再帰関数」ってなに? 身近な例でイメージしよう
再帰関数とは一言でいうと、「関数の中で、自分自身をもう一度呼び出す仕組み」のことです。
イメージとしては、ロシアの民芸品「マトリョーシカ」を思い浮かべてください。大きな人形を開けたら、中から少し小さな人形が出てくる。それを開けたら、さらに小さな人形が出てくる……という構造ですね。
VBAの世界では、例えば「階乗(N! = N × (N-1) × (N-2) × … × 1)」や、組織図のようなツリー構造を上から順に辿っていくような処理で大活躍します。
通常の再帰関数の落とし穴
しかし、この再帰関数には一つ大きな弱点があります。それは「同じ計算を何度も何度も繰り返してしまう」という無駄遣いです。
例えば、有名な「フィボナッチ数列(前の2つの数字を足していく数列:1, 1, 2, 3, 5, 8, 13……)」を普通の再帰関数で計算させると、コンピュータは次のように同じ計算を何回もやり直します。
- 「5番目の数」を求めるために、「4番目」と「3番目」を計算する。
- 「4番目」を求めるために、「3番目」と「2番目」を計算する。(あれ、さっき3番目を計算したのにまた計算してる……!)
これが数千回、数万回と積み重なると、Excelがフリーズしたかのように重くなってしまうのです。
—
2. 救世主 `Static` 変数! 計算結果を「記憶」させる
ここで登場するのが、今回の主役である `Static` 変数 です。
通常のプログラミングでは、SubやFunctionの中で宣言した変数は、その処理が終わった瞬間に記憶がリセットされて消えてしまいます(これをローカル変数と呼びます)。
しかし、`Dim` の代わりに `Static` と書いて宣言された変数は、「マクロが終了するまで、その値をずっと記憶し続ける(保持する)」という特殊な能力を持ちます。
メモ帳(キャッシュ)を持たせる発想
この `Static` 変数を使い、「一度計算した結果は、メモ帳(配列やDictionaryなど)に書き留めておく」ようにします。これをエンジニアの世界では「メモ化(キャッシュ)」と呼びます。
2回目以降に同じ計算が必要になったときは、再計算するのではなく、メモ帳をペラッとめくって「あ、さっき計算したから答えはこれだね」と一瞬で答えを引き出すのです。これにより、計算量が劇的に削減されます。
—
3. 実践! 爆速フィボナッチ計算コード
百聞は一見に如かず。実際に `Static` 変数を使った「メモ化による再帰関数の最適化」のコードを見てみましょう。
以下のコードをVBE(VBAのコードエディタ)の標準モジュールに貼り付けて実行してみてください。
Option Explicit
‘ メインの実行プロシージャ
Sub RunFastFibonacci()
Dim targetNumber As Long
Dim startTime As Double
Dim result As Double
targetNumber = 35 ‘ 求たいフィボナッチ数列の何番目か(例:35番目)
‘ 処理速度を計測するためのタイマー開始
startTime = Timer
‘ 初回実行時はメモ(キャッシュ)を初期化するためにクリア用フラグを渡すなどしますが、
‘ 今回はStatic配列のサイズを自動拡張するスマートな方法を使います。
result = GetFibonacciWithMemo(targetNumber)
‘ 結果を出力
MsgBox targetNumber & “番目のフィボナッチ数: ” & format(result, “#,
0″) & vbCrLf & _
“処理時間: ” & format(Timer – startTime, “0.00秒”) & ” 秒”, _
vInformation, “最適化された再帰処理”
End Sub
‘ 【最適化された再帰関数】
‘ Static変数で計算結果をキャッシュ(記憶)し、二度目の計算を防ぐ
Function GetFibonacciWithMemo(ByVal n As Long) As Double
‘ Static宣言により、関数の呼び出しが終わっても値が消えず保持されます
‘ これが今回の「メモ帳」になります。
Static memo() As Double
Static initialized As Boolean
Dim i As Long
‘ 初回実行時のみ、メモ帳のサイズを初期化する
If Not initialized Then
ReDim memo(0 To 1000) ‘ 最大1000番目まで記憶できるように確保
For i = 0 To 1000
memo(i) = -1 ‘ まだ計算していない印として「-1」を入れておく
Next i
initialized = True
End If
‘ ① 終了条件(ベースケース)
If n <= 1 Then
GetFibonacciWithMemo = n
Exit Function
End If
' ② 【超重要】すでにメモ帳に答えがあるかチェック!
If memo(n) <> -1 Then
‘ あれば、再計算せずにメモから一瞬で引き出す(キャッシュヒット!)
GetFibonacciWithMemo = memo(n)
Exit Function
End If
‘ ③ メモにない場合のみ、通常通り再帰で計算する
memo(n) = GetFibonacciWithMemo(n – 1) + GetFibonacciWithMemo(n – 2)
‘ 求めた答えを戻り値にする
GetFibonacciWithMemo = memo(n)
End Function
コードのここがポイント!
1. `Static memo() As Double`: この変数がメモリ上に居座り続け、計算した結果をどんどん蓄えていきます。
2. `If memo(n) <> -1 Then`: 「あ、この番号の答えはもう知ってるぞ」と気づいた瞬間、無駄な再帰呼び出しをバッサリとスキップします。
もしこの最適化をせずに「35番目」を通常の再帰だけで計算させると、パソコンの性能によっては数秒〜数十秒かかり、場合によってはフリーズします。しかし、このメモ化コードを通せば、一瞬(0.00秒台)で答えが出てきます。このスピード感、鳥肌モノですよ!
—
4. 初学者が陥りやすいエラーと注意点
非常に便利な `Static` 変数と再帰関数ですが、強力ゆえに気をつけるべきポイントがいくつかあります。ここをクリアすれば完璧です。
注意点1: メモ(Static変数)が残り続けることによる「状態の持ち越し」
`Static` 変数はマクロが終了するまで値が消えません。つまり、2回目に別の目的でこの関数を呼び出したとき、前回のメモが残ったままになるという現象が起きます。
- 対策: 今回のコードのように、初期化フラグ(`initialized`)を用意して、必要に応じて配列をリセットする仕組みを必ず組み込みましょう。
注意点2: 無限ループ(スタックオーバーフロー)
再帰関数で最も恐ろしいのが、「終了条件」を書き忘れたり、間違えたりするミスです。
マロリー棒のように呼び出しが無限に積み重なると、Excelのメモリ限界を超えてしまい、「メモリ不足です(Stack Overflow)」というエラーが出てVBAが強制終了します。
- 対策: 「これ以上小さくなったら終わる」というベースケース(今回のコードの `If n <= 1 Then` の部分)を必ず最初に記述する癖をつけましょう。
—
まとめ
今回は、`Static` 変数を使った再帰関数の最適化(メモ化)について解説しました。
- 再帰関数は自分自身を呼び出す強力な仕組みだが、同じ計算を繰り返すと重くなる。
- `Static` 変数を使えば、処理が終わっても値を記憶し続けることができる。
- 計算結果をキャッシュ(メモ)し、2回目以降はそれを使い回すことで、処理速度を劇的に向上させることができる。
「マクロの記録」の向こう側にある、こうしたアルゴリズムの妙を理解できるようになると、Excel VBAを書くことがまるでパズルを解くように楽しくなってきます。
ここをクリアしたあなたなら、実務での複雑なデータ処理やツリー構造の解析も怖くありません。ぜひ自分の手でコードを動かし、この爆速の快感を体感してみてくださいね!
