小さな配列の最適化 — 小さいNで挿入ソートが勝つ理由
本番ソートライブラリが小さな配列で挿入ソートに切り替える理由を発見。O(n²)がO(n log n)を実際に上回るクロスオーバーポイントを学びます。
Practical Topics
詳細な説明
小さな配列のパラドックス
クイックソートやマージソートのようなO(n log n)アルゴリズムは漸近的にはO(n²)アルゴリズムより高速ですが、小さな配列では、よりシンプルなO(n²)アルゴリズムの方が実時間で実際に高速です。これが、すべての本番ソート実装がハイブリッドアプローチを使用する理由です。
シンプルなアルゴリズムが小さいNで勝つ理由
アルゴリズムの実際の実行時間は T(n) = c * f(n) で、cは定数係数、f(n)は計算量関数です。小さいnでは:
挿入ソート: T(n) = c₁ * n²
マージソート: T(n) = c₂ * n * log(n)
しかしc₁はc₂よりはるかに小さいです:
- 再帰オーバーヘッドなし: 挿入ソートはシンプルなループを使用
- 補助メモリなし: 一時配列の割り当てやコピーなし
- より良いキャッシュ動作: 逐次メモリアクセスパターン
- シンプルな分岐: 条件分岐が少なく、分岐予測が向上
- 分割ステップなし: マージソートの分割とマージステップは小さな配列でもオーバーヘッドがある
クロスオーバーポイント
| 実装 | クロスオーバー | 使用閾値 |
|---|---|---|
| Python(Timsort) | 〜32-64要素 | 64(MIN_RUN) |
| Java(Arrays.sort) | 〜47要素 | 47 |
| C++(libstdc++) | 〜16要素 | 16(_S_threshold) |
| V8 JavaScript | 〜10要素 | 10 |
| .NET | 〜16要素 | 16 |
ハイブリッドアルゴリズムの動作
function hybridSort(arr, low, high):
if high - low < THRESHOLD:
insertionSort(arr, low, high) // 小さなサブ配列にはシンプルなアルゴリズム
return
pivot = partition(arr, low, high)
hybridSort(arr, low, pivot - 1)
hybridSort(arr, pivot + 1, high)
通常、クロスオーバーは10〜64要素の間で、16–32が最も一般的な選択です。
ユースケース
小さな配列の最適化の理解は、本番コードで効率的なソートを実装するために不可欠です。特定のデータ型やドメイン向けのカスタムソート(物理エンジンでのパーティクルソートやレンダラーでの描画コールソートなど)を作成する場合、挿入ソートへの切り替え閾値を適切に選択することで10–30%のパフォーマンス向上が得られます。