仮想 通貨

フィボナッチ数列の計算量について

フィボナッチ数列の計算量について
The Held–Karp algorithm, also called Bellman–Held–Karp algorithm, is a dynamic programming algorithm proposed in 1962 independently by Bellman [1] フィボナッチ数列の計算量について フィボナッチ数列の計算量について and by Held and Karp [2] to solve the Traveling Salesman Problem (TSP). TSP is an extension of the Hamiltonian circuit problem. From Wikipedia, the free encyclopedia

[計算量をざっくり理解] アルゴリズムの実行時間

Big Ω 記法 Big O 記法では、\(f(x) = O(g(x)) \)は,関数\( g(x) \)が関数\( f(x) \)の上界であることを表しました。 Big Ω 記法では、 \(f(x) = \Omega (g(x)) \)は,関.

wikipedia より

\( O(1) \) 定数 Constant、\( O ( \log n) \) 対数 Logarithmic、\( O (n) \) 線形 Linear、\( O ( n /times log n) \) 準線形 Linearithmic 、\( O (n^k), \exists k \gt 1 \) 多項式 Polynomial、\( O (c^n) \) 指数 Exponential、\( O (n !) \) 階乗 Factorial とそれぞれ呼ばれます。

準線形 Linearithmic かそれより速いアルゴリズムであれば実用性があり、より遅い場合は時間がかかりすぎて実用性がないと考えれば、基本的には良いようです。

\( O(1) \) 定数 Constant

ハッシュテーブルの検索や追加

ハッシュテーブル (英: hash table) は、キーと値の組(エントリと呼ぶ)を複数個格納し、キーに対応する値をすばやく参照するためのデータ構造。ハッシュ表ともいう。ハッシュテーブルは連想配列や集合の効率的な実装のうち1つである。

出典: フリー百科事典『ウィキペディア(Wikipedia)』

\( O ( \log n) \) 対数 Logarithmic

\( O (n) フィボナッチ数列の計算量について \) 線形 Linear

線形探索(せんけいたんさく、英: linear search, sequential search)は、検索のアルゴリズムの一つ。 リストや配列に入ったデータに対する検索を行うにあたって、 先頭から順に比較を行い、それが見つかれば終了する。

出典: フリー百科事典『ウィキペディア(Wikipedia)』

\( O ( n /log n) \) 準線形 Linearithmic

マージソート

マージソートは、ソートのアルゴリズムで、既に整列してある複数個の列を1個の列にマージする際に、小さいものから先に新しい列に並べれば、新しい列も整列されている、というボトムアップの分割統治法による。大きい列を多数の列に分割し、そのそれぞれをマージする作業は並列化できる。

出典: フリー百科事典『ウィキペディア(Wikipedia)』

ヒープソート

ヒープソート (heap sort) とはリストの並べ替えを二分ヒープ木を用いて行うソートのアルゴリズムである [2] (ヒープ領域とは無関係であることに注意する)。

出典: フリー百科事典『ウィキペディア(Wikipedia)』

クイックソート

\( O (n^k), \exists k \gt 1 \) 多項式 Polynomial

バブルソート

バブルソート (bubble sort) は、ソートのアルゴリズムの一つ。隣り合う要素の大小を比較しながら整列させること。最悪計算時間がO(n 2 )と遅いが、アルゴリズムが単純で実装が容易なため、また並列処理との親和性が高いことから、しばしば用いられる。安定な内部ソート。基本交換法隣接交換法ともいう [1] 。(単に交換法と言う場合もある)

出典: フリー百科事典『ウィキペディア(Wikipedia)』

挿入ソート

挿入ソートインサーションソート)は、ソートのアルゴリズムの一つ。整列してある配列に追加要素を適切な場所に挿入すること。平均計算時間・最悪計算時間がともにO(n 2 )と遅いが、アルゴリズムが単純で実装が容易なため、しばしば用いられる。安定な内部ソート。基本挿入法ともいう。in-placeアルゴリズムであり、オンラインアルゴリズムである

出典: フリー百科事典『ウィキペディア(Wikipedia)』

\( O (c^n) \) 指数 Exponential

ハノイの塔

ハノイの塔(ハノイのとう、Tower of Hanoi)はパズルの一種。 バラモンの塔または ルーカスタワー(Lucas’ Tower) [注 1] とも呼ばれる。

出典: フリー百科事典『ウィキペディア(Wikipedia)』

フィボナッチ数列

動的計画法を用いた巡回セールスマン問題

The Held–Karp algorithm, also called Bellman–Held–Karp algorithm, is a dynamic programming algorithm proposed in 1962 independently by Bellman [1] and by Held and Karp [2] to solve the Traveling Salesman Problem (TSP). TSP is an extension of the Hamiltonian circuit problem.

From Wikipedia, the free encyclopedia

\( O (n !) \) 階乗 Factorial

巡回セールスマン問題

巡回セールスマン問題(じゅんかいセールスマンもんだい、英: traveling salesman problem、TSP)は、都市の集合と各2都市間の移動コスト(たとえば距離)が与えられたとき、全ての都市をちょうど一度ずつ巡り出発地に戻る巡回路のうちで総移動コストが最小のものを求める(セールスマンが所定の複数の都市を1回だけ巡回する場合の最短経路を求める)組合せ最適化問題である。

出典: フィボナッチ数列の計算量について フリー百科事典『ウィキペディア(Wikipedia)』

参考 3 クラス P,N P, N P 完全,N P 困難 複雑性クラス 複雑性クラス(ふくざつせいクラス、英:Complexity class)は、計算複雑性理論において関連する複雑性の問題の集合を指す。典型的な複雑性クラスは以下のよ.

計算量の評価

$f(n)$ と $g(n)$ フィボナッチ数列の計算量について フィボナッチ数列の計算量について が $\mathbb$ 上の関数であるとき、 \[ f(n)= \mathcal(g(n)) \qquad \text \] であるとは、正の実数 $c_1, c_2$ および $n_0$ が存在して、$n_0\leqq n$ なる $n$ フィボナッチ数列の計算量について に対して \[ c_1\lvert g(n) \rvert \leqq \lvert f(n) \rvert \leqq c_2\lvert g(n) \rvert \qquad \text \] であるときをいう。 このとき、$f(n)フィボナッチ数列の計算量について フィボナッチ数列の計算量について $ は $g(n)$ の オーダー (order)であるといい、big $\mathcal$ を使って、$f(n)= \mathcal(g(n))$ あるいは $f(n) \sim \mathcal(フィボナッチ数列の計算量について g(n))$ などと書く。 この 記法は $n$ が十分大きくなったときの関数 $f(n)$ の挙動を表していることに注意する。

$f_1(n)= \mathcal(g_1(n))$ および $f_2(n)= フィボナッチ数列の計算量について \mathcal(g_2(n))$ が正値関数であるとき、定義から次が成立する。 \begin & f_1(n)f_2(n)= \mathcal(g_1(n)g_2(n))\\ & f_1(n) + フィボナッチ数列の計算量について f_2(n)= \mathcal(g_1(n) + g_2(n)). \end

たとえば、次の多項式 $p(n)$ を考えてみる。 \[ p(n)= a_k n^k + a_ フィボナッチ数列の計算量について フィボナッチ数列の計算量について n^+\dots +a_2 n^2 + a_1 n + a_0 \] $n>1$の時には、自明な関係 \[ 1\leqq n\leqq n^2 \leqq \dots \leqq n^\leqq n^k \] から直ちに得られる関係 \begin a_k n^k \leqq p(n) & \leqq a_k n^k + a_ n^k + \dots + a_2 n^k + a_1 n^k + a_0 フィボナッチ数列の計算量について n^k\\ & \leqq n^k (a_k + a_ + \dots + a_1 + a_0) \end は、$p(n)=\mathcal(n^k)$ であることを示している。

これより、多項式については $n$ の最大次数に着目して \begin p_0(n) & 5000000000=\mathcal(1) p_1(n) &= 20000 n + 3000000=\mathcal(n)\\ p_2(n) &= 0.000001 n^2 -31412 フィボナッチ数列の計算量について n - 2600 = \mathcal(n^2)\\ p_3(n) &= 300 n^2 + 600 n + 2500 = \mathcal(n^2) \end となり、$p_2(n)$ と $p_3(n)$ は同じ $n^2$ のオーダーで $n$ が十分に大きくなると同じ程度に増加し、$n$のオーダーの $p_1(n)$ を圧倒し、$p_1(n)$ は $p_0(n)$ フィボナッチ数列の計算量について を圧倒することがわかる(この場合、関数の$\log$-$\log$グラフを書いてみるとこの事実は分かりやすい)。

アルゴリズムの計算量オーダーを低くすることは、大きなデータサイス $n$ に対しては劇的な効果があることになる。 あるいは、指数的オーダーを持つ計算量アルゴリムは大きなデータサイス $n$ に対して 手に負えない (intractable)ことになる。

big $\mathcal$-記法:小さいオーダー順に
order名前
$\mathcal(1)$定数
$\mathcal(\log n)$対数的
$\mathcal(n)$線形的
$\mathcal(フィボナッチ数列の計算量について フィボナッチ数列の計算量について n\log n)$対数線形的
$\mathcal(n^2)$2乗的
$\mathcal(n^3)$3乗的
$\mathcal(2^n)$指数的
$\mathcal(n!)$階乗的

階乗的オーダーが指数的オーダーよりも大きいのは Stirling's の近似公式からわかる。 \[ n! \sim フィボナッチ数列の計算量について \sqrt <2\pi n>n^n \mathrm^ \] 比 $n! /2^n$ の対数をとると \[ \log \left(\frac \right) \sim \left( n+\frac12\right) n -(1+\log 2)n +\text \] から、$n\rightarrow \infty$につれて比はいくらでも大きくなるからである。

代表的なアルゴリズムの計算量

ベクトルの内積

$L_1$ノルムの問題サイズはベクトルの長さ $n$ である。

$L_1$ノルム計算の再帰版 one_norm_recur の時間計算量を評価する。 簡単のためにベクトルの長さ $n$ を $n=2^k$ と仮定しても一般性を失わない($k=\log n$)。 10行目の判断で時間 $T_$と15行目の足し算計算に時間 $T_$を費やすのであるが、13行目と14行目で半分の長さの$L_1$ノルム計算をしていることを考慮すると \[ T_(2^k) = T_ フィボナッチ数列の計算量について フィボナッチ数列の計算量について + T_ + 2T_(2^) \] の関係、つまり $c= T_ + T_$ を定数とすると \[ T_(n)= c + 2T_(n/2) \] が成立する。 このことから、次のように書くことができる。 \begin T_(n) &= c + 2T_(n/2)\\ &= c + 2(c + 2 T_(n/2^2))= c + 2c + 2^2 T_(n/2^2)\\ &= c + 2c + 2^2(c + 2T_(n/2^3))=c + 2c + 2^2c + フィボナッチ数列の計算量について 2^3T_(n/2^3)\\ & \vdots\\ &= c + 2c + 2^2 c+ 2^3 c+ \dots + 2^c + 2^T_(2)\\ フィボナッチ数列の計算量について フィボナッチ数列の計算量について &= c\sum_^ 2^i + n(T_ + T_)\\ &= \fracc + dn \qquad d = T_ + T_\\ &=(c+d)n フィボナッチ数列の計算量について -c=\mathcal(n) \end

メモリ計算量を考えよう。 $L_1$ノルムを計算するために長さ $n$ のベクトルを保持するための入力メモリ量 $M_(n)$ とは区別して、関数内で再帰呼び出しするために必要なメモリを考える必要がある。 サイズ $n$ の計算に再帰呼び出しで必要なメモリ$M_(n)$は \[ M_(n) = c + M_(フィボナッチ数列の計算量について n/2) \] である。$c$ には left_norm や right_norm (など)に必要な固定領域である。 この漸化式は上の時間計算量と同様に評価でき、$M_(n)=\mathcal(n)$ である。

入力メモリ量 $M_(n)$ は明らかに $=\mathcal(n)$ であることから、関数 one_norm_recur のメモリ計算量 $M_(n)$ は次のようになる。 \[ M_(n) = M_(n) + M_(n) = \mathcal(n) + \mathcal(n) = \mathcal(n). \]

先の関数 one_norm_recur は、リスト分割して再帰呼び出ししている様子の1行の表現が長くなって見づらい。 そこで次のように、リストを左右に分割してそれぞれを left_list と right_list に代入してコードを分かりやすくした関数を one_norm_recur2 としてみた。 しかしながら、以下の計算から明らかになるように、オーダーが上がったメモリ計算量を必要とするようになる。結果、$n$が大きくなると無視できない影響を及ぼしてしまう。

この場合、5行目と6行目のリスト(長さは約半分の $n/2$) left_list と right_list のために必要なメモリが発生するため $M_(n)$ は次のようになる。 \[ M_(n) = c + d n + 2M_(n/2) \]

したがって、 \begin M_(n) &= c + dn+ 2M_(n/2)\\ &= c + dn + 2(c + dn/2 + 2M_(n/2^2))= c + 2c + 2dn + 2^2M_(n/2^2)\\ フィボナッチ数列の計算量について フィボナッチ数列の計算量について & \vdots\\ &= c + 2c + 2^2 c+ 2^3 c+ \dots + \fracc + dkn + nM_(1)\\ &= フィボナッチ数列の計算量について c\sum_^ 2^i +d (n\log n) + en\\ &= cn - c +d(n\log n) + en\\ &=\mathcal(n\log n) \end つまり、関数 one_norm_recur2 フィボナッチ数列の計算量について のメモリ計算量 $M_(n)$ は次のようになる。 \[ M_(n) = M_(n) + M_(n) = \mathcal(n) + \mathcal(n\log n) = フィボナッチ数列の計算量について フィボナッチ数列の計算量について \mathcal(n\log n). \] 同じアルゴリズムでありながら、再帰計算の場合にはメモリ計算量のオーダーが $\mathcal(n)$ から $\mathcal(n\log n)$ へと上がってしまうことがわかった。

Fibonacci数の計算

再帰呼び出すしを使ってn番目のFibonacci数を返す杉の関数 fibonacci_recur の計算量評価を考える。

二項係数 $\binom$ は \[ \binom = \frac \] で定義される。 次の二項係数を計算するスクリプトは、明らかに推奨できない。

演習: 上のfor文を使う階乗計算に必要な時間およびメモリ計算量を評価しなさい。 階乗計算にfor文をつかった繰り返し文をつかって二項係数の定義そのままに計算する方法と、上の再帰的方法による計算計算量を比較してみなさい。 どちらの方法でも計算の不安定性は依然として残っているのだが、計算時間には優位な差があることを確認しなさい。

では、次のように二項係数で成立する漸化式 \[ \binom = \binom + \binom \] を使って再帰的によって計算するのはどうだろうか。 この場合、$k\approx n/2$ 程度で再帰呼び出しの深さが最大になることに注意しよう。

この関係を使うと次の二項係数の再帰的定義を書くことができる。 ただし、n と k の掛け算の位置には配慮が必要である。

演習: 以上で、都合3つの二項係数 フィボナッチ数列の計算量について フィボナッチ数列の計算量について $\binom$ の計算方法を考えた(forを使う定義そのものと、2つの再帰的定義)。 これら3つの計算量の評価をまとめ、さらに$n=1,2,3,\dots$ , $k=\approx n/2$ についての計算時間を比較する実験を行いなさい。

Pythonではじめるアルゴリズム入門 伝統的なアルゴリズムで学ぶ定石と計算量

【扱うアルゴリズム】
FizzBuzz|フィボナッチ数列|線形探索|二分探索|幅優先探索|
深さ優先探索|番兵|8クイーン問題|n-クイーン問題|ハノイの塔|
ミニマックス法|選択ソート|挿入ソート|バブルソート|ヒープソート|
マージソート|クイックソート|最短経路問題|ベルマン・フォード法|
ダイクストラ法|A*アルゴリズム|文字列探索の力任せ法|Boyer-Moore法|
逆ポーランド記法|ユークリッドの互除法

【章構成】
■第1章 Pythonの基本とデータ構造を知る
■第2章 基本的なプログラムを作ってみる
■第3章 計算量について学ぶ フィボナッチ数列の計算量について フィボナッチ数列の計算量について フィボナッチ数列の計算量について
■第4章 いろいろな探索方法を学ぶ
■第5章 データの並べ替えにかかる時間を比べる
■第6章 実務に役立つアルゴリズムを知る
■Appendix Pythonのインストール

第1章 Pythonの基本とデータ構造を知る
1.1 プログラミング言語の選択
1.2 プログラミング言語Pythonの概要
1.3 四則演算と優先順位
1.4 変数と代入、リスト、タプル
1.フィボナッチ数列の計算量について 5 文字と文字列
1.6 条件分岐と繰り返し
1.7 リスト内包表記
1.8 関数とクラス
理解度Check!

第2章 基本的なプログラムを作ってみる
2.1 フローチャートを描く
2.2 FizzBuzzを実装する
2.3 自動販売機でお釣りを計算する フィボナッチ数列の計算量について
2.4 基数を変換する
2.5 素数を判定する
2.6 フィボナッチ数列を作る
理解度Check!

第3章 計算量について学ぶ
3.1 計算コストと実行時間、時間計算量
3.2 データ構造による計算量の違い
3.3 アルゴリズムの計算量と問題の計算量
理解度Check!

第4章 いろいろな探索方法を学ぶ フィボナッチ数列の計算量について フィボナッチ数列の計算量について
4.1 線形探索
4.2 二分探索
4.3 木構造での探索
4.4 さまざまな例を実装する
理解度Check!

第5章 データの並べ替えにかかる時間を比べる
5.1 身近な場面でも使われる「並べ替え」とは?
5.2 選択ソート
5.フィボナッチ数列の計算量について 3 挿入ソート
5.4 バブルソート
5.5 ヒープソート
5.6 マージソート
5.7 クイックソート
5.8 処理速度を比較する
理解度Check!

第6章 実務に役立つアルゴリズムを知る
6.1 最短経路問題とは? フィボナッチ数列の計算量について フィボナッチ数列の計算量について
6.2 ベルマン・フォード法
6.3 ダイクストラ法
6.4 A*アルゴリズム
6.5 文字列探索の力任せ法
6.6 Boyer-Moore法
6.7 逆ポーランド記法
6.8 ユークリッドの互除法
理解度Check!

付録A フィボナッチ数列の計算量について Pythonのインストール
A.1 Pythonの処理系を知る
A.2 AnacondaでPythonをインストールする
A.3 複数のバージョンのPythonを切り替える
A.4 パッケージのインストールと削除
A.5 インストールがエラーになった場合

1.2 手続きとプロセス

この計算が展開して作るパターンを考えよう.このプロセスで(fib 5)を計算したとした場合のプロセスの発展の形は以下のようである. この木のような(正確には2分木)パターンになるのは手続きfibが1回ごとにfib自身を2回呼ぶことによる.またこのパターンを見ていると非常に冗長な計算が行われていることに気づく.例えばfib フィボナッチ数列の計算量について 3が2回行われていることである.fib 3は以下の木全体を作り上げる仕事の約半分にまで達してしまう.

この木の末端(葉の部分,fib 0あるいはfib 1)の数を数えるとそれはFib(n+1)に等しいことがわかる(なぜか? * ).

この計算量の増大が如何に悪い状況にあるかを確認するには単純にFib(n)の値の増加が指数関数的であることを見ればわかる.実際にFib(n)はφ n /√5にもっとも近い整数をとる.ここで

  • 階層的データ構造では木構造の再帰プロセスが有効である
  • 計算問題でもfibで見たように再帰的に考えれば理解は早く,プログラムもすぐに記述できる

両替の組み合せ数(Count Change)

  • n種類の枠内のなかで最高値のコイン(これを最初のコインと呼ぶ)以外のすべてのコインでaを両替する方法の数(a, n-1) +
  • n種類すべてのコインを使い,a-dを両替させる方法の数(a-d, n)(ここでdは最初のコインの金額)
    フィボナッチ数列の計算量について
  1. もしa=0なら,両替方法は1(これはaをaのみで両替する場合に対応、上の第2項目に出てくる;d=aのときに対応している)
  2. もしa
  3. もしn=0なら,両替方法は0
  4. もしa>0なら,両替方法は(a, n-1)の両替方法と(a-d, n)の両替方法の和(ここに再帰的呼び出しが登場)

下線の引いてあるところは上の定義より0.amount=0で1となるところは2箇所.これより(cc 5 2)は1+1の2種類となる.つまり5円を1枚の5円玉か5枚の1円玉の2つの両替方法である.

このccを自作してみよ.そのためには日本の硬貨システムを体系化させる関数を作らなければならない.つまり上の箇条書き項目4で(cc a n)=(cc a n-1)+(cc (- a d) n)を計算するのだがdはn(プログラムではncoins)による.しかし一義的に決まる関数である;

  • n=6ではd=500円
  • n=5ではd=100円
  • n=4ではd=50円
  • n=3ではd=10円
  • n=2ではd=5円
  • n=1ではd=1円

この関数を(define (first-coin ncoins) …)で作成しよう.分岐が多いがncoinsの数のみなので丹念にcondを使って場合わけする.

続いて(define (cc amount ncoins) …)を作る.上の箇条書きをそのままschemeに翻訳する.ただし2と3はともに戻り値0なので論理演算子orを使おう.さらに条件4はそれら以外ではという意味でelseを使おう.また4の第2項目のdは(first-coin ncoins)フィボナッチ数列の計算量について を使う.

ハノイの搭

Tower of Hanoi

  1. n-1枚の円盤をaからbに動かす
  2. 一番下の1枚をaからcに動かす
  3. n-1枚の円盤をbからcに動かす

Exercise 1.11

Exercise 1.12

Exercise 1.13

Fib(n)がφ n /√5にもっとも近い整数をとることを証明せよ.φ=(1+√5)/2である.ヒントψ=(1-√5)/2とする.フィボナッチ数列の定義と帰納法によりFib(n)=(φ n -ψ n )/√5であることを証明せよ.

1.2.3 計算量見積りの尺度

  • n:問題の大きさを測るパラメータ
  • R(n):サイズnの問題で使用するリソースの量

R (n)が計算量Θ(f (n))フィボナッチ数列の計算量について を持つという時,R(n)=Θ(f(n))と書く.そしてこれはk1とk2というnに独立な正の整数を持ってきてこれらが以下の条件を満足していることを意味することにする.

  • 1.2.1で議論した線形再帰法による階乗計算のステップ数,メモリ使用量はともにnに比例する:Θ(n)
  • 反復法の計算ではステップ数はΘ(n)だがメモリ使用量はΘ(1)だ.これはnによらない定数である.

このように定義された計算量は粗い見積りしかできない.Θ(1000n 2 )もΘ(3n 2 )もまとめてΘ(フィボナッチ数列の計算量について n 2 )である.しかし一方で計算量を知れば,例えばnを2倍にしたらΘ(n)の問題ではリソースを2倍使うだろう,指数関数的Θ(α n )であればnを1つ増やすごとにリソースを定数倍(α)しなければならないなどと知ることができる.

Exercise 1.14

1.2.4 ベキ乗

Exercise 1.14.1

はヒント1を参考にしてください. countを1減じているのでproductはどうすべきかを考えたらわかると思います.

ステップ数が対数的に増加するフィボナッチ数列の計算する方法がある.1.2.2のfibo-iterプロセスで状態変数a,bの変換a←a+b,b←aを思い出してみよう.この変換をTと呼ぶ.Tをa=1,b=0から初めてなんども繰り返すと、a=Fib(n+1),b=Fib(n)となるのだった.これはフィボナッチ数は(1,0)をT n (Tのn乗)回繰り返すことにより得られること.

Tpqの自乗の方法を上記のように定義しT n をfast-myexptのときのように連続自乗で計算することができる.そしてそのステップ数増加は対数的となる.以下の空白を埋めて完成させろ.

1.2.5 最大公約数

2整数a,bの最大公約数(GCD:greatest フィボナッチ数列の計算量について フィボナッチ数列の計算量について フィボナッチ数列の計算量について Common Divisor)とはどちらの数を割っても余りが0である最大整数のこと.例えば16と28のGCDは4である.

GCD(24,9) = GCD(9,6) = GCD(6,3) = GCD(3,0) = 3.

ラメ(Lamé)の定理

1.2.6 素数判定

除数(割る数)を探す

フェルマーテスト

フェルマー(Fermat)の小定理

今2整数a,bがありこれらのもう一つの整数mで割ったときの剰余が等しい時,a,フィボナッチ数列の計算量について bはmを法(modulus)として合同である(congruent)であるといい,a ≡ b (mod m)と書く.今nが素数であれば,aをnより小の整数として、a n ≡ a (mod n)が成り立つ.これをフェルマーの小定理という.証明略).

  1. 素数判定したい整数をnとして,a
  2. a n をnで割った余りをもとめる.
  3. もしこの余りがaでなければnは素数ではないだろう,またそれがaなら素数である確率は高い.
  4. さらに別の乱数a
  5. この繰り返しを多数回重ねれば素数判定の信頼度は増す.

これはある数baseをexp乗してそれをmで割り,余りを求めるもの.つまりbase exp フィボナッチ数列の計算量について フィボナッチ数列の計算量について modulo mを求める.1.2.4のfast-myexptに似ていて,連続自乗法を使っている.乗数とともにステップ数は対数的に増加する.

フェルマーテストは乱数aを1≦a≦nのもとで発生させてa n modulo nの値がaかどうかチェックする.以下のプログラムでは手続きrandomを使う.これは0(含む)と引数として与えられた数(含まず)で乱数を発生させるもの,つまり(0≦random値

  • USB scheme(この教室で利用)では乱数利用時に1回(require 'random)というコマンドを発行させます.
  • 以下のプログラムの(require 'random)はその利用例です.その他のscheme実装(例えばパソコンでよく使われるGauche-schemeとか)ではそれぞれの独自の乱数の利用法があると思われるのでそれに従ってください.schemeでは乱数利用の標準的な使い方の指定はないようです.
  • UMB schemeでは前述したように(require 'random)は1回のScheme実行で1度だけの実行でよいです.つまり以下のプログラム例のように(require 'フィボナッチ数列の計算量について フィボナッチ数列の計算量について フィボナッチ数列の計算量について random)をファイルのなかに組み込む必要はなくschemeを始めたらこのコマンドを1回発行すれば終わるまでずっとrandom関数の利用ができるというものです.
  • なお乱数は(random n)として呼び出すと(ここでnは整数)0からn-1までの整数を出現確率一様で戻します.(random x)では(xは実数)0.0 ≦R < xなるRを戻します.Rの出現確率もその範囲で一様となります.

WS教室での(require 'random) 利用上の注意

  • Linuxでは(つまりhatoxxを使っている限りschemeで(require 'random)は利用できません.
  • そこでUnix(IBM AIX)マシーンに移行する必要があります.以下の手順でそれらの機材にリモートログインします:
    1. >> フィボナッチ数列の計算量について ssh taka01(01は02でも03でも04でもよい、あるいは自習用サーバmozuとしてもよい)
    2. パスワードの入力(Linuxと同じパスワード)
    3. ファイルシステムはLinuxとUnixは共用なので環境はなにもかわらず頭のみが入れ替わったことになる
    4. 作業が終了したらプロンプトの後にexitと入力.Linuxの自分の本来の機材に戻ってくる(プロンプトで確認できる).

確率的方法

でもこれはちょっと違っている.非常にまれではあるが素数でなくてもa n がaと法nで合同ではない整数nがすべてのa

この変更を実装するために手続きnextを定義せよ.これは最初が2,次に3,そしてその後は+2増やしていく.smallest-divisorを変更して(+ test-divisor 1)を(next test-divisor)にする.そしてこれを用いてExercise 1.22のsearch-for-primesを同じ条件で実行させる.実行時間の変化を観測せよ.テスト数が半減したので多分時間も1/2になったはずである.そうなっただろうか?そうでない場合,減少レートはどのくらいか?なぜそうなのか説明せよ.

失敗しない変形フェルマーテストがミラーとラビンにより提唱されている.Miller-Rabin testと呼ばれる.これはフェルマーの小定理の変形を使う.それはa n-1 ≡ 1 (mod フィボナッチ数列の計算量について n) である.a n-1 のnで割った剰余はexpmodを使って計算する.しかしこの方法ではexpmodで自乗(square)計算中,常に「自明でない(nontrivialな)1 modulo nの平方根」があったかどうか調べなければならない.その数字とは自乗すると1 でもないしn-1でもないものである.もしこのような「自明でない(nontrivialな)1 modulo nの平方根」が見つかった場合,逆にnは素数でないと判断できる.nが奇数で素数でない場合,a

計算量の評価

$f(n)$ と $g(n)$ が $\mathbb$ 上の関数であるとき、 \[ f(n)= フィボナッチ数列の計算量について フィボナッチ数列の計算量について \mathcal(g(n)) \qquad \text \] であるとは、正の実数 $c_1, c_2$ および $n_0$ が存在して、$n_0\leqq n$ なる $n$ に対して \[ c_1\lvert g(n) \rvert \leqq \lvert f(n) \rvert \leqq c_2\lvert g(n) \rvert \qquad \text \] であるときをいう。 このとき、$f(n)$ は $g(n)$ の オーダー (order)であるといい、big $\mathcal$ を使って、$f(n)= \mathcal(g(n))$ あるいは $f(n) \sim \mathcal(g(n))$ などと書く。 この 記法は $n$ が十分大きくなったときの関数 フィボナッチ数列の計算量について フィボナッチ数列の計算量について $f(n)$ の挙動を表していることに注意する。

$f_1(n)= \mathcal(g_1(n))$ および $f_2(n)= \mathcal(g_2(n))$ が正値関数であるとき、定義から次が成立する。 \begin & f_1(フィボナッチ数列の計算量について フィボナッチ数列の計算量について n)f_2(n)= \mathcal(g_1(n)g_2(n))\\ & f_1(n) + f_2(n)= \mathcal(g_1(n) + g_2(フィボナッチ数列の計算量について n)). \end

たとえば、次の多項式 $p(n)$ を考えてみる。 \[ p(n)= a_k n^k + a_ n^+\dots +a_2 n^2 + a_1 n + a_0 フィボナッチ数列の計算量について \] $n>1$の時には、自明な関係 \[ 1\leqq n\leqq n^2 \leqq \dots \leqq n^\leqq n^k \] から直ちに得られる関係 \begin a_k n^k \leqq p(n) & \leqq a_k n^k + a_ n^k + \dots + a_2 n^k + a_1 n^k + a_0 n^k\\ & \leqq n^k (a_k + a_ フィボナッチ数列の計算量について + \dots + a_1 + a_0) \end は、$p(n)=\mathcal(n^k)$ であることを示している。

これより、多項式については $n$ の最大次数に着目して \begin p_0(n) & 5000000000=\mathcal(1) p_1(n) &= 20000 n + 3000000=\mathcal(n)\\ p_2(n) &= 0.000001 n^2 -31412 n - 2600 = \mathcal(n^2)\\ p_3(n) &= 300 n^2 + 600 n + 2500 = \mathcal(n^2) \end となり、$p_2(n)$ と $p_3(n)$ は同じ $n^2$ のオーダーで $n$ が十分に大きくなると同じ程度に増加し、$n$のオーダーの $p_1(n)$ を圧倒し、$p_1(n)$ は $p_0(n)$ を圧倒することがわかる(この場合、関数の$\log$-$\log$グラフを書いてみるとこの事実は分かりやすい)。

アルゴリズムの計算量オーダーを低くすることは、大きなデータサイス $n$ に対しては劇的な効果があることになる。 あるいは、指数的オーダーを持つ計算量アルゴリムは大きなデータサイス $n$ に対して 手に負えない (intractable)ことになる。

big $\mathcal$-記法:小さいオーダー順に
order名前
$\mathcal(1)$定数
$\mathcal(\log n)$対数的
$\mathcal(n)$線形的
$\mathcal(n\log n)$対数線形的
$\mathcal(n^2)$2乗的
$\mathcal(n^3)フィボナッチ数列の計算量について $3乗的
$\mathcal(2^n)$指数的
$\mathcal(n!)$階乗的

階乗的オーダーが指数的オーダーよりも大きいのは Stirling's の近似公式からわかる。 \[ n! \sim \sqrt <2\pi n>n^n \mathrm^ \] 比 $n! /2^n$ の対数をとると \[ \log \left(\frac \right) \sim \left( n+\frac12\right) n -(1+\log 2)n +\text \] から、$n\rightarrow \infty$につれて比はいくらでも大きくなるからである。

代表的なアルゴリズムの計算量

ベクトルの内積

$L_1$ノルムの問題サイズはベクトルの長さ $n$ である。

$L_1$ノルム計算の再帰版 one_norm_recur の時間計算量を評価する。 フィボナッチ数列の計算量について フィボナッチ数列の計算量について フィボナッチ数列の計算量について 簡単のためにベクトルの長さ $n$ を $n=2^k$ と仮定しても一般性を失わない($k=\log n$)。 10行目の判断で時間 $T_$と15行目の足し算計算に時間 $T_$を費やすのであるが、13行目と14行目で半分の長さの$L_1$ノルム計算をしていることを考慮すると \[ T_(2^k) = T_ + T_ + 2T_(2^) \] の関係、つまり $c= T_ + T_$ を定数とすると \[ T_(n)= c + 2T_(n/2) \] が成立する。 このことから、次のように書くことができる。 \begin T_(n) &= c + 2T_(n/2)\\ &= c + 2(c + 2 T_(n/2^2))= c + 2c + 2^2 T_(n/2^2)\\ &= c + 2c + 2^2(c + 2T_(n/2^3))=c + 2c + 2^2c + 2^3T_(n/2^3)\\ & \vdots\\ &= c フィボナッチ数列の計算量について + 2c + 2^2 c+ 2^3 c+ \dots + 2^c + 2^T_(2)\\ &= c\sum_^ 2^i + n(T_ + T_)\\ &= \fracc + dn \qquad d = T_ + T_\\ &=(c+d)n -c=\mathcal(n) \end

メモリ計算量を考えよう。 $L_1$ノルムを計算するために長さ $n$ のベクトルを保持するための入力メモリ量 $M_(n)フィボナッチ数列の計算量について $ とは区別して、関数内で再帰呼び出しするために必要なメモリを考える必要がある。 サイズ $n$ の計算に再帰呼び出しで必要なメモリ$M_(n)$は \[ M_(n) = c + M_(n/2) \] である。$c$ には left_norm や right_norm (など)に必要な固定領域である。 フィボナッチ数列の計算量について この漸化式は上の時間計算量と同様に評価でき、$M_(n)=\mathcal(n)$ である。

入力メモリ量 $M_(n)$ は明らかに $=\mathcal(n)$ であることから、関数 one_norm_recur のメモリ計算量 $M_(n)$ は次のようになる。 \[ M_(フィボナッチ数列の計算量について フィボナッチ数列の計算量について フィボナッチ数列の計算量について n) = M_(n) + M_(n) = \mathcal(n) + \mathcal(n) = \mathcal(n). \]

先の関数 one_norm_recur は、リスト分割して再帰呼び出ししている様子の1行の表現が長くなって見づらい。 そこで次のように、リストを左右に分割してそれぞれを left_list と right_list に代入してコードを分かりやすくした関数を one_norm_recur2 としてみた。 しかしながら、以下の計算から明らかになるように、オーダーが上がったメモリ計算量を必要とするようになる。結果、$n$が大きくなると無視できない影響を及ぼしてしまう。

この場合、5行目と6行目のリスト(長さは約半分の $n/2$) left_list と right_list のために必要なメモリが発生するため $M_(n)$ は次のようになる。 \[ M_(n) = c + d フィボナッチ数列の計算量について n + 2M_(n/2) \]

したがって、 \begin M_(n) &= c + dn+ 2M_(n/2)\\ &= c + dn + 2(c + dn/2 + 2M_(n/2^2))= c + 2c + 2dn + 2^2M_(n/2^2)\\ & \vdots\\ &= c + 2c + 2^2 c+ 2^3 c+ \dots + \fracc + dkn + nM_(1)\\ &= c\sum_^ 2^i +d (n\log n) + en\\ &= フィボナッチ数列の計算量について フィボナッチ数列の計算量について フィボナッチ数列の計算量について cn - c +d(n\log n) + en\\ &=\mathcal(n\log n) \end つまり、関数 one_norm_recur2 のメモリ計算量 $M_(n)$ は次のようになる。 \[ M_(n) = M_(n) + M_(n) = \mathcal(n) + \mathcal(n\log n) = \mathcal(n\log n). \] 同じアルゴリズムでありながら、再帰計算の場合にはメモリ計算量のオーダーが $\mathcal(n)$ フィボナッチ数列の計算量について フィボナッチ数列の計算量について から $\mathcal(n\log n)$ へと上がってしまうことがわかった。

Fibonacci数の計算

再帰呼び出すしを使ってn番目のFibonacci数を返す杉の関数 fibonacci_recur の計算量評価を考える。

二項係数 $\binom$ は \[ \binom = \frac \] で定義される。 次の二項係数を計算するスクリプトは、明らかに推奨できない。

演習: 上のfor文を使う階乗計算に必要な時間およびメモリ計算量を評価しなさい。 階乗計算にfor文をつかった繰り返し文をつかって二項係数の定義そのままに計算する方法と、上の再帰的方法による計算計算量を比較してみなさい。 どちらの方法でも計算の不安定性は依然として残っているのだが、計算時間には優位な差があることを確認しなさい。

では、次のように二項係数で成立する漸化式 \[ \binom = \binom + \binom \] を使って再帰的によって計算するのはどうだろうか。 この場合、$k\approx n/2$ 程度で再帰呼び出しの深さが最大になることに注意しよう。

この関係を使うと次の二項係数の再帰的定義を書くことができる。 ただし、n と k の掛け算の位置には配慮が必要である。

演習: 以上で、都合3つの二項係数 $\binom$ の計算方法を考えた(forを使う定義そのものと、2つの再帰的定義)。 これら3つの計算量の評価をまとめ、さらに$n=1,2,3,\dots$ , $k=\approx n/2$ についての計算時間を比較する実験を行いなさい。

関連記事

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次
閉じる