980 likes | 1.18k Views
コンピュータ基礎演習 ー探索、整列ー. 岩井 儀雄 iwai@sys.es.osaka-u.ac.jp. 線形探索と二分探索. 線形探索法 (linear search) データを最初から順番に探索する 例) {2, 4, 5, 8, 9, 11, 6, 7, 15, 20} から 15 を探す 最初の要素から始めて9回目→計算量 O(n) 二分探索法 (binary search) データをあらかじめソートしておき,中央の要素から検索する.. 二分探索 (binary search).
E N D
コンピュータ基礎演習 ー探索、整列ー 岩井 儀雄 iwai@sys.es.osaka-u.ac.jp
線形探索と二分探索 • 線形探索法(linear search) • データを最初から順番に探索する 例){2, 4, 5, 8, 9, 11, 6, 7, 15, 20} から15を探す 最初の要素から始めて9回目→計算量O(n) • 二分探索法(binary search) • データをあらかじめソートしておき,中央の要素から検索する.
二分探索(binary search) • {2, 4, 5, 6, 7, 8, 9, 11, 15, 20} から15を探す 1回目 {2, 4, 5, 6, 7, 8, 9, 11, 15, 20}
二分探索(binary search) • {2, 4, 5, 6, 7, 8, 9, 11, 15, 20} から15を探す 2回目 {2, 4, 5, 6, 7, 8, 9, 11, 15, 20}
二分探索(binary search) • {2, 4, 5, 6, 7, 8, 9, 11, 15, 20} から15を探す 3回目 {2, 4, 5, 6, 7, 8, 9, 11, 15, 20} 探索回数 3回 計算量 log2(n) O(log n)
整列(sort) • 内部整列(internal sort) • 主記憶上で行う整列 • 外部整列(external sort) • 外部記憶装置(テープ等)上で行う 整列アルゴリズムの優劣→比較回数と交換回数の大小
安定な整列(stable sort) • 同じ値を持つデータ間の順序関係が整列の前後で保たれている 例)整列前: 7 9 5A 4 8 5B 2 整列後: 2 4 5A 5B 7 8 9 (安定) 整列後: 2 4 5B 5A 7 8 9 (不安定)
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 整列前 20 6 55 74 3 45 13 87 46 30 1回目 比較
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 1回目 20 6 55 74 3 45 13 87 30 46 入替
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 1回目 20 6 55 74 3 45 13 87 30 46 比較
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 1回目 20 6 55 74 3 45 13 30 87 46 入替
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 1回目 20 6 55 74 3 45 13 30 87 46 比較
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 1回目 20 6 55 74 3 45 13 30 87 46 比較
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 1回目 20 6 55 74 3 13 45 30 87 46 入替
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 1回目 20 6 55 74 3 13 45 30 87 46 比較
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 1回目 20 6 55 74 3 13 45 30 87 46 20 6 55 3 74 13 45 30 87 46 20 6 3 55 74 13 45 30 87 46 20 3 6 55 74 13 45 30 87 46 3 20 6 55 74 13 45 30 87 46
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 2回目 3 20 6 55 74 13 45 30 87 46 46 87 30 46 30 45 13 30 13 74 13 55 6 13 6 20
単純な整列(バブルソート) • 配列の後ろから先頭に向かって走査し,もし隣り合う二つの要素の大小関係が逆であったら入れ替える 2回目 36 20 13 55 74 30 45 46 87 3回目 3 6 13 20 30 55 74 45 46 87 4回目 3 6 13 20 30 45 55 74 46 87 5回目 3 6 13 20 30 45 46 55 74 87 6回目 3 6 13 20 30 45 46 55 74 87 7回目 3 6 13 20 30 45 46 55 74 87 8回目 3 6 13 20 30 45 46 55 74 87 9回目 3 6 13 20 30 45 46 5574 87
バブルソート(疑似コード) for (i←0..n-1) for (j←n-1..i) if !(a[j-1] ≦ a[j]) swap(a[j-1],a[j]) 右辺値の交換 整列アルゴリズムが満たす事後条件 計算量は O(n2)
バブルソート(C言語) for (i←0..n-1) for (j←n-1..i) if !(a[j-1] ≦ a[j]) swap(a[j-1],a[j]) void bubble_sort(int a[],int n) { int I,j,t; for (int I=0; I<n-1; ++I) for (int j=n-1; j>I; --j) if (a[j-1] > a[j]) { t = a[j]; a[j] = a[j-1]; a[j-1] = t; } } swap(a[j-1],a[j])に相当する
単純な整列(選択ソート) • 未整列部分から最小の要素を選び出し,それを未整列部分の先頭と入れ替える 未整列部分 整列済 未整列部分から最小値を取り出し 続けると下記の条件を満たす 整列アルゴリズムが満たす事後条件
選択ソート 整列前 20 6 55 74 3 45 13 87 46 30 1回目 入替
選択ソート 整列前 20 6 55 74 3 45 13 87 46 30 1回目 3 6 55 74 20 45 13 87 46 30
選択ソート 整列前 20 6 55 74 3 45 13 87 46 30 1回目 3 6 55 74 20 45 13 87 46 30 2回目 3 6 55 74 20 45 13 87 46 30 入替
選択ソート 整列前 20 6 55 74 3 45 13 87 46 30 1回目 3 6 55 74 20 45 13 87 46 30 2回目 3 6 55 74 20 45 13 87 46 30 3回目 3 613 74 20 45 55 87 46 30 4回目 3 613 20 74 45 55 87 46 30 5回目 3 613 2030 45 55 87 46 74 6回目 3 613 2030 45 55 87 46 74 7回目 3 613 2030 45 46 87 55 74 8回目 3 613 2030 45 46 55 87 74 9回目 3 613 2030 45 46 55 74 87
選択ソート(疑似コード) for (i←0..n-2) lowest←argmin(a[j]) i<j<n swap(a[i],a[lowest])
選択ソート(C言語) void selection_sort(int a[],int n) { int i,j,t,lowest,lowval; for (i = 0; i<n-1; ++i) { lowest = i; lowval = a[i]; for (j = i+1; j < n; ++j) if (a[j] < lowval) { lowval = a[j]; lowest = j; } t = a[I]; a[I] = a[lowest]; a[lowest] = t; } } argminの計算部分
選択ソートの計算量 • ループ n 回,argmin の計算量 O(n) • 選択ソートの計算量は O(n2)
単純な整列(挿入ソート) • 配列の一部分が整列済みの時に,残りの要素を一つずつ整列済みの中に挿入する 整列前 20 6 55 74 3 45 13 87 46 30 1回目 6 20 55 74 3 45 13 87 46 30 2回目 6 2055 74 3 45 13 87 46 30 3回目 6 205574 3 45 13 87 46 30 4回目 3 6 20 55 74 45 13 87 46 30 5回目 3 6 20 45 55 74 13 87 46 30 6回目 3 6 13 20 45 55 74 87 46 30 7回目 3 6 13 20 45 55 74 87 46 30 8回目 3 6 13 20 45 46 55 74 87 30 9回目 3 6 13 20 30 45 46 55 74 87
挿入ソート(疑似コード) for (i←0..n-1) j←i while (! (a[j-1] ≦ a[j]) ) swap( a[j-1], a[j] ) j←j-1 計算量:外側ループ O(n) 内側ループ O(n) 合計: O(n2)
コンピュータ基礎演習 ー計算量ー 岩井 儀雄 iwai@sys.es.osaka-u.ac.jp
計算量(complexity) • 時間計算量(time complexity) • アルゴリズムがデータに対してどれくらい時間がかかるかを示す • 空間計算量(space complexity) • アルゴリズムがデータに対してどれくらい記憶領域を必要とするかを示す • 時間計算量と空間計算量はトレードオフの関係にあることが多い • 計算機にとって記憶資源は潤沢にあるので,通常時間計算量の方が重きを置かれることが多い
オーダー記法O • 計算量T(n)の上界値を評価するとき,O(f(n))という記法を用い,オーダーf(n) と読む. ある正定数cとn0が存在して,n0以上のnに対して, 常に T(n) ≦cf(n) が成立するという意味 n0の役割は有限個の例外を許すことにある.
オーダー記法Ω • 計算量の下界値のオーダーを表すには,記法Ωを用いる.T(n)=Ω(f(n))とは,
計算量とオーダ表記の関係 計算量の上界O 計算量 実際の計算量 有限個の 例外はOK 計算量の下界Ω n n0
オーダーの演算 例)
最悪計算量と平均計算量 • 同じアルゴリズムでも入力するデータに応じて計算量が変化する.そこで,客観的に測定し評価する必要がある. • 最悪計算量 worst case complexity • 全ての入力パターンに対して最大の計算量を要するものに基づいて定める • 平均計算量 average complexity • 全ての入力パターンとその入力の生起確率に基づいて計算量の平均を求める
コンピュータ基礎演習 ー再帰ー 岩井 儀雄 iwai@sys.es.osaka-u.ac.jp
再帰 • 再帰的(recursive)な構造とは,自分自身(n次)を定義するのに,自分自身より1次低い集合(n-1次)を用い,さらにその部分集合は,より低次の部分集合を用いて定義するということを繰り返す構造.このような構造を一般的に再帰(recursion)と呼ぶ.
再帰 • 数学ではいろいろな概念を再帰的に定義している. 例)数列
再帰呼び出し • 関数の中で自分自身を関数として呼び出すこと 例)数列 int A(int n) { if (n == 1) return 1; /* 再帰呼び出しの停止条件 */ else return A(n-1)+2*n-1; }
再帰呼び出し例 • 階乗(n!)の計算プログラム #include <stdio.h> int factorial(int n) { if (n == 0) return 1; else return n*factorial(n-1); } int main() { int n; for (n=0; n<10; ++n) printf( “ %2d! = %d\n”, n, factorial(n) ); return 0; }
階乗(n!)の計算実行例 10! = 3628800 11! = 39916800 12! = 479001600 13! = 1932053504 14! = 1278945280 15! = 2004310016 16! = 2004189184 17! = -288522240 18! = -898433024 19! = 109641728 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 演算結果が正しくない 13! = 6227020800 14! = 87178291200 15! = 1307674368000 16! = 20922789888000 17! = 3555687428096000 18! = 64023737057280000 19! = ? 実行:PowerBook G4, MacOS X 10.2.5, gcc 3.1 intが32ビットなら,正の数は 231 = 2147483648までしか表現できない
演習課題 • フィボナッチ数列を再帰を用いて求める #include <stdio.h> int fibonacci(int n) { if (n == 1 || n == 2) return 1; else return fibonacci(n-1)+fibonacci(n-2); } int main() { int n; for (n=0; n<20; ++n) printf( “ %2d! = %d\n”, n, fibonacci(n) ); return 0; }
コンピュータ基礎演習 ーリスト構造ー 岩井 儀雄 iwai@sys.es.osaka-u.ac.jp
ADT Stack (スタック) • データの集まり • 一番上の要素しか操作できない • イメージ的には物を上に積んだ状態 スタックに可能な操作 ・一番上の要素の値を見る(top) ・一番上の要素を取り除く(pop) ・上に要素を積む(push) ・スタックが空?(isEmpty) ・スタックの要素数(size)
ADT Queue(待ち行列) • データの集まり • 要素の挿入,削除が端でしかできない • イメージ的には人の待ち行列 待ち行列の先頭
ADT Stack, ADT Queue の実装 • 前回は配列を使って実装した • 今回はリスト構造を使って実装する • →まずはリスト構造について
リスト構造 • 連結リスト • 循環リスト • 双方向リスト
連結リスト(Linked list) • データの集まり • データが一覧表(リスト)のように連なっている構造 • 連なりの表現には効率のため,ポインタが用いられることが多い • もちろん,配列とインデックスでも実装できる(リスト容量が制限)