最近在Python程序设计中遇到一道设计矩阵计算类的题目,原题目要求计算矩阵加和和矩阵乘积,而我出于设计和挑战自己的目的,为自己增加难度,因此设计出矩阵计算类,不仅可以求出矩阵加和和矩阵乘积,还能计算出矩阵转置、矩阵行列式值、伴随矩阵和逆矩阵。在此和大家分享一下,如有不足之处请多多指教。
矩阵计算类中最普遍使用的是列表的方法,由于数据结构还在学习,所以我只使用简单的列表方法来实现。其中我设计了两个类,一个是父类matrix,一个是子类matrixcalcu,采用单继承。父类包括了构造函数以及矩阵输入打印函数,在子类中,构造函数没有重写,而是包括矩阵的各类计算函数以及析构函数。
父类matrix的定义如下:
class matrix:
def __init__(self,i,j,d,r,matrix,matrixo): #构造函数,说明属性
self.i = i #matrix列
self.j = j #matrix行
self.d = d #matrixo列
self.r = r #matrixo行
self.matrix = matrix
self.matrixo = matrixo
def scanf(self,matrixes,m,n): #定义矩阵打印函数
self.matrixes = matrixes
self.m = m
self.n = n
matrain = []
if self.n>len(self.matrixes):
print('Error.')
else:
for s in range(self.m): #对每一行
d=self.matrixes[self.n*s:self.n*(s+1)] #用切片提取出每一行的行向量
matrain.append(d)
matrains = np.array(matrain) #采用数组形式表示
return matrains
父类构造函数里面定义了两个计算对象,以及他们的行列值。而输入打印函数scanf是将输入列表转化为矩阵的形式,通过切片方法提取出行向量,再附加到空列表中,为了方便接下来调用矩阵元素,我是用numpy中的array方法,将其以数组的形式打印出来。
接下来是子类matrixcalcu里面的矩阵计算函数,在矩阵乘积中需要判断左右矩阵的列、行是否相等,因此设计行列属性判断函数Victium:
class matrixcalcu(matrix): #继承
def Victium(self): #行列属性判断函数,用于矩阵乘积
if self.i == self.r:
return 0
else:
return 1
矩阵加和函数就比较简单了,只需要将矩阵之间对应元素相加即可:
def Add(self): #矩阵加和函数
if len(self.matrix)!=len(self.matrixo): #维度判断
print('Error.')
else:
res =[]
for n in range(len(self.matrix)):
resma= self.matrix[n]+self.matrixo[n] #对应元素相加
res.append(resma) #计算结果作为元素载入列表
self.matrixes = res
resend = self.scanf(self.matrixes,self.j,self.i) #打印结果
return resend
矩阵转置函数需要根据转置时索引的变化规律来设计for循环,同时注意到,非方阵在转置后行列属性颠倒,因此不可调用scanf函数进行打印,需要重写打印函数:
def Transpose(self,matrixess,vitr,vith): #转置函数
self.matrixess = matrixess
self.vitr = vitr #行
self.vith = vith #列
mid=[]
Tran = []
if self.i>len(self.matrix): #转置条件判断
print('Error.')
else:
for s1 in range(self.vith):
for s2 in range(self.vitr):
midd = self.matrixess[s1+self.vith*s2] #在两层for循环中,依次提取每列行 #索引一样的元素,作为新的列向量的元素
mid.append(midd)
self.matrixes = mid
for s in range(self.vith): #由于转置后行列数颠倒,因此打印时for #循环的条件也要变化,即行变为列
midout=self.matrixes[self.vitr*s:self.vitr*(s+1)] #输出的列用切片提取
Tran.append(midout)
Trans = np.array(Tran)
return (Trans)
而对于矩阵乘积函数即点乘函数,需要考虑到左乘和右乘的问题,利用if条件语句即可分开,然后把左乘或者右乘的选择作为函数参数,当然,不同维度的矩阵相乘也放在左乘的计算程序里面。开始时需要调用Victium函数进行矩阵乘积条件判断:
#点乘函数分左乘和右乘,普通乘包括在左乘中,点乘函数设计思路是调用转置函数
#Transpose将右矩阵转置,然后左矩阵和右矩阵的转置中各行向量之间进行对应元
#素相乘后求和,作为结果矩阵的元素。
def Pointmul(self,LR): #点乘函数,参数LR即选择左乘(包括普通乘)或右乘
self.LR = LR
judge = self.Victium()
pointmul = []
pandas = []
if judge == 1: #点乘条件判断
print("Error.")
else:
matranspose = self.Transpose(self.matrix,self.j,self.i)
matransposeo = self.Transpose(self.matrixo,self.r,self.d)
matrain = self.scanf(self.matrix,self.j,self.i)
matraino = self.scanf(self.matrixo,self.r,self.d)
pointadd = 0
if LR == 'left' or LR == 'not equal': #左乘(matrix*matrixo)或者普通乘法 #(非方阵相乘)
for p1 in range(self.j): #第一层for循环是matrix中行向量的索引
for p2 in range(self.d): #第二层for循环是matrixo的转置中行向量的索引
for p3 in range(self.i): #第三层for循环是每个行向量中元素值的索引
point = (matrain[p1])[p3]*(matransposeo[p2])[p3]
pointadd=pointadd+point
pointmul.append(pointadd)
pointadd = 0
elif LR == 'right': #右乘(matrixo*matrix)
for p1 in range(self.j): #第一层for循环是matrixo中行向量的索引
for p2 in range(self.d): #第二层for循环是matrix的转置中行向量的索引
for p3 in range(self.i): #第三层for循环是行向量中元素值的索引
point = (matraino[p1])[p3]*(matranspose[p2])[p3]
pointadd=pointadd+point
pointmul.append(pointadd)
pointadd = 0
else: #遇到恶意输入,显示出错
print("Error.")
self.matrixes = pointmul
for q in range(self.j): #若左右矩阵行列数不同,则需要更换for循环条件
panda=self.matrixes[self.d*q:self.d*(q+1)] #输出的列用切片提取
pandas.append(panda)
pandaso = np.array(pandas)
return (pandaso)
求矩阵行列式的值函数,需要判断矩阵是否为方阵,然后该函数最多可计算到四维方阵,计算时,采用代数余子式的方式求解行列式值,并统一以第一行元素来求:
#矩阵行列式的值函数,采用的是计算第一行元素的代数余子式与其乘积再求和的方法求解
def Det(self,matrixd,vit): #矩阵行列式的值函数(仅限三维方阵和四维方阵)
self.matrixd = matrixd
self.vit = vit
matrixding = self.scanf(self.matrixd,self.vit,self.vit)
row = list(range(self.vit)) #行数变化
del row[0] #因为求第一行的元素行列式,所以row=[1,2]
rest = []
det = 0
if (self.i !=self.j or self.d !=self.r) and(self.vit**2 !=len(self.matrixd)):
print("Error.")
elif self.vit == 3: #三维方阵
for v in range(self.vit): #求取代数余子式,对与第一行的每个元素
for w in row : #对于除第一行外的每一行,即索引[1]和索引[2]
s = np.delete(matrixding[w],v) #删除第v个元素
rest.append(s) #构成该元素代数余子式元素行向量
cdet =self.matrixd[v]* ((-1)**v)*(rest[0][0]*rest[1][1]-rest[0][1]*rest[1][0 ]) #计算代数余子式和元素之积
det = det+cdet
rest = [] #rest必须清空,重新装填下一个代数余子式元素行向量
elif self.vit == 4: #四维方阵
row = list(range(self.vit))
del row[0]
matrixcopy = self.matrixd[:] #由于接下来要调用Det函数,因此原列表matrixd需要拷贝
for v in range(self.vit):
for w in row :
s = np.delete(matrixding[w],v)
for I in range(3): #由于接下来要调用Det函数,因此rest必须是列表形式
rest.append(s[I]) #用for循环将获取的元素附加到rest空列表中,而不是 #保留数组形式
detn = self.Det(rest,3) #类似递归调用,调用自身的Det函数,求取代数余子式值
cdet = matrixcopy[v]*((-1)**v)*detn #此时,matrixd的内容已经变化,需要用拷贝 #的matrixcopy来调用第一行元素
det = det+cdet
rest = [] #rest清空,理由同上
else:
print("Error.") #遇到恶意输入,显示出错
return det
对于伴随矩阵,是由原先矩阵的各个元素的代数余子式作为其元素值,同时在位置上进行转置,计算时,不仅要考虑Det函数里面计算代数余子式的方法和思路,还要考虑换行,一共需要三个for循环,最后调用Transpose函数将矩阵转置,得到伴随矩阵。
#伴随矩阵函数,设计思路是参考Det函数,计算代数余子式,并再次调用Transpose函数转置
def Accompany(self,matrixf,vit): #伴随矩阵函数
self.matrixf = matrixf
self.vit = vit
matrixfing = self.scanf(self.matrixf,self.vit,self.vit)
fish = []
fishparent = []
if (self.i !=self.j or self.d !=self.r) and(self.vit**2 !=len(self.matrixf)): #伴随矩阵条件判断
print("Error.")
elif self.vit == 3: #三维方阵
row = list(range(self.vit))
for J in range(self.vit): #第一层for循环是随行变化,求完一行元素的代数余子式,进行下一行
f = row[J] # 将row[J]赋值与f,便于row最后恢复
del row[J] #删除对应的行,输出依次为[1,2],[0,2],[0,1]
for v1 in range(self.vit): #第二层for循环是每一行的元素的索引
for w1 in row: #第三层for循环是除该行外的另外两行
sg = np.delete(matrixfing[w1],v1) #将该两行进行代数余子式元素求取, #装入fish列表中
fish.append(sg)
fisher = ((-1)**(v1+w1))*(fish[0][0]*fish[1][1]-fish[0][1]*fish[1][0]) #计算代数余子式
fishparent.append(fisher)
fish = [] #fish列表清空,装载下一行元素的代数余子式,至此 #完成一行的元素的代数余子式
row.insert(J,f) #恢复row,将f插入到row中,row恢复为[0,1,2]
fishg = self.Transpose(fishparent,self.vit,self.vit)
#将fishparent转置,fishg为原矩阵的伴随矩阵
elif self.vit == 4: #四维方阵
row = list(range(self.vit))
for J in range(self.vit):
f = row[J]
del row[J]
for v1 in range(4):
for w1 in row:
sg = np.delete(matrixfing[w1],v1) #至此求解方法和三维矩阵一样
for I in range(3):
fish.append(sg[I]) #由于下面计算代数余子式的值要调用Det函数,因 #此fish必须为列表
harry = self.Det(fish,3) #调用Det函数计算代数余子式
fisher = ((-1)**(v1+w1))*harry
fishparent.append(fisher)
fish = []
row.insert(J,f)
fishg = self.Transpose(fishparent,4,4)
else:
print("Error.") #遇到恶意输入,显示出错
return fishg
最后计算逆矩阵,根据逆矩阵计算公式,如果有原矩阵的伴随矩阵及其行列式的值,就可以得到该矩阵的逆矩阵了,直接调用Accompany函数和Det函数即可:
#逆矩阵函数,有了伴随矩阵函数Accompany和矩阵行列式值函数Det,逆矩阵函数就直接调用这两个函数即
#可简单实现
def Reverse(self,matrixh,viti): #逆矩阵函数
self.matrixh = matrixh
self.viti = viti
accompanison = self.Accompany(matrixh,viti) #该矩阵的伴随矩阵accompansion
detison = self.Det(matrixh,viti) #该矩阵的行列式的值detison
reversion = []
for u in range(viti):
for t in range(viti):
end = accompanison[u][t]/detison #求解逆矩阵每个元素的值
reversion.append(end)
reverse = self.scanf(reversion,viti,viti) #逆矩阵打印
return reverse
最后是析构函数:
def __del__(self): #析构函数
print("Matrix's task is finished.")
在主函数中调用矩阵计算类的各个方法进行验证:
if __name__ == '__main__':
S1 = [1,2,3,4,5,3,2,3,4]
S2 = [0,9,3,2,6,1,8,3,0]
S3 = [0,9,3,2,6,1,8,3,0,4,4,6,0,9,1,1]
D1 = matrix(3,3,3,3,S1,S2)
D2 = matrix(3,3,3,3,S2,S1)
S = matrixcalcu(3,3,3,3,S1,S2)
print("矩阵1:\n",D1.scanf(S1,3,3))
print("矩阵2:\n",D2.scanf(S2,3,3))
print("矩阵加和:\n",S.Add())
print("矩阵1转置:\n",S.Transpose(S1,3,3))
print("矩阵2转置:\n",S.Transpose(S2,3,3))
print("矩阵S3转置:\n",S.Transpose(S3,4,4))
print("矩阵左乘积:\n",S.Pointmul('left'))
print("矩阵右乘积:\n",S.Pointmul('right'))
print("矩阵1行列式值:\n",S.Det(S1,3))
print("矩阵S3行列式值:\n",S.Det(S3,4))
print("矩阵1的伴随矩阵:\n",S.Accompany(S1,3))
print("矩阵2的伴随矩阵:\n",S.Accompany(S2,3))
print("矩阵S3的伴随矩阵:\n",S.Accompany(S3,4))
print("矩阵1的逆矩阵:\n",S.Reverse(S1,3))
print("矩阵2的逆矩阵:\n",S.Reverse(S2,3))
del S
通过设置三个列表S1、S2、S3作为测试对象,来验证矩阵计算类,其中S1、S2为含9个元素的列表,S3是含16个元素的列表,输出结果如下:
矩阵1:
[[1 2 3]
[4 5 3]
[2 3 4]]
矩阵2:
[[0 9 3]
[2 6 1]
[8 3 0]]
矩阵加和:
[[ 1 11 6]
[ 6 11 4]
[10 6 4]]
矩阵1转置:
[[1 4 2]
[2 5 3]
[3 3 4]]
矩阵2转置:
[[0 2 8]
[9 6 3]
[3 1 0]]
矩阵S3转置:
[[0 6 0 0]
[9 1 4 9]
[3 8 4 1]
[2 3 6 1]]
矩阵左乘积:
[[28 30 5]
[34 75 17]
[38 48 9]]
矩阵右乘积:
[[42 54 39]
[28 37 28]
[20 31 33]]
矩阵1行列式值:
-3
矩阵S3行列式值:
-408
矩阵1的伴随矩阵:
[[ 11 -1 9]
[-10 2 -9]
[ 2 -1 3]]
矩阵2的伴随矩阵:
[[ -3 -9 9]
[ 8 24 -6]
[-42 -72 18]]
矩阵S3的伴随矩阵:
[[-302 -68 19 286]
[ -12 0 -6 60]
[ 300 0 -54 -276]
[-192 0 108 144]]
矩阵1的逆矩阵:
[[-3.66666667 0.33333333 -3. ]
[ 3.33333333 -0.66666667 3. ]
[-0.66666667 0.33333333 -1. ]]
矩阵2的逆矩阵:
[[ 0.05555556 0.16666667 -0.16666667]
[-0.14814815 -0.44444444 0.11111111]
[ 0.77777778 1.33333333 -0.33333333]]
Matrix's task is finished.
通过自己计算验证,该结果是正确的。本次测试中输入的是两个相同维度的矩阵,读者可以通过下载程序后输入任意m*n矩阵进行验证,结果也是可以的,各类方法都严格遵循矩阵计算的前提条件,因此如果输入的不是方阵,那么部分方法输出会是Error。
若有不足或者更好的思路,可以在评论区留言,晚生可以好好学习并改进,多谢您的过目。
矩阵类py文件提取地址:
链接:https://pan.baidu.com/s/1ypULq5l7GZdD0quPAbfidw
提取码:plfv