計算量#
計算量とは、アルゴリズムが問題を解くときに、どれくらいの計算資源を使うかを表す指標
計算量の種類#
時間と空間#
時間計算量(time complexity):処理にどれくらい時間がかかるか
空間計算量(space complexity):メモリをどれくらい使うか
入力・操作に対するケース分類#
同じアルゴリズムでも、入力やランダム性・操作の文脈によって計算量は異なる。 以下の5つの観点で整理される。
種類 |
対象 |
記法の慣習 |
|---|---|---|
最良(best-case) |
最も有利な入力 |
\(\Omega(\cdot)\) |
最悪(worst-case) |
最も不利な入力 |
\(O(\cdot)\) |
平均(average-case) |
入力の確率分布に対する期待値 |
\(\Theta(\cdot)\) |
期待(expected) |
アルゴリズム内のランダム性に対する期待値 |
\(O(\cdot)\) |
償却(amortized) |
一連の操作全体の平均コスト |
\(O(\cdot)\) |
最良計算量(Best-case)#
最も有利な入力が与えられたときの計算量。
最良計算量は「都合のよい場合」の性能なので、実用上は最悪計算量や平均計算量の方が重視されることが多い。
例:線形探索
最良:先頭要素が目標値 → \(O(1)\)
最悪:目標値が末尾or存在しない → \(O(n)\)
def linear_search(lst, target):
for i, x in enumerate(lst):
if x == target:
return i # 最良は i=0 のとき O(1)
return -1 # 見つからない場合は O(n)
最悪計算量(Worst-case)#
最も不利な入力が与えられたときの計算量。\(O\) 記法で上界を表す
アルゴリズム評価で最もよく使われる指標。「どんな入力に対しても必ずこの時間以内に終わる」という保証になる
例:クイックソート
最悪:ピボットが常に最大/最小値(整列済み入力など) → \(O(n^2)\)
最良・平均:\(O(n \log n)\)
最悪ケースを避けるためにランダムピボットを使う → 期待計算量 \(O(n \log n)\) が保証される
平均計算量(Average-case)#
入力の確率分布 を仮定したときの、計算量の期待値
入力が一様分布に従うなど、確率モデルの仮定が必要
最悪より楽観的な評価になることが多い
仮定した分布と実際の入力が乖離すると意味を失う
例:クイックソート(ランダム入力)
入力が一様ランダムなら、ピボットが偏らず \(O(n \log n)\) になる
例:線形探索(目標値が一様分布で存在する場合)
期待計算量(Expected complexity)#
アルゴリズム自体のランダム性(乱数使用)に対する計算量の期待値
平均計算量との違い:
平均計算量 |
期待計算量 |
|
|---|---|---|
ランダム性の源 |
入力の分布 |
アルゴリズムの乱数 |
入力 |
確率的に変化する |
最悪ケース入力でも成立 |
期待計算量はどんな入力に対しても期待値として成立するため、より強い保証
例:ランダムピボットのクイックソート
ランダムにピボットを選ぶ → 最悪入力(整列済み)に対しても期待 \(O(n \log n)\)
例:ハッシュテーブル(チェイン法)
良いハッシュ関数(or ランダムハッシュ)を使えば、探索の期待計算量は \(O(1)\)
償却計算量(Amortized complexity)#
一連の操作全体 にかかるコストを操作数で割った、1操作あたりの平均コスト
個々の操作は高くなることがあるが、それが稀であれば全体として安く抑えられる
例:動的配列(Pythonの list)への append
通常:\(O(1)\)(末尾に追加するだけ)
容量超過時:\(O(n)\)(新しいメモリを確保して全要素コピー)
容量は2倍ずつ増やすため、コピーは \(n\) 回の操作で1回しか起きない
例:スタック(push/pop/multipop)
multipop(k)は \(O(k)\) だが、pushした要素しかpopできないため、 \(n\) 回の操作全体の合計コストは \(O(n)\) → 償却 \(O(1)\)
集計法(aggregate method):\(n\) 回の操作の合計コストを \(n\) で割る
ポテンシャル法(potential method):データ構造の「蓄積エネルギー」を定義して各操作のコストを補正する
まとめ:どれを使うか#
種類 |
使いどころ |
|---|---|
最悪 |
アルゴリズムの安全な保証を示したいとき(最も一般的) |
平均 |
典型的な入力でのパフォーマンスを示したいとき |
期待 |
ランダム化アルゴリズムの性能を示すとき |
償却 |
一連の操作の実際のスループットを示したいとき |
最良 |
下界の確認・理論的な議論のとき |
「\(O(1)\) で速い」という主張が 最悪なのか償却なのか期待なのか を区別することが重要。
たとえばPythonの list.append は最悪 \(O(n)\)、償却 \(O(1)\)。