zoukankan      html  css  js  c++  java
  • 数论

    矩阵乘法

    可以把矩阵理解为二维数组,数存在里面,矩阵乘法的规则:A*B=C


    实现代码如下:

    const int N=50;
    int c[N][N];
    void multi(int a[N][N],int b[N][N],int n);
    {
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            for(int k=1;k<=n;k++)
            {
                if(a[i][k]==0) continue;
                for(int j=1;j<=n;j++)
                {
                    c[i][j]+=a[i][k]*b[k][j];
                }
            }
        }
    
    }
    

    快速幂和矩阵快速幂

    • 利用位运算判断奇偶性
    if(i&1)
    {
        奇数....
    }
    else
    {
        偶数...
    }
    
    • 程序终止问题
      exit(0):正常运行程序并退出程序;

    exit(1):非正常运行导致退出程序;

    return():返回函数,若在主函数中,则会退出函数并返回一值。

    快速幂

    • 快速幂原理
      如果当前的指数是偶数,我们把指数拆成两半,得到两个相同的数,然后把这两个相同的数相乘,可以得到原来的数;
      如果当前的指数是奇数,我们把指数拆成两半,得到两个相同的数,此时还剩余一个底数,把这两个相同的数和剩余的底数这三个数相乘,可以得到原来的数。
    • 当n>0代码如下
    long long res = 1;
    // 进行快速幂运算,n 为当前的指数值,n 为 0 的时候运算结束
    while (n) {
        // 用位运算的方式判断 n 是否为奇数,速度更快,等价于 n%2 
        if (n & 1) {
            // 如果 n 是奇数,那么需要将 x 存入运算结果中
            res *= x;
        }
        // 更新当前的 x 的值
        x *= x;
        // 用位运算的方式进行 n/2,速度更快,等价于 n/=2
        n >>= 1;
    }
    

    完整的代码如下:

    #include <iostream>
    #include <cstdlib>
    using namespace std;
    
    // 使用快速幂求出 x^n 的值并返回,不考虑高精度,请控制参数范围
    double myPow(double x, int n) {
        // 任何不是 0 的数的 0 次幂为 1 
        if (x && n == 0) {
            return 1;
        } else if (x == 0 && n == 0) {
            exit(1);
        }
        // 如果 n 是负数,那么返回结果要进行处理 
        bool judge = false;
        if (n < 0) {
            judge = true;
            n = -n;
        }
    
        double res = 1;
        while (n) {
            // 用位运算的方式判断 n 是否为奇数,速度更快,等价于 n%2 
            if (n & 1) {
                res =res*res%p;
            }
            x = x*x%p;
            // 用位运算的方式进行 n/2,速度更快,等价于 n/=2
            n >>= 1;
        }
        // n 是负数?1.0/res 否则 res 
        if(judge)
        {
            return 1.0/res;
        }
        else
        {
            return res;
        }
    } 
    
    int main() {
        double x;
        int n;
    
        while (cin >> x >> n) {
            cout << myPow(x, n) << endl << endl; 
        } 
    
        return 0;
    } 
    

    矩阵快速幂模板代码如下

    模板题目链接

    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define MAXN 200
    #define MOD 1000000007
    using namespace std;
    typedef long long ll;
    
    int A[MAXN][MAXN];
    int res[MAXN][MAXN];
    ll tmp[MAXN][MAXN];
    int n; ll k;
    
    void mulit(int a[][MAXN],int b[][MAXN])
    {
        memset(tmp,0,sizeof(tmp));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                for(int k=1;k<=n;k++)
                {
                    tmp[i][j]=((ll)a[i][k]*b[k][j]%MOD+tmp[i][j])%MOD;
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                a[i][j]=(int)tmp[i][j];
            }
        }
    
    }
    
    void Pow(int a[][MAXN],ll k)
    {
        memset(res,0,sizeof(res));
        for(int i=1;i<=n;i++)
        {
            res[i][i]=1;
        }
        while(k)
        {
            if(k&1)
            {
                mulit(res,a);
            }
        mulit(a,a);
        k>>=1;
        }
        
    }
    int main()
    {
        scanf("%d%lld",&n,&k);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&A[i][j]);
            }
        }
        Pow(A,k);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                printf("%d ",res[i][j]);
            }
            cout<<endl;
        }
    }
    
    

    (代码借用了https://blog.csdn.net/wust_zzwh/article/details/52058209)

    利用矩阵快速幂求斐波那契数列

    利用矩阵快速幂求递推数列的关键是找到合适的递推关系式
    如此大佬博客中所推蓝桥杯 算法提高 递推求值(矩阵快速幂)详解
    利用矩阵快速幂求斐波那契数列的模板题链接P1939 【模板】矩阵加速(数列)
    模板题代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define MAXN 200
    #define MOD 1000000007
    #define ll long long
    using namespace std;
    
    ll n;
    ll A[MAXN][MAXN];
    ll B[MAXN][MAXN];
    ll res[MAXN][MAXN];
    ll tmp[MAXN][MAXN];
    
    void mulit(ll a[][MAXN],ll b[][MAXN])
    {
        memset(tmp,0,sizeof(tmp));
        for(int i=1;i<=6;i++)
        {
            for(int j=1;j<=6;j++)
            {
                for(int k=1;k<=6;k++)
                {
                    tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
                }
            }
        }
        for(int i=1;i<=6;i++)
        {
            for(int j=1;j<=6;j++)
            {
                a[i][j]=tmp[i][j];
            }
        }
    }
    
    void pow(ll a[][MAXN],ll k)
    {
        memset(res,0,sizeof(res));
        for(int i=1;i<=6;i++)
        {
            res[i][i]=1;
        }
        
        while(k)
        {
            if(k&1)
            {
                mulit(res,a);
    //			printf("k=%lld
    ",k);
    //		printf("res=%lld
    ",res[1][1]);
            }
            mulit(a,a);
            k>>=1;
        }
    }
    int main()
    {
        scanf("%lld",&n);
        if(n==1||n==2)
        {
            printf("1");
            return 0;
        }
        A[1][1]=1;
        A[1][2]=1;
        A[2][1]=1;
        A[2][2]=0;
        B[1][1]=1;
        B[2][1]=1;
        pow(A,n-2);
    //	cout<<1;
        mulit(res,B);
        
        printf("%lld ",res[1][1]);
        return 0;
    }
     
    

    类似题及代码如下:
    P1939 【模板】矩阵加速(数列)
    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define MAXN 7
    #define MOD 1000000007
    #define ll long long
    using namespace std;
    
    ll n;
    ll A[7][7];
    ll B[7][7];
    ll res[7][7];
    ll tmp[7][7];
    
    void mulit(ll a[][MAXN],ll b[][MAXN])
    {
    	memset(tmp,0,sizeof(tmp));
    	for(int i=1;i<=6;i++)
    	{
    		for(int j=1;j<=6;j++)
    		{
    			for(int k=1;k<=6;k++)
    			{
    				tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
    			}
    		}
    	}
    	for(int i=1;i<=6;i++)
    	{
    		for(int j=1;j<=6;j++)
    		{
    			a[i][j]=tmp[i][j];
    		}
    	}
    }
    
    void pow(ll a[][MAXN],ll k)
    {
    	memset(res,0,sizeof(res));
    	for(int i=1;i<=6;i++)
    	{
    		res[i][i]=1;
    	}
    	
    	while(k)
    	{
    		if(k&1)
    		{
    			mulit(res,a);
    //			printf("k=%lld
    ",k);
    //		printf("res=%lld
    ",res[1][1]);
    		}
    		mulit(a,a);
    		k>>=1;
    	}
    }
    
    int main()
    {
    	ll T;
    	scanf("%lld",&T);
    	while(T--)
    	{
    		scanf("%lld",&n);
    		if(n==1||n==2||n==3)
    		{
    			printf("1
    ");
    			continue;
    		}
    		A[1][1]=1;
    		A[1][2]=0;
    		A[1][3]=1;
    		A[2][1]=1;
    		A[2][2]=0;
    		A[2][3]=0;
    		A[3][1]=0;
    		A[3][2]=1;
    		A[3][3]=0;
    		B[1][1]=1;
    		B[2][1]=1;
    		B[3][1]=1;
    		pow(A,n-2);
    		mulit(res,B);
    		printf("%lld
    ",res[2][1]);
    	}
    }
    

    有一个小结论叫做gcd(F(n),F(m))=F(gcd(n,m))

    P1306 斐波那契公约数
    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define MAXN 9
    #define MOD 100000000
    #define ll long long 
    using namespace std;
    
    ll n,m;
    ll A[MAXN][MAXN];
    ll B[MAXN][MAXN];
    ll res[MAXN][MAXN];
    ll tmp[MAXN][MAXN];
    
    void mulit(ll a[][MAXN],ll b[][MAXN])
    {
    	memset(tmp,0,sizeof(tmp));
    	for(int i=1;i<=5;i++)
    	{
    		for(int j=1;j<=5;j++)
    		{
    			for(int k=1;k<=5;k++)
    			{
    				tmp[i][j]=((a[i][k]*b[k][j])%MOD+tmp[i][j])%MOD;
    			}
    		}
    	}
    	for(int i=1;i<=5;i++)
    	{
    		for(int j=1;j<=5;j++)
    		{
    			a[i][j]=tmp[i][j]; 
    		} 
    	}
    }
    
    void pow(ll a[][MAXN],ll k)
    {
    	memset(res,0,sizeof(res));
    	for(int i=1;i<=5;i++)
    	{
    		res[i][i]=1;
    	}
    	while(k)
    	{
    		if(k&1)
    		{
    			mulit(res,a);
    		}
    		mulit(a,a);
    		k>>=1;
    	}
    }
    
    int main()
    {
    	scanf("%lld%lld",&n,&m);
    	A[1][1]=1;
    	A[1][2]=1;
    	A[2][1]=1;
    	A[2][2]=0;
    	B[1][1]=1;
    	B[2][1]=1;
    	ll num;
    	num=__gcd(n,m);
    //	printf("num=%lld",num);
    	if(num==1||num==2)
    	{
    		printf("1");
    		return 0;
    	}
    	pow(A,num-2);
    	mulit(res,B);
    	printf("%lld",res[1][1]);
    	
    }
    

    乘法逆元

    乘法逆元的几种计算方法
    线性推核心代码如下:

    	inv[1]=1;
    	printf("1
    ");
    	for(int i=2;i<=n;i++)
    	{
    		inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
    		printf("%lld
    ",inv[i]); 
    	}
    

    拓展欧几里得算法求乘法逆元代码如下

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    
    using namespace std;
    
    void exgcd(int a,int b,int &x,int &y)
    {
    	if(b==0)
    	{
    		x=1;
    		y=0;
    		return;
    	}
    	else
    	{
    		int tx,ty;
    		exgcd(b,a%b,tx,ty);
    		y=tx-(a/b)*ty;
    		x=ty;
    		return;
    	}
    }
    
    int main()
    {
    	int n,p;
    	scanf("%d%d",&n,&p);
    	for(int i=1;i<=n;i++)
    	{
    		int x,y;
    		exgcd(i,p,x,y);
    		if(x<0)
    		{
    			x=x+p;
    		 } 
    		 printf("%d
    ",x);
    	}
     } 
    

    费马小定理代码如下:

    ll Pow(ll a, ll k)
    {
    	ll res=1;
    	while(k)
    	{
    		if(k&1)
    		{
    			res=res*a%MOD;
    		}
    		a=a*a%MOD;
    		k>>=1;
    	}
    	return res;
    }
    

    模板题链接:P3811 【模板】乘法逆元
    拓展题链接:P2613 【模板】有理数取余
    拓展题代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    #define MAXN 100010 
    
    
    using namespace std;
    const int MOD=19260817; 
    ll a,b;
    char str[MAXN];
    
    ll Pow(ll a,ll k)
    {
        ll res=1;
        while(k)
        {
            
            if(k&1)
            {
                res=res*a%MOD;
            }
            a=a*a%MOD;
            k>>=1;
        }
        return res;
    }
    
    int main()
    {
        
         scanf( "%s", str+1 );
        for( int i = 1, en = (int)strlen( str+1 ); i <= en; ++i )
            a = ( a * 10 + str[i] - '0' ) % MOD;
        scanf( "%s", str+1 );
        for( int i = 1, en = (int)strlen( str+1 ); i <= en; ++i )
            b = ( b * 10 + str[i] - '0' ) % MOD;
        if(b==0)
        {
            printf("Angry!");
            return 0;
        }
        ll ans=(Pow(b,MOD-2)%MOD*a)%MOD;
        printf("%lld",ans);
     } 
    
  • 相关阅读:
    燕之屋燕窝被指97%为糖水和增稠剂 营养价值不如鸡蛋
    烧烤需要准备些什么东西?烧烤步骤
    烧烤材料清单都有什么?
    西雅图华人码农生存实录
    【读书】李建:《读书的“有用”与“无用”》
    孩子,我为什么要求你读书
    读书和不读书差别在哪里?
    招人不是HR第一职责,留住人才是
    select 可输入的下拉框
    toLocaleString 日期
  • 原文地址:https://www.cnblogs.com/LITTLESUNwl/p/10953125.html
Copyright © 2011-2022 走看看