描述

这是一篇面对初级coder的题解。

知识点:数学 递推

难度:五星


题解

题目: 求斐波那契数列前n项和的前n项和的前n项和。

分析: 斐波那契数列本身就有一定的递推特性,需要结合数学知识递推求得

方法一 递推:

已知f(n)=f(n1)+f(n2)f(n)=f(n-1)+f(n-2)f(n)=f(n1)+f(n2)

T(n)=i=1nf(i)T(n)=\sum_{i=1}^nf(i)T(n)=i=1nf(i)T(n)=T(n1)+f(n)T(n)=T(n-1)+f(n)T(n)=T(n1)+f(n)

G(n)=i=1nT(i)G(n)=\sum_{i=1}^nT(i)G(n)=i=1nT(i)G(n)=G(n1)+T(n)G(n)=G(n-1)+T(n)G(n)=G(n1)+T(n)

F(n)=i=1nG(i)F(n)=\sum_{i=1}^nG(i)F(n)=i=1nG(i)F(n)=F(n1)+G(n)F(n)=F(n-1)+G(n)F(n)=F(n1)+G(n)

F(n)F(n)F(n) 即为所求 这样的话就优化了递推的式子。

class Solution {
public:
    const int mod=1e9+7;
    int getSum(int n) {
        /*初始值*/
        int f[3]={0,1,1};//斐波那契数列
        int T=1;//f前n项和T
        int G=1;//T前n项和G
        int F=1;//G前n项和F——所求
        for(int i=2;i<=n;i++){
            f[2]=(f[1]+f[0])%mod;//f(n)=f(n-1)+f(n-2)=1
            f[0]=f[1];//f(n-2)=f(n-1)
            f[1]=f[2];//f(n-1)=f(n)
            T=(T+f[2])%mod;//T(n)=T(n-1)+f(n)
            G=(G+T)%mod;//G(n)=G(n-1)+T(n)
            F=(F+G)%mod;//F(n)=F(n-1)+G(n)
        }
        return F;
    }
};

运行测试:

9/10 组用例通过 运行时间 1001ms 占用内存 432KB

复杂度分析:

采用递归和记忆化搜索的方式,避免了4重循环

故时间复杂度为O(n)O(n)O(n)——只遍历一遍 空间复杂度为O(1) 仅占用常数空间

方法二 通项公式 递推:

斐波那契数列前n项和:

考虑如下式子

f(n+2)=f(n+1)+f(n)f(n+2)=f(n+1)+f(n)f(n+2)=f(n+1)+f(n)

f(n+1)=f(n)+f(n1)f(n+1)=f(n)+f(n-1)f(n+1)=f(n)+f(n1)

f(n)=f(n1)+f(n2)f(n)=f(n-1)+f(n-2)f(n)=f(n1)+f(n2)

....

f(3)=f(2)+f(1)f(3)=f(2)+f(1)f(3)=f(2)+f(1)

对于上面所有式子累加求和有 f(n+2)=i=1nf(i)+f(1)f(n+2)=\sum_{i=1}^nf(i)+f(1)f(n+2)=i=1nf(i)+f(1)

T(n)=i=1nf(i)=f(n+2)1 T(n)=\sum_{i=1}^nf(i) = f(n+2)-1T(n)=i=1nf(i)=f(n+2)1

同样的G(n)=i=1nT(i)=T(n+2)T(2)n=f(n+4)f(4)n=f(n+4)(n+3) G(n)=\sum_{i=1}^nT(i) = T(n+2)-T(2)-n=f(n+4)-f(4)-n=f(n+4)-(n+3)G(n)=i=1nT(i)=T(n+2)T(2)n=f(n+4)f(4)n=f(n+4)(n+3)

依然类似 F(n)=i=1nG(i)=T(n+4)T(4)i=4nn+3=f(n+6)f(6)(n+7)n/2 F(n)=\sum_{i=1}^nG(i) =T(n+4)-T(4)-\sum_{i=4}^nn+3=f(n+6)-f(6)-(n+7)*n/2F(n)=i=1nG(i)=T(n+4)T(4)i=4nn+3=f(n+6)f(6)(n+7)n/2

最后一项是等差数列求和公式且f(6)=8f(6)=8f(6)=8

故所求F(n)=f(n+6)8(n+7)n/2F(n)=f(n+6)-8-(n+7)*n/2F(n)=f(n+6)8(n+7)n/2

通过这样的计算 减少了递推的次数 问题也划归为求斐波那契数列的第n+6项

class Solution {
public:
    const int mod=1e9+7;
    int getSum(int n) {
        /*初始值*/
        int f[3]={0,1,1};//斐波那契数列
        for(int i=2;i<=n+6;i++){
            f[2]=(f[1]+f[0])%mod;//f(n)=f(n-1)+f(n-2)=1
            f[0]=f[1];//f(n-2)=f(n-1)
            f[1]=f[2];//f(n-1)=f(n)
        }
        long long ans=(mod+f[2]-8-(long long)(n+7)*(long long)n/2%mod)%mod;
        return (int) ans;
    }
};

运行测试:

9/10 组用例通过 运行时间 1001ms 占用内存 432KB

复杂度分析:

采用递归和记忆化搜索的方式,避免了4重循环

故时间复杂度为O(n)O(n)O(n)——只遍历一遍

空间复杂度为O(1) 仅占用常数空间

但是仍然超时 说明递推求斐波那契数列的第n+6项仍然耗费过多时间,需要用二分法做进一步优化。

方法三 通项公式 二分法递推:

考虑用数学归纳法证明如下两个关于斐波那契数列的性质

性质一:

若i为奇数, f(i)f(i)=f(i1)f(i+1)+1f(i)*f(i)=f(i-1)*f(i+1)+1f(i)f(i)=f(i1)f(i+1)+1,否则f(i)f(i)=f(i1)f(i+1)1f(i)*f(i)=f(i-1)*f(i+1)-1 f(i)f(i)=f(i1)f(i+1)1

性质二:

f(n)=f(1)f(n2)+f(2)f(n1)=f(2)f(n3)+f(3)f(n2)=f(3)f(n4)+f(4)f(n3)=f(i)f(ni1)+f(i+1)f(ni)f(n)=f(1)*f(n-2)+ f(2)*f(n-1) \\ =f(2)*f(n-3)+ f(3)*f(n-2) \\ =f(3)*f(n-4)+ f(4)*f(n-3) \\ =f(i)*f(n-i-1)+f(i+1)*f(n-i)f(n)=f(1)f(n2)+f(2)f(n1)=f(2)f(n3)+f(3)f(n2)=f(3)f(n4)+f(4)f(n3)=f(i)f(ni1)+f(i+1)f(ni)

i=n/2i=n/2i=n/2,可采用二分法用来计算较大的fibonacci数除以某个数的余数,以乘法代替加法,从而将递推复杂度变为O(logn)O(logn)O(logn)

#define max 1000000//测试这个值比较合适 时间短
class Solution {
public:
    const int mod=1e9+7;
    int Fib(int n,vector<int> &f){//二分法计算前n项和
        long long ans;//返回值
        if(n<=max)
            return f[n];//分情况 二分太细乘法复杂度会上升 故先打表
        else{
            int i=n/2;
            //核心二分公式 详见解析 f(n)=f(i)*f(n-i-1)+f(i+1)*f(n-i)
            ans=((long long) Fib(i,f)*(long long) Fib(n-i-1,f)+(long long)Fib(i+1,f)*(long long)Fib(n-i,f))%mod;
        }
        return (int) ans;
    }
    int getSum(int n) {
        vector<int> f(max);//斐波那契数列
        /*初始值*/
        f[0]=0;
        f[1]=1;
        for(int i=2;i<max;i++)//预打表
            f[i]=(f[i-1]+f[i-2])%mod;//f(n)=f(n-1)+f(n-2)=1
        long long ans=(mod+Fib(n+6,f)-8-(long long)(n+7)*(long long)n/2%mod)%mod;//推导公式直接计算
        return (int) ans;
    }
};

运行测试:

通过全部用例 运行时间 21ms 占用内存 4296KB

复杂度分析:

实际测试发现,频繁做小数的乘法耗时长,故定义一个分界值max

小于此值时用递推的结果,大于此值用二分的结果

故时间复杂度为O(logn)O(logn)O(logn)——二分法递推

空间复杂度为O(1) 仅占用常数空间

总结

数学很重要

当n进一步增大时 即n+6>modn+6>modn+6>mod

考虑斐波那契数列通项公式 alt

与费马小定理

alt

f(n+6)%mod=f(n+6mod)%modf(n+6) \% mod=f(n+6-mod) \% modf(n+6)%mod=f(n+6mod)%mod