マージソートのソースを表示
←
マージソート
ナビゲーションに移動
検索に移動
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループに属する利用者のみが実行できます:
登録利用者
。
このページのソースの閲覧やコピーができます。
{{Infobox algorithm |name= マージソート |class= ソート |image=[[File:Merge-sort-example-300px.gif]] |caption=マージソートの例。まずリストを小さな単位に分け、二つのリストをそれぞれの要素の先頭を比較してマージする。最後までこの操作をくり返すと、リストはソートされている。 |data=配列 |time= <math>O(n \log n)</math> |best-time=<math>O(n \log n)</math> typical, <math>O(n)</math> natural variant |average-time=<math>O(n \log n)</math> |space=<math>O(n)</math> 外部記憶 }} [[Image:Merge sort animation2.gif|right|thumb|マージソートの様子]] '''マージソート'''は、[[ソート]]の[[アルゴリズム]]で、既に整列してある複数個の列を1個の列にマージする際に、小さいものから先に新しい列に並べれば、新しい列も整列されている、というボトムアップの[[分割統治法]]による。大きい列を多数の列に分割し、そのそれぞれをマージする作業は[[並列コンピューティング|並列化]]できる。 n個のデータを含む配列をソートする場合、最悪[[計算複雑性理論|計算量]][[ランダウの記号|O]](n log n)である。分割と統合の実装にもよるが、一般に[[安定ソート|安定なソート]]を実装できる。[[In-placeアルゴリズム|インプレース]]な(すなわち入力の記憶領域を出力にも使うので、追加の作業記憶領域を必要としない)バリエーションも提案されているが、一般には、O(n)の追加の作業記憶領域を必要とする<ref name="algo">{{cite book | 1=和書 | title=C言語による最新アルゴリズム事典 | publisher=[[技術評論社]] | author=奥村晴彦 | authorlink=奥村晴彦 | year=1991 | page=267 | isbn=4-87408-414-1}}</ref>。 (ナイーブな)[[クイックソート]]と比べると、最悪計算量は少ない<ref name="algo" />。[[乱数|ランダム]]なデータでは通常、クイックソートのほうが速い。 1945年、[[ジョン・フォン・ノイマン|フォン・ノイマン]]によって考案された<ref>{{cite book | last = Knuth | first = Donald | authorlink = Donald Knuth | series = [[The Art of Computer Programming]] | volume= 3 | title= Sorting and Searching | edition = 2nd | publisher = Addison-Wesley | year= 1998 | chapter = Section 5.2.4: Sorting by Merging | pages = 158 | isbn = 0-201-89685-0 | ref = harv}}</ref>。 ==アルゴリズム== 基本的な手順は以下の通りである。 #データ列を分割する(通常、二等分する) #分割された各データ列で、含まれるデータが1個ならそれを返し、2個以上ならステップ1から3を[[再帰]]的に適用してマージソートする #二つのソートされたデータ列(1個であればそれ自身)を[[マージ]]する ステップ1と2の途中(すなわち細分化するまでの部分)についてこのアルゴリズムの手順に含めず、あらかじめ局所的に整列されている多数の列が与えられるもの、とすることもある。後述する、テープをマージする手法の場合、最初のテープから主記憶を可能な限り全部使って整列できるだけ整列した部分列を、順次書き出したテープから始める。 ステップ3のマージでは、2本のデータ列の先頭同士を比べ小さい方をデータ列から取り出して出力し、残りのデータをもつ2本のデータ列に対して再帰的に同じ処理を、両方が空になるまで行う。ソートすべきデータ列が部分的に順次得られる場合、[[オンラインアルゴリズム]]として、部分データ列をソートして後でマージするという変形も可能である。 クイックソート等と同様、完全に細分化せずにスレッショルドとして適度に大きい個数を設定し、それ以下になった時点でなんらかの「ごく少数の対象専用の高速なコードによるソート」を併用するという高速化手法がある<ref name="algo" />。手順として書き出すと次のようになる。 #データ列を分割する(通常、二等分する) #分割された各データ列で、含まれるデータが設定個数以下ならそれを別の高速なアルゴリズムでソートして返し、設定個数超ならステップ1から3を[[再帰]]的に適用してマージソートする #二つのソートされたデータ列を[[マージ]]する 他に特殊な応用例として、外部ソートの1手法でテープ(のようなシーケンシャルアクセスメディア)に収められたデータをソートする、というものがある。最も単純な、4本のテープを2本ずつ使う「平衡2系列マージ」を例に説明する。まず元のデータから、利用可能な内部記憶をできるだけ使って、部分的に整列されている列をテープに書き出す。この時、2本のテープに交互に書き出すようにする。 次に、2本のテープのそれぞれの先頭部分にある、それぞれの部分列をマージしながら、別の2本のテープに交互に書き出す。この操作により、より長い整列した列が得られる。全てのマージが終わったら、コピー元とコピー先を入れ替え、同様に繰り返す。繰返し毎に各部分列の長さは約2倍に、列の個数は約半分になると期待できる。 最終的に、テープを切り替えることなく、1本のテープに全てのデータが出力されたら完了である。この技法はコンピュータがまだ高価で、テープが大容量外部記憶の主力だった時代にさかんに研究され、さまざまなバリエーションが編み出された。『[[The Art of Computer Programming]]』の§5.4にそれらの詳細がある。 ===実装例=== ==== C ==== <syntaxhighlight lang="c"> #include <stdio.h> void merge(int A[], int B[], int left, int mid, int right) { int i = left; int j = mid; int k = 0; int l; while (i < mid && j < right) { if (A[i] <= A[j]) { B[k++] = A[i++]; } else { B[k++] = A[j++]; } } if (i == mid) { /* i側のAをBに移動し尽くしたので、j側も順番にBに入れていく */ while (j < right) { B[k++] = A[j++]; } } else { while (i < mid) { /* j側のAをBに移動し尽くしたので、i側も順番にBに入れていく */ B[k++] = A[i++]; } } for (l = 0; l < k; l++) { A[left + l] = B[l]; } } void merge_sort(int A[], int B[], int left, int right) { int mid; if (left == right || left == right - 1) { return; } mid = (left + right) / 2; merge_sort(A, B, left, mid); merge_sort(A, B, mid, right); merge(A, B, left, mid, right); } int main(void) { int a[10] = {8,4,7,2,1,3,5,6,9,10}; int b[10] = {0}; const int n = 10; int i; merge_sort(a, b, 0, n); for (i = 0; i < n; i++) { printf("%d ", a[i]); } printf("\n"); } </syntaxhighlight><!-- C99より前の規格でもコンパイルできるように記述している。 --> ====Ruby==== <syntaxhighlight lang="ruby"> def mergesort lst return _mergesort_ lst.dup # 副作用で配列が壊れるので、複製を渡す end def _mergesort_ lst if (len = lst.size) <= 1 then return lst end # pop メソッドの返す値と副作用の両方を利用して、lst を二分する lst2 = lst.pop(len >> 1) return _merge_(_mergesort_(lst), _mergesort_(lst2)) end def _merge_ lst1, lst2 len1, len2 = lst1.size, lst2.size result = Array.new(len1 + len2) a, b = lst1[0], lst2[0] i, j, k = 0, 0, 0 loop { if a <= b then result[i] = a i += 1 ; j += 1 break unless j < len1 a = lst1[j] else result[i] = b i += 1 ; k += 1 break unless k < len2 b = lst2[k] end } while j < len1 do result[i] = lst1[j] i += 1 ; j += 1 end while k < len2 do result[i] = lst2[k] i += 1 ; k += 1 end return result end </syntaxhighlight> ====Haskell==== (※ [[Haskell]]のリストは「長さを測って半分ずつに分ける」という操作には適さないため、要素を1個ずつ振り分ける関数を使っている。この実装では安定ではない) <syntaxhighlight lang="haskell"> mergesort :: Ord t => [t] -> [t] mergesort lst = case lst of [] -> lst [_] -> lst _ -> merge (mergesort a) (mergesort b) where (a, b) = split lst merge [] [] = [] merge xxs [] = xxs merge [] yys = yys merge xxs@(x : xs) yys@(y : ys) | x < y = x : (merge xs yys) | otherwise = y : (merge xxs ys) split [] = ([], []) split ~(x : xs) = (x : zs, ys) where (ys, zs) = split xs </syntaxhighlight> ==== Scheme ==== <syntaxhighlight lang="scheme"> ;; use-modules は 処理系 Guile 固有の機能である。 ;; SRFI 機能を使うための仕組み処理系に依る。 (use-modules (srfi srfi-1 )) ; split-at (use-modules (srfi srfi-11)) ; let-values (define (merge left-half right-half) (let loop ((ls left-half) (rs right-half) (result (list))) (cond ((null? rs) (append (reverse result) ls)) ((null? ls) (append (reverse result) rs)) (else (let ((l (car ls)) (r (car rs))) (if (<= l r) (loop (cdr ls) rs (cons l result)) (loop ls (cdr rs) (cons r result)))))))) (define (merge-sort xs) (cond ((null? xs ) xs) ((null? (cdr xs)) xs) (else (let-values (((left-half right-half) (split-at xs (quotient (length xs) 2)))) (merge (merge-sort left-half) (merge-sort right-half)))))) </syntaxhighlight> ==アルゴリズムの動作例== 初期データ: 8 4 3 7 6 5 2 1 # データ列を二分割する。 #: 8 4 3 7 | 6 5 2 1 # もう一度、二分割する。 #: 8 4 | 3 7 | 6 5 | 2 1 # 各データ列にデータ数が2以下になったところで、各データ列内のデータをソートする。 #: 4 8 | 3 7 | 5 6 | 1 2 # この例の場合は、右2つのデータ列、左2つのデータ列をそれぞれマージとソートし、2つのデータ列にする。 #: 3 4 7 8 | 1 2 5 6 # 2つのデータ列をマージとソートする。 #: 1 2 3 4 5 6 7 8 == 脚注 == <references/> == 関連項目 == * [[マージ]] == 外部リンク == *[http://www.codecodex.com/wiki/Merge_sort マージソートコード(英語)] {{ソート}} [[Category:ソート|まあしそおと]] [[no:Sorteringsalgoritme#Flettesortering]]
このページで使用されているテンプレート:
テンプレート:Cite book
(
ソースを閲覧
)
テンプレート:Infobox algorithm
(
ソースを閲覧
)
テンプレート:ソート
(
ソースを閲覧
)
マージソート
に戻る。
ナビゲーション メニュー
個人用ツール
ログイン
名前空間
ページ
議論
日本語
表示
閲覧
ソースを閲覧
履歴表示
その他
検索
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
特別ページ
ツール
リンク元
関連ページの更新状況
ページ情報