ここでは解決にかかる最小の時間がわかっている問題を取り上げます。
この両方が成り立つ
これより、 g は f を無限回上回るも、無限回下回らなければならない。 ここで、 g は f よりオーダの大きな関数と小さな関数を行き来すると仮定す るとうまくいく。 つまり、 f(n)=n^2 としたとき、 g(n) は n と n^3 の あいだを行き来する関数と定義する。
つまり、 g(n) は g(1)=1 とし、 g(2)=2^3 とする。 そして、 g(2^3-1) までは 2^3 としておき、 g(2^3)= (2^3)^3 = 2^(3^2) とする。 そして、 g(2^(3^2)-1)までは 2^(3^2) とする。 つまり、 g(n) は 2^(3^k)≤ n < 2^(3^(k+1))-1 の時、2^(3^(k+1)) とする。
問題を解くのにかかる最小の手数を求めるのは困難で、 ほとんどの問題でわかってません。 ここでは Knuth が示した Sorting の比較の下界について述べます。
計算時間を示す関数の増加の仕方を下から抑えるには
「ある数
与えられた数列を小さい順(大きい順)に並べ換えて出力する問題を Sorting と言うことにします。
最悪計算量がもっとも良い Sorting アルゴリズムとしてヒープソートを紹介 します。 但し、平均的な問題に対して、実際に計算機にプログラムを与え動かすと、ヒー プソートよりクイックソートの方が速くソートできるようです。
順序木とは、二分木において、頂点の大小関係が左から右にならんでいること を言います。 根から見ると、左の子の頂点やその下の子の頂点の値全ては根の値より小さく、 反対に右の頂点やその下の子の頂点の値全ては根の値より大きいです。
クイックソートは与えられたデータの列をこの二分木にはめ込むように列を整 列させます。 クイックソートで使われる partition(s, t) という 関数は次のような処理をします。 なお、この s, t という変数は s 番目から t 番目までのデータに着目するこ とを意味します。
この処理により、順序木の左に置かれるデータと右側に置かれるデータを区分
されます。そして、要素一つになるまで区分を繰り返すことにより、与えられ
たデータが整列されることになります。
partition 関数の実行時間は与えられたデータを全て読むので
しかし、与えられたデータ全てが等しいときなどは完全二分木にならず、一直 線の形の木になってしまうので、実行時間は次のようになります。
ヒープと言うデータ構造は木構造において親は子より常に大きいという条件を
満たすものです。
そのため、最大値は根に配置されることになりますが、他の頂点上の値は気の
左右どちらにも置くことができるという任意性があります。
また、データの数や値に関わらず、常に完全二分木に保つことが可能になりま
す。
木の頂点数を n で表すと、
木構造を完全二分木に保てるため、以下で説明するようにデータの挿入と、根の
値の削除を
共に
ヒープソートはこの考え方を利用して、
(1)与えられたデータをもとにヒープを作り
(
ここでは、比較をすることだけで並べかえをする時、必要な比較の 回数を求めます。
ここで注意が必要なのは、 Sorting を行うプログラムには比較以外の手法を 考えられるということです。 つまり、何らかの方法で比較以外の Sorting プログラムを考案した時、ここ で示した結果とは何ら関連せずに高速なアルゴリズムになる可能性があります。
では、最低限比較の数を考えます。
一回の比較では大小関係が一つだけ求まります。
これでプログラムの流れは 2 つに分かれます。
さて、ここでプログラムの流れを図に表すことを考えます。
「データの i 番目と j 番目を比較する」という
ような比較をノードにすると、プログラムの流れは二分木になります(計算の
流れを木で表したものを 計算木と呼びます)。
計算木は入力の可能性すべてを表しますので、配列の内容は未知でも配列
の数が決まっていれば、プログラムの流れの分岐の仕方を全て記した計算木は
一意に定まります。
ところで、プログラムに与える入力は、データの数を
これにより、ソーティングに必要な比較回数は
Sorting の計算量の下限が
2×2 の行列の積は高々 8 回でできるが、これは実は掛け算の回数を 7 回に することができる。 その方法を考えよ。
次回は非決定性 Turing 機械についてお話します。