出力の行数が多くて一画面に収まりきらない場合は ./a.out の代わりに 「./a.out | more」とする。 画面左下に「--続ける--」が出たらスペースバーで次の画面、 Enter で次の行、q で中止。
プログラムの実行時間を計るには、 「date; ./a.out; date」として開始・終了時刻を表示させ、 引き算すればよい。 ただし、./a.out の出力が多いと初めの date の出力が消えてしまいうまくゆかない。
配列は a[0], a[1], ..., a[N-1] とする。 全部で N 項である。 配列の要素には等しい値をもつものがあるかも知れないが、 その場合のことには深入りしないので、各自考えること。 添字の範囲のうち、常識でわかるものは断らない。
「a[i] > a[i+1] を満たす i があったら a[i] と a[i+1] とを交換する。これをくり返してゆく」 というのがこのソートのアイディアである。
「転倒数」を # {(i, j) | i < j かつ a[i] > a[j]} で定義する。 これは Σj # {i | i < j かつ a[i] > a[j]} とも思える。 次の事実の証明はやさしい。
付) 配列に格納されている値が 0, 1, 2, ..., N-1 を並べかえたものであるとき、 a は置換と思える。 その符号 (sgn) は転倒数が偶数なら +1, 奇数なら -1 である。 よって、置換の符号を転倒数から定義することもできる。 なお、このとき、 転倒数は a を (i i+1) のタイプの互換の積で書いたときの互換の数の最小に等しい。
a[i] > a[i+1] なる i のさがし方はいろいろ考えられる。その一つ。
このアルゴリズムでの、比較回数(= 比較の回数)はおよそ N2/2 である。 交換回数(= 交換を行なった回数)は最悪の場合およそ N2/2, 最も lucky な場合は 0, 平均すると N2/4 とみてよいだろう。 どちらもΟ(N2) である。
課題 4-1) 乱数列で初期化された配列をこの素朴な方法でソートするプログラムを書け。 また、配列の大きさと所要時間の関係を調べよ。
課題 4-2) 次のように書き足すと、 関数 swap() を呼び出した回数がわかる。 これを利用し、配列の大きさと交換を行なった回数との関係を調べよ。
#include ... int count = 0; int main() { ... printf("%d 回交換しました.\n", count); } void swap(int i, int j) { ... count++; }
付) 比較の回数もカウントするなら、 上と同様に int 型変数(たとえば)ccount を定義し、 if (a[i] > a[i+1]) を if (ccount++, a[i] > a[i+1]) と変えればよい。 このカンマはカンマ演算子である。
以上から適当に選んで、メールでレポートしてください。
プログラムも送ってください。 その際、Active!Mail のメール作成画面の「添付ファイル」 *ではなく* 本文の中にソースファイルを貼りつけること。
所要時間を計るにせよ回数を数えるにせよ、 数回くり返して測定し、それらのデータそのものおよび平均値をレポートに書くこと。 それから、理論上の値などと比較すること。 感想は書かなくてもよい。
宛て先は私(岩瀬)の実習用アカウント cf5326@mailedu1.ipc.kanazawa-u.ac.jp です。 件名は「kadai4」(←全て半角文字、アルファベットは小文字、 途中にスペースをいれない)としてください。 自分の学籍番号、氏名(として大学に届けてあるもの) をメールの *本文内にも* 書くのを忘れずに。
先学期の「計算機基礎論3B」の最後に紹介した、 最大値を求めて交換してゆくやり方も、素朴なソートの一種である。 これは交換回数は(高々)N だが、比較回数はおよそ N2/2 である。 これも、興味のある人は上にならっていろいろ実験してみるとよい。