を参照していく。
前回
4. ソートを改良する
4.1 問題を分割する
アルゴリズムにおいても問題を分割することは有用である。
4.1.1 小さなソート
多くの数をソートすることを考えるとき、そうではなく、既にソートされた配列(複数の列)を1つの数値と見なし、更にそれらをまとめた新たな配列と見なして更にソートすることを繰り返していくことを考える。すなわち
問題
2つのソートされた配列をまとめて1つのソートされた配列を作る手順を考える。
この作業をマージと呼ぶことにする。
4.1.2 ソートされた配列のマージ
参照している配列の要素を示すものとしてポインタを考える。このソート問題では、ポインタがある場所の数値を比較するだけでよい点が望ましい。
4.1.3 マージの計算量
マージの計算量を考える。数の比較を行う回数は、比較する2つの配列の長さをそれぞれとすれば、各ステップでそれぞれのポインタを指す要素が比較されることから、であり、すなわちである。これが2つのソート済みの配列を基に1つのソートされた配列を作るのにかかる計算量である。
def merge_arrays(left, right = []): res = [] i, j = 0,0 n, m = len(left), len(right) # 一方の配列を調べ尽くしたら終了とする while i < n and j < m: if left[i] < right[j]: res.append(left[i]) i +=1 else: res.append(right[j]) j += 1 # 残りはそのまま後ろに連結する return res + left[i:] + right[j:] def step(array): res = [] for i in range(0, len(array), 2): res.append(merge_arrays(*array[i:i+2])) return res # 実際にテストデータを使って動かしてみる import random random.seed(4) my_array =[random.randint(1,100) for i in range(150)] my_array = [[v] for v in my_array] step1 = step(my_array) print(step1) print('###################') step2 = step(step1) print(step2) print('###################') step3 = step(step2) print(step3) print('###################') step4 = step(step3) print(step4) print('###################') step5 = step(step4) print(step5) print('###################') step6 = step(step5) print(step6) print('###################') step7 = step(step6) print(step7) print('###################') step8 = step(step7) print(step8)
4.1.4 マージソートの計算量
マージソートは、二分木と呼ばれるイメージでマージする過程を図示できる。
二分木のイメージ
ソートする入力配列のサイズをとする。ステップの計算では2つの配列をマージする計算だとする。高さがを満たすような最小のとなるような木を用意する。このとき
である。木の高さを1つ減らすために必要なマージの回数は葉の枚数の半分であり、これは高々回である。したがって
がマージソートの計算量である。
4.1.5 ソートの計算量
マージソートの計算量よりも少ない計算量のソートアルゴリズムは存在しない。
長さの配列を考える。を満たすような添え字に対してならば2つの要素を入れ替え、小さな要素が先頭により近くなるようにする。一方で長さの配列の要素がすべて違うならば、この個の要素を用いて表現できる配列の種類は通り存在する。これは種類の要素を1列に並べる順列の数である。1回の比較演算では比較した要素を入れ替えるか、そのままにするかの二択が存在する。このときの二分木における高さがソートアルゴリズムに最低限掛かる計算量である。
木の高さをとすると、葉の枚数はである。アルゴリズムはすべての可能性を実現できなければならないから、
でなければならない。
Sterlingの公式より
と近似できるから、
を得る。したがって最右辺の最も次数の高いを取ることでを得る。
4.1.5 分割統治法
マージソートの本質は問題を小さく分割し簡単な問題を解くことを重ねて全体の解を得る点にある。このような方法を一般に分割統治法という。