美文网首页
lint0949. lint0140.

lint0949. lint0140.

作者: 日光降临 | 来源:发表于2019-02-28 23:19 被阅读0次
949. Fibonacci II
keyword:快速幂,二进制, 记忆化

计算Fibonacci数列第n项的最后四个数字,也就是 %1000的值
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:


lint0949-1.png

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

Example

Given: n = 9
Return: 34

Notice

1.0 ≤ n ≤ 1,000,000,000
2.If the last four digits of Fn are all zeros, print ‘0’; otherwise, omit any leading zeros(print Fn mod 10000)

  • 本题出自「POJ3070」Fibonacci
  • 这道题用到了矩阵快速幂,第一个问题什么是快速幂,快速幂是指利用矩阵的乘法结合律来实现快速计算矩阵幂的过程。
    矩阵快速幂和整数快速幂是一样的原理,a8 如果一个一个乘,那么要计算7次。但是如果按((a2)2)2,则只计算4次,也就是一个是O(n)的时间复杂度,一个是O(logn), 而程序的实现怎么做呢?快速幂好理解,但是实现起来不好办。

要计算ab, 最快的方式就是尽可能少用a*a,仅仅a2用a*a, 其他的幂,仅仅利用a*a的结果(记忆化,并且记忆化贯穿始终),但是不用a*a的过程,也就是完全用a的2的乘方次幂相乘
a2 = a1*a1, 权值*权值
a3 = a1*a2, 已有结果*权值
a4 = a2*a2, 权值*权值
a5 = a1*a4, 已有结果*权值
a6 = a2*a4, 已有结果*权值
a7 = a1*a2*a4, 已有结果*权值,
a8 = a4*a4, 权值*权值
a19=a(24+21+20), 19的二进制10011,19 = 24 + 21 + 20,因此我们将a19 转换为算 a16 * a2 * a1, 当然这里面用到了xm*xn = xm+n

int quick_pow(int a,int b)
{
    int weight= a;//初始化权值
    int ret = 1;//初始化结果
    while(b){
        if(b&1){//判断b的最后一位是否为1
            ret *= weight;
        }
        weight *= weight;//更新权值
        b >>= 1;
    }
    return ret;
}
  • 对于矩阵的幂,原理是一样的,只是两个矩阵相乘又比较复杂,需要单独的函数来完成,比如下面的函数
 c = a*b
a[0][0] a[0][1] b[0][0] b[0][1] c[0][0] c[0][1]
a[1][0] a[1][1] b[1][0] b[1][1] c[1][0] c[1][1]

i=0 j=0:   i  k    k  j
c[0][0]+=a[0][0]*b[0][0] k=0
c[0][0]+=a[0][1]*b[1][0] k=1
i=0 j=1:   i  k    k  j
c[0][1]+=a[0][0]*b[0][1] k=0
c[0][1]+=a[0][1]*b[1][1] k=1
i=1 j=0:   i  k    k  j
c[1][0]+=a[1][0]*b[0][0] k=0
c[1][0]+=a[1][1]*b[1][0] k=1
i=1 j=1:   i  k    k  j
c[1][1]+=a[1][0]*b[0][1] k=0
c[1][1]+=a[1][1]*b[1][1] k=1

void multi_mtrx1(int a[2][2],int b[2][2])
{
   int i,j,k;
   int c[2][2];
   memset(c,0,sizeof(c));
   for(i=0;i<2;i++){
      for(j=0;j<2;j++){
         for(k=0;k<2;k++){
            c[i][j]+=a[i][k]*b[k][j];
            c[i][j]%=10000;/*加上这一句*/
         }
      }
   }
   for(i=0;i<2;i++)
      for(j=0;j<2;j++)
         a[i][j]=c[i][j];
}
  • 另外一个矩阵乘法函数 以及快速幂的函数
 /* 这种方法比较难于理解,好处是如果n比较大,且矩阵比较稀疏的时候可以剪枝
i=0 k=0: i k k j
c[0][0]+=a[0][0]*b[0][0] j=0
c[0][1]+=a[0][0]*b[0][1] j=1
i=0 k=1: i k k j
c[0][0]+=a[0][1]*b[1][0] j=0
c[0][1]+=a[0][1]*b[1][1] j=1
i=1 k=0: i k k j
c[1][0]+=a[1][0]*b[0][0] j=0
c[1][1]+=a[1][0]*b[0][1] j=1
i=1 k=1: i k k j
c[1][0]+=a[1][1]*b[1][0] j=0
c[1][1]+=a[1][1]*b[1][1] j=1
*/
void multi_mtrx2(int a[2][2],int b[2][2])
{
   int i,j,k;
   int c[2][2];
   memset(c,0,sizeof(c));
   for(i=0;i<2;i++){
      for(k=0;k<2;k++)
      {
         if(0==a[i][k])/*剪枝,但其实对于2x2的矩阵意义不大*/
            continue;

         for(j=0;j<2;j++){
            c[i][j]+=a[i][k]*b[k][j];
            c[i][j]%=10000;/*加上这一句*/
         }
      }
   }
   for(i=0;i<2;i++)
      for(j=0;j<2;j++)
         a[i][j]=c[i][j];
}

void quick_pow(int ret[2][2],int power)
{
   int fi[2][2];
   ret[0][0]=ret[1][1]=1;
   ret[0][1]=ret[1][0]=0;
   fi[1][1]=0;
   fi[0][0]=fi[0][1]=fi[1][0]=1;
   while(power){
      if(power&1)
         multi_mtrx2(ret,fi);
      multi_mtrx2(fi,fi);
      power>>=1;
   }
}
  • Java版
public class Solution {
    public String lastFourDigitsOfFn(int n) {
        if (n == 0) return "0";
        if (n == 1) return "0001";
        int[][] f = {{1, 1}, {1, 0}};
        int[][] res = powHelper(f, n-1);
        String s = "" + res[0][0];
        if (s.length() == 1) return "000" + s;
        if (s.length() == 2) return "00" + s;
        if (s.length() == 3) return "0" + s;
        return s;
    }
    private int[][] powHelper(int[][] f, int n) {
        if (n == 1) return f;
        if (n == 2) return multipyHelper(f, f);
        int[][] ans =  powHelper(powHelper(f, n/2), 2);
        if (n % 2 == 0) return ans;
        return multipyHelper(f, ans);
    }
    private int[][] multipyHelper(int[][] f1, int[][] f2) {
        // f[0][0] f[0][1]
        // f[1][0] f[1][1]
        int n = 10000;
        int x = (f1[0][0]*f2[0][0]%n + f1[0][1]*f2[1][0]%n)%n;
        int y = (f1[0][0]*f2[0][1]%n + f1[0][1]*f2[1][1]%n)%n;
        int p = (f1[1][0]*f2[0][0]%n + f1[1][1]*f2[1][0]%n)%n;
        int q = (f1[1][0]*f2[0][1]%n + f1[1][1]*f2[1][1]%n)%n;
        return new int[][]{{x, y},{p, q}};
    }
}
lint0140. Fast Power

Calculate the an % b where a, b and n are all 32bit positive integers.
特别注意的是一个表达式必须完全计算完才能mod b,不可用中间结果mod操作

快速幂版
public class Solution {
    /**
     * @param a: A 32bit integer
     * @param b: A 32bit integer
     * @param n: A 32bit integer
     * @return: An integer
     */
    public int fastPower(int a, int b, int n) {
        long weight = a;
        long ret = 1;
        while(n!=0){
            if((n&0x1)==1){
                ret = (ret*weight) % b;//不可用 ret*=(weight%b)
            }
            weight = (weight*weight) % b;//不可用 weight *= (weight%b)
            n >>= 1;
        }
        return (int)ret%b;
    }
}
递归版
class Solution {
    /*
     * @param a, b, n: 32bit integers
     * @return: An integer
     */
    public int fastPower(int a, int b, int n) {
        if (n == 1) {
            return a % b;
        }
        if (n == 0) {
            return 1 % b;
        }
        
        long product = fastPower(a, b, n / 2);
        product = (product * product) % b;
        if (n % 2 == 1) {
            product = (product * a) % b;
        }
        return (int) product;
    }
};

相关文章

  • lint0949. lint0140.

    949. Fibonacci II keyword:快速幂,二进制, 记忆化 计算Fibonacci数列第n项的最...

网友评论

      本文标题:lint0949. lint0140.

      本文链接:https://www.haomeiwen.com/subject/wuwluqtx.html