追加メモリを使用せずにソートをマージ

長い間、追加のメモリを使用しないようにマージして配列のソートを記述することは不可能であると考えていましたが、ランタイムはO(N * log(N))のままでした。 したがって、 karlicosがそのようなアルゴリズムの説明へのリンクを共有したとき、私は興味がありました。 ネットワーク上で検索した結果、人々はアルゴリズムについて知っていますが、誰もアルゴリズムに特に関心はなく、困難で効果がないと考えられています。 おそらく、それらはこのアルゴリズムのある種の「安定した」バージョンを意味しますが、それでも誰も不安定なものを必要としません。



しかし、私はまだ試してみることにしました。







線形マージ





アルゴリズムのアイデアは非常に簡単です。 O(N)比較と要素の交換で同じ配列の2つの順序付けられた部分をマージするだけです。 これは次のように行われます。



説明は非常に長いですが、理解できます。 1つの合併の交換数は約5 * Nで、比較の数は約6 * Nです (残基の長さに依存します)。 完全なソートのために、これらの数値にlog(N)が掛けられ、ロットが取得されます。



ソートのためのアルゴリズム適応





私たちが簡単になり、アルゴリズムがより効率的に機能するように、次のことに注意してください。





これらの考えを武器に、プログラムを作成しています(これまではint []型の配列のみ)。 最初に、2の累乗に等しい長さのフラグメントをソートおよびマージし、右側に空きスペースができるように厳密に左から右に移動します。 クリップボードに到達したら、未融合だがソートされたフラグメントをマージします。 クリップボード+残りを並べ替え、結果を残りの配列とマージし、新しく作成されたクリップボードを並べ替えます-配列が並べ替えられます。 約100行のアルゴリズムが判明しました。 真実はかさばると言われました。 効率はどうですか?



正直なところ、彼が標準のqsortに3回まで負けないことを望んでいました。 しかし、実際の条件(最大10 8の長さの配列)での比較では、qsortアルゴリズム比較回数で約10%を上回り 、総操作時間で1.2から1.3倍のパフォーマンスを示しました。 おそらくこれは、icmp関数(指定されたアドレスの2つの整数を比較する)がインラインで置換されるという事実によるものですが、挿入されるコードはかなりひどいものになります(チェックしました)。



一般的に、すべてが彼らが言ったほど悪くはありません。



しかし、アルゴリズムの説明にはどのようなエラーがありましたか? 事実は、要素がクリップボードまたは残りの部分に到達した場合、ソート後に最初のR位置の1つにあるはずであるため、最終的には配置されません。 修正するには、最後のマージでインデックスRを持つセル内の要素がどこに落ちたかを追跡し、この場所への最初のフラグメントに対して最後のソートを行う必要があります(その長さはRよりわずかに長い場合があります)。



UPD:アルゴリズムでは、ここで説明したように、「配列の断片の並べ替え」に関連するエラーがもう1つ発見されました。 ソース配列に多数の同一の要素がある場合、配列の同じフラグメントの異なる部分に同じ最初の要素がある場合があります。 また、ソート中にこれらのピースの順序に違反すると、配列全体のソート結果が不正確になる可能性があります。



この効果に対処するには、並べ替えの際に、ピースの最初の要素を比較し、同じ場合は最後の要素を比較する必要があります。 そして、これらの要素のペアで辞書順に並べ替えます。



All Articles