はじめに
VBAで大量のデータを格納した配列を並べ替える際、処理速度は非常に重要な要素になります。数あるソートアルゴ-リズムの中でも、「クイックソート」は、その名の通り、一般的に最も高速なアルゴリズムの一つとして知られています。
クイックソートは、「分割統治法」という考え方に基づいています。これは、基準となる値を一つ決め、その値を基準にデータを大小二つのグループに分け、さらにそのグループを…と再帰的に分割していくことで、最終的に全体を整列させる手法です。
この記事では、クイックソートの考え方と、それをVBAで「インプレース(in-place)」、つまり余計な配列をほとんど作らずに効率的に実装する方法を解説します。
クイックソートのアルゴリズム
クイックソートは、主に以下の3つのステップで動作します。
- ピボットの選択 (Pivot Selection): 配列内から基準となる要素を一つ選びます。これを「ピボット」と呼びます。(中央の値を選ぶのが一般的です)
- 分割 (Partitioning): ピボットより小さい値を全てピボットの左側へ、大きい値を全て右側へ移動させます。この操作が終わると、ピボットはソート後の最終的な位置に確定します。
- 再帰 (Recursion): ピボットの左側のグループと、右側のグループ、それぞれに対して、再びクイックソートを(1〜3のステップを)適用します。
この再帰的な処理を、グループの要素が一つになるまで繰り返すことで、配列全体が綺麗にソートされます。
VBAでの実装コード
以下のコードは、ランダムな数値が入った配列を生成し、それをクイックソートで昇順に並べ替えて、結果をシートに書き出すものです。
完成コード
' クイックソートの実行サンプル
Sub RunQuickSortExample()
Dim dataArray() As Variant
Dim arraySize As Long, i As Long
arraySize = 20 ' 生成するデータの数
ReDim dataArray(1 To arraySize)
'--- 1. ランダムなテストデータを生成し、A列に書き出し ---
For i = 1 To arraySize
Randomize
dataArray(i) = Int(1000 * Rnd + 1)
Worksheets("Sheet1").Cells(i, "A").Value = dataArray(i)
Next i
'--- 2. クイックソートを実行 ---
' 配列全体(インデックスの最初から最後まで)を対象にソートを開始
ExecuteQuickSort dataArray, LBound(dataArray), UBound(dataArray)
'--- 3. ソート後の結果をB列に書き出し ---
For i = 1 To arraySize
Worksheets("Sheet1").Cells(i, "B").Value = dataArray(i)
Next i
MsgBox "A列の値を昇順ソートしてB列に書き込みました。"
End Sub
' クイックソートを再帰的に実行するプロシージャ
Private Sub ExecuteQuickSort(ByRef arr As Variant, ByVal leftIdx As Long, ByVal rightIdx As Long)
Dim pivot As Variant
Dim temp As Variant
Dim i As Long, j As Long
' 左のインデックスが右のインデックスより小さい場合のみ処理
If leftIdx < rightIdx Then
' ピボットとして中央の値を選択
pivot = arr((leftIdx + rightIdx) \ 2)
i = leftIdx
j = rightIdx
'--- 分割処理 (Partitioning) ---
Do
Do While arr(i) < pivot
i = i + 1
Loop
Do While arr(j) > pivot
j = j - 1
Loop
If i >= j Then Exit Do
' 値を交換(Swap)
temp = arr(i)
arr(i) = arr(j)
arr(j) = temp
i = i + 1
j = j - 1
Loop
'--- 再帰呼び出し ---
ExecuteQuickSort arr, leftIdx, j ' ピボットの左側をソート
ExecuteQuickSort arr, i, rightIdx ' ピボットの右側をソート
End If
End Sub
コードの解説
ExecuteQuickSort
プロシージャ
この Sub
は、ByRef arr As Variant
のように、配列そのものを参照渡しで受け取ります。これにより、プロシージャ内で配列の要素を入れ替えると、呼び出し元の dataArray
が直接書き換えられます(インプレースソート)。
leftIdx
,rightIdx
: 現在ソート対象となっている配列の区間(開始インデックスと終了インデックス)を示します。
分割処理 (Partitioning Loop)
Do ... Loop
の中が、クイックソートの心臓部です。
Do While arr(i) < pivot
: 左から右へポインタi
を進め、ピボット以上の値を見つけます。Do While arr(j) > pivot
: 右から左へポインタj
を進め、ピボット以下の値を見つけます。- 値の交換:
i
とj
が見つかったら、その2つの要素を交換します。 - これを
i
とj
が交差するまで繰り返すことで、ピボットを基準に大小のグループができます。
再帰呼び出し
分割処理が終わると、ExecuteQuickSort arr, leftIdx, j
でピボットより左側の部分配列を、ExecuteQuickSort arr, i, rightIdx
で右側の部分配列を、それぞれ引数として渡して、自身を再度呼び出します。これにより、分割された各グループがさらにソートされていきます。
まとめ
今回は、VBAで非常に高速なソートアルゴリズムである「クイックソート」を実装する方法を解説しました。
- クイックソートは「分割統治法」に基づく、平均的に非常に高速なアルゴリズム。
- ピボットを基準に大小2つのグループに分け、それを再帰的に繰り返す。
- 配列の区間を引数で渡すことで、**インプレース(in-place)**で効率的にソートできる。
コードのロジックはマージソートと同様に複雑ですが、そのパフォーマンスは絶大です。大量の配列データを扱うマクロの高速化を図る上で、最強の選択肢の一つと言えるでしょう。