C++矩阵及其加速—————求斐波拉契数列第n项讲解

版权声明:蒟蒻原创博客,大佬转载也需附上链接: https://blog.csdn.net/weixin_43810158/article/details/88947288

前言:

也许你只是不小心点入了此博客,为了不眛自己的良心,首先我们会介绍什么是矩阵。

概念:

在数学中,矩阵(Matrix)是一个按照长方阵列排列的实数或复数集合,最早来自于方程组的系数及常数所构成的方阵。

由m×n个数aij排成的m行n列的数表称为m行n列的矩阵,简称m×n矩阵。记作:

而矩阵中的各个元素就是元。

元素是实数的矩阵称为实矩阵,元素是复数的矩阵称为复矩阵。而行数与列数都等于n的矩阵称为n阶矩阵或n阶方阵。n阶方阵中所有i=j的元素aij组成的斜线称为(主)对角线,所有i+j=n+1的元素aij组成的斜线称为辅对角线。

矩阵的运算:

一、加法

矩阵的加法就是两个同行同列的矩阵中的各个对应元素相加,即c[i][j]=b[i][j]+a[i][j]

当然加法也适用运算律。

交换律:A+B=B+A

结合律:A+(B+C)=(A+B)+C

二、减法

矩阵的减法与加法类似,也是两个同行同列的矩阵中的各个对应元素相减,即c[i][j]=b[i][j]-a[i][j]

三、数乘

矩阵的数乘就是一个矩形中的每个元素与数相乘,即c[i][j]=a[i][j]*n

数乘也有运算律:

分配律:\lambda (A+B)=\lambda A+\lambda B

结合律:\lambda \mu A=\lambda( \mu A )

三、转置

矩阵的转置就是一个矩阵的行转成列,列转成行,即:

\begin{bmatrix} 2 & 4& 8\\ 5&1 & 7 \end{bmatrix}^{T}=\begin{bmatrix} 5 & 2\\ 1& 4\\ 7& 8 \end{bmatrix}

而矩阵的运算还有共轭和共轭转置,在此就不必多讲。

矩阵的乘法:

也许你会看到题目会有些迷惑,为什么还要讲矩阵的乘法,数乘不就是吗?

开始我也是十分的迷。

现在我才知道这是矩阵乘以矩阵。

但是该怎么算呢?

两个矩阵的乘法仅当第一个矩阵A的列数和第二个矩阵B的行数相等时才能定义(做乘法)。如A是m×n矩阵,B是n×p矩阵,它们的乘积C是一个m×p矩阵C=(cij),它的任意一个元素值为:

矩阵的乘法运算满足结合律、左分配率、右分配律,但是不满足交换律。即:

 (AB)C=A(BC),

(A+B)C=AC+BC,

C(A+B)=CA+CB,

AB不一定能BA(除非行列完全一样)。

而代码就有两种了:

朴素法:

#include<cstdio>
#include<iostream>
int an,a[105][105],b[105][105],c[105][105],am,bn,bm;
int main()
{
    scanf("%d%d",&an,&am);
    for(int i=1;i<=an;i++)
        for(int j=1;j<=am;j++)
            scanf("%d",&a[i][j]);
    bn=am;
    scanf("%d",&bm);
    for(int i=1;i<=bn;i++)
        for(int j=1;j<=bm;j++)
            scanf("%d",&b[i][j]);
    for(int i=1;i<=an;i++)
        for(int j=1;j<=bm;j++)
            for(int k=1;k<=am;k++)
                c[i][j]+=a[i][k]*b[k][j];
} 

结构体重载运算符法(装B法):

node operator*(const node &a)
{
        node r;
        r.n=n;
        r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]=(r.c[i][j]+(c[i][k]*a.c[k][j])%mod)%mod;
        return r;
}

矩阵的行列式:

一个n×n的方阵A的行列式记为det(A)或者|A|,一个2×2矩阵的行列式可表示如下:

把一个n阶行列式中的元素aij所在的第i行和第j列划去后,留下来的n-1阶行列式叫做元素aij的余子式,记作Mij。记Aij=(-1)i+jMij,叫做元素aij的代数余子式。例如:

一个n×n矩阵的行列式等于其任意行(或列)的元素与对应的代数余子式乘积之和,即:

题目描述:

输入:

输入n,m。

输出:

输入样例:

5 1000

输出样例:

5

思路分析:

由题目我们可知,这一题使用的是矩阵加速,而矩阵加速究竟是个什么东西?

我们首先要知道 矩阵A × 矩阵B = 矩阵C,矩阵C一定是A的行B的列;如果我们构造一个矩阵B,它的行列数恰好是矩阵A列数的方阵,那么矩阵C的行列数一定和矩阵A完全一样!

比如:矩阵A是(m行n列),矩阵B是(n行n列),那么相乘之后,矩阵C一定是(m行n列)。

那我们就可以构造A,B矩阵来求答案了。

A为[f_{1}\;\;f_{2}],B为\begin{bmatrix} 0 & 1\\ 1& 1 \end{bmatrix}

再用快速幂就可以求出答案,答案在A[1][2];

代码实现:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
ll n1,mod;
struct node{
    ll n,m,c[10][10];
    node ()
    {
        memset(c,0,sizeof(c));
    }
    node operator*(const node &a)
    {
        node r;
        r.n=n;
        r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]=(r.c[i][j]+(c[i][k]*a.c[k][j])%mod)%mod;
        return r;
    }
}b,f1;
node qkpow(node a,ll p)
{
    node res;
    res.n=res.m=2;
    for(int i=1;i<=res.n;i++)
        res.c[i][i]=1;
    while(p)
    {
        if(p&1)
            res=res*a;
        a=a*a;
        p/=2;
    }
    return res;
}
int main()
{
    scanf("%lld%lld",&n1,&mod);
    b.n=2;
    b.m=2;
    b.c[1][2]=1;
    b.c[2][1]=1;
    b.c[2][2]=1;
    node t;
    t=qkpow(b,n1-2);
    f1.n=1;
    f1.m=2;
    f1.c[1][1]=1;
    f1.c[1][2]=1;
    f1=f1*t;
    printf("%lld",f1.c[1][2]);
}

猜你喜欢

转载自blog.csdn.net/weixin_43810158/article/details/88947288