データ構造とアルゴリズム
第三回
(2014年10月3日)
漸近的計算量と O 記法
http://www.sw.it.aoyama.ac.jp/2014/DA/lecture3.html
Martin J. Dürst
© 2009-14 Martin
J. Dürst 青山学院大学
目次
- 前回の残り・まとめ・宿題
- アルゴリズムの実行時間: 具体から抽象へ
- 関数の漸近的増加の分類
- 漸近記号
- O 記法
前回の残り
前回のまとめ
- アルゴリズムの表現方法:
自然言語の文書、図、疑似コード、プログラム
- それぞれに利点と問題点
- 疑似コードは構造化プログラムに近いが、余計な詳細を省く
- Ruby
プログラム言語を「動く疑似コード」として活用
- アルゴリズムの評価で一番大事なのは実行時間
(計算量)
前回の宿題 1:
ステップの漸近的増加の例
(漸近的な増加 = asymptotic growth)
[都合により削除]
漸近的増加の考え方
- アルゴリズムの計算時間やステップ数は入力項目の数に依存
- 依存を関数で表す:f(n) (n
が入力項目数)
- 関数の比較に使う原則:
- 入力項目数が増えるところに注目
→ 入力項目数が少ないときを無視
→ 一定時間の差は無視 (例えば初期化の時間)
- アルゴリズムの本質に注目
→ ハードウエアや実装の差を無視
→ 定数倍の差を無視
⇒
ハードウェア、実装の詳細、ステップの数え方に依存しない
⇒ アルゴリズムの本質的な差が簡潔に表せる
先週の宿題 2: 関数の増加の比較
それぞれ二つの関数で、n
が増加すると大きい方はどちらか
[都合により削除]
増加の比較: Ruby の使用
- irb (Interactive Ruby) を立ち上げる
- ループの記述:
(初期値..修了値).each { |n|
比較 }
- 比較の具体例:
puts n, 1.1**n, n**20
- 初期値と修了値を適切に変更
- 必要に応じて比較しやすいように整数を実数に変更
- 階数の定義:
def fac(n) n>1 ? n*fac(n-1) : 1 end
注:
理論的にどちらが大きくなるか分からない場合、使う意味がない
漸近的増加による関数の分類
- 線形増加 (linear growth): f(n) = n,
2n+15, 100n-40, 0.001n,...
- 二次増加 (quadratic growth): f(n) =
n2,
500n2+30n+3000,...
- 三次増加 (cubic growth): f(n) =
n3,
5n3+7n2+80,...
- 対数増加 (logarithmic growth): f(n) = ln
n, log2n, 5
log10n2+30,...
- 指数増加 (exponential growth): f(n) =
1.1n, 2n,
20.5n+1000n15,...
- ...
漸近的増加の関数の集合
- O(g(n)): g(n)
の増加以下の関数の集合 (O: big Oh)
- Ω(g(n)): g(n)
の増加以上の関数の集合 (Ω: オメガ
(大文字))
- Θ(g(n)): g(n)
の増加と同等の関数の集合 (Θ: シータ
(大文字))
O の定義
∃c>0: ∃n0≥0:
∀n≥n0:
f(n)≤c·g(n) ⇔
f(n)∈O(g(n))
- g(n) を f(n)
の漸近的上界 (asymtotic upper bound) という
- 文献によって
- f(n)∈O(g(n))
の代わり
f(n)=O(g(n))
と記述
- この場合、O(g(n)) は必ず右側に
- しかし
f(n)∈O(g(n))
が正確で分かりやすい
Ω と Θ の定義
∃c>0: ∃n0≥0:
∀n≥n0:
c·g(n)≤f(n) ⇔
f(n)∈Ω(g(n))
∃c1>0: ∃c2>0:
∃n0≥0: ∀n≥n0:
c1·g(n)≤f(n)≤c2·g(n) ⇔
f(n)∈Θ(g(n))
f(n)∈Θ(g(n)) ⇔
f(n)∈O(g(n)) ∧
f(n)∈Ω(g(n))
Θ(g(n)) =
O(g(n)) ∩
Ω(g(n))
O 記法の具体例
漸近記号の用途
- O 記法:
アルゴリズムが漸近的に最大で有する計算量
- Ω 記法:
ある問題を解くに必要な最低時間の漸近的増加など
- Θ 記法:
「最大で必要」だけではなく、実際にそれに達していることの表現
この授業も含め、アルゴリズムとデータ構造では
O 記法は非常に多い
O 記法の確認
- 方法 1: 定義の利用
適切に大きい n0 と c
を選択、定義を確認
- 方法 2: 極限の利用
limn→∞(f(n)/g(n)):
- 0 の場合:
O(f(n))⊊O(g(n)),
f(n)∈O(g(n))
- 0 < d < ∞:
O(f(n))=O(g(n)),
f(n)∈O(g(n))
- ∞:
O(g(n))⊊O(f(n)),
f(n)∉O(g(n))
O 記法の単純化
- O
記法ではできるだけ一番簡単な関数の記法を使用
- 具体例:
- 定数関数: O(1)
- 線形関数: O(n)
- 二次関数: O(n2)
- 三次関数: O(n3)
- 対数関数: O(log n)
指数の低い項の無視
具体例: f(n) = an +
b = O(n)
O の定義: f (n) ≤
cg (n) [n >
n0; n0, c > 0]
an + b ≤ cn
[n > n0; n0,
c > 0]
可能な値: c = a +
b/n0
確認: an + b ≤ (a +
b/n0)n [n
> n0; n0, c >
0]
an + b ≤ an +
(b/n0)n [n
> n0; n0 > 0]
b ≤
(b/n0)n [n
> n0; n0 > 0]
1 ≤ n/n0 [n >
n0; n0 > 0]
具体的な値: n0 = 1, c = a +
b; n0 = 2, c = a +
b/2 等 (複数の組み合わせ可能)
一般化: a > b > 0 ⇒
O(na +
nb) =
O(na)
対数の底
O(log2 n) と O(log10
n) はどう違うか
(参考: logb a =
logc a / logc
b)
漸近記号の具体例: 探索アルゴリズムの比較
- 線形探索のステップ数は an +
b
⇒ 線形探索は O(n)
- 2分探索のステップ数は a log2 n
+ b
⇒ 2分探索は O(log n)
- O(log n) ⊊ O(n)
のため、2分探索が早い
まとめ
- アルゴリズムそのもの計算量の評価:
- 定数の項 (初期化など) を無視
- 定数の因子 (実装やハードウェアの差) を無視
- 入力の大きさ・データ項目の数による漸近的増加
- 関数の漸近的増加は関数の集合で表すのが可能
- 表記として O(), Ω(), Θ() を利用
- アルゴリズムの計算量は O(log n), O(n),
O(n2), O(2n)
などで表現可能
宿題
(提出不要)
ウェブなどで O(1), O(log n), O(n),
O(n log n), O(n2),
O(n3), O(2n), O(n!)
のアルゴリズムを探す