Fibonacci - POJ 3070 矩阵乘法快速幂

本文介绍了一种高效计算斐波那契数列中任意项最后四位数字的方法,通过矩阵快速幂运算实现,并提供了两种AC代码示例。

Fibonacci
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 8570 Accepted: 6093

Description

In the Fibonacci integer sequence, F0 = 0, F1 = 1, and Fn = Fn − 1 + Fn − 2 for n ≥ 2. For example, the first ten terms of the Fibonacci sequence are:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …

An alternative formula for the Fibonacci sequence is

.

Given an integer n, your goal is to compute the last 4 digits of Fn.

Input

The input test file will contain multiple test cases. Each test case consists of a single line containing n (where 0 ≤ n ≤ 1,000,000,000). The end-of-file is denoted by a single line containing the number −1.

Output

For each test case, print the last four digits of Fn. If the last four digits of Fn are all zeros, print ‘0’; otherwise, omit any leading zeros (i.e., print Fn mod 10000).

Sample Input

0
9
999999999
1000000000
-1

Sample Output

0
34
626
6875

Hint

As a reminder, matrix multiplication is associative, and the product of two 2 × 2 matrices is given by

.

Also, note that raising any 2 × 2 matrix to the 0th power gives the identity matrix:

.


这道题是矩阵乘法快速幂,简单举个例子,比如你想计算a的9次方,先有a*a变成a的2次方,再相乘变成a的4次方,再相乘变成a的8次方,再乘以a变成a的9次方。

这里还用到一种思想就是二进制如9的二进制位1001一共有4位,第一位肯定是1,那么就为a,第二位是0,就把用前面得到的数相乘,a*a=a^2,第三位也是0,继续相乘a^2*a^2=a^4,第四位是1,就a^4*a^4*a=a^9。

我们可以正着写,也可以反着写(下面代码是反着写的),定义一个ret和一个first,ret为最后结果,每次first相乘先让first为a,9,的第四位是1,所以ret为a^1,first为a^2;第三位为0,ret不变,fitst为a^4,第二位为0,ret不变,fitst为a^8;第一位为1,ret=ret*first=a*a^8=a^9,first=a^16。

    或者这么说吧,就是 1*a^8  *  0*a^4  *  0*a^2  *  1*a^1

这里a是一个矩阵,那么就要再加上矩阵相乘。

注意最后的答案要mod10000。


另外我最开始的时候没有用这种方法,像这种有规律递归的算法再mod一个数,一般也都是有规律可循的,所以我先写一个计算斐波那契的程序,且mod10000,然后当这个数为1的时候,输出这是第几个数,后来发现输出的是1,2,15001,15002,30001,30002……说明这个数是15000循环一次,且0和15000对应的数都是0。(有的可能0和一次循环15000的结果不一样,虽然这道题不存在这种情况,但是有必要注意一下。)


先给出矩阵相乘的AC代码:

#include<cstdio>
#include<cstring>
using namespace std;

struct node
{ int f[2][2];
};
node mul(node a,node b)  //定义一个相乘的方法
{ int i,j,k;
  node m;
  for(i=0;i<=1;i++)      //得到的矩阵的第i行,第j列的数
  { for(j=0;j<=1;j++)
    { m.f[i][j]=0;
      for(k=0;k<=1;k++)  //前面矩阵的第i行乘以后面矩阵的第j列
       m.f[i][j]+=a.f[i][k]*b.f[k][j];
      m.f[i][j]%=10000;  //取模
    }
  }
  return m;
}
int main()
{ int n,l;
  while(~scanf("%d",&n)  && n>=0)
  { if(n<=1)     //如果输入为1,输出的f(1)=1,输入为0,输出的f(0)=0
     printf("%d\n",n);
    else
    { node ret;
      node first;
      n--;  //因为最开始的时候让ret=1,所以最后乘出的结果也要除以一个基本矩阵,也就是说求n-1即可
      first.f[0][0]=first.f[0][1]=first.f[1][0]=ret.f[0][0]=ret.f[0][1]=ret.f[1][0]=1;
      first.f[1][1]=ret.f[1][1]=0;
      while(n)
      { if(n&1)                   //如果二进制最后一位是1的话,这句话等同于n%2==1
         ret=mul(ret,first);      //乘以first矩阵
        first=mul(first,first);   //first矩阵自乘
        n/=2;
      }
      printf("%d\n",ret.f[0][1]);
    }
  }
}




然后是利用循环规律的AC代码:

#include<cstdio>
int f[15005];
int main()
{ int i,j,n;
  f[0]=0;
  f[1]=f[2]=1;
  for(i=3;i<=15000;i++)        //每15000循环一次
   f[i]=(f[i-1]+f[i-2])%10000;
  while(~scanf("%d",&n)&&n>=0)
  { n=n%15000;
    printf("%d\n",f[n]);
  }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值