主にソートされたデータに対してどのソートアルゴリズムが最適に機能しますか?[閉まっている]


174

主にソートされたデータに対してどのソートアルゴリズムが最も効果的ですか?


コンテキストの欠如から推測-あなたは中間結果をディスクにこぼす必要がないメモリ内ソートについて質問していますか?
ジョナサンレフラー、

1
これらのアニメーションによると、挿入ソートは、ほとんどの場合ソートされたデータで最もよく機能します。
12

回答:


259

アニメーションgifを見る非常に科学的な方法に基づいて、挿入とバブルの並べ替えは良い候補だと思います。


19
ところで、これは素晴らしいリンクです。称賛と+1
2008年

5
バブルソートはひどいです。常にO(n ^ 2)です。それが正しいように、少なくともあなたの答えからそれを取り除いてください。
jjnguy 2008年

79
jjnguy、それはただの間違いです。アルゴリズムクラスを再受験する必要があると思います。ほぼソートされたデータ(適応型の場合)では、O(N)です。ただし、データを2回通過する必要があり、挿入はほぼソートされたデータに対して1回のみを使用するため、挿入が勝者となります。バブルはまだ良いです
mmcdole 2008年

3
ただし、データがほとんどソートされていない場合は、パフォーマンスが大幅に低下します。個人的にはまだ使いません。
Blorgbeardは

5
私がそれを試したとき、そのリンクは壊れていました。代わりにこれを試してください:sorting-algorithms.com
Michael La Voie

107

少数のアイテムのみ=> INSERTION SORT

アイテムはほとんどソート済みです=>挿入ソート

最悪のシナリオが懸念される=> HEAP SORT

良い平均ケースの結果に興味がある=> QUICKSORT

アイテムは密な宇宙から描画されます=>バケットソート

できるだけ少ないコードを書くことを望んでいる=>挿入ソート


1
それはまさに私が探していた答えの種類です。私は本を読みましたが、特定のケースでのアルゴリズムの選択について明確な説明が見つからないようです。詳しく説明するか、リンクを渡して、私が理解できるようにしてください。もう少し?ありがとう
Simran kaur 2014年

9
「データは既に別の基準でソートされている=> MERGE SORT」を追加する必要があります
Jim Hunziker

30

ティムソート

Timsortは「適応的で安定した自然なマージソート」であり、「部分的に順序付けられた多くの種類の配列で超自然的なパフォーマンスを発揮します」(lg(N!)未満の比較が必要で、N-1程度)の」を備えています。Pythonの組み込みsort()このアルゴリズムをしばらく使用してきましたが、明らかに良い結果が得られています。これは、実際のデータセットで頻繁に発生する、入力内の部分的にソートされたサブシーケンスを検出して利用するように特別に設計されています。通常、ポインタを交換するだけなので、比較はリスト内のアイテムを交換するよりもはるかにコストがかかります。これにより、timsortが優れた選択肢になることがよくあります。ただし、比較が常に非常に安価であることを知っている場合(たとえば、32ビット整数をソートするおもちゃのプログラムを作成するなど)、パフォーマンスが向上する可能性が高い他のアルゴリズムが存在します。もちろん、timsortを利用する最も簡単な方法はPythonを使用することですが、Pythonはオープンソースであるため、コードを借用することもできます。または、上記の説明には、独自の実装を作成するのに十分な詳細が含まれています。


16
log(n!)はΟ(n * log(n))なので、「超自然的」ではありません。
jfs 2008年

JDK7に含まれるJava実装は次のとおりです。cr.openjdk.java.net/
Tim

log(n!)は高速ではありません。wolframalpha.com/input/?i=plot[log(N!)、{N、0,1000 }]
Behrooz

9
@JF Sebastian:timsortはlg(n!)、ほぼソートされた配列での比較よりもずっと高速O(n)です。| @behrooz:いいえ比較ソートはより良いの平均的なケースを持つことができるO(n log n)、とlg(n!)ありますO(n log n)。したがって、timsortの最悪のケースは、他の比較ソートのそれよりも漸近的に悪くはありません。さらに、その最良のケースは、他の比較ソートと同等かそれ以上です。
Artelius 2009

3
Timsortは最悪の場合でもO(nlogn)ですが、その良いケースは非常に喜ばしいものです。ここでの比較は、いくつかのグラフで、です:stromberg.dnsalias.org/~strombrg/sort-comparison Cythonでtimsortがほぼ同じ速PythonのCのtimsortに建てられたようでなかったこと注
user1277476

19

次の動作の挿入ソート:

  1. kスロットの各要素について1..n、最初に次のことを確認しel[k] >= el[k-1]ます。その場合は、次の要素に進みます。(明らかに最初の要素をスキップします。)
  2. そうでない場合は、要素のバイナリ検索を使用し1..k-1て挿入位置を決定し、要素をスクートします。(これは、k>TどこTかにしきい値がある場合にのみ行う可能性があります。小さい場合、kこれはやりすぎです。)

このメソッドは、比較の数が最小になります。


ソートされていない要素の数が非常に少ない場合(1つまたは2つなど)は、バブルソートがこれに勝る可能性があると思いますが、一般的に、これがおそらく最良の解決策だと思います。
ソル

手順1のため、既に並べ替えられている要素については、比較が1回、データ移動が0回であり、これは明らかに実行できる最善の方法です。ステップ2は改善できるものですが、バブルによって同じ数の要素が移動し、実装によってはより多くの比較が行われる可能性があります。
Jason Cohen

実際、さらに考えてみると、バブルソートの方が思っていたよりも強いと思います。それは実際にはかなりトリッキーな質問です。たとえば、リストが最後にあるはずの要素が最初であることを除いて、リスト全体が並べ替えられている場合、バブルソートはあなたが説明したものよりもはるかに優れています。
ソル

これを実装しようとしましたが、要素を挿入するためにブロック全体を移動する必要があるため、バイナリ検索はあまり改善されていません。したがって、2xrangeの代わりにrange + logb(range)を取得します。
この

11

内省的なソートを試してください。http://en.wikipedia.org/wiki/Introsort

これはクイックソートに基づいていますが、ほぼソートされたリストに対してクイックソートが持つ最悪の場合の動作を回避します。

トリックは、このソートアルゴリズムが、クイックソートがワーストケースモードに入り、ヒープまたはマージソートに切り替わるケースを検出することです。ほぼソートされたパーティションは、いくつかの非ナイーブパーティションメソッドによって検出され、小さなパーティションは挿入ソートを使用して処理されます。

より多くのコードと複雑さのコストで、すべての主要なソートアルゴリズムの中で最高のものを手に入れます。また、データがどのように見えても、最悪の場合の動作に決して遭遇しないと確信できます。

C ++プログラマーの場合は、std :: sortアルゴリズムを確認してください。内部で内省ソートをすでに使用している可能性があります。


7

Splaysortは、に基づいて曖昧ソーティング方法であるスプレー木、適応的バイナリツリーのタイプ。スプレイソートは、部分的にソートされたデータだけでなく、部分的に逆ソートされたデータ、または実際に何らかの種類の既存の順序を持​​つデータにも適しています。一般的な場合はO(nlogn)、データが何らかの方法(順方向、逆方向、オルガンパイプなど)で並べ替えられている場合はO(n)です。

挿入ソートよりも優れている点は、データがまったくソートされていない場合にO(n ^ 2)の動作に戻らないため、データを使用する前にデータが部分的にソートされていることを完全に確認する必要はありません。 。

その不利な点は、必要なスプレイツリー構造の余分なスペースオーバーヘッド、およびスプレイツリーの構築と破棄に必要な時間です。ただし、予想されるデータのサイズと事前ソートの量によっては、速度の向上のためにオーバーヘッドが価値がある場合があります。

splaysortの論文が実践&経験-ソフトウェアに掲載されました。



5

ダイクストラのスムースソートは、すでにソートされたデータに対して優れたソートです。これは、O(n lg n)ワーストケースとO(n)ベストケースで実行されるヒープソートバリアントです。あなたがそれがどのように機能するか知りたければ、私アルゴリズムの分析書きました

自然なマージソートは、このためのもう1つの本当に良いものです。これは、入力を複数の異なるソート範囲の連結として扱い、マージアルゴリズムを使用してそれらを結合することによって機能するボトムアップマージソートバリアントです。すべての入力範囲が並べ替えられるまで、このプロセスを繰り返します。データがすでにソートされていて、O(n lg n)が最悪の場合、これはO(n)時間で実行されます。非常にエレガントですが、実際にはTimsortやsmoothsortのような他のいくつかの適応ソートほど良くありません。


他の並べ替えアルゴリズムと比較したsmoothsortの実行時定数は何ですか?(つまり、同じデータのruntime(smoothsort)/ runtime(insertionsort))
Arne Babenhauserheide

4

要素が既に並べ替えられているか、要素が少ない場合は、挿入並べ替えの完全なユースケースになります。


3

挿入ソートには時間がかかりますO(n +反転の数)。

反転は次の(i, j)ようなペアですi < j && a[i] > a[j]。つまり、順不同のペアです。

「ほぼソートされている」ことの1つの指標は、反転の数です。「ほとんどソートされたデータ」は、反転がほとんどないデータを意味する可能性があります。反転の数が線形であることがわかっている場合(たとえば、ソートされたリストにO(1)要素を追加したばかりの場合)、挿入ソートにはO(n)時間かかります。


2

他の誰もが言ったように、素朴なQuicksortには注意してください-ソートされたデータまたはほとんどソートされたデータでO(N ^ 2)のパフォーマンスが得られる可能性があります。それでも、ピボットの選択に適切なアルゴリズム(ランダムまたは3つの中央値- クイックソートのピボットの選択を参照)を使用すると、クイックソートは正常に機能します。

一般に、挿入ソートなどのアルゴリズムを選択することの難しさは、Quicksortが実際に高速になるほどデータの順序が適切でない場合を決定することです。


2

実際の答えを得るには、アルゴリズムをコーディングして、代表的なデータサンプルに対してそれらをプロファイリングする必要があると思うので、ここですべての答えを装うつもりはありません。しかし、私は一晩中この質問について考えていました。これが私にこれまでに起こったことであり、何がどこで最もうまく機能するかについての推測があります。

Nをアイテムの総数、Mを順序外の数とする。

バブルソートでは、2 * M + 1のようにN個のアイテムすべてを通過させる必要があります。Mが非常に小さい場合(0、1、2?)、これは非常に難しいでしょう。

Mが小さい(たとえば、log Nより小さい)場合、挿入ソートの平均パフォーマンスは非常に高くなります。ただし、私が見られないトリックがない限り、ワーストケースのパフォーマンスは非常に悪くなります。(正しいですか?注文の最後の項目が最初に来る場合、私が見る限り、すべての項目を挿入する必要があります。これにより、パフォーマンスが低下します。)このための信頼性の高い並べ替えアルゴリズムがあると思います。ケースですが、それが何かはわかりません。

Mが大きい(たとえば、log Nと等しいか大きい)場合、内省的ソートがほぼ確実に最適です。

これらすべての例外:実際に事前にソートされていない要素がわかっている場合は、それらのアイテムを引き出して、内省的ソートを使用してソートし、2つのソート済みリストを1つのソート済みリストにマージするのが最善の策です。故障しているアイテムがすぐにわかる場合は、これも良い一般的な解決策になります。しかし、これを行う簡単な方法を見つけることはできませんでした。

さらなる考察(一晩):M + 1 <N / Mの場合、リストをスキャンして、ソートされた行でN / Mのランを探し、そのランをいずれかの方向に展開して、アウトオブアウトを見つけることができます。 -注文商品。これは最大で2Nの比較になります。次に、並べ替えられていないアイテムを並べ替え、2つのリストで並べ替えられたマージを実行できます。全体の比較は、4N + M log2(M)のようなものより少なくなるはずです。これは、特殊化されていない並べ替えルーチンを打ち負かすものだと思います。(さらに考えました:これは私が考えていたよりもトリッキーですが、それでも合理的に可能だと思います。)

質問の別の解釈は、順序が狂っている項目の多くがあるかもしれないが、それらはリスト内のあるべき場所に非常に近いということです。(並べ替えられたリストから始めて、他のすべてのアイテムをその後ろに来るものと交換することを想像してください。)その場合、バブルソートは非常にうまく機能すると思います-パスの数はアイテムの最も遠い場所に比例すると思いますです。順不同のすべてのアイテムが挿入をトリガーするため、挿入ソートはうまく機能しません。内省的なソートなどもうまくいくと思います。


1

ソートアルゴリズム、データ構造、または上記へのリンクを持つ何かのための特定の実装が必要な場合、CodePlex の優れた「データ構造とアルゴリズム」プロジェクトをお勧めできますか?

ホイールを再発明することなく、必要なものがすべて揃っています。

私の小さな塩の粒だけ。


1

回答でこの目的のためのこのソートアルゴリズムの素晴らしいコレクションは、Gnome Sortを欠いているように見えます。これも適切であり、おそらく実装の労力が最小です。


0

挿入の並べ替えは、並べ替えられた入力のO(n)が最適です。そして、ほとんどソートされた入力に非常に近いです(クイックソートより優れています)。


0

ポンダーヒープを試してみてください。私はそれがO(n lg n)ソートの中で最も一貫していると信じています。


ここでは一貫性は問題ではありません。ヒープソートは、ソートされたデータでもO(n lg n)を提供し、実際には適応的ではありません。実行可能なオプションは、挿入ソート、ティムソート、およびバブルソートです。
最大

0

バブルソート(または、より安全で双方向のバブルソート)は、ほとんどの場合ソートリストに理想的ですが、調整したくしソート(初期ギャップサイズがはるかに小さい)は、リストがなかった場合は少し速くなると思いますt完全にソートされています。コムソートはバブルソートに低下します。


0

まあそれはユースケースに依存します。変更される要素がわかっている場合は、削除および挿入が私にとっては最良のケースです。


1
この「私に関する限り」のアルゴリズム効率のテストは私の一日を明るくしました:)しかし、「削除して挿入する」と書いているときに真剣であるということは、挿入ソート(以前の回答ですでに述べた)を意味するのか、それとも新しい種類のアルゴリズム?もしそうなら、あなたの答えを拡大してください。
yoniLavi 2015

0

バブルソートは間違いなく勝者ですレーダーの次の問題は挿入ソートです。


4
説明付きで回答を投稿してください。

1
重複を避けるために、投稿する前に利用可能な回答を確認することをお勧めします。
占領者

-1

QuickSortに近づかないでください。事前にソートされたデータに対しては非常に非効率的です。挿入ソートは、できるだけ少数の値を移動することにより、ほとんどソートされたデータを適切に処理します。



1
はい。ただし、高価にならない限り、ピボットの選択は完璧ではありません。
user1277476
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.