データ構造とアルゴリズム
第三回
(2013年10月11日)
漸近的計算量と O 記法
http://www.sw.it.aoyama.ac.jp/2013/DA/lecture3.html
Martin J. Dürst
© 2009-13 Martin
J. Dürst 青山学院大学
目次
- 前回のまとめ・宿題
- アルゴリズムの実行時間: 具体から抽象へ
- 関数の漸近的増加の分類
- 漸近記号
- O 記法
前回のまとめ
- アルゴリズムの表現方法:
自然言語の文書、図、疑似コード、プログラム
- それぞれに利点と問題点
- 疑似コードは構造化プログラムに近いが、余計な詳細を省く
- Ruby
プログラム言語を「動く疑似コード」として活用
- アルゴリズムの評価で一番大事は実行時間 (計算量)
アルゴリズムの評価の概要
主な評価基準:
評価のときに必要な情報:
実行時間の比較: 具体から抽象へ
- 実行時間の測定
- ステップの数え上げ
- 最悪の場合などのステップの数え上げ
- 漸近記号
(簡単な式で実行時間の動向を表現)
先週の宿題 1:
ステップの漸近的増加の例
(漸近的な増加 = asymptotic growth)
ステップの数
n (データの個数) |
1 |
10 |
100 |
1,000 |
10,000 |
100,000 |
1,000,000 |
線形探索 (+, []) |
2 |
20 |
200 |
2000 |
20,000 |
~199,930 |
- |
2分探索 (-, +, /, []) |
5 |
13~17 |
25~31 |
41 |
53~57 |
65~69 |
77~81 |
ステップの数を式で表す (近似する) とどうなる
線形探索: 2n; 2分探索: 4
⌈log2(n+1)⌉ + 2
データの個数が多くなると式のどの項が重くなるか
線形探索: 2n; 2分探索: 4 log2n
漸近的増加の考え方
- アルゴリズムの計算時間やステップ数は入力項目の数に依存
- 依存を関数で表す: f(入力項目数)
(f(n))
- 関数の比較に使う原則:
- 入力項目数が増えるところに注目
→ 入力項目数が少ないときを無視
→ 一定時間の差は無視 (例えば初期化)
- アルゴリズムの本質に注目
→ ハードウエアや実装の差を無視
→ 定数倍の差を無視
⇒
ハードウェア、実装の詳細、ステップの数え方に依存しない
⇒ アルゴリズムの本質的な差が簡潔に表せる
先週の宿題 2: 関数の増加の比較
それぞれ二つの関数で、n
が増加すると大きい方はどちらか
左 |
右 |
結果 |
100n |
n2 |
右 (n ≥ 100) |
1.1n |
n20 |
左 (n ≥ 1541) |
5 log2 n |
10 log4 n |
同じ (log2 x = 2
log4 x)
|
20n |
n! |
右 (n ≥ 52) |
100·2n |
2.1n |
右 (n ≥ 95) |
増加の比較: 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,...
漸近的増加の関数の集合
- 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 の定義: 0 ≤ 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 等
一般化: O(na +
nb) ∧a > b
⇒O(na)
対数の底
O(log2 n) と O(log10
n) はどう違うか
(参考: logb a =
logc a / logc
b)
漸近記号の具体例: 探索アルゴリズムの比較
- 線形探索のステップ数は an + b
⇒ 線形探索は O(n)
- 2文探索のステップ数は a log2 n
+ b ⇒ 線形探索は 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!)
のアルゴリズムを探す