Rubyの一部の実装では、並べ替えは安定していますが、それに依存するべきではありません。Rubyのソートの安定性は、実装によって定義されます。
ドキュメントの内容
ドキュメントには、ソートが安定していることに依存すべきではないと書かれています。
結果は安定しているとは限りません。2つの要素の比較が0を返す場合、要素の順序は予測できません。
これは、ソートが安定しているかどうかを示すものではないことに注意してください。安定しているとは限らないというだけです。Rubyの特定の実装は、安定したソートを持ち、それでもドキュメントと一貫性がある可能性があります。また、ソートが不安定になったり、ソートがいつでも安定するかどうかが変わる可能性があります。
Rubyが実際に行うこと
このテストコードはtrue
、Rubyの並べ替えが安定しfalse
ている場合、または安定していない場合に出力されます。
Foo = Struct.new(:value, :original_order) do
def <=>(foo)
value <=> foo.value
end
end
size = 1000
unsorted = size.times.map do |original_order|
value = rand(size / 10)
Foo.new(value, original_order)
end
sorted = unsorted.sort
stably_sorted = unsorted.sort_by do |foo|
[foo.value, foo.original_order]
end
p [RUBY_PLATFORM, RUBY_VERSION, RUBY_PATCHLEVEL, sorted == stably_sorted]
LinuxボックスにインストールしたすべてのRubiesの結果は次のとおりです。
["java", "1.8.7", 357, false]
["java", "1.9.3", 551, false]
["x86_64-linux", "1.8.7", 374, false]
["x86_64-linux", "1.8.7", 374, false]
["x86_64-linux", "1.8.7", 376, false]
["x86_64-linux", "1.9.3", 392, false]
["x86_64-linux", "1.9.3", 484, false]
["x86_64-linux", "1.9.3", 551, false]
["x86_64-linux", "2.0.0", 643, false]
["x86_64-linux", "2.0.0", 648, false]
["x86_64-linux", "2.1.0", 0, false]
["x86_64-linux", "2.1.10", 492, false]
["x86_64-linux", "2.1.1", 76, false]
["x86_64-linux", "2.1.2", 95, false]
["x86_64-linux", "2.1.3", 242, false]
["x86_64-linux", "2.1.4", 265, false]
["x86_64-linux", "2.1.5", 273, false]
["x86_64-linux", "2.1.6", 336, false]
["x86_64-linux", "2.1.7", 400, false]
["x86_64-linux", "2.1.8", 440, false]
["x86_64-linux", "2.1.9", 490, false]
["x86_64-linux", "2.2.0", 0, true]
["x86_64-linux", "2.2.1", 85, true]
["x86_64-linux", "2.2.2", 95, true]
["x86_64-linux", "2.2.3", 173, true]
["x86_64-linux", "2.2.4", 230, true]
["x86_64-linux", "2.2.5", 319, true]
["x86_64-linux", "2.2.6", 396, true]
["x86_64-linux", "2.3.0", 0, true]
["x86_64-linux", "2.3.1", 112, true]
["x86_64-linux", "2.3.2", 217, true]
["x86_64-linux", "2.3.3", 222, true]
["x86_64-linux", "2.4.0", 0, true]
["x86_64-linux", "2.4.0", -1, true]
["x86_64-linux", "2.4.0", -1, true]
["x86_64-linux", "2.4.0", -1, true]
["x86_64-linux", "2.4.0", -1, true]
["x86_64-linux", "2.4.1", 111, true]
["x86_64-linux", "2.4.2", 198, true]
["x86_64-linux", "2.4.5", 335, true]
["x86_64-linux", "2.4.9", 362, true]
["x86_64-linux", "2.5.0", 0, true]
["x86_64-linux", "2.5.3", 105, true]
["x86_64-linux", "2.5.7", 206, true]
["x86_64-linux", "2.6.0", 0, true]
["x86_64-linux", "2.6.2", 47, true]
["x86_64-linux", "2.6.3", 62, true]
["x86_64-linux", "2.6.4", 104, true]
["x86_64-linux", "2.6.5", 114, true]
["x86_64-linux", "2.6.6", 146, true]
["x86_64-linux", "2.7.0", 0, true]
["x86_64-linux", "2.7.1", 83, true]
["x86_64-linux", "2.7.2", 137, true]
["x86_64-linux", "3.0.0", 0, true]
["x86_64-linux", "3.0.0", -1, true]
["x86_64-linux", "3.0.1", 64, true]
["x86_64-linux", "3.0.2", 107, true]
LinuxではJRubyが不安定で、2.2より前のMRIが不安定であることがわかります。MRI> = 2.2.0は安定しています(これもLinux上で)。
ただし、プラットフォームは重要です。上記の結果は、LinuxのMRI 2.4.1でソートが安定していることを示していますが、同じバージョンはWindowsでは不安定です。
["x64-mingw32", "2.4.1", 111, false]
LinuxではMRIのソートが安定しているのに、Windowsでは安定していないのはなぜですか?
Ruby実装の単一バージョン内であっても、ソートアルゴリズムは変更される可能性があります。MRIは、少なくとも3つの異なる種類を使用できます。ソートルーチンは、 util.cの一連の#ifdefを使用してコンパイル時に選択されます。MRIには、少なくとも2つの異なるライブラリからのソートを使用する機能があるようです。また、独自の実装があります。
あなたはそれについて何をすべきですか?
ソートは安定している可能性がありますが、安定性を保証することはできないため、Rubyのソートが安定していることに依存するコードを記述しないでください。そのコードは、別のバージョン、実装、またはプラットフォームで使用すると破損する可能性があります。