seegongsik
アルゴリズム

マージソート わかりやすく

前回は、隣どうしを比べて大きいものを後ろへ送りながら列を並べました。うまくいくことはいきますが、列が長くなると遅すぎます。カードが多いほど、比べる回数がとてつもなく増えるからです。もっと賢い方法はないのでしょうか。あります。列を半分に分けるのです。

01

一マスずつでは遅い

前回のやり方は、単純なのがよいところでした。隣どうしを比べて、大きいものを後ろへ送ればよかったのです。
でも列が長くなると、比べることが多くなりすぎます。
カードが二倍になると、比較は四倍に跳ね上がります。四枚ならすぐですが、三十二枚になると気が遠くなります。

列の長さ4
一マスずつの比較回数6
列が二倍になるたび、比較は四倍に跳ね上がります。一マスずつ比べる並べかえは、列が長くなるとすぐ重くなります。

列を二倍にしてみます。長さ二倍 → 比較は四倍に急増。

棒がおそろしい勢いで伸びていきます。
列が長くなるほど、遅さはひどくなります。
問題は、列全体を一つのかたまりとして見て、最後まで比べることにあります。
では、かたまりを分けてみたらどうでしょう。

02

半分に分ける

長い列をまるごと扱うのではなく、半分に分けます。
その半分をまた半分に、さらにその半分も半分にと分け続けると、最後には一マスだけが残ります。
そして一マスは、もう並んでいるのと同じです。一人きりなので、比べる相手がいないからです。

5
2
8
1
9
4
7
3
束の数1
一つの束をクリックで半分ずつ分け続けてみましょう。分けるほど束は小さく、数は多くなります。

一つの束を半分に分け続けます。8 → 4+4 → 2+2+2+2 → 一マスずつ。

分けるだけで終わりでしょうか。
いいえ。分けたものは、また集めないと列になりません。一マスのものを二つずつ組にして合わせ、その合わせたものをまた合わせます。
そして、並んだ二つを合わせるのは、意外と簡単なのです。

03

並んだ二つを合わせる

並んだ二つの束を一列に合わせるときの秘訣は、先頭だけを見ることです。
二つの束の最初のマスを比べ、小さいほうを結果に落とします。その場所は次のマスが埋め、また最初のマスどうしを比べます。
ファスナーを閉めるように、一マスずつかみ合わさっていきます。

左の束(並び済み)
2
4
7
右の束(並び済み)
1
5
8
合わせた列
—
二つの束の先頭だけを比べて、小さい方を結果に落とします。それぞれの束はもう並んでいるので、先頭だけ見れば十分です。

二つの束の先頭を比べ、小さいほうを落とします。ファスナーのように一マスずつ合わせます。

ふしぎではありませんか。ばらばらな全体を比べるときは遅かったのに、それぞれ並んだ二つを合わせるときは先頭だけ見ればよいので速いのです。
分けたおかげで小さく並んだかけらができ、そのかけらを順々に合わせていけば、いつのまにかきちんとした列になります。

04

なぜ速いのか:層の数

なぜこのほうが速いのでしょうか。
半分ずつ分けるので、分ける層があまり多くなりません。列が二倍になっても、層はたった一つ増えるだけです。
一つの層を合わせる費用は列の長さくらいなので、全体の費用は層の数かける列の長さです。
一マスずつ比べるとき(長さかける長さ)より、ずっとゆっくり大きくなります。

列の長さ8
分けた層の数3
一層の費用8
合わせる費用(層の数 x 一層)24
一マスずつなら28
列が二倍になっても、層は一つだけ増えます。だから層の数かける一層の費用は、一マスずつ(長さかける長さ)よりずっとゆっくり大きくなります。

層を広げて、列の長さを変えてみます。層の数 x 一層 = 一マスずつよりゆっくり大きくなる。

正確な式は、あとでくわしく学びます。
いまは感覚だけ持ち帰れば十分です。半分に分ければ層はなかなか増えず、それぞれの層は軽く合わさるので、全体が速くなるということ。
分けて合わせるこの一手が、速さをがらりと変えるのです。

05

まとめ

一本の糸につなぐと、こうなります。
一マスずつ比べる並べ替えは、列が長いほど遅くなります。
そこで列を半分に分け、最後まで分けて、もう並んでいる一マスのかけらを作ります。
その並んだ二つをファスナーのように合わせれば、層の数だけ合わせればよいので、列全体がずっと速くそろいます。
分けて合わせる、これが賢い並べ替えのかなめです。

四つの段階を順に押して、ひと回りたどります。分ける → 一マス → 合わせる → 層の数だけ = 速い。

ここでは、いつも落ち着いて半分ずつ分けました。
でも、分ける基準を少し違うふうに取るいとこもいます。真ん中ではなく、あるマスを基準にして、小さいものと大きいものに分ける方法です。
その基準をどう選ぶかは、また先の話にしておきます。
きょうは、半分に分けて合わせれば速くなる、ということだけ持ち帰ってください。

一言でいうと隣どうしを一マスずつ比べる並べ替えは、列が長いほど比較が急増して遅くなります。もっと賢い方法は、列を半分に分け、それぞれを並べたあと、並んだ二つをファスナーのように合わせることです。最後まで分けると一マスだけが残り、一マスはもう並んでいるのと同じで、合わせるのは両方の先頭を比べるだけなので速いのです。半分に分けるので層はゆっくりしか増えず、各層の費用は列の長さくらいなので、全体は一マスずつよりずっと速く終わります。
← アルゴリズム
このページがお役に立ったなら支援する