斐波那契和矩阵快速幂

快速幂

用于对底数的高次幂求模,例如a32常规运算是a连乘32次,快速幂则计算a2,随后计算a4,a8,a16,a32,只运算了5次,贼快。

ll power(ll a,ll b,ll q)
{
    ll res=1;
    while(b)
    {
        if(b%2)
            res=res*a%q;
        b=b/2;
        a=a*a%q;
    }
    return res%q;
}
快速幂模板代码

 


矩阵快速幂

1.矩阵相乘

前者行*后者列。结果矩阵的行列分别是前者行数,后者列数。(既然是幂次方,必然是一个n×n的矩阵)

例如:矩阵A是n×p的矩阵,矩阵B为p×m的矩阵,这样A和B才能相乘,结果是矩阵C,C的维度是n×m。

C的计算公式:

举例说明:

2.矩阵快速幂和数字快速幂一样,只是每一次矩阵相乘都需要三重循环,看起来挺吓人的,耐心看个5min其实也是能懂的。

P3390:n阶矩阵快速幂全裸,java只能过90分?(不知道有没有改进的余地,请大佬赐教)

import java.io.BufferedInputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;

public class Main{        //P3390【模板】矩阵快速幂
    
    static long [] [] a=new long [105][105];
    static long [] [] e=new long [105][105];//单位矩阵
    static long p=1000000007;
    static int n;
    static long k;
    public static void main(String []args) {
        //Scanner scan=new Scanner(new BufferedInputStream(System.in));
        Scanner scan=new Scanner( System.in);
        n=scan.nextInt();
        k=scan.nextLong();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                a[i][j]=scan.nextLong();
        for(int i=1;i<=n;i++)
            e[i][i]=1;
        long [][] ans=pow(a, k);
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++)
                System.out.print(ans[i][j]+" ");
            System.out.println();
        }
        
    }
    
    public static long[][] mul(long[][] x,long [][] y){//n阶矩阵相乘
        long [] [] c=new long [105][105];
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                c[i][j]=0;
        //对于c矩阵的结果,是x的列与y的行相乘的结果,使用X的列数作为k,必然对应Y的行
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    c[i][j]=(c[i][j]+x[i][k]*y[k][j])%p;
        return c;
    }
    
    public static long[][] pow(long[][] x,long k ){
        long [] [] res=e;//单位矩阵
        while(k!=0) {
            if(k%2==1)
                res=mul(res,x);
            x=mul(x, x);
            k=k/2;
        }
        return res;
    }
}
P3390 java版
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
using namespace std;

struct matrix{
    ll x[105][105];
};
matrix a,e;
int n;
ll p=1000000007;
ll b;


matrix mul(matrix a,matrix b){
    matrix res;
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
        res.x[i][j]=0;//初始化清0操作
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
        for(int k=1;k<=n;k++)
          res.x[i][j]=(res.x[i][j]+a.x[i][k]*b.x[k][j]  )%p;
    return res;
}

matrix pow( matrix a,ll b)
{
    matrix res=e;
    while(b){
        if(b%2==1)
            res=mul(res,a);
        b=b/2;
        a=mul(a,a);
    }
    return res;

}


int main()//P3390
{
    scanf("%d %lld",&n,&b);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%lld",&a.x[i][j]);
    for(int i=1;i<=n;i++)
        e.x[i][i]=1;

    matrix ans=pow(a,b);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            printf("%lld ",ans.x[i][j]);
        printf("\n");
    }

    return 0;
}
P3390 C++

 


 斐波那契矩阵快速幂 

1.斐波那契数列递推式 f(n)=f(n-1)+f(n-2)

默认第一项为0,f(0)=0;f(1)=1;f(2)=1;f(3)=2;f(4)=3;f(5)=5;f(6)=8;...

2.融合进矩阵运算公式

底数矩阵为[  f(2),f(1);f(1),f(0)  ];n次幂后取矩阵An右上角的数为f(n)

手动验证一下[ 1,1; 1,0 ]*An = [ 1,1; 1,0 ]*[ f(n+1),f(n); f(n),f(n-1) ] = [ f(n+1)+f(n),f(n)+f(n-1); f(n+1),f(n) ] = [ f(n+2),f(n+1); f(n+1),f(n) ];


 

斐波那契求前n项平方和

公式和图证如下:

 

题目:https://ac.nowcoder.com/acm/contest/3282/A,套用斐波那契矩阵快速幂的模板。

import java.io.BufferedInputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;

public class Main{    
    
    static long[][] a=new long[3][3];//底数矩阵
    static long[][] e=new long[3][3];//单位矩阵
    static long p=1000000007;
    public static void main(String []args) {
        //Scanner scan=new Scanner(new BufferedInputStream(System.in));
        Scanner scan=new Scanner( System.in);
        a[1][1]=1;a[1][2]=1;a[2][1]=1;a[2][2]=0;
        e[1][1]=e[2][2]=1;
        long k=scan.nextLong();
        long[][] res=pow(a, k);
        long ans=res[1][1]*res[1][2]%p;
        System.out.println(ans);
        
    }
    
    public static long[][] mul(long[][] x,long [][] y){//n阶矩阵相乘
        long [] [] c=new long [3][3];
        for(int i=1;i<=2;i++)
            for(int j=1;j<=2;j++)
                for(int k=1;k<=2;k++)
                    c[i][j]=(c[i][j]+x[i][k]*y[k][j])%p;
        return c;
    }
    public static long[][] pow(long[][] x,long k ){
        long [] [] res=e;//单位矩阵
        while(k!=0) {
            if(k%2==1)
                res=mul(res,x);
            x=mul(x, x);
            k=k/2;
        }
        return res;
    }

}
牛客小白20A

 

 


 

斐波那契循环节

求斐波那契数列模N意义下的循环节
1.唯一分解定理
定义:任何一个数N可以表示成 素数的次方 的乘积
分解公式: 
求N的因子个数公式:num=(a1+1)×(a2+1)×......×(an+1);
2.分别计算Fib数列每个pm的循环节长度,假设长度为L(pm),用L(p)表示斐波那契数列模p意义下的循环节
有一个定理:L(pm)=L(p) × pm-1 
 
未完待补,二次剩余实在是看不下去了。。。
 
posted @ 2019-12-27 23:44  守林鸟  阅读(804)  评论(0编辑  收藏  举报