在OI簡單數論中 斐波拉契是常常出現的東西
|是什麼
斐波那契數列,又稱黃金分割數列、因數學家列昂納多·斐波那契以兔子繁殖爲例子而引入,故又稱爲“兔子數列”,指的是這樣一個數列:1、1、2、3、5、8、13、21、34、……在數學上,斐波納契數列以如下被以遞歸的方法定義:F(0)=1,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)在現代物理、準晶體結構、化學等領域,斐波納契數列都有直接的應用.
(百度百科)
|模板
1,遞歸
inline int Fib(int n)
{
if (n == 1 || n == 2)
{
return 1;
}
return Fib(n - 1) + Fib(n - 2);
}
複雜度分析
由於對於每一個1都是最後一層遞歸返回上來的故會遞歸F(n)次 , 由於斐波拉契數列是隨着指數上升的 故複雜度約爲O(2^n)
2,循環
inline int Fib(int n)
{
F[1] = F[2] = 1;
for(int i = 3 ; i <= n ; ++ i)
F[i] = F[i - 1] + F[i - 2];
return F[n];
}
複雜度分析 :O(n)
3,矩陣乘法優化
講數列放在矩陣乘法中會發現
即如果求F[n]
將
拓展到2 * 2即
#include <iostream>
#include <cstdio>
#include <cstring>
#define inc(i) (++ i)
#define int long long
using namespace std;
const int Mod = 1000000000 + 7;
struct JX
{
int a[3][3];
friend JX operator * (JX a , JX b)
{
JX c;
memset(c.a , 0 , sizeof(c.a));
for(int i = 1 ; i <= 2 ; inc(i))
for(int j = 1 ; j <= 2 ; inc(j))
for(int k = 1 ; k <= 2 ; inc(k))
c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % Mod;
return c;
}
}Ans , A;
int n;
inline void KSM()
{
int p = n - 2;
while(p)
{
if(p & 1) Ans = Ans * A;
p >>= 1 , A = A * A;
}
}
signed main()
{
scanf("%ld" , &n);
if(n <= 2) putchar(49);
else
{
Ans.a[1][1] = A.a[1][1] = 1;//直接跳到n = 2時的矩陣
Ans.a[1][2] = A.a[1][2] = 1;
Ans.a[2][1] = A.a[2][1] = 1;
KSM();
printf("%lld" , Ans.a[1][1]);
}
return 0;
}
4, 算不上什麼優化的優化(斐波拉契數列膜p的循環節)
轉自Fib數模n的循環節(代碼原創)
我們知道Fibonacci數列,現在我們來求一個Fib數模n的循環節的長度。
對於一個正整數n,我們求Fib數模n的循環節的長度的方法如下:
(1)把n素因子分解,即
(2)分別計算Fib數模每個的循環節長度,假設長度分別是
(3)那麼Fib模n的循環節長度
從上面三個步驟看來,貌似最困難的是第二步,那麼我們如何求Fib模的循環節長度呢?
這裏有一個優美的定理:
Fib數模的最小循環節長度等於,其中表示Fib數模素數的最小循環節長度。可以看出我們現在最重要的就是求
對於求我們利用如下定理:
如果5是模的二次剩餘,那麼循環節的的長度是的因子,否則,循環節的長度是的因子。
順便說一句,對於小於等於5的素數,我們直接特殊判斷,loop(2)=3,loop(3)=8,loop(5)=20。
那麼我們可以先求出所有的因子,然後用矩陣快速冪來一個一個判斷,這樣時間複雜度不會很大。
模板代碼:(巨醜 見諒)
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
#define inc(i) (++ i)
#define dec(i) (-- i)
using namespace std;
const int N = 100000 + 7;
int n , Prime[N + 7] , tot , Pri[N] , Cnt[N] , cnt , TOT;
int Factor[N] , P;
bool No_Prime[N + 7];
struct JX
{
int a[4][4];
friend JX operator * (JX a , JX b)
{
JX c;
memset(c.a , 0 , sizeof(c.a));
for(int i = 1 ; i <= 2 ; inc(i))
for(int j = 1 ; j <= 2 ; inc(j))
for(int k = 1 ; k <= 2 ; inc(k))
c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j] % P) % P;
return c;
}
}A , X;
inline JX KSM_2(JX a , int q , int P)
{//矩陣優化斐波拉契數列
JX Ans = X , x = a;
while(q)
{
if(q & 1) Ans = Ans * x;
q >>= 1 , x = x * x;
}
return Ans;
}
inline void Get_Prime()
{//得出質數便於之後分解質因數
No_Prime[1] = 1;
for(int i = 2 ; i <= N ; inc(i))
{
if(!No_Prime[i]) Prime[inc(tot)] = i;
for(int j = 1 ; j <= tot && i * Prime[j] <= N ; inc(j))
{
No_Prime[i * Prime[j]] = 1;
if(i % Prime[j] == 0) break;
}
}
}
inline void Resolve(int n , int Pri[] , int Cnt[])
{//分解質因數 , pri存質因子 , cnt存次數
cnt = 1;
int t = sqrt(n) , TOT;
for(int i = 1 ; Prime[i] <= t ; inc(i))
if(n % Prime[i] == 0)
{
TOT = 0;
Pri[cnt] = Prime[i];
while(n % Prime[i] == 0)
{
n /= Prime[i];
inc(TOT);
}
Cnt[cnt] = TOT;
inc(cnt);
}
if(n ^ 1)
{
Pri[cnt] = n , Cnt[cnt] = 1;
inc(cnt);
}
dec(cnt);
}
inline int KSM_1(int x , int q , int P)
{//普通的快速冪
int Ans = 1;
x %= P;
while(q)
{
if(q & 1) Ans = Ans * x % P;
q >>= 1 , x = x * x % P;
}
return Ans;
}
inline void Work(int n)
{//求n的因數
TOT = 0;
int t = sqrt(n);
for(int i = 1 ; i <= t ; inc(i))
{
if(n % i == 0)
{
if(i * i == n) Factor[inc(TOT)] = i;
else
{
Factor[inc(TOT)] = i;
Factor[inc(TOT)] = n / i;
}
}
}
}
inline int Gcd(int a , int b)
{
return a % b == 0 ? b : Gcd(b , a % b);
}
inline int Find_Loop(int n)
{
Resolve(n , Pri , Cnt);
int Ans = 1 , loop;
for(int i = 1 ; i <= cnt ; inc(i))
{
loop = 1;
P = Pri[i];
switch(P)
{
case 2: loop = 3; break;
case 3: loop = 8; break;
case 5: loop = 20; break;
default :
{
if(KSM_1(5 , (P - 1) >> 1 , P) == 1)
Work(P - 1);
else
Work(2 * (P + 1));
sort(Factor , Factor + TOT + 1);
for(int j = 1 ; j <= TOT ; inc(j))
{
JX B = KSM_2(A , Factor[j] - 1 , P);
int x = (B.a[1][1] + B.a[1][2]) % P;
int y = (B.a[2][1] + B.a[2][2]) % P;
if(x == 1 && y == 0)
{
loop = Factor[j];
break;
}
}
}break;
}
loop *= KSM_1(P , Cnt[i] - 1 , 9223372036854775807);
if(loop < Ans) swap(loop , Ans);
Ans = Ans / Gcd(loop , Ans) * loop;
}
return Ans;
}
inline int Mod(char* a , int b)//高精度a除以低精度b
{
int d = 0 , l = strlen(a);
for(int i = 0 ; i < l ; inc(i)) d = (d * 10 + (a[i] - '0')) % b;//求出餘數
return d;
}
char pp[30000007];
signed main()
{
A.a[1][1] = A.a[1][2] = A.a[2][1] = 1;
X.a[1][1] = X.a[2][2] = 1;
Get_Prime();
scanf("%s%lld" , pp , &n);
int loop = Find_Loop(n);
int Ans = Mod(pp , loop);
if(!Ans) Ans = loop;
if(Ans <= 0) putchar(48);
else
if(Ans <= 2) printf("%lld" , 1ll % n);
else
{
A.a[1][1] = A.a[1][2] = A.a[2][1] = 1;
X.a[1][1] = X.a[2][2] = 1;
P = n;
X = KSM_2(A , Ans - 1 , n);
printf("%lld" , X.a[1][1]);
}
return 0;
}