最近在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。

若有不足或者更好的思路,可以在评论区留言,晚生可以好好学习并改进,多谢您的过目。