[インデックス 17400] ファイルの概要
このコミットは、Go言語の標準ライブラリ sort
パッケージ内の sort_test.go
ファイルに対する変更です。具体的には、安定ソートのテスト (TestStableBM
) における許容されるスワップ操作数の上限を厳格化しています。
コミット
commit 2f81dfd53f5f0b422b038793f514cbf4ce58e573
Author: Volker Dobler <dr.volker.dobler@gmail.com>
Date: Tue Aug 27 08:41:43 2013 -0700
sort: harden limit in stable test
Reduce the number of allowed swap operations during stable sort.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12907045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2f81dfd53f5f0b422b038793f514cbf4ce58e573
元コミット内容
安定ソートのテストにおいて、許容されるスワップ操作の回数を削減し、上限を厳格化する。
変更の背景
このコミットの背景には、Go言語の sort
パッケージにおける安定ソートの実装の堅牢性を高める目的があります。安定ソートは、等しい要素の相対的な順序を保持するソートアルゴリズムです。テストにおいて、ソートアルゴリズムが期待される性能特性(特に操作回数)を満たしているかを確認することは非常に重要です。
Bentley-McIlroy
テストは、ソートアルゴリズムの性能と正確性を検証するための厳格なテストフレームワークです。このテストは、特に最悪ケースに近い入力データに対してアルゴリズムがどのように振る舞うかを評価するのに役立ちます。
以前のテストでは、安定ソートのテスト (TestStableBM
) における許容されるスワップ操作数の上限が、実際の安定ソートの効率的な実装が達成すべき値よりも緩やかであった可能性があります。この緩い上限は、非効率な実装や、安定ソートの特性を損なう可能性のあるバグを見逃すリスクをはらんでいました。
コミットの目的は、この上限をより厳しく設定することで、安定ソートの実装がより効率的で、かつ安定性の保証を確実に満たしているかを検証することにあります。これにより、将来的な変更や最適化が行われた際にも、安定ソートの品質が維持されることを保証します。
前提知識の解説
安定ソート (Stable Sort)
安定ソートとは、ソート対象の要素の中に同じ値を持つものが複数存在する場合に、それらの要素のソート前の相対的な順序が、ソート後も保持されるソートアルゴリズムのことです。
例えば、以下のようなデータがあるとします。
[(5, 'A'), (2, 'B'), (5, 'C'), (1, 'D')]
ここで、括弧内の最初の数値はソートキー、2番目の文字は元の順序を識別するための情報とします。
不安定ソートの場合、ソート結果は以下のようになる可能性があります。
[(1, 'D'), (2, 'B'), (5, 'C'), (5, 'A')]
この場合、元の順序では 'A' が 'C' より前でしたが、ソート後には 'C' が 'A' より前になっています。
安定ソートの場合、ソート結果は必ず以下のようになります。
[(1, 'D'), (2, 'B'), (5, 'A'), (5, 'C')]
元の順序が保持され、'A' が 'C' より前に来ています。
安定ソートは、複数のキーでソートを行う場合や、データの元の順序に意味がある場合に重要となります。Go言語の sort.Stable
関数は、この安定ソートを提供します。
Bentley-McIlroy テスト
Bentley-McIlroy テストは、ソートアルゴリズムの堅牢性と性能を評価するために、Jon Bentley と Doug McIlroy によって考案されたテスト手法です。彼らは、ソートアルゴリズムの最悪ケースに近い入力データ(例えば、ほとんどソートされているが一部が逆順になっているデータ、多くの重複値を含むデータなど)を生成し、それらのデータに対してアルゴリズムが正しく動作し、かつ効率的であるかを検証します。
このテストは、特にクイックソートのようなアルゴリズムが特定の入力パターンで性能が劣化する可能性があることを考慮して設計されています。ソートアルゴリズムのテストにおいて、単にランダムなデータだけでなく、エッジケースや最悪ケースを網羅的にテストすることは、アルゴリズムの信頼性を保証するために不可欠です。
n * lg(n) * lg(n)
の意味
n * lg(n) * lg(n)
(ここで lg(n)
は log2(n)
を意味することが多い) は、ソートアルゴリズムの計算量のオーダーを示す式の一部として使われることがあります。
n
: ソート対象の要素数。lg(n)
(またはlog n
): 対数関数。ソートアルゴリズムの多くは、比較ベースのソートにおいてO(n log n)
の計算量を持つことが知られています(例:マージソート、ヒープソート、クイックソートの平均ケース)。これは、要素を分割したり、ツリー構造を操作したりする際に現れる特性です。
n * lg(n) * lg(n)
は、O(n log^2 n)
とも書かれ、O(n log n)
よりもわずかに悪い計算量を示します。これは、特定のソートアルゴリズムや、ソート中に発生する追加の操作(例えば、安定性を維持するための要素の移動や、特定のデータ構造の操作)によって現れる可能性があります。
このコミットの文脈では、TestStableBM
関数が安定ソートのテストに使用されており、n * lg(n) * lg(n)
は、安定ソートのテストにおいて許容されるスワップ操作数の上限を計算するための基準値として使用されています。この値は、安定ソートの理論的な複雑性や、特定の安定ソートアルゴリズム(例えば、マージソートをベースにした安定ソート)が実行する操作の総数に関連していると考えられます。
技術的詳細
このコミットは、src/pkg/sort/sort_test.go
ファイル内の TestStableBM
関数における変更です。
変更前:
func TestStableBM(t *testing.T) {
testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) })
}
変更後:
func TestStableBM(t *testing.T) {
testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) / 3 })
}
TestStableBM
関数は、testBentleyMcIlroy
というヘルパー関数を呼び出しています。このヘルパー関数は、ソート対象の関数 (Stable
、Go言語の sort.Stable
に対応) と、許容される操作回数を計算するための関数を引数に取ります。
変更の核心は、許容される操作回数を計算する匿名関数 func(n int) int { return n * lg(n) * lg(n) }
が func(n int) int { return n * lg(n) * lg(n) / 3 }
に変更された点です。
これは、n * lg(n) * lg(n)
で計算される元の許容上限を 3
で割ることを意味します。つまり、安定ソートのテストにおいて許容されるスワップ操作の回数が、以前の約3分の1に厳格化されたことになります。
この変更は、以下のことを示唆しています。
- より効率的な実装の要求: 安定ソートの実装が、以前考えられていたよりも少ないスワップ操作で達成可能である、あるいはそうあるべきだという認識。
- テストの感度向上: テストが、非効率な実装や、安定性を損なう可能性のあるバグをより早期に、より正確に検出できるようになる。
- アルゴリズムの特性への適合:
sort.Stable
の内部実装が、特定の安定ソートアルゴリズム(例えば、インプレースマージソートの最適化版など)の特性を反映しており、そのアルゴリズムがn * lg(n) * lg(n) / 3
のオーダーでスワップ操作を完了できることを示唆している可能性があります。
lg(n)
は通常 log2(n)
を指し、n
はソート対象の要素数です。この式は、ソートアルゴリズムの計算量のオーダーに関連しており、特に安定ソートにおける要素の移動や比較の回数を概算するために用いられます。上限を厳しくすることで、Go言語の sort.Stable
が高い性能基準を満たしていることを保証しようとしています。
コアとなるコードの変更箇所
--- a/src/pkg/sort/sort_test.go
+++ b/src/pkg/sort/sort_test.go
@@ -350,7 +350,7 @@ func TestHeapsortBM(t *testing.T) {
}
func TestStableBM(t *testing.T) {
- testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) })
+ testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) / 3 })
}
// This is based on the "antiquicksort" implementation by M. Douglas McIlroy.
コアとなるコードの解説
変更は src/pkg/sort/sort_test.go
ファイルの TestStableBM
関数内の一行のみです。
元のコード:
testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) })
変更後のコード:
testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) / 3 })
この変更は、testBentleyMcIlroy
関数に渡される3番目の引数、すなわち許容される操作回数を計算する匿名関数を変更しています。
t
: テストヘルパー (*testing.T
)。Stable
:sort.Stable
関数への参照。これがテスト対象の安定ソートアルゴリズムです。func(n int) int { return n * lg(n) * lg(n) / 3 }
: この匿名関数は、入力サイズn
に応じて、Stable
関数が実行を完了するために許容される最大スワップ操作数を返します。変更前はn * lg(n) * lg(n)
であったものが、変更後はその値を3
で割ったものになっています。
この変更により、TestStableBM
は、sort.Stable
の実装がより少ないスワップ操作で安定ソートを達成できることを要求するようになります。もし sort.Stable
の現在の実装がこの新しい、より厳しい上限を満たせない場合、このテストは失敗するでしょう。これは、Go言語の sort
パッケージの品質と性能を継続的に向上させるための、テスト駆動開発の一環と見なすことができます。
関連リンク
- Go言語の
sort
パッケージのドキュメント: https://pkg.go.dev/sort - Go言語の
sort.Stable
関数のドキュメント: https://pkg.go.dev/sort#Stable - Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにある
https://golang.org/cl/12907045
は、このGerritインスタンスへのリンクです)
参考にした情報源リンク
- https://github.com/golang/go/commit/2f81dfd53f5f0b422b038793f514cbf4ce58e573
- Go言語の
sort
パッケージのソースコード (sort_test.go
): https://github.com/golang/go/blob/master/src/sort/sort_test.go - 安定ソートに関する一般的な情報 (Wikipediaなど)
- ソートアルゴリズムの計算量に関する情報 (O記法など)
- Bentley-McIlroy テストに関する情報 (ソートアルゴリズムのテスト手法として)