145-重学线性代数

开篇词 | 从今天起，学会线性代数

你好，我是朱维刚。欢迎你跟我一起重学线性代数！

我们为什么要学线性代数？

“数学”其实是一个老生常谈的话题。我出生在 80 年代，在我这一代，父母一直给我们灌输“学好数理化，走遍天下都不怕”的思想。我们每个人都是从很小的时候就开始学习数学了。那你有没有想过，学了那么多年数学，我们到底把这些数学知识都学到哪里去了呢？它们到底可以用在什么地方呢？

我自己对数学这个学科比较感兴趣，从小就在想这些问题，但是一直百思不得其解。一直到了大学和研究生阶段，学了工程数学之后，我才真正恍然大悟——原来数学可以用在各类工程上，比如我现在正在从事的人工智能中的机器学习项目。

说起来，我与机器学习结缘还是比较早的。

2006 年年初，我开始接触机器学习，加入了 IronPort（后来被思科收购了）公司的 TDA（Threat Data Analyst）团队。当时公司的主要业务集中在“反垃圾邮件”这个领域，我们这些工程师的主要职责，就是每天分析全球大量邮件数据、人工打分类标签、写匹配规则来优化机器学习引擎。其实有点类似如今的数据分析师和数据科学家，可以说基本上每天都在跟“数学”打交道。如今 14 年过去了，目前我致力于建筑行业数字孪生的工作，我希望通过人工智能技术，推动建筑行业进入“智慧建造”时代。

做了这么多年机器学习，我经常听到有人跟我说，机器学习很难啊，你到底是怎么学会的？

其实，我想说，机器学习本身没有多大难度，因为经过多年的积累后，很多规则已经成型了。对于我们来说真正难的，是机器学习背后的算法所涉及的基础数学原理，包括向量、矩阵等等。

我们可以来看下机器学习的整个知识体系。单从数学角度来看，这个覆盖范围非常广，有向量积分、矩阵分解等等，但最最核心的还是线性代数。所以说，不要再问我为什么自己学不会机器学习、人工智能了，因为你没有学好线性代数。

不过，你可千万不要觉得，学了线性代数之后，实际应用就只有机器学习。如果这么想，那就太局限了。

线性代数是计算机很多领域的基础。比如，如何让 3D 图形显示到二维屏幕上？这是线性代数在图形图像学中的应用。如何提高密码被破译的难度？这个密码学问题，用线性代数中的有限向量空间可以很好地解决。

线性代数的应用真的非常广泛。掌握了线性代数这样的基础学科知识，我们其实就相当于有了数学这个利器，为其他领域的实际应用打下了非常好、非常扎实的基础。最简单、最直接的利益——你不仅可以在工作中进行算法调优，还能成为公司创新团队的主力。

到底该怎么学线性代数？

既然线性代数是机器学习最底层的知识，如果我们想要在机器学习上有所作为，学会线性代数是必须的。那该怎么学呢？

我估计你肯定看过外面很多书或者课程，我也看过。它们无一例外都是直接上来就给你讲机器学习的应用实践，然后里面穿插了一些数学知识，从实践的角度切入。这样编排课程当然没问题，优点是入门容易，但它的缺点也是显而易见的。这样学下来，很多时候，你只知道固定的应用场景，死记硬背几个知识点容易，但是数学知识并不牢固。当遇到实际问题的时候，你除了套公式之外，还是只能干瞪眼，根本没有真正吃透背后的原理。

因此，从我自己学习的经验出发，在技术领域里，我更推荐自下而上的学习方式，也就是从底层基础概念开始，一步步循序渐进往上走，一直走到应用实践。当然，这个方式也有缺点，那就是入门的时候困难，可能会遇到很多知识阻碍，很多人都会中途放弃。这些学习经历我都深有体会。

所以，我运用了自下而上的方式来进行讲解，同时，讲解每个知识点的时候，我都会加入一些和理论有关的实践讲解。这样就能够帮你由里及表，融会贯通，在搭建起知识体系的同时，可以获得螺旋式上升的学习效果。

为了让你能更加系统地学习线性代数，在设计“重学线性代数”这门课时，我还真是下了一番苦功夫。下面就给你详细介绍下这门课的两大模块。

第一个模块是基础篇，我们主要讲线性代数的理论基础。

我会从最简单、也是你最熟悉的线性方程组说起，在这基础上引出向量和矩阵，并通过矩阵来讲“解线性方程组”的不同方法（有直接法，也有实践中用得最多的间接迭代法）。然后，我会在向量和矩阵的基础上讲线性空间，因为在实践中，更多的是对集合的操作，也就是对线性空间的操作。线性空间好比是容器，它包含了向量以及向量的运算。基础篇的最后，我还会为你介绍解析几何，是解析几何使得向量从抽象走向了具象，让向量具有了几何的含义，比如：计算向量的长度、之间的距离和角度，这在机器学习的主成分分析 PCA 中是非常有用的。

第二个模块是应用篇，我会结合线性代数的基础理论，讲解线性代数在计算机科学中的应用。

有了之前的基础后，你再来看应用实践就会觉得简单很多。当其中会涉及一些线性代数以外的数学领域时，我也会给予一定的说明。

特别地，我要强调一下。在每一讲最后我都特意设计了“线性代数练习场”，带你通过练习来巩固学到的知识点。所以，这个小板块一定要重视起来，期待和你一起在实践中探索。

所以从整体来说，“重学线性代数”可以满足你四个层次的需求：

第一层次：在研究应用领域时，希望能够理解数学公式的意义。

第二层次：在阅读线性代数参考书时，希望理解书中的内容。

第三层次：能够自己实践、自己计算。

第四层次：能够踏入大规模矩阵计算的世界。

好了，到这里，我想说的已经差不多了，不知道你有没有准备好，跟我一起学习了呢？

进入 DT 时代后，很多企业都开始着手做数字化转型。站在从业者的角度，有了数字化的基础数据，我相信，终有一天人工智能将定义下一代软件解决方案，这是一个巨大的机会。

我希望在这个机会真正到来前，你能和我一起，一步步地、深入浅出地学习线性代数这门数学基础课，成为企业研究机构的创新力量之一。我也非常希望，通过这门课程的学习，你能对线性代数能有一个重新的认识，让线性代数融入到你的工作和生活中，真正改变你的工作和生活，让它成为你的翅膀。

当然，线性代数只是一个开始，在实际解决问题时，还需要用到很多其它的数学知识，我也会继续保持学习的心态，来和你一起探讨数学，一起更新最新的前沿知识，学习永无止境。

最后，让我们整装出发，一起来探索线性代数的世界，感受腾飞的乐趣吧！

01 | 导读：如何在机器学习中运用线性代数工具？

你好，我是朱维刚。欢迎你跟我一起重学线性代数！

在开篇词中，我和你大致讲过我自己的经历，从 2006 年开始到现在 14 年的时间里，我都专注于机器学习领域。对于线性代数在机器学习中的应用，我非常了解。而这也是线性代数最主要的应用场景之一。因此，今天第一节课，我想先和你聊一聊，如何在机器学习中运用线性代数工具，在我们开始自下而上的学习之前，先从上层来看一看。

我们都知道，“数据”是机器学习的前提，机器学习的第一步就是要进行数据的收集、预处理和特征提取；而模型就是通过数据来学习的算法；学习则是一个循环过程，一个自动在数据中寻找模式，并不停调优模型参数的过程。那我们就从机器学习的三个核心概念：数据、模型和学习说起。

你看，不论是模型，还是学习，都涉及数据，而数据加上模型和学习，就是数学的一般过程了，也就是：观察、实验、推理和抽象。所以，我认为学好数学，不仅有利于理解复杂的机器学习系统，还能调优算法参数，甚至能帮助你创建新的机器学习解决方案。

从机器学习到线性代数

那机器学习和线性代数之间到底有着怎样的关系呢？我想，用一个实际的机器学习算法的例子来解释，你可能更容易搞清楚。接下来，我使用 KNN（K-Nearest Neighbor，K 最近邻分类算法）来让你简单了解一下机器学习，以及它和线性代数之间的关系。

之所以选 KNN 分类算法，因为它是一个理论上比较成熟的方法，也是最简单的机器学习算法之一。这个方法的思路是：如果一个样本在特征空间中的 K 个最相似（即特征空间中最邻近）的样本中的大多数属于某一个类别，则该样本也属于这个类别。

这里有个前提，KNN 算法中，所选择的“邻居”都是已经正确分类的对象。KNN 分类算法在分类决策上只依据最邻近的一个或者几个样本的类别，来决定待分样本所属的类别。我们通过图来理解的话或许更容易一些。

假设图片中那个绿色圆就要是我们要决策的对象，那么根据 KNN 算法它属于哪一类？是红色三角形还是蓝色四方形？

如果 K=3（实线圆），也就是包含离绿色圆最近的 3 个，由于红色三角形所占比例为 2/3，绿色圆就属于红色三角形那个类。但如果 K=5（虚线圆），就是包含离绿色圆最近的 5 个，由于蓝色四方形比例为 3/5，绿色圆就属于蓝色四方形那个类。

鸢尾花分类问题中的线性代数

通过前面这个小例子，你应该已经理解了 KNN 算法的概念。那么接下来，我们就试着使用 KNN 在给定鸢尾花特征值的情况下，给鸢尾花做花种分类，带你来实际看一下线性代数在这里起到的作用。

特别说明一下，鸢尾花分类问题是一个国际上通用的案例，一般都被作为机器学习入门来使用，所以它的数据集也是公开的。

1. 数据集的收集、加载和分析

首先，我们要做的是数据集的收集、加载和分析，你也可以点击这里下载原始数据集，来看看原始数据长什么样，下面是获取和加载数据的代码，sklearn 数据集已经包含了样本数据，你可以直接用。

import pandas as pd

from sklearn import datasets
iris = datasets.load_iris()

species = [iris.target_names[x] for x in iris.target]

iris = pd.DataFrame(iris['data'], columns = ['Sepal_Length', 'Sepal_Width', 'Petal_Length', 'Petal_Width'])

iris['Species'] = species

从显示的结果，我们能够看出鸢尾花有四个特征：花萼的长、宽和花瓣的长、宽。我们来看下这四个特征的数据类型：

iris.dtypes
Sepal_Length    float64
Sepal_Width     float64
Petal_Length    float64
Petal_Width     float64
Species          object
dtype: object

这些特征都是数值型，而且标签 Species 表示的是花种，是一个字符串类型的变量。我们继续看一下鸢尾花的分类统计：

iris['count'] = 1
iris[['Species', 'count']].groupby('Species').count()

这里我们直接能够看到，鸢尾花有三个花种，每个种类有 50 个实例，或者说 50 条数据，我们再用图来更直观地显示这三种鸢尾花。

%matplotlib inline

def plot_iris(iris, col1, col2):
    import seaborn as sns
    import matplotlib.pyplot as plt

    sns.lmplot(x = col1, y = col2,
               data = iris,
               hue = "Species",
               fit_reg = False)

    plt.xlabel(col1)

    plt.ylabel(col2)

    plt.title('Iris species shown by color')

    plt.show()

plot_iris(iris, 'Petal_Width', 'Sepal_Length')

plot_iris(iris, 'Sepal_Width', 'Sepal_Length')

蓝、黄、绿，这三种颜色分别代表了三种鸢尾花，显示还是很清楚的。

2. 数据集的准备

接下来的第二步就是数据集的准备了。在训练任何机器学习模型前，数据准备都相当重要，这里也要涉及两步准备。

第一步，特征数值标准化。如果我们不做标准化，后果就是大数值特征会主宰模型训练，这会导致更有意义的小数值特征被忽略。这里我们用 Z Score 标准化，使每一类特征平均值为 0，方差为 1.0，我们可以通过代码实现来看下效果。

from sklearn.preprocessing import scale

import pandas as pd

num_cols = ['Sepal_Length', 'Sepal_Width', 'Petal_Length', 'Petal_Width']

iris_scaled = scale(iris[num_cols])

iris_scaled = pd.DataFrame(iris_scaled, columns = num_cols)

print(iris_scaled.describe().round(3))

你可以看到，每一列平均值为 0，标准差大约是 1.0。为了分类需要，我们用字典把花种从字符串类型转换成数字表示。

levels = {'setosa':0, 'versicolor':1, 'virginica':2}

iris_scaled['Species'] = [levels[x] for x in iris['Species']]

iris_scaled.head()

第二步，把数据集随机分割成样本训练集和评估数据集，训练集用来训练 KNN 模型，评估集用来测试和评估 KNN 的分类结果。

from sklearn.model_selection import train_test_split

import numpy as np

np.random.seed(3456)

iris_split = train_test_split(np.asmatrix(iris_scaled), test_size = 75)

iris_train_features = iris_split[0][:, :4]

iris_train_labels = np.ravel(iris_split[0][:, 4])

iris_test_features = iris_split[1][:, :4]

iris_test_labels = np.ravel(iris_split[1][:, 4])

print(iris_train_features.shape)

print(iris_train_labels.shape)

print(iris_test_features.shape)

print(iris_test_labels.shape)

通过代码，我们得到了下面这样的结果。

(75, 4)
(75,)
(75, 4)
(75,)

3. 训练模型

数据准备好后，就是第三步训练模型了。这里我们使用 K=3 来训练 KNN 模型，当然你也可以调整这个参数来进行观察和调优。

from sklearn.neighbors import KNeighborsClassifier

KNN_mod = KNeighborsClassifier(n_neighbors = 3)

KNN_mod.fit(iris_train_features, iris_train_labels)

4. 模型测试

执行 KNN 训练后，我们来到了最后一步，模型测试，这里我们使用测试集来测试模型。

iris_test = pd.DataFrame(iris_test_features, columns = num_cols)

iris_test['predicted'] = KNN_mod.predict(iris_test_features)

iris_test['correct'] = [1 if x == z else 0 for x, z in zip(iris_test['predicted'], iris_test_labels)]

accuracy = 100.0 * float(sum(iris_test['correct'])) / float(iris_test.shape[0])

print(accuracy)

96.0

最终，我们得到的准确率是 96.0，说明了 KNN 的训练模型不错，适用这类场景。我们通过代码把其中的两个分类 setosa 和 versicolor 打印出来看看。

levels = {0:'setosa', 1:'versicolor', 2:'virginica'}

iris_test['Species'] = [levels[x] for x in iris_test['predicted']]

markers = {1:'^', 0:'o'}

colors = {'setosa':'blue', 'versicolor':'green',}

def plot_shapes(df, col1,col2,  markers, colors):
    import matplotlib.pyplot as plt
    import seaborn as sns

    ax = plt.figure(figsize=(6, 6)).gca() # define plot axis

    for m in markers: # iterate over marker dictioary keys
        for c in colors: # iterate over color dictionary keys
            df_temp = df[(df['correct'] == m)  & (df['Species'] == c)]
            sns.regplot(x = col1, y = col2,
                        data = df_temp,
                        fit_reg = False,
                        scatter_kws={'color': colors[c]},
                        marker = markers[m],
                        ax = ax)
    plt.xlabel(col1)
    plt.ylabel(col2)
    plt.title('Iris species by color')
    return 'Done'

plot_shapes(iris_test, 'Petal_Width', 'Sepal_Length', markers, colors)
plot_shapes(iris_test, 'Sepal_Width', 'Sepal_Length', markers, colors)

从显示的效果来说，分类还是挺明显的，熟悉了最基础的机器学习过程后，你可能会问，讲了半天，线性代数到底在哪里呢？关键就在 KNeighborsClassifier 模块上，这个模型算法的实现背后，其实用到了线性代数的核心原理。

首先，因为每种鸢尾花都有四个特征：花萼的长、宽和花瓣的长、宽，所以每条数据都是四维向量。

接着，量化样本之间的相似度，也就是计算向量之间的距离。而向量之间距离的运算有很多方式，比如：曼哈顿距离、欧式距离、切比雪夫距离、闵可夫斯基距离等等。其中，欧式距离你应该很熟悉了，因为我们初中都学过，在二维平面上计算两点之间的距离公式：

d=(x1​−x2​)2+(y1​−y2​)2​

扩展到我们实例中的四维向量，也是同样的算法。

你看，这就是线性代数在机器学习中的一种应用场景。KNN 是一种监督学习算法，因为在样本集中有分类信息，通过计算距离来衡量样本之间相似度，算法简单，易于理解和实现。还有另一种机器学习算法是无监督学习，底层的数学原理其实也是差不多的，总的思想就是“物以类聚”。

现在，你是不是有一种豁然开朗的感觉？终于看到了线性代数原来那么有意义，而且再简单的公式也是美的。

本节小结

好了，到这里导读这一讲就结束了，最后我再总结一下前面讲解的内容。

这一讲我使用机器学习的监督学习算法 KNN，在给定鸢尾花特征值的情况下，给鸢尾花做花种分类，让你了解机器学习最基本的过程外，能够真正了解其背后的线性代数真相，为你进入后面课程的学习提供一个感性的认知。

机器学习中用到的线性代数知识点比比皆是，而且往往软件架构上看上去复杂的事情，在数学上反而很简单，希望你在学习了这门课程后，能够多从数学角度出发去构思解决问题的方案。

同时，欢迎你在留言区说说自己对机器学习的理解，也可以分享一下自己的线性代数学习经历，如果你有所收获，也欢迎你把这篇文章分享给你的朋友。

02 | 基本概念：线性代数研究的到底是什么问题？

你好，我是朱维刚。欢迎你跟我学习线性代数。今天我们要讲的是“线性代数这门课的基本概念”。

线性代数可以运用在很多领域，比如：工程学、计算机科学、经济学、信号处理等等。我们来看一个在经济学中常见的例子：消费矩阵。

假设有 n 个行业，比如：化学、食品和石油。制造一单位的某化学品需要 0.2 单位的另一类化学品，0.3 单位的食品，以及 0.4 单位的石油，而制造一单位的某食品和某石油也同样分别需要这三类产品的输入，于是，我们就能构造这样一个消费矩阵：

∣∣∣∣∣∣​化学输出食品输出石油输出​∣∣∣∣∣∣​=⎣⎡​0.20.40.5​0.30.40.1​0.40.10.3​⎦⎤​∣∣∣∣∣∣​化学输入食品输入石油输入​∣∣∣∣∣∣​

当然，我们也可以用一般的线性方程组 Ax=b 的形式来表达：

⎩⎨⎧​0.2x1​+0.3x2​+0.4x3​=b1​0.4x1​+0.4x2​+0.1x3​=b2​0.5x1​+0.1x2​+0.3x3​=b3​​

从本质上来说，消费矩阵解决的是输入和输出之间的关系。不仅如此，线性代数在现实生活中的运用还有很多，比如，我们可以借用特征值和特征向量，预测若干年后的污水水平；在密码学中，可以使用矩阵及其逆矩阵对需发送的消息加密；在机器学习中，可以使用线性方程组的共轭迭代法来训练神经网络，等等。

刚才我分别用矩阵和线性方程组的形式给出了消费矩阵，当然，在实际生活中，你可以灵活选择最有效的方式来解决问题。

我们可以看到，线性方程组可以表示成一般形式，也就是你初中学到的 Ax=b 的形式，也可以表示成矩阵形式。矩阵是由向量组合而成的，比如刚才例子中的系数矩阵的每一行都是一个行向量，每一列都是一个列向量。

⎣⎡​0.20.40.5​0.30.40.1​0.40.10.3​⎦⎤​

从这里我们能看出，向量其实就会是线性代数最基础的核心。数学对抽象思维要求很高，简单来说，抽象思维就是抽取同类事物的共性。所以，在进入具体的线性方程组的主题前，我要先从数学抽象的角度说一说代数和线性代数，这也是深入理解后面内容的前提。

代数和线性代数的基本概念

那什么是代数呢？百度百科的解释是这样的：

代数是研究数、数量、关系、结构与代数方程（组）的通用解法及其性质的数学分支。

但我觉得这个解释其实没有说出代数这个概念的重点。我的理解是这样的：代数是构造一系列对象和一系列操作这些对象的规则。

所以你看，代数这个概念的核心就两点，对象和操作对象的规则，这样就很好理解了吧？那有了代数的定义，线性代数就很好定义了。我们类比来看，线性代数其实就是向量，以及操作这些向量的规则。这里，向量映射到对象，向量的规则映射到对象的规则，因此线性代数是代数的具像化表达。

向量的基本概念

那什么是向量呢？从样子来看，向量其实就是由字母加上它上面的箭头来表示的，比如我们一版会写成 x。我估计你在高中或大学里已经接触过“几何向量”。那么，下面我用更抽象的数学观点来给你解释一下。

向量，也叫欧几里得向量（Euclidean Vector），其实就是能够互相相加、被标量乘的特殊对象。而标量也叫“无向量”，它只有数值大小，没有方向。怎么理解呢？我们来看一些向量的例子，通过这些例子来深入理解向量的概念。

几何向量是有向线段，在二维空间（也就是平面）中，两个几何向量能够相加，比如，向量 x 加上向量 y 等于向量 z，x+y​=z ，x 向量也能被一个标量乘。再比如，标量 λ 乘向量 x 结果也是向量，λx,λ∈R。几何向量通过大小和方向来简化向量的表达，所以，一般数学课程一开始都会拿几何向量来进行举例。

多项式其实也是向量。两个多项式能够相加，它也能够被标量乘，结果也是多项式。

矩阵的一行或一列也是向量。就比如下面这样形式的向量。

x∈R3:x=⎣⎡​123​⎦⎤​

两个向量能够相加，它也能够被标量乘，结果也是向量。它和现代大部分的编程语言中的数组一致，而且数组的运算简化了向量操作的算法实施。

矢量图、音频信号也是向量。它们都能被一系列数字表示，比如，在音频信号处理中，常用到数据增强的方法，通过向量的操作就能到达目标。

看了这些向量例子，不知道你现在有点感觉了吗？其实，线性代数的本质就是寻找向量之间的相似性，比如在做分类时，我们常常需要估算不同样本之间的相似性度量（Similarity Measurement），这时通常采用的方法就是计算样本间的“距离”（Distance）。

可以看出来，向量非常重要，我们后面很多内容都是从向量延伸而来的，比如矩阵和求解线性方程组。

下面我用一张图来表达和向量有关的所有概念，也就是线性代数所有的核心内容，其中大部分内容你都会学到，希望通过这个图，你对线性代数有个大概的认知。相信学习完所有课程后，你再回过头来看时，肯定会有一些新的认知。

从图中最左侧这一列，我们可以看出，向量组合成矩阵，矩阵可以表示成线性方程组，而线性方程组可以通过高斯消元法求解，也可以求逆矩阵。

同时，向量又可以组合成向量空间，向量空间和矩阵都可以做线性映射或者线性变换，线性映射在实践中可以用在解析几何和分类问题中。

而且，向量和线性独立是强相关的，也就是说，线性独立指的是向量的线性独立，而线性独立又可以引出能够生成整个空间的基（Basis），基在实践中可以用在分类和降维中。

这里，我再额外提一个非常重要的、在数学中经常用到的概念——封闭性，或者俗称闭包（Closure）。封闭性的定义是，如果我们要对某个集合的成员进行一种运算，生成的仍然是这个集合的成员，那这个集合就可以称为在这个运算下闭合。

我为什么要提这个概念呢？这是因为，向量的线性运算是封闭的，也就是说向量的加法、数乘结果仍属于向量空间，即向量的任意线性组合仍属于向量空间。

线性方程组的应用

到这里，相信你已经对线性代数、向量这些基本概念有了一个应试教育之外的、新的认识，接下来我就切入本篇的最重点的内容了，那就是线性方程组。

线性方程组在线性代数中有着举足轻重的地位，许多现实生活中的问题都可以表达成线性方程组。关于线性方程组的概念，我想不用我多说了，这是初中的内容，你应该已经非常了解了。现在我举几个例子来说一说，线性方程组在现实生活中的运用，让你从应用的角度再理解下线性方程组。

第一个例子是计算旅游团人数。假设，一个旅游团由孩子和大人组成，去程时他们一起坐大巴，每个孩子的票价 3 元，大人票价 3.2 元，总共花费 118.4 元。回程时一起坐火车，每个孩子的票价 3.5 元，大人票价 3.6 元，总共花费 135.2 元。请问这个旅游团中有多少孩子和大人？

假设小孩人数为 x1​，大人人数为 x2​，于是我们得到了一个方程组：

{3x1​+3.2x2​=118.43.5x1​+3.6x2​=135.2​

使用初中的解二元一次方程组的知识，我们可以得出这个方程组的解是：

{x1​=16x2​=22​

这个就是典型的线性方程组的现实例子。当然，我们也可以用矩阵来解这个方程组。你可能会说，这问题很简单啊，为什么还要用矩阵来解呢？整这么复杂有必要吗？别着急，这里我先卖个关子，具体我会在下一讲“矩阵篇”中进行讲解。

第二个例子是找出公司的最优生产计划。我们从公司的角度来看一看，假设生产苹果的几大主流产品：iPhone、Macbook、iMac，以及 iWatch 四款产品，需要用到芯片、摄像头模组、电池、IPS 屏幕四大类资源，产品分别用 N1​，N2​，N3​，N4​ 来表示，资源分别用 R1​，R2​，R3​，R4​ 来表示。

生产一单位的产品 N1​，也就是 iPhone，需要 a11​，a21​，a31​，a41​ 的资源，也就是芯片、摄像头模组、电池和 IPS 屏幕资源，其他产品也是一样。

我们的目标是在资源有限的情况下，找到一个最优生产计划，使每个产品的产出数量最大化。也就是说，在总共有 bi​ 个单位资源 Ri​ 可用情况下，每个产品 Ni​ 有多少单元 xi​ 应该被生产。因此，我们可以得到一个下面这样的线性方程组：

⎩⎪⎪⎨⎪⎪⎧​a11​x1​+a12​x2​+a13​x3​+a14​x4​=b1​a21​x1​+a22​x2​+a23​x3​+a24​x4​=b2​a31​x1​+a32​x2​+a33​x3​+a34​x4​=b3​a41​x1​+a42​x2​+a43​x3​+a44​x4​=b4​​

于是，我们得到线性方程组的通用表达方式，x1​，⋯，xn​ 是未知变量，每个满足方程组表达式的 n 元组 (x1​，⋯，xn​) 都是它的解。

⎩⎪⎪⎨⎪⎪⎧​a11​x1​+a12​x2​+⋯+a1n​xn​=b1​a21​x1​+a22​x2​+⋯+a2n​xn​=b2​⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯am1​x1​+am2​x2​+⋯+amn​xn​=bm​​

线性方程组的解是有几何意义的，从几何角度出发，有利于你理解和记忆线性方程组解的条件，接下来我们先来看看线性方程组解的几种情况，之后我再具体讲解线性方程组的几何表达。

首先我们来看无解的情况。假设有下面这样一个线性方程组：

⎩⎨⎧​x1​+x2​+x3​=3x1​−x2​+2x3​=22x1​+3x3​=1​

第一行和第二行相加后，我们可以得到 2x1​+3x3​=5，很明显，这个方程和第三行是矛盾的，所以，这个线性方程组无解。

其次是只有一个解的情况。假设有下面这样一个线性方程组：

⎩⎨⎧​x1​+x2​+x3​=3x1​−x2​+2x3​=2x2​+x3​=2​

这个方程组求解很简单，第一行乘以 −1 和第二行相加得到 x1​=1 ，第一行和第二行相加，得到 2x1​+3x3​=5 ，代入 x1​ 后，得到 x3​=1，最后，我们可以得到 (1,1,1) 是这个线性方程组的唯一解。

最后是无穷解的情况。我们有下面这样一个线性方程组：

⎩⎨⎧​x1​+x2​+x3​=3x1​−x2​+2x3​=22x1​+3x3​=5​

方程组第一行和第二行相加等于第三行，从这个角度来说，我们就可以定义一个自由变量 x3​=a ，a 属于实数，那方程组的解就是：

(25​−23​a,21​+21​a,a)

其中 a 属于实数，所以，这就包含了无穷多个解。

线性方程组的几何表达

好了，了解了线性方程组解的三种情况，现在我们是时候从几何的角度来看一看线性方程组和它的解了，从几何意义出发有利于你更直观地理解线性方程组。

在一个只有两个变量 x1​,x2​ 的线性方程组中，我们定义一个 x1​,x2​ 平面。在这个平面中，每个线性方程都表达了一条直线。由于线性方程组的唯一解必须同时满足所有的等式，所以，线性方程组的唯一解其实就是线段的相交点，无穷解就是两线重合，而无解的情况，也就是两条线平行。

我拿一个例子来说明，设线性方程组：

{4x1​+4x2​=52x1​−4x2​=1​

把其中的两个线性方程在 x1​,x2​ 平面中画出来后，我们可以得到两线段的交点 (1,41​)，也就是这个线性方程组的解。

我再把这个概念延伸一下，当遇到三个变量的情况时，每个线性方程在三维空间中表达了一个平面，由于线性方程组的一个解必须同时满足所有的等式，所以，线性方程组的解其实就是平面，也可以是线、点或者空，空也就是没有共同的平面相交。

其实还有一个更好的方法来表达和解线性方程组，这个方法就是下一篇要讲的矩阵，在这里我先简单提一下。我们把系数组合成向量，把向量组合成矩阵，也就是说，我们可以把线性方程组写成这样的形式：

x1​∣∣∣∣∣∣∣​a11​⋮am1​​∣∣∣∣∣∣∣​+x2​∣∣∣∣∣∣∣​a12​⋮cm2​​∣∣∣∣∣∣∣​+⋯+xn​∣∣∣∣∣∣∣​a1n​⋮amn​​∣∣∣∣∣∣∣​=∣∣∣∣∣∣∣​b1​⋮bm​​∣∣∣∣∣∣∣​

再进一步最终可以写成矩阵的形式：

⎣⎢⎢⎡​a11​a21​…am1​​a12​a22​…am2​​…………​a1n​a2n​…amn​​⎦⎥⎥⎤​∣∣∣∣∣∣∣∣∣​x1​⋮.xn​​∣∣∣∣∣∣∣∣∣​=∣∣∣∣∣∣∣∣∣​b1​⋮.bm​​∣∣∣∣∣∣∣∣∣​

本节小结

好了，到这里这一讲就结束了，我带你总结一下前面讲解的内容。

这节课我带你重新认识了代数和线性代数的概念。代数是构造一系列对象和一系列操作这些对象的规则，线性代数是向量，以及操作这些向量的规则，所以，线性代数是代数的具像化表达。从线性代数，我们引出了向量的基本概念，我带你看了一个和向量有关的所有概念，即线性代数所有核心内容的图。

可以说，线性代数的一切皆从向量而来。

最后，我带你从二维平面几何角度，更直观地观察线性方程组和它几种解的情况，而二维空间的线性方程组的解其实就是线、点或者空，也就是没有共同的线段相交。你也可以自己试着把它扩展到三维空间几何中来观察，或许会更有趣哦！

线性代数练习场

我们一起来看一个练习题。这是一道极其简单的初中线性方程组题，你可以回顾一下，也为下一节课做好铺垫。

李大叔去年承包了 10 亩地，种植甲、乙两种蔬菜，共获利 18000 元。其中甲蔬菜每亩获利 2000 元，乙蔬菜每亩获利 1500 元。

请问，李大叔去年甲、乙两种蔬菜各种植了多少亩？

欢迎在留言区晒出你的运算过程和结果。如果有收获，也欢迎你把这篇文章分享给你的朋友。

03 | 矩阵：为什么说矩阵是线性方程组的另一种表达？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我们要讲的内容是“矩阵”。

在开始学习之前，我想先问你个问题，你觉得，学习矩阵有什么用呢？你可以先自己想一想。之后我们讲任何一个知识的时候，你都可以从这个角度出发，自己先思考一下，这样有助于你对所学内容理解得更深刻。

对于刚才那个问题，我的答案很简单，就一句话，从我们程序员的角度去理解的话，矩阵可以极大地提高计算机的运算效率。怎么说呢？我给你举一个例子。在机器学习中（特别是深度学习，或者更具体一点，神经网络），并行计算是非常昂贵的。

上图是一个典型的神经网络架构，在这时候，矩阵就能发挥用武之地了，计算 H 隐藏层输出的公式是：H=f(W.x+b)，其中 W 是权重矩阵，f 是激活函数，b 是偏差，x 是输入层矩阵。而这个计算过程就叫做向量化（Vectorization），这也是 GPU 在深度学习中非常重要的原因，因为 GPU 非常擅长做类似矩阵乘之类的运算。

X=∣∣∣∣​x1​x2​​∣∣∣∣​

W=∣∣∣∣∣∣​w1​w4​x3​​w2​w5​w6​​∣∣∣∣∣∣​

H=f⎝⎛​∣∣∣∣∣∣​w1​w4​x3​​w2​w5​w6​​∣∣∣∣∣∣​∣∣∣∣​x1​x2​​∣∣∣∣​+b⎠⎞​

不过，矩阵也不仅仅局限于神经网络的应用，同时它也可以用在计算机图形图像的应用中，比如，三维物体从取景到屏幕的显示，就需要经历一系列的空间变换，才能生成二维图像显示在显示器上。在这个计算过程中，我们都需要用到矩阵。

矩阵是非常实用的，但它正式作为数学中的研究对象出现，其实是在行列式的研究发展起来之后。英国数学家 Arthur Cayley 被公认为矩阵论的创立人，他提出的矩阵概念可能来自于行列式。但我相信另一种说法，提出矩阵是为了更简单地表达线性方程组，也就是说，矩阵是线性方程组的另一种表达。

矩阵的基本概念

线性方程组的概念很简单，上节我们已经简单提过。你在小学或中学肯定也学过二元一次方程和二元一次方程组。

ax+by=c

{a1​x+b1​y+C1​=0a2​x+b2​y+C2​=0​

在这样一个方程组中，a1、a2、b1、b2 不能同时为 0。当我们把二元一次方程组再扩展一下，变成多元一次方程组时，我们就能得到线性方程组的一般表达，即 AX=B。

⎩⎪⎪⎨⎪⎪⎧​a11​x1​+a12​x2​+⋯+a1n​xn​=b1​a21​x1​+a22​x2​+⋯+a2n​xn​=b2​⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯am1​x1​+am2​x2​+⋯+amn​xn​=bm​​

于是，这个线性方程组的所有系数就构成了一个 m×n 的 m 行 n 列矩阵：

A=⎣⎢⎢⎡​a11​a21​…am1​​a12​a22​…am2​​…………​a1n​a2n​…amn​​⎦⎥⎥⎤​

我们把 A 称为该方程组的系数矩阵，而当我们把等式右边的常数 b 放入矩阵后，就是下面这样：

A=⎣⎢⎢⎡​a11​a21​…am1​​a12​a22​…am2​​…………​a1n​a2n​…amn​​b1​b2​…bm​​⎦⎥⎥⎤​

这样我们就得到了 A 矩阵的增广矩阵 A ，可以表示为 (A,B)，这里的 B 表示的是方程组常数项所构成的列向量，也就是 m×1 的 m 行 1 列矩阵：

B=∣∣∣∣∣∣∣∣​b1​b2​⋯bm​​∣∣∣∣∣∣∣∣​

如果设 X 为 n×1 的 n 行 1 列矩阵：

X=∣∣∣∣∣∣∣∣​x1​x2​⋯xn​​∣∣∣∣∣∣∣∣​

那么线性方程组 A，就可以表示为 AX=B 的矩阵形式。如果我们再换一种表示形式，设：a1​,a2​,…,an​,β 表示增广矩阵 A 的列向量，则线性方程组 A 又可表示为 a1​x1​+a2​x2​+⋯+an​xn​=β。

线性方程组的矩阵和向量形式都是线性方程组的其他表达形式。在工作中，你可以用它们来简化求解，甚至可以提升计算效率，就如之前提到的神经网络的隐藏层的输出计算、图形图像的三维空间变换。在数学中也是同样的，你可以经常运用它们来简化求解。具体线性方程组求解的内容比较多，我们下一节课再来详细讲解求解过程。

通过前面的讲解，我相信你对矩阵有了一定的了解，现在我们再回头来看看矩阵的定义吧。

矩阵的定义是：一个 (m,n) 矩阵 A，是由 m×n 个元素组成，m 和 n 是实数，其中元素 aij​,i=1,…,m,j=1,…,n 按 m 行 n 列的矩形排布方式后可以形成矩阵 A：

A=⎣⎢⎢⎡​a11​a21​…am1​​a12​a22​…am2​​…………​a1n​a2n​…amn​​⎦⎥⎥⎤​

其中 aij​ 属于实数 R，按通常的惯例，(1,n) 矩阵叫做行，(m,1) 矩阵叫做列，这些特殊的矩阵叫做行或列向量。

定义完矩阵后，我接着讲一个比较有趣的概念，矩阵转换（Matrix transformation）。矩阵转换经常被用在计算机图形图像的转换中，比如，一张彩色图片从 RGB 角度来说是三维的，如果要转换成灰度图片，也就是一维图片，那就要做矩阵转换。

我们来看一下矩阵转换的过程。设 Rm×n 是实数矩阵 (m,n) 的集合，A∈Rm×n 可以表示成另一种形式 a∈Rmn 。我们把矩阵的 n 列堆叠成一个长向量后完成转换。这个转换也叫做 reshape，其实就是重新调整原矩阵的行数、列数和维数，但是元素个数不变。

矩阵的运算

了解了矩阵的基本定义后，我们才能进入矩阵的运算环节，就是矩阵的加和乘。

加运算很简单，两个矩阵 A∈Rm×n，B∈Rm×n 的加运算其实就是矩阵各自元素的加。

A+B=⎣⎢⎢⎢⎢⎡​a11​+b11​⋅⋅⋅am1​+bm1​​……​a1n​+b1n​⋅⋅⋅amn​+bmn​​⎦⎥⎥⎥⎥⎤​∈Rm×n

我推荐你使用 NumPy 的 einsum 来高效地做这类运算，因为它在速度和内存效率方面通常可以超越我们常见的 array 函数。

C= np.einsum('il, lj', A, B)

接下来，我们一起来看看矩阵的乘。这里你需要注意，矩阵的乘和通常意义上“数之间的乘”不同，矩阵的乘有多种类型，这里我讲三种最普遍，也是在各领域里用得最多的矩阵乘。

1. 普通矩阵乘

普通矩阵乘是应用最广泛的矩阵乘，两个矩阵 A∈Rm×n，B∈Rn×k，普通矩阵则乘可以表示为 C=AB∈Rm×k，C 中元素的计算规则是矩阵 A、B 对应两两元素乘积之和。

cij​=k=1∑n​aik​bkj​,i=1,…,m,j=1,…,l

我们举例来说明。C 的第一个元素 c11​=a11​×b11​+a12​×b21​+a13​×b31​=1×1+2×2+3×3。

C=AB=[14​25​36​]⎣⎡​123​456​⎦⎤​=[1×1+2×2+3×34×1+5×2+6×3​1×4+2×5+3×64×4+5×5+6×6​]=[1432​3277​]

这里需要特别注意的是，只有相邻阶数匹配的矩阵才能相乘，例如，一个 n×k 矩阵 A 和一个 k×m 矩阵 B 相乘，最后得出 n×m 矩阵 C，而这里的 k 就是相邻阶数。

AB=C

但反过来 B 和 A 相乘就不行了，因为相邻阶数 m 不等于 n。

2. 哈达玛积

哈达玛积理解起来就很简单了，就是矩阵各对应元素的乘积，cij​=aij​×bij​ 。举个例子：

C=A∗B=[14​25​][12​45​]=[1∗14∗2​2∗45∗5​]=[18​825​]

哈达玛积其实在数学中不常看到，不过，在编程中哈达玛积非常有用，因为它可以用来同时计算多组数据的乘积，计算效率很高。

3. 克罗内克积

克罗内克积是以德国数学家利奥波德·克罗内克（Leopold Kronecker）的名字命名的。它可以应用在解线性矩阵方程和图像处理方面，当然从更时髦的角度说，它还能用在量子信息领域，我们也称之为直积或张量积。

和普通矩阵乘和哈达玛积不同的是，克罗内克积是两个任意大小矩阵间的运算，表示为 A×B，如果 A 是一个 m×n 的矩阵，而 B 是一个 p×q 的矩阵，克罗内克积则是一个 mp×nq 的矩阵。

接下来我们需要定义一个在矩阵的乘法中起着特殊作用的矩阵，它就是单位矩阵。高等代数中，在求解相应的矩阵时，若添加单位矩阵，通过初等变换进行求解，往往可以使问题变得简单。按照百度百科的解释，单位矩阵如同数的乘法中的 1，这种矩阵就被称为单位矩阵。它是个方阵，从左上角到右下角的对角线，也就是主对角线上的元素均为 1，除此以外全都为 0。

在线性代数中，大小为 n 的单位矩阵就是在主对角线上均为 1，而其他地方都是 0 的 n×n 的方阵，它用 In​ 表示，表达时为了方便可以忽略阶数，直接用 I 来表示：

I1​=[1],I2​=[10​01​],I3​=⎣⎡​100​010​001​⎦⎤​,…,In​=⎣⎢⎢⎢⎢⎡​10..0​01..0​……….…​00..1​⎦⎥⎥⎥⎥⎤​

矩阵的性质

在了解了矩阵加和乘，以及单位矩阵后，我们是时候来看一看矩阵的性质了。了解矩阵的性质是进行矩阵计算的前提，就像我们小时候学加减乘除四则运算法则时那样。所以，这块内容对你来说应该不难，你作为了解就好，重点是之后的运算。

1. 结合律

任意实数 m×n 矩阵 A，n×p 矩阵 B，p×q 矩阵 C 之间相乘，满足结合律 (AB)C=A(BC)。这个很好理解，我就不多说了。

∀A∈Rm×n,B∈Rn×p,C∈Rp×q:(AB)C=A(BC)

2. 分配律

任意实数 m×n 矩阵 A 和 B，n×p 矩阵 C 和 D 之间相乘满足分配律 (A+B)C=AC+BC，A(C+D)=AC+AD。

∀A,B∈Rm×n,C,D∈Rn×p:(A+B)C=AC+BC,A(C+D)=AC+AD

3. 单位矩阵乘

任意实数 m×n 矩阵 A 和单位矩阵之间的乘，等于它本身 A。

∀A∈Rm×n:Im​A=AIn​=A

注意，这里的行和列不同，m​=n 意味着，根据矩阵乘，左乘和右乘单位矩阵也不同，也就是 Im​​=In​。

逆矩阵与转置矩阵

了解矩阵基本概念、运算，以及性质后，我来讲一讲矩阵应用中的两个核心内容——逆矩阵和转置矩阵。逆矩阵和转置矩阵在实际应用中大有用处，比如：坐标系中的图形变换运算。我们先来看下什么是逆矩阵。

逆矩阵

下面这个图你应该非常熟悉了，图中表现的是数字的倒数，2 的倒数是 21​，21​ 的倒数是 2。

其实逆矩阵也有着类似的概念，只不过是写法不一样，我们会把逆矩阵写成 A−1。那为什么不是 A1​ 呢？那是因为数字 1 无法被矩阵除。

我们知道，2 乘以它的倒数 21​ 等于 1。同样的道理，A 乘以它的逆矩阵 A−1 就等于单位矩阵，即 A×A−1=I（I 即单位矩阵），反过来也一样，A−1×A=I。

为方便你理解，我用一个 2×2 矩阵 A 来解释一下逆矩阵的算法。首先，我们交换 a11​ 和 a22​ 的位置，然后在 a12​ 和 a21​ 前加上负号，最后除以行列式 a11​a22​−a12​a21​。

A−1=[a11​a21​​a12​a22​​]−1=a11​a22​−a12​a21​1​[a22​−a21​​−a12​a11​​]

那我们该如何验证这是不是正解呢？

方法其实很简单，记得刚才的公式就行，A×A−1=I。现在我们就代入公式来验证一下，A 和它的逆矩阵相乘，通过刚才的算法最终得出的结果是单位矩阵。

A×A−1=[a11​a21​​a12​a22​​][a11​a21​​a12​a22​​]−1=[a11​a21​​a12​a22​​][a11​a22​−a12​a21​a22​​a11​a22​−a12​a21​−a21​​​a11​a22​−a12​a21​−a12​​a11​a22​−a12​a21​a11​​​]=[a11​a22​−a12​a21​a11​×a22​​+a11​a22​−a12​a21​a12​×(−a21​)​a11​a22​−a12​a21​a21​×a22​​+a11​a22​−a12​a21​a22​×(−a21​)​​a11​a22​−a12​a21​a11​×(−a12​)​+a11​a22​−a12​a21​a12​×a11​​a11​a22​−a12​a21​a21​×(−a12​)​+a11​a22​−a12​a21​a22​×a11​​​]=[10​01​]

这里有一点需要特别说明，不是每一个矩阵都是可逆的。如果一个矩阵是可逆的，那这个矩阵我们叫做非奇异矩阵，如果一个矩阵是不可逆的，那这个矩阵我们就叫做奇异矩阵，而且如果一个矩阵可逆，那它的逆矩阵必然是唯一的。

还记得行列式 a11​a22​−a12​a21​ 吗？如果我们要证明矩阵是可逆的，只要证明行列式不等于零就行。更高阶的逆矩阵的算法也是一样的原理。

最后，我想通过一个现实生活中的案例来让你更多地了解逆矩阵。

一个旅游团由孩子和大人组成，去程他们一起做大巴，每个孩子的票价 3 元，大人票价 3.2 元，总共花费 118.4 元。回程一起做火车，每个孩子的票价 3.5 元，大人票价 3.6 元，总共花费 135.2 元。请问旅游团里有多少小孩和大人？

首先，我们设置一些矩阵，组成线性方程 XA=B。

要解 X，我们就要先计算 A 的逆矩阵 A−1：

A−1=[33.2​3.53.6​]−1=3×3.6−3.5×3.21​[3.6−3.2​−3.53​]=[−98​8.75−7.5​]

接下来再计算 X=BA−1：

[x1​​x2​​]=[118.4​135.2​][−98​8.75−7.5​]=[16​22​]

最终，我们得出这个旅游团有 16 个小孩和 22 个大人。

这也是解线性方程组的一种方法，类似这样的计算被广泛应用在各领域中，比如建筑工程、游戏和动画的 3D 效果上。虽然现在有很多程序包封装了这类数学计算的底层实现，但如果你能很好地理解这些概念，就可以为编程或算法调优打下坚实的基础。

Last but not least，方程次序很重要，也就是说，AX=B 和 XA=B 的结果是不同的，这个一定要牢记哦！

转置矩阵

一般伴随逆矩阵之后出现的就是转置矩阵。在计算机图形图像处理中，如果要对一个物体进行旋转、平移、缩放等操作，就要对描述这个物体的所有矩阵进行运算，矩阵转置就是这类运算之一，而矩阵的转置在三维空间中的解释就相当于“得到关于某个点对称的三维立体”。所以，转置矩阵的定义很简单。

将矩阵的行列互换，得到的新矩阵就叫做转置矩阵（transpose）。转置矩阵的行列式不变。我们把 m×n 矩阵 A 的行列互换，得到转置矩阵 AT。

A=⎣⎢⎢⎡​a11​a21​…am1​​a12​a22​…am2​​…………​a1n​a2n​…amn​​⎦⎥⎥⎤​

AT=⎣⎢⎢⎡​a11​a12​…a1n​​a21​a22​…a2n​​…………​am1​am2​…amn​​⎦⎥⎥⎤​

最后，为了方便你理解，我们再总结一下逆矩阵和转置矩阵的性质。你不用死记硬背，重在理解。

矩阵和自身逆矩阵相乘得道单位矩阵，AA−1=I=A−1A；

AB 两矩阵相乘的逆，等于逆矩阵 B 和逆矩阵 A 相乘，这里强调一下乘的顺序很重要，(AB)−1=B−1A−1；

AB 两矩阵相加后的逆矩阵，不等于各自逆矩阵的相加， (A+B)−1​=A−1+B−1；

矩阵转置的转置还是它本身，(AT)T=A；

AB 两矩阵相加后的转置矩阵，等于各自转置矩阵的相加，(A+B)T=AT+BT；

AB 两矩阵相乘后的转置矩阵，等于转置矩阵 B 和转置矩阵 A 的相乘，这里再次强调乘的顺序很重要，(AB)T=BTAT。

本节小结

好了，到这里矩阵这一讲就结束了，最后我再带你总结一下前面讲解的内容。

今天的知识，你只需要知道矩阵是线性方程组的另一种表达，了解和掌握矩阵的定义和性质就足够了。当然，矩阵还有很多内容，但我认为掌握了我讲的这些内容后，就为以后的一些矩阵应用场景打下了坚实的数学基础，也是下一讲的解线性方程组的前置知识。

线性代数练习场

对于 10 维列向量 x=(x1​,…,x10​)T， v=(v1​,…,v10​)T，如果要计算 y=xxT(I+vvT)x，其中 I 是 10 阶单位矩阵。你会怎么做？

友情提醒，这里有多种方式解题。你能不能找到一个最简单的方法来解这道题？虽然结果很重要，但我想说的是过程更重要，而且往往解题过程不同，从计算机角度来说，运算的效率会有极大的不同。

欢迎你在留言区晒出你的运算过程和结果。如果有收获，也欢迎你把这篇文章分享给你的朋友。

04 | 解线性方程组：为什么用矩阵求解的效率这么高？

你好，我是朱维刚。欢迎你跟我一起重学线性代数！

在上一节课中，我讲解了线性方程组的另一种表达——矩阵。那么今天，我们就来讲解一下如何使用矩阵来解线性方程组，也就是如何求线性方程组的特殊解和通用解。

简单的线性方程组，我们当然可以运用初中学过的知识来求解，那复杂的呢？硬来几乎是不可能的了，一方面是因为人工计算的错误率很高，另一方面，即使我们使用计算机，用类似 for 或 while 循环来实现算法，它的计算效率也是极低的。你需要用更科学的方式、方法，从另一个角度来看待和求解线性方程组。

而矩阵就是为我们打开高效之门的钥匙，从计算机科学的角度来说，使用矩阵的运算效率实在是高太多了，因为它可以利用计算机的并行能力，甚至在一些迭代法中，还能实现分布式并行计算（迭代法会在后面“应用篇”中讲解）。

线性方程组解的寻找

现在，就让我们开始去寻找线性方程组的解。在之前的课程中，我们已经引入了线性方程组的一般表达，你可以看看下面的例子。

⎩⎪⎪⎨⎪⎪⎧​a11​x1​+a12​x2​+⋯+a1n​xn​=b1​a21​x1​+a22​x2​+⋯+a2n​xn​=b2​⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯am1​x1​+am2​x2​+⋯+amn​xn​=bm​​

其中，aij​ 和 bi​ 属于实数，而且是已知常数，而 xj​ 是未知变量，i 和 j 的取值范围分别是：i=1,…,m；j=1,…,n 。如果我们用矩阵的简单表达方式来看的话，就是 Ax=B。

要搞清楚概念，我们还是要多看具体的例子。让我们先来看一个实例，来加深一下理解。

[10​01​82​−412​]⎣⎢⎢⎡​x1​x2​x3​x4​​⎦⎥⎥⎤​=[428​]

很明显，这是一个矩阵表达方式，它的一般线性方程组表达方式是中学的基础知识，你应该很熟悉了。

{1×x1​+0×x2​+8×x3​+(−4)×x4​=420×x1​+1×x2​+2×x3​+12×x4​=8​

在这个一般线性方程组中，有四个未知变量，但只有两个等式，这就意味着这个线性方程组有无穷多个解（这个是中学数学的范畴）。通过细心观察，我们可以发现第一列和第二列都是由 0 和 1 组成的，因此你很容易就能发现其中一个解。

42[10​]+8[01​]=[428​]

这个解就是 [42​8​0​0​]T，也就是说四个未知变量分别为 42、8、0、0。

⎩⎪⎪⎨⎪⎪⎧​x1​=42x2​=8x3​=0x4​=0​

这个解也叫做特殊解。我们刚才已经说过，这个线性方程组有无穷多个解，那我们确实需要一个聪明的方式来找到其他的解，最直观的方式就是通过矩阵的列来构造 0。例如，对于第三列来说，我们可以使用第一和第二列的组合形式来表达。

8[10​]+2[01​]=[82​]

通过计算 Ax=0，我们得出解 [8​2​−1​0​]T。而事实上，这个解可以乘以任何实数 λ1​，使得 Ax=0 成立。

[10​01​82​−412​]⎝⎜⎜⎛​λ1​⎣⎢⎢⎡​82−10​⎦⎥⎥⎤​​⎠⎟⎟⎞​=0

同理，对于第四列来说，我们可以使用第一和第二列的组合形式来表达，得出另一套解，使得 Ax=0。

[10​01​82​−412​]⎝⎜⎜⎛​λ2​⎣⎢⎢⎡​−4120−1​⎦⎥⎥⎤​​⎠⎟⎟⎞​=0

现在，我们可以把之前的特殊解与刚得出的两套解相组合，得出最终解，这个解也就是我们所说的通用解了。

x∈R4:x=⎣⎢⎢⎡​42800​⎦⎥⎥⎤​+λ1​⎣⎢⎢⎡​82−10​⎦⎥⎥⎤​+λ2​⎣⎢⎢⎡​−4120−1​⎦⎥⎥⎤​,λ1​,λ2​∈R

我来总结一下寻找通用解的过程，这个过程分为三步：

我们要寻找一个特殊解，使得 Ax=b；

找到 Ax=0 的所有解；

组合第一和第二步的解形成通用解。

看到了这里，你有没有发现有些奇怪呢？或者说，有没有觉得哪里有点别扭？是的，好像有点太顺利了。那是因为这个线性方程组比较特别，第一列和第二列是由 1 和 0 组成的。所以，我们只通过观察就能得出特殊解和通用解。

然而，你不可能每次都行大运，就像我们在现实中碰到的这类线性方程组，一般都比这个复杂得多。不过不要慌，有一个算法可以来帮助我们转换任意线性方程组，形成类似的特殊形式，这个算法叫做高斯消元法。

高斯消元法的核心就是线性方程组的初等变换，于是，我们可以通过高斯消元法，得到围绕初等变换形成的简单矩阵表达形式，接下来我们就可以运用之前的三个步骤来寻找通用解了。

初等变换的一般形式

既然高斯消元法的核心就是线性方程组的初等变换，那为了方便你使用高斯消元法，我就有必要来讲一讲初等变换的一般形式有哪些：

两个等式的交换，也就是矩阵行交换；

一个等式，或者说矩阵行乘以一个实数常量；

两个等式相加，或者说矩阵的两行相加。

道理是这样的道理，那我们通过一个例子来看看，究竟该怎么做线性方程组的初等变换。假设 a 属于实数，现在我们试着来寻找下面这个线性方程组的所有解。我把这个过程细细地拆解为 11 个步骤，建议你仔细看过并理解后，再进入下一阶段的学习。

⎩⎪⎪⎨⎪⎪⎧​−2x1​+4x2​−2x3​−x4​+4x5​=−34x1​−8x2​+3x3​−3x4​+x5​=2x1​−2x2​+x3​−x4​+x5​=0x1​−2x2​−3x4​+4x5​=a​

1. 我们要把这个线性方程组转换成矩阵的表达形式，Ax=b。

⎣⎢⎢⎡​−2411​4−8−2−2​−2310​−1−3−1−3​4114​∣∣∣∣​−320a​⎦⎥⎥⎤​

2. 接着我们来交换第一和第三行。

⎣⎢⎢⎡​14−21​−2−84−2​13−20​−1−3−1−3​1144​∣∣∣∣​02−3a​⎦⎥⎥⎤​

注意，你知道我们为什么选择第一行和第三行交换吗？其实，这是为了便于计算。而具体交换哪一行是有个小技巧的，如果某行的第一个元素有 1，我们就可以把这一行移到第一行。

3. 我们以第一行为基础，开始执行乘和加变换，将第一行乘以 -4 的结果和第二行相加，从而获得了下面这样的结果。

⎣⎢⎢⎡​10−21​−204−2​1−1−20​−11−1−3​1−344​∣∣∣∣​02−3a​⎦⎥⎥⎤​

4. 然后，我们用同样的方法，将第一行乘以 2 的结果，再和第三行相加，得到了下面这样的结果。

⎣⎢⎢⎡​1001​−200−2​1−100​−11−3−3​1−364​∣∣∣∣​02−3a​⎦⎥⎥⎤​

5. 以此类推，我们将第一行乘以 -1 的结果，和第四行相加，继续获得新矩阵。

⎣⎢⎢⎡​1000​−2000​1−10−1​−11−3−2​1−363​∣∣∣∣​02−3a​⎦⎥⎥⎤​

6. 将第二行乘以 -1 的结果，和第四行相加，得到下面这样的结果。

⎣⎢⎢⎡​1000​−2000​1−100​−11−3−3​1−366​∣∣∣∣​02−3a−2​⎦⎥⎥⎤​

7. 将第三行乘以 -1 的结果，和第四行相加。

⎣⎢⎢⎡​1000​−2000​1−100​−11−30​1−360​∣∣∣∣​02−3a+1​⎦⎥⎥⎤​

8. 第二行乘以 -1，第三行乘以 −31​。

⎣⎢⎢⎡​1000​−2000​1100​−1−110​13−20​∣∣∣∣​0−21a+1​⎦⎥⎥⎤​

9. 现在，这个矩阵就是一个简单形式的矩阵，也叫做行阶梯形矩阵（Row-Echelon Form，REF）。

⎩⎪⎪⎨⎪⎪⎧​x1​−2x2​+x3​−x4​+x5​=0x3​−x4​+3x5​=−2x4​−2x5​=10=a+1​

一个矩阵成为行阶梯形矩阵需满足两个条件：

如果它既有零行，又有非零行，则零行在下，非零行在上；

如果它有非零行，则每个非零行的第一个非零元素所在列号自上而下严格单调上升，正如之前的这个矩阵，列号自上而下是 1、3、4，是严格单调上升的。

10. 你可以看出，只有在 a=−1 的情况下，这个线性方程组才有解，特殊解是 [2​0​−1​1​0​]T。

11. 最后，我们得出这个线性方程组的通用解，如下图所示。

x∈R5:x=⎣⎢⎢⎢⎢⎡​20−110​⎦⎥⎥⎥⎥⎤​+λ1​⎣⎢⎢⎢⎢⎡​21000​⎦⎥⎥⎥⎥⎤​+λ2​⎣⎢⎢⎢⎢⎡​20−121​⎦⎥⎥⎥⎥⎤​,λ1​,λ2​∈R

注意，这里有一个概念很重要，那就是主元。主元就是在矩阵消元过程中，每列要保留的非零元素，我们可以用它把该列其他元素消去。在阶梯型矩阵中，每个非零行第一个非零元素就是主元。

拿之前的第 8 步计算后的结果来举例，第一行的第一个元素 1 就是主元，第二行第三个元素 1 是主元，第三行的第四个元素 1 也是主元。

对应行阶梯形矩阵主元的变量叫做基本变量，而其他的变量叫做自由变量，这个例子中，x1​、x3​、x4​ 就是基本变量，x2​、x5​ 则是自由变量。使用行阶梯形矩阵能更简单地得出特殊解，所以我们可以使用主元列来表达线性方程组：

b=i=1∑P​λi​pi​,i=1,…,P

在之前的例子中，我们使用主元列来表达成下面这样的矩阵形式：

λ1​⎣⎢⎢⎡​1000​⎦⎥⎥⎤​+λ2​⎣⎢⎢⎡​1100​⎦⎥⎥⎤​+λ3​⎣⎢⎢⎡​−1−110​⎦⎥⎥⎤​=⎣⎢⎢⎡​0−210​⎦⎥⎥⎤​

于是，我们最终得出 λ3​=1，λ2​=−1，λ1​=2 ，分别对应于 x4​、x3​、x1​。不要忘了，对于非主元列，我们已经隐式地把系数设置成了 0，所以这个线性方程组的特殊解是 x=[2​0​−1​1​0​]T。

简化行阶梯形矩阵

这里我们再引入一个概念，简化行阶梯形矩阵，因为引入简化行阶梯形矩阵对于线性方程组的求解来说会更简单。其实，高斯消元法的核心就是通过初等变换，把线性方程组转换成简化行阶梯形矩阵。那么一个方程组是简化行阶梯形矩阵，必须满足哪几个条件呢？

这个方程组必须是行阶梯形矩阵；

方程组的每一个主元都是 1；

主元在它的列中是唯一的非 0 元素。

现在，我们再通过一个实例，看看该如何通过高斯消元法计算一个矩阵的逆矩阵。设矩阵 A 如下图：

A=⎣⎢⎢⎡​1111​0121​2001​0011​⎦⎥⎥⎤​

首先，我们形成 A 的增广矩阵（具体方法参见上一节）。

⎣⎢⎢⎡​1111​0121​2001​0011​∣∣∣∣​1000​0100​0010​0001​⎦⎥⎥⎤​

其次，使用我们前面刚刚讲过的高斯消元法计算出简化行阶梯形矩阵。

⎣⎢⎢⎡​1000​0100​0010​0001​∣∣∣∣​−111−1​2−1−10​−221−1​2−2−12​⎦⎥⎥⎤​

最后，我们就得到 A 的逆矩阵，如下图所示。

A−1=⎣⎢⎢⎡​−111−1​2−1−10​−221−1​2−2−12​⎦⎥⎥⎤​

接下来，我们只要使用公式 AA−1=I 就可以对结果进行验证了。

更多解线性方程组的方法

到目前为止，相信你已经了解了如何解线性方程组，包括特殊解和通用解，以及如何使用高斯消元法来解线性方程组。最后，我再总结一些解方法来作为你的知识扩展。

第一个方法，假设一个矩阵 A 是方阵（行数与列数相等的矩阵），并且可逆，Ax=B ，那 x 解就可以写成 x=A−1B，但如果 A 矩阵不可逆，也不是方阵，那我们就只能使用下面这个变换来求 x 解了。

Ax=B⇔ATAx=ATB⇔x=(ATA)−1ATB

其中，矩阵 A 的转置矩阵和 A 相乘的逆矩阵，再和 A 的转置矩阵相乘，我们把它叫做穆尔彭罗斯伪逆矩阵（Moore-Penrose pseudo inverse），简称伪逆。

(ATA)−1AT

这个方法有两个弊端：第一，矩阵乘和逆矩阵的计算太复杂；第二，数值精确度不高。因此，从实践角度来说，我一般不推荐使用。

第二个方法是高斯消元法。高斯消元法是非常直观的，它在很多计算中都起到了关键的作用，比如：

计算行列式；

检查向量是否是线性独立的；

计算矩阵的逆矩阵；

计算矩阵的秩；

决定向量空间的基。

但当高斯消元法面对百万、千万级别的变量时，就捉襟见肘了。而这类级别的计算才是我们在实践中经常会遇到的，因此从实践角度来说，我也一般不推荐使用。因为高斯消元法属于直接法，直接法是经历有限次的运算得到方程组精确解的方法。但是，学习直接法是有意义的，虽然直接法在实际工作中不常用，但是它也能处理一些日常小问题，更重要的是，它稳固了我们进一步学习其它方法的基础。

我要讲的第三种方法，就是与直接法对应的间接法了。在实践中，线性方程组的求解都是间接的，也就是运用迭代法。

迭代法是采用极限过程，用线性方程组的近似解逐步逼近精确解的方法。所以，迭代法的关键在于每次迭代残余错误的减少，以及如何能够收敛到解。常见的迭代法有两类，定常迭代法（Stationary iterative method）和 Krylov 子空间方法（我会在应用篇中讲解）。

定常迭代法：理查德森迭代法（Richardson method）、雅可比方法（Jacobi method）、Gauß-Seidel 方法、逐次超松弛法（Successive over-relaxation method，简称 SOR）。

Krylov 子空间方法：共轭梯度法（Conjugate gradient）、 广义极小残余算法（Generalized minimal residual）、双共轭梯度法（Biconjugate gradient）。

这里提到的几种迭代法都是在实践中比较常用的，也是计算机编程中经常实现的算法，但由于迭代法更多属于微分和极限领域，所以这里就不详细介绍了，我会在线性代数应用篇的“数值线性代数”那节课中再做讲解。

如果在课程内容结束后，你还有余力学习更多的内容，这里我先推荐两本书给你作参考，一本是《Introduction to Numerical Analysis》，另一本是《Linear Algebra》。这两本书里面都有进一步地讲解了线性方程组的迭代法求解的内容。

1.《Introduction to Numerical Analysis》

作者：Stoer, Josef, Bulirsch, R.

2002 年出版

2.《Linear Algebra》

作者：Liesen, Jörg, Mehrmann, Volker

2015 年出版

本节小结

好了，到这里解线性方程组这一讲就结束了，最后我再总结一下前面讲解的内容。

首先，我用一个简单的线性方程组，通过直接观察的方法来计算这个方程组的特殊解和通用解，接着通过实例详细地介绍了高斯消元法，最后我给出了一些在实践中常用的线性方程组解方法。只有弄清楚这些基础知识的本质，你才能更进一步，去了解其他计算方法。

线性方程组的求解已经成为了世界上最快计算机的测试标准，因为通过矩阵运算，计算机的并行计算能力暴露无遗。希望你能够在这些基础之上，阅读我推荐的两本书，并且把这些方法运用到实践中，特别是机器学习，因为机器学习也用到了很多迭代方法。

线性代数练习场

练习时刻到了，今天的练习题比较简单，请你用高斯消元法求下面的线性方程组。

⎩⎪⎪⎨⎪⎪⎧​x1​+x2​−2x3​−x4​=−1x1​+5x2​−3x3​−2x4​=03x1​−x2​+x3​+4x4​=2−2x1​+2x2​+x3​−x4​=1​

欢迎在留言区和部落里晒出你的运算过程和结果，留下你的学习痕迹。如果你有所收获，也欢迎你把这篇文章分享给你的朋友。

05 | 线性空间：如何通过向量的结构化空间在机器学习中做降维处理？

你好，我是朱维刚。欢迎你跟我一起重学线性代数！

今天我们来聊一聊“线性空间”。在“基本概念”那一节课中，我讲到了向量，你也看到了，线性方程组是能够通过矩阵或向量来表达的。那为什么我们还要学习线性空间呢？

说到线性空间，其实你可以通过“空间”这个词把线性空间和我们的生活做个类比。就像我们生活在三维世界中，在这个空间中，一切物质都是运动的，而运动也是有一定规律的。这么来看的话，空间其实就是一个具有实际意义的集合，其中包含了对象和运动。

把这个理解平移到线性空间也是一样的，向量就是对象，如果把向量看成是线性空间中的点，那向量的变换就是点在空间中的运动。所以，线性空间也是一个集合，它的意义在于，赋予了向量生命和活力，只有掌握了线性空间，我们才能真正在实际运用中有的放矢。因为所有的活动都要在这个空间中发生，比如：线性空间中用到的傅立叶变换。

组

还是老样子，我们要先从学习线性空间会用到的基础知识开始讲起。

我们先来讲一下“组”，看一看组有什么性质。说到“组”，它其实是一个通用的概念，和线性空间没有什么关系，但我之所以要先说组，是因为组和空间是类似的，也是集合，性质也差不多，如果你了解了组，就更容易理解线性空间了。而且，组在计算机科学中是得到了广泛应用的，特别是在计算机密码学和图形图像处理中。

说了这么多，“组”到底是什么呢？组，其实就是包含一系列元素的集合，在对这些集合元素实施某类运算后，这个集合仍然保持着封闭性。可能这么说你会有些疑惑，我还是通过数学方法来定义组，可能会让你的思路更加清晰一些。

我们先来定义一个集合 G 和集合上的某一类运算，比如：乘 ⊗，使得 G⊗G 的结果还是属于 G，如果我们要 G:=(G,⊗) 是一个组，则需要满足以下这些条件：

1.G 在 ⊗ 运算中是封闭的，也就是：∀x,y∈G:x⊗y∈G。

2. 满足结合律，也就是：∀x,y,z∈G:(x⊗y)⊗z=x⊗(y⊗z)。

3. 恒等元素（或者叫做中性元素）e，满足：∃e∈G,∀x∈G:x⊗e=x,e⊗x=x，这里的恒等元素 e 在一般数字中你可以认为是 1，而在矩阵中就可以认为是单位矩阵。

4. 有 x 的逆元素 y，使得：∀e∈G,∃x∈G:x⊗y=e,y⊗x=e，其中 e 是恒等元素。

再补充一点，如果满足 ∀x,y∈G:x⊗y∈y⊗x，则 G:=(G,⊗) 就叫作交换组。

现在我们来做个测试，看看你是否理解了组的定义。

一个 n×n 的实数矩阵 A 和它的乘法运算是一个组吗？通过符号表达就是：(An×n,⋅)。

想要知道这个问题的答案，我们就需要用前面满足组的这几个条件来分析一下。

首先，是封闭性和结合律，从矩阵乘的定义就能直接看出来，它们是满足的；其次，我们来看恒等元素，单位矩阵就是矩阵元素，也满足组条件；最后，我们看看逆元素，假设 A 矩阵的逆矩阵 A−1 存在，那很明显，满足 AA−1=I，这里 I 就是恒等元素。

于是，我们可以说 (An×n,⋅) 是一个组，而矩阵乘不符合交换律，所以这个组并不是交换组。

向量空间

如果我们在“组”的基础上再扩展一下，就能够很顺利地来到“线性空间”。说起线性空间，它也叫作向量空间，它在一些书本和网络上的解释都是比较晦涩难懂的，但如果我们在“组”的基础上来解释它，你应该会比较容易理解了。

刚才我们说的组只包含了某一类运算，这类运算是在集合元素上的内部运算，我们把它定义为加 （+） 运算，现在再引入一类外部运算，标量乘 （⋅）。于是，你可以想象一下，我们可以把内部运算看成是加法，把外部运算看成是“缩放”，因为标量乘就是一个标量和向量相乘。如果从二维坐标系的角度来看一下，点 (1,1) 和标量 2 相乘就是 (2,2)，这个就是放大效果。

在通过“组”来认识向量空间后，再从数学角度去看向量空间的定义，你应该就能完全理解了。

一个实数向量空间 V 是一个集合，它包含了两类运算，一类是加，一类是标量乘，而且运算都满足 V 的封闭性，也就是说，V 中元素的运算结果还是属于 V。

+:V+V→V⋅:λ⋅V→V​

这个向量空间可以表示成 V:=(V,+,⋅)，其中：

1. 向量空间 V 的 (V,+) 是一个交换组。

2.V 满足分配律：∀λ∈R,x,y∈V:λ⋅(x+y)=λ⋅x+λ⋅y；以及 ∀λ,φ∈R,x∈V:(λ+φ)⋅x=λ⋅x+φ⋅x。

3.V 外部运算满足结合律：∀λ,φ∈R,x∈V:λ⋅(φ⋅x)=(λ⋅φ)⋅x。

4.V 外部运算的恒等元素满足：∀x∈V:1⋅x=x。

在向量空间 V 中的元素 x 是向量，向量空间加运算 (V,+) 的恒等元素是零向量 0=[0,​…​,0​]T。这里的加运算是内部运算，也叫做向量加，元素 λ 属于实数，叫做标量，外部运算乘 ⋅ 是标量乘。

好了，我给出了向量空间的一般描述和数学定义，如果你还是有一些不理解，也没有关系，我再举两个例子来加深你对向量空间的理解。

例 1：进一步理解向量加和标量乘

对于向量空间的向量加和标量乘：我们定义一个实数向量空间 Rn，n 表示向量元素：

“加”定义为向量之间的加：x+y=(x1​,…,xn​)+(y1​,…,yn​)=(x1​+y1​,…,xn​+yn​)。 加的结果还是属于向量空间 Rn。

标量乘就是向量乘标量：λx=λ(x1​,…,xn​)=(λx1​,…,λxn​)。

标量乘的结果还是属于向量空间 Rn。

例 2：进一步理解矩阵加和标量乘

对于向量空间的矩阵加和标量乘：我们定义一个实数向量空间 Rm×n，用 m×n 表示 m 行 n 列矩阵元素：

我们把“加”定义为矩阵之间的加。加的结果还是属于向量空间 Rm×n。

A+B=⎣⎢⎢⎢⎢⎡​a11​+b11​⋅⋅⋅am1​+bm1​​……​a1n​+b1n​⋅⋅⋅amn​+bmn​​⎦⎥⎥⎥⎥⎤​

而标量乘就是矩阵乘标量。标量乘的结果还是属于向量空间 Rm×n。

λA=⎣⎢⎢⎡​λa11​⋅⋅λm1​​……​λa1n​⋅⋅λa˙mn​​⎦⎥⎥⎤​

到这里，相信你应该了解了向量空间的基本概念，接下来这一讲的重头戏就要来了，它就是向量子空间。

向量子空间

为什么说向量子空间是重头戏？那是因为它在机器学习中的地位相当重要，被用在了降维算法中。这里我会分两步来讲解，先讲向量子空间的基本概念，再通过一个机器学习的例子，能让你更了解它，并灵活运用在工作实践中。

什么是向量子空间？

从“子”这个字，我们可以很直观地想到，它是被包含在向量空间中的，事实也确实如此。

已知 V:=(V,+,⋅) 是一个向量空间，如果 U⊆V,U​=0，那么 U:=(U,+,⋅) 就是 V 的向量子空间，或者叫做线性子空间。向量子空间 U 自然继承 V 的许多属性，其中包括：交换组的属性、分配律、结合律和中性元素。除此以外，要判断 U 是不是向量子空间，我们还需要这两个条件：

1.U​=0，但 0∈U。

2. U 的封闭性满足外部运算：∀λ∈R,∀x∈U:λx∈U，同时满足内部运算：∀x,y∈U:x+y∈U。

介绍完向量子空间基本概念后，我们一起来通过一个例子来巩固一下所学的知识，看看你是否已经掌握了向量子空间。

请你观察下面列举的 A、B、C 三张图像，里面有 R2 的向量子空间吗？

这里我不会给出答案，你可以自己思考一下，友情提醒：A、B、C 中只有一个是向量子空间。

机器学习中的向量子空间

结合实践来看向量子空间的时候到了。在机器学习中，直接计算高维数据困难重重，一方面是数据处理和分析困难，使得数据可视化几乎不可能；另一方面是因为数据存储量太大，计算要付出的代价太高。

所以，我们要从向量空间中去除冗余数据，形成向量子空间。这样数据存储量就被极大地压缩了，处理和分析数据也简单了很多。因为高维数据中其实有很多维是冗余的，它们可以被其它维组合表示，也就是“降维”。

降维就是利用结构化和相关性，在尽量保证信息不损失的情况下，转换数据表现形式，让数据更“紧凑”。换句话说，你可以把降维看成是数据压缩技术，类似图像的 jpeg 和音频的 MP3 压缩算法。或者简单地说，降维就是将数据投射到一个低维子空间，比如：三维数据集可以降成二维，也就是把数据映射到平面上。

机器学习中运用最多的降维算法就是主成分分析，简称 PCA（Principal Component Analysis），也叫做卡尔胡宁 - 勒夫变换（Karhunen-Loeve Transform）。它是一种用于探索高维数据结构的技术，已经存在超过 100 年了，但至今仍然广泛被使用在数据压缩和可视化中。

我们来看一个例子：假设你负责的是机器学习算法，而你的应用场景是车辆的牌照识别，也就是 OCR（Optical Character Recognition）光学字符识别。在这个场景中，大街上的摄像头必须实时捕捉运动车辆的牌照，一旦发现问题车辆就需要快速识别牌照，并移交交警监管部门来做进一步处理。你会怎么处理呢？

牌照被拍下后就是图片，为了减小图像原始数据量，减少后续处理时的计算量，这些图片首先需要进行经过灰度处理（牌照只需要数字，不需要对彩色图像的 RGB 三个分量都进行处理），处理后就会变成类似这样的形式：

假定每个数字是一个 28∗28 尺寸的灰度图片，包含 784 个像素，那每张灰度数字图片就是一个向量，这个向量就有 784 个维度，可以表示成 x∈R784，而你的样本库少说也有几十万个样本数据，如果按一般的方法是不可能做到实时识别的。所以，这样的场景就需要使用 PCA 来压缩数据，进行大幅度降维。

这里我们简单一些，从二维的角度来看看 PCA。在 PCA 中，最关键的就是寻找数据点 xn​ 的相似低维投影 yn​，而 yn​ 就是子向量空间。

考虑 R2 和它的两个基，e1​=[1,0]T、e2​=[0,1]T，x∈R2 能够表示成这两个基的线性组合（“基”会在第 7 节课中详细介绍）。

[53​]=5e1​+3e2​

于是，相似低维投影 yn​ 就可以表示成下面这种形式。

yn​=[0z​]∈R2,z∈R

同时，yn​ 也可以写成这样的形式：yn​=0e1​+ze2​。

这里的 z 就是我们要找的值，而 yn​ 就是一个向量子空间 U，它的维度是一维。最后，我们再通过下图来更直观地说明一下 PCA 的过程。

图的左边是原始向量空间 x，经过压缩后，我们找到了子向量空间 z，z 经过重构后，形成了最终的向量空间 y，y 还是属于原来的向量空间，但 y 却拥有比 x 更低的维度表现。

从数学的角度看，我们其实就是在寻找 x 和 z 之间的线性关系，使得 z=BTx，以及 y=Bz，其中 B 是矩阵。如果我们从数据压缩技术方向来看就更容易理解了，图中的左边箭头是编码过程，也就是压缩，右边的箭头是解码过程，也就是映射，而矩阵 B 就是把属于 RM 向量空间的低维的 z，映射回原来的向量空间 RD。同理，矩阵 BT 就是把属于原来 RD 向量空间的高维 x 压缩成低维的 z。

本节小结

好了，到这里线性空间这一讲就结束了，最后我再总结一下前面讲解的内容。

今天的知识很重要，实践中都是围绕向量空间展开的，也就是说向量空间是实践的基本单位，你也一定要掌握子向量空间，因为现实中数据都是高维度的，从向量空间降维后找到子向量空间，这样就能大大提高数据运算和分析的效率。

再次特别提醒：这一讲非常重要，因为后面几讲都是围绕向量空间展开的，如果你哪里没看懂，一定要多看几次，确保完全明白了。有任何问题，你也可以随时在留言区向我提问。

线性代数练习场

之前我讲了一个现实的向量空间降维场景：车辆的牌照识别，这里，我们通过另一个现实场景，来练习一下向量空间降维的思维。

目前市场上语音识别的应用有很多，比如：天猫精灵、苹果 Siri、小爱等等，而语音识别涉及的技术有很多，有语言建模、声学建模、语音信号处理等等。在语音信号处理中，语音声波通过空气传播，并被麦克风捕获，麦克风将压力波转换为可捕获的电活动。我们对电活动进行采样，用以创建描述信号的一系列波形采样。

采样是数据收集的过程，数据收集后需要做数据预处理，而预处理的关键一步就是特征提取，现在请你从“特征提取”的方向上思考下，有哪些和目前所学到的数学知识有关？

友情提醒：特征提取就是数字化过程，也是向量化后形成向量空间的过程。

欢迎在留言区写出你的思考，我会及时回复。如果有收获，也欢迎你把这篇文章分享给你的朋友。

06 | 线性无关：如何理解向量在N维空间的几何意义？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我们要讲的内容是“线性无关”。

上一节课中，我讲的是线性空间的基本概念，是立足于宏观角度来讲的。那么今天，我们就要深入线性空间，从微观角度，再来看看线性空间中元素之间的关系，也就是线性组合。

线性组合有线性相关和线性无关，而线性无关是线性代数中最重要的概念之一，为什么这么说呢？因为线性相关的向量组中存在多余的向量，去掉它们不影响我们所考虑的问题。而线性无关向量集合是没有任何冗余的，也就是说，失去集合中任意一个向量，我们都会失去一些东西。接下来我们就开始把这个“直观上的理解”固化成实实在在的知识体系。

在正式开始讲解前，我还是一样，先通过一个例子，让你对线性组合有个大致的了解。

假设，有一家物流运输公司 Y，Y 主要靠车辆的货物运输来赚钱，而且 Y 拥有很多直营的运输车辆，设共有 n 辆车，｛x1​,x2​,…,xn​｝，那么，Y 公司的收入可以表示成：Yi​=a0​+a1​x1​+a2​x2​+⋯+an​xn​。

这是个线性方程，它的系数 a 表示了各辆车对收入的贡献率，a0​ 表示企业的日常总支出。这时企业内，任何一辆车对收入的贡献大小和这个企业其他的车都没有关系，所以各车之间就是线性无关的。如果这时，我们再聚焦到每辆车本身的利润上，比如我们都知道的这个公式：利润 = 收入 - 成本，那每辆车的利润、成本和收入之间就是线性相关的。

线性组合

在例子中，我们已经知道了线性组合中的线性相关、线性无关的意思。那么接下来，我们来看线性组合的确切定义：一个向量空间 V 和属于这个向量空间的有限数量的向量 ｛x1​,x2​,…,xk​｝，对于属于向量空间 V 的每个向量 v，都有这样的表达形式：v=λ1​x1​+λ2​x2​+⋯+λk​xk​=∑i=1k​λi​xi​。

那么，v 就是向量 ｛x1​,x2​,…,xk​｝ 的线性组合。

我们继续看线性相关和线性无关的定义，一个向量空间 V 和属于这个向量空间的有限数量的向量 ｛x1​,x2​,…,xk​｝，有一个非平凡线性组合：0=λ1​x1​+λ2​x2​+⋯+λk​xk​=∑i=1k​λi​xi​。如果其中至少有一个 λ 不等于 0，这时，向量 ｛x1​,x2​,…,xk​｝ 是线性相关的。而如果有平凡解存在，例如：λ1​=…=λk​=0，则向量 ｛x1​,x2​,…,xk​｝ 是线性无关的。

注意：这里有个数学用语“平凡解”，术语“平凡”经常用于结构非常简单的对象，一般来说，Ax=0 中的零解，即 x=0，就叫做平凡解。

现在，我们通过一个实际的例子来加深一下理解。

假如你想从上海去杭州，有两条路线可以供你选择：一条是从上海出发，行驶 84.9 公里后到苏州，再从苏州出发行驶 120.14 公里后到达杭州；另一条是从上海出发行驶 164.7 公里后直接到达杭州。

我们可以把地理位置坐标系看成是一个二维的向量空间，上海到苏州可以表示成向量 v1，苏州到杭州可以表示成向量 v2，上海到杭州可以表示成向量 v3，这样很明显可以看出，向量 v1 和 v2 是线性无关的，而上海到杭州 v3 却可以被另两个向量 v1 和 v2 表达，于是我们可以说 v1，v2 和 v3 之间是线性相关的。

线性无关的判断方式

线性无关的判断，对于实践中数据冗余的判断非常有用，那有没有一些方法来判断向量之间是线性无关的呢？我们来看一些有用的方法吧：

k 向量要么线性无关，要么线性相关，没有第三个选择。

已知向量集合 ｛x1​,x2​,…,xk​｝ 中至少有一个是 0 向量，则它们是线性相关的。

已知有向量集合 ｛x1​,x2​,…,xk​｝ ，其中 xk​​=0 ，如果一个向量等于另一向量和一个标量的乘，xi​=λxj​ ，那么，向量集合 ｛x1​,x2​,…,xk​｝ 是线性相关的。

之前的方式方法都是偏理论的，而更加实践的方法，就是用高斯消元法来判断向量集合 ｛x1​,x2​,…,xk​｝ 是否是线性无关的。将所有向量组合成矩阵的列，做高斯消元，一直到形成行阶梯型矩阵为止。如果所有的列都是主元列，那矩阵所有列向量是线性无关的，反之，如果有至少一个非主元列，那矩阵所有列向量是线性相关的。

现在我们来做个小练习，就用高斯消元法来看一下这 3 个向量是否是线性无关的。

x1​=⎣⎢⎢⎡​12−34​⎦⎥⎥⎤​,x2​=⎣⎢⎢⎡​1102​⎦⎥⎥⎤​,x3​=⎣⎢⎢⎡​−1−211​⎦⎥⎥⎤​

首先，我们把它表示成一般线性方程形式，或者一个非平凡线性组合。

λ1​x1​+λ2​x2​+λ3​x3​=λ1​⎣⎢⎢⎡​12−34​⎦⎥⎥⎤​+λ2​⎣⎢⎢⎡​1102​⎦⎥⎥⎤​+λ3​⎣⎢⎢⎡​−1−211​⎦⎥⎥⎤​=0

接着，把向量组合成矩阵的列，运用行运算，一直到能够识别出主元列为止。

⎣⎢⎢⎡​12−34​1102​−1−211​⎦⎥⎥⎤​⋯⎣⎢⎢⎡​1000​1100​−1010​⎦⎥⎥⎤​

这里，矩阵每一列都是主元列，所以，它没有非平凡解，只有在 λ1​=0，λ2​=0，λ3​=0 的情况下，方程才有解。因此，我们能说向量 ｛x1​,x2​,xk​｝ 是线性无关的。

更普遍和复杂的线性无关判断

理论是这样的，接下来我们再扩展一下学到的知识，把它用到更普遍和复杂的情况中，也就是有 k 个线性无关的向量 ｛b1​,b2​,…,bk​｝，以及 m 个线性组合的情况：

x1​x2​⋅⋅⋅xm​​=i=1∑k​λi1​bi​=i=1∑k​λi2​bi​=i=1∑k​λim​bi​​

如果把这 k 个线性无关的向量组合成矩阵 B，B=[b1​​…​bk​​] ，我们就能用更紧凑的形式来表达：

xj​=Bλi​,λj​=⎣⎢⎢⎢⎢⎡​λ1j​⋅⋅⋅λkj​​⎦⎥⎥⎥⎥⎤​,j=1,…,m

这时，如何判断 ｛x1​,x2​,…,xm​｝ 是否是线性无关的呢？首先，我们用一个非平凡线性组合来测试，就和之前的方法一样，把它表示成这样的形式：∑i=1m​φj​xj​=∑i=1m​φj​Bλj​=B∑i=1m​φj​λj​=0。

接着，从这样的等式可以很容易看出，只有向量 ｛λ1​,λ2​,…,λm​｝ 线性无关，｛x1​,x2​,…,xm​｝ 才是线性无关的。

还是老样子，我们通过一个例子来看下。假设，有一组线性无关的向量 ｛b1​,b2​,b3​,b4​｝，和 4 个线性组合。

⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧​x1​x2​x3​x4​​=b1​−2b2​+b3​−b4​=−4b1​−2b2​+4b4​=2b1​+3b2​−b3​−3b4​=17b1​−10b2​+11b3​+b4​​

接下来我们该怎么判断 ｛x1​,x2​,x3​,x4​｝ 是否是线性无关的呢？按刚才说的方法，我们需要首先找到 λ 向量，通过 λ 向量组合成这样的矩阵：

⎣⎢⎢⎡​1−21−1​−4−204​23−1−3​17−10111​⎦⎥⎥⎤​

接着，使用高斯消元法，一直到形成行阶梯型矩阵为止。高斯消元法的用法已经在第四节课中详细说了，如果你有些记不清，可以回去复习一下。这里我们直接得到了行阶梯型矩阵：

⎣⎢⎢⎡​1000​0100​0010​−7−15−180​⎦⎥⎥⎤​

矩阵的最后一列不是主元列，而且你可以很直观地发现 x4​=−7x1​−15x2​−18x3​，所以，我们可以判断 ｛x1​,x2​,x3​,x4​｝ 是线性相关的，x4​ 能由 ｛x1​,x2​,x3​｝ 的线性组合来表达。

友情提醒：已知在一个向量空间 V 中，有 k 个向量 ｛x1​,x2​,…,xk​｝ 和 m 个线性组合，如果 m>k，那么我们可以说，这 k 个向量 ｛x1​,x2​,…,xk​｝ 的 m 个线性组合是线性相关的。所以，在这样的情况下，就为你省去了计算的时间。

线性组合在机器学习中的应用

在了解了线性组合的概念，以及线性组合中的线性相关，特别是线性无关的判断后，我们来看一个机器学习中的实践例子，来了解一下线性组合是怎么体现在机器学习中的。

机器学习中，最经典，也是最简单的线性组合应用莫过于线性回归了。线性回归是比较常见，也是简单实用的机器学习算法，它是利用数理统计中的回归分析，来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。

线性回归假设目标值与特征之间线性相关，也就是说满足一个多元一次方程。它可以通过构建“损失（loss）”函数，来求解损失函数最小时的参数 w 和 b，也就是说，整个机器学习的过程就是通过样本数据，得到最后的参数 w 和 b。

我们通过一个例子来看看线性回归。

这是一个典型的一元线性回归模型。图中的空心圆点是真实数据点，而红线是一元线性回归模型，是用来做数据预测的，也就是 y=ωTx+b。我们可以根据给定的 x 值通过方程式来计算 y 值。从分布在红色线周围的真实数据点来看，其实我们可以直观的得出结论：这个一元线性回归模型可以被用来很好的做预测。

线性回归在现实生活中的一个典型的应用场景是健身卡路里的燃烧预测，比如输入数据是年龄、性别、身高、体重、健身心跳、健身持久时间，而输出则是燃烧掉的卡路里。

怎么样？机器学习是不是很简单？

其实，机器学习的本质就是用数学来解决现实的问题。而很多看起来简单的数学公式可以解决很多问题，比如这里说的线性组合应用——线性回归。

我最近发现，有不少同学遇到问题，都想着怎么用复杂的机器学习算法去解决问题，特别是高大上的深度学习、神经网络之类的。但其实对于机器学习来说，算法不是越复杂越好，而是越适用越好。你可以先从简单的算法模型入手，先验证效果后，再做进一步的判断，是否需要用更复杂的算法模型。

本节小结

好了，到这里线性无关这一讲就结束了，最后我再总结一下前面讲解的内容。

向量空间是实践的基本单位，之前都是从宏观角度出发的，而今天的知识的重点在于，我从微观角度，深入讲解了线性空间中元素之间的关系，也就是线性组合，线性组合有线性相关和线性无关，而线性无关是线性代数中最重要的概念之一。所以，你一定要掌握线性组合的概念，以及它包含的线性相关，特别是线性无关的判断方式，希望你能多练习线性组合中的线性无关的判断，为实践打好坚实的基础。

线性代数练习场

练习时刻到了，今天的练习题简单一些，和之前举过的例子相似，假设我们有一组线性无关的向量 ｛b1​,b2​,b3​,b4​｝，和 4 个线性组合。

⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧​x1​x2​x3​x4​​=b1​+b2​−2b3​−b4​=b1​+5b2​−3b3​−2b4​=3b1​−b2​+b3​+4b4​=−2b1​+2b2​+b3​−b4​​

请你判断 ｛x1​,x2​,x3​,x4​｝ 是线性无关的吗？

友情提示：通过高斯消元法，我们能得到行阶梯型矩阵，通过行阶梯型矩阵，就可以判断 ｛x1​,x2​,x3​,x4​｝ 是否是线性无关的。

欢迎你在留言区或部落里晒出你的运算过程和结果。如果有收获，也欢迎你把这篇文章分享给你的朋友。

07 | 基和秩：为什么说它表达了向量空间中“有用”的向量个数？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我们要讲的内容是“基和秩”。

了解向量空间和线性组合后，我们必然会推进到基和秩的学习，为什么这么说呢？因为秩表达了向量空间中“有用”的向量个数。

这里“有用”两个字就限定了范围，也就是说，在一个向量空间 V 中，我们是只对某些特殊的向量集合 A 感兴趣的。对于 A 来说，任意属于向量空间 V 的向量，都能够被 A 中的一个向量的线性组合来表示。这里“感兴趣的向量集合”就和秩的应用场景有关了，我们来举几个秩的应用场景：

秩可以帮助我们研究向量组的线性相关问题，从图形图像应用角度来说，就是处理冗余数据的表达，可以被用来恢复数据，或者降噪；

我们可以通过秩来快速判断线性方程组解的情况，有时快速判断线性方程组有解、无解、无穷解几种情况，比“解”本身更重要，因为它能初步得出线性方程组的大致轮廓，让你不用纠结于纯粹的求解；

在解析几何方面，秩可以判断两条直线的位置关系，用在图形的形状和夹角判断上；

秩还可以把解决线性空间的维数问题，简化成分析向量个数问题，化繁为简。

生成集合与基

刚才简单介绍了“有用”的向量，接下来，我来详细刻画一下我们感兴趣的“特殊的向量集合”。

在重点介绍基之前，我们得先来了解“生成集合”的定义：一个向量空间 V，和一个向量集合 A=｛x1​,x2​,…,xk​｝，向量空间 V 包含 A，如果 V 的每一个向量都可以被 x1​,x2​,…,xk​ 的一个线性组合表示，那么 A 就是 V 的一个生成集合。A 的向量的所有线性组合的集合就是 A 的生成空间，我们可以写成这样的形式：V=span[A] 或者 V=span[x1​,x2​,…,xk​]。

我们再进一步，来看看最小生成集合。最小生成集合，顾名思义就是小得不能再小的生成集合，向量空间 V 的每个线性无关的生成集合都是最小生成集合，也叫做基（basis）。基是刻画向量空间的基本工具，掌握了线性映射作用在向量空间的一组基上的效果，就等于掌握了线性映射对向量空间中任意元素的效果，所以它非常重要，我想这也是它叫“基”或“基底”的原因吧。

有了生成集合的铺垫，我们就能得出这样 3 个结论，假设 B 是 V 的一个基，那么我们就可以得到三个结论：

B 是一个最小生成集合；

B 是 V 中的一个最大向量线性无关集合，因为增加任何一个向量到集合中，就会导致它线性相关；

任意属于向量空间 V 的向量，都能够被 B 中的一个向量的线性组合来表示，而且每一个线性组合都是唯一的。

但只了解理论是不够的，我们还需要一些练习。现在请你来思考一下，下面这些集合是不是基？

1. 在实数三维向量空间中，下面这个集合是基吗？

A=⎩⎨⎧​⎣⎡​100​⎦⎤​,⎣⎡​010​⎦⎤​,⎣⎡​001​⎦⎤​⎭⎬⎫​

2. 在实数三维向量空间中，下面这两个集合都是基吗？

A1​=⎩⎨⎧​⎣⎡​100​⎦⎤​,⎣⎡​110​⎦⎤​,⎣⎡​111​⎦⎤​⎭⎬⎫​

A2​=⎩⎨⎧​⎣⎡​0.50.80.4​⎦⎤​,⎣⎡​1.80.30.3​⎦⎤​,⎣⎡​−2.2−1.33.5​⎦⎤​⎭⎬⎫​

3. 在实数四维向量空间中，下面这个集合是基吗？

A=⎩⎪⎪⎨⎪⎪⎧​⎣⎢⎢⎡​1234​⎦⎥⎥⎤​,⎣⎢⎢⎡​2−102​⎦⎥⎥⎤​,⎣⎢⎢⎡​110−4​⎦⎥⎥⎤​⎭⎪⎪⎬⎪⎪⎫​

小提示：你可以先判断它们是否线性相关。每个向量空间 V 都有一个基，也可能有多个基。

这里你需要注意的是，一个向量空间的维度和它的基向量数是一致的，而它的维度不是一定和向量元素的数量相等的，就比如下面这个向量空间。

V=span[[01​]]

这个向量空间是一维的，但它的基向量有两个元素。

了解了向量空间的基，那我们该如何得到向量子空间的基呢？别怕，你只要通过三步操作，就能得到向量子空间 U 的基，U=span[x1​,x2​,…,xm​]。

把向量 x1​,x2​,…,xm​ 组合成矩阵 A 的列；

得到 A 的行阶梯形矩阵；

和主元列相关的向量就是 U 的一个基。

现在，我通过一个例子来说明如何判断向量子空间的基。一个向量子空间 U 属于五维实数向量空间，U 的生成集合是 x1​,x2​,x3​,x4​。

x1​=⎣⎢⎢⎢⎢⎡​12−1−1−1​⎦⎥⎥⎥⎥⎤​,x2​=⎣⎢⎢⎢⎢⎡​2−112−2​⎦⎥⎥⎥⎥⎤​,x3​=⎣⎢⎢⎢⎢⎡​3−435−3​⎦⎥⎥⎥⎥⎤​,x4​=⎣⎢⎢⎢⎢⎡​−18−5−61​⎦⎥⎥⎥⎥⎤​

x1​,x2​,x3​,x4​ 向量中的哪些是 U 的基？要解这个问题，首先就要判断 x1​,x2​,x3​,x4​ 是否是线性无关的，也就是解方程组：λ1​x1​+λ2​x2​+λ3​x3​+λ4​x4​=0，接下来我们就按刚才说的三步，一起来走一遍。

第一步，我们把向量 x1​,x2​,x3​,x4​ 组合成矩阵的列。

⎣⎢⎢⎢⎢⎡​12−1−1−1​2−112−2​3−435−3​−18−5−61​⎦⎥⎥⎥⎥⎤​

第二步，我们把上一步中得到的矩阵转换成行阶梯形矩阵。

⎣⎢⎢⎢⎢⎡​10000​21000​32000​−1−2100​⎦⎥⎥⎥⎥⎤​

第三步，我们从主元列可以判断，x1​,x2​,x4​ 是线性无关的。所以，我们就得出了结论：向量集合 x1​,x2​,x4​ 是 U 的一个基。

秩

聊完了生成集合和基，我们来看看线性代数中的秩。我在前面说过，线性代数中的秩的作用可大了，它可以用来求方程组的通解的个数，可以用来判断向量组中的线性无关向量的个数，可以判定非齐次方程组有无解，还可以判断矩阵的行列式的值是否为零等等。

那我们就得来看看秩的定义了：一个矩阵 A 的线性无关列数，和它的线性无关行数相等，则这个列数或行数就是矩阵的秩，表示成：rk(A)，rk 是英语 rank 的缩写。矩阵的秩有一些重要性质需要你熟练掌握：

列秩等于行秩，也就是 rk(A)=rk(AT)；

m∗n 矩阵 A 的列组成子空间 U⊆Rm 的生成空间，而且 U 的维度等于矩阵 A 的秩，那么，U 的一个基可以通过 A 的高斯消元法得到的主元列来获取；

m∗n 矩阵 A 的行组成子空间 W⊆Rn 的生成空间，而且 W 的维度等于矩阵 A 的秩，那么，W 的一个基可以通过 AT 的高斯消元法得到的主元列来获取；

n∗n 方阵 A 只有在它的秩等于 n 时可逆；

m∗n 矩阵 A 和实数 b 组成的线性方程：Ax=b，只有在 A 的秩和 A 的增广矩阵的秩相等的情况下，Ax=b 才有解；

m∗n 矩阵 A，对于 Ax=0 的解的子空间的维度等于 n−rk(A)，这个子空间叫做零空间，零空间会在下一篇线性映射中介绍；

m∗n 矩阵 A，如果满足 A 的秩等于 m 行数和 n 列数中的最小值，rk(A)=min(m,n)，则矩阵 A 就是满秩矩阵，非满秩矩阵也叫做不满秩矩阵。

说了那么多矩阵秩的性质，现在我们还是通过两个例子，来看一看如何计算矩阵的秩。

1. 已知矩阵 A 如下：

⎣⎡​100​010​110​⎦⎤​

因为它是行阶梯形矩阵，所以我们可以很直观地判断，它有两个线性无关的列或行，所以 A 的秩就是 2，rk(A)=2。

2. 已知矩阵 A 如下：

⎣⎡​1−23​2−35​110​⎦⎤​

针对这样的矩阵，我们可以先使用高斯消元法，得到它的行阶梯形矩阵。

⎣⎡​100​210​130​⎦⎤​

通过主元列判断后，我们发现，线性无关的列或行数是 2，所以，A 的秩是 2，rk(A)=2。

秩在图像恢复和降噪上的运用

在了解了基和秩的定义后，是时候来看看在应用方面的运用了。在线性空间那节课的讲解中，我通过车辆的牌照识别应用场景，提到了一种机器学习的降维算法：主成分分析 PCA。

其实，它在图像降噪的场景中也是大有用处的，特别是它和低秩矩阵结合（低秩是指矩阵的秩比较小，而矩阵的低秩性是指矩阵的秩相对矩阵的行数或列数而言很小）。我们为什么要进行图像降噪呢？那是因为，现实中的数字图像在数字化和传输过程中常受到成像设备与外部环境噪声干扰等影响，含有“杂数据”，也就是没有意义的数据。

当我们把图像转换成一个矩阵后，它的秩如果远远小于矩阵大小的话，图像就是低秩的，那就是说，低秩矩阵的每行或每列都可以用其它的行或列线性表示，这也说明了这个矩阵包含了大量的冗余信息。而这些冗余信息就是我们要处理的对象，我们可以用冗余信息来对错误的图像进行恢复，或者可以去除这些冗余信息对图片进行降噪。

现在我用一个简单的例子来说明一下图像的恢复和降噪到底是怎么做的，在现实中，一张图片很多地方其实是相似的，比如这张来自百度搜索的绿化图片。

这两张图片中大部分都是绿化的背景，相比较而言，左侧的图片就是低秩的，因为它包含的信息量很少。而右侧这张再加上向日葵和飞机的图片就不同了，包含的信息会多很多，那么这时，它的秩要比全是绿化的秩要高很多，也就是说向日葵和飞机会增加图像矩阵的秩。

现实中的图片往往是低秩的，因为取景的对象，不论是和外部还是其自身都有很多相似性，如果图片的秩比较高，就说明它的噪声比较严重，所以，图像处理的低秩在图片的降噪上的运用比较多。

如果你想要使用图像的低秩来恢复图片或者降噪，首先要构建低秩矩阵的先验模型，再求解这个模型，得到低秩矩阵，这种模型就是低秩矩阵恢复模型（LRMR）。LRMR 的具体算法就用到了 PCA，或者增强型 PCA、Robust PCA。具体 RPCA 的算法由于还涉及到了其他方面的知识，比如：迭代阈值算法、加速近端梯度算法、对偶方法，以及拉格朗日乘子法，感兴趣的话可以参考类似文章。

本节小结

好了，到这里基和秩这一讲就结束了，最后我再总结一下前面讲解的内容。

向量空间和线性组合更多的是理论导向，而基和秩就有些不同了，在前者理论的基础上，叠加的是实践，强调的是“有用”和“最小化”。基是刻画向量空间的基本工具，掌握了线性映射作用在向量空间的一组基上的效果，就等于掌握了线性映射对向量空间中任意元素的效果，而秩可以用来求方程组的通解的个数，判断向量组中的线性无关向量的个数，判定非齐次方程组有无解，判断矩阵的行列式的值是否为零等等。所以，你一定要掌握基和秩，为后面的学习打好基础。

线性代数练习场

好，今天练习时刻到了，我们来看一看如何计算这个矩阵的秩：

⎣⎢⎢⎡​−2411​4−8−2−2​−2310​−1−3−1−3​4114​⎦⎥⎥⎤​

提示：你可以通过高斯消元法，获取行阶梯形矩阵后，再进行判断。

欢迎在留言区或部落里晒出你的运算过程和结果。如果你有所收获，也欢迎你把这篇文章分享给你的朋友。

08 | 线性映射：如何从坐标系角度理解两个向量空间之间的函数？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我们要讲的内容是“线性映射”。

前面我们学的内容都是局限在一个线性空间，或者说一个向量空间中，但今天不一样哦，我们要来看看两个向量空间之间的关系，也就是线性映射。

之前我说过，向量也是对象，是能够相加，能够被标量乘的对象，而且这样计算的结果还是向量。而加和标量乘这样的运算同样适用线性映射。比如：两个实数向量空间 V 和 W，有一个函数 ϕ 来完成向量空间 V 到 W 的映射，如果我们想要同时保持向量空间结构不变，那么 ϕ 就要满足：

ϕ(x+y)=ϕ(x)+ϕ(y)ϕ(λx)=λϕ(x)​

其中，所有 x 和 y 属于向量空间 V， λ 属于实数。于是，我们得到了线性映射的定义。

线性映射定义

假设有两个向量空间 V 和 W，ϕ 是一个函数，它完成了向量空间 V 到 W 的线性映射，那么线性映射必须满足等式：

ϕ(λx+φy)=λϕ(x)+φϕ(y)

其中，任意 x 和 y 都属于向量空间 V，而任意 λ 和 φ 都属于实数。

当然，我们能把线性映射表示成矩阵，也就是线性映射矩阵，或者叫做变换矩阵。但因为向量还能组合成矩阵的列，所以我们要特别注意区分矩阵表示的是线性映射还是向量的集合。向量的集合是静态的，而变换矩阵则是动态的哦。

接下来我们再来看看两个任意集合 V 到 W 的三类特殊映射，了解一下函数 ϕ 在不一样的情况下究竟表达了怎样的关系。

函数 ϕ 是单射（Injective）时：如果 ϕ(x)=ϕ(y) ，那么 x=y ，其中任意 x 和 y 都属于集合 V，从图中可以看出它表达的是一对一的关系，也就是我们可以由集合 V 的一个元素唯一确定一个集合 W 的元素。

函数 ϕ 是满射（Surjective）：也就是满足等式 ϕ(V)=W，从图中我们可以看出它表达的是多对一的关系，也就是多个集合 V 的元素能够确定一个集合 W 的元素。

函数 ϕ 是双射（Bijective），就意味着它既是单射又是满射，从图中我们可以看出，它表达的是，所有 V 集合的元素都和 W 集合的元素一一对应，不多不少。

通过这些定义，我们就可以引入几个线性映射的特殊概念了。

同构（ Isomorphism）：即函数 ϕ 使 V 到 W 是线性且双射的；

自同态（Endomorphism）：即函数 ϕ 使 V 到 V 是线性的；

自同构（Automorphism）：即函数 ϕ 使 V 到 V 是线性且双射的；

把 V 到 V，元素 x 到 x 的映射定义为恒等映射。

那么，为什么你需要了解这几个特殊的概念呢？那是因为我们需要通过这些概念引出一个定理：有限维度的向量空间 V 和 W，如果它们的维度相等，那么它们就是同构的。那就是说，同一维度的向量空间某种程度来说是一样的，因为它们能在没有发生损失的条件下互相转换。

比如，Rm×n 矩阵向量空间，和 Rmn 长度是 mn 的向量空间，我们可以认为它们是相同的，因为它们维度都是 mn，而且存在一个线性映射和双射使得它们能互相转换。还记得在矩阵那节课中，我提到的矩阵转换吗？很多时候这类转换就是为了计算方便。

线性映射的矩阵表示

刚才提到了线性映射的矩阵表达，那在了解了线性映射的定义后，现在是时候来具体看一看这个更直观，且有实践意义的表达了。之所以说它有实践意义，那是因为我们赋予了它动态特性，让矩阵表示线性变换的过程。

如果存在一个 n 维向量空间 V 和它的一个有序基 B=(b1​,⋯,bn​)，那么对于任意一个属于 V 的 x，我们能得到一个这样的线性组合：x=α1​b1​+⋯+αn​bn​，我们可以说

α=⎣⎢⎢⎢⎢⎡​α1​⋅⋅⋅αn​​⎦⎥⎥⎥⎥⎤​

是 x 的坐标向量或坐标表达。

我们都了解二维直角坐标系，现在我们通过一个例子，看看一个同样的向量在两个不同坐标系中的表示，加深一下你对线性映射的理解。

假设图中绿色是坐标系 V，黄色是坐标系 W。

在 V 中，e1​ 和 e2​ 是 V 的标准基，向量 x 由线性组合 e1​ 和 e2​ 表示，x 的坐标是 (2,2)，于是，x 在 V 中可以被表示成：x=2e1​+2e2​。

在 W 中，b1​ 和 b2​ 是 W 的标准基，向量 x 由线性组合 b1​ 和 b2​ 表示，而这里的 x 坐标就不同了，变成了 (1.09,0.72)，于是，x 在 W 中可以被表示成：x=1.09b1​+0.72b2​。

说到这，你是不是对线性映射有了一个更直观的感受？现在我们就把矩阵引入到线性映射中，于是，我们就有了变换矩阵，也就是用矩阵表示线性变换的过程。

变换矩阵的定义是：我们有向量空间 V 和 W，它们各自有相应的有序基 B=(b1​,⋯,bn​) 和 C=(c1​,⋯,cn​) ，而 ϕ 就是 V 到 W 的线性映射：ϕ(bj​)=α1j​c1​+⋯+αmj​cm​。

线性映射 ϕ 中的 j 是从 1 到 n，于是，我们就能得到一个 ϕ 的 m×n 的变换矩阵 Aϕ​，这个变换矩阵中的元素是 Aϕ​(i,j)=αij​，也就是说，ϕ(bj​) 的坐标就是 Aϕ​ 的第 j 列。

我们可以来简化一下表达，把它表示成这样： y=Aϕ​(x)。其中，x 是 V 基于 B 基的坐标向量，y 是 W 基于 C 基的坐标向量。所以，变换矩阵可以被用来在有序基上，映射坐标。

我们来看一个变换矩阵例子：已知两个向量空间 V 和 W，它们各自相应的有序基是 B=(b1​,⋯,b3​) 和 C=(c1​,⋯,c4​) ，线性映射 ϕ 表示成以下形式。

ϕ(b1​)=c1​−c2​+3c3​−c4​ϕ(b2​)=2c1​+c2​+7c3​+2c4​ϕ(b3​)=3c2​+c3​+4c4​​

于是，我们可以通过这些条件得到变换矩阵 Aϕ​ 如下。

Aϕ​=⎣⎢⎢⎡​1−13−1​2172​0314​⎦⎥⎥⎤​

理解到这里还不算透彻，我们要更进一步，看看在现实图形图像处理中的线性变换是什么样的。接下来我们通过下面三个图形的例子来理解一下。

第一个图形是原始数据，你可以把它看成是由几百个向量的密集点组成的图。

第二个图形的效果看起来很简单，是由原始数据经过 45 度变换后得到的，它的变换矩阵是下面这样的。

A1​=[cos(4π​)sin(4π​)​−sin(4π​)cos(4π​)​]

第三个图形是原始数据沿平行轴拉伸两倍的效果，变换矩阵是下面这样的。

A2​=[20​01​]

当然，这三个图形是比较简单的例子，是为了方便你理解，其实我们还能做更复杂的变换，比如：旋转、伸缩的组合等等。

基的改变

之前我们讨论的线性映射都是在基是一定的情况下，通过变换矩阵做线性映射。那如果基改变了呢？讨论基的改变在实践中也是有重要意义的。比如，为了最小化数据压缩损失，我们需要找到一个合适的被用来数据投影的基。

现在我们就来看看，如果我们改变向量空间 V 和 W 的基，线性映射 ϕ 的变换矩阵会如何改变。我们给向量空间 V 和 W 各自增加两个有序基：B=(b~1​,…,bn​) 和 C=(c~1​,…,cm​)，而 A~ϕ​ 是基于新的有序基的变换矩阵。这样，A~ϕ​ 变换矩阵的计算公式就是：A~ϕ​=T−1Aϕ​S。

在这个新的公式中，S 是 Rn×n 向量空间 V 的恒等映射变换矩阵，向量空间 V 的恒等映射把基于 B 的坐标，映射到基于 B 的坐标上。同理，T 是 Rm×m 向量空间 W 的恒等映射变换矩阵，向量空间 W 的恒等映射把基于 C 的坐标，映射到基于 C 的坐标上。

理论是这样的，那我们还是要通过一个例子来看一下，基改变后，新的线性映射 ϕ 的变换矩阵到底是如何获取的。我们已知，一个三维实数向量空间 R3 到四维实数向量空间 R4 的一个线性映射，它们各自有标准基 B 和 C。

B=⎩⎨⎧​⎣⎡​100​⎦⎤​,⎣⎡​010​⎦⎤​,⎣⎡​001​⎦⎤​⎭⎬⎫​,C=⎩⎪⎪⎨⎪⎪⎧​⎣⎢⎢⎡​1000​⎦⎥⎥⎤​,⎣⎢⎢⎡​0100​⎦⎥⎥⎤​,⎣⎢⎢⎡​0010​⎦⎥⎥⎤​,⎣⎢⎢⎡​0001​⎦⎥⎥⎤​⎭⎪⎪⎬⎪⎪⎫​

基于它们各自的标准基 B 和 C，它的变换矩阵是：

Aϕ​=⎣⎢⎢⎡​1−13−1​2172​0314​⎦⎥⎥⎤​

那么现在，我们来看一下，基 B 和 C 改变为 B 和 C 之后，会有怎样的变化。

B=⎩⎨⎧​⎣⎡​110​⎦⎤​,⎣⎡​011​⎦⎤​,⎣⎡​101​⎦⎤​⎭⎬⎫​,C=⎩⎪⎪⎨⎪⎪⎧​⎣⎢⎢⎡​1100​⎦⎥⎥⎤​,⎣⎢⎢⎡​1010​⎦⎥⎥⎤​,⎣⎢⎢⎡​0110​⎦⎥⎥⎤​,⎣⎢⎢⎡​1001​⎦⎥⎥⎤​⎭⎪⎪⎬⎪⎪⎫​

对于新基 B 和 C，我们得到 S 和 T：

S=⎣⎡​110​011​101​⎦⎤​,T=⎣⎢⎢⎡​1100​1010​0110​1001​⎦⎥⎥⎤​

于是，我们就可以通过公式得到想要的 A~ϕ​ 了。

A~ϕ​=T−1Aϕ​S=⎣⎢⎢⎡​−4641​−4086​−2043​⎦⎥⎥⎤​

两个重要的子空间

最后，我再来讲两个重要的子空间——核空间和像空间，说它们重要是有原因的。核空间可以帮助我们研究线性方程组的性质，同时还可以帮助我们把复杂问题简化，也就是将复杂的大集合分解成小集合的并来研究。而像空间可以帮助我们快速得到线性方程组的秩，这样就能快速判断线性方程组解的情况。现在我们来具体了解一下。

核空间

核空间也叫做零空间，你还记得 Ax=b 吗？核空间关注的就是 Ax=0，也就是向量空间 V 中所有经过 ϕ 映射为零的向量集合，用符号表示就是：ker(ϕ) 。核的维数叫做零化度（nullity），表示成：dim(ker(ϕ))。

像空间

向量空间 V 中所有经过 ϕ 映射后的向量集合，叫做像空间，用符号表示就是：im(ϕ)，像空间维数就是秩，表示成：rk(ϕ)。

通过图形表达出来，你应该能够更好地理解。

最后我以一个定理来结束本节的内容，秩 - 零化度定理：V 的维数等于核空间维数与像空间维数之和 dim(V)=dim(ker(ϕ))+rk(ϕ)。

本节小结

好了，到这里线性映射这一讲就结束了，最后我再总结一下前面讲解的内容。

线性映射赋予了线性代数灵魂，也让它在计算机科学中发挥了很大的作用。线性映射的矩阵变换是这一节的重点，你要牢固掌握，因为它在无数现实场景中都在使用，比如：三维图形图像处理中的线性变换，图形的伸缩、旋转等等。

线性代数练习场

好，今天练习时刻到了，刚刚我们通过图形图像处理中的线性变换的例子，讲了矩阵变换。现在我们还是以这个例子为基础，不过这一次轮到你来解答问题了。如果我们把原始数据整体沿平行轴收缩两倍，那变换矩阵会是怎样的呢？

欢迎在留言区或者部落里晒出你的变换矩阵。如果你觉得有所收获，也欢迎你把这篇文章分享给你的朋友。

09 | 仿射空间：如何在图形的平移操作中大显身手？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我们要讲的内容是“仿射空间”。

一听到仿射空间，你也许会觉得很奇怪，之前我们说过了线性空间，现在怎么又来一个空间？特别是“仿射”这个词，它有什么含义？它和线性空间的区别和联系又是什么呢？这我们就要从线性空间开始说起了。

我们知道，线性空间中有向量和标量两个对象，而仿射空间与线性空间的区别就在于它又加了一个对象，那就是“点”，而且它们的运算规则也不相同。比如，在仿射空间中，点和标量之间没有定义运算；向量和点之间有加法，运算结果是点；点和点之间有减法，运算结果是向量。

所以，仿射空间可以说是点和向量的空间，而且可以被看成是一个没有原点的线性空间。那你有没有想过，我们为什么要研究仿射空间呢？

那是因为仿射空间在计算机图形处理中有着极其重要的地位。在线性空间中，我们可以用矩阵乘向量的方法表示各种线性变换。但是，有一种常用的变换却不能用线性变换的方式表示，那就是平移，一个图形的平移是非线性的。为了表示平移，以及方便现实世界的描述，就需要使用仿射空间。

仿射子空间

和向量子空间一样，我们现在需要把注意力转移到更有实践意义的仿射子空间上。仿射子空间在计算机科学中的运用主要体现在计算机图形处理中，比如：图形的平移、缩放和旋转等等。

如果我们把 Ax 看成是线性，那么 Ax+b 就是仿射，其实就是多了一个平移。也就是说，我们完全可以把仿射子空间看成是线性子空间的平移。

这样理解很容易，不过，我们还是要看看数学上对仿射子空间的严格定义：V 是一个向量空间，U 是 V 的一个向量子空间，x0​ 是 V 中的元素，那仿射子空间 L 就等于：L=x0​+U:={x0​+u:u∈U}。

这里的 U 叫做方向，x0​ 叫做支撑点。仿射子空间在实数三维 R3 中有点、线和面，它们在坐标系中都是不过原点的。

仿射子空间也经常由参数来描述。因为，我们能通过参数，把表达式组合成方程形式，这样更有助于计算。假设，有一个 k 维的仿射子空间 L，它可以表示成：L=x0​+U。如果 U 有一个有序基 (b1​,⋯,bk​)，那么，每一个属于仿射子空间 L 的元素 x，都能够表示成：x=x0​+λ1​b1​+⋯+λk​bk​。

在这个表达式中，(λ1​,⋯,λk​) 是实数参数，我们把这个表达式叫做“参数方程”，而 (b1​,⋯,bk​) 就是方向向量。

现在我们来看看，仿射子空间在不同维度中的几个例子，让你能从几何角度更了解仿射子空间。

一维仿射子空间，也叫做“线”，参数方程是：y=x0​+λb1​。也就是说，一条线是由一个支撑点 x0​ 和一个方向向量 b1​ 定义的。

二维仿射子空间，也叫做“平面”，参数方程是：y=x0​+λ1​b1​+λ2​b2​。也就是说，一个平面是由一个支撑点 x0​ 和两个线性独立的向量 b1​ 和 b2​ 定义的。

n−1 维仿射子空间，也叫做“超平面”，参数方程如下。

y=x0​+i=1∑n−1​λi​bi​

在这个参数方程中，(b1​,⋯,bn−1​) 是 n−1 维子空间的一个基。也就是说，超平面是由一个支撑点 x0​ 和 n−1 个线性独立的向量 (b1​,⋯,bn−1​) 所定义的。

仿射映射

空间说完，必定会来到动态部分，对应到今天的内容就是仿射映射了。仿射映射和向量空间之间的线性映射是类似的，很多线性映射的特性也能使用在仿射映射上。现在，我们试着来定义两个仿射空间之间的仿射映射。

现在我们有两个向量空间 V 和 W，一个 V 到 W 的线性映射 ϕ，以及一个属于向量空间 W 的向量 a。

φ:​ →Wx→a+ϕ(x)​

上面这样的映射就是 V 到 W 的仿射映射，其中，你可以看到 x 元素是通过线性映射函数 ϕ 和平移向量 a 进行仿射变换的。

事情就是那么简单，看上去就是一个线性映射加上一个向量，或者从几何角度说，就是做了一次线性变换后，进行了一次平移操作。而更专业一点的说法就是，每个 V 到 W 的仿射映射，都是“一个 V 到 W 的线性映射”和“一个 W 到 W 平移”的组合。

从这里你也可以判断出，仿射映射是保持了原几何结构和维度不变的。

接下来，我们再来看看三维世界中物体的仿射映射，这就包括物体的平移、缩放和旋转了。在几何空间中，物体的平移、旋转、放大缩小，这类操作如果从局部坐标系来看，就是在局部坐标系定义的点，或者向量 x 经过变换后，得到点或向量 y。这类变换可以用公式 y=Ax+v 来表示，其中 A 就是 3×3 矩阵，v 就是三维向量。

当然，我们还可以用矩阵来表示这类变换，也就是仿射变换矩阵和向量乘，这和公式 y=Ax+v 达到的效果是一样的。不过，由于仿射变换矩阵是在实践中经常用的方式，所以我们要来具体看看仿射变换矩阵。

仿射变换矩阵

在三维世界中，物体的平移、缩放和旋转，这些操作其实都可以放到一个 4×4 的矩阵中，并且统一用这个矩阵与向量的乘操作来进行物体的变换，或者说向量的空间变换。这个 4×4 仿射变换矩阵是下面这样的。

A′=⎣⎢⎢⎡​a11​a21​a31​0​a12​a22​a32​0​a13​a23​a33​0​a14​a24​a34​1​⎦⎥⎥⎤​

其中，a11​ 到 a33​ 的 3×3 矩阵就是变换公式 y=Ax+v 中的 A 矩阵，表示的是线性变换。而右上角的 a14​,a24​,a34​ 表示的是平移变换，也就是变换公式中的 v 向量。右下角的数字表示的则是整体缩放，现在它是 1，也就意味着不进行整体缩放。

在计算机图形图像处理中，仿射变换尤其重要，那是因为它能保持变换后的共线或共面性。也就是说，线段变换到线段，还是一条直线，变换前的线段中心点就是变换后的线段中心点。同样，三角形变换后，原三角形的重心还是变换后新三角形的重心。

现在，我们拿一个三角形来举例，因为三角形从计算机图形图像上来说是最基础的图形，是组合成其它多边形的基础。如果我们要变换一个三角形，只要对三个定点 a、b、c 进行仿射变换就行了，对于三角形边上的其它点，变换后还是一样会在边上，这样计算量会极大地降低，变换效率就提高了。

接下来，我们把具体的这几个仿射变换矩阵：平移、缩放和旋转，都单独拿出来，看看它们长什么样。

平移矩阵

我们先来看看 4×4 的平移仿射变换矩阵 A′，你有没有注意到矩阵中的 x、y、z？它们就像公式里写的那样固定在矩阵的右边，定义了矩阵在三个轴方向上的平移距离。

A′=⎣⎢⎢⎡​1000​0100​0010​xyz1​⎦⎥⎥⎤​

缩放矩阵

接下来，我们再来看看缩放仿射变换矩阵 A′，这里 x、y、z 的位置产生了变化，固定在了矩阵的对角线上，定义了矩阵在三个轴方向上的缩放大小。

A′=⎣⎢⎢⎡​x000​0y00​10z0​0001​⎦⎥⎥⎤​

旋转矩阵

最后，我们来看看旋转仿射变换矩阵 A′。这里有三种情况：绕 x 轴旋转、绕 y 轴旋转、绕 z 轴旋转。

绕 x 轴旋转的矩阵：

A′=⎣⎢⎢⎡​1000​0cosθsinθ0​0−sinθcosθ0​0001​⎦⎥⎥⎤​

绕 y 轴旋转的矩阵：

A′=⎣⎢⎢⎡​cosθ0−sinθ0​0100​sinθ0cosθ0​0001​⎦⎥⎥⎤​

绕 z 轴旋转的矩阵：

A′=⎣⎢⎢⎡​cosθsinθ00​−sinθcosθ00​0010​0001​⎦⎥⎥⎤​

在 3D 图形实践中，一般仿射变换矩阵上的操作都是可以叠加的。也就是说，我们可以通过连续的矩阵乘，来完成一系列的对象变换。比如，如果我们的对象要先平移，再缩放，最后再绕 x 轴旋转，那么我们就可以通过矩阵的三连乘来表达这个变换。

⎣⎢⎢⎡​1000​0100​0010​xyz1​⎦⎥⎥⎤​⎣⎢⎢⎡​x000​0y00​10z0​0001​⎦⎥⎥⎤​⎣⎢⎢⎡​1000​0cosθsinθ0​0−sinθcosθ0​0001​⎦⎥⎥⎤​

在这里，我也给你推荐一个数学库作为研究使用。因为工作的缘故，我们做的事情和 WebGL 有关，所以我推荐的是一个前端 TS 实现的数学库 TSM，你可以访问GitHub来了解。TSM 很好地封装了仿射映射，刚才的仿射变换矩阵的叠加操作也是封装好的，比如：平移（translate(vector:vec3):mat4）；缩放（scale(vector:vec3):mat4）；旋转（rotate(angle:number,u:vec3):mat4）。

本节小结

这一节课的重点是仿射空间和仿射映射。有关仿射空间，你一定要掌握的是仿射子空间在不同维度中的几个例子，特别是 n−1 维仿射子空间，也叫做“超平面”。因为超平面在机器学习的分类算法中很重要，比如 SVM 支持向量机的二分类算法就会用到它。

而在仿射映射中，仿射变换矩阵是重点。因为在 3D 计算机图形图像处理中，它能够被用来进行物体的平移、缩放和旋转。而且，在计算性能方面，仿射变换矩阵可以极大地降低计算机的运算量，提高变换效率。

线性代数练习场

好，今天的练习时刻到了，不过今天的练习会有一些特别。刚刚我推荐了前端 TS 实现的数学库 TSM，这里我再推荐另一个在 Python 中广泛使用的库 OpenCV。

我希望你能够使用它对一张 JPG 图片做一个简单的仿射变换：平移，平移 (50,20)。JPG 素材如下图所示。

友情提示：你可以使用 CV2 来读图片，NumPy 的 shape 来获取图片的行和列数据，再使用 NumPy 的 float32 产生仿射变换矩阵，最后使用 CV2 的 warpAffine 来完成图片的平移操作。我贴了部分代码在下面，其中的仿射变换矩阵的产生和仿射变换这两行代码是需要你来完成的。

import cv2
import numpy as np

//读取图片
img = cv2.imread('09.jpg',0)
rows,cols = img.shape

//这里是你要完成的代码

//显示原图片和仿射变换后的图片
cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindow

当然，你也可以用其它的库来完成，比如 TSM，这些都没问题，你可以自由发挥。

欢迎在留言区贴出你的代码和最后的输出结果，我会及时回复。同时，也欢迎你把这篇文章分享给你的朋友，一起讨论、学习。

10 | 解析几何：为什么说它是向量从抽象到具象的表达？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我们要讲的内容是“解析几何”。

前面所有章节我们都是围绕向量、矩阵，以及向量空间来展开的。但这一节课有点不一样，我要讲的是解析几何，它使得向量从抽象走向了具象，让向量具有了几何的含义。比如，计算向量的长度、向量之间的距离和角度，这在机器学习的主成分分析 PCA 中是非常有用的。

范数

讲解析几何我们得从“范数”开始讲起。

因为很多人看到几何向量的第一反应就是，它是从原点开始的有向线段，并且向量的长度是这个有向线段的终端和起始端之间的距离。而范数，就是被用来度量某个向量空间或矩阵中的每个向量的长度或大小的。

现在，我们先来看一下范数的数学定义：一个向量空间 V 上的一个范数就是一个函数，它计算 V 中的每一个向量 x 的长度，用符号来表示的话就是：∥x∥∈R，它满足三种性质：

正齐次性： 如果输入参数扩大正 λ 倍，其对应的函数也扩正大倍。设 λ∈R，x∈V，∥λx∥=∣λ∣∥x∥；

次可加性：类似三角不等式，两边之和大于第三边。设 x,y∈V，∥x+y∥≤∥x∥+∥y∥；

正定性：向量 x 的长度一定大于等于零。∥x∥≥0。

看到这里，你也许会问，范数似乎和以前老师教的向量的模一样啊。先别急，它们还真有那么一点关系，你听我慢慢道来。由于范数是度量某个向量空间或矩阵中的每个向量的长度或大小的，所以它和向量空间维度是有关系的，于是，我们可以把范数写成这样的模式来区分不同维度的大小计算：L1​,L2​,…,L∞​。

L1​ 范数：曼哈顿范数，也叫曼哈顿距离，设 x∈Rn，得到下面这个表达式。

∥x∥1​=i=1∑n​∣xi​∣

L2​ 范数：欧式范数，也叫欧式距离，设 x∈Rn，得到下面这个表达式。

∥x∥2​=i=1∑n​xi2​​

L∞​ 范数：切比雪夫范数，也叫切比雪夫距离，设 x∈Rn，得到下面这个表达式。

∥x∥∞​=max(∣x1​∣,∣x2​∣,…,∣xn​∣)

我们发现，向量的模和 L2​ 范数的计算方式都是一样的，都表示的是欧氏距离，所以，我们可以简单地认为向量的模等于 L2​ 范数。而其他的范数模式和向量的模则没有任何关系。

内积

学习解析几何时，我们必须掌握的第二个概念就是内积。

如果说范数是模式，是用来描述向量长度或大小的概念性表达，那么内积可以让我们很直观地了解一个向量的长度、两个向量之间的距离和角度，它的一个主要目的就是判断向量之间是否是正交的，正交这个概念我们会在后面讲解。

点积

我们从特殊到一般，先来看点积，它和第三篇矩阵中说的“普通矩阵乘”形式一样，点积是特殊的内积，为什么说它特殊呢？那是因为在表示两个向量之间的距离时，它就是大家熟悉的欧式距离，点积可以表示成这样的形式：

xTy=i=1∑n​xi​yi​

其他内积

除了点积外，我们再来看另一个不同的内积：设内积空间 V 是 R2，定义内积 ⟨x,y⟩=x1​y1​−(x1​y2​+x2​y1​)+2x2​y2​，一看便知这个和点积完全不同。

内积空间

最后，我们再来看一般内积和内积空间。因为解析几何关注的是向量的长度、两个向量之间的距离和角度，所以，我们要在原来向量空间上加一个额外的结构，这个额外结构就是内积，而加了内积的向量空间，我们就叫做内积空间。

为了表达方便，我们可以把内积写成 ⟨ ⋅,⋅⟩ 这样的形式，那么内积空间 V 可以被表示成这样：(V,⟨ ⋅,⋅⟩)。这时，如果一般内积由点积来表达，那这个向量空间就变成了更具体的欧式向量空间。

接下来看下内积空间有什么性质？我们定义一个内积空间 V 和它的元素 x、y、z，以及一个 c∈R：

满足对称性：x 和 y 的内积等于 y 和 x 的内积，⟨x,y⟩=⟨y,x⟩；

满足线性性：x 和 y+cz 的内积等于，x 和 y 的内积，与 x 和 z 的内积乘以 c 后的和，

⟨x,y+cz⟩=⟨x,y⟩+c⟨x,z⟩；

满足正定性：x 和 y 的内积大于等于零，⟨x,y⟩≥0。

对称正定矩阵

内积还定义了一类矩阵，这类矩阵在机器学习中很重要，因为它可以被用来判定多元函数极值，而在深度学习中，它更是被用来获取最小化损失函数，我们把这类矩阵叫做对称正定矩阵。

对称正定矩阵的定义是：如果一个对称矩阵 A 属于方阵 Rn×n，对任意非零向量 x，都有 xTAx>0，那么 A 就是对称正定矩阵。

我们来看两个例子，判断它们是不是对称正定矩阵。

第一个例子，请你回答下面这个矩阵是对称正定矩阵吗？

A=[96​65​]

答案：是的，它是对称正定矩阵。因为 xTAx>0。

xTAx=[x1​​x2​​][96​65​][x1​x2​​]=(3x1​+2x2​)2+x22​>0

第二个例子，请你看下面这个矩阵是对称正定矩阵吗？

A=[96​63​]

答案：不是的，它只是对称矩阵。因为 xTAx 可能小于 0。

xTAx=[x1​​x2​​][96​63​][x1​x2​​]=(3x1​+2x2​)2−x22​

长度、距离和角度

前面我们通过范数讲了向量的长度，但从内积的角度来看，我们发现，内积和范数之间有着千丝万缕的关系。我们来看看下面这个等式。

∥x∥=⟨x,x⟩​

从这个等式我们发现，内积可以用来产生范数，确实是这样。不过，不是每一个范数都能被一个内积产生的，比如：曼哈顿范数。接下来，我们还是来关注能由内积产生的范数上，从不同的角度来看看几何上的长度、距离和角度的概念。

我们先用内积来计算一个向量的长度，比如：向量 x=[1​1​]T，我们可以使用点积来计算，计算后得出 x 的范数是 2​，具体计算过程是这样的：∥x∥=xTx​=12+12​=2​。

接着，我们再来看一下向量之间的距离，一个内积空间 V，(V,⟨ ⋅,⋅⟩)，x 和 y 是它的两个向量，那么 x 和 y 之间的距离就可以表示成：d(x,y)=∥x−y∥=⟨x−y,x−y⟩​。

如果用点积来计算 x 和 y 之间的距离，那这个距离就叫做欧式距离。

再接着，来看看两个向量之间的角度。我们使用柯西 - 施瓦茨不等式（Cauchy-Schwarz Inequality）来表示内积空间中两个向量 x 和 y 之间的角度：a。

−1≤∥x∥∥y∥⟨x,y⟩​≤1

取值是从 −1 到 1 之间，那么角度就是从 0 到 π 之间，我们用 cos 来表示就是：

cos(a)=∥x∥∥y∥⟨x,y⟩​

其中 a 就是角度，a 的角度取值是 0 到 π 之间。我们很容易就能发现，其实两个向量之间的角度，就是告诉了我们两个向量之间方向的相似性。例如：x 和 y=4x，使用点积来计算它们之间的角度是 0，也就是说它们的方向是一样的，y 只是对 x 扩大了 4 倍而已。

现在，我们通过一个例子，再来更清楚地看下两个向量之间角度的计算，设 x=[1​1​]T，y=[1​2​]T，使用点积来计算，我们得出：

cos(a)=⟨x,x⟩⟨y,y⟩​⟨x,y⟩​=xTxyTy​xTy​=10​3​

那么，这两个向量之间的角度如下。

arccos(10​3​)≈0.32

我们可以用图来更直观地表达一下。

于是，我们最后可以引出一个概念，也就是我们在一开始提到的正交性。如果两个向量 x 和 y 内积等于 0，⟨x,y⟩=0，那么 x 和 y 是正交的，这可以写成：x⊥y。再如果，x 和 y 的范数都等于 1，∥x∥=∥y∥=1，也就是说，如果它们都是单位向量，那么 x 和 y 就是标准正交的。

我们用图来更直观地表达一下。

正交投影

在理论讲解之后，我们要来了解一下解析几何在实践中经常运用的概念——正交投影，它是一种重要的线性变换，在图形图像、编码理论、统计和机器学习中扮演了重要角色。

在机器学习中，数据一般都是高维的。众所周知，高维数据是很难来分析和可视化的。而且，不是所有的高维数据都是有用的，可能只有一些数据包含了大部分的重要信息。

正交投影就是高维到低维的数据投影，在第 5 节课线性空间中，我简单介绍了高维数据投影到低维后，我们就能在低维空间更多地了解数据集、提炼相关模式。而在机器学习中，最普遍的降维算法——PCA 主成分分析，就是利用了降维的观点。

接下来，我开始讲解正交投影，在给出定义之前，先从一张图来了解会更直观。

图中的蓝点是原二维数据，黄点是它们的正交投影。所以，实际降维后，在一维空间中就形成了这条黑线表示，它近似地表达了原来二维数据表示的信息。

现在我们可以来看一下投影的定义：V 是一个向量空间，U 是 V 的一个向量子空间，一个从 V 到 U 的线性映射 Φ 是一个投影，如果它满足：Φ2=Φ∘Φ=Φ。因为线性映射能够被变换矩阵表示，所以，这个定义同样适用于一个特殊类型变换矩阵：投影矩阵 PΦ​，它也满足：PΦ2​=PΦ​。

投影到一维子空间上（线）

接下来，我们来看看如何投影到一维子空间，也就是把内积空间的向量正交投影到子空间，这里我们使用点积作为内积。

假设有一条通过原点的线，这条线是由基向量 b 产生的一维子空间 U，当我们把一个向量 x 投影到 U 时，需要寻找另一个最靠近 x 的向量 ΦU​(x)。还是老样子，我们通过图来看一下。

首先，投影 ΦU​(x) 靠近 x，也就是要找出 x 和 ΦU​(x) 之间的 ∥x−ΦU​(x)∥ 最小距离，从几何角度来说，就是线段 ΦU​(x)−x 和 b 正交，满足等式：⟨ΦU​(x)−x,b⟩=0。其次，投影 ΦU​(x) 必须是 U 的一个元素，也就是，基向量 b 的一个乘来产生 U，ΦU​(x)=λb。

于是，我们可以通过三个步骤来分别得到 λ、投影 ΦU​(x) 和投影矩阵 PΦ​，来把任意 x 映射到子空间 U 上。

第一步，计算 λ，通过正交条件产生这样的等式：

⟨x−ΦU​(x),b⟩=0。因为 ΦU​(x)=λb，所以它可以转变成：⟨x−λb,b⟩=0。

利用内积的双线性：⟨x,b⟩−λ⟨b,b⟩=0，我们得到：

λ=⟨b,b⟩⟨x,b⟩​=∥b∥2⟨b,x⟩​

然后，我们通过点积得到：

λ=∥b∥2bTx​

如果 ∥b∥=1，那 λ 就等于 bTx。

接着第二步，是计算投影点 ΦU​(x)。从 ΦU​(x)=λb，我们得到：

ΦU​(x)=λb=∥b∥2⟨x,b⟩​b=∥b∥2bTx​b

通过点积来计算，我们就得到了 ΦU​(x) 的长度：

∥ΦU​(x)∥=∥b∥2∣∣​bTx∣∣​​∥b∥=∣cos(a)∣∥x∥∥b∥∥b∥2∥b∥​=∣cos(a)∥x∥

这里的 a，是 x 和 b 之间的夹角。

最后第三步，是计算投影矩阵 PΦ​，投影矩阵是一个线性映射。所以，我们可以得到：ΦU​(x)=PΦ​x，通过 ΦU​(x)=λb，我们可以得到：

ΦU​(x)=λb=bλ=b∥b∥2bTx​=∥b∥2bbT​x

这里，我们立即可以得到投影矩阵 PΦ​ 的计算等式：

PΦ​=∥b∥2bbT​

本节小结

这一节课覆盖的知识点有点多，因为要把解析几何的知识点，浓缩到核心的几个点来讲解是一项艰巨的任务。不过不要怕，前面的几个知识点都是为这一节的重点“正交投影”来铺垫的。范数，被用来度量某个向量空间或矩阵中的每个向量的长度或大小，而内积让我们很直观地了解一个向量的长度、两个向量之间的距离和角度，以及判断向量之间是否是正交的。

所以，希望你能掌握范数和内积的理论知识，并把它和正交投影结合，运用在一些实践应用场景中，比如：3D 图形图像的坐标变换、数据压缩，以及机器学习的降维。

线性代数练习场

请用之前学到的正交投影的投影矩阵算法，来计算一条线上的投影矩阵 PΦ​。

这条线通过原点，由基 b=[1​2​2​]T 产生，PΦ​ 计算后，再通过一个 x 来验证一下它是否在 b 产生的子空间中，我们取 x=[1​1​1​]T。

欢迎在留言区晒出你的运算结果，我会及时回复。同时，也欢迎你把这篇文章分享给你的朋友，一起讨论、学习。

11 | 如何运用线性代数方法解决图论问题？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我要讲的内容是“如何运用线性代数方法解决图论问题”。

“图”这个字在计算机科学领域很常见，特别是在数据结构中。一说到图，是必定要联系到图论（Graph Theory）的，因为它是以图为研究对象的数学的一个分支。图论中的图，是由若干给定的点及连接两点的线所构成的图形，这种图形通常用来描述某些事物之间的某种特定关系，用点代表事物，用连接两点的线表示相应两个事物间具有这种关系。

说到这，你也许会问，这个和线性代数、矩阵有什么关系？

图的数学定义

既然是数学课，我们还是要先讲一下图的数学定义：一个图 G 是指一个有序三元组 (V,E,ϕ)，V 是非空的顶点集；E 是不与 V 相交的边集；ϕ 是关联函数，它使 G 的每条边对应于 G 的无序顶点对。如果 e 是一条边，u 和 v 是顶点，使得 ϕ(e)=uv，则 e 连接 u 和 v，也就是顶点 u 和 v 是 e 的端点。

好了，现在是时候通过两个应用场景来看下，如何把矩阵和图论关联起来，并运用在解决实际问题中了。

邻接矩阵应用

首先，是邻接矩阵（Adjacency Matrix），邻接矩阵是表示顶点之间相邻关系的矩阵。假设 G 是一个图，V(G) 是 G 的顶点集，E(G) 是 G 的边集，设 G 中有 n 个顶点，v1​,v2​,…,vn​，A=(aij​)n×n​ 是 G 的邻接矩阵。

ai​={1,vi​vj​∈E(G)0,vi​vj​∈/​E(G)′,i,j=1,2,…,n​

已知情况是这样的，那我们现在来看一下邻接矩阵在现实问题中的应用。这个例子来源于 1994 年全国大学生数学建模竞赛试题 B 题。

某厂生产一种弹子锁具，每个锁具的钥匙有 5 个槽，每个槽的高度从 ｛1,2,3,4,5,6｝ 中任取一数。由于工艺及其他原因，制造锁具时对 5 个槽的高度还有两个限制。

至少有 3 个不同的数；

相邻两槽的高度之差不能为 5。

满足以上条件制造出来的所有互不相同的锁具称为一批。销售部门在一批锁具中随意地取每 60 个装成一箱出售。问：每一批锁具有多少个，装多少箱？

我们先来看下弹子锁具的样子，否则自己想象会要一些时间。

这是一个 7 个槽的弹子锁具，只是比我们例子的 5 个槽多了两个槽。有了弹子锁具形象化输出后，我们开始解题。锁具装箱是一个排列组合的数学问题，但如果我们用图论的邻接矩阵方法来解这个问题，就能够起到化繁为简的作用。

首先，我们构造一个 6 节点的图：把 1、2、3、4、5、6 这 6 个数作为 6 个节点，如果两个数字可以相邻，那这两个节点之间就加一条边，每个节点有自己到自己的一条边。于是，我们得到了锁具各槽之间的关系图：

接着，构建邻接矩阵 A，根据前面说的，如果两点之间有一条边，那在矩阵中，相应位置的值就是 1，比如：节点 1 和 2 之间有一条边，那矩阵第一行第二列和第二行第一列的值就是 1，节点 1 和 6 之间没有边，那矩阵第一行第六列和第六行第一列的值就是 0，因为每个节点有自己到自己的一条边，所以第一行第一列的值就是 1，其它 5 个节点也是一样的。

A=⎣⎢⎢⎢⎢⎢⎢⎡​111110​111111​111111​111111​111111​011111​⎦⎥⎥⎥⎥⎥⎥⎤​

因为我们从没有 1、6 相邻的关系图得到了邻接矩阵 A，所以 A 中所有元素之和表示两个槽高无 1、6 相邻的锁具个数。而每个无 1、6 相邻的 5 位数与关系图中长度是 1 的一条链一一对应。于是，Ak 中各元素之和就是长度为 k 的链接个数。比如，A2 中第 i 行第 j 列的元素就是 i 开始经过两条边到达 j 的链接个数。我们这里因为是 5 个元素，也就是要经过 4 条边，所以需要计算 A4。

A4=⎣⎢⎢⎢⎢⎢⎢⎡​141165165165165140​165194194194194165​165194194194194165​165194194194194165​165194194194194165​140165165165165141​⎦⎥⎥⎥⎥⎥⎥⎤​

把 A4 中的元素求和，就能得到相邻高差为 5 的锁具数是 6306。

最后，因为题目的限制提到了槽的高度至少有 3 个不同的数，我们还要把 6306 这个数字减去仅有一个、两个槽高的锁具：6306−(6+(C62​−1)(25−2))=5880。

所以，我们得到一批锁具的个数是 5880，总共装 5880/60=98 箱。

这样，我们通过邻接矩阵的图论知识，解决了一批锁具的数量问题，比其它方法看起来更简单。

特别提示：文中用到的 Ak 在图论中的实际意义，是来自刘亚国的一篇文献《图论中邻接矩阵的应用》。

关联矩阵应用

接下来，我们在邻接矩阵上再升级一下，把边变成有向边。这样就形成了另一类矩阵：关联矩阵。关联矩阵经常被用在图论中，现在我们就来看一下关联矩阵和图之间的关系。如下图所示，我们定义了一个拥有 4 个节点和 6 条边的图。

接下来，定义一个 6×4 的矩阵来描述这个图，其中列表示点 (1) 到点 (4)，行表示边 1 到边 6：

A=⎣⎢⎢⎢⎢⎢⎢⎡​−1−10−100​10−10−10​01100−1​000111​⎦⎥⎥⎥⎥⎥⎥⎤​

矩阵 A 只包含了三类元素：-1、1、0。-1 表示点的箭头的出方向，1 表示点的箭头的入方向，0 则表示点和点之间没有关联。举例来说，矩阵 A 的第一行元素是 -1、1、0、0，那对于边 1 和点 (1)、点 (2) 说，我们从图中可以看到边 1 是从点 (1) 到点 (2)，A 中 -1 对于点 (1) 来说是箭头的出方向，1 对于点 (2) 来说是箭头的入方向，而边 1 和点 (3)、点 (4) 没有任何关系，所以第一行第三列和第四列都是 0。

这里，我们把关联矩阵用到现实场景中，比如：让它为电子电路服务，用它来分析整个电路的情况，也就是电路的拓扑结构，这里的电路指的是基尔霍夫定律，是分析和计算较为复杂电路的基础。假设 x1​,x2​,x3​,x4​ 是这几个点的电压值，我们来看一下 Ax 的结果：

Ax=⎣⎢⎢⎢⎢⎢⎢⎡​−1−10−100​10−10−10​01100−1​000111​⎦⎥⎥⎥⎥⎥⎥⎤​⎣⎢⎢⎡​x1​x2​x3​x4​​⎦⎥⎥⎤​=⎣⎢⎢⎢⎢⎢⎢⎡​x2​−x1​x3​−x1​x3​−x2​x4​−x1​x4​−x2​x4​−x3​​⎦⎥⎥⎥⎥⎥⎥⎤​

由此可见，结果是差值，也就是沿着边 1 到 6 的电势差，有了电势差，就说明有电流，但如果 Ax=0 会怎样呢？也就是方程满足这样的等式：

Ax=⎣⎢⎢⎢⎢⎢⎢⎡​−1−10−100​10−10−10​01100−1​000111​⎦⎥⎥⎥⎥⎥⎥⎤​⎣⎢⎢⎡​x1​x2​x3​x4​​⎦⎥⎥⎤​=⎣⎢⎢⎢⎢⎢⎢⎡​x2​−x1​x3​−x1​x3​−x2​x4​−x1​x4​−x2​x4​−x3​​⎦⎥⎥⎥⎥⎥⎥⎤​=⎣⎢⎢⎢⎢⎢⎢⎡​000000​⎦⎥⎥⎥⎥⎥⎥⎤​

很明显，这些差值，也就是电势差都等于 0，意味着没有电流。同理，如果把电压值换成温度呢？那应用场景就切换成热传导了。

刚才我们看到了 Ax=0 的情况，你还记得第八篇中说的零空间吗？它关注的就是 Ax=0，也就是向量空间 V 中所有经过 ϕ 映射为零的向量集合，用符号表示就是：ker(ϕ)，它的维数叫做零化度（nullity），表示成：dim(ker(ϕ))。

而在电路例子中，它表示的是所有六个电势差都是 0，也就意味着：所有四个电压值是相等的，在零空间中的每个 x 都是一个常向量：x=(c,c,c,c)。所以，A 的零空间是一条线。无论我们怎么同时升高或降低电压量 c，都不会改变电势差 0。

刚才我们说的是电压，现在我们来具体看看关联矩阵在基尔霍夫电流定律上的运用。我们来定义一个拥有 4 个节点和 5 条边的图：

基尔霍夫电流定律定义：ATy=0，其中 y 是向量 y1​,y2​,y3​,y4​,y5​，我们把这个图以关联矩阵的形式写出来就是：

⎣⎢⎢⎡​−1100​0−110​−1010​−1001​00−11​⎦⎥⎥⎤​⎣⎢⎢⎢⎢⎡​y1​y2​y3​y4​y5​​⎦⎥⎥⎥⎥⎤​=⎣⎢⎢⎡​0000​⎦⎥⎥⎤​

这里 -1，0，1 的含义上面有所描述，第一行和 y 向量相乘后得到：−y1​−y3​−y4​=0，说明从节点 1 出来的总电流等于 0，满足守恒定律；第二行和 y 向量相乘后得到：y1​−y2​=0，说明流入节点 2 的电流和从节点 2 流出的电流相等；同样，后面两行分别和 y 向量相乘后得到：y2​+y3​−y5​=0 和 y4​+y5​=0，和图表示的都一致，也都符合守恒定律。

好了，到这里简单电路的数学知识，也就是关联矩阵讲完了，如果碰到更复杂的电路，比如：在节点之间，也就是边上有电流源，那么，等式就要从 ATy=0 变成 ATy=f。

本节小结

本节是第一篇应用篇，所以我从更贴近生活的例子来讲解线性代数的应用，通过弹子锁具，让你能够了解，邻接矩阵与图论之间是怎么关联的；通过基尔霍夫定律，让你能够了解关联矩阵与图论之间是怎么关联的。

所以，邻接矩阵、关联矩阵的最大意义就是用数学的方式描述图，进而来描述某些事物之间的某种特定关系，是不是发现问题后通过数学工具来解决问题很美妙呢？

线性代数练习场

这次的练习题稍微有些难度，是一道传统练习题。

三名商人各带一个随从乘船渡河，现有一只小船只能容纳两个人，由他们自己划行，若在河的任一岸的随从人数多于商人，他们就可能抢劫财物。但如何乘船渡河由商人决定，试给出一个商人安全渡河的方案，使得渡河的次数最少。

注意：这里的问题包含两层含义——安全渡河和渡河次数最少。

提示：使用本节的第一个例子的邻接矩阵和 Ak 来解这道题。

欢迎在留言区晒出你的运算过程和结果，我会及时回复。同时，也欢迎你把这篇文章分享给你的朋友，一起讨论、学习。

12 | 如何通过矩阵转换让3D图形显示到二维屏幕上？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我要讲的内容是“如何通过矩阵转换让 3D 图形显示到二维屏幕上”。

在第八篇的线性映射中，我从二维直角坐标系的角度，讲解了线性映射和变换矩阵。其中，我特别讲到了，二维平面图形图像处理中的线性变换，比如物体的拉伸和旋转。在第九篇的仿射空间中，更是提到了 3D 的平移矩阵、缩放矩阵和旋转矩阵。

而这一篇则有些不一样，我会从更实践的角度，让你了解到二维平面和三维空间的变换，以及 3D 图形是如何显示到二维屏幕上的。矩阵在这里扮演的角色可以说是功不可没，接下来我们一起来看下矩阵到底是怎么做到的。

三维空间变换

我们都知道，计算机图形图像处理的是图片，且计算机屏幕是二维的。那你有没有想过，我们在屏幕上看到的静态和动态三维世界到底是怎么回事呢？这个就要涉及到三维到二维的投影技术了，这类技术都离不开矩阵，而且是超大规模矩阵运算。

三维空间的变换依赖于 4×4 矩阵，可能你会想，为什么不是 3×3 呢？这是因为四个关键运算中有一个无法用 3×3 矩阵来完成，其他三个运算为了统一也就都采用 4×4 矩阵了，这四个关键运算是：

平移；

缩放；

旋转；

投影。

平移就是那个无法用 3×3 矩阵来完成的特殊运算，也是看起来最简单的运算，只是每个点都加上向量 v0​，也就是点 (x0​,y0​,z0​)。

但是，你别被这个假象欺骗了，平移这个运算是非线性的。这一点只需要看平移前各点与原点的连线，以及平移后各点与原点之间的连线就知道了。或者，你也可以从公式的角度理解，就是 f(a+b) 不等于 f(a)+f(b)。而为了表示平移，以及现实世界的描述，就需要使用第九篇中说的仿射空间。所以，3×3 矩阵是无法平移原点的。

但是，如果我们把原点坐标变成 (0,0,0,1)，那就能解决平移的问题了。点 (x,y,z) 的齐次坐标就是 (x,y,z,1)，这就变成了 4×4 矩阵。接下来，我分别介绍这四个关键运算，它们是 3D 图形显示在屏幕上的第一步，也就是坐标系变换要做的事情，比如：将一个点从局部坐标系变换到世界坐标系是通过平移、缩放及旋转矩阵进行的。

平移

我们沿着向量 v0​ 平移整个三维空间，把原点平移到了 (x0​,y0​,z0​)，这也就意味着三维空间的每个点都加上了点 (x0​,y0​,z0​)。使用齐次坐标，把整个空间平移了 v0​ 的 4×4 矩阵 T 如下所示。

T=⎣⎢⎢⎡​100x0​​010y0​​001z0​​0001​⎦⎥⎥⎤​

这里很重要的一点是，计算机图形图像是基于行向量计算的。也就是说，计算方法是行乘矩阵，而不是矩阵乘列，比如：[0​0​0​1​]T=[x0​​y0​​z0​​1​]。

平移的整个过程是这样的：假设要把原来的某个点 (x,y,z) 平移 v0​，我们需要切换到齐次坐标 (x,y,z,1)，然后，(x,y,z,1) 再乘 T，就能得到每个原来的向量 v 平移到 v+v0​ 的最终结果：[x​y​z​1​]T=[x+x0​​y+y0​​z+z0​​1​]。

这里你需要注意：一个行向量乘 T 的结果还是一个行向量。

缩放

在前端开发中，我们经常会调整图片宽度和高度来适配页面，比如：把图片整体放大 90%，那么在线性代数中就是 0.9 乘单位矩阵。在二维平面中，我们通常用 2×2 矩阵来表达缩放，在三维立体中则是 3×3 矩阵。而在计算机图形图像的齐次坐标中，就不一样了，需要大一个维度，也就是说，3×3 矩阵变成了 4×4 矩阵。

比如，二维平面中图片放大 90% 就是：

S=⎣⎡​0.900​00.90​001​⎦⎤​

三维立体中图片放大 90% 就是：

S=⎣⎢⎢⎡​0.9000​00.900​000.90​0001​⎦⎥⎥⎤​

缩放还可以在不同的方向上进行，比如：一个二维平面图片从整页适配调整到半页适配，y 方向就要乘 21​，创建一个 41​ 的页边留白，x 方向就要乘 43​，这样得到的缩放矩阵就是：

S=⎣⎡​43​00​021​0​001​⎦⎤​

平移和缩放组合情况会怎样呢？如果我们要先平移再缩放，那应该这样乘：vTS，如果我们要先缩放再平移，那应该这样乘：vST。注意：它们乘的顺序是不同的，哪个运算先做就先乘，因为矩阵的左乘和右乘的结果是不同的。

在第九篇的仿射空间中提到了平移和缩放矩阵，你也可以回过头再去看看。

旋转

二维和三维空间的旋转由正交矩阵 Q 来完成，它的行列式是 +1。同样我们使用齐次坐标，一个平面旋转的正交矩阵 Q 就从 2×2 就变成了 3×3 矩阵 R。

Q=[cosθsinθ​−sinθcosθ​]

R=⎣⎡​cosθsinθ0​−sinθcosθ0​001​⎦⎤​

这个矩阵是围绕原点旋转了平面，那如果矩阵旋转时围绕的不是原点，而是其他点呢？这个就稍微复杂一些，不是直接旋转，而是先平移再旋转，比如我们要围绕点 (4,5)，让平面旋转 θ 角度的话：

首先，要把 (4,5) 平移到 (0,0)；

接着，旋转 θ 角度；

最后，再把 (0,0) 平移回 (4,5)。

整个过程通过数学公式来表达就是：

vT00​RT45​=[x​y​1​]⎣⎡​10−4​01−5​001​⎦⎤​⎣⎡​cosθsinθ0​−sinθcosθ0​001​⎦⎤​⎣⎡​104​015​001​⎦⎤​

说完二维我们再来说三维。不过在三维空间中，旋转就有些不一样了，因为它是围绕一个轴“翻转”的。更“数学”的说法就是，围绕 λ=1 的特征向量的一条线翻转。

现在，我们来看看分别围绕 x、y 和 z 轴方向旋转的矩阵 R 有什么不同？

1. 围绕 x 轴方向旋转：

Rx​=⎣⎢⎢⎡​1000​0cosθsinθ0​0−sinθcosθ0​0001​⎦⎥⎥⎤​

2. 围绕 y 轴方向旋转：

Ry​=⎣⎢⎢⎡​cosθ0−sinθ0​0100​sinθ0cosθ0​0001​⎦⎥⎥⎤​

3. 围绕 z 轴方向旋转：

Rz​=⎣⎢⎢⎡​cosθsinθ00​−sinθcosθ00​0010​0001​⎦⎥⎥⎤​

你看出来哪里不同了吗？其实主要就是 1 的位置不同，以及 y 轴方向旋转的 sin 互换了。

投影

现在，我们想把 3D 图形显示到二维屏幕上，该怎么做呢？

从数学角度理解就是把三维向量投影到平面上。在线性代数中，我们看到的大部分的平面都是通过原点的，但在现实生活中则不是。一个通过原点的平面是一个向量空间，而其他的平面则是仿射空间，具体仿射空间的定义你可以回顾一下第九篇的内容。

我们先来看看平面通过原点的情况。假设一个通过原点的平面，它的单位法向量是 n，那么平面中的向量 v，满足这个等式：nTv=0。

而投影到平面的投影矩阵是：I−nnT。

如果把原来的向量和这个投影矩阵相乘，就能投影这个向量。我们可以用这个投影矩阵来验证一下：单位法向量 n 投影后成为了 0 向量，而平面向量 v 投影后还是其自身。

(I−nnT)n=n−n(nTn)=0

(I−nnT)v=v−n(nTv)=v

接下来，我们在齐次坐标中来看一下 4×4 的投影矩阵：

P=⎣⎢⎢⎡​0​I−nnT0​0​0001​⎦⎥⎥⎤​

假设现在有一个不过原点的平面，v0​ 是这个平面上的一个点，现在要把 v0​ 投影到这个平面，则需要经历三个步骤，和刚才介绍的围绕点 (4,5)，让平面旋转 θ 角度经历的三个步骤类似：

把 v0​ 平移到原点；

沿着 n 方向投影；

再平移回 v0​。

整个过程通过数学公式来表达就是：

T−v0​​PT+v0​​=[I−v0​​01​][I−nnT0​01​][Iv0​​01​]

计算机 3D 图形介绍

有了数学知识的铺垫，我们再来看计算机 3D 图形显示到二维屏幕上的过程。在 3D 环境中，三维物体从取景到屏幕显示，需要经历一系列的坐标变换（又称为空间变换），才能生成二维图像显示在输出设备上。

将一个 3D 物体显示出来需要经历三个步骤，其中，第一步，也是最重要的一步就是坐标系变换，将局部坐标系表示的点变换到世界坐标系中，然后再变换到视图坐标系（或叫摄像机坐标系），接着继续变换到裁剪坐标系（投影坐标系）。

将一个点从局部坐标系变换到世界坐标系是通过平移、缩放及旋转矩阵进行的。

如果将世界坐标系中的一个点变换到视图坐标系（摄像机坐标系），则可以使用视图矩阵进行操作。视图矩阵我们这里没有详细说明，它有个相对复杂的推导过程的，感兴趣的同学可以参考我后面推荐的两本书。

如果将视图坐标系（摄像机坐标系）中的一个点变换到裁剪坐标系（投影坐标系），则可以使用投影矩阵进行操作。

最后，我推荐两本非常好的书作为你继续研究计算机 3D 图形的参考。

《TypeScript 图形渲染实战：基于 WebGL 的 3D 架构与实现》，作者：步磊峰，这本书描述了 3D 图形处理的基本数学知识的同时，更注重 WebGL 框架下的图形渲染实战。

《Computer Graphics: Principles and Practice (3rd Edition)》，作者：Hughes, Van Dam, McGuire, Skylar, Foley, Feiner, Akeley，这本书虽然也有实践，但更偏重计算机图形理论一些。

本节小结

今天的整篇内容都是围绕三维空间的变换展开的，你需要掌握三维空间中的四个关键运算：平移、缩放、旋转和投影的基本概念，以及对应的平移、缩放、旋转和投影矩阵，这些都是继续深入学习计算机 3D 图形处理的数学基础。

因为在 3D 环境中，三维物体从取景到屏幕显示，需要经历一系列的坐标变换，才能生成二维图像显示在输出设备上。了解了这些之后，你就能掌握计算机 3D 图形处理的本质，也许还能在将来的实践中优化图形渲染效率。

线性代数练习场

今天我要给你一道开放题：如果把正方形投影到一个平面上，你会得到一个什么形状的图形？

欢迎在留言区晒出你的结果和思考，我会及时回复。同时，也欢迎你把这篇文章分享给你的朋友，一起讨论、学习。

13 | 如何通过有限向量空间加持的希尔密码，提高密码被破译的难度？

你好，我是朱维刚。欢迎你继续跟我学习线性代数。

今天我要讲的内容是“如何通过有限向量空间加持的希尔密码，提高密码被破译的难度”。

这篇的内容会非常有趣，是和密码加密、解密有关的。不知道你有没有看过电影《模仿游戏》，故事描述的是阿兰·图灵在二战期间破译德军的恩尼格玛密码机（Enigma），很精彩，我看了很多遍。

不过电影毕竟是电影，有许多内容是不现实的，好在表达出来的破译恩尼格玛密码的核心观点是正确的。要破译一份被恩尼格玛机加密的密文，需要这三类信息：

恩格玛机的工作原理及内部构造，包括每个转子的线路连接；

德军对恩格玛机的操作守则；

德军所使用的每日初始设置。恩格玛机的每日初始设置包含了三个信息：即转子的排列顺序、每个转子的初始位置，以及插线板的设置。这些信息被印刷在密码本上分发至德军全军，每 24 小时更换一次设置，每月更换一次密码本。

这些在电影里确实都交代了，我也不过多剧透了。其实，恩尼格玛密码机的本质就是替换密码。而今天我要讲的也是一种替换密码——希尔密码。因为我们专栏讲的是线性代数，所以，这篇应用我们会以矩阵论原理为基础，来进行讲解。

为什么需要希尔密码？

要讲密码，我们得先知道人们为什么需要它。

最古老、最原始的加密算法，会把明文的字母按照某种配对关系替换成其他的字母，从而得到一段别人看不懂的密文，许多谍战剧用到过这类方法。看起来，这个方法好像很难人为进行破解，但从语言和统计学角度看，它其实是漏洞百出的。

举个例子，在一篇普通英语文章中，各字母出现的概率有很大的不同。如果我们对足够多的文本进行分析，就可以统计出每一个字母在英文文本中出现的平均概率。

上面这张图来自维基百科，显示的是 26 个字母在普通的英文文本中出现的概率。

只要我们能够获取足够长的密文进行分析的话，通过字母出现的频率，我们同样能够猜到相应的原始字母，这并不安全。所以，随着安全性需求的提高，人们有必要寻找一种容易将字母的自然频度隐蔽或均匀化，并使得统计分析足够安全可靠的加密方法。而希尔密码能基本满足这一要求，那么希尔密码是怎么做到这一点的呢？

希尔密码原理

我们先来看一下希尔密码的原理。根据百度百科的定义，希尔密码（Hill Cipher）是运用基本矩阵论原理的替换密码，由 Lester S. Hill 在 1929 年发明。每个字母当作 26 进制数字：A=0，B=1，C=2… ，把一串字母当成 n 维向量，和一个 n×n 的矩阵相乘，再将得出的结果和 26 进行模运算。

所以，希尔加密算法的基本思想是，通过线性变换将固定数量的明文字母转换为同样数量的密文字母，解密只要作一次逆变换就可以了，而密钥就是变换矩阵本身。

现在，我们再通过数学的方式来表达一下，希尔密码是如何通过三步来实现加密的。

第一步，设置加密矩阵 E。

第二步，对照字母编码表（自行设定）得到数字，并把明文消息分割成大小为 n 的多个块：v1​,v2​,…，并且忽略空格。这里之所以忽略空格，是因为一般情况下密码传递的信息不会过于复杂。如果密码过于复杂，是可以分多次传递的。这里的 n 表示的密钥的阶数，密钥的阶数越高，也就是 n 越大的话，破译的难度也就越大，所需要的计算量也就越大。

第三步，每个消息块和加密矩阵 E 相乘：Ev1​,Ev2​,…，并和 26 进行模运算，最后对照字母编码表得到密文。

同样，我们把这三步倒过来，就能实现解密了。

第一步，计算加密矩阵 E 的逆矩阵 D≡E−1(mod26)。

第二步，对照字母编码表得到数字，把它和解密矩阵 D 相乘，并和 26 进行模运算。

第三步，对照编码表，得到原始明文。

这里你需要注意的是，加密矩阵很关键，它就是我们通常意义上所说的“密钥”，也就是打开密码的钥匙。

通过前面讲解的加密解密步骤，我们可以看出，希尔密码之所以很难被破译，是因为它设置了三道关卡：

列矩阵的维度未知；

对应字母表的排列未知；

加密矩阵（或者说密钥）未知。

想要破解希尔密码，就需要同时获取到通过这三道关卡的钥匙，这谈何容易。

希尔密码实例

好了，原理都讲完了，现在我们通过一个例子来实际地看下希尔密码加密和解密的过程。

假设：A 和 B 双方有一条重要消息要沟通，双方很早就建立了密钥沟通机制，每过一段时间都会更新密钥。在这次的密钥更新周期中，正确的密钥，也就是加密矩阵是一个 3×3 矩阵。

E=⎣⎡​61320​241617​11015​⎦⎤​

这一次 A 要给 B 的消息是“ILIKEBODYCOMBAT”，我们用之前的三步在 A 方先来加密：

第一步，定义加密矩阵，也就是刚才的 E 矩阵。

第二步，对照字母编码表得到数字：8、11、8、10、4、1、14、3、24、2、14、12、1、0、19。接下来，把明文消息分割成大小为 3 的 5 个块，也就是维度为 3 的 5 个列矩阵。

v1​=⎣⎡​8118​⎦⎤​,v2​=⎣⎡​1041​⎦⎤​,v3​=⎣⎡​14324​⎦⎤​,v4​=⎣⎡​21412​⎦⎤​,v5​=⎣⎡​1019​⎦⎤​

第三步，将每个消息块和加密矩阵 E 相乘：

Ev1​=⎣⎡​61320​241617​11015​⎦⎤​⎣⎡​8118​⎦⎤​=⎣⎡​320360467​⎦⎤​mod26=⎣⎡​82225​⎦⎤​

Ev2​=⎣⎡​61320​241617​11015​⎦⎤​⎣⎡​1041​⎦⎤​=⎣⎡​157204283​⎦⎤​mod26=⎣⎡​12223​⎦⎤​

Ev3​=⎣⎡​61320​241617​11015​⎦⎤​⎣⎡​14324​⎦⎤​=⎣⎡​180470691​⎦⎤​mod26=⎣⎡​24215​⎦⎤​

Ev4​=⎣⎡​61320​241617​11015​⎦⎤​⎣⎡​21412​⎦⎤​=⎣⎡​360370458​⎦⎤​mod26=⎣⎡​22616​⎦⎤​

Ev5​=⎣⎡​61320​241617​11015​⎦⎤​⎣⎡​1019​⎦⎤​=⎣⎡​25203305​⎦⎤​mod26=⎣⎡​252119​⎦⎤​

最后，对照字母编码表得到密文：“IWZBWXBCGWGQZVT”。

B 拿到这个密文后，使用三步来解密：

第一步，计算加密矩阵 E 的逆矩阵 D：

D≡⎣⎡​61320​241617​11015​⎦⎤​−1(mod26)≡⎣⎡​82121​5812​10218​⎦⎤​

第二步，对照字母编码表得到数字，把它和解密矩阵 D 相乘，并和 26 进行模运算，得到相应结果。

⎣⎡​82121​5812​10218​⎦⎤​⎣⎡​82225​⎦⎤​=⎣⎡​424869632​⎦⎤​mod26=⎣⎡​8118​⎦⎤​

⎣⎡​82121​5812​10218​⎦⎤​⎣⎡​12223​⎦⎤​=⎣⎡​348680469​⎦⎤​mod26=⎣⎡​1041​⎦⎤​

⎣⎡​82121​5812​10218​⎦⎤​⎣⎡​24215​⎦⎤​=⎣⎡​352835648​⎦⎤​mod26=⎣⎡​14324​⎦⎤​

⎣⎡​82121​5812​10218​⎦⎤​⎣⎡​22616​⎦⎤​=⎣⎡​366846662​⎦⎤​mod26=⎣⎡​21412​⎦⎤​

⎣⎡​82121​5812​10218​⎦⎤​⎣⎡​252119​⎦⎤​=⎣⎡​4951092929​⎦⎤​mod26=⎣⎡​1019​⎦⎤​

最后，B 通过对照编码表，得到了原始明文：“ILIKEBODYCOMBAT”。

这里，你也许会问，密钥为什么用的是 3×3 的可逆矩阵？那是我为了例子方便而设置的，你完全可以设置更高阶的矩阵。就像之前说的，密钥的阶数越高，也就是 n 越大的话，破译的难度也就越大，所需要的计算量也就越大。

所以，从破译密码的角度来看，传统的密码有一个致命弱点，就是破译者可从统计出来的字符频率中找到规律，进而找出破译的突破口。尤其是在计算机技术高度发达的今天，破译的速度更快。而希尔密码算法则完全克服了这一缺陷，它通过采用线性代数中的矩阵乘法运算和逆运算，能够较好地抵抗频率分析，很难被攻破。

本节小结

这一节课的内容都和密码学有关，感觉像是搞谍战一样。但其实它的核心很简单，就是通过基础篇中学到的矩阵和逆矩阵的知识，来实现希尔密码。希尔密码的关键就是定义加密矩阵，或者说密钥、字母表排列方式和列矩阵的维度，通过线性变换将固定数量的明文字母转换为同样数量的密文字母，而解密则只要作一次逆变换就可以了。

当然，现实中还有更复杂的加密算法，其中最著名的，且用到线性代数的加密算法是 AES，想必你平时也经常看到或用到过。AES 是一个迭代的、对称密钥分组的密码，它可以使用 128、192 和 256 位密钥，并且用 128、192 和 256 位分组加密和解密数据，其中密钥长度与分组长度是独立的。

线性代数练习场

请你做一回“特工”，尝试使用希尔密码来给明文“MACHINELEARNING”做加密和解密。

提醒：你可以自行定义加密矩阵、字母表排列方式和列矩阵的维度。加密矩阵可以使用之前介绍的 3×3 可逆矩阵，也可以使用其它 n×n 的可逆矩阵。

欢迎在留言区晒出你的加密和解密过程，我会及时回复。同时，也欢迎你把这篇文章分享给你的朋友，一起讨论、学习。

14 | 如何在深度学习中运用数值代数的迭代法做训练？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我要讲的内容是“数值线性代数的迭代法，以及如何在实践中运用迭代法求解线性方程组”。

大密度线性方程组的计算已经成为了世界上最快计算机的测试标准。2008 年，IBM 为美国能源部 Los Alamos 国家实验室建造了“Roadrunner”计算机系统，它的运算速度达到了 1.026 petaflop/s（千万亿次 / 秒，petaflop 是衡量计算机性能的一个重要单位，1 petaflop 等于每秒钟进行 1 千万亿次的数学运算）。按摩尔定律计算，现在世界上最快的计算机已经达到了 200 petaflop，我国也早就进入了世界前列，并有望实现 1 exaflop/s（百亿亿次 / 秒），成为世界第一。

可能你会有些疑惑，为什么我要在课程后期来讲数值线性代数呢？

那是因为数值线性代数是一门特殊的学科，是特别为计算机上进行线性代数计算服务的，可以说它是研究矩阵运算算法的学科，偏向算法实践与工程设计。有了之前基础知识的铺垫后，学习数值线性代数会更有效，而且它是可以直接运用在计算机科学中的，比如：在图像压缩中，使用奇异值分解（SVD）来节省内存；在深度学习中，使用共轭梯度来加速神经网络的收敛。

迭代方法说明

课程内容的前期一直都在用直接法来解线性方程组，比如高斯消元法。但在实践中，我们在面对复杂场景时，更多的会使用迭代法来求解（也就是所谓的间接法），因为很多场景会用到大型稀疏矩阵。所以，我打算在这里讲讲机器学习中的迭代法应用。这里需要注意，不是说直接法不重要，直接法解决了许多相对简单的问题，也是其他方法的基础。

现在我就来说一说什么是迭代法？

我们还是通过线性方程组 Ax=b 来看看。在这里我们分解 A，使得 A=S−T，代入等式后得出：Sx=Tx+b（等式①）。

按这样的方式持续下去，通过迭代的方式来解 Sx。这就类似于把复杂问题层层分解和简化，最终使得这个迭代等式成立：Sxk+1​=Txk​+b（等式②）。

更具体一点来说，我们其实是从 x0​ 开始，解 Sx1​=Tx0​+b。然后，继续解 Sx2​=Tx1​+b。一直到 xk+1​ 非常接近 xk​ 时，又或者说残余值 rk​=b−Axk​ 接近 0 时，迭代停止。由于线性方程组的复杂程度不同，这个过程经历几百次的迭代都是有可能的。所以，迭代法的目标就是比消元法更快速地逼近真实解。

那么究竟应该如何快速地逼近真实解呢？

这里，A=S−T，A 的分解成了关键，也就是说 A 的分解目标是每步的运算速度和收敛速度都要快。每步的运算速度取决于 S，而收敛速度取决于“错误”(error)，ek​，这里的错误 ek​ 是 x−xk​，也就是说 x 和 xk​ 的差应该快速逼近 0，我们把错误表示成这样：ek+1​=S−1Tek​（等式③）。

它是等式②和①差后得出的结果，迭代的每一步里，错误都会被 S−1T 乘，如果 S−1T 越小，那逼近 0 的速度就更快。在极端分解情况下，S=A，T=0，那 Ax=b 又回来了，第一次迭代就能完成收敛，其中 S−1T 等于 0。

但是，这一次迭代的成本太高，我们回到了非迭代方式的原点。所以，你也知道，鱼和熊掌不能兼得，S 的选择成为了关键。那我们要如何在每一次迭代的速度和快速收敛之间做出平衡呢？我给你 S 选择的几种常见方法：

雅可比方法（Jacobi method）：S 取 A 的对角部分。

高斯 - 赛德尔方法（Gauss-Seidel）：S 取 A 的下三角部分，包含对角。

ILU 方法（Incomplete LU）：S=L 估计乘 U 估计。

雅可比方法实践

总体介绍了迭代法理论之后，我们就进入迭代法运用的实践环节。

首先，我们先来试试使用雅可比方法解线性方程组，雅克比迭代法是众多迭代法中比较早且较简单的一种。所以，作为迭代法的实践开篇比较合适。让我们设一个 2×2 的线性方程组：

Ax=b

{2u−v=4−u+2v=−2​

我们很容易就能得出这个方程组的解如下。

[uv​]=[20​]

现在我们就用雅可比方法来看看怎么解这个方程组：

首先，我们把线性方程组转换成矩阵形式。

[2−1​−12​]=[4−2​]

接着，把 A 的对角线放在等式左边，得出 S 矩阵。

S=[20​02​]

其余部分移到等式右边，得出 T 矩阵。

T=[01​10​]

于是，雅可比迭代就可以表示成下面这样的形式。

Sxk+1​=Txk​+b

{2uk+1​=vk​+42vk+1​=uk​−2​

现在是时候进行迭代了，我们从 u0​=v0​=0 开始。

[u0​v0​​]=[00​]

第一次迭代后，我们得到：

[u1​v1​​]=[2−1​]

第二次迭代后得到：

[u2​v2​​]=[23​0​]

第三次迭代后得到：

[u3​v3​​]=[2−41​​]

第四次迭代后得到：

[u4​v4​​]=[815​0​]

第五次迭代后，我们得到：

[u5​v5​​]=[2−161​​]

经过五次迭代后发现收敛，因为它的结果接近真实解。

真实解[20​]

现在，再来看一下错误等式，$\mathrm{Se}{k+1}=T e{k}，我们把S和T$ 代入等式，得出：

[20​02​]ek+1​=[01​10​]ek​

计算 S 的逆矩阵和 T 相乘 S−1T 得出：

ek+1​=[021​​21​0​]ek​

这里，S 的逆矩阵和 T 相乘 S−1T 有特征值 21​ 和 −21​，所以，它的谱半径是 ρ(B)=21​。这里的谱半径是用来控制收敛的，所以非常重要。谱半径从数学定义上是：矩阵（或者有界线性算子的谱半径）是指其特征值绝对值集合的上确界。这个概念是不是很难理解？具体谱半径的概念你可以查互联网来获取，为了方便你理解，这里我还是用数学方法来简单表达一下。

B=S−1T=[021​​21​0​]

通过 S 的逆矩阵和 T 相乘 S−1T，我们得到：∣λ∣max​=21​，以及：

[021​​21​0​]2=[41​0​041​​]

这里的特征值 21​ 非常小，所以 10 次迭代后，错误就很低了，即 2110​=10241​。而如果特征值是 0.99 或者 0.999，那很显然迭代次数就要多得多，也就是说需要更多时间来做运算。

高斯 - 赛德尔方法实践

现在我们再来看下高斯 - 赛德尔方法，高斯 - 赛德尔迭代可以节约存储和加速迭代，每迭代一次只需一组存储单元，而雅可比迭代需要两组单元。

S 取 A 的下三角部分，还是使用之前雅可比方法中的例子，我们得出方程组：

{uk+1​=21​vk​+2vk+1​=21​uk+1​−1​

这里有一个比较大的变化，那就是 uk​ 消失了，通过 vk​，我们可以直接得到 uk+1​ 和 vk+1​，这样有什么好处呢？两大好处是显而易见的，就是节约存储和加速迭代。

接下来，我们从 u0​=0，v0​=−1 来测试一下迭代。

第一次迭代后，我们得到：

[u1​v1​​]=[23​4−1​​]

第二次迭代后得到：

[u2​v2​​]=[815​16−1​​]

第三次迭代后得到：

[u3​v3​​]=[3263​−641​​]

经过三次迭代后发现收敛，因为第三次迭代后的结果接近真实解。

错误经过计算分别是 −1,4−1​,16−1​,64−1​，和刚才使用雅可比方法得出的错误 2,21​,81​,321​。比较后我们可以发现，无论是迭代次数还是收敛速度方面，高斯 - 赛德尔方法比雅可比方法速度快、精确度也高得多。

逐次超松弛方法

最后，我们在高斯 - 赛德尔方法上做个小调整，在迭代中引入一个参数“omega”，ω，即超松弛因子。然后选择一个合适的 ω，使得 S−1T 的谱半径尽可能小，这个方法就叫做逐次超松弛方法（Successive over-relaxation method，简称 SOR）。

SOR 方法的方程是：ωAx=ωb，矩阵 S 有 A 的对角线，对角线下是 ωA，等式右边 T 是 S−ωA，于是，我们还是使用之前雅可比方法中的例子，得到 SOR 方程组如下。

{2uk+1​=(2−2ω)uk​+ωvk​+4ω−ωuk+1​+2vk+1​=(2−2ω)vk​−2ω​

是不是看起来更复杂了？

没关系，其实它只是在我们眼中看起来复杂，对计算机来说是没区别的。对 SOR 来说，只是多了一个 ω，而 ω 选择越好就越快。具体 ω 的选择，以及迭代的过程就不赘述了，我给你一个小提示，你可以在“ω 大于 1”和“ω 小于 1”两种情况下来多选择几个 ω 进行尝试，最后你应该会得到结论：

在 ω 大于 1 时，ω 越大，迭代的次数就越多，收敛速度就越慢，ω 接近 1 时，迭代的次数越小，收敛速度越快。

在 ω 小于 1 时，ω 越小，迭代的次数就越多，收敛速度就越慢，ω 接近 1 时，迭代的次数越小，收敛速度越快。

所以，SOR 迭代法的关键就是 ω 的选择，它可以被看作是高斯 - 赛德尔法的扩充。

雅可比法、高斯 - 赛德尔法，以及 SOR 迭代法都是定常迭代法。接下来我讲一下和定常迭代法不同的另一类方法，也是实践中用的比较多的方法——共轭梯度法（Conjugate gradient），它属于 Krylov 子空间方法。简单来说，Krylov 子空间方法是一种 “降维打击” 手段，是一种牺牲精度换取速度的方法。

共轭梯度法

要讲共轭梯度法，我们要先解释一下“共轭”，共轭就是按一定的规律相配的一对，通俗点说就是孪生。“轭”是牛拉车用的木头，那什么是共轭关系呢？同时拉一辆车的两头牛，就是共轭关系。

我们根据这个定义再来解释一下共轭方向，向量 p,q∈R， 若满足条件 pAq=0， 则称 p 和 q 关于 A 是共轭方向，或者 p 和 q 关于 A 共轭。有了共轭和共轭方向的概念后，再来看共轭梯度法就简单多了。共轭梯度法的出现不仅是为了解决梯度下降法的收敛速度慢，而且也避免了牛顿法需要存储和计算黑塞矩阵（Hessian Matrix）并求逆的缺点。

现在来看看共轭梯度算法，设 Ax=b，其中 A 是一个实对称正定矩阵。

首先，我们设初始值 x0​ 为 0 或者一个估计值，来计算 r0​:=b−Ax0​。如果 r0​ 非常小，那 x0​ 就是结果，如果不是就继续。

接下来设 p0​:=r0​，k:=0。现在我们开始迭代循环。

a. 计算 αk​ 。

αk​:=pkT​Apk​rkT​rk​​

b. 计算 xk+1​ 。

xk+1​:=xk​+αk​pk​

c. 计算 rk+1​。

rk+1​:=rk​−αk​Apk​

d. 如果 rk+1​ 非常小，循环结束，如果不是就继续。

e. 计算 βk​ 。

βk​:=rkT​rk​rk+1T​rk+1​​

f. 计算 pk+1​ 。

pk+1​:=rk+1​+βk​pk​

g.k:=k+1。

返回结果 xk+1​。

从算法中我们可以看出，共轭梯度法的优点是存储量小和具有步收敛性。如果你熟悉 MATLAB，就会发现共轭梯度法的实现超级简单，只需要短短十几行代码（下方代码来自于 MATLAB/GNU Octave 的例子）。

function x = conjgrad(A, b, x)
    r = b - A * x;
    p = r;
    rsold = r' * r;

    for i = 1:length(b)
        Ap = A * p;
        alpha = rsold / (p' * Ap);
        x = x + alpha * p;
        r = r - alpha * Ap;
        rsnew = r' * r;
        if sqrt(rsnew) < 1e-10
              break;
        end
        p = r + (rsnew / rsold) * p;
        rsold = rsnew;
    end
end

机器学习中的共轭梯度

共轭梯度法经常被用在训练神经网络中，在实践中已经证明，它是比梯度下降更有效的方法，因为就像刚才讲的，它不需要计算黑塞矩阵。那我现在就来讲一讲，使用共轭梯度法的神经网络训练过程。

在整个训练过程中，参数改进是重点，当然这也是所有神经网络训练的重点。这个过程是通过计算共轭梯度的训练方向，然后计算训练速率来实现的。在共轭梯度训练算法中，搜索是按共轭方向进行的，也就是说，训练方向是共轭的。所以，收敛速度比梯度下降要快。

现在我们来看训练方向的计算方法。首先，我们设置训练方向向量为 d，然后，定义一个初始参数向量 w0，以及一个初始训练方向向量 d0=−g0，于是，共轭梯度法构造出的训练方向可以表示成：di+1=gi+1+di⋅γi。

其中，g 是梯度向量，γ 是共轭参数。参数通过这个表达式来更新和优化。通常训练速率 η 可使用单变量函数优化方法求得。

wi+1=wi+di⋅ηi

本节小结

好了，到这里数值线性代数的迭代法这一讲就结束了，最后我再总结一下前面讲解的内容。

首先，我先解释了数值线性代数，接着再整体讲解了迭代方法。然后，举了一个线性方程组的例子，运用迭代法中的几个比较著名的实践方法：雅可比方法、高斯 - 赛德尔方法，以及逐次超松弛方法，来解这个线性方程组。最后，我把共轭梯度法用在了深度学习的神经网络训练中。

希望你能在了解了数值线性代数，以及迭代法后，更多地在计算机科学领域中，运用迭代法做矩阵运算。如果有兴趣，你也可以学习其它在实践中使用的迭代法。

线性代数练习场

练习时刻到了，这次继续使用第一篇线性方程组里的例子，你可以挑选任意一个迭代法来求解这个线性方程组。

假设，一个旅游团由孩子和大人组成，去程时他们一起坐大巴，每个孩子的票价 3 元，大人票价 3.2 元，总共花费 118.4 元。回程时一起做火车，每个孩子的票价 3.5 元，大人票价 3.6 元，总共花费 135.2 元。请问这个旅游团中有多少孩子和大人？

设小孩人数为 x1​，大人人数为 x2​，于是我们得到了一个方程组：

{3x1​+3.2x2​=118.43.5x1​+3.6x2​=135.2​

这个方程组的解是：

{x1​=16x2​=22​

你可以计算一下多少次迭代后它能收敛，也就是逼近真实解？以及它的错误 e 又分别是多少？

欢迎在留言区晒出的你的运算过程和结果。如果有收获，也欢迎你把这篇文章分享给你的朋友。

15 | 如何从计算机的角度来理解线性代数？

你好，我是朱维刚。欢迎你继续跟我学习线性代数，今天我要讲的内容是“如何从计算机的角度来理解线性代数”。

基础和应用篇整体走了一圈后，最终我们还是要回归到一个话题——从计算机的角度来理解线性代数。或者更确切地说，如何让计算机在保证计算精度和内存可控的情况下，快速处理矩阵运算。在数据科学中，大部分内容都和矩阵运算有关，因为几乎所有的数据类型都能被表达成矩阵，比如：结构化数据、时序数据、在 Excel 里表达的数据、SQL 数据库、图像、信号、语言等等。

线性代数一旦和计算机结合起来，需要考虑的事情就多了。你还记得开篇词中我讲到的四个层次的最后一层——“能够踏入大规模矩阵计算的世界”吗？当我们面对大规模矩阵的时候，计算机的硬件指标就需要考虑在内了，这也是硬性的限制条件。在碰到大规模矩阵的时候，这些限制条件会被放大，所以精度、内存、速度和扩展这四点是需要你思考的。

精度：计算机的数字计算是有有限精度的，这个想必你能理解，当遇到迭代计算的情况下，四舍五入会放大很小的误差；

内存：一些特殊结构的矩阵，比如包含很多 0 元素的矩阵，可以考虑优化内存存储方式；

速度：不同的算法、并行执行、以及内存数据移动的耗时，这些都和速度有关；

扩展：当单机内存不够时，你在考虑横向扩展的同时，还要考虑如何分片，也就是如何分布矩阵运算的算力。

接下来，我就从这几点深入讲解一下。

精度

首先是精度，我们先从计算机如何存储数字的角度入手，来做一个练习。你可以执行一下这个 Python 代码，想想会发生什么情况呢？

def f(x):
    if x <= 1/2:
        return 2 * x
    if x > 1/2:
        return 2*x - 1

x = 1/10

for i in range(80):
    print(x)
    x = f(x)

这个结果可能会和你想象的有很大的不同。

其实数学和计算机之间存在很大的不同，数学是连续的、无穷的，而计算机是离散的、有限的。就像这个练习，计算机存储的数字精度是有限的，而很小的误差通过很多次的迭代会累加，最终放大成比较大的错误。一个惨痛的例子就是 1996 年欧洲航天局的 Ariane 5 号火箭的发射失败，最后发现问题出在操作数误差上，也就是 64 位数字适配 16 位空间发生的整数溢出错误。

那我们该怎么来理解数学是连续的，而计算机是离散的呢？举个简单的例子，我们来看下数学中的区间表达[1,2]，这个形式就是连续的；但如果在计算机中以双精度来表达同样的东西，则会是这样的离散形式：

1,1+2−52,1+2×2−52,1+3×2−52,…,2

于是，我们就引出了一个计算机领域精度计算的概念——机械最小值（EPSILON），对于双精度来说，IEEE 标准指定机械最小值是：ε=2−53。

内存

刚才我们看的是数字精度，现在我们接着来看看矩阵的存储方式。我们都知道，内存是有限的，所以，当你面对大矩阵时，千万不要想着把矩阵所有的元素都存储起来。解决这个问题的一个最好方式就是只存储非零元素，这种方式就叫做稀疏存储。稀疏存储和稀疏矩阵是完美匹配的，因为稀疏矩阵大部分的元素都是零。

我们来看一个机器学习中比较简单的稀疏矩阵的例子：Word Embedding 中的 One-Hot 编码。One-Hot 编码就是给句子中的每个字分别用一个 0 或 1 编码，一个句子中有多少个字，就有多少维度。这样构造出来的矩阵是很大的，而且是稀疏矩阵。比如：“重学线性代数”这六个字，通过 One-Hot 编码，就能表达成下面这样的形式。

⎣⎢⎢⎢⎢⎢⎢⎡​100000​010000​001000​000100​000010​000001​⎦⎥⎥⎥⎥⎥⎥⎤​

所以，一般稀疏矩阵的大致形态如下图所示。

⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡​10000000​01000000​002021​000​00010010​0021​04020​00000100​00020010​00000001​⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤​

也有特殊类型的结构化矩阵，比如：对角线矩阵、三对角矩阵、海森堡矩阵等等，它们都有自己的特殊稀疏表达，也都通常被用来减少内存存储和计算量。可以说，数值线性代数在运算方面更多地聚焦在稀疏性上。

速度

接下来我们再来看速度。速度涉及到很多方面，比如计算复杂度、单指令多数据矢量运算、存储类型和网络等。

计算复杂度

算法通常由计算复杂度表达，同一问题可用不同算法解决，而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适的算法或者优化算法。

那么我们该如何评价一个算法呢？主要就是从时间复杂度和空间复杂度来综合考虑的。从矩阵计算的角度看，计算复杂度的考虑是很有必要的。简单的计算，我们可以用直接法来计算，而有的比较复杂的计算就要用间接迭代法了。特别是在很多场景中会用到的大型稀疏矩阵，这些计算复杂度都是不同的。我在第 4 篇“解线性方程组”中提到了迭代法，你可以回顾一下，同时你也可以参考上一节课的迭代法应用细节。

单指令多数据矢量运算

现代 CPU 和 GPU 都支持在同一时间以同步方式执行同一条指令。

举个例子来说，一个向量中的 4 个浮点数的指数幂运算可以同时执行，从而极大地提高运算效率，这类单指令多数据矢量运算处理就叫做 SIMD（Single Instruction Multiple Data）。这在矩阵运算中非常重要，虽然我们不用太关心底层的实现，但如果你可以了解一些矩阵运算的包和库，那么你就可以在实际的开发中直接使用它们了，其中比较出名的就是 python 的 NumPy，以及 BLAS（Basic Linear Algebra Subprograms）和 LAPACK。

LAPACK 是用 Fortran 写的，这里也纪念一下 Fortran 创始人约翰·巴库斯 (John W. Backus)，不久前在美国俄勒冈州的家中去世，享年 82 岁。

存储类型和网络

计算机存储类型有很多，比如：缓存、内存、机械盘、SSD，这些不同存储媒介的存储延迟都是不一样的；在网络方面，不同 IDC、地区、地域的传输速率也是不同的。

上图中的这些数据是所有程序员必须要知道的，因为毕竟各类计算机资源是有限的，在做解决方案时，必须综合考虑存储和网络的性能和成本来做组合，最终达到最大的产出投入比。这些数据来自GitHub，可以调整年限值来观察每年的动态变换。

扩展

最后我再来讲一下扩展，扩展分为单机多核、多处理器的垂直方向扩展，以及多节点平行方向扩展。

当我们想要利用多处理器能力来处理大型矩阵运算的时候，传统的方法就是把大矩阵分解成小矩阵块。比如：一台拥有 4 个处理器的服务器，现在有这样的两个 6×6 矩阵 A 和 B 要做乘运算。

我们可以把这两个矩阵分别分成四块，每块都是 3×3 矩阵，例如：A11、A12、A21 和 A22、B11、B12、B21 和 B22，A 和 B 的乘运算就是把各自分好的块分配到各处理器上做并行处理，处理后的结果再做合并。

比如：处理器 P1 处理 A11 和 B11，处理器 P2 处理 A12 和 B12，处理器 P3 处理 A21 和 B21，处理器 P4 处理 A22 和 B22。于是，4 个处理器分别处理 A 和 B 的乘计算来获取结果：C11、C12、C21 和 C22。拿 C11 来看，C11=A11×B11+A12×B21，虽然 B21 不在 P2 中，但可以从 P3 传递过来再计算。

当计算机垂直扩展到极限后，就需要考虑扩展到多节点计算了，其实原理也是一样的，不一样的是需要从应用层面来设计调度器，来调度不同的计算机节点来做计算。

本节小结

线性代数运用在计算机科学中就是数值线性代数，它是一门特殊的学科，是特别为计算机上进行线性代数计算服务的，可以说它是研究矩阵运算算法的学科。这里，你需要掌握很重要的一个点就是：数学和计算机之间存在很大的不同，数学是连续的、无穷的，而计算机是离散的、有限的。

所以，从计算机角度来执行矩阵运算，需要考虑很多方面：精度、内存、速度和扩展，这样，你在做解决方案时，又或者在写程序时，才能在计算机资源有限的情况下做到方案或程序的最优化，也可以避免类似 1996 年欧洲航天局的 Ariane 5 号火箭发射失败的这类错误。

线性代数练习场

我想让最后一篇的练习成为一个知识点的补充。

针对矩阵高性能并行计算，我前面在“扩展”一模块讲的是一般传统方法，也就是把大矩阵分解成小矩阵块，利用多处理器能力来处理大型矩阵运算。现在，请你研究一下如何用 Cannon 算法解决这个问题？

Cannon 算法是一种存储效率很高的算法，也是对传统算法的改进，目标就是减少分块矩阵乘法的存储量。而且你也可以把它看成是 MPI 编程的一个例子。

欢迎在留言区分享你的研究成果，大家一起探讨。同时，也欢迎你把这篇文章分享给你的朋友，一起讨论、学习。

基础通关 | 线性代数5道典型例题及解析

你好，我是朱维刚。欢迎你继续跟我学习线性代数。

今天这一节课的内容是基础通关。这里会用 5 道典型例题，让你巩固一下线性代数的基础知识，这也是进入应用篇学习之前的一次动手机会。从课程上线到现在快有一个月了，这期间我收到了不少同学的提问和建议，有些问题也是我没有想到的，非常有深度，说实话这让我感觉挺意外的，希望你再接再厉。

现在，你可以看一下基础通关的 5 道例题了，题目和解析都放在了正文中，你可以自己试着做一下。基础通关后，我们应用篇再见。

例题一

找到线性方程组 Ax=b 的所有解，其中：

A=⎣⎡​13−1​202​⎦⎤​,b=⎣⎡​101​⎦⎤​

解析：

这里考察了解线性方程组的方法，特别是高斯消元法，你可以参考第 4 节的内容。

首先，形成增广矩阵：

⎣⎡​13−1​202​111​101​⎦⎤​

接着，分步计算增广矩阵的行阶梯形矩阵：

第一行乘 -3 和第二行相加。

第一行和第三行相加。

⎣⎡​100​2−64​111​1−32​⎦⎤​

第二行乘 31​ 和第一行相加。

第二行乘 32​ 和第三行相加。

第三行乘 −61​。

⎣⎡​100​010​111​021​0​⎦⎤​

最后得出该线性方程组的唯一解：

x=[021​​]

例题二

找到线性方程组 Ax=b 的所有解，其中：

A=[10​22​32​],b=[11​]

解析：

这里考察了解线性方程组的方法，特别是高斯消元法。你可以参考第 4 节的内容，和例题一不同的是，例题二这里得到的会是无穷解。所以，这一题里找特殊解和通用解的方法是关键。

首先，形成增广矩阵：

[10​22​32​11​11​]

接着，形成增广矩阵：分步计算增广矩阵的行阶梯形矩阵：

第一行乘 -1 和第二行相加；

第二行乘 1/2。

[10​01​11​11​021​​]

使用主元列，得到特殊解：

x=⎣⎡​021​0​⎦⎤​

下一步，获取线性方程组 Ax=0 的通用解，从增广矩阵的左边，能够立即得出：

λ⎣⎡​11−1​⎦⎤​

最后，把特殊解和通用解组合起来就是：

x=⎣⎡​021​0​⎦⎤​+λ⎣⎡​11−1​⎦⎤​

例题三

计算矩阵乘 AB。

A=[10​2−1​32​],B=[40​−12​21​]

解析：

这里考察了基本的矩阵乘运算，特别是普通矩阵乘，只有相邻阶数匹配的矩阵才能相乘，你可以参考第 3 节的内容。

矩阵乘无法完成，因为 A 是 2 行 3 列矩阵，B 也是 2 行 3 列矩阵，A 和邻居维度不同。

例题四

计算矩阵乘 AB。

A=[10​2−1​32​],B=⎣⎡​422​−101​⎦⎤​

解析：

这里考察了基本的矩阵乘运算，特别是普通矩阵乘，你可以参考第 3 节的内容。

矩阵乘可以完成，因为两个矩阵的邻居维度相同，拿 a11​ 举例：a11​=1×4+2×2+3×2=14，结果：

AB=[142​22​]

例题五

假设 R3 和它的运算 ⟨ ⋅,⋅⟩，x,y∈R3，我们有：

⟨x,y⟩=xTAy,A=⎣⎡​401​24−1​1−15​⎦⎤​

那么，⟨ ⋅,⋅⟩ 是内积吗？

解析：

这里考察了内积，以及内积的性质之一：对称性，你可以参考第 10 节的内容。

选择 x=[1​1​0​]T，y=[1​2​0​]T，通过计算，能够得到：

⟨x,y⟩=16⟨y,x⟩=14⟨x,y⟩​=⟨y,x⟩​

于是，⟨ ⋅,⋅⟩ 是不对称的。

强化通关 | 线性代数水平测试20题

你好，我是朱维刚。

《重学线性代数》这门课程就正式完结了，很感谢你一直以来的认真学习和支持！

为了帮助你检验自己的学习效果，我特别给你准备了一套结课测试题。这套测试题一共有 20 道题目，都是单选题，其中大部分都来自历届的自学考和考研的线性代数题，来重新回顾一下当年吧。试卷满分 100 分，我建议你在 45 分钟以内完成。

还等什么，快点击下面的按钮开始测试吧！

结束语 | 和数学打交道这么多年，我的三点感悟

你好，我是朱维刚。

一眨眼就到了课程结束的时候了，其实我特别不喜欢“结束”这个词，因为学习是没有终点的。我想了很久结束语应该写些什么内容，最后，我还是想从自身的经历，以及技术人的角度，分享一些故事给你，希望能对你有些启发，能够更多地从底层逻辑去思考问题、从数学角度去思考问题。

很多问题归根结底都是数学问题

在课程开篇词里我分享过，自己出生于上世纪 80 年代初，从小父母就一直给我灌输“学好数理化，走遍天下都不怕”的思想。但一开始，我只知道要学好数学，对数学能用在什么地方根本没什么概念。直到进入大学和研究生阶段，学了工程数学之后才真正恍然大悟——原来数学可以用在各类工程上。

后来正式参加工作，我和数学之间的关系也愈加紧密。2008 年，我第一次出国，去比利时首都布鲁塞尔工作。得空的时候，我就坐上小火车去一些名校蹭课、听讲座。像比利时的鲁汶大学、荷兰的乌得勒支大学等等，都是我经常光顾的地方。

每次我都会去听一些教授的理工类课程，这些知识的摄入也让我慢慢形成了自己的底层逻辑思维，特别是会更多地从数学角度来考虑解决问题的方法。

虽然和数学打了这么多年交道，但有意思的是，我觉得自己其实不算是对数学本身特别感兴趣。真正让我对数学保持探究精神的原因是，我对什么东西都很好奇，我总是想看看它背后到底有着怎样的原理。只是我看到最后发现，很多问题其实都变成了数学问题。比如，在导读中说的 KNN 监督学习算法的底层，其实就是计算向量之间的距离。

好奇心让我们不停地刨根问底，也是促使我们不断学习的原动力。所以在我看来，不管是学习数学还是学习其他学科或技术，保持一颗好奇心非常重要。用乔布斯当年在斯坦福大学毕业典礼上给年轻人的那句赠言：“Stay hungry. Stay foolish.”

任何复杂问题都能解决，就看你分解到多细

我在开篇词里说过，我是从 2006 年年初开始从事机器学习相关工作的。当时我是加入了 IronPort（后来被思科收购了）公司的 TDA（Threat Data Analyst）团队。不过那会儿我才刚毕业没多久，并不知道自己做的是机器学习的工作，也根本不知道什么是机器学习，而且机器学习在当时各应用领域并不火。我只是想着工作和数据分析有关，就“上船”了。

当时，公司的主要业务集中在“反垃圾邮件”这个领域，我的主要职责就是每天分析全球大量邮件数据、人工打分类标签，并且写匹配规则来优化反垃圾邮件引擎，希望能够阻挡更多的垃圾邮件。这个工作其实有点类似如今的数据分析师和数据科学家，基本上每天都在跟“数学”打交道。

不了解的人可能会觉得这些工作没什么大不了的，不就是每天看看邮件，根据设定好的规则给邮件打上 Spam 或 Ham 这类标签，以及做一些正则匹配吗？但事实并非如此。

摆在我们团队面前的，有许多技术层面的挑战。比如，公司的产品面向的是全球用户，邮件内容包含了全世界各类语言，再厉害的机器学习分类算法，也很难做到语言的全覆盖。即便是放到今天，NLP 也有很长的路要走，更何况是 14 年前。那该怎么办呢？

当时，我们团队主要是从两方面入手来解决这个难题。一方面，把问题层层分解，引入了反垃圾邮件的多层防御体系，多层过滤垃圾邮件，将一个个复杂问题分解成可以解决的小问题；另一方面，针对分类算法无法识别的内容，采用了部分用人工来分析数据，靠经验打标签的方式来解决，同时还搭建了机制把人工积累的内容转换成经验模型，最后反馈到机器学习算法中。

这段工作经历让我悟出了一个道理：任何复杂问题都能解决，就看你能分解到多细。就像马斯克做特斯拉、做火箭，这些看起来非常复杂的产品，其实都是把一个个复杂问题，层层分解到细枝末节的。

当然，遇到问题也要学会多角度思考。一个问题会有很多解决方案，不一定复杂的方案就是好的，有时候可能一个简单的数学公式，就能完美地解决问题。

程序员和年龄没有关系，重要的是要足够专注

后来，我又去国外工作了很多年，曾带领国际团队主导比利时电信云 BeCloud，以及新加坡政府云 G-Cloud 的建设工作。

在国外工作的那段时间，我的思维方式变换了许多。我开始认识到程序员这个工作和年龄没有关系，重要的是要足够专注。“35 岁程序员被清退”在国内是热议，很多媒体也经常会拿程序员的中年危机做文章。但在国外，大龄程序员是一抓一大把。

就拿我当时所在的团队来说，整个团队除了我，全都是叔叔辈的，而且还都是大牛级别的。当时团队里最年轻的程序员是来自荷兰的弗雷德里克（Frederik），45 岁；年纪最大的是来自德国的弗兰克（Frank），60 岁。

和这些“高龄”大牛程序员们接触一段时间后我发现，人家不仅工作效率相当高，而且从底层解决问题和记忆的能力，都着实让人惊叹。除此以外，他们还让我认识到了，做技术是需要有产品思维的。更确切地说，技术人要把产品思维融入自己的血液里，变成自己的一部分。这样，技术人在理解需求时会更快、更准，看待问题多了一个角度，也能看得更广、更全。

总之，数学这门学科和程序员的关系可谓是非常密切，甚至经常有人说“数学决定了一个程序员技术水平的上限，会决定一个程序员未来的发展潜力”。不过，在我看来，数学更多的是扮演一个“工具”的角色，帮助程序员在实际工作中更好地发现问题、解决问题，它是程序员必备的素质。

所以，我希望“学习数学”在你的人生中没有“结束”的那一天，请让线性代数融入到你的工作和生活中吧！