アルゴリズムと計算量fuchida/lecture/algorithm/alg...algorithms and data structures on c...
TRANSCRIPT
Algorithms and Data Structures on C
アルゴリズムと計算量
Algorithms and Data Structures on C
この回の要点
• アルゴリズムの性能の基準は計算量• 計算量(complexity)
– 時間計算量(time complexity)– 空間計算量(space complexity)– 最大計算量(worst case complexity)– 平均計算量(expected comlexity)
• オーダー(order)の概念• 計算量の増加の形
– 指数時間アルゴリズム(exponetial time alg.)– 多項式時間アルゴリズム(polynomial time alg.)
• 再帰的アルゴリズム
Algorithms and Data Structures on C
アルゴリズムの性能
• アルゴリズムの性能はどのようにして計るか?– 実際にプログラムを作ってみて、実行時間を比べる?(ベンチマークテスト)
– 問題点• 使用するマシンの性能に依存する
• コードを書く人間の技量に依存する
• 使用する(高級)言語の性能に依存する
• 使用した入力データに依存する
– 仮想的な計算モデルを使用する• レジスタのビット数は不問
• CPUのクロック数も不問
• 1つの命令は単位時間で実行できる。
Algorithms and Data Structures on C
計算量とは
• プログラム:入力→出力を得るもの– ある特定の入力例には関心がない
• 偏った入力に対する結果は意味がない
– 入力データ数 n (入力の規模・サイズ)
– n が増えたときに、出力が得られるまでの時間がどのように変わるか?
– アルゴリズムの計算時間を n の関数として f(n) と表し、この f(n) をアルゴリズムの時間計算量と呼ぶ。
– 「時間」を「空間(メモリ)」に置き換えた場合、空間計算量と呼ぶ。
• サイズ n の入力データにはいろいろある– そのアルゴリズムにとって最悪のデータを使った場合を最大計算量という
– すべての入力の計算量の平均をとったものを平均計算量という
Algorithms and Data Structures on C
計算量の例(1)
• ハイ&ローゲーム– 2人(親と子)で遊ぶ
– 親は、1から128までの数字の中から1つを子に内緒で思う
– 子は、親がどの数字を思っているかをできるだけ少ない回数で当てる
– 子は親に以下の質問をすることができる• あなたの思っている数字は○○より小さいですか?
• どのような戦略が考えられるだろうか?
• 数字が1から n までの場合はどうだろうか?
32
5
78
21
98104
39
11
?
Algorithms and Data Structures on C
計算量の例(2)
• 線形探索(リニアサーチ)– 子は、2から128までの数字を順番に言っていく
• あなたの思っている数字は2より小さいですか?• あなたの思っている数字は3より小さいですか?• あなたの・・・
– 最大計算量は?• 親がいつも128を思っていたとき• 常に127回が必要
– 平均計算量は?• 親がランダムに1つの数字を選んで思っていたとき• 場合によって早く当たることもあれば、遅く当たることもある• 平均すると、64回くらいで当たる
– ちょうど真ん中だから。
Algorithms and Data Structures on C
計算量の例(3)
• 二分探索(バイナリサーチ)– 探索範囲を半分に分ける
• あなたの思っている数字は64より小さいですか?→いいえ
• あなたの思っている数字は96より小さいですか?→はい
• あなたの思っている数字は80より小さいですか?→はい
• あなたの・・・
– 最大計算量=平均計算量• どの数字を思っていても、7回で当てることができる
1 12864 72 968076
74
⑦
①②③
④⑤
⑥
なぜ7回?
27=128 だから
Algorithms and Data Structures on C
計算量の例(4)
• 思う数字が1から n までだったら?
– 線形探索• 最大計算量= n
• 平均計算量= n/2
– 二分探索• 最大計算量=平均計算量=log2 n
• この計算量は、親への「問い」に関するもの
– 1回の問いに一定の時間必要ならば、時間計算量
– 1回の問いに一定のメモリーが必要ならば、空間計算量
Algorithms and Data Structures on C
n と log n の計算量(1)
0
20000
40000
60000
80000
100000
0 20000 40000 60000 80000 100000
n
n/2
log n
通常のグラフ
Algorithms and Data Structures on C
n と log n の計算量(2)
1
10
100
1000
10000
100000
1 10 100 1000 10000 100000
n
n/2
log n
対数グラフ
Algorithms and Data Structures on C
C言語による実装
• ハイ&ローゲームをC言語で実装してみる• ソースコード(HiAndLow.cc)• コンパイル(Cygwin環境)
– g++ –o HiAndLow HiAndLow.cc -lwinmm
• 実行方法– HiAndLow L 1000
• 1から1000までの範囲の数字を線形探索で探す
– HiAndLow B 20000• 1から20000までの範囲の数字を二分探索で探す
• 結果(csv形式)– 最大数, 1回の探索に要した時間[ms]
• 連続実行– HiAndLow.sh
• いくつかの n に対して上記2つの方法を連続的に実行する
Algorithms and Data Structures on C
実行結果
1.E-05
1.E-04
1.E-03
1.E-02
1.E-01
1.E+00
1.E+01
1.E+02
1.E+03
1.E+04
1.E+05
1.E+00 1.E+01 1.E+02 1.E+03 1.E+04 1.E+05 1.E+06
b_search
l_search
n
log n
Algorithms and Data Structures on C
オーダーの概念
• 計算量のオーダー記法– 入力データの大きさ n に対する計算量を f(n)
– ある関数 g(n) と2つの定数 n0,c が存在し、n>n0 なる n に対し常にf(n)<cg(n) となるとき、f(n)=O(g(n)) と記し、計算量はオーダー g(n) である、という
• 十分大きな n に対しては f(n) は g(n) の定数倍である
• n0 は n が小さい場合の例外を許すため
– 例:• のオーダーは O(n2)
• なぜなら、十分大きい n に対しては
• 定数倍の違いは、計算量の増え方には関係しない
– オーダーの計算• O(n2)+O(n)=O(n2) :和は大きい方になる
• O(n2)O(n3)=O(n5) :積は積になる
52)( 2 nnnf23)( nnf
Algorithms and Data Structures on C
計算量の増加の形(1)
• ある問題を解くのに7種類のアルゴリズム
– A:log(n)
– B:n
– C:n log(n)
– D:n2
– E:n3
– F:2n
– G:n!
Algorithms and Data Structures on C
計算量の増加の形(2)
1
1E+10
1E+20
1E+30
1E+40
1E+50
1E+60
1E+70
1E+80
1E+90
1E+100
1E+110
1E+120
1E+130
1E+140
1E+150
10 100 1000 10000 100000
log(n)
n
n log(n)
n*n
n*n*n
2^n
n!
Algorithms and Data Structures on C
計算量の増加の形(3)
1
10
100
1000
10000
100000
1000000
10000000
100000000
1E+09
1E+10
1E+11
1E+12
1E+13
1E+14
1E+15
10 100 1000 10000 100000
log(n)
n
n log(n)
n*n
n*n*n
Algorithms and Data Structures on C
計算量の増加の形(4)
• 指数時間アルゴリズム(exponential time alg.)– 時間計算量が n の指数関数(2n や 3n など、n!も含む)
– n の増加に伴い急激に計算量が増加する厄介な問題
• 多項式時間アルゴリズム(polynomial time alg.)– 時間計算量が n の多項式(n や n2 など)
– ある程度実用的なアルゴリズム
• NP困難(完全)問題(NP-hard(complete) problem)– NP = Nondeterministic-Polynomial (非決定的多項式)
– 指数時間アルゴリズムは得られているが、多項式時間アルゴリズムがわからないような問題
– 巡回セールスマン問題、ハミルトン閉路問題、ナップサック問題など
– 規模が大きいと、事実上解けない
– 規模を小さくするか、近似解でごまかすか?
Algorithms and Data Structures on C
巡回セールスマン問題(TSP)
• N個の都市– 各都市間の距離は決まっている– 距離はコストをあらわす(時間などでも良い)
• 問題– 一人のセールスマンがある都市を出発し、すべての都市を1回だけ訪問して元の都市に戻るとき、最短経路を求めよ。
位置は同じ道のりは?
Algorithms and Data Structures on C
TSPの解法
• NP困難な問題– 多項式時間では解けそうにない– 一般には規模(=都市数)が大きくなると、近似的にしか解けない
– 完全解を求めるには全探索法しかない• 枝刈り等で若干探索空間を狭めることは可能
• 全探索法– 考えられるすべての経路長を計算し、その中で最も短いものを探す
• 最初の都市を選ぶ→N-1通り• 次の都市を選ぶ→N-2通り• その次の都市を選ぶ→N-3通り• ・・・• 最後の都市を選ぶ→1通り
– すべての組み合わせは、(N-1)!通り
Algorithms and Data Structures on C
TSP解法時間(1つの組み合わせに1ミリ秒かかるとした場合)
都市数
組み合わせの数 時間
5 (4!)/2 = 12 12ミリ秒
6 (5!)/2 = 60 60ミリ秒
7 (6!)/2 = 360 360ミリ秒
8 (7!)/2 = 2620 2.6秒
9 (8!)/2 = 20160 20秒
10 (9!)/2 = 181440 (18万) 3分
11 (10!)/2 = 1814400 (181万) 30分
12 (11!)/2 = 19958400 (2000
万)
5.5時間
都市数
組み合わせの数 時間
13 (12!)/2 = 239500800 (2億4000万) 2.8日
14 (13!)/2 = 3113510400 (31億) 36日
15 (14!)/2 = 43589145600 (435億) 1.4年
16 (15!)/2 = 653837184000 (6500億) 20.7年
17 (16!)/2 = 10461394944000 (10兆) 332年
18 (17!)/2 = 177843714048000 (177
兆)
5639年
19 (18!)/2 = 3200兆 10万年
20 (19!)/2 = 60000兆 200万年
Algorithms and Data Structures on C
再帰的アルゴリズム(1)
• アルゴリズムの中で自分自身を使っているような形のアルゴリズム– 階乗の計算– ユークリッドの互除法– ハノイの塔– クラス HiAndLow の searchBinary()
– 漸化式で表される数列も
• 帰納的アルゴリズムとも言う• どこかで「底入れ」が行われなければならない
– 処理を中断・終了する条件が必要– 暴走、もしくはスタックオーバーフロー
Algorithms and Data Structures on C
再帰的アルゴリズム(2)
• 階乗の計算– long fact(long n){
if(n==0) return 1; // 0!は1return fact(n-1)*n; // n!は(n-1)!*n
}
• ユークリッドの互除法– int gcd(int m,int n){
if(n==0) return m;
return gcd(n,m%n);
}
Algorithms and Data Structures on C
課題191017
• 再帰呼び出しによるユークリッドの互除法を用いて、与えられた2つの整数の最大公約数を出力するCのプログラムGCD.cc を作成し、そのソースコードと実行結果を示せ。– $ GCD 45 18 ←入力
– 9 ←出力(結果)
– $ GCD 130 39
– 13 《提出要領》• 作成方法:Cのソースと実行結果の画像もしくはテキストファイルをワードに貼り付けて提出すること。(課題はCygwinで作成しなくてもVisualStudio等の他の環境で作成しても良い)• ファイル名はscXXXXXX-al191017.docx
• 提出方法:メールで• 提出先:[email protected]
• メールタイトル:“アルゴリズム課題191017” ← 厳守!• メール本文に氏名と学生番号を明記すること• 提出期限:2019年10月20日(日) 24:00
• 警告:内容が酷似している2つ以上のレポートがあった場合は、双方、不可になる可能性がある。
Algorithms and Data Structures on C
アルゴリズムと計算量終了
Algorithms and Data Structures on C
HiAndLow.cc
main( )
playManually( )exp( )
searchBinary( )
searchLinear( )
ask( )
answer
min_can
max_can
globals
$ HiAndLow L 1000
Algorithms and Data Structures on C
HiAndLow.cc
/***
*** ハイアンドローゲーム
*** ---
*** 親が思っている数字を当てるゲーム
*** 子が可能な質問は「その数字は○○より小さいですか?」のみ
*** 質問によって答えの存在範囲を限定していき、1つに絞れた時点で終了
***/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <mmsystem.h> // -lwinmmが必要
long answer; // 親が思っている数字
long min_can; // 最小の候補
long max_can; // 最大の候補
Algorithms and Data Structures on C
HiAndLow.cc (main)
// メイン
int main(int argc,char **argv){
srand(time(NULL));
if(argc==1)
playManually();
else if(argc==3)
exp(argv[1][0],atoi(argv[2]));
else
fprintf(stderr,"Usage: HiAndLow [L|B max]");
return 0;
}
Algorithms and Data Structures on C
HiAndLow.cc (playManually)
// 手動探索モード
void playManually(){
long n1=1,n2=100,num,cnt=0;
min_can=n1;
max_can=n2;
answer=(int)((double)rand()*(n2-n1+1)/RAND_MAX)+n1;
while(min_can!=max_can){
printf("Is your number lower than this number ?(from %d to %d) : ",n1,n2);
scanf("%ld",&num);
if(ask(num))
printf("Yes. My number is lower than %ld.¥n",num);
else
printf("No. My number is NOT less than %ld.¥n",num);
cnt++;
}
printf("You found %ld! (%d times)¥n",min_can,cnt);
}
Algorithms and Data Structures on C
HiAndLow.cc (exp)// 連続実験モード
void exp(char type,long max){
clock_t start=timeGetTime();
long n1=1;
long n2=max;
long cnt=(type=='L'?100000:10000000);
for(int i=0;i<cnt;i++){
answer=(long)((double)rand()*(n2-n1+1)/RAND_MAX)+n1;
min_can=n1;
max_can=n2;
switch(type){
case 'L': searchLinear(n1,n2); break;
case 'B': searchBinary(n1,n2); break;
}
}
clock_t end=timeGetTime();
printf("%ld %g¥n",max,((double)end-start)/cnt/CLOCKS_PER_SEC*1000);
}
Algorithms and Data Structures on C
HiAndLow.cc (searchBinary)
// 二分探索
// 何回で当てたかを返す
long searchBinary(long n1,long n2){
if(min_can==max_can) return 1;
long m=(n1+n2+1)/2;
if(ask(m))
return searchBinary(n1,m-1)+1;
else
return searchBinary(m,n2)+1;
}
Algorithms and Data Structures on C
HiAndLow.cc (searchLinear)
// 線形探索
// 何回で当てたかを返す
long searchLinear(long n1,long n2){
long i;
for(i=n1+1;min_can!=max_can;i++)
ask(i);
return i-n1+1;
}
Algorithms and Data Structures on C
HiAndLow.cc (ask)
// 親への質問「その数字はnより小さいですか?」
// その答えを返す(Yesなら1、Noなら0)
// 同時に、答えの存在範囲を限定する
int ask(long n){
if(answer<n){
if(max_can>n-1)
max_can=n-1;
return 1;
}
else{
if(min_can<n)
min_can=n;
return 0;
}
}
Algorithms and Data Structures on C
HiAndLow.sh
• シェルスクリプト– コマンドライン入力をまとめて実行する仕組み
– シェルによって読み込まれて1行ずつ実行される
– 変数や、for や if などの制御構文も利用可能
– cmd.exeとは異なる
• Windows環境のCygwinでbashを使う場合– シェルスクリプトの改行コードはLFでなければならない
• Windowsの通常の改行コードはCR+LF
– サクラエディタなどで変更できる
Algorithms and Data Structures on C
HiAndLow.sh#!bash
L_NUMS="2 3 5 7 ¥
10 20 30 50 70 ¥
100 200 300 500 700 ¥
1000 2000 3000 5000 7000 ¥
10000 20000 30000 50000 70000 ¥
100000"
OUT="lap_L.csv"
echo "*** Search by Linear ***" >> $OUT
date >> $OUT
for i in $L_NUMS
do
echo $i
./HiAndLow L $i >> $OUT
done
date >> $OUT
maxを変化させる数のリスト文字列
実験前の出力
実行するシェル名
実験後の出力
変数 i に L_NUMSに入っている数を1つずつ代入しながらdo ~ doneの間を実行する
注意:このコードは、bashというシェル(Cygwinに付属)でのみ実行可能
Algorithms and Data Structures on C
HiAndLow.shB_NUMS="2 3 5 7 ¥
10 20 30 50 70 ¥
100 200 300 500 700 ¥
1000 2000 3000 5000 7000 ¥
10000 20000 30000 50000 70000 ¥
100000 200000 300000 500000 700000 ¥
1000000"
OUT="lap_B.csv"
echo "*** Search by Binary ***" >> $OUT
date >> $OUT
for i in $B_NUMS
do
echo $i
./HiAndLow B $i >> $OUT
done
date >> $OUT
二分探索も同様