切換
舊版
前往
大廳
主題

ZeroJudge - e299: PF. Fibnocci of Fibnocci(費⽒數列的費⽒數列) 解題心得

Not In My Back Yard | 2019-08-10 22:45:57 | 巴幣 0 | 人氣 361

題目連結:


題目大意:
設第 x 項的費氏數列之值為 fib(x) 。

給定一正整數 n (1 ≦ n ≦ 10 ^ 5),代表有 n 筆測試資料。每筆測試資料佔一列,每列給定一正整數 x ,求 fib(fib(x)) 除以 M = 1000000007 的餘數。

註:存在一 M' 值(M' ≦ 10 × M),使得 fib(fib(x) mod M') mod M ≡ fib(fib(x)) mod M ,其中「mod」代表取餘數。



範例輸入:
6
1
1
2
3
5
8


範例輸出:
1
1
1
1
5
10946


解題思維:
我們需要找到滿足 fib(x) ≡ fib(x + kM') 且 fib(x + 1) ≡ fib(x + kM' + 1) (mod M')的 M' 值,才能使  fib(X mod M') mod M ≡ fib(X) mod M ,其中 X 即是題目的 fib(x) 值。

根據費氏數列的第 0 項 = 0 以及第 1 項 = 1 可以知道此 M' 值是費氏數列模 M 下的皮薩諾週期(Pisano Period)之長度。

設 X = t + kM' ,而 fib(t) 與 fib(t + kM') 正好差了模 M 下的 k 個週期。因此 fib(t) 與 fib(t + kM') 同餘。代入原本的 X 值即可得到 fib(fib(x) mod M') ≡ fib(fib(x)) (mod M)。

而皮薩諾週期的可由單純的迭代費氏數列而得(要 mod M)。第 0 項 = 0 ,第 1 項 = 1 ,迭代一次得第 1 項 、 第 2 項 ,迭代第二次得第 2 項、第 3 項……迭代到當第 M' 項 = 0 、第 M' + 1 項 = 1 為止。此時的 M' 即是模 M 下的皮薩諾週期。

而用程式跑一下可得 M = 1000000007 的皮薩諾週期之長度為 M' = 2000000016 。

接著先用矩陣快速冪(如此題的作法)計算出 mod M' 後的 fib(x) 。接著再將此值代入一次矩陣快速冪求得最後模 M 下的 fib(fib(x)) 之值。

此次分享到此為止,如有任何更加簡潔的想法或是有說明不清楚之地方,也煩請各位大大撥冗討論。

創作回應

胖胖貓
用程式跑一下可得 M = 1000000007 的皮薩諾週期之長度為 M' = 2000000016 。
弱弱的問一下這句話,理論可行,但實際操作時需要用<map>紀錄循環是否發生了,這樣記憶體耗量不會太大嗎?其次是時間的問題...
2020-05-16 01:08:26
Not In My Back Yard
因為費氏數列稍微特別一點(似乎是因為其為一個 Homogeneous 的數列,所以滿足以下性質(這點因果關係是否正確我不確定)),循環節必定從第 0 項(或是從第 1 項開始)。

所以只需要找到有某相鄰兩項等於最一開始的兩項(第 0 、 1 項,第 1 、 2 項也行)即可,不用紀錄中間出現過的項次。當然,「計算過」幾項這點還是要記錄的,畢竟是要求週期。

時間的話,我記得挺快就算出來的。畢竟沒有用 map 紀錄每一項,時間複雜度的常數並不會太大。因為題目有給定一個定理,表示皮薩諾週期不會大於 10M (這邊的 M 是模數)所以頂多跑 5 個 int 大小左右(約 100 億)的數字,所以就算是最差的情況,其耗時仍可接受。

2020-05-16 01:52:13
Not In My Back Yard
對於那些好奇為何費氏數列模 n 會循環且為何循環開始於第 0 項、第 1 項的人:
循環性:
首先,因為餘數只會有 0 ~ n - 1 這 n 個數字,而費氏數列可以無限地迭代算下去。而遞迴關係式不含任何隨機性,對於同一筆輸入將會產生同樣的輸出。

每兩項的數值可能性 n ^ 2 就好比有 n ^ 2 個籠子,有無限項次就像是有著無限隻鴿子。因此根據鴿籠原理(Pigeonhole Principle),數列中相鄰兩項必定會在某時出現與先前重複的組合。

因此費氏數列模 n 之項次必定循環。



循環開始位置:
誠如前面所述——費氏數列的遞迴關係式對於同一筆輸入將會產生同樣的輸出。

假設其循環節開始於費氏數列第 i 項 Fi 且 i 不等於 0 或 1 並結束於 Fj(i ≦ j)。則此時我們可以看到此時會有兩條路徑可以迭代至 Fi:一條是 F0 到 Fi(循環節前面的項次)、另一條則是 Fi 到 Fj 再回到 Fi。

因為一開始的假設(i ≠ 0 、 1)使得這兩條路徑將不相同,而這違背了同一筆輸入將會產生同樣的輸出之確定性。

因此我們藉由反證法得到了循環節必定從第 0 項、第 1 項(即數列最前面兩項)開始。



而以上論述實際上不限於費氏數列,任何前幾項次的線性組合、常係數的遞迴關係式之數列也是可行的。例如佩爾數(Pell Number):Pn = 2Pn-1 + Pn-2 等等。
2021-08-29 23:32:51
Not In My Back Yard
題外話:一年過去了,回來看到這個命題才發現其實蠻好證明的。可惜當初沒有立刻想到XD
2021-08-29 23:33:57

相關創作

更多創作