アルゴリズムとデータ構造 第1章 アルゴリズムと計算量 5月6日 情報知能学科 白井英俊 計算量 問題を解く方法 何かを計算し、求める方法 アルゴリズム は何通りかある プログラムは、アルゴリズムをプログラミング言 語で実現し、動くようにしたもの アルゴリズムの計算量を調べれば 速いプログラムかどうかが分かる 要するに、計算量とは、入力に対するアルゴリズムの処理量 計算量(続き) • 逆に言えば、 計算量の小さい プログラムを作る前に 速いアルゴリズムを選び、 それに基づいてプログラムを作る ことが大事 オーダーとは、計算量の大体の目安のこと 速いアルゴリズムを選ぶ例 • 問題:n+1個の実数x1, x2, …, xn ,yが与えら れた時、 x1, x2, …, xnのうちでyにもっとも近 いものを見つけるアルゴリズム 次の方法が考えられる: (1) データを一つ一つ調べ、yとの差が最小値となる 要素を返す(逐次探索)(nearestS.rb) (2) データを降順にソートしておき、「二分探索」により 該当する要素を探す(nearestB.rb) ちょっと「探索」の話を n=17, y=77とする: • 逐次探索: 先頭から一つ一つyと比較し、そ の差が最小の要素を探す 最初の要素から一つ一つ調べるために「逐 次」探索という 差 76 67 55 42 候補 37 29 22 17 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] 1 3 1 10 22 35 40 48 55 60 76 80 82 88 90 93 96 98 99 ちょっと「探索」の話を(続) n=17, y=77とする: • 二分探索 探索の範囲の真ん中の要素をチェックし続け ることで、範囲の絞り込みを行う: チェックする候補 77 > 76 から大の方 77< 90から小の方 77< 82から小の方 (0 + 16) / (8 + 16) / (8 + 12) / (8 + 10) / 2 2 2 2 =8 = 12 = 10 =9 差= 1 13 5 3 (8+9)/2 = 8 なので、[8]と[9]の要素を比較して終了 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] 1 10 22 35 40 48 55 60 76 80 82 88 90 93 96 98 99 逐次探索と二分探索 • 今の例では、探索対象となった数の個数が少ない (n=17)ので、どちらが速いかは分かりにくかったか もしれない。 • でも、想像力を働かせて、n=1,000,000のときを考 えてみよう。どちらの方式が(いろいろなyの値に対し て)速いだろうか? • 分からない? それでは、プログラムを走らせて試してみよう。でも その前に、「プログラムの実行時間を計測する」とい う話を。。。 時間について • 実時間=経過時間とも • CPU時間=プログラムの実行でCPUを使った時間 キーボードから入力する場合、経過時間は増えるが、CPU時 間は増えない---実時間よりもCPU時間の方がアルゴリズム の「速さ」を表している • ユーザーCPU時間:アプリケーションプログラム自体が直接 CPUを使っている時間。計算時間など。 • システムCPU時間:アプリケーションの依頼でOSが仕事を する(例えばディスクを読む)時にOSが使うCPU時間。ディ スクを読む場合には、実際にディスクを読んでいる時間中で なくその前後でOSがCPUを使う。 • Rubyでは… (Time.nowとは違うよ) Process.times アルゴリズムが速いかどうか… • ベンチマークテスト 「プログラム」(やコンピュータなど)に対し、特 定の処理をさせて、それに要する時間(ユー ザー時間+システム時間)を計測する • この方法は、アルゴリズムにも適用できる。し かし、アルゴリズムを「解析」すれば、処理時 間や処理に要するメモリを求めることができる 速さの比較 • 100万個の整数が入っている sortedNums.txt • それに対して、二分探索(nearestB.rb)と逐次探索 (nearestS.rb)の二つの方法でプログラムを書き、速さ を比較する • どちらが速いだろうか? 内容:どちらも、getNumbersとnearestという関数を含み、 処理時間を計測する。 getNumersはファイルから整数の配列を作る。 nearestは、その配列で、対象の数に最も近い数を返 す。 計算量とオーダ • アルゴリズムの「理論的な」計算量は、処理す る計算機の性能と、アルゴリズムの手続きを解 析することにより求められる。 • 入力の大きさをnとすると、例えば、 3n**2+8*n+6 のように求めることができる。 • これを、できるだけ簡単なnの関数で表したも のがオーダ。 確認:n**2とn は同じ 今の例では、 O(n2) となる 2 アルゴリズムの計算量 以下のような繰り返しでは、計算量は繰り返しの回数に比例 例1: for i in 1..n print i, i**2, i**3,”\n” end 累乗計算やprint文の実行の計算量をまとめて(計算機やプログラミング言 語の実装方法で異なるので)定数のaで表すとする。この例では、これがn 回繰り返されるので、計算量は a*n、オーダは O(n) と表される for i in 1..n # n回繰り返す for j in 1..n # n回繰り返す print a[i,j]*a[j,i],”\n” end end これは二重の繰り返しとなっている。 の部分がn回、その中で、 の部分がn回繰り返されている。つごう、 の部分がn**2 回繰り返される。この例でも配列の参照やprint文の実行の計算量をまとめ て定数のbで表すと、この例の計算量は b*n**2、オーダーはO(n**2) 例2: アルゴリズムの計算量(続) • 関数を用いている場合 関数の性質によって計算量は異なる 入力の大きさを n とすると一般に: ソート(並び替え) ⇒ O(n*log(n)) 配列への操作(代入、アクセス) ⇒ O(1) 配列への要素の追加 ⇒ O(n) • 再帰関数の場合はいろいろ...第5章参照 階乗計算の場合はO(n), ハノイの塔の場合はO(2n) 速いアルゴリズムを選ぶ例(再) • 問題:n+1個の実数x1, x2, …, xn ,yが与えら れた時、 x1, x2, …, xnのうちでyにもっとも近 いものを見つけるアルゴリズム 計算量はO(log(n)) データ(n個)が降順にソートされているとすれば、 (1) 「二分探索」により該当する要素を探す (nearestB.rb) 計算量はO(n) (2) データを一つ一つ調べ、yとの差が最小値となる要 素を返す(逐次探索)(nearestS.rb) 速いアルゴリズムを選ぶ別な例 問題:n個のデータから最も大きいものk個を見つ け出す。(例えば n =1,000,000、 k =10) 計算量はO(n*log(n)) 計算量はO(k) 次の方法が考えられる: (1) データを降順にソートし、上から順にk個選ぶ (2) データの最大値を選び、それをデータから削 除する。これをk回繰り返す。 計算量はO(n) 計算量はO(k*n) 計算量とオーダ • 計算量の式からオーダを求めるには、 1.定数は無視する ただし、元の式がnを含まない場合は1とする 2.nが無限大に近付くときに最も大きくなるnの 項を取り出す 。 例えば、3n**2+8*n+6のオーダはn**2。 これを O(n2) と表す 計算量とオーダ(続) • オーダを求めるには、 log(n), n, n*log(n), n2, n2*log(n), n3, 2n, n!, nn のような関数が、nの値の変化に対して(特にnが大き な値のときに)どのように大きくなるかを認識する必 要がある。 • それを図示するソフトウェア の一つが GnuPlot。 • 対数 の知識も不可欠。 Gnuplotの使用例 log(x), (log(x)) 2, x, x*log(x), x2の比較 1e+008 x log(x) x*log(x) x**2 (log(x))**2 1e+007 1e+006 100000 10000 1000 100 10 1 0 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000 Gnuplotの使用例 log(x), 2 3 , x, x , x の比較 x 1e+009 x**0.5 x 1e+008 log(x) x**2 x**3 1e+007 1e+006 100000 10000 1000 100 10 1 100 200 300 400 500 600 700 800 900 1000 Gnuplotの使用例 x, x2, x3, 2x, x!, xxの比較 1e+090 x x**x 1e+080 fact(x) 2**x x**3 1e+070 1e+060 1e+050 1e+040 1e+030 1e+020 1e+010 1 5 10 15 20 25 30 35 40 45 50 計算量:時間と空間 • 時間計算量、もしくは時間複雑度(time complexity) そのアルゴリズムを実行するのに、最悪の場合、どのくらい 時間がかかるかを、入力の大きさの関数として表す • 空間計算量、もしくは空間複雑度(space complecity) そのアルゴリズムを実行するのに、最悪の場合、どのくらい 記憶領域が必要かを、入力の大きさの関数として表す 計算量(複雑度)=時間計算量+空間計算量 時間計算量の常識的な評価 • 教科書p.8 O(1) --- 理想的に速い O(log n) --- 非常に速い O(n) --- 速い O(n log(n)) --- 速いほう O(n2) --- かなり遅いが、許されないほどではない O(n3) --- かなり遅い。許される限界。 O(cn ) --- (cは1より大きい定数)。指数オーダ (exponential order)。とっても遅い。 計算量の演習1 • 教科書p.11 1.1 次の5種類の計算時間を、nを横軸にとって、一つ のグラフに重ねて表せ。そして、そのグラフから オーダの大小関係が読み取れることを観察せよ。 f1(n) = 10000 n f2(n) = 10000n log(n) f3(n) = 100 n**2 f4(n) = 10 n**3 f5(n) = 2**n 計算量の演習2 • 教科書p.11 1.2 ステップ数がf(n)=100000n, g(n)=10n**3, h(n)=2**nの3つのアルゴリズムにn=100の大きさの データを与えたとする。1ステップが10**(-9)秒で 実行できるコンピュータで走らせたときの計算時間 は次のどれに近いか? 1ミリ秒、1秒、1分、1時間、1日、1ヶ月、1年、1世紀 計算量の演習3 • 教科書p.11 1.4 次の式のオーダを、最も忠実で簡単な式を用いて 表せ。 (1) 6.5n3 + 8n2 + 5 (2) 5 n log(n) + 3 n2 (3) 2.5 n √n + 3.6 n log(n) (4) 5 n + 8 log(n) (5) 2n + 5 n 5 (6) 800 n log(n) + n 予習:「2章リスト構造」のために ポインタ(pointer) • ポインタの元々の意味は「指さし」。 コンピュータにおいて「何が何を指すか」というと、 「変数の値」が 「ある実体(オブジェクト)」を指すので ある。 つまり、「変数」の値として 123のような数という「実 体」が入っているのではなく、「実体」 への手がかり (記憶番地、アドレス)が入っている場合、その手が かり(アドレス)をポインタという。 • 参考: プログラミング言語Cでは、int *x のように書 いて、変数xは、整数オブジェクトへのポインタ(アド レス)を値とする、というように宣言する ポインタの詳細 今までの「変数」の イメージ: 変数名 x n 数や文字列を記憶 するためのラベル 記憶場所 123 関係付け(ラベル) ポインタの詳細(続) • アドレス(記憶番地):コンピュータの記憶場所 ここでは少し拡張して「データの格納場所の手が かり」(場所の情報)と考える 例: 配列のインデックス 変数名 x 記憶場所 記憶場所 アドレス かなりの場所を 必要とする複 雑なデータ
© Copyright 2024 ExpyDoc