【VBAリファレンス】VBA練習問題VBA100本ノック 魔球編:閉領域の塗り潰し

スポンサーリンク

はじめに:VBAによるグラフィカルなデータ処理の極致

こんにちは。Excel VBA講師の現場担当です。これまで数多くの受講生を見てきましたが、「VBAで図形や色を自在に操る」という領域に足を踏み入れた瞬間、エンジニアとしての視界が劇的に広がります。

今回取り上げるのは、VBA100本ノックの中でも「魔球編」と称される難問、「閉領域の塗り潰し」です。これは単にセルに色を塗る作業ではありません。画像処理アルゴリズムの基礎である「シードフィル(塗り潰し)」を、Excelのグリッド上で再現するという、非常に高度なロジックを要する課題です。

本稿では、この難問を解き明かすためのアルゴリズム理論から、再帰呼び出しの罠、そして実務における最適化手法まで、プロフェッショナルな視点から徹底解説します。

詳細解説:塗り潰しアルゴリズムの核心

塗り潰し(Flood Fill)とは、ある点から開始し、同じ色で連結している隣接セルを次々と塗り替えていく処理です。これを実現するためのアプローチは主に2つあります。

1. 再帰的アプローチ(DFS: 深さ優先探索)
2. キューを用いたアプローチ(BFS: 幅優先探索)

VBAにおいて、再帰処理には「スタックオーバーフロー」という致命的なリスクが伴います。Excelのセル範囲が広大になると、再帰の深さがVBAの許容限界を超えてしまうのです。そのため、プロの現場では「スタックを明示的に管理する」あるいは「キューを用いたBFS」を採用するのが定石です。

今回の「魔球編」では、効率的かつ安全に実装するために、スタックを自前で実装するアプローチを推奨します。

サンプルコード:スタックを用いた塗り潰しアルゴリズム

以下に、指定したセルから隣接する同じ色の範囲を塗り潰す、堅牢なコードを提示します。


' 閉領域の塗り潰しアルゴリズム(非再帰的スタックアプローチ)
Sub FloodFill(ByVal StartCell As Range, ByVal NewColor As Long)
    Dim TargetColor As Long
    Dim Stack As Collection
    Dim CurrentCell As Range
    Dim r As Long, c As Long
    
    TargetColor = StartCell.Interior.Color
    If TargetColor = NewColor Then Exit Sub
    
    Set Stack = New Collection
    Stack.Add StartCell
    
    Application.ScreenUpdating = False
    
    Do While Stack.Count > 0
        Set CurrentCell = Stack(Stack.Count)
        Stack.Remove Stack.Count
        
        r = CurrentCell.Row
        c = CurrentCell.Column
        
        ' 範囲チェック(シートの境界)
        If r >= 1 And r <= Rows.Count And c >= 1 And c <= Columns.Count Then
            If Cells(r, c).Interior.Color = TargetColor Then
                ' 色を塗り替え
                Cells(r, c).Interior.Color = NewColor
                
                ' 隣接セルをスタックに追加(上下左右)
                If r > 1 Then Stack.Add Cells(r - 1, c)
                If r < Rows.Count Then Stack.Add Cells(r + 1, c)
                If c > 1 Then Stack.Add Cells(r, c - 1)
                If c < Columns.Count Then Stack.Add Cells(r, c + 1)
            End If
        End If
    Loop
    
    Application.ScreenUpdating = True
End Sub

技術的深掘りとプロの実務アドバイス

このコードがなぜ「魔球」と呼ばれるのか、そしてなぜこの実装が優れているのかを解説します。

1. 再帰を使わない理由

VBAの再帰関数は、呼び出しごとにメモリを消費します。数千セルを塗り潰す際、再帰では簡単に「メモリ不足」や「スタック溢れ」が発生します。上記のコードでは`Collection`オブジェクトをスタックとして代用することで、ヒープメモリを有効活用し、安定性を確保しています。

2. 処理速度の最適化

Excel VBAにおいて最も重い処理は「セルへのアクセス」です。`Cells(r, c).Interior.Color`を何度も呼び出すのは非常に低速です。実務レベルで数万セルを扱う場合は、以下の最適化を行ってください。
- **配列への取り込み**: `UsedRange.Value`(あるいは`Interior.Color`の情報を保持する配列)を一度メモリ上に展開し、メモリ内で判定処理を行ってから、最後に一括で色を反映させる。
- **ScreenUpdatingの制御**: コード内の通り、画面描画を停止させることは必須です。

3. 境界条件の管理

「閉領域」という言葉の通り、塗り潰しが暴走しないための「壁」が必要です。実務では、塗り潰したくない境界線を特定のパターンや色で定義しておき、判定条件に`If CurrentColor = TargetColor And CurrentColor <> WallColor Then`といったロジックを加えることが求められます。

4. ユーザー体験への配慮

塗り潰しは時間がかかる処理です。ユーザーを不安にさせないために、`Application.StatusBar`を使用して進捗率を表示することをお勧めします。「処理中」のメッセージを出すだけで、ツールの信頼性は格段に向上します。

まとめ:VBAエンジニアとしてのステップアップ

「閉領域の塗り潰し」をマスターするということは、単に色が塗れるようになることではありません。それは、「アルゴリズムをVBAという制約の多い言語でいかに効率的に実装するか」というエンジニアリングの勘所を養うプロセスです。

プログラミングにおいて、最も大切なのは「なぜそのコードを書くのか」という根拠です。再帰かループか、配列かセルか。この選択肢を常に持ち、状況に応じて最適な手段を選べるようになること。それこそが、VBAを使いこなすベテランへの道です。

VBA100本ノックの魔球編は、あなたの論理的思考力を極限まで引き出します。ぜひこのコードをベースに、自分なりの改良を加えてみてください。例えば、「対角線方向の塗り潰し」を追加してみる、あるいは「塗り潰しの履歴をUNDO(元に戻す)できるようにする」など、応用範囲は無限大です。

皆さんのVBAライフが、より創造的で、より高度なものになることを心から応援しています。現場からは以上です。

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