狠狠撸

狠狠撸Share a Scribd company logo
Information
Science
Fibonacci Sequence is Magic

2014/01/28 tozangezan
はじめに
? シケプリではありません(特に「プリ」が
違う)
? コラムが延々と続く感じです
? Ruby書きたくないのでまともなソース
コードは載せません
? 今回は漸化式で表される数列の計算をし
ます。
扱う漸化式
? 以下の式を扱います

? このうちのkと求める項の番号nが与えら
れるので、anを答える方法を考えます。
? k=2としたものがFibonacci数列です。
? やり方はたくさんあります。
?

(以下、漸化式の係数をいじればすこし一般化できます)
関数の再帰を使う
F(n){
if n < K then 1
else
F(n-1)+F(n-2)+…+F(n-k)
}
(注)どの言語でもないので実行できません

? 再帰関数というのは
定義した関数の中に
同じ関数を呼び出す
機構があるものだと
思っておけばよいと
思います。
? 漸化式をそのまま
コードに書いたらこ
んな感じになります。
遅い!
? よく考えると、さっきのコードは少なく
とも「1を返す回数」だけ関数が呼ばれて
います
? これは答えの数と一致
? 例えば、50番目のフィボナッチ数は
12586269025で、このプログラムだと数分
はかかりそう
? 計算量はだいたい O(αn) αは何かの定数
? (Fibonacci数列なら黄金数とか)
計算量:指数関数
? 基本的に指数関数はダメです

? 参考
http://www.youtube.com/watch?v=Q4gTV4r0
zRs
? 「F(n)は何度も計算したところで毎回値同
じでは…」という発想だけで高速になりま
す。
関数のメモ化再帰をする
F(n){
if “F(n)計算済み”
then “計算済みの値”
if n < K then 1
else
F(n-1)+F(n-2)+…+F(n-k)
}
(注)どの言語でもないので実行できません

? さっきのものに2行書
きたし、無駄に計算
しないようにしまし
た。
? かなり速く動くよう
になります。
? まだ漸化式から想像
がつくでしょう。
メモ化再帰での計算量
? 関数はnの値あたり1回しか呼び出されない
としてよい
? (正確には何回か呼ばれてるだろうけど最初
の1文だけだしどうでもいい)
? nの値によって関数は1回ずつ呼ばれて計
O(n)
? 関数1回ではK個の数の和を計算するので
O(k)
? これら全部まとめるとO(nk)
動的計画法を使う
a[N] # 配列
? 今度は関数を使わず
に、ループをまわす
a*1+=a*2+=…=a*k+=1
だけでかいてみまし
for i in (k+1)..n
た。
a[i]=a[i-1]+a[i-2++…+a*i-k]
? 本当はk個の数の和を
求めるのでもループ
を使うんですがね。
? コードが短め。
動的計画法での計算量
? メモ化再帰と同じ
? n回くらいループをまわしていて、 (O(n))
? それぞれではk個の数の和を計算 (O(k))
? 全部まとめると O(nk)
? やっぱりメモ化再帰と同じ
問題
? 以下の数列を考える

? このうちのkと求める項の番号nが与えら
れるので、anを109+7で割ったあまりを答
えよ。
? k ≦ 100
? n ≦ 100 000
? 解答例: さっきのメモ化再帰,動的計画法
問題
? 以下の数列を考える

? このうちのkと求める項の番号nが与えら
れるので、anを109+7で割ったあまりを答
えよ。
? k ≦ 100
? n ≦ 1 000 000 000 000 000 000
? 解答例: さっきのメモ化再帰,動的計画法
別世界
? ここから先はコード書くの面倒なのでか
きません。
? 情报科学の試験レベルなら別にこのくら
いでゴールして良いんじゃないでしょう
か。
? ここから先はkが大きいのには対処できま
せんが、nが大きいのに対処できる方法で
す。
行列累乗
? k=2のとき、
? k=3のとき、

? 办=4のとき、
行列累乗
? こういう感じの形になるので、anを得るた
めには1を並べたベクトルにn-k回さっきの
ような行列をかければよい。
? 行列のかけ算
? A, A2, A4, A8, A16, A32, …は同じものをかける
処理をすると、全部でO(log n)回で求めら
れる
? ほかのものが得たいときは、例えば
A21= A × A4 × A16 などと2進数っぽくする。
行列累乗での計算量
? ということは、行列のかけ算はO(log n)回
行えばよい。
? ここで使う行列はk次正方行列なので1回
のかけ算はO(k3)かかる (定義に従って素直
に計算するしかない)
? ということで全体での計算量はO(k3 log n)
問題
? 以下の数列を考える

? このうちのkと求める項の番号nが与えら
れるので、anを109+7で割ったあまりを答
えよ。
? k ≦ 100
? n ≦ 1 000 000 000 000 000 000
? 解答例: 行列累乗
問題
? 以下の数列を考える

? このうちのkと求める項の番号nが与えら
れるので、anを109+7で割ったあまりを答
えよ。
? k ≦ 1 000
? n ≦ 1 000 000 000 000 000 000
? 解答例: 行列累乗
整式の割り算を使う方法
? 実は、この問題の第n項の値は、
「xnをxk-xk-1-xk-2-…-1で割ったあまりの係数
の総和」に等しいです。(証明略)

xnをxk-xk-1-xk-2-…-1で割ったあまりは、x[n/2]を
xk-xk-1-xk-2-…-1で割ったあまりを2乗し(nが奇
数ならさらにxをかける)、その式をxk-xk-1-xk2-…-1で割ったあまりを求めればよいです。
整式の割り算を使う方法
? k-1次の多項式二つの乗算はO(k2)でできま
す
? この多項式の乗算はO(log n)回呼ばれます

? ということで全体での計算量はO(k2 log n)
です。
参考問題
? http://tdpc.contest.atcoder.jp/tasks/tdpc_fibo
nacci
? けっこう多くの言語で(Rubyでも)解くこと
ができるっぽいです。
? 解答例
http://tdpc.contest.atcoder.jp/submissions/1193
05 (C++, 48行)
おまけ
? 整式のかけ算は畳み込みなのでFFTで計算
できてO(k log k)になります。
? が、その整式を割ったあまりの計算がど
うやったらO(k2)から落ちるかわかりませ
ん。
? 誰か教えてください
? あと、今回109+7で割ったあまりを求めた
のは答えが大きくなりすぎるからです。
The End

More Related Content

情报科学シケスラ 贵颈产辞苍补肠肠颈