矩阵乘法回顾


经典例题


网上有什么十大经典例题,但无非3种操作:矩阵的基础运算(例一+加减乘+阶乘),找辅助矩阵解题,与其他知识点结合用作矩阵加速(降复杂度,一般降成logn)


操作一:单单矩阵的基础操作

给定n个点,m个操作,构造O(m+n)的算法输出m个操作后各点的位置。操作有平移、缩放、翻转和旋转,置换

有些模拟题也会用到矩阵的基础操作,但很少这种矩阵运算。

在这里插入图片描述

高阶操作:反复置换,共轭转置,酉矩阵,(半)正定矩阵等等
上述定义


操作二 :阶乘

给定矩阵A,请快速计算出A^n(n个A相乘)的结果,输出的每个数都mod p。
给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值

变式:用1 x 2的多米诺骨牌填满M x N的矩形有多少种方案,M<=5,N<2^31,输出答案mod p的结果。

这种题本质上还是一样的,利用状态转换,画个有向图,然后跟上面的问题一样了。
例题:规定宽度为4,给定长度为n,求用12和21的瓷砖,将其完全铺满能有多少种方法。(链接一,还有杜教BM)
链接二,有推导过程

例如 :二阶矩阵如下

const int N=2;
typedef struct matrix{	int a[N][N];	}mt;
mt muti(mt x,mt y){
	mt z;	memset(z.a,0,sizeof(z.a));
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++)
			for(int k=0;k<N;k++)
				z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod;
	return z;
}
mt qpow(mt x,int k){
	mt y;	memset(y.a,0,sizeof(y.a));
	for(int i=0;i<N;i++)	y.a[i][i]=1;
	while(k){
		if(k&1)	y=muti(y,x);
		k>>=1;	x=muti(x,x);
	}
	return y;
}

一个nm的矩阵乘一个mp的矩阵会得到一个n*p的矩阵

//n*m的矩阵乘 m*p的矩阵 
typedef struct matrix{	int n,m,a[maxn][maxn];	}mt;
mt muti(mt x,mt y){
	mt z;	memset(z.a,0,sizeof(z.a));	
	for(int i=0;i<x.n;i++)
		for(int j=0;j<y.m;j++)
			for(int k=0;k<x.m;k++)
				z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod;
	return z;
}

操作三 :找辅助矩阵
1、专业点说是:用矩阵乘法优化的线性齐次递推公式求值。
2、线性关系推不出的话,可以尝试杜教BM,暴力推出系数,如果推不出,应该不是线性关系。(小编还不会杜教BM)
3、这类题有时也可以用二分,dfs+剪枝,dp等等做。


题型:

1、给定n和p,求第n个Fibonacci数mod p的值,n不超过2^31
普通的有两种方法:详解

往往是变式题,要自己找辅助矩阵
对于f(n)=af(n-1)+bf(n-2)这种递推公式。有一种方法是提特征根方程:讲解
但上面的方法对于特征根是无理数时,无效,无特征根时又要考虑其是否有周期性。而用矩阵乘法写的普适性更强。

找辅助矩阵练习: 
一:求Fibonacci前n项和
矩阵递推式;
Sn = 1 * Sn-1 + 1 * fn + 0 * fn-1;
fn+1 = 0 * Sn-1 + 1 * fn + 1 * fn-1;
fn = 0 * Sn-1 + 1 * fn + 0 * fn-1;
{1 1 0}
{0 1 1}
{0 1 0}

二:求Tn= ∑k*fk mod m;

    原式
    f[i] = f[i-1]+f[i-2]
    T[n] = f[1]+f[2]*2+f[3]*3+...+f[n]*n

    令
        S[n] = f[1]+f[2]+f[3]+...+f[n]
    n*S[n] = n*f[1]+n*f[2]+n*f[3]+...+n*f[n]--> P[n] = n*S[n]-T[n]
    --> P[n] = (n-1)*f[1]+(n-2)*f[2]+...+(n-n)*f[n]
    因为
    --> P[n-1] = (n-1)*S[n]-T[n-1]
    --> P[n-1] = (n-2)*f[1]+(n-3)*f[2]+...+(n-1-(n-1))*f[n-1]--> S[n-1] = f[1]+f[2]+f[3]+....+f[n-1]
    所以
    P[n]=P[n-1]+S[n-1]
    
    P[n]=1*P[n-1]+1*S[n-1]+0*f[n]+0*f[n-1];
    S[n]=0*P[n-1]+1*S[n-1]+1*f[n]+0*f[n-1];
  f[n+1]=0*P[n-1]+0*S[n-1]+1*f[n]+1*f[n-1];
    f[n]=0*P[n-1]+0*S[n-1]+1*f[n]+0*f[n-1];
	    
 	P[i] S[i] f[i] f[i-1]
    
    {1 1 0 0} 
    {0 1 1 0} 
    {0 0 1 1} 
    {0 0 1 0} 
    
    最后 T[n] = n*S[n]-P[n];

2、二分递归或找辅助矩阵
题目大意:给定矩阵A,求A + A^2 + A^3 + … + A^k的结果(两个矩阵相加就是对应位置分别相加)。输出的数据mod m。k<=10^9。
一共有4种解法,归于两大种,年代已久,链接已失(其实是懒得再找了)。

3、求下列函数的整数部分(含根号)在这里插入图片描述

推导过程:矩阵快速幂+共轭复数

然后是通用公式,求整数部分在这里插入图片描述
注意这类都有一个大前提才能共轭构造,见推导过程



总结

还在刷题,这篇还在更新,没什么好总结的。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
矩阵乘法是计算机科学中非常基础的一种算法,它在图像处理、人工智能等领域都有广泛的应用。而分治法是一种常见的算法思想,它可以将一个问题分成多个子问题,再将子问题的结果合并起来得到最终结果。本文将介绍如何使用分治法实现矩阵乘法。 首先,我们来回顾一下矩阵乘法的定义。对于矩阵A和B,它们的乘积C的第i行第j列的元素可以表示为: C[i][j] = sum(A[i][k] * B[k][j]), k = 1,2,...,n 其中n为矩阵的大小。 接下来,我们将使用分治法来实现矩阵乘法。具体思路如下: 1.将矩阵A和B分别划分成4个子矩阵,即A11、A12、A21、A22和B11、B12、B21、B22。 2.递归地计算子矩阵的乘积,得到C11、C12、C21和C22。 3.将C11、C12、C21和C22合并成一个大的矩阵C。 下面是Python代码实现: ```python def matrix_multiply(A, B): # 判断矩阵大小是否相等 assert len(A[0]) == len(B) # 矩阵大小为1x1的情况 if len(A) == 1 and len(A[0]) == 1 and len(B) == 1 and len(B[0]) == 1: return [[A[0][0] * B[0][0]]] # 将矩阵A和B分成4个子矩阵 A11, A12, A21, A22 = split_matrix(A) B11, B12, B21, B22 = split_matrix(B) # 递归地计算子矩阵的乘积 C11 = matrix_add(matrix_multiply(A11, B11), matrix_multiply(A12, B21)) C12 = matrix_add(matrix_multiply(A11, B12), matrix_multiply(A12, B22)) C21 = matrix_add(matrix_multiply(A21, B11), matrix_multiply(A22, B21)) C22 = matrix_add(matrix_multiply(A21, B12), matrix_multiply(A22, B22)) # 合并C11、C12、C21和C22成一个大的矩阵C return merge_matrix(C11, C12, C21, C22) def split_matrix(matrix): # 将矩阵按行、列均分为两个子矩阵 n = len(matrix) m = len(matrix[0]) A = [[matrix[i][j] for j in range(m // 2)] for i in range(n // 2)] B = [[matrix[i][j] for j in range(m // 2, m)] for i in range(n // 2)] C = [[matrix[i][j] for j in range(m // 2)] for i in range(n // 2, n)] D = [[matrix[i][j] for j in range(m // 2, m)] for i in range(n // 2, n)] return A, B, C, D def merge_matrix(A, B, C, D): # 将四个子矩阵合并成一个大的矩阵 n = len(A) + len(C) m = len(A[0]) + len(B[0]) matrix = [[0] * m for i in range(n)] for i in range(len(A)): for j in range(len(A[0])): matrix[i][j] = A[i][j] for i in range(len(C)): for j in range(len(C[0])): matrix[i + len(A)][j] = C[i][j] for i in range(len(B)): for j in range(len(B[0])): matrix[i][j + len(A[0])] = B[i][j] for i in range(len(D)): for j in range(len(D[0])): matrix[i + len(A)][j + len(A[0])] = D[i][j] return matrix def matrix_add(A, B): # 矩阵加法 n = len(A) m = len(A[0]) matrix = [[0] * m for i in range(n)] for i in range(n): for j in range(m): matrix[i][j] = A[i][j] + B[i][j] return matrix ``` 可以使用以下代码进行测试: ```python A = [[1, 2], [3, 4]] B = [[5, 6], [7, 8]] C = matrix_multiply(A, B) print(C) # [[19, 22], [43, 50]] ``` 上面的代码实现了分治法实现矩阵乘法的基本思路,但是它的时间复杂度依然是O(n^3),因为我们在合并子问题的结果时需要遍历整个矩阵。实际上,我们可以在递归计算子问题时将子矩阵的结果直接传递到合并函数中,这样可以避免重复计算,从而将时间复杂度优化到O(n^2.81)。感兴趣的读者可以自行了解 Strassen 算法的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值