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 <string.h>
#include <algorithm>
#include <stdio.h>
#include <math.h>

using namespace std;
typedef unsigned long long LL;

const int M = 2;

struct Matrix
{
    LL m[M][M];
};

Matrix A;
Matrix I = {1,0,0,1};

Matrix multi(Matrix a,Matrix b,LL MOD)
{
    Matrix c;
    for(int i=0; i<M; i++)
    {
        for(int j=0; j<M; j++)
        {
            c.m[i][j] = 0;
            for(int k=0; k<M; k++)
                c.m[i][j] = (c.m[i][j]%MOD + (a.m[i][k]%MOD)*(b.m[k][j]%MOD)%MOD)%MOD;
            c.m[i][j] %= MOD;
        }
    }
    return c;
}

Matrix power(Matrix a,LL k,LL MOD)
{
    Matrix ans = I,p = a;
    while(k)
    {
        if(k & 1)
        {
            ans = multi(ans,p,MOD);
            k--;
        }
        k >>= 1;
        p = multi(p,p,MOD);
    }
    return ans;
}

LL gcd(LL a,LL b)
{
    return b? gcd(b,a%b):a;
}

const int N = 400005;
const int NN = 5005;

LL num[NN],pri[NN];
LL fac[NN];
int cnt,c;

bool prime[N];
int p[N];
int k;

void isprime()
{
    k = 0;
    memset(prime,true,sizeof(prime));
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            p[k++] = i;
            for(int j=i+i; j<N; j+=i)
                prime[j] = false;
        }
    }
}

LL quick_mod(LL a,LL b,LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % m;
            b--;
        }
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

LL legendre(LL a,LL p)
{
    if(quick_mod(a,(p-1)>>1,p)==1) return 1;
    else                           return -1;
}

void Solve(LL n,LL pri[],LL num[])
{
    cnt = 0;
    LL t = (LL)sqrt(1.0*n);
    for(int i=0; p[i]<=t; i++)
    {
        if(n%p[i]==0)
        {
            int a = 0;
            pri[cnt] = p[i];
            while(n%p[i]==0)
            {
                a++;
                n /= p[i];
            }
            num[cnt] = a;
            cnt++;
        }
    }
    if(n > 1)
    {
        pri[cnt] = n;
        num[cnt] = 1;
        cnt++;
    }
}

void Work(LL n)
{
    c = 0;
    LL t = (LL)sqrt(1.0*n);
    for(int i=1; i<=t; i++)
    {
        if(n % i == 0)
        {
            if(i * i == n) fac[c++] = i;
            else
            {
                fac[c++] = i;
                fac[c++] = n / i;
            }
        }
    }
}

LL find_loop(LL n)
{
    Solve(n,pri,num);
    LL ans=1;
    for(int i=0; i<cnt; i++)
    {
        LL record=1;
        if(pri[i]==2)
            record=3;
        else if(pri[i]==3)
            record=8;
        else if(pri[i]==5)
            record=20;
        else
        {
            if(legendre(5,pri[i])==1)
                Work(pri[i]-1);
            else
                Work(2*(pri[i]+1));
            sort(fac,fac+c);
            for(int k=0; k<c; k++)
            {
                Matrix a = power(A,fac[k]-1,pri[i]);
                LL x = (a.m[0][0]%pri[i]+a.m[0][1]%pri[i])%pri[i];
                LL y = (a.m[1][0]%pri[i]+a.m[1][1]%pri[i])%pri[i];
                if(x==1 && y==0)
                {
                    record = fac[k];
                    break;
                }
            }
        }
        for(int k=1; k<num[i]; k++)
            record *= pri[i];
        ans = ans/gcd(ans,record)*record;
    }
    return ans;
}

void Init()
{
    A.m[0][0] = 1;
    A.m[0][1] = 1;
    A.m[1][0] = 1;
    A.m[1][1] = 0;
}

int main()
{
    LL n;
    Init();
    isprime();
    while(cin>>n)
        cout<<find_loop(n)<<endl;
    return 0;
}



典型题目


题目一http://acm.hdu.edu.cn/showproblem.php?pid=3977

题目二http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5075

题目三:http://acm.hdu.edu.cn/showproblem.php?pid=3978

题目四:http://acm.hdu.edu.cn/showproblem.php?pid=4291


上面的题目一和题目二基本上是模板题,没有什么可以说的。对于第三题和第四题,我们可以看出题目四实际上是

题目三的简单版,都是一层一层找循环节,我们找出每一层的循环节后即可解决。


下面给出题目三的代码:

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <math.h>

using namespace std;
typedef long long LL;

const int M=2;

struct Matrix
{
    LL m[M][M];
};

Matrix per= {1,0,0,1};

Matrix multi(Matrix a,Matrix b,LL MOD)
{
    Matrix c;
    int i,j,k;
    for(i=0; i<M; i++)
    {
        for(j=0; j<M; j++)
        {
            c.m[i][j]=0;
            for(k=0; k<M; k++)
            {
                c.m[i][j]+=a.m[i][k]*b.m[k][j]%MOD;
            }
            c.m[i][j]%=MOD;
        }
    }
    return c;
}

Matrix power(Matrix a,LL k,LL MOD)
{
    Matrix ans=per,p=a;
    while(k)
    {
        if(k&1)
        {
            ans=multi(ans,p,MOD);
            k--;
        }
        k>>=1;
        p=multi(p,p,MOD);
    }
    return ans;
}

LL gcd(LL a,LL b)
{
    return b? gcd(b,a%b):a;
}

LL quick_mod(LL a,LL b,LL m)
{
    LL ans=1;
    a%=m;
    while(b)
    {
        if(b&1)
        {
            ans=ans*a%m;
            b--;
        }
        b>>=1;
        a=a*a%m;
    }
    return ans;
}

//勒让德符号
int legendre(int a,int p)
{
    if(quick_mod(a,(p-1)>>1,p)==1) return 1;
    else                           return -1;
}

const int N=1000005;
const int NN=50005;

bool prime[N];
int p[N];
int num[NN],pri[NN];
int num1[NN],pri1[NN];
int arr[NN];
int loop[N];
int k,cnt,c;

void isprime()
{
    k=0;
    int i,j;
    memset(prime,true,sizeof(prime));
    for(i=2; i<N; i++)
    {
        if(prime[i])
        {
            p[k++]=i;
            for(j=i+i; j<N; j+=i)
            {
                prime[j]=false;
            }
        }
    }
}

void find(int n,int pri[],int num[])
{
    cnt=0;
    int t=(int)sqrt(1.0*n);
    for(int i=0; p[i]<=t; i++)
    {
        if(n%p[i]==0)
        {
            int a=0;
            pri[cnt]=p[i];
            while(n%p[i]==0)
            {
                a++;
                n/=p[i];
            }
            num[cnt]=a;
            cnt++;
        }
    }
    if(n>1)
    {
        pri[cnt]=n;
        num[cnt]=1;
        cnt++;
    }
}

void dfs(int dept,int product=1)
{
    if(dept==cnt)
    {
        arr[c++]=product;
        return;
    }
    for(int i=0; i<=num1[dept]; i++)
    {
        dfs(dept+1,product);
        product*=pri1[dept];
    }
}

int find_loop(int n)
{
    find(n,pri,num);
    int cnt1=cnt;
    LL ans=1;
    for(int i=0; i<cnt1; i++)
    {
        c=0;
        int record=1;
        if(pri[i]==2)
            record=3;
        else if(pri[i]==3)
            record=8;
        else if(pri[i]==5)
            record=20;
        else
        {
            if(legendre(5,pri[i])==1)
                find(pri[i]-1,pri1,num1);
            else
                find(2*(pri[i]+1),pri1,num1);
            dfs(0,1);
            sort(arr,arr+c);
            for(int k=0; k<c; k++)
            {
                Matrix A;
                A.m[0][0]=1;
                A.m[0][1]=1;
                A.m[1][0]=1;
                A.m[1][1]=0;
                Matrix a=power(A,arr[k]-1,pri[i]);
                int x=(a.m[0][0]+a.m[0][1])%pri[i];
                int y=(a.m[1][0]+a.m[1][1])%pri[i];
                if(x==1&&y==0)
                {
                    record=arr[k];
                    break;
                }
            }
        }
        for(int k=1; k<num[i]; k++)
            record*=pri[i];
        ans=ans/gcd(ans,record)*record;
    }
    return ans;
}

void Solve(int p,int k)
{
    loop[0]=p;
    for(int i=1; i<=k; i++)
        loop[i]=find_loop(loop[i-1]);
}

int work(int n,int k,int p)
{
    int t=n;
    LL ret,MOD;
    Matrix ans;
    Matrix A;
    A.m[0][0]=1;
    A.m[0][1]=1;
    A.m[1][0]=1;
    A.m[1][1]=0;
    Solve(p,k);
    for(int i=k; i>=0; i--)
    {
        MOD=loop[i];
        ans=power(A,t,MOD);
        ret=(ans.m[1][0]+ans.m[1][1])%MOD;
        t=ret;
    }
    return ret;
}

int main()
{
    isprime();
    int T,n,k,p,tt=1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&k,&p);
        printf("Case #%d: %d\n",tt++,work(n,k,p));
    }
    return 0;
}


 

题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1195

 

分析:由于本题数据特别多,开始一直TLE,后来发现矩阵乘法里面如果取模太多会导致速度变得很慢,以前POJ

     的一道题关于矩阵求和也是因为这个TLE,尽量减少一些取模,速度会有几十倍甚至几百倍的提升。

 

代码:

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <map>

using namespace std;
typedef long long LL;

const int M = 2;

struct Matrix
{
    LL m[M][M];
};

Matrix A;
Matrix I = {1,0,0,1};

Matrix multi(Matrix a,Matrix b,LL MOD)
{
    Matrix c;
    for(int i=0; i<M; i++)
    {
        for(int j=0; j<M; j++)
        {
            c.m[i][j] = 0;
            for(int k=0; k<M; k++)
                c.m[i][j] += a.m[i][k] * b.m[k][j];
            c.m[i][j] %= MOD;
        }
    }
    return c;
}

Matrix power(Matrix a,LL k,LL MOD)
{
    Matrix ans = I,p = a;
    while(k)
    {
        if(k & 1)
        {
            ans = multi(ans,p,MOD);
            k--;
        }
        k >>= 1;
        p = multi(p,p,MOD);
    }
    return ans;
}

int gcd(int a,int b)
{
    return b? gcd(b,a%b):a;
}

const int N = 400005;
const int NN = 5005;

int num[NN],pri[NN];
int fac[NN];
bool flag[NN];
int c;

bool prime[N];
int p[N];
int k;

int cnt1;
int pri1[NN],num1[NN];

void isprime()
{
    k = 0;
    memset(prime,true,sizeof(prime));
    for(int i=2; i<N; i++)
    {
        if(prime[i])
        {
            p[k++] = i;
            for(int j=i+i; j<N; j+=i)
                prime[j] = false;
        }
    }
}

LL quick_mod(LL a,LL b,LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % m;
            b--;
        }
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

int legendre(int a,int p)
{
    if(quick_mod(a,(p-1)>>1,p)==1) return 1;
    else                           return -1;
}

void Solve(int n,int pri[],int num[],int &cnt)
{
    cnt = 0;
    int t = (int)sqrt(1.0*n);
    for(int i=0; p[i]<=t; i++)
    {
        if(n%p[i]==0)
        {
            int a = 0;
            pri[cnt] = p[i];
            while(n%p[i]==0)
            {
                a++;
                n /= p[i];
            }
            num[cnt] = a;
            cnt++;
        }
    }
    if(n > 1)
    {
        pri[cnt] = n;
        num[cnt] = 1;
        cnt++;
    }
}

void dfs(int dept,int cnt,LL product,int pri1[],int num1[])
{
    if(dept == cnt)
    {
        fac[c++] = product;
        return;
    }
    for(int i=0; i<=num1[dept]; i++)
    {
        dfs(dept+1,cnt,product,pri1,num1);
        product *= pri1[dept];
    }
}

map<int,int> mp;

LL find_loop(LL n)
{
    int cnt = 0;
    Solve(n,pri,num,cnt);
    LL ans = 1;
    for(int i=0; i<cnt; i++)
    {
        int record=1;
        if(mp.find(pri[i]) != mp.end())
        {
            record = mp[pri[i]];
            goto Test;
        }
        if(pri[i]==2)
            record=3;
        else if(pri[i]==3)
            record=8;
        else if(pri[i]==5)
            record=20;
        else
        {
            if(legendre(5,pri[i])==1)
            {
                c = 0;
                Solve(pri[i]-1,pri1,num1,cnt1);
                dfs(0,cnt1,1,pri1,num1);
            }
            else
            {
                c = 0;
                Solve(2*(pri[i]+1),pri1,num1,cnt1);
                dfs(0,cnt1,1,pri1,num1);
            }
            sort(fac,fac+c);
            for(int r=0; r<c; r++)
                flag[r] = 1;
            for(int k=c-1; k >= 0; k--)
            {
                if(!flag[k]) continue;
                Matrix a = power(A,fac[k]-1,pri[i]);
                int x = (a.m[0][0]%pri[i]+a.m[0][1]%pri[i])%pri[i];
                int y = (a.m[1][0]%pri[i]+a.m[1][1]%pri[i])%pri[i];
                if(x==1 && y==0)
                {
                    record = fac[k];
                }
                else
                {
                    for(int j=0; j<=k; j++)
                    {
                        if(fac[k] % fac[j] == 0)
                            flag[j] = 0;
                    }
                }
            }
            mp[pri[i]] = record;
        }
Test:
        for(int k=1; k<num[i]; k++)
            record *= pri[i];
        ans=ans/gcd(ans,record)*record;
    }
    return ans;
}

void Init()
{
    A.m[0][0] = 1;
    A.m[0][1] = 1;
    A.m[1][0] = 1;
    A.m[1][1] = 0;
}

int main()
{
    int T,n;
    Init();
    mp.clear();
    isprime();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        LL ans = find_loop(n);
        printf("%I64d\n",ans);
    }
    return 0;
}


 

<think>我们正在讨论递归函数的设计,特别是斐波那契数列函数`intFib(intn)`。根据递归问题的三个条件:1.问题可分解为同构子问题:斐波那契数列的定义就是第n项等于前两项之和(n>=3),即$Fib(n)=Fib(n-1)+Fib(n-2)$,这自然地将问题分解为两个同构子问题。2.存在明确的边界条件:斐波那契数列的前两项为1(通常定义$Fib(1)=1,Fib(2)=1$),这就是边界条件。在递归函数中,当n为1或2时,直接返回1。3.递归深度有限:由于每次递归调用n都在减小(n-1和n-2),并且最终会达到边界条件(n=1或n=2),因此递归深度是有限的。但是,直接使用递归计算斐波那契数列会存在重复计算的性能问题。例如,计算$Fib(5)$需要计算$Fib(4)$和$Fib(3)$,而计算$Fib(4)$又需要计算$Fib(3)$和$Fib(2)$,这里$Fib(3)$就被重复计算了。随着n增大,重复计算量会指数级增长。因此,虽然递归实现简单直观,但效率较低。我们可以通过记忆化(缓存已计算的结果)或改为迭代(循环)来优化。下面给出递归实现和迭代优化的代码,并分析其原理。###递归实现(基础版本)```cintFib(intn){if(n==1||n==2)//边界条件return1;elsereturnFib(n-1)+Fib(n-2);//分解为子问题}```**原理分析**:-当$n=1$或$n=2$时,直接返回1,这是递归的出口。-当$n>2$时,递归调用自身计算前两项之和。**时间复杂度**:$O(2^n)$(树形递归,指数级)**空间复杂度**:$O(n)$(递归调用栈深度为$n$)###迭代优化(避免重复计算)```cintFib(intn){if(n<1)return0;//可处理非法输入if(n==1||n==2)return1;intprev=1,curr=1;//初始化前两项for(inti=3;i<=n;i++){intnext=prev+curr;prev=curr;curr=next;}returncurr;}```**原理分析**:-从第3项开始,用循环依次计算每一项,直到第n项。-用两个变量`prev`和`curr`分别记录前两项,每次循环更新这两个变量。**时间复杂度**:$O(n)$**空间复杂度**:$O(1)$###设计条件验证1.**问题可分解**:斐波那契数列的定义本身就是递归的,满足条件。2.**边界条件**:明确给出$n=1$和$n=2$时的返回值。3.**递归深度有限**:递归版本中,每次递归n至少减1(至多减2),最终会达到边界条件。>**注意**:递归版本虽然满足递归条件,但效率低下。实际应用中,对于斐波那契数列,迭代方法更优。递归思想在分治算法(如汉诺塔、快速排序)中更具优势[^1][^2][^3][^4][^5]。---###相关问题1.递归计算斐波那契数列的时间复杂度如何推导?为什么是指数级?2.除了迭代和递归,还有哪些优化斐波那契数列计算的方法(如矩阵快速幂)?3.递归调用栈的最大深度由什么决定?如何避免堆栈溢出?4.记忆化递归(Memoization)如何实现?请给出斐波那契数列的记忆化递归代码。5.斐波那契数列的迭代实现中,如何用滚动数组优化空间复杂度?</think>### 递归函数 `int Fib(int n)` 的设计原理与实现分析 该函数用于计算斐波那契数列第 $n$ 项的值,其实现严格遵循递归问题的三个核心条件: #### 一、满足递归的三大条件 1. **问题可分解为同构子问题** 斐波那契数列的定义本身就是递归的: $$Fib(n) = Fib(n-1) + Fib(n-2)$$ 每个问题 $Fib(n)$ 被拆解为两个同构子问题 $Fib(n-1)$ 和 $Fib(n-2)$,且子问题规更小[^1][^3][^4]。 2. **存在明确的边界条件** 当 $n=1$ 或 $n=2$ 时直接返回 1,这是递归的终止点: ```c if (n == 1 || n == 2) return 1; // 递归出口 ``` 确保递归不会无限进行[^1][^3][^4]。 3. **递归深度有限** 每次递归调用时 $n$ 严格递减($n \to n-1$ 或 $n \to n-2$),最终必然到达边界条件 $n=1$ 或 $n=2$[^4]。 #### 二、实现代码分析 ```c int Fib(int n) { if (n == 1 || n == 2) // 边界条件 return 1; else return Fib(n-1) + Fib(n-2); // 分解为子问题 } ``` **执行流程**(以 $n=4$ 为例): ``` Fib(4) → Fib(3) + Fib(2) → [Fib(2) + Fib(1)] + 1 → (1 + 1) + 1 = 3 ``` #### 三、性能问题与优化 尽管满足递归条件,但存在**重复计算**缺陷(如 $Fib(2)$ 被多次计算): ```mermaid graph TD Fib4 --> Fib3 & Fib2 Fib3 --> Fib2 & Fib1 Fib2 --> 1 ``` **优化方案**: 1. **记忆化递归**:缓存已计算结果 2. **迭代法**(时间复杂度 $O(n)$,空间复杂度 $O(1)$): ```c int Fib(int n) { if (n <= 2) return 1; int a = 1, b = 1, c; for (int i = 3; i <= n; i++) { c = a + b; a = b; b = c; } return b; } // 引用[2] ``` #### 四、递归适用性总结 | 条件 | 斐波那契数列案例 | |-------------------|------------------------------------------| | 问题可分解 | $Fib(n)=Fib(n-1)+Fib(n-2)$ | | 边界条件 | $Fib(1)=1,\ Fib(2)=1$ | | 递归深度有限 | $n$ 每次至少减 1,最终到达 $n=1$ 或 $n=2$ | > **核心结论**:斐波那契数列是递归的经典案例,但需注意朴素递归的效率问题。实际应用中建议采用迭代或记忆化优化[^1][^2][^3][^4][^5]。 --- ### 相关问题 1. 递归计算斐波那契数列的时间复杂度为什么是指数级的 $O(2^n)$?如何推导? 2. 记忆化递归如何将斐波那契数列的时间复杂度优化到 $O(n)$?请给出代码实现。 3. 斐波那契数列的迭代实现中,如何通过滚动数组将空间复杂度降为 $O(1)$? 4. 哪些数学问题必须使用递归解决(如汉诺塔问题)?它们如何满足递归三条件? 5. 递归调用栈的最大深度如何计算?什么情况下会导致堆栈溢出?
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值