今天稍微学习了一下矩阵乘法和应用,我线代也不是很好。。。
矩阵的定义和求解方法在线性代数中已经有所定义,我们在这里采用不太严谨的定义,矩阵就是一个有行有列的数表,体现在计算机中就是二维数组。
class Mat
{
public:
int mat[15][15];
};
这个数表和那个数表可以相加,可以相乘,数表自身也可以求幂。
单位阵定义:主对角线为1,其余都是0的特殊矩阵,线代中用E表示。
void initE()
{
for(int i=0;i<15;i++)
E.mat[i][i]=1;
}
1、加法:很简单,这个矩阵的对应行列加那个矩阵的对应行列。
Mat operator+(Mat a,Mat b)
{
Mat c;
int i,j;
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
c.mat[i][j] = a.mat[i][j]+b.mat[i][j];
}
return c;
}
2、乘法:也不难,第一个矩阵的行乘以第二个矩阵的列对应的值,然后求和作为新的矩阵的总值(
只有矩阵A的行等于矩阵B的列时,才能相乘,否则无法相乘,这也是为什么相乘没有交换律的原因,交换了行不一定等于列了)。
举个例子,一看就明白是怎么个行乘列相加了。。
重要性质:矩阵乘法没有交换律,但是有结合律。
在计算机中求解主要是采用朴素的O(n^3)方法,因为再怎么优化最好的目前算法只能到O(n^2.3)。。。就不费这个劲了。
Mat operator*(Mat a,Mat b)
{
int i,j,k;
Mat c;
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
{
c.mat[i][j] = 0;
for (k=0;k<n;k++)
{
c.mat[i][j]+=(a.mat[i][k]*b.mat[k][j]);
}
//c.mat[i][j]%=MOD;
}
}
return c;
}
3、求幂:这个要好好讲讲。
定义的话很简单,A的平方就相当于矩阵乘法中的A*A,立方就是A*A*A。。如果直接用定义在计算机中求解的话,效率为O((n^3)*k),实在太慢,那应该怎么办呢。
可以根据结合律进行二分加速。比如求A^6=A*A*A*A*A*A,需要5次。
也可以 => (A*A)*(A*A)*(A*A)
这样变的好处是,你只需要计算一次A*A,然后将结果(A*A)连乘自己两次就能得到A^6,即(A*A)^3=A^6。算一下发现这次一共乘了3次,少于原来的5次,效率变成(lgk*N^3)。
奇数的话手动乘一次之后变成偶数了再二分。有些代码采用了位运算,优化少许时间而已(x&1就是x%2==1,x>>=1,就是x/=2)。在这里给出正常点的代码。
Mat operator^(Mat a,int x)
{
Mat p = E,q = a;
while (x>=1)
{
if(x%2==1)
p = p*q;
x/=2;
q = q*q;
}
return p;
}
4、有些情况下让你求A^1+A^2+...+A^k,依然可以二分加速,二次二分效率更省。
Mat solve(Mat a,int p)
{
if(p==1)
return a;
else if(p&1)
return (a^p)+solve(a,p-1);
else
return ((a^(p>>1))+E)*solve(a,p>>1);
}
计算log(n)个A^k即可。