我继续发表我的演讲,最初是为那些学习“数字地质”专业的学生而准备的。 这是该中心出版的第三本出版物,第一篇是介绍性文章,无需阅读。 但是,要理解本文,有必要阅读线性方程组的
介绍 ,即使您知道它是什么,因为我将大量参考该介绍中的示例。
因此,今天的任务是:学习最简单的几何处理,例如,能够将我的头变成复活节岛的偶像:

当前的演讲计划:
官方课程资料库位于
此处 。 这本书还没有读完,我正在慢慢地编辑哈布雷出版的文章。
在本文中,我的主要工具将是寻找最小二次函数; 但是在我们开始使用此工具之前,您至少需要了解他在哪里拥有“打开/关闭”按钮。 首先,刷新内存,然后回忆一下什么是矩阵,什么是正数以及什么是导数。
矩阵和数字
在本文中,我将大量使用矩阵表示法,因此让我们回顾一下它是什么。 不要在文本中进一步看,要暂停几秒钟,然后尝试公式化什么是矩阵。
不同的矩阵解释
答案很简单。 矩阵只是一个存放辣根的柜子。 每个狗屎都位于其单元格中,单元格按行和列按行分组。 在我们的特殊情况下,通常的实数是废话; 对于程序员而言,最容易呈现矩阵 
喜欢的东西
float A[m][n]; 
为什么需要这种存储? 他们描述什么? 也许我会让你不高兴,但是矩阵本身并没有描述任何东西,它存储着。 例如,您可以在其中存储任何函数的系数。 让我们忘记矩阵一秒钟,然后想象我们有一个数字 
一 。 什么意思 是的,魔鬼知道...例如,它可能是函数内部的一个系数,该系数以一个数字作为输入,并以另一个数字作为输出:
f ( x ): m a t h b b R r i g h t a r r o w m a t h b b R   
数学家可以写这样一个函数的一个版本:
f ( x ) = 斧
好吧,在程序员的世界里,它看起来像这样:
 float f(float x) { return a*x; } 
另一方面,为什么要这样的功能,而又不完全是另一个? 让我们再来一个!
f ( x ) = 斧 2
自从我开始学习程序员以来,我必须写她的代码:)
 float f(float x) { return x*a*x; } 
这些函数之一是线性的,第二个函数是二次的。 哪一个是正确的? 不,数字本身 
一 没有定义它,只是存储了一些价值! 您需要什么功能,像这样构建一个。
矩阵也会发生同样的事情,如果没有足够的单个数字(标量),这是数字的一种扩展,这些就是需要的存储。 就像在数字上一样,在矩阵上方也定义了加法和乘法运算。
假设我们有一个矩阵 
例如,尺寸为2x2:
甲= \开始b 中号一吨- [R 我X 一11和一个12a21&a22\结束bmatrix
这个矩阵本身并不意味着任何东西,例如,它可以解释为一个函数
f(x): mathbbR2 rightarrow mathbbR2, quadf(x)=Ax
 vector<float> f(vector<float> x) { return vector<float>{a11*x[0] + a12*x[1], a21*x[0] + a22*x[1]}; } 
此函数将二维向量转换为二维向量。 在图形上,将其表示为图像转换很方便:我们将图像提供给输入,在输出处得到其拉伸和/或旋转(甚至可以镜像!)版本。 很快我将给出带有矩阵解释的各种示例的图片。
并且可以矩阵 
想象一下将二维向量转换为标量的函数:
f(x): mathbbR2 rightarrow mathbbR, quadf(x)=x topAx= sum limitsi sum limitsjaijxixj
请注意,矢量的乘幂不是很明确,所以我不能写 
x2 ,就像他在写普通数字时一样。 我强烈建议那些不习惯处理矩阵乘法的人,再次回忆一下矩阵乘法规则,并验证表达式 
x topAx 通常允许,并且确实在出口处给出了标量。 为此,例如,您可以明确地放在方括号中 
x topAx=(x topA)x 。 我提醒你,我们有 
x 是维度2的向量(存储在维度2x1的矩阵中),我们明确编写所有维度:
 underbrace underbrace left( underbracex top1 times2 times underbraceA2 times2 right)1 times2 times underbracex2 times11 times1
回到温暖蓬松的程序员世界,我们可以编写相同的二次函数,如下所示:
 float f(vector<float> x) { return x[0]*a11*x[0] + x[0]*a12*x[1] + x[1]*a21*x[0] + x[1]*a22*x[1]; } 
什么是正数?
现在我将问一个非常愚蠢的问题:什么是正数? 我们有了一个称为谓词的强大工具 
> 。 但是花点时间回答这个数字 
一 当且仅当 
a>0 那太容易了。 让我们定义正性如下:
定义1
编号 
一 当且仅当对于所有非零实数为正 
x in mathbbR, x neq0 条件满足 
ax2>0 。
它看起来很棘手,但它完全适用于矩阵:
定义2
方阵 
如果为非零,则称为正定 
x 条件满足 
x topAx>0 也就是说,除了原点外,其他所有地方的对应二次形式都严格为正。
为什么我需要积极性? 正如我在文章开头提到的那样,我的主要工具将是搜索二次函数的最小值。 但是至少要进行搜索,如果它存在的话也不错! 例如一个功能 
f(x)=−x2 因为数字-1不是正数,所以最小值显然不存在,并且 
f(x) 随着增长而不断减少 
x :抛物线的分支 
f(x) 往下看 正定矩阵是好的,因为相应的二次形式形成了具有(唯一)最小值的抛物面。 下图显示了七个不同的矩阵示例。 
2\乘2 ,无论是正定的还是对称的,事实并非如此。 上排:矩阵作为函数的解释 
f(x): mathbbR2 rightarrow mathbbR2 。 中排:将矩阵解释为函数 
f(x): mathbbR2 rightarrow mathbbR 。

因此,我将使用正定矩阵,它们是正数的推广。 而且,在我的情况下,矩阵也将对称! 很好奇的是,当人们谈论积极确定性时,它们也常常意味着对称,而对称性可以通过以下观察(可以理解以下文字)来间接解释。
二次形式矩阵对称性的抒情离题
让我们看一下二次形式 
x\前Mx 对于任意矩阵 
M ; 然后 
M 添加并立即减去其转置版本的一半:
M=\底线 frac12(M+M top):=Ms+\底线 frac12(MM top):=Ma=Ms+Ma
矩阵 
Ms 对称的: 
M stop=Ms ; 矩阵 
Ma 反对称: 
M atop=−Ma 。 一个显着的事实是,对于任何反对称矩阵,对应的二次形式都等于零。 这来自以下观察:
q=x topMax=(x topM atopx) top=−(x topMax) top=−q
也就是说,二次形式 
x topMax 同时等于 
q 和 
−q ,仅当 
q\等于0 。 从这个事实可以得出,对于任意矩阵 
M 对应的二次形式 
x\前Mx 可以通过对称矩阵表示 
Ms :
x topMx=x top(Ms+Ma)x=x topMsx+x topMax=x topMsx。
我们正在寻找二次函数的最小值
让我们简短地回到一维世界。 我想找到最少的功能 
f(x)=斧2−2bx 。 编号 
一 肯定地,因此存在一个最小值; 为了找到它,我们将相应的导数等于零: 
 fracddxf(x)=0 。 区分劳动的一维二次函数不是: 
 fracddxf(x)=2ax−2b=0 ; 所以我们的问题归结为求解方程式 
ax−b=0 ,通过艰苦的努力,我们找到了解决方案 
x∗=b/a 。 下图说明了两个问题的等效性:解决方案 
x∗ 方程式 
ax−b=0 与方程的解一致 
 mathrmargminx(ax2−2bx) 。

我倾向于这样一个事实,即我们的总体目标是最小化二次函数,但是我们正在谈论最小二乘。 但是与此同时,我们真正知道如何做得很好的是求解线性方程(以及线性方程组)。 而且一个等于另一个很酷!
给我们留下的唯一一件事就是确保一维世界中的等价性扩展到案例 
n 变量。 为了验证这一点,我们首先证明三个定理。
三个定理或微分矩阵表达式
第一个定理指出矩阵 
1\乘以1 关于换位的不变式:
定理1
x in mathbbR Rightarrowx top=x证明太复杂了,为了简洁起见,我不得不省略它,但是请尝试自己找到它。
第二定理使我们能够区分线性函数。 对于一个变量的实函数,我们知道 
 fracddx(bx)=b 但是在实函数的情况下会发生什么 
n 变量?
定理2
 nablab topx= nablax topb=b也就是说,不出意外,只是记录同一学校成绩的矩阵记录。 证明非常简单,只需编写渐变的定义即可:
 nabla(b topx)=\开始bmatrix frac\部分(b topx)\部分x1 vdots frac\部分(b topx)\部分xn\结束bmatrix=\开始bmatrix frac\部分(b1x1+\点+bnxn)\部分x1 vdots frac\部分(b1x1+\点+bnxn)\部分xn\结束bmatrix=\开始bmatrixb1 vdotsbn\结束bmatrix=b
应用第一个定理 
b topx=x topb ,我们完成了证明。
现在我们传递给二次形式。 我们知道在一个变量的实函数的情况下 
 fracddx(ax2)=2ax ,并且在二次形式的情况下会发生什么?
定理3
 nabla(x topAx)=(A+A top)x顺便提一下,如果矩阵 
对称,那么定理说 
 nabla(x topAx)=2Ax 。
该证明也很简单,只需将二次形式写为双倍和:
x topAx= sum limitsi sum limitsjaijxixj
然后,让我们看看这个双重和关于变量的偏导数是什么 
xi :
\开始{align *}
\ frac {\部分(x ^ \ top A x)} {\部分x_i}
&= \ frac {\部分} {\部分x_i} \左(\ sum \极限_ {k_1} \ sum \极限_ {k_2} a_ {k_1 k_2} x_ {k_1} x_ {k_2} \右)= \\
&= \ frac {\部分} {\部分x_i} \左(
\ underbrace {\ sum \ limit_ {k_1 \ neq i} \ sum \ limits_ {k_2 \ neq i} a_ {ik_2} x_ {k_1} x_ {k_2}} _ _ {k_1 \ neq i,k_2 \ neq i} + \底线{\ sum \ limits_ {k_2 \ neq i} a_ {ik_2} x_i x_ {k_2}} _ {k_1 = i,k_2 \ neq i} +
\ underbrace {\ sum \ limits_ {k_1 \ neq i} a_ {k_1 i} x_ {k_1} x_i} _ {k_1 \ neq i,k_2 = i} +
\ underbrace {a_ {ii} x_i ^ 2} _ {k_1 = i,k_2 = i} \ right)= \\
&= \ sum \ limits_ {k_2 \ neq i} a_ {ik_2} x_ {k_2} + \ sum \ limits_ {k_1 \ neq i} a_ {k_1 i} x_ {k_1} + 2 a_ {ii} x_i = \\
&= \ sum \ limits_ {k_2} a_ {ik_2} x_ {k_2} + \ sum \ limits_ {k_1} a_ {k_1 i} x_ {k_1} = \\
&= \ sum \ limits_ {j}(a_ {ij} + a_ {ji})x_j \\
\结束{align *}
我将双和分为四个案例,以大括号突出显示。 这四种情况均微不足道。 它仍然要做最后的动作,收集梯度向量中的偏导数:
 nabla(x topAx)=\开始bmatrix frac\部分(x topAx)\部分x1 vdots frac\部分(x\顶部Ax)\部分xn\结束bmatrix=\开始bmatrix\和 limitj(a1j+aj1)xj vdots sum limitsj(anj+ajn)xj endbmatrix=(A+A top)x
二次函数的最小值和线性系统的解
让我们不要忘记旅行的方向。 我们看到一个正数 
一 对于一个变量的实函数,求解方程 
ax=b 或最小化二次函数 
 mathrmargminx(ax2−2bx) 是一回事。
我们想在对称正定矩阵的情况下显示相应的连接 
。
所以我们想找到二次函数的最小值
 mathrmargminx in mathbbRn(x topAx−2b topx)
就像在学校一样,我们将导数等于零:
 nabla(x topAx−2b topx)=\开始bmatrix0 vdots0 endbmatrix
Hamilton算子是线性的,因此我们可以按以下形式重写方程式:
 nabla(x topAx)−2 nabla(b topx)=\开始bmatrix0 vdots0 endbmatrix
现在我们应用第二和第三阶微分定理:
(A+A top)x−2b=\开头bmatrix0 vdots0\结束bmatrix
回想一下 
对称,并将方程分为两部分,我们得到所需的线性方程组:
斧=b
Hooray从一个变量变为多个变量,我们什么都没有丢失,并且我们可以有效地最小化二次函数!
我们传递到最小二乘
最后,我们可以继续本讲座的主要内容。 想象一下,我们在飞机上有两个点 
(x1,y1) 和 
(x2,y2) ,我们想找到穿过这两个点的直线方程。 这条线的方程可以写成 
y= alphax+ beta ,也就是说,我们的任务是找到系数 
 alpha 和 
 beta 。 此练习是针对该学校的七年级学生,我们写下了方程组:
\左\ {\开始{拆分} \ alpha x_1 + \ beta&= y_1 \\ \ alpha x_2 + \ beta&= y_2 \\ \结束{split} \ right。
我们有两个带有两个未知数的方程( 
 alpha 和 
 beta ),其余的都是已知的。
在一般情况下,解决方案是存在的并且是唯一的。 为了方便起见,我们以矩阵形式重写相同的系统:
 underbrace beginbmatrixx1&1x2&1 endbmatrix:=A underbrace beginbmatrix alpha beta endbmatrix:=x=\底线\开始bmatrixy1y2\结束bmatrix:=b
我们得到一个类型的方程 
斧=b 琐碎地解决了 
x∗=A−1b 。
现在想象一下,我们有
三个点需要画一条直线:
\左\ {\开始{拆分} \ alpha x_1 + \ beta&= y_1 \\ \ alpha x_2 + \ beta&= y_2 \\ \ alpha x_3 + \ beta&= y_3 \\ \结束{split} \ right 。
该系统以矩阵形式编写如下:
 underbrace beginbmatrixx1&1x2&1x3&1 endbmatrix:=A\,(3 2倍) underbrace beginbmatrix alpha beta endbmatrix:=x\,(2 times1)=\底线\开始bmatrixy1y2y3 endbmatrix:=b\,(3\乘以1)
现在我们有了一个矩阵 
矩形,它根本不存在反向! 这是完全正常的,因为我们只有两个变量和三个方程,在一般情况下,该系统没有解。 这是现实世界中的一种完全正常的情况,当这些点是来自噪声测量的数据时,我们需要找到参数 
 alpha 和 
 beta 最接近的测量数据。 在第一个演讲中,当我们对杆秤进行校准时,我们已经考虑了这个示例。 但是那时我们的解决方案纯粹是代数的,非常麻烦。 让我们尝试一种更直观的方法。
我们的系统可以编写如下:
 alpha underbrace\开始bmatrixx1x2x3 endbmatrix:= veci+ beta underbrace\开始bmatrix11  1\结束bmatrix:= vecj=\开始bmatrixy1y2y3\结束bmatrix
或者,更简单地说,
 alpha veci+ beta vecj= vecb。
向量 
 vec我 , 
 vecj 和 
 vecb 已知,需要找到标量 
 alpha 和 
 beta 。
显然是线性组合 
 alpha veci+ beta vecj 产生一架飞机,如果向量 
 vecb 不在这个平面上,那么就没有确切的解决方案; 但是,我们正在寻找一种近似的解决方案。
让我们将决策错误定义为 
 vece( alpha, beta):= alpha veci+ beta vecj− vecb 。 我们的目标是找到这样的参数值 
 alpha 和 
 beta 最小化向量的长度 
 vece( alpha, beta) 。 换句话说,问题写为 
 mathrmargmin alpha, beta | vece( alpha, beta) | 。
这是一个例证:

给定向量 
 vec我 , 
 vecj 和 
 vecb ,我们尝试最小化误差向量的长度 
 vece 。 显然,如果它的长度垂直于在矢量上拉伸的平面,则其长度将最小化 
 vec我 和 
 vecj 。
但是,等等,最小的平方在哪里? 现在他们会的。 根提取功能 
 sqrt cdot 因此单调 
 mathrmargmin alpha, beta | vece( alpha, beta) | = 
 mathrmargmin alpha, beta | vece( alpha, beta) |2 !
很明显,如果误差向量的长度垂直于在向量上延伸的平面,则它的长度将最小化 
 vec我 和 
 vecj ,可以通过将对应的标量乘积等于零来编写:
\左\ {\开始{分割} \ vec {i} ^ \ top \ vec {e}(\ alpha,\ beta)&= 0 \\ \ vec {j} ^ \ top \ vec {e}(\ alpha,\ beta)&= 0 \ end {split} \ right。
以矩阵形式,该同一系统可以写成
\开始bmatrixx1&x2&x31&1&1\结束bmatrix\向左( alpha\开始bmatrixx1x2x3\结束bmatrix+ beta\开始bmatrix111\结束bmatrix−\开始bmatrixy1y2y3 endbmatrix right)=\开始bmatrix00 endbmatrix
否则
\开始bmatrixx1&x2&x31&1&1\结束bmatrix\向左(\开始bmatrixx1&1x2&1x3&1\结束bmatrix\开始bmatrix alpha beta\结束bmatrix−\开始bmatrixy1y2y3\结束bmatrix right)=\开始bmatrix00\结束bmatrix
但是我们不会止步于此,因为可以进一步减少记录:
A top(Ax−b)=\开始bmatrix00 endbmatrix
最近的转换:
A topAx=A topb。
让我们计算尺寸。 矩阵 
有大小 
3\乘2 因此 
A topA 有大小 
2\乘2 。 矩阵 
b 有大小 
3\乘1 但是矢量 
A topb 有大小 
2\乘以1 。 也就是说,在一般情况下,矩阵 
A topA 可逆的! 而且, 
A topA 它具有几个不错的属性。
定理4
A topA 对称的。
这一点一点都不难显示:
(A topA) top=A top(A top) top=A topA.
定理5
A topA 正半定: 
 forallx in mathbbRn quadx topA topAx geq0这是由于以下事实: 
x topA topAx=(Ax) topAx>0 。
也 
A topA 正定如果 
具有线性独立的列(排名 
等于系统中的变量数)。
总之,对于具有两个未知数的系统,我们证明了二次函数的最小化 
 mathrmargmin alpha, beta | vece( alpha, beta) |2 等效于求解线性方程组 
A topAx=A topb 。 当然,所有这些推理都适用于任何其他数量的变量,但让我们使用简单的代数计算将所有内容再次紧凑地写在一起。 我们从最小二乘问题开始,对熟悉形式的二次函数进行最小化,然后得出结论,这等效于求解线性方程组。 因此,我们开始:
\开始{拆分} \ Mathrm {argmin} \ | Ax-b \ | ^ 2&= \ mathrm {argmin}(Ax-b)^ \ top(Ax-b)= \\&= \ mathrm {argmin}(x ^ \ top A ^ \ top-b ^ \ top)(Ax-b)= \\&= \ mathrm {argmin}(x ^ \ top A ^ \ top A x-b ^ \ top Ax-x ^ \ top A ^ \ top b + \ underbrace {b ^ \ top b} _ {\ rm const})= \\&= \ mathrm {argmin}(x ^ \ top A ^ \ top A x-2b ^ \ top Ax)= \\&= \ mathrm {argmin}( x ^ \ top \ underbrace {(A ^ \ top A)} _ {:= A'} x-2 \ underbrace {{A ^ \ top b)} _ {:= b'} \ phantom {} ^ \ top x)\结束{拆分}
所以最小二乘问题 
 mathrmargmin |斧头−b |2 等效于最小化二次函数 
 米一吨ħ ř 米一个ř 克米我Ñ (X 吨ö p 甲“ X - 2 b ” 吨ö p X ) (一般情况下)有一个对称的正定矩阵 
A ' ,又相当于求解线性方程组 
甲' X = b ' 。 ff 理论已经结束。
让我们继续练习
示例一,一维
让我们回想一下
上一篇文章中的一个示例:
 
我们有32个元素的常规x数组,其初始化如下:

然后,我们将执行以下过程一千次:对于每个单元x [i],我们将邻近单元的平均值写入其中:x [i] =(x [i-1] + x [i + 1])/ 2。 唯一的例外:我们不会用数字0、18和31重写数组的元素。这就是数组在前一百五十次迭代中的样子:

正如我们
在前一篇文章中发现的
那样 ,事实证明,我们的四行python代码宏代码通过Gauss-Seidel方法解决了以下线性方程组:

他们发现了,但是这个系统是从哪里来的呢? 什么样的魔术? 让我们离题,尝试解决稍微不同的系统。 我仍然有32个变量,但是现在我希望它们全都相等。 写下这个并不难,写下一个方程式系统就足够了,该方程式指出所有相邻元素成对相等:

上面的Python代码原则上不会触及三个变量的值:x0,x18,x31,因此让我们将它们传递到方程组的右侧,即从未知数集中排除:

我固定初始值x0 = 1,第一个方程式说x1 = x0 = 1,第二个方程式说x2 = x1 = x0 = 1依此类推,直到我们得到方程式1 = x0 = x17 = x18 = 2哦 但是这个系统没有解决方案:((
不管,我们是程序员! 让我们叫最小平方来寻求帮助! 我们无法解决的系统可以用矩阵形式编写 
斧 = b ;
 大概解决我们的系统,就足以解决系统 甲⊤甲X = 甲⊤ b 。
然后事实证明,这正是上一篇文章中的一对一方程组!凭直觉,可以想象创建系统矩阵的过程,如下所示:我们通过弹簧连接要实现其相等性的值。例如,在我们的情况下,我们想要X 我是等于x i + 1,所以我们加上等式x i - x i + 1 = 0,在这些值之间悬挂一个弹簧,将它们拉在一起。但是,由于x0,x18和x31的值是固定不变的,因此除了均匀拉伸弹簧之外,没有其他任何自由值,从而产生(在这种情况下)分段线性解决方案。让我们编写一个解决该方程组的程序。在上一篇文章中,我们看到Gauss-Seidel方法的编程非常简单,但是收敛速度很慢,因此最好使用线性方程组的实数求解器。在最简单的情况下,对于我们的一维示例,代码可能看起来像这样: import numpy as np n = 32  
该代码产生一个x的32个元素的列表,其中存储了解决方案。我们正在建立一个矩阵 建筑矢量 b,我们得到一个带有解的向量X = (甲⊤甲)- 1甲⊤ b,然后插入到该载体中提供了固定的值x0 = 1,X18 = X31 = 2和1。这段代码可以完成必要的工作,但是将固定变量的值传递到右侧是非常不愉快的。有可能作弊吗?可以!让我们看下面的代码: import numpy as np n = 32  
从程序员的角度来看,这要好得多,可以立即获得所需尺寸的向量x,并且可以更轻松地填充矩阵A和b。但是,诀窍是什么?让我们来写一个求解的方程组: 仔细查看最后三个方程:100 x0 = 100 *1100 x18 = 100 * 2100 x31 = 100 * 1编写它们的方法是否容易如下?x0 = 1x18 = 2x31 = 1不,不容易。正如我已经说过的,每个方程式都悬挂着弹簧,在这种情况下,x0和值1之间的弹簧,x18和值2之间的弹簧,最后,变量x31也将被吸引。但是存在100的系数是有原因的。我们记得这个系统只是无法求解,我们从最小二乘的意义上解决了它,从而最小化了相应的二次函数。将最后三个方程式的每个系数乘以100,我们引入了将x0,x18和x32偏离期望值的惩罚,超过了偏离等式的惩罚的一万倍(100 ^ 2)x i = x i + 1
仔细查看最后三个方程:100 x0 = 100 *1100 x18 = 100 * 2100 x31 = 100 * 1编写它们的方法是否容易如下?x0 = 1x18 = 2x31 = 1不,不容易。正如我已经说过的,每个方程式都悬挂着弹簧,在这种情况下,x0和值1之间的弹簧,x18和值2之间的弹簧,最后,变量x31也将被吸引。但是存在100的系数是有原因的。我们记得这个系统只是无法求解,我们从最小二乘的意义上解决了它,从而最小化了相应的二次函数。将最后三个方程式的每个系数乘以100,我们引入了将x0,x18和x32偏离期望值的惩罚,超过了偏离等式的惩罚的一万倍(100 ^ 2)x i = x i + 1 。
粗略地讲,后三个方程的弹簧比其他所有方程都难上万倍。这种二次惩罚方法是引入限制的非常方便的工具;比减少一组变量要简单得多(尽管并非没有缺点)。实例二,三维
让我们继续进行一些比平滑一维数组的元素更有趣的事情!首先,我们将做完全一样的工作,但要使用更多有趣的数据和实际工具。我想获取一个三维曲面,固定其边界,并尽可能使曲面的其余部分平滑:此处提供了 代码,以防万一我将给出主文件的完整列表:
代码,以防万一我将给出主文件的完整列表: #include <vector> #include <iostream> #include "OpenNL_psm.h" #include "geometry.h" #include "model.h" int main(int argc, char** argv) { if (argc<2) { std::cerr << "Usage: " << argv[0] << " obj/model.obj" << std::endl; return 1; } Model m(argv[1]); for (int d=0; d<3; d++) { // solve for x, y and z separately nlNewContext(); nlSolverParameteri(NL_NB_VARIABLES, m.nverts()); nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE); nlBegin(NL_SYSTEM); nlBegin(NL_MATRIX); for (int i=0; i<m.nhalfedges(); i++) { // fix the boundary vertices if (m.opp(i)!=-1) continue; int v = m.from(i); nlRowScaling(100.); nlBegin(NL_ROW); nlCoefficient(v, 1); nlRightHandSide(m.point(v)[d]); nlEnd(NL_ROW); } for (int i=0; i<m.nhalfedges(); i++) { // smooth the interior if (m.opp(i)==-1) continue; int v1 = m.from(i); int v2 = m.to(i); nlRowScaling(1.); nlBegin(NL_ROW); nlCoefficient(v1, 1); nlCoefficient(v2, -1); nlEnd(NL_ROW); } nlEnd(NL_MATRIX); nlEnd(NL_SYSTEM); nlSolve(); for (int i=0; i<m.nverts(); i++) { m.point(i)[d] = nlGetVariable(i); } } std::cout << m; return 0; } 
我编写了wavefront .obj文件的最简单的解析器,因此该模型在一行中加载。作为求解器,我将使用OpenNL,它是解决稀疏矩阵最小二乘问题的强大求解器。请注意,矩阵的尺寸可能是巨大的:例如,如果我们要获取1000 x 1000像素的黑白图片,则矩阵一个⊤一个我们将有一百万的大小,以一百万!但是,实际上,此矩阵的绝大多数元素通常都是零,因此一切并不是那么可怕,但是您仍然需要使用特殊的求解器。因此,让我们看一下代码。首先,我向求解器宣布我要执行的操作:  nlNewContext(); nlSolverParameteri(NL_NB_VARIABLES, m.nverts()); nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE); nlBegin(NL_SYSTEM); nlBegin(NL_MATRIX); 
我想要一个矩阵 甲⊤甲大小(科尔维尔顶点)×(科尔维尔顶点)。注意,我们给求解器一个矩阵 和 一个⊤一个它会自身建设,这是非常方便!另外,请注意以下事实:我不是求解一个方程组,而是针对x,y和z串联求解三个方程组(我认为问题是三维的,但仍然是一维的!),然后逐行声明矩阵。首先,我修复曲面边缘上的顶点:  for (int i=0; i<m.nhalfedges(); i++) {  
您可以看到我使用100 ^ 2的平方惩罚来修复边顶点。好吧,然后为每对相邻的顶点在它们之间悬挂一个刚度为1的弹簧:  for (int i=0; i<m.nhalfedges(); i++) {  
我们执行代码,看看如何在弯曲的线上将表面平滑到肥皂膜的表面。我们刚刚解决了Dirichlet问题,获得了面积最小的表面。我们提升特色
让我们将自己的脸变成一个怪诞的面具: 在上一篇文章中,我已经展示了如何在膝盖上执行此操作,在这里,我给出了一个使用最小二乘法求解器的真实代码:
在上一篇文章中,我已经展示了如何在膝盖上执行此操作,在这里,我给出了一个使用最小二乘法求解器的真实代码:  for (int d=0; d<3; d++) {  
完整的代码在这里。他如何工作?与前面的示例一样,我从固定边顶点开始。然后,对于每条边缘我(安静地,系数为0.2)说,如果它的形状与原始曲面完全相同,那就太好了:  for (int i=0; i<m.nhalfedges(); i++) {  
如果您什么都不做,并按原样解决问题,那么我们应该确切地了解输出的输出内容:输出边界等于输入边界,加上输出的差异等于输入的差异。但是我们不会止步于此!  for (int v=0; v<m.nverts(); v++) {  
我在系统中添加了更多方程式。对于每个顶点,我计算输入曲面中围绕它的曲面的曲率,然后将其乘以两个半,即输出曲面到处都应具有较大的曲率。然后我们调用求解器并获得良好的讽刺画。今天的最后一个例子
到目前为止,我们还没有看到一个新的示例,所有内容已在上一篇文章中显示。让我们尝试做一些比最简单的平滑更简单的事情,而无需使用任何求解器就可以很容易地获得它。该代码在此处可用,但让我给您列出: #include <vector> #include <iostream> #include "OpenNL_psm.h" #include "geometry.h" #include "model.h" const Vec3f axes[] = {Vec3f(1,0,0), Vec3f(-1,0,0), Vec3f(0,1,0), Vec3f(0,-1,0), Vec3f(0,0,1), Vec3f(0,0,-1)}; int snap(Vec3f n) { // returns the coordinate axis closest to the given normal double nmin = -2.0; int imin = -1; for (int i=0; i<6; i++) { double t = n*axes[i]; if (t>nmin) { nmin = t; imin = i; } } return imin; } int main(int argc, char** argv) { if (argc<2) { std::cerr << "Usage: " << argv[0] << " obj/model.obj" << std::endl; return 1; } Model m(argv[1]); std::vector<int> nearest_axis(m.nfaces()); for (int i=0; i<m.nfaces(); i++) { Vec3f v[3]; for (int j=0; j<3; j++) v[j] = m.point(m.vert(i, j)); Vec3f n = cross(v[1]-v[0], v[2]-v[0]).normalize(); nearest_axis[i] = snap(n); } for (int d=0; d<3; d++) { // solve for x, y and z separately nlNewContext(); nlSolverParameteri(NL_NB_VARIABLES, m.nverts()); nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE); nlBegin(NL_SYSTEM); nlBegin(NL_MATRIX); for (int i=0; i<m.nhalfedges(); i++) { int v1 = m.from(i); int v2 = m.to(i); nlRowScaling(1.); nlBegin(NL_ROW); nlCoefficient(v1, 1); nlCoefficient(v2, -1); nlRightHandSide(m.point(v1)[d] - m.point(v2)[d]); nlEnd(NL_ROW); int axis = nearest_axis[i/3]/2; if (d!=axis) continue; nlRowScaling(2.); nlBegin(NL_ROW); nlCoefficient(v1, 1); nlCoefficient(v2, -1); nlEnd(NL_ROW); } nlEnd(NL_MATRIX); nlEnd(NL_SYSTEM); nlSolve(); for (int i=0; i<m.nverts(); i++) { m.point(i)[d] = nlGetVariable(i); } } std::cout << m; return 0; } 
我们仍然在与以前一样的表面上进行工作;我们为每个三角形调用snap()函数。该函数表示坐标系的哪个轴最接近该三角形的法线。也就是说,我们想知道这个三角形更可能是垂直或水平的。好吧,那么我们要更改表面的几何形状,以使靠近水平面的东西变得更近!该图的左侧显示了snap()函数的结果: 因此,我们用三种颜色绘制了曲面,分别对应于坐标系的三个轴。好吧,那么对于我们系统的每个边缘,我们添加以下方程式:
因此,我们用三种颜色绘制了曲面,分别对应于坐标系的三个轴。好吧,那么对于我们系统的每个边缘,我们添加以下方程式:  nlRowScaling(1.); nlBegin(NL_ROW); nlCoefficient(v1, 1); nlCoefficient(v2, -1); nlRightHandSide(m.point(v1)[d] - m.point(v2)[d]); nlEnd(NL_ROW); 
也就是说,我们指示输出表面的每个边缘都类似于输入表面的相应边缘,即弹簧1的刚度。然后,如果我们允许例如x尺寸,并且该边缘应垂直,那么我们简单地说一个顶点等于另一个顶点,并具有弹簧的刚度。 2:  nlRowScaling(2.); nlBegin(NL_ROW); nlCoefficient(v1, 1); nlCoefficient(v2, -1); nlEnd(NL_ROW); 
好吧,这是我们程序的结果:一个 完全合理的问题:复活节岛的偶像当然很酷,但是地质与它有什么关系?是否有实际问题的例子?当然可以让我们看一看地壳的一部分:
完全合理的问题:复活节岛的偶像当然很酷,但是地质与它有什么关系?是否有实际问题的例子?当然可以让我们看一看地壳的一部分: 对于地质学家来说,不同岩石的层非常重要,它们之间的边界(水平)以绿色显示,而地质断层则以红色显示。我们在输入处有我们感兴趣的区域的三角形网格(3D中的四面体),但是需要四边形(3D中的六面体)对某些过程进行建模。我们对模型进行变形,以使断层变为垂直而水平(惊奇)变为水平:
对于地质学家来说,不同岩石的层非常重要,它们之间的边界(水平)以绿色显示,而地质断层则以红色显示。我们在输入处有我们感兴趣的区域的三角形网格(3D中的四面体),但是需要四边形(3D中的六面体)对某些过程进行建模。我们对模型进行变形,以使断层变为垂直而水平(惊奇)变为水平: 然后,我们只取一个规则的正方形网格,将我们的域切成均匀的正方形,并对它们应用逆变换,以获得必要的四边形网格。
然后,我们只取一个规则的正方形网格,将我们的域切成均匀的正方形,并对它们应用逆变换,以获得必要的四边形网格。结论
最小二乘法是一种强大的数据处理工具,与Excel列中的统计信息相比,它们的使用范围广泛。由于将二次函数最小化和线性方程组求解是相同的事实,这些可能性为我们打开了大门,而且我们(人类)已经学会了非常非常好地求解线性方程组,甚至完全解决了具有数十亿个变量的非人类尺寸。但是,有时我们可能没有足够的线性模型。例如,在这里,神经网络可以为您提供帮助。在下一篇文章中,我将尝试证明拒绝排放的OLS等效于神经网络的公式之一。保持在线!