ソートの動きを見るツール

代表的な7種類のソート(並べ替え)アルゴリズムが、配列のどこを比べ、どこを書き換えるのかを棒グラフで1手ずつ再生します。データの数や最初の並び(ばらばら・ほぼ整列・逆順・同じ値が多い)を変えて、比較と書き込みの回数がどう変わるかを確かめられます。基本情報技術者試験の科目Bのように、ソートのプログラムを1行ずつたどる練習の助けにもなります。

設定

並べ替えの様子

 

比較の回数
–
書き込みの回数
–
手順
–

比較中 書き込み 位置が確定

同じデータでの比較

上と同じデータを、7種類のアルゴリズムで並べ替えたときの回数です。データの数や最初の並びを変えると、どれが有利かが変わります。

アルゴリズム比較の回数書き込みの回数計算量(平均 / 最悪)安定

交換は2回の書き込みとして数えます。クイックソートは末尾の要素を基準値(ピボット)にする方法(Lomutoの分割)、シェルソートは間隔を 1, 4, 13, 40 … とする方法で、実装によって回数は変わります。データの数が20以下のときは、棒の下に値を表示します。

比較による並べ替えの基本: O(n²) の3つ

バブルソートは隣どうしを比べて大きい方を後ろへ送る操作を繰り返し、1周するごとに最大値が末尾に確定します。選択ソートは残りの中から最小値を探して先頭と交換するので、比較は常に n(n−1)/2 回ですが、交換は最大 n−1 回と少なくなります。挿入ソートは、整列済みの部分に次の値を正しい位置へ差し込む方法で、すでにほぼ整列しているデータでは比較がほぼ n 回で済みます。3つとも平均の計算量は O(n²) で、データが増えると急に遅くなります。データの数を64にして「ばらばら」と「ほぼ整列」を切り替えると、挿入ソートと、交換がなくなった時点で終わるバブルソートの比較の回数が大きく減り、選択ソートは2016回のまま変わらない様子を確かめられます。

分割統治の O(n log n): マージソートとクイックソート

マージソートは配列を半分に分けてそれぞれを並べ替え、2つの整列済みの並びを先頭から比べて1つにまとめ(マージ)ます。分ける段数が log₂ n 段、各段の作業が n に比例するので、どんなデータでも O(n log n) ですが、作業用に n 個分の記憶領域を使います。クイックソートは基準値(ピボット)より小さいものを前、大きいものを後ろに分け、それぞれをさらに分けていきます。平均は O(n log n) で実際にも速い一方、すでに整列したデータで端の要素をピボットにすると分割が偏り、O(n²) になります。最初の並びを「逆順」にしてクイックソートを選ぶと、比較の回数が n(n−1)/2 に近づくことが分かります。実際のライブラリでは、ピボットを3つの値の中央値にする、偏ったらヒープソートに切り替える(イントロソート)などの工夫をします。

ヒープソートとシェルソート

ヒープソートは、親が子より大きいという条件を満たす二分木(二分ヒープ)を配列の中に作り、根にある最大値を末尾と交換してヒープを1つ縮める操作を繰り返します。配列の i 番目の子は 2i+1 番目と 2i+2 番目です。最悪でも O(n log n) で、作業用の領域もいりませんが、離れた位置どうしを比べるのでキャッシュの効率はよくありません。シェルソートは、間隔を空けた要素どうしで挿入ソートを行い、間隔を縮めていく方法です。最初に遠くの要素を大きく動かしておくことで、最後の普通の挿入ソートの手間を減らします。

安定なソートと計算量の読み方

同じ値(キー)の要素が、並べ替えた後も元の順番のまま残るソートを安定なソートといいます。たとえば社員を部署で並べ替えたとき、同じ部署の中では社員番号の順が保たれる、という性質です。バブルソート、挿入ソート、マージソートは安定で、選択ソート、クイックソート、ヒープソート、シェルソートは一般に安定ではありません。計算量の O(n²) や O(n log n) は、データの数 n が大きくなったときの回数の増え方を表します。n = 1000 なら n² は100万、n log₂ n は約1万なので、大きなデータでは O(n log n) のアルゴリズムが圧倒的に有利です。比較だけで並べ替える方法では、最悪の場合に O(n log n) より速くはできないことが知られています。

関連する問題を解く

配列 5, 3, 8, 1, 4 をバブルソートで昇順に並べる。先頭から隣どうしを比べ、左の方が大きければ交換する操作を末尾まで1回行った(1パス)後の配列はどれか。

  1. A1, 3, 8, 5, 4
  2. B3, 5, 1, 4, 8
  3. C1, 3, 4, 5, 8
答えと解説を見る

正解:B. 3, 5, 1, 4, 8

5と3を交換して 3, 5, 8, 1, 4、5と8はそのまま、8と1を交換して 3, 5, 1, 8, 4、8と4を交換して 3, 5, 1, 4, 8 になります。1パスで最大値の8が末尾まで運ばれて位置が確定し、次のパスでは残りの4つを同じように並べます。1, 3, 8, 5, 4 は最小値を探して先頭と交換する選択ソートの1パス後の配列、1, 3, 4, 5, 8 はすべて並べ終えた結果です。

配列 29, 10, 14, 37, 13 を選択ソート(未整列の部分から最小値を探し、未整列の部分の先頭と交換する)で昇順に並べる。この操作を2回行った後の配列はどれか。

  1. A10, 13, 14, 37, 29
  2. B10, 14, 29, 37, 13
  3. C10, 14, 13, 29, 37
答えと解説を見る

正解:A. 10, 13, 14, 37, 29

1回目は全体の最小値10を先頭の29と交換して 10, 29, 14, 37, 13 になります。2回目は2番目以降の最小値13を2番目の29と交換して 10, 13, 14, 37, 29 になります。選択ソートの比較は常に n(n−1)/2 回ですが、交換は1回の操作につき最大1回なので、書き込みの重いデータで有利なことがあります。10, 14, 29, 37, 13 は挿入ソートで2つの要素を差し込んだ後、10, 14, 13, 29, 37 はバブルソートを2パス行った後の配列です。

要素数10の配列を挿入ソートで昇順に並べる。2番目以降の要素を順に取り出し、左隣から順に比べて、取り出した値以下の要素が見つかるか先頭に達するまで比べる。最初から昇順に並んでいる場合と、降順に並んでいる場合の比較の回数の組はどれか。

  1. Aどちらも45回
  2. B10回と100回
  3. C9回と45回
答えと解説を見る

正解:C. 9回と45回

昇順に並んでいれば、取り出した値は左隣と1回比べるだけで位置が決まるので、9個の要素で合計9回です。降順なら、i 番目に取り出す値は左にある i 個すべてと比べるので、1 + 2 + … + 9 = 45回になります。このように挿入ソートは、最良 O(n)、最悪 O(n²) と入力によって大きく変わり、ほぼ整列済みのデータには非常に速いという特徴があります。この性質を活かし、間隔を空けた要素どうしで先に挿入ソートを行うのがシェルソートで、Timsort なども短い区間に挿入ソートを使います。

最悪の場合でも計算量が O(n log n) で、作業用の配列などの追加の記憶領域をほとんど使わずに並べ替えられるソートはどれか。

  1. Aマージソート
  2. Bヒープソート
  3. Cクイックソート
答えと解説を見る

正解:B. ヒープソート

ヒープソートは、配列の中に二分ヒープを作り、根の最大値を末尾と交換してヒープを縮めることを繰り返すので、最悪でも O(n log n) で、追加の領域は定数で済みます。マージソートも最悪 O(n log n) ですが、配列で行うには作業用に n に比例する領域が必要です。クイックソートは平均 O(n log n) で実際には速いものの、ピボットの選び方とデータによっては O(n²) になります。なお、ヒープソートは離れた位置の要素を比べるのでキャッシュの効率が悪く、安定でもありません。

(名前, 点数) のデータ (A, 80)、(B, 70)、(C, 80)、(D, 70) がこの順に並んでいる。点数の昇順に安定ソートで並べ替えたときの名前の順はどれか。

  1. AB, D, A, C
  2. BD, B, A, C
  3. CB, D, C, A
答えと解説を見る

正解:A. B, D, A, C

安定ソートでは、キー(ここでは点数)が同じ要素どうしは、並べ替える前の順番がそのまま保たれます。70点の B と D は元の順に B、D、80点の A と C も元の順に A、C となるので、B, D, A, C です。安定なソートにはバブルソート、挿入ソート、マージソートがあり、選択ソート、クイックソート、ヒープソートは一般に安定ではありません。安定ソートを使うと、先に名前順に並べてから点数で並べ直すといった、複数のキーでの並べ替えができます。

クイックソートの計算量が O(n²) になる典型的な場合はどれか。

  1. Aデータがばらばらに並んでいて、ピボット(基準値)を無作為に選ぶ場合
  2. Bデータの個数が2のべき乗の場合
  3. Cすでに整列済みのデータで、常に先頭の要素をピボットにする場合
答えと解説を見る

正解:C. すでに整列済みのデータで、常に先頭の要素をピボットにする場合

クイックソートは、ピボットより小さい要素と大きい要素に分け、それぞれをさらに分けていきます。整列済みのデータで常に先頭をピボットにすると、毎回ピボットが最小値になり、分割が「0個と残り全部」に偏るため、比較は n(n−1)/2 回(n = 100 なら4,950回)になります。ピボットを無作為に選んだり、3つの値の中央値にしたりすると、偏った分割が続く可能性は非常に小さくなります。データの個数は計算量に関係しません。

マージソートで、昇順に整列済みの2つの列 1, 4, 7, 9 と 2, 3, 8, 10 を、先頭どうしを比べて小さい方を出力することを繰り返して1つの列にまとめる(マージする)。片方の列が空になったら、残りは比べずにそのまま出力する。比較の回数は何回か。

  1. A4回
  2. B7回
  3. C8回
答えと解説を見る

正解:B. 7回

1と2、4と2、4と3、4と8、7と8、9と8、9と10を比べ、ここで最初の列が空になるので残りの10は比べずに出力します。比較は7回で、長さ m と n の列をマージするときの最大の回数 m + n − 1 = 7 に当たります。マージは整列済みの列を先頭から1回ずつ見るだけなので O(m + n) で済み、これを log₂ n 段繰り返すのでマージソート全体は O(n log n) になります。

よくある質問

いちばん速いソートはどれですか?

データによって変わります。一般的なデータではクイックソートが速いことが多く、最悪の場合も O(n log n) を保証したいならマージソートかヒープソート、ほぼ整列したデータや数が少ないデータでは挿入ソートが向いています。実際のプログラミング言語の標準ライブラリは、これらを組み合わせたもの(イントロソートや Timsort)を使っています。

比較と書き込みの回数は何を表しますか?

比較の回数は、2つの要素の大小を調べた回数です。書き込みの回数は、配列の要素を書き換えた回数で、交換は2回として数えます。比較の重いデータ(長い文字列など)では比較の回数が、書き込みの重い記憶装置(フラッシュメモリなど)では書き込みの回数が、実際の速さに大きく影響します。

試験ではどのように問われますか?

基本情報技術者試験や応用情報技術者試験では、ソートのプログラムの空欄を埋める問題、ある時点での配列の状態を答える問題、計算量のオーダーや安定性を選ぶ問題がよく出ます。このツールで「1手進める」を使い、配列の変化を自分の予想と比べると、トレースの練習になります。

データの数を増やすと、回数はどのくらい増えますか?

O(n²) のアルゴリズムでは、データを2倍にすると回数はおよそ4倍になります。O(n log n) のアルゴリズムでは、およそ2倍強です。データの数を16、32、64と変えて、同じデータでの比較の表を見ると確かめられます。

関連する用語

計算量 安定ソート 二分ヒープ

関連する計算ツール

半導体・電子回路・情報の計算ツール →