edubun
日本語

ソート2:マージソートとクイックソート

100万個の値をすべての組を比べて並べると約5,000億(500 billion)回の比較が必要ですが、マージソートなら約2,000万(20 million)回ですみます。

問題を分けて、半分ずつ解く

前のユニットの方法はリスト全体を一度に扱い、その代わりに約 n² 回の比較がかかります。このユニットの2つの方法は、分割統治法と呼ばれる別の作戦を使います。リストを小さな部分に分け、それぞれを同じ方法で並べてから、整列した部分を組み合わせるのです。分割は部分の値が1個か0個になるまで続き、値が1個のリストはもう整列しています。この方法は小さなリストに対して自分自身を呼び出すので、再帰的です(再帰はユニット6でくわしく見ます)。

マージソート:仕事はマージ(併合)の中にある

マージソートはリストを半分に分け、左半分を並べ、右半分を並べ、整列した2つの半分を1つにマージします。かしこいのはマージの部分です。整列した2つのリストは、先頭の値だけを見て組み合わせられます。2つの先頭を比べ、小さいほうを書き出して、その先へ進みます。片方のリストが空になるまでくり返し、もう一方の残りをそのまま写します。[1, 4, 7] と [2, 3, 8] のマージは、1(1対2)、2(4対2)、3(4対3)、4(4対8)、7(7対8)と進み、最後に8を写します。6個の値で比較は5回です。合わせてn個の値を持つ2つのリストのマージでは、比較が n − 1 回を超えることはありません。

function merge(left, right) {
  const out = [];
  let i = 0, j = 0;
  while (i < left.length && j < right.length) {
    if (right[j] < left[i]) out.push(right[j++]);
    else out.push(left[i++]);
  }
  return out.concat(left.slice(i), right.slice(j));
}

function mergeSort(a) {
  if (a.length <= 1) return a;          // 整列済み
  const mid = Math.floor(a.length / 2);
  return merge(mergeSort(a.slice(0, mid)), mergeSort(a.slice(mid)));
}

なぜこれが速いのでしょうか。半分にしていく様子を段として思い浮かべましょう。8個の値のリストは、4個のリスト2つ、2個のリスト4つ、1個のリスト8つと分かれます。8は3回半分にできるので、段は3つです。どの段でも、すべてのマージを合わせると各値に1回ずつふれるので、1つの段にかかる比較は多くても約n回です。段が log₂ n 個あるので、合計は約 n log₂ n 回です。100万個の値なら約2,000万(20 million)回で、組ごとに比べる方法の約5,000億(500 billion)回とは大違いです。ラボと同じように [38, 27, 43, 3] と [9, 82, 10] に分けると、リスト [38, 27, 43, 3, 9, 82, 10] の比較は14回です。上のコードは3個と4個に分けるので、13回です。

その代わりにメモリを使います。マージには出力を書く場所が必要なので、マージソートは入力と同じ大きさの2つ目のリストを使います。またマージソートは安定です。等しいと比べられる値は元の順番を保ちます。先頭どうしが等しいとき、マージは左のリストから取るからです。これは表をある列で並べたあとで別の列で並べるとき、たとえば市で並べてから名前で並べるときに大切になります。

読み込み中

ドラッグで回転、スクロールまたはピンチで拡大・縮小。

マージソートはマージ中の部分に色をつけ、クイックソートはピボットを紫で示します。値の数を40に増やして、カウンターを挿入ソートと比べましょう。

クイックソート:ピボットのまわりで分ける

クイックソートは別の方法でリストを分けます。ピボットと呼ぶ値を1つ選び、ピボットより小さいものはすべてその前に、ほかはすべてその後ろに来るようにリストを並べかえます。この手順を分割(パーティション)と言います。これでピボットは最終的な位置に入り、両側は同じ方法で別々に並べられます。左側の値はどれも右側のどの値より小さいので、あとでマージする必要はありません。

ラボでは、各部分の最後の値をピボットにする、教科書によくある版を使います。[5, 1, 4, 2, 8] では、ピボットの8をほかの4つの値と比べますが、どれも小さいので8は末尾に残ります。次の部分 [5, 1, 4, 2] はピボット2を使います。小さいのは1だけなので、2回交換するとリストは [1, 2, 4, 5, 8] になります。[4, 5] はあと1回の比較ですみます。合計は比較8回、交換2回です。

ピボットが真ん中近くに入れば、分けるたびに問題は半分になり、クイックソートの比較はマージソートと同じく約 n log₂ n 回です。しかも2つ目の写しを使わず、元のリストの中で作業できます。けれども悪いピボットは痛手です。リストがすでに整列していてピボットがいつも最後の値なら、分けるたびに値が1つはがれるだけで、クイックソートの比較は約 n²/2 回に逆戻りします。[1, 2, 3, 4, 5] では10回で、選択ソートと同じです。実際のライブラリは、たとえばランダムに選んだり、3つの見本の値の真ん中を選んだりして、ピボットをもっとよく選ぶことでこれを避けています。

背景どちらの方法も由来がわかっています。マージソートは、最初のプログラム内蔵式コンピューターが設計されていた1945年に、ジョン・フォン・ノイマンが考案しました。クイックソートは、1959年にモスクワ大学にいたイギリス人学生トニー・ホーアが生み出しました。彼はロシア語から英語への機械翻訳の仕事を持ちかけられました。辞書は磁気テープに収められていたので、まず各文の単語をアルファベット順に並べる必要がありました。最初の案では文の長さの2乗に比例する時間がかかりましたが、2つ目の案がクイックソートになりました。彼はそれを1961年に発表しました。
方法比較追加のメモリ
挿入ソートふつう約 n²/4、最悪約 n²/2なし。安定
マージソートいつも約 n log₂ nn個の値の2つ目のリスト。安定
クイックソートふつう約 n log₂ n、最悪約 n²/2(悪いピボット)少し。ふつうの版は安定でない
比較によるソートで n log₂ n より速くできるでしょうか。できません。組を比べることでしか情報を得ないソートは、ありうる n! 通りのはじめの並びをすべて見分けられなければなりません。比較の結果は2通りなので、最悪の場合には少なくとも log₂(n!) 回の比較が必要で、これは n log₂ n のように増えます。10個の値なら、その下限は比較22回です。
分割統治法問題を分け、各部分を同じ方法で解き、結果を組み合わせる
マージ小さいほうの先頭の値をくり返し取って、整列した2つのリストを組み合わせる
マージソート半分に分け、それぞれを並べ、マージする。比較は約 n log₂ n 回
ピボットクイックソートがリストを分けるときの基準になる値
分割(パーティション)小さい値がピボットの前に、残りが後ろに来るように並べかえること
クイックソートピボットのまわりで分割し、それぞれの側を並べる
安定なソート等しい値が元の順番を保つ
インプレース少ない追加メモリで、元のリストの中で並べる
  1. [1, 4, 7] と [2, 3, 8] のマージには何回の比較が必要ですか?
  2. [1, 2, 3] と [4, 5, 6] のマージには何回の比較が必要ですか?
  3. 16個の値のリストを、各部分が1個になるまで半分にしていきます。半分にする段はいくつありますか?
  4. 100万個の値に対して、マージソートはおおよそ何回比較しますか?
    • 約20回
    • 約2,000万(20 million)回
    • 約5,000億(500 billion)回
    • ちょうど100万回
  5. 分割の後、ピボットはどこにありますか?
    • 整列後の最終的な位置
    • 先頭
    • 末尾
    • どこでもよい。あとでまた動く
  6. 最後の値をピボットにするクイックソートを、すでに整列したリスト [1, 2, 3, 4, 5] で実行します。比較は何回ですか?
  7. 入力と同じ大きさの2つ目のリストが必要なのはどの方法ですか?
    • マージソート
    • クイックソート
    • 挿入ソート
  8. 1945年にマージソートを考案したのはだれですか?
    • トニー・ホーア
    • ジョン・フォン・ノイマン
    • エイダ・ラブレス
    • ユークリッド
  9. トニー・ホーアがクイックソートを思いつくきっかけになった問題は何ですか?
    • 磁気テープ上の辞書を引くために、文の単語を並べること
    • 図書館の本を並べること
    • ウェブページを順位づけすること
    • 銀行の小切手を並べること
  10. マージソートが安定と呼ばれるのはなぜですか?
    • 等しい値が元の順番を保つ
    • 決してクラッシュしない
    • いつも同じ時間がかかる
    • 追加のメモリを使わない
  11. 10個の値の比較ソートで、最悪の場合の比較回数として考えられる最小の数はいくつですか(log₂ 10! を切り上げたもの)?
ポイント
  • 分割統治法:分けて、各部分を解き、組み合わせる。
  • マージソート:比較はいつも約 n log₂ n 回、追加のメモリが必要、安定。
  • クイックソート:ピボットのまわりで分割する。平均では速いが、悪いピボットでは約 n²/2 回。
  • 100万個の値なら、比較は約5,000億(500 billion)回ではなく約2,000万(20 million)回。
  • どんな比較ソートも、最悪の場合に約 n log₂ n より速くはできない。
このコースの出典 (11)
  • MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
  • lecture notes 1 Introduction, 3 Sorting, 9 Breadth-First Search, 10 Depth-First Search (ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes) and Fall 2011 lecture 13 BFS notes (ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011)
  • Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (MIT Press)
  • with the CLRS-based Khan Academy algorithms unit by Thomas Cormen and Devin Balkcom of Dartmouth (khanacademy.org/computing/computer-science/algorithms: binary search, selection sort analysis, merge sort, quicksort overview and analysis)
  • MacTutor History of Mathematics, Al-Khwarizmi (mathshistory.st-andrews.ac.uk/Biographies/Al-Khwarizmi: word algorithm from the Latin title Algoritmi de numero Indorum)
  • D. E. Joyce, Euclid's Elements Book VII Propositions 1 and 2 (mathcs.clarku.edu/~djoyce/elements/bookVII)
  • Wolfram MathWorld, Merge Sort (mathworld.wolfram.com/MergeSort.html: proposed by John von Neumann in 1945, after Knuth 1998 p. 159)
  • Stanford CS "Tony Hoare: Quicksort" (cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html: devised while a student at Moscow State University for Russian-English machine translation with a tape dictionary, programmed in Algol 60 and published 1961; the year 1959 from the Wikipedia Quicksort article and its cited sources)
  • Bodleian Libraries, Ada Lovelace (blogs.bodleian.ox.ac.uk/adalovelace: 1843 paper, Note G Bernoulli numbers)
  • Fourmilab text of Menabrea's Sketch with Lovelace's notes (fourmilab.ch/babbage/sketch.html)
  • Seven Bridges of Konigsberg (Euler 1736) and Tower of Hanoi (Edouard Lucas, 1883) checked against the Wikipedia articles and their cited sources.

確認日: 2026-10-17