計算量のはなし
株式会社SRA
阪井 誠
<sakai @ sra.co.jp>
(時間)計算量
2
• 最近こんな記述を見ませんか?
• O(1) 、O(N) 、 O(logN)とはどんな処理?
ソート済みセット型のコマンド(Redisマニュアル)
ZADD(key, score, member) New in version 1.1.
計算時間: O(log(N)) Nはソート済みセット内の要素数
Deque: 両端における append や pop を高速に行えるリスト風のコンテナ(Python)
Deque はどちらの側からも append と pop が可能で、スレッドセーフでメモリ効率が
よく、どちらの方向からもおよそ O(1) のパフォーマンスで実行できます。
list オブジェクトでも同様の操作を実現できますが、これは高速な固定長の操作に特
化されており、内部のデータ表現形式のサイズと位置を両方変えるような pop(0) や
insert(0, v) などの操作ではメモリ移動のために O(n) のコストを必要とします。
(その他の操作)両端についてインデックスアクセスは O(1) ですが、中央部分につい
ては O(n) の遅さです。高速なランダムアクセスが必要ならリストを使ってください。
最大値の計算量
3
• 配列arrayにN個の正整数が入っています。
最大値を求めてください。
– 配列のサイズ:N
– 配列のデータ:1,20,3,8,4,6,15,12,5,…
– プログラム
max=-1
indexをインクリメントして0~N-1まで繰り返す
If max < array[index]
max = array[index]
ループは何回?
データの追加の際のループは何回?
常にソートするとすれば、、
最大値の計算量
4
• 配列arrayにN個の正整数が入っています。
最大値を求めてください。
– 配列のサイズ:N
– 配列のデータ:1,20,3,8,4,6,15,12,5,…
– プログラム
max=-1
indexをインクリメントして0~N-1まで繰り返す
If max > array[index]
max = array[index]
ループは何回?N(時間計算量)・・・これをO(N)と書く
データの追加の際のループは何回?1・・・O(1)
常にソートするなら?N(位置決め+ずらす)・・・O(N)
バブルソートの計算量
5
• 配列arrayにN個の正整数が入っています。
バブルソートしてください。
– 配列のサイズ:N
– 配列のデータ:1,20,3,8,4,6,15,12,5,…
– プログラム
indexをインクリメントして0~N-1まで繰り返す
index2をインクリメントしてindex~N-1まで繰り返す
If array[index] > array[index2]
array[index] と array[index2]を入れ替える
ループは何回?約N2回 ・・・ O(N2)
バイナリツリー
6
• ソートデータを2分木で管理すると階層回で追加・検索
4 5
1 3 6 8 12 15 20
ノード数
・・・1
(2の0乗)
・・・2
(2の1乗)
・・・4
(2の2乗)
・・・8
(2の3乗)
・・・16
(2の4乗)
8 9
Logの定義
7
• A = logxN のとき xをA乗するとNになる
• log28 は2を3乗すれば8(=2x2x2)なので3
• 情報系では2は省略してよいので
log8 = 3
• 2分木は階層が深くなると葉が2倍に増える
– 葉がN個なら深さはlogN
• データの追加・検索はO(logN)で可能
– ソートにはN倍必要なのでO(NlogN)
計算量の比較
8一週間で身につくアルゴリズムとデータ構造 http://sevendays-study.com/algorithm/
• Nが1、1024、1メガ、1ギガの場合の計算量
• O(1): 1、1、1、1
• O(logN): 1、10、20、30
• O(N): 1、1,024、1,048,576、1,073,741,824
• 木構造の処理に10倍かかっても1、100、200、300
まとめと気を付けること
9
• O(1) < O(logN) < O(N) < O(N2) < O(N3)
– O(NlogN)はどこでしょう?
• 計算量の少ない処理を使う方が良い
• ただし、呼び出しにN回ループを使っていると
O(logN)はO(NlogN)となりO(N)より大きくなる
• 全体の構造を意識してプログラムを設計しな
いといけない
おまけ1:O(1)のデータ構造/アルゴリズム例
10
• 配列(固定長)のn番目のデータの取出し
– データサイズ×nでアドレスが求まる
• 双方向リスト(可変長)の最初と最後の取出し
– 先頭と最後を覚えておけば直接アクセスできる(スタック、キューなど)
– N番目は先頭からn個追わないといけない
• ハッシュ
– 与えられたキーからハッシュ関数でアドレスを決める
– ハッシュ=メチャメチャに切る(例:ハッシュドビーフ)
ハッシュ
関数
おまけ2:計算量の応用分野
• ビッグデータ
– 計算量の少ないアルゴリズムを用いて、大量デー
タを効率よく扱う
• 暗号
– 素因数分解など、計算量の多い難しい問題を利
用して強い暗号を作る
• 人工知能
– 計算量の多いアルゴリズムはあきらめて、簡便な
方法を探す
ナップサック問題と
グリーディーアルゴリズム
12
ポテト
チップ
ポテト
チップ
• 最適な答えを得るのは難しい
• 大きなものから詰め込むと効率が良い
=> 「優先度の高いものから実施せよ!」
(ゲームで用いられるα・β法にもつながる考え)
宿題
13
• それぞれのデータ構造/アルゴリズムを
考えてみましょう
ソート済みセット型のコマンド(Redisマニュアル)
ZADD(key, score, member) New in version 1.1.
計算時間: O(log(N)) Nはソート済みセット内の要素数
Deque: 両端における append や pop を高速に行えるリスト風のコンテナ(Python)
Deque はどちらの側からも append と pop が可能で、スレッドセーフでメモリ効率が
よく、どちらの方向からもおよそ O(1) のパフォーマンスで実行できます。
list オブジェクトでも同様の操作を実現できますが、これは高速な固定長の操作に特
化されており、内部のデータ表現形式のサイズと位置を両方変えるような pop(0) や
insert(0, v) などの操作ではメモリ移動のために O(n) のコストを必要とします。
(その他の操作)両端についてインデックスアクセスは O(1) ですが、中央部分につい
ては O(n) の遅さです。高速なランダムアクセスが必要ならリストを使ってください。

計算量のはなし(Redisを使うなら必読!O(logN)など)