HDU3317---Fibonacci Numbers(矩阵快速幂+pow+log)

本文介绍了一种利用矩阵快速幂求解斐波那契数的方法,并针对特殊情况(如数值长度超过8位时)提供了输出前四位和后四位的具体实现方案。文章通过实例详细解释了如何使用log10函数进行前四位的计算,以及如何处理矩阵乘法和加法运算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目来源】https://cn.vjudge.net/problem/HDU-3117
【题意】
求第n个斐波那契数,如果这个数值长度大于8位,就输出前四位和后四位。
【思路】
小伙伴说到这道题的时候,我就想到了另外一道题,求k^n的前三位,后三位
里面使用到了log10函数,log10的使用方法刚才那道题的题解有,自行点击,不再多说。
然后,就是在这道题里求前四位该如何使用:
也就是我们可以把矩阵里的数值使用log10后的数字表示,那me就不会炸,但是0该如何表示呢?用负数把,因为0在log10之后的代表的是1,(log10(0)==1),所以初始矩阵为:
0 0
0 -1
其次就是矩阵乘法该如何写?
在普通矩阵乘法里,0乘任何数都是0,而在log10后的矩阵里,负数代表的是0,所以我们就可以特判,如果”将要相乘”的两个数有一个是-1,那么直接略去,那么普通矩阵里乘法相当于log10后的矩阵里的加法(可以自行模拟想一下),但是会有人问,不仅有乘法,还有加法呢,该怎么算呢?假如将要相加的两个数是x,y,那么他们各自代表的数为:10^x,10^y,那么他们相加就可以变为10^min(x,y)*(1+10^(max(x,y)-min(x,y)),写的有点麻烦,其实就是提取公因式啦。。后面那一大部分用pow函数就可以了。
然后后四位直接矩阵快速幂取模,从样例发现,第40个斐波那契数值刚好大于8位,那么前面的只需要打个表就行。
【代码】

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
struct mat
{
    int a[3][3];//后四位的矩阵
    double b[3][3];//前四位的矩阵
};
mat operator*(mat &s,mat &t)//后四位的矩阵快速幂
{
    mat r;
    memset(r.a,0,sizeof(r.a));
    for(int i=1; i<=2; i++)
        for(int j=1; j<=2; j++)
            for(int k=1; k<=2; k++)
            {
                r.a[i][j]+=s.a[i][k]*t.a[k][j];
                if(r.a[i][j]>=10000)
                    r.a[i][j]%=10000;
            }
    return r;
}
mat operator/(mat &s,mat &t)//因为“*”被后四位的快速幂占用了,所以换成了'/'(其实什么都可以)
{
    mat r;
    for(int i=1; i<=2; i++)
        for(int j=1; j<=2; j++)
        {
            int f1=1,f2=1;
            if(s.b[i][1]<0||t.b[1][j]<0) f1=0;//判断是否存在-1
            if(s.b[i][2]<0||t.b[2][j]<0) f2=0;
            if(!f1&&f2)
                r.b[i][j]=s.b[i][2]+t.b[2][j];
            else if(f1&&!f2)
                r.b[i][j]=s.b[i][1]+t.b[1][j];
            else if(f1&&f2)//若都不是-1
            {
                double x=s.b[i][1]+t.b[1][j],y=s.b[i][2]+t.b[2][j];
                r.b[i][j]=min(x,y)+log10(1+pow(10,max(x,y)-min(x,y)));//提取公因式
            }
            else if(!f1&&!f2)//若都是-1,那么负值为-1,相当于普通矩阵的0
                r.b[i][j]=-1.0;
        }
    return r;
}
int pow_int(mat base,int k)//后四位
{
    mat ans;
    memset(ans.a,0,sizeof(ans.a));
    ans.a[1][1]=ans.a[2][2]=1;
    int cases=0;
    while(k)
    {
        if(k&1)
            ans=ans*base;
        base=base*base;
        k>>=1;
    }
    return ans.a[1][1];
}
double pow_double(mat base,int k)//前四位
{
    mat ans;
    ans.b[1][2]=ans.b[2][1]=-1.0;
    ans.b[1][1]=ans.b[2][2]=log10(1.0);
    while(k)
    {
        if(k&1)
            ans=ans/base;
        base=base/base;
        k>>=1;
    }
    int x=(int)ans.b[1][1];
    ans.b[1][1]-=x;
    return pow(10.0,ans.b[1][1])*1000;
}
int main()
{
    int p[40];//前39位预处理
    p[0]=0;
    p[1]=1;
    for(int i=2; i<=39; i++)
    {
        p[i]=p[i-1]+p[i-2];
    }
    while(~scanf("%d",&n))
    {
        if(n<=39)
            printf("%d\n",p[n]);
        else
        {
            mat base;
            base.a[2][1]=base.a[1][2]=base.a[1][1]=1;
            base.a[2][2]=0;
            base.b[1][2]=base.b[2][1]=base.b[1][1]=log10(1.0);
            base.b[2][2]=-1.0;
            int r_ans=pow_int(base,n-1);
            int l_ans=(int)pow_double(base,n-1);
            printf("%d...%04d\n",l_ans,r_ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值