マージアルゴリズムは、複数のソート済みリストを入力として受け取り、入力リストのすべての要素をソートされた順序で含む単一のリストを出力するアルゴリズム群です。これらのアルゴリズムは、さまざまなソートアルゴリズムのサブルーチンとして使用され、最も有名なのはマージソートです。

マージアルゴリズムは、比較に基づくソートアルゴリズムであるマージソートアルゴリズムにおいて重要な役割を果たします。概念的には、マージソートアルゴリズムは次の2つのステップで構成されます。
マージアルゴリズムは、マージソートアルゴリズムの中で繰り返し使用されます。
図には、マージソートの例が示されています。まず、7つの整数からなる未ソート配列を用意します。この配列を7つのパーティションに分割し、各パーティションには1つの要素を含めてソートします。次に、ソートされたパーティションをマージして、より大きなソート済みパーティションを作成し、最終的に1つのパーティション(ソート済みの配列)が残るまでこの処理を繰り返します。
2 つのソート済みリストを 1 つにマージすることは、線形時間と線形または定数空間 (データ アクセス モデルによる)で実行できます。次の擬似コードは、入力リスト (リンク リストまたは配列) AとBを新しいリストCにマージするアルゴリズムを示しています。[ 1 ] [ 2 ] : 104関数headはリストの最初の要素を返します。要素を「削除」するとは、通常はポインタまたはインデックスをインクリメントすることによって、その要素をリストから削除することを意味します。
アルゴリズムmerge(A, B)は、入力A, B : リスト を返します C := 新しい空のリスト A が空でなく、B も空でない間、 head(A) ≤ head(B)ならば、 Cにhead(A)を追加する Aの頭文字を落とす それ以外 Cにhead(B)を追加する Bの頭を落とす // この時点で、A または B のいずれかが空になっている。残りの入力リストを空にする。while A が空でない間、 Cにhead(A)を追加する Aの頭文字を落とす Bが空でない間、 Cにhead(B)を追加する Bの頭を落とす Cを返す
入力が連結リストの場合、このアルゴリズムは一定量の作業領域のみを使用するように実装できます。リストのノード内のポインタは、記録管理と最終的な結合リストの構築に再利用できます。
マージソートアルゴリズムでは、このサブルーチンは通常、単一の配列Aの2 つのサブ配列A[lo..mid]、A[mid+1..hi] をマージするために使用されます。これは、サブ配列を一時配列にコピーしてから、上記のマージアルゴリズムを適用することで実行できます。[ 1 ]一時配列の割り当ては回避できますが、速度とプログラミングの容易さが犠牲になります。さまざまなインプレースマージアルゴリズムが考案されており、[ 3 ]線形時間境界を犠牲にしてO ( n log n )アルゴリズムを生成することもあります。[ 4 ]マージソート §バリアントの説明を参照してください。
k分割マージは、バイナリマージを任意の数kのソート済み入力リストに一般化したものです。k 分割マージの応用例としては、ペイシェンスソート[ 5 ]や、入力をメモリに収まるk = 1 / M - 1 個のブロックに分割し、それらを 1 つずつソートしてからマージする外部ソートアルゴリズム[ 2 ] : 119–120など、さまざまなソートアルゴリズムが挙げられます。
この問題にはいくつかの解決策が存在する。単純な解決策としては、k個のリストをループ処理して毎回最小の要素を選択し、すべてのリストが空になるまでこのループを繰り返す方法がある。
最悪の場合、このアルゴリズムは、リストに合計n個の要素がある場合、処理を実行するために( k − 1)( n − k / 2 )回の要素比較を実行します。 [ 6 ]リストを最初の要素をキーとする優先度付きキュー(最小ヒープ) に格納することで、このアルゴリズムを改善できます。
出力する次の最小要素の検索(find-min)とヒープ順序の復元は、O (log k )時間(より具体的には、2⌊log k⌋回の比較[ 6 ] )で実行でき、問題全体はO ( n log k )時間(約2n⌊log k⌋回の比較)で解決できる。[ 6 ] [ 2 ]: 119-120
この問題に対する3つ目のアルゴリズムは、二項結合アルゴリズムを基にした分割統治法である。
このアルゴリズムへの入力リストが長さ順に並べられ、短いものから順に並べられている場合、必要な比較回数はn ⌈log k ⌉未満、つまりヒープベースのアルゴリズムで使用される回数の半分以下になります。実際には、ヒープベースのアルゴリズムとほぼ同じくらいの速さまたは遅さになる可能性があります。[ 6 ]
バイナリマージアルゴリズムの並列バージョンは、並列マージソートの構成要素として使用できます。次の擬似コードは、このアルゴリズムを並列分割統治スタイルで示しています(Cormen et al. [ 7 ] : 800から改変)。これは、2 つのソート済み配列AとBに対して動作し、ソートされた出力を配列Cに書き込みます。表記A[i...j]は、インデックスiから j までのAの部分を表します(iとjは含みません)。
アルゴリズムmerge(A[i...j], B[k...ℓ], C[p...q])は入力A, B, C : 配列 i、j、k、ℓ、p、q:インデックス m = j - iとする。 n = ℓ - k m < nの場合、 A と B を交換する // A がより大きな配列であることを確認する: i、j は引き続き A に属し、k、ℓ は B に属する mとnを入れ替える m ≤ 0の場合、以下を返す// 基本ケース、マージするものはありませんlet r = ⌊(i + j)/2⌋ let s = binary-search(A[r], B[k...ℓ]) let t = p + (r - i) + (s - k) C[t] = A[r] 並行して merge(A[i...r], B[k...s], C[p...t]) merge(A[r+1...j], B[s...ℓ], C[t+1...q])
このアルゴリズムは、 AまたはBのうち大きい方を (ほぼ) 等しい半分に分割することによって動作します。次に、もう一方の配列を、最初の配列の中間値より小さい値を持つ部分と、それより大きいか等しい値を持つ部分に分割します。 (バイナリサーチサブルーチンは、 A [ r ]がBにある場合のB内のインデックスを返します。これは常にkからℓの間の数値です。) 最後に、各半分のペアが再帰的にマージされ、再帰呼び出しは互いに独立しているため、並列に実行できます。再帰の基本ケースにシリアルアルゴリズムを使用するハイブリッドアプローチは、実際にはうまく機能することが示されています[ 8 ] 。
合計n個の要素を持つ 2 つの配列に対してアルゴリズムが実行する作業、つまりその逐次バージョンの実行時間はO ( n )です。n 個の要素をCにコピーする必要があるため、これは最適です。アルゴリズムのスパンを計算するには、漸化式を導出する必要があります。merge の 2 つの再帰呼び出しは並列であるため、 2 つの呼び出しのうちコストの高い方のみを考慮すれば十分です。最悪の場合、再帰呼び出しの 1 つに含まれる要素の最大数は、最大で要素数の多い配列は完全に半分に分割されるため、二分探索のコストから、上限として次の漸化式が得られます。
解決策はつまり、プロセッサ数が無制限の理想的なマシンでは、それだけの時間がかかるということである。[ 7 ]: 801-802
注:このルーチンは安定していません。AとBを分割して同じ項目を分離すると、 Cではそれらが混在してしまいます。また、AとBを入れ替えると、同じ項目が両方の入力配列に分散している場合、順序が崩れてしまいます。そのため、このアルゴリズムをソートに使用すると、安定しないソート結果が生成されます。
また、2つのソート済みリストをマージする単一の処理内で並列性を導入するアルゴリズムも存在する。これらは、フィールドプログラマブルゲートアレイ(FPGA)、専用ソート回路、および単一命令複数データ(SIMD)命令を備えた最新のプロセッサで使用できる。
既存の並列アルゴリズムは、バイトニックソーターまたは奇偶マージソートのいずれかのマージ部分の修正に基づいています。[ 9 ] 2018年、Saitoh M.らは、ハードウェアでの効率的なパイプライン処理を妨げていたマルチサイクルフィードバックデータパスの除去に焦点を当てたFPGA向けMMS [ 10 ]を発表しました。同じく2018年、Papaphilippou P.らは、ハードウェア利用率とパフォーマンスを向上させるために、必要なリソースを最小限に抑えたFLiMS [ 9 ]を発表しました。P/2個の比較交換ユニットからなるパイプラインステージを、FPGAサイクルあたりP個の要素の並列処理と統合する。
一部のプログラミング言語では、ソートされたコレクションをマージするための組み込み機能またはライブラリ機能が提供されています。
C ++の標準テンプレートライブラリには、2 つのソート済みイテレータ範囲をマージする関数std::mergeと、2 つの連続するソート済み範囲をインプレースでマージするstd::inplace_merge があります。さらに、std::list (リンクリスト) クラスには、別のリストを自身にマージする独自のmergeメソッドがあります。マージされる要素の型は、小なり演算子 ( < ) をサポートしているか、カスタム比較器を指定する必要があります。
C++17では、逐次実行、並列実行、並列非逐次実行など、さまざまな実行ポリシーが認められています。[ 11 ]
Pythonの標準ライブラリ(2.6以降)には、 heapqモジュールに複数のソート済みイテラブルを受け取り、それらを単一のイテレータにマージするmerge関数もあります。 [ 12 ]