矩阵快速幂算法

矩阵快速幂算法

矩阵快速幂的本质就是 矩阵+快速幂,思路没什么变化。
快速幂的思路见之前的 快速幂介绍,这里就不多说了。
至于矩阵的话,知道矩阵是啥,怎么算乘法就可以了。

相关矩阵基础

注:已经了解矩阵的可以跳过本部分内容。

简单来说 n ∗ m n*m nm 的矩阵就是个 n n n m m m 列 的大方块。
矩阵要进行乘法运算必须保证第一个矩阵的列数=第二个矩阵的行数。
也就是说 n 1 ∗ m 1 n_1 * m_1 n1m1 的矩阵 a 与 n 2 ∗ m 2 n_2 * m_2 n2m2 的矩阵 b 相乘,需要保证 m 1 = n 2 m_1=n_2 m1=n2 最终得到的结果为 n 1 ∗ m 2 n_1*m_2 n1m2 的矩阵 c。
对于得到的矩阵 c ,它每个位置的结果为第一个矩阵的第 i i i 行 与第二个矩阵的第 j j j 列相乘求和得到。
即结果矩阵中 i i i j j j 列的元素 c [ i ] [ j ] = ∑ k = 1 m 1 a [ i ] [ k ] ∗ b [ k ] [ j ] c[i][j]=\sum_{k=1}^{m_1}a[i][k]*b[k][j] c[i][j]=k=1m1a[i][k]b[k][j]

具体例子:

[ 1 2 ] ∗ [ 1 2 3 4 ] = [ 1 ∗ 1 + 2 ∗ 3 1 ∗ 2 + 2 ∗ 4 ] = [ 7 6 ] \begin{bmatrix} 1 & 2 \end{bmatrix}* \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix}= \begin{bmatrix} 1*1+2*3 & 1*2+2*4 \end{bmatrix}= \begin{bmatrix} 7 & 6 \end{bmatrix} [12][1324]=[11+2312+24]=[76]

矩阵快速幂

只需要在原来快速幂的基础上,把数字换成矩阵即可。
以下为原版整数的快速幂:

#include 
using namespace std;

int main()
{
	long long a,b,c,ans;
	while(cin>>a>>b>>c)
	{
		ans=1;
		while(b>0)
		{
			if(b&1)ans=ans*a%c;
			a=a*a%c;
			b>>=1;
		}
		cout<<ans<<endl;
	}
	return 0;
}

代码以洛谷 P3390 模板题为例,关键部分在 qpow 函数中。
为保证原有结构尽可能不被修改,矩阵快速幂代码矩阵乘法部分放在重载运算符中。
Matrix::unit 为单位矩阵,这里在读入数据时顺便初始化了,也可以在 qpow 中进行初始化。

#include 
using namespace std;
typedef long long ll; 

ll n,k,mod=1e9+7;
class Matrix
{
public:
	static Matrix unit;
	ll data[100][100];
	int n,m;
	
	Matrix operator *(Matrix &mat)
	{
		Matrix tmp=Matrix::unit;
		tmp.n=this->n;
		tmp.m=mat.m;
		for(int i=0;i<this->n;i++)
		{
			for(int j=0;j<mat.m;j++)
			{
				tmp.data[i][j] = 0;
				for(int k=0;k<this->m;k++)
				{
					tmp.data[i][j] += this->data[i][k]*mat.data[k][j];
					tmp.data[i][j] %= mod;
				}
			}
		}
		return tmp;
	}
	
	void print()
	{
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				if(j!=0)cout<<" ";
				cout<<this->data[i][j];
			}
			cout<<"\n";
		}
	}
};

Matrix Matrix::unit;
Matrix mat;

Matrix qpow(Matrix m1,ll b,ll mod)
{
	Matrix ans=Matrix::unit;
	while(b>0)
	{
		if(b&1)ans=ans*m1;
		m1=m1*m1;
		b>>=1;
	}
	return ans;
}

int main()
{
	cin>>n>>k;
	mat.n=mat.m=n;
	Matrix::unit.n=Matrix::unit.m=n;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<n;j++)
		{
			cin>>mat.data[i][j];
		}
		Matrix::unit.data[i][i]=1;
	}
	mat=qpow(mat,k,mod);
	mat.print();
	return 0;
}

补充说明:
如果题目涉及多次取模,建议将矩阵乘法写成 Matrix 的成员函数或者单独一个乘法函数。

应用:快速计算斐波那契数列

斐波那契数列定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
如果需要求的 n 值很大,可能会在递推时超时 ,此时可以利用矩阵快速幂求解。
首先将递推式构造成矩阵形式:
[ F ( n ) F ( n − 1 ) ] = [ 1 1 1 0 ] ∗ [ F ( n − 1 ) F ( n − 2 ) ] \begin{bmatrix} F(n) \\ F(n-1) \end{bmatrix}= \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}* \begin{bmatrix} F(n-1) \\ F(n-2) \end{bmatrix} [F(n)F(n1)]=[1110][F(n1)F(n2)]
这样计算数列的第 n 项时可以利用中间矩阵的 n-1 次幂乘上数列的前两项:
[ F ( n ) F ( n − 1 ) ] = [ 1 1 1 0 ] n − 1 ∗ [ F ( 1 ) F ( 0 ) ] \begin{bmatrix} F(n) \\ F(n-1) \end{bmatrix}= \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^{n-1}* \begin{bmatrix} F(1) \\ F(0) \end{bmatrix} [F(n)F(n1)]=[1110]n1[F(1)F(0)]
因为 F(0)=0 F(1)=1,所以只需要快速计算中间的矩阵,矩阵的第一行第一列位置就是要计算的 F(n)。
由于快速幂文章中介绍的模运算性质,如果题目需要计算时需要对斐波那契数列取模,那么可以直接在矩阵运算过程中进行取模。
题目可以参考 NEFU OJ 2348 小蓝与斐波拉契数列

你可能感兴趣的:(ACM,矩阵,算法,矩阵快速幂)