全网最最最最最详细的c++算法讲解(二)矩阵乘法及相关知识的介绍

hello!

这里是y_immortal noip2017省一蒟蒻QWQ分特别低

ps:这篇博文里有很多内容是源自我的一位学长的课件

感觉已经把信竞博客写成数学博客了


首先

什么是矩阵?

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


这m×n 个数称为矩阵A的元素,aij位于矩阵A的第i行第j列。


矩阵的一些基本概念

相等  A=B

当A和B的行数,列数都相等,且每一行和每一列的元素都对应相等的时候,我们称A和B是相等的矩阵


元素  

矩阵中的每一个数都是一个元素,其实A的第i行第j列的元素可以写作entry(A,i,j)


对角线元素

顾名思义,就是对角线上的元素咯,也就是 entry(A,i,i)。


零矩阵   O

所有元素都是0的矩阵


矩阵的迹 

表示A的主对角线上的元素之和,记为tr(A),tr(A)=sigma(entry(A,i,i))


向量
行向量、列向量

emmmm 相信各位都是高中的人士?如果没学过向量的话,可以把向量看成......带方向的线段?嗯 差不多

相信大家平常接触的都是二维向量例如(x,y)一类的


而在矩阵中

我们把一个1*n的矩阵看成一个n维的向量(x1,x2,x3,x4....,xn)

把一个n*1看成一个n维的列向量(y1,y2,y3,y4.......yn)


同时科普一个知识:

两个向量的内积,是 二者对应的各维元素的乘积之和  例如向量(x1,y1)和(x2,y2)的内积就是x1*x2+y1*y2


好啦!

这里正式介绍矩阵乘法,A*B=C

则C矩阵中的第i行第j列元素,就是A矩阵的第i个行向量和B的第j个列向量的内积

举个例子,字丑勿喷~



那么我们显而易见一个很重要的结论:

只要当A矩阵为x*y,B矩阵的乘法y*z时,二者才可以进行乘法

并且乘出的C矩阵是一个x*z的矩阵


那....嗯 估计差不多了,权当大家理解的矩阵乘法。

要是不理解的话,去网上找几道练习题 很快就熟练掌握了


矩阵的一些基本性质 ~这里不作过多解释了,



有一些要注意的问题:

(1)矩阵乘法不满足交换律(因为交换后,可能无法进行乘法,详解请看上面那个显而易见的结论

(2)AC=O不能推出A=O或C=O(相加后为零,并不代表最初是0矩阵)

(3)AB=AC,A≠O不能推出B=C  (emmmm 我也不知道怎么解释,找一个反例就是了)

(4)(A+B)^2=A^2+AB+BA+B^2≠A^2+2AB+B^2(因为不满足交换律~)

(5)朴素的矩阵乘法是O(n^3)的

(6)与所有n阶矩阵乘法可交换的矩阵是纯量阵(哈哈哈,这条我也不知道啥意思~)


这时候再看矩阵乘法的代码 有没有清晰一点点?

struct Ju{
	int a[5][5];
	int x,y;
	Ju operator* (Ju b)
	{
		Ju ans;
		memset(ans.a,0,sizeof(ans.a));
		ans.x=x;
		ans.y=b.y;
		for(int i=0;i<=ans.x;i++)
			for(int j=0;j<=ans.y;j++)
				for(int k=0;k<=y;k++)
					ans.a[i][j]=(ans.a[i][j]+a[i][k]*b.a[k][j])%mod;
		return ans;
	}
};

Ju aa,b,c;
void kpow(Ju a,int b)
{
	Ju ans;
	memset(ans.a,0,sizeof(ans.a));
	for (int i=1;i<=a.x;i++) ans.a[i][i]=1;
	ans.x=a.x;
	ans.y=a.y;
	while (b)
	{
		if (b&1) ans=ans*a;
		a=a*a;
		b>>=1;
	} 
	aa=aa*ans;
} 


这里附上一道矩阵乘法的经典例题 poj3070

大致题意是:

求Fibonacci数列的第n项
F0=1
F1=1

Fn=Fn-1+Fn-2   (n>=2)


n<=1000000000


一看这个复杂度 很容易就想到矩阵快速幂了QWQ (因为别的算法跑不过呀)

那么,我们该怎么建立联系呢?




那么我们就可以从最基本的想,f0,f1如何转移到f1,f2呢?

经过Fn=Fn-1+Fn-2 我们可以发现,f1=f1,而f2=f1+f0 这个式子 很像一个内积哦

好了

那么A矩阵就是

0 1

1 1了

构造完成。

那么怎么快速计算A的n次方? ——就是快速幂了啦~

时间复杂度O(2^3 log n)

下面附上代码

#include<iostream>

#include<algorithm>

#include<cstdio>

#include<cstring>

using namespace std;

const int maxn = 110;

const int mod = 10000;

int n;

struct Ju{
	int a[5][5];
	int x,y;
	Ju operator* (Ju b)
	{
		Ju ans;
		memset(ans.a,0,sizeof(ans.a));
		ans.x=x;
		ans.y=b.y;
		for(int i=0;i<=ans.x;i++)
			for(int j=0;j<=ans.y;j++)
				for(int k=0;k<=y;k++)
					ans.a[i][j]=(ans.a[i][j]+a[i][k]*b.a[k][j])%mod;
		return ans;
	}
};

Ju aa,b,c;
void kpow(Ju a,int b)
{
	Ju ans;
	memset(ans.a,0,sizeof(ans.a));
	for (int i=1;i<=a.x;i++) ans.a[i][i]=1;
	ans.x=a.x;
	ans.y=a.y;
	while (b)
	{
		if (b&1) ans=ans*a;
		a=a*a;
		b>>=1;
	} 
	aa=aa*ans;
}
int main()
{
	while (1)
	{
	  aa.x=1;
	  aa.y=2;
	  aa.a[1][1]=1;
	  aa.a[1][2]=1;
	  c.x=2;
	  c.y=2;
	  c.a[1][1]=0;
	  c.a[1][2]=1;
	  c.a[2][1]=1;
	  c.a[2][2]=1;
	  scanf("%d",&n);
      if (n==-1) return 0;
	  if (n==0) {
	  	 printf("0\n");
	  }
	  else
	  {
	  	 kpow(c,n-2);
	  	 printf("%d\n",aa.a[1][2]);
	  }
}
}

早年码风,比较清奇


好啦,矩阵乘法就介绍这么多。

如果您们能从我的博客得到任何一点收获,对我来说都是莫大的荣幸


共勉!

from y_immortal



猜你喜欢

转载自blog.csdn.net/y752742355/article/details/79933759