>>> from env_helper import info; info()
页面更新时间: 2023-04-15 21:23:40
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-7-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

1.3. 矩阵运算

NumPy和Matlab不一样,对于多维数组的运算,缺省情况下并不使用矩阵运算,如果你希望对数组进 行矩阵运算的话,可以调用相应的函数。

1.3.1. matrix对象

numpy库提供了matrix类,使用matrix类创建的是矩阵对象,它们的加减乘除运算缺省采用矩阵方式计算,因此用法和matlab十分类似。 但是由于NumPy中同时存在ndarray和matrix对象,因此用户很容易将两者弄混。 这有违Python的“显式优于隐式”的原则,因此并不推荐在较复杂的程序中使用matrix。

下面是使用matrix的一个例子:

>>> import numpy as np
>>> a = np.matrix([[1,2,3],[5,5,6],[7,9,9]])
>>> a
matrix([[1, 2, 3],
        [5, 5, 6],
        [7, 9, 9]])
>>> a*a**-1
matrix([[ 1.00000000e+00,  0.00000000e+00, -5.55111512e-17],
        [ 4.44089210e-16,  1.00000000e+00,  3.33066907e-16],
        [ 4.44089210e-16,  0.00000000e+00,  1.00000000e+00]])

因为 a 是用matrix创建的矩阵对象,因此乘法和幂运算符都变成了矩阵运算,于是上面计算的是矩阵a和其逆矩阵的乘积,结果是一个单位矩阵。 矩阵的乘积可以使用dot函数进行计算。对于二维数组,它计算的是矩阵乘积,对于一维数组,它计算的是其点积。

当需要将一维数组当作列矢量或者行矢量进行矩阵运算时,推荐先使用reshape函数将一维数组转换为二维数组:

>>> a = np.array([1, 2, 3])
>>> a.reshape((-1,1))
array([[1],
       [2],
       [3]])
>>> a.reshape((1,-1))
array([[1, 2, 3]])

除了dot计算乘积之外,NumPy还提供了inner和outer等多种计算乘积的函数。 这些函数计算乘积的方式不同,尤其是当对于多维数组的时候,更容易搞混。

  • dot :

对于两个一维的数组,计算的是这两个数组对应下标元素的乘积和(数学上称之为内积);对于二维数组,计算的是两个数组的矩阵乘积;对于多维数组,它的通用计算公式如下,即结果数组中的每个元素都是:数组a的最后一维上的所有元素与数组b的倒数第二位上的所有元素的乘积和:

dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])

下面以两个3为数组的乘积演示一下dot乘积的计算结果: 首先创建两个3维数组,这两个数组的最后两维满足矩阵乘积的条件:

>>> a = np.arange(12).reshape(2,3,2)
>>> b = np.arange(12,24).reshape(2,2,3)
>>> a, b
(array([[[ 0,  1],
         [ 2,  3],
         [ 4,  5]],

        [[ 6,  7],
         [ 8,  9],
         [10, 11]]]),
 array([[[12, 13, 14],
         [15, 16, 17]],

        [[18, 19, 20],
         [21, 22, 23]]]))
>>> c = np.dot(a,b)
>>> c
array([[[[ 15,  16,  17],
         [ 21,  22,  23]],

        [[ 69,  74,  79],
         [ 99, 104, 109]],

        [[123, 132, 141],
         [177, 186, 195]]],


       [[[177, 190, 203],
         [255, 268, 281]],

        [[231, 248, 265],
         [333, 350, 367]],

        [[285, 306, 327],
         [411, 432, 453]]]])

dot乘积的结果c可以看作是数组a,b的多个子矩阵的乘积:

>>> np.alltrue( c[0,:,0,:] == np.dot(a[0],b[0]) )
True
>>> np.alltrue( c[1,:,0,:] == np.dot(a[1],b[0]) )
True
>>> np.alltrue( c[0,:,1,:] == np.dot(a[0],b[1]) )
True
>>> np.alltrue( c[1,:,1,:] == np.dot(a[1],b[1]) )
True
  • inner :

和dot乘积一样,对于两个一维数组,计算的是这两个数组对应下标元素的乘积和;对于 多维数组,它计算的结果数组中的每个元素都是:数组a和b的最后一维的内积,因此数组a和b 的最后一维的长度必须相同:

inner(a, b)[i,j,k,m] = sum(a[i,j,:]*b[k,m,:])

下面是inner乘积的演示:

>>> a = np.arange(12).reshape(2,3,2)
>>> b = np.arange(12,24).reshape(2,3,2)
>>> c = np.inner(a,b)
>>> c.shape
(2, 3, 2, 3)
>>> c[0,0,0,0] == np.inner(a[0,0],b[0,0])
True
>>> c[0,1,1,0] == np.inner(a[0,1],b[1,0])
True
>>> c[1,2,1,2] == np.inner(a[1,2],b[1,2])
True
  • outer :

只按照一维数组进行计算,如果传入参数是多维数组,则先将此数组展平为一维数组之 后再进行运算。outer乘积计算的列向量和行向量的矩阵乘积:

>>> np.outer([1,2,3],[4,5,6,7])
array([[ 4,  5,  6,  7],
       [ 8, 10, 12, 14],
       [12, 15, 18, 21]])

矩阵中更高级的一些运算可以在NumPy的线性代数子库linalg中找到。例如inv函数计算逆矩阵, solve函数可以求解多元一次方程组。下面是solve函数的一个例子:

>>> a = np.random.rand(10,10)
>>> b = np.random.rand(10)
>>> x = np.linalg.solve(a,b)
>>> np.sum(np.abs(np.dot(a,x) - b))
4.6629367034256575e-15

solve函数有两个参数a和b。a是一个N*N的二维数组,而b是一个长度为N的一维数组,solve函数找 到一个长度为N的一维数组x,使得a和x的矩阵乘积正好等于b,数组x就是多元一次方程组的解。 有关线性代数方面的内容将在今后的章节中详细介绍。