基礎勉強会 #6
アルゴリズム
今回の話
- アルゴリズムとは?
- 様々なアルゴリズム
- アルゴリズム設計
- 競技プログラミングのすすめ
- 参考資料
今回話さないこと
- 計算量理論
- P とか NP とか計算量クラス
アルゴリズムとは?
アルゴリズム
有限個の操作で構成される問題の解決方法
- プログラミングに限らない
- 元々は数学の文脈で出てきた
- プログラミングの文脈では
- 特定の問題を効率よく解決するコード
ユークリッドの互除法
最古のアルゴリズム
正整数 a と b について次の手順で最大公約数を求められる
- c = a mod b
- c が
- 0 なら c が最大公約数
- 0 でないなら b を a、c を b にして 1 に戻る
アルゴリズムは
仕事の役に立つか?
求人で見る
求人で見てみると
- アルゴリズムとデータ構造
- 機械学習アルゴリズム
- 検索アルゴリズム
- 画像処理アルゴリズム
- 自動取引アルゴリズム
- ロボット制御アルゴリズム
etc, etc
分野ごとに特化したアルゴリズムが多い
「アルゴリズムとデータ構造」という場合は基礎知識
通常業務でアルゴリズムを使っているか?
何かしらの形で使っている
- ライブラリ内部
- 自分で書いていてもあまり意識しないことも
- 見方を変えれば for ループを使った処理もアルゴリズムと言えるかも
For 文のない世界
7 million humans から
定期的にでる「アルゴリズムの学習」不要論
「アルゴリズムよりライブラリの使い方を覚えるべき!」
Pros
- ライブラリ使えればできる作業は多い
- ライブラリ以外でも覚えることはたくさんある
- 知らないまま死ぬ人も多分いる
Cons
- 専門性の高い作業では必須
- 複雑な作業では必要になることが多い
- 一見複雑な作業を省コード・高性能で
個人的には
仕事ではよく使用・調査する
パフォーマンスの調査
- ライブラリのスペック
- 採用アルゴリズムの理解
コンパイラ関連
- 構文木関連のアルゴリズム
- 依存の解決
並列・平行・分散処理
- 負荷分散
まとめると
- ライブラリなどで意識せずにアルゴリズムを使っている
- 専門性が高くなるとアルゴリズムを設計する側に
計算量(復習)
アルゴリズムの良し悪しをどう判断するか?
- 実際の実行時間を使う?
- メモリ、CPU など環境によって違う
- 入力の大きさによってどのように変化するかを知りたい
- → 計算量
計算量
次のような計算を考える
for (i = 0; i < n; i ++) {
for (j = 0; j < n; j++) {
c[i][j] = a[i] * b[j];
}
}
- メモリアクセス m や 演算 c は環境依存
- 入力の大きさによって変化するのは n の部分だけ
これをビッグオー記法にすると、
ビッグオー記法
- 入力サイズ n に対する実行時間を O(f(n)) で表す
- f(n) - 増加率
大きい項だけ残す
大きければ性能が悪い
比較
計算量の例
計算量 | 例 |
---|---|
O(1) | ArrayList から値の取り出し |
O(log N) | 二分木の探索 |
O(N) | 単純なループ |
O(N log N) | クイックソート |
O(N ^ 2) | 2重ループ |
O(N!) | N 個すべての組み合わせの列挙 |
アルゴリズム
どうやってアルゴリズムを適用するか?
まず問題を把握する
- ソートなどはわかりやすい
- 一見では既知の問題とわからないような問題もある
それができてからどのアルゴリズムを使うか考える
- 既にソルバがある場合は利用を考える
- 問題を他の既知の問題に変換できないかも考えてみる
- ex) 依存関係に関する問題はグラフ問題に変換できる
例) 依存関係があるライブラリ
ライブラリを依存関係が満たされる順番で列挙したい
- ソートでは解決できない!
- 全てのライブラリ同士が比較可能ではない
- 循環する依存関係があるかも
- 依存関係を有向な枝と考えるといい
Library 1
Library 2
依存関係を矢印で表す
基礎的なアルゴリズム
-
ソートアルゴリズム
-
グラフアルゴリズム
-
文字列アルゴリズム
-
線形計画法
- など様々
ソート
リストの整列を行うアルゴリズム
- バブルソート
- 挿入ソート
- マージソート
- 基数ソート
- クイックソート
バブルソート
- 2つを比較して大きいものを後ろにずらす
- 最大のものが確定したら最初に戻る
5
3
1
2
4
3
5
1
2
4
3
1
2
4
5
クイックソート
- 基準となる値を1つ選ぶ (ピボット)
- ピボットより大きい、小さいでグループ分けする
- それぞれのグループで 1. から再度実行
マージソート
- リストを小さいリストに分ける
- それぞれのリストでマージソート
- 小さいリストを順序を維持しつつつなぎ合わせる
どのアルゴリズムを選ぶか?
- 性能
- 計算量で比較する
- 得意・不得意の把握
- 同じ問題に対するアルゴリズムでも得意不得意あり
- 特定の傾向がある問題に強い
- 並列化しやすさ
- 同じ問題に対するアルゴリズムでも得意不得意あり
- 実際の計測
- 実際に測ってみると思ったより遅かった、というのはよくある
- キャッシュがうまく使えてなかった
- 想定していないボトルネックがあった
- 切り替えられるように実装するとよい
- 実際に測ってみると思ったより遅かった、というのはよくある
グラフアルゴリズム
グラフに対するアルゴリズム
- 最短経路問題
- 最小全域木
- マッチング問題
ダイクストラ法
最短経路を求めるアルゴリズム
3
4
2
5
6
4
5
ダイクストラ法
最短経路を求めるアルゴリズム
3
4
2
5
6
4
5
3
4
9
5
9
アルゴリズムの設計
アルゴリズムの設計
- 基礎
-
- 再帰
- 探索
- 例示
- 全探索
- 分割統治法
- 貪欲法
- 動的計画法
- 探索範囲の制限
アルゴリズムの主要部品
-
再帰
-
後戻り
再帰 recursion
自分自身を呼び出す関数
- アルゴリズムは再帰を使って記述されることが多い
- 分割統治、動的計画法でも使われる
/* n の階乗を計算する */
int fact(int n) {
if (n == 1) return 1;
return n * fact(n - 1);
}
ハノイの塔
N個のブロックを別の軸に移動するのに何手順かかる?
- ブロックは別の軸に移動できる
- 大きいブロックが小さいブロックより上に置けない
ハノイの塔
難しそうだが、再帰的に考えられる
- N-1個の移動
- N番目の移動
- N-1個の移動
1
2
3
後戻り backtracking
深さ優先探索での帰りがけ順にあたる
- メモリの消費を葉までの経路に抑えられる
- 枝刈りに繋げられるためよく使われる
アルゴリズム設計のテクニック
- 最小例・具体例での検討
- 貪欲法
- 分割統治法
- メモ化
- 動的プログラミング
- 探索空間の制限
最小例・具体例での検討
- N=1 の例
- 具体例
- サイズN-1の問題が解けている場合に、サイズNの問題が解けそうか
貪欲法
各段階でその段階に適した判断を行う
局所最適が存在しない場合は最適解を導ける
ex) ダイクストラ法
ただし実際に適用するのは難しい
- 局所最適が存在しないかどうか見極めが必要
分割統治法
分割: 問題を小さな部分問題に分割する
再帰: 部分問題を再帰的に解決する
統治: それらの解を適切に結合する
メモ化
入力に対して出力が変わらない関数
→ 結果を保存・再利用できる
hast_t h;
int heavy_function(int a);
int memoize_function(int a) {
if (hash_exists(h, a)) {
return hash_get(a);
}
int ret = heavy_function(a);
hash_put(h, ret);
return ret;
}
キャッシュに近いが、計算結果は変わらない
= 一貫性は気にしなくて良い
動的計画法
分割統治法+メモ化
小問題間に依存がある場合、小問題をメモ化することで計算量を削減する
引用: https://xkcd.com/399/
フィボナッチ関数
- 再帰だけでやろうとすると非常に時間がかかる
この例では 14 回の関数呼び出しが必要
フィボナッチ関数
f(N) の計算結果は変わらない=>メモ化
int fibs[100] = {0};
int
fib(int n) {
int ret;
if (fibs[n] > 0) return fibs[n];
ret = fib(n - 1) + fib(n - 2);
fibs[n] = ret;
return ret;
}
フィボナッチ関数
1 ... N-1 まで問題を分割できる->分割統治法
int fibs[100] = {0};
fibs[0] = 0;
fibs[1] = 1;
int
fib(int n) {
for (i = 2; i <= n; i++) {
if (fibs[i] ==0)
fibs[i] = fibs[i - 1] + fibs[i - 2];
}
return fibs[n];
}
動的計画法は、
メモ化を使いながら表(分割された処理結果)を埋めていくイメージ
探索空間の制限
通常探索する空間は膨大になる(全探索)
探索する範囲を制限することで計算量を抑える
- 枝刈り
- ある部分木の探索中にその部分木に解がないとわかったら探索を切り上げる
- α β 枝刈り
- 最大最小問題向けの枝刈り
競技プログラミングのすすめ
競技プログラミング
- 出された問題にコードで回答するオンラインゲーム
- 回答時間や正確性、パフォーマンスなどを競う
有名所だと TopCoder
競技プログラミングの楽しみ方
- コンテストにでなくてもOK
- 対戦ゲームが苦手な人もいるよね…
- オンラインジャッジ
- コードをサーバ側で答え合わせしてくれる
- エレガントな解法を探してみる
- マイナーな言語でチャレンジしてみる
- とにかく短く書いてみる
- → コードゴルフ (これはちょっと別ジャンル)
競技プログラミングの Pros Cons
Pros
- アルゴリズム設計の勉強
- 成績によって履歴書に書ける
- ジョブインタビューに使える
- 報奨が出るコンテストもある
- ex) Google Code Jam - シャツ、Google I/O への招待
Cons
- コンテストの難しさ
- 自分の得意な言語での参加が難しい場合がある
競技プログラミングサイト(日本語対応あり)
- AtCoder
- AOJ
- 会津大学オンラインジャッジ
- どちらも過去問のオンラインジャッジができる
引用: https://twitter.com/chokudai/status/1016244862344036352
実演
参考資料
or 単なるおすすめ
参考資料 1
- 読みもの
- 史上最大の発明アルゴリズム
- テキスト
- データ構造とアルゴリズム
- 入門 データ構造とアルゴリズム
- みんなのデータ構造
- 組み合わせ最適化 理論とアルゴリズム
参考資料 2
- ゲーム
- human resource machine
- 7 million human
- アプリ
- アルゴリズム図鑑
- Webサイト
- https://visualgo.net/ja
参考資料 3
- 競技プログラミング系
- 最速アルゴリズマー養成読本
- TopCoder 向け
- プログラミングコンテスト攻略のためのアルゴリズムとデータ構造
- AOJ 向け
- プログラミングコンテストチャンレンジブック
- 最速アルゴリズマー養成読本
おわり
基礎勉強会#6 アルゴリズム
By Shingo Suzuki
基礎勉強会#6 アルゴリズム
アルゴリズムの基礎
- 1,208