斐波拉契

在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[n1]F[n2]][1110]=[F[n]F[n1]]

即如果求F[n]

[F[n]F[n1]]=[F[n1]F[n2]][1110]=[F[n2]F[n3]][1110]2=....=[F[1]F[2]][1110]n1

[F[1]F[2]]

拓展到2 * 2即
[1001]
#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章