【VBAリファレンス】VBAで数独を解くアルゴリズムの極意とパフォーマンス最適化の全技術

スポンサーリンク

概要

Excel VBAは単なる事務作業の自動化ツールではありません。メモリ管理やアルゴリズムの構築次第では、複雑なパズルを瞬時に解く強力な計算エンジンへと変貌します。本稿では、数独(ナンプレ)を解くための「バックトラッキング法」を軸に、VBAにおける再帰呼び出しの最適化、配列操作による高速化、そして実務レベルで応用できる探索効率の向上手法について深く掘り下げます。数独という、一見単純に見えて組み合わせ爆発を起こしやすい課題を題材に、VBAの性能を極限まで引き出すための設計思想を解説します。

詳細解説:バックトラッキングの構造

数独を解くための最も基本的かつ強力な手法が「バックトラッキング(Backtracking)」です。これは、空いているマスに数字を順に当てはめ、矛盾が生じた場合に一つ前の状態へ戻り、別の選択肢を試すという「深さ優先探索」の一種です。

VBAでこれを実装する際、最も重要なのは「探索の順序」と「状態の保持」です。単純にセルをループさせるのではなく、制約の強いマス(候補が最も少ないマス)を優先的に埋める「MRV(Minimum Remaining Values)法」を取り入れることで、探索回数を劇的に減らすことが可能です。

また、VBAのボトルネックになりがちな「セルへのアクセス」を排除し、処理をすべてメモリ上の「二次元配列」内で完結させる設計が不可欠です。セルからデータを読み込み、メモリ上で計算し、最後に一括でセルへ書き出す。このプロセスを徹底するだけで、実行速度は数百倍に向上します。

サンプルコード:再帰的バックトラッキングの基本実装

以下は、9×9の数独をメモリ上で解くための基本的なバックトラッキング関数の実装例です。


' 数独を解く再帰関数
Function SolveSudoku(ByRef board() As Long) As Boolean
    Dim r As Long, c As Long
    Dim num As Long
    
    ' 空マスを探す(0が空マス)
    If Not FindEmpty(board, r, c) Then
        SolveSudoku = True ' 全て埋まれば成功
        Exit Function
    End If
    
    ' 1から9までの数字を試す
    For num = 1 To 9
        If IsValid(board, r, c, num) Then
            board(r, c) = num
            
            ' 再帰呼び出し
            If SolveSudoku(board) Then
                SolveSudoku = True
                Exit Function
            End If
            
            ' バックトラック(失敗時は0に戻す)
            board(r, c) = 0
        End If
    Next num
    
    SolveSudoku = False
End Function

' 数字が配置可能か判定する関数
Function IsValid(board() As Long, r As Long, c As Long, num As Long) As Boolean
    Dim i As Long, startR As Long, startC As Long
    
    ' 行と列のチェック
    For i = 0 To 8
        If board(r, i) = num Or board(i, c) = num Then Exit Function
    Next i
    
    ' 3x3ブロックのチェック
    startR = (r \ 3) * 3
    startC = (c \ 3) * 3
    For i = 0 To 2
        Dim j As Long
        For j = 0 To 2
            If board(startR + i, startC + j) = num Then Exit Function
        Next j
    Next i
    
    IsValid = True
End Function

パフォーマンス向上のための技術的勘所

VBAで複雑なパズルを解く際、パフォーマンスを左右するのは「再帰呼び出しのオーバーヘッド」と「判定ロジックの冗長性」です。以下の3点を改善することで、さらに高速化が可能です。

1. 配列アクセスの最適化:
VBAの二次元配列はメモリ上で連続したアドレスに配置されますが、アクセス時にはインデックスの境界チェックが行われます。これを最小化するため、可能な限り変数をローカルスコープに留め、頻繁に参照する値は変数にキャッシュします。

2. 制約伝播(Constraint Propagation)の導入:
単に数字を当てはめるだけでなく、ある数字を置いた瞬間に「置けるはずのない数字」を論理的に除外する処理( Naked Singles / Hidden Singles )を組み込むことで、再帰の深さを大幅に減らせます。

3. 型宣言の徹底:
Variant型は柔軟ですが、計算処理において大きな負荷となります。すべての変数を Long や Boolean で明示的に型宣言することで、CPUの演算効率が向上します。特に数独のような純粋な整数演算では、Long型への統一がベストプラクティスです。

実務アドバイス:VBAの限界と可能性

実務において、数独のようなアルゴリズムを実装する機会は稀かもしれません。しかし、ここで学べる「メモリ内でのデータ操作」「再帰的アルゴリズムの制御」「条件の絞り込み」という手法は、複雑な業務フローの最適化や、膨大なデータからのマッチング処理、あるいはスケジューリングの自動化に直接応用できます。

VBAは現代のプログラミング言語と比較して実行速度で劣ることは事実です。しかし、Excelという強力なインターフェースと直結している点は、他の言語にはない圧倒的なアドバンテージです。重い処理をVBAで行う際は、まず「Excelの画面更新を停止する(Application.ScreenUpdating = False)」ことと、「自動計算をオフにする(Application.Calculation = xlCalculationManual)」ことを忘れないでください。これだけで、アルゴリズムそのものの改善以上に劇的な効果が得られることもあります。

まとめ

数独を解くアルゴリズムを通じて、VBAにおける「データ構造の重要性」と「探索の効率化」について解説しました。再帰処理は強力な武器ですが、一歩間違えればスタックオーバーフローや極端な低速化を招きます。今回紹介したバックトラッキングの基本形をベースに、MRV法や制約伝播のロジックを追加していくことで、あなた自身のVBAコードは一段上のレベルへと進化するはずです。

プログラミングの本質は、コンピュータにいかに効率よく思考させるかにあります。VBAであっても、アルゴリズムの設計思想はC++やPythonと何ら変わりません。まずは小さなパズルから、ご自身のロジックを磨き上げてみてください。次回の記事では、さらに高度な探索アルゴリズム「Dancing Links」のVBA実装について掘り下げます。

タイトルとURLをコピーしました