データ構造とアルゴリズム
第四回
(2013年10月18日)
抽象データ型とデータ構造、スタック、キューなど
http://www.sw.it.aoyama.ac.jp/2013/DA/lecture4.html
Martin J. Dürst
© 2009-13 Martin
J. Dürst 青山学院大学
目次
- 前回の残り、まとめ、宿題
- 多項式の重要性
- アルゴリズムの漸近的計算量の求め方
- 漸化式
- 抽象データ型
前回の残りとまとめ
前回の宿題
(提出不要)
ウェブなどで O(1), O(log n), O(n),
O(n log n), O(n2),
O(n3), O(2n), O(n!)
のアルゴリズムを探す
よくあるオーダー
O(n) (linear order, linear time):
データの大きさに比例、全てのデータをチェック
O(log n) (logarithmic order/time),
O(n log n):
二分探索など、データを小分けして処理する場合
O(n2) (quadratic order/time),
O(n3) (cubic order/time):
データの二つ、三つの組み合わせを考える場合
O(2n):
データの全ての部分集合を検討する場合など
O(n!):
データの全ての順番を検討する場合など
多項式増加と指数的増加の比較
実例:
1.1n ≶ n20
log(1.1)·n ≶ log(n)·20
n/log10(n) ≶ 20/log10(1.1)
≊483.2
n0 ≊ 1541
一般的に、a, b > 1 の場合、an
の漸近的な増加は nb
の漸近的な増加より必ず大きい
多項式の重要性
- 「有用」といわれる計算量は問題によって違う
- 一般的には
- 多項式時間 (polynomial time) は有用
- 指数時間 (exponential time) は現実的でない
アルゴリズムの漸近的計算量の求め方
- 入力の大きさの変数 (n など) の決定
- アルゴリズム内の一番多く実行される操作の特定
- 操作の数の算出 (総和または漸化式)
- 操作の数から漸近的計算量の算出
入力の大きさの変数の決定
- 多くの場合、データの項目の数 (例: 探索、整列)
- 行列などの場合、幅や高さ (n × n や
n × m の行列)
- 一データ項目の大きさ (例: 非固定長整数の演算)
一番多く実行される操作の特定
- ループ (特に多重ループ) 内の操作が多い
- 複数の独立したループなどの場合、別々に調査
- 実行される操作の数が入力の具体値に依存の場合、最悪のケースで調査
(最善や平均もあり)
- 操作が関数呼び出しの場合、その関数の中身も考慮
操作の数の算出 (総和)
操作の数の算出 (漸化式)
- 具体例:
binsort(array, low, high, key)
middle = (high+low)/2
if low==high
if array[low]==key
return low
else
return nil
elsif key>array[middle]
return binsort(array, middle+1, high, key)
else
return binsort(array, low, middle, key)
- 操作の数を漸化式で表現:
B(n) = B(⌈n/2⌉) + 1
B(1) = 1
漸化式
(recurrence 又は recurrence relation)
- (プログラムの)
再帰的関数と同様に定義されている数学的関数
- 解くには様々な方法が必要
- 繰り返しの置き換えでパターンの発見:
B(n) = B(⌈n/2⌉) + 1 =
B(⌈⌈n/2⌉/2⌉) + 1 + 1 =
B(⌈n/22⌉) + 2 =
= B(⌈n/23⌉) + 3 =
B(⌈n/2k⌉) + k
- B(1) = 1 を利用:
⌈n/2k⌉ = 1 ⇒ 1
≥ n/2k (>1/2) ⇒
2k ≥ n (> 2k-1)
⇒ k ≥ log2 n (> k-1) ⇒
k = ⌈log2 n⌉
- B(n) = 1 + ⌈log2 n⌉
- 漸近的計算量: O(log n)
抽象データ型
(abstract data type, ADT)
- データとそれを操作する関数のセット
- データは関数以外で操作不可能 (カプセル化)
- データの完全性のための概念
例: 生年月日と年齢
- 膨大なソフトウェアのモジュール化のための概念
- 型理論と連結
- オブジェクト指向による実装が多い
- 型 → クラス (class)
- 関数 → メソッド (method)
抽象データ型の典型例
スタック
(stack)
- 具体例
- 食堂の食膳の山
- IT の例
- プログラムの関数呼び出しのためのスタック
- 原理
- last-in-first-out (LIFO)
- 主なメソッド
- 新規作成 (new), 追加 (push), 削除 (pop)
- その他のメソッド
- 空かどうかのチェック
(empty?)、最上のデータ項目をのぞく (top)
スタックの公理
つぎの四つの公理でスタックの定義が可能
- Stack.new.empty? ↔ true
- s.push(e).empty? ↔ false
- s.push(e).top ↔ e
- s.push(e).pop ↔ s (pop
はデータではなく、スタックを返す場合)
(s が任意のスタック、e が任意のデータ項目)
公理は実装側と使用側の間の約束事
キュー
(queue、待ち行列)
- 具体例
- 食堂などの待ち行列
- IT の例
- 実行待ちプロセスのキュー
- 原理
- first-in-first-out (FIFO)
ADT の比較
実装: 4ADTs.rb
ADT |
スタック |
キュー |
実装 |
Array |
LinearList |
Array |
LinearList |
新規作成 |
O(n) |
O(1) |
O(n) |
O(1) |
項目追加 |
O(1) |
O(1) |
O(1) 又は
O(n) |
O(1) |
項目削除 |
O(1) |
O(1) |
O(n) 又は
O(1) |
O(1) |
empty? |
O(1) |
O(1) |
O(1) |
O(1) |
長さ |
O(1) |
O(n) |
O(1) |
O(n) |
宿題 (提出なし): 4ADTs.rb
にあるクラスを使うプログラムを作成しなさい。
キューの実装
リングバッファ (ring buffer)
@@@
次回のための準備
- 次の関数をオーダの順にならび、その理由を付けなさい。
O(n2), O(n!),
O(n log log n), O(n
log n), O(20n),
- 4ADTs.rb
にあるクラスを使うプログラムを作成しなさい。
- 順位キュー (priority queue) という ADT を実装しなさい
(Ruby でも他言語でもよい)
順位キューは各要素ごとに優先度 (整数など)
が付く。一番簡単な場合にデータ項目は優先度だけ。優先度の高いものが先にキューから出る。実装は配列でも連結リストでもよい。