1. 背景介绍
上(下)三角矩阵有许多性质使得计算机可以方便地对它们进行各种线性代数基础运算。例如对三角矩阵的求逆非常简单易理解。因此在计算非奇异矩阵的逆时常将原矩阵AAA分解为一个下三角矩阵LLL(LLL矩阵的对角线元素为111,对角线以上部分元素为000)和一个上三角矩阵UUU(UUU矩阵对角线以下部分元素为000)相乘的形式,再分别求下三角矩阵的逆L−1L^{-1}L−1和上三角矩阵的逆U−1U^{-1}U−1,再将它们相乘获得最终的结果。
理论上利用LU分解求矩阵逆分为三步:
- 对矩阵进行LU分解:A=LUA=LUA=LU;
- 求解线性方程组:LX=ILX=ILX=I,X=L−1X=L^{-1}X=L−1;
- 求解线性方程组:UB=L−1UB=L^{-1}UB=L−1, B=U−1L−1=A−1B=U^{-1}L^{-1}=A^{-1}B=U−1L−1=A−1;
LU分解可以用于任意宽高的矩阵,本文只讨论方阵。
2. 理论基础
在进行LULULU分解时,需要构造一个下三角矩阵和一个上三角矩阵,这时需要满秩变换来逐向量地将原矩阵变换为上三角矩阵UUU,并记录变换矩阵LLL。
2.1 高斯消元
2.1节介绍LULULU分解的关键步骤-高斯消元。假设此时需要将nnn维矩阵AAA的第kkk(1≤k≤n1 \leq k \leq n1≤k≤n)列向量a⋅,k=[a1,k,a2,k,⋯ ,an,k]Ta_{\cdot,k}=\left[a_{1,k},a_{2,k},\cdots,a_{n,k} \right]^Ta⋅,k=[a1,k,a2,k,⋯,an,k]T中的第k+1k+1k+1个元素到第nnn个元素变换为000,需要构造变换矩阵MkM_kMk。首先需要构造高斯消元向量τk\tau_kτk:
τkT=[0,⋯ ,0,τk+1,⋯ ,τn],其中τi=ai,kak,k,i=k+1,⋯ ,n
\tau_k^T=\left[ 0,\cdots,0,\tau_{k+1},\cdots,\tau_{n} \right],\qquad 其中\tau_{i}= \frac {a_{i,k}}{a_{k,k}},i=k+1,\cdots,n
τkT=[0,⋯,0,τk+1,⋯,τn],其中τi=ak,kai,k,i=k+1,⋯,n
再定义辅助矩阵EkE_kEk:
Ek=[0⋯1⋯0⋮⋮⋮0⋯1⋯0⋮⋮⋮0⋯1⋯0],Ek中第k列全为1,其他元素为0
E_k=
\begin{bmatrix}
0 & \cdots & 1 & \cdots & 0 \\
\vdots & & \vdots & & \vdots \\
0 & \cdots & 1 & \cdots & 0 \\
\vdots & & \vdots & & \vdots \\
0 & \cdots & 1 & \cdots & 0
\end{bmatrix},E_k中第k列全为1,其他元素为0
Ek=⎣⎢⎢⎢⎢⎢⎢⎡0⋮0⋮0⋯⋯⋯1⋮1⋮1⋯⋯⋯0⋮0⋮0⎦⎥⎥⎥⎥⎥⎥⎤,Ek中第k列全为1,其他元素为0
于是可以得到高斯消元矩阵MkM_kMk:
Mk=I−τk⊙Ek,(⊙表示点乘)
M_k=I-\tau_k \odot E_k,(\odot表示点乘)
Mk=I−τk⊙Ek,(⊙表示点乘)
最后对a⋅,ka_{\cdot,k}a⋅,k进行高斯消元:
Mk⋅a⋅,k=[1⋯00⋯0⋮⋱⋮⋮⋱⋮0⋯10⋯00⋯−τk+11⋯0⋮⋱⋮⋮⋱⋮0⋯−τn0⋯1][a1,k⋮ak,kak+1,k⋮an,k]=[a1,k⋮ak,k0⋮0](2.1.1)
M_k \cdot a_{\cdot,k}=
\begin{bmatrix}
1 & \cdots & 0 & 0 & \cdots & 0 \\
\vdots & \ddots & \vdots & \vdots & \ddots & \vdots \\
0 & \cdots & 1 & 0 & \cdots & 0 \\
0 & \cdots & -\tau_{k+1} & 1 & \cdots & 0 \\
\vdots & \ddots & \vdots & \vdots & \ddots & \vdots \\
0 & \cdots & -\tau_{n} & 0 & \cdots & 1
\end{bmatrix}
\begin{bmatrix}
a_{1,k} \\ \vdots \\ a_{k,k} \\ a_{k+1,k} \\ \vdots \\ a_{n,k} \\
\end{bmatrix}=
\begin{bmatrix}
a_{1,k} \\ \vdots \\ a_{k,k} \\ 0 \\ \vdots \\ 0 \\
\end{bmatrix} \tag{2.1.1}
Mk⋅a⋅,k=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡1⋮00⋮0⋯⋱⋯⋯⋱⋯0⋮1−τk+1⋮−τn0⋮01⋮0⋯⋱⋯⋯⋱⋯0⋮00⋮1⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡a1,k⋮ak,kak+1,k⋮an,k⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡a1,k⋮ak,k0⋮0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤(2.1.1)
2.2 矩阵三角化
对nnn维方阵AAA的每一列从左到右重复nnn次高斯消元便可以得到上三角矩阵UUU:
Mn(Mn−1(⋯(M1A))=U(2.1.1)
M_n(M_{n-1}(\cdots(M_1A))=U \tag{2.1.1}
Mn(Mn−1(⋯(M1A))=U(2.1.1)
LLL矩阵即是nnn个高斯消元矩阵的乘积:
A=(MnMn−1⋯M1)−1U=LU(2.1.2)
A=(M_nM_{n-1}\cdots M_1)^{-1}U=LU \tag{2.1.2}
A=(MnMn−1⋯M1)−1U=LU(2.1.2)
L=M1−1M2−1⋯Mn−1(2.1.3) L=M_1^{-1}M_2^{-1}\cdots M_n^{-1} \tag{2.1.3} L=M1−1M2−1⋯Mn−1(2.1.3)
2.3 LU分解的一般形式
设非奇异矩阵A∈Rn×nA \in \mathbb{R}^{n\times n}A∈Rn×n
A=[αωTvB],其中α∈R,ωT∈R1×(n−1),v∈R(n−1)×1,B∈R(n−1)×(n−1)
A=\begin{bmatrix}
\alpha & \omega^{T} \\
v & B
\end{bmatrix},
\qquad 其中\alpha \in \mathbb{R},
\omega^T \in \mathbb{R}^{1\times(n-1)},
v \in \mathbb{R}^{(n-1)\times1},
B \in \mathbb{R}^{(n-1)\times(n-1)}
A=[αvωTB],其中α∈R,ωT∈R1×(n−1),v∈R(n−1)×1,B∈R(n−1)×(n−1)
那么高斯消元的第111步分解结果是:
[α1ω1Tv1B1]=[10z1/α1I1][100B1−z1w1T/α1][α1ω1T0I1](2.3.1)
\begin{bmatrix}
\alpha_1 & \omega_1^{T} \\
v_1 & B_1
\end{bmatrix} =
\begin{bmatrix}
1 & 0 \\
z_1/\alpha_1 & I_{1}
\end{bmatrix}
\begin{bmatrix}
1 & 0 \\
0 & B_1 - z_1w_1^T/\alpha_1
\end{bmatrix}
\begin{bmatrix}
\alpha_1 & \omega_1^{T} \\
0 & I_{1}
\end{bmatrix} \tag{2.3.1}
[α1v1ω1TB1]=[1z1/α10I1][100B1−z1w1T/α1][α10ω1TI1](2.3.1)
第k,k=2,3,⋯ ,nk, k=2,3,\cdots,nk,k=2,3,⋯,n步可以重复以上步骤分解Bk−1−zk−1wk−1T/αk−1=Lk−1Uk−1B_{k-1} - z_{k-1}w_{k-1}^T/\alpha_{k-1} = L_{k-1}U_{k-1}Bk−1−zk−1wk−1T/αk−1=Lk−1Uk−1。那么:
Lk−1Uk−1=[10zk/αkIk][100Bk−zkwkT/αk][αkωkT0Ik]=[10zk/αkLk][αkωkT0Uk](2.3.2)
L_{k-1}U_{k-1}=
\begin{bmatrix}
1 & 0 \\
z_k/\alpha_k & I_k
\end{bmatrix}
\begin{bmatrix}
1 & 0 \\
0 & B_k - z_k w_k ^T/\alpha_k
\end{bmatrix}
\begin{bmatrix}
\alpha_k & \omega_k^{T} \\
0 & I_{k}
\end{bmatrix}=
\begin{bmatrix}
1 & 0 \\
z_k/\alpha_k & L_k
\end{bmatrix}
\begin{bmatrix}
\alpha_k & \omega_k^{T} \\
0 & U_k
\end{bmatrix} \tag{2.3.2}
Lk−1Uk−1=[1zk/αk0Ik][100Bk−zkwkT/αk][αk0ωkTIk]=[1zk/αk0Lk][αk0ωkTUk](2.3.2)
3. 算法实现
对于方阵AAA,假设在已经过k−1k-1k−1步高斯消元,那么下一步是对A(:,k)A(:, k)A(:,k)进行高斯消元。再解下来便是利用高斯消元继续更新A(k+1:n,k+1:n)A(k+1:n,k+1:n)A(k+1:n,k+1:n)。由此可以推导出3.1节中的算法。
3.1 朴素的LU分解算法
假设A∈Rn×nA \in \mathbb{R}^{n \times n}A∈Rn×n非奇异,LLL矩阵是下三角矩阵(对角线元素为111),UUU是上三角矩阵,在进行LULULU分解之后,AAA的下三角部分被LLL覆盖(不包括对角线),上三角部分被UUU覆盖(包括对角线)。以下是算法伪代码:
for k=1:n−1k = 1:n-1k=1:n−1
ρ=k+1:n\rho = k+1:nρ=k+1:n
A(ρ,k)=A(ρ,k)/A(k,k)A(\rho, k) = A(\rho, k) / A(k, k)A(ρ,k)=A(ρ,k)/A(k,k)
A(ρ,ρ)=A(ρ,ρ)−A(ρ,k)⋅A(k,ρ)A(\rho, \rho) = A(\rho, \rho) - A(\rho, k) \cdot A(k, \rho)A(ρ,ρ)=A(ρ,ρ)−A(ρ,k)⋅A(k,ρ)
end
朴素的LULULU分解算法在C语言实现时有三层循环,那么排列组合A33A^3_3A33之后一共有666个版本。假设矩阵在内存中以数组的形式按行顺序存储,那么一种版本的C语言代码为:
void LU_decomposition(double* A, const size_t n)
{
size_t i, j, k;
double r;
for (k=0; k<n-1; k++)
{
for (i=k+1; i<n; i++)
{
A[i * n + k] = A[i * n + k] / A[k * n + k];
for (j=k+1; j<n; j++)
A[i * n + j] = A[i * n + j] - A[i * n + k] * A[k * n + j];
}
}
}
注意此处编译器优化:
例如在第9行的C语言代码中,会取出A元素的第i * n + k个元素,编译器判断每一次取数的地址是等间隔递增的,因此在程序编译完成后的汇编代码会使用简单的加法替代乘法。在函数上下文中,参数A和n的值在寄存器r0和r1中,假设A[i * n + k]元素的地址已在r6寄存器中:MUL r5, r1, #8(行间隔字节量,双精度浮点数占8字节)
…
ADD r6, r6, r5
FLDD d0, [r6]
…
3.2 Gaxpy vs 外积
在工程应用中,两种计算量相同的算法常常会有不同的数据迁移量。考虑一下n×nn \times nn×n的Gaxpy计算和外积计算:
y=y+Ax,
y = y + Ax,
y=y+Ax,
A=A+yxT,A∈Rn×n,x,y∈Rn×1 A = A + yx^T, \quad A \in \mathbb{R}^{n \times n}, \quad x, y \in \mathbb{R}^{n\times 1} A=A+yxT,A∈Rn×n,x,y∈Rn×1
比较两种计算量相同算法的计算形式,用←\gets←表示内存和寄存器之间的读写,对于Gaxpy:
rx←x,ry←yr_x \gets x, \quad r_y \gets yrx←x,ry←y
for j=1:nj = 1:nj=1:n
ra←A(:,j)r_a \gets A(:, j)ra←A(:,j)
ry=ry+rarx(j)r_y = r_y + r_a r_x(j)ry=ry+rarx(j)
end
y←ryy \gets r_yy←ry
需要(3+n)n(3 + n)n(3+n)n次读写操作,而对于外积计算:
rx←x,ry←yr_x \gets x, \quad r_y \gets yrx←x,ry←y
for j=1:nj = 1:nj=1:n
ra←A(:,j)r_a \gets A(:, j)ra←A(:,j)
ra=ra+ryrx(j)r_a = r_a + r_y r_x(j)ra=ra+ryrx(j)
A(:,j)←raA(:, j) \gets r_aA(:,j)←ra
end
需要(2+2n)n(2 + 2n) n(2+2n)n次读写操作,强弱悬殊高下立判。
3.3 存取优化-Gaxpy LU分解
Gaxpy的目标是结果以向量的形式来读写。那么这里计划的目标是在第jjj步时计算出LLL矩阵和UUU矩阵的第jjj列。当j=1j=1j=1时,由于U(2:n,1)U(2:n,1)U(2:n,1)的值皆为000,比较A=LUA = LUA=LU的第一列可知:
L(2:n,j)=A(2:n,1)/A(1,1),U(1,1)=A(1,1)
L(2:n, j) = A(2:n, 1) / A(1, 1), \quad U(1, 1) = A(1, 1)
L(2:n,j)=A(2:n,1)/A(1,1),U(1,1)=A(1,1)
那么假设现在L(:,1:j−1)L(:, 1:j-1)L(:,1:j−1)和U(1:j−1,1:j−1)U(1:j-1, 1:j-1)U(1:j−1,1:j−1)已知(隐含AAA已知),那么根据以下算式可以求出U(1:j−1,j)U(1:j-1, j)U(1:j−1,j):
A(1:j−1,j)=L(1:j−1,1:j−1)⋅U(1:j−1,j)
A(1:j-1, j) = L(1:j-1, 1:j-1) \cdot U(1:j-1, j)
A(1:j−1,j)=L(1:j−1,1:j−1)⋅U(1:j−1,j)
当U(1:j−1,j)U(1:j-1, j)U(1:j−1,j)已求出之后,根据:
A(j:n,j)=L(j:n,1:j)⋅U(1:j,j)
A(j:n, j) = L(j:n, 1:j) \cdot U(1:j, j)
A(j:n,j)=L(j:n,1:j)⋅U(1:j,j)
可以求出U(j,j)U(j, j)U(j,j)和L(j+1:n,j)L(j+1:n, j)L(j+1:n,j)。为了缩减Gaxpy LU分解所需要的空间,可以添加nnn维的辅助向量vvv:
v(j:n)=A(j:n,j)−L(j:n,1:j−1)⋅U(1:j−1,j)
v(j:n) = A(j:n, j) - L(j:n, 1:j-1) \cdot U(1:j-1, j)
v(j:n)=A(j:n,j)−L(j:n,1:j−1)⋅U(1:j−1,j)
于是L(j+1:n,j)=v(j+1:n)/v(j)L(j+1:n, j) = v(j+1:n) / v(j)L(j+1:n,j)=v(j+1:n)/v(j),U(j,j)=v(j)U(j, j) = v(j)U(j,j)=v(j)。算法伪代码如下:
for j=1:nj = 1:nj=1:n
if j=1j = 1j=1
v=A(:,1)v = A(:, 1)v=A(:,1)
else
a~=A(:,j)\tilde{a} = A(:, j)a~=A(:,j)
Solve L(1:j−1,1:j−1)⋅z=a~(1:j−1)L(1:j-1, 1:j-1) \cdot z = \tilde{a}(1:j-1)L(1:j−1,1:j−1)⋅z=a~(1:j−1) for z∈Rj−1z \in \mathbb{R}^{j-1}z∈Rj−1
U(1:j−1,j)=zU(1:j-1, j) = zU(1:j−1,j)=z
end
U(j,j)=v(j)U(j, j) = v(j)U(j,j)=v(j)
L(j+1:n,j)=v(j+1:n)/v(j)L(j+1:n, j) = v(j+1:n) / v(j)L(j+1:n,j)=v(j+1:n)/v(j)
end
下面是C语言代码:
void gaxpyLU_decomposition(double* A, double* v, size_t n)
{
size_t i, j, k;
v[0] = A[0];
for (i=1; i<n; i++)
{
v[i] = A[i * n];
A[i * n] = A[i * n] / v[0];
}
for (i=1; i<n; i++)
{
for (j=1; j<i; j++)
for (k=0; k<j; k++)
A[j * n + i] -= A[j * n + k] * A[k * n + i];
for (j=i; j<n; j++)
{
v[j] = A[j * n + i];
for (k=0; k<i; k++)
v[j] -= A[j * n + k] * A[k * n + i];
}
A[i * n + i] = v[i];
for (j=i+1; j<n; j++)
A[j * n + i] = v[j] / v[i];
}
}
3.4 行方向Gaxpy LU分解(本文独有)
根据内存中矩阵存储方式(按行或列),选择不同的分解方式可以减少数据存取的次数。下面推导按行方向的Gaxpy LU分解。
根据下角矩阵的性质可知:
L(i,j)=0,i,j∈N+,i<j
L(i, j) = 0, \quad i,j \in \mathbb{N^+}, \quad i<j
L(i,j)=0,i,j∈N+,i<j
U(i,j)=0,i,j∈N+,i>j U(i, j) = 0, \quad i, j \in \mathbb{N^+}, \quad i > j U(i,j)=0,i,j∈N+,i>j
计划目标是在第jjj步时计算出第jjj行的值(包括LLL和UUU),当j=1j=1j=1时可知:
L(1,1)=1,L(1,2:n)=0 ⟹ U(1,i)=A(1,i),i=1,2,…,n
L(1, 1) = 1, \quad L(1, 2:n) = 0 \ \Longrightarrow \ U(1, i) = A(1, i), \quad i = 1,2,\dots, n
L(1,1)=1,L(1,2:n)=0 ⟹ U(1,i)=A(1,i),i=1,2,…,n
现在假设L(1:j−1,1:j−1)L(1:j-1, 1:j-1)L(1:j−1,1:j−1)和U(1:j−1,1:j−1)U(1:j-1, 1:j-1)U(1:j−1,1:j−1)已知,那么通过
A(j,1:j−1)=L(j,1:j−1)⋅U(1:j−1,1:j−1)
A(j, 1:j-1) = L(j, 1:j-1) \cdot U(1:j-1, 1:j-1)
A(j,1:j−1)=L(j,1:j−1)⋅U(1:j−1,1:j−1)
可以求出L(j,1:j−1),L(j, 1:j-1),L(j,1:j−1),已知L(j,j)=1L(j, j) = 1L(j,j)=1,根据:
A(j,:)=L(j,1:j)⋅U(1:j,:)
A(j, :) = L(j, 1:j) \cdot U(1:j, :)
A(j,:)=L(j,1:j)⋅U(1:j,:)
从而求出U(j,:)U(j, :)U(j,:)。该算法不需要辅助空间,可以进行原地分解,但是在伪代码中为了清晰还是区分了LLL和UUU矩阵,伪代码如下:
A(2:n,1) /=A(1,1)A(2:n, 1) \ / = A(1, 1)A(2:n,1) /=A(1,1)
for j=2:nj=2:nj=2:n
Solve z⋅U(1:j−1,1:j−1)z \cdot U(1:j-1,1:j-1)z⋅U(1:j−1,1:j−1) for z∈Rj−1z \in \mathbb{R}^{j-1}z∈Rj−1
L(j,1:j−1)=z, L(j,j)=1L(j, 1:j-1)=z, \ L(j,j)=1L(j,1:j−1)=z, L(j,j)=1
Solve L(j,1:j)⋅w=A(j,:)L(j, 1:j) \cdot w = A(j, :)L(j,1:j)⋅w=A(j,:) for w∈Rjw \in \mathbb{R}^{j}w∈Rj
U(j,:)=wU(j, :) = wU(j,:)=w
end
C语言代码如下:
void rowbased_gaxpyLU_decomposition(double* A, size_t n)
{
size_t i, j, k;
double r;
for (i=1; i<n; i++)
{
A[i * n] /= A[0];
for (j=1; j<i; j++)
{
for (k=0; k<j; k++) A[i * n + j] -= A[i * n + k] * A[k * n + j];
A[i * n + j] /= A[j * n + j];
}
for (j=i; j<n; j++)
for (k=0; k<i; k++) A[i * n + j] -= A[i * n + k] * A[k * n +j];
}
}
4 数值稳定性和主元分解
4.1 稳定性分析
在LULULU分解后若出现数值极大的元素,会导致误差和数值不稳定,原因请参考《Matrix Computations》第4版的3.3节。下面举一个简单的例子解释:
A=[0.0001111]=[10100001][0.000110−9999]=LU(4.1.1)
A =
\begin{bmatrix}
0.0001 & 1 \\
1 & 1
\end{bmatrix}=
\begin{bmatrix}
1 & 0 \\
10000 & 1
\end{bmatrix}
\begin{bmatrix}
0.0001 & 1 \\
0 & -9999
\end{bmatrix}=LU \tag{4.1.1}
A=[0.0001111]=[11000001][0.000101−9999]=LU(4.1.1)
AAA中数值极小的主元导致了分解后特别大数值的出现,克服此难题的一个方法是进行行交换,若使用行交换矩阵PPP:
P=[0110]
P =
\begin{bmatrix}
0 & 1 \\
1 & 0
\end{bmatrix}
P=[0110]
来左乘矩阵AAA之后再进行LULULU分解:
PA=[110.00011]=[100.00011][1100.9999]=LU(4.1.2)
PA =
\begin{bmatrix}
1 & 1 \\
0.0001 & 1
\end{bmatrix}=
\begin{bmatrix}
1 & 0 \\
0.0001 & 1
\end{bmatrix}
\begin{bmatrix}
1 & 1 \\
0 & 0.9999
\end{bmatrix}=LU \tag{4.1.2}
PA=[10.000111]=[10.000101][1010.9999]=LU(4.1.2)
可以保证数值的稳定。
若要交换AAA矩阵的第iii列和第jjj列,只要交换单位矩阵III的第iii列和第jjj列得到矩阵PPP左乘AAA矩阵即可。
4.2 列主元LULULU分解
参考2.2节中的高斯消元法,为了保证数值的稳定,在第jjj步消元时,需要左乘行交换矩阵Πi\Pi_iΠi将第jjj列中从jjj到nnn位置值最大的元素交换到第jjj行,再进行高斯消元,完整的步骤如下:
Mn−1Πn−1⋯M1Π1A=U(4.2.1)
M_{n-1} \Pi_{n-1} \cdots M_1 \Pi_1 A = U \tag{4.2.1}
Mn−1Πn−1⋯M1Π1A=U(4.2.1)
接下来需要找出LLL矩阵,从最简单的关系开始:
PA=LU,P=Πn−1⋯Π1(4.2.2)
PA = LU, \quad P = \Pi_{n-1} \cdots \Pi_1 \tag{4.2.2}
PA=LU,P=Πn−1⋯Π1(4.2.2)
结合式(4.2.1)可以得出以下结果:
M~n−1M~n−2⋯M~1PA=U(4.2.3)
\tilde{M}_{n-1} \tilde{M}_{n-2} \cdots \tilde{M}_{1} P A = U \tag{4.2.3}
M~n−1M~n−2⋯M~1PA=U(4.2.3)
由于ΠiAΠi=A\Pi_i A \Pi_i= AΠiAΠi=A,显然:
M~k=(Πn−1⋯Πk+1)Mk(Πk+1⋯Πn−1)(4.2.4)
\tilde{M}_k=(\Pi_{n-1} \cdots \Pi_{k+1})M_k(\Pi_{k+1} \cdots \Pi_{n-1}) \tag{4.2.4}
M~k=(Πn−1⋯Πk+1)Mk(Πk+1⋯Πn−1)(4.2.4)
由线性代数中所学的知识可知,左乘矩阵是进行行变换,而右乘矩阵则是进行列变换。直观地说,交换单位矩阵III的第iii行和第jjj行得到行交换矩阵Π\PiΠ,用Π\PiΠ左乘任意矩阵的结果便是交换该矩阵的第iii行和第jjj行。
在进行LULULU分解时为保持数值稳定,会在每一步的高斯消元时进行行交换,并保存行交换的信息在矩阵PPP中。最终分解的公式变为:
PA=LU(4.2.5)
PA = LU \tag{4.2.5}
PA=LU(4.2.5)
以下算法伪代码完成了4.2.54.2.54.2.5式的计算:
P=IP = IP=I
for k=1:n−1k=1:n-1k=1:n−1
Find μ\muμ where ∣A(μ,k)∣=∥A(k:n,k)∥∞|A(\mu, k)| = \| A(k:n, k) \|_{\infty}∣A(μ,k)∣=∥A(k:n,k)∥∞
Exchange(A(μ,:),A(k,:))Exchange(A(\mu, :), A(k, :))Exchange(A(μ,:),A(k,:))
Exchange(P(μ,:),P(k,:))Exchange(P(\mu, :), P(k, :))Exchange(P(μ,:),P(k,:))
if A(k,k)≠0A(k, k) \neq 0A(k,k)=0
ρ=k+1:n\rho = k+1:nρ=k+1:n
A(ρ,k)=A(ρ,k)/A(k,k)A(\rho, k) = A(\rho, k)/A(k, k)A(ρ,k)=A(ρ,k)/A(k,k)
A(ρ,ρ)=A(ρ,ρ)−A(ρ,k)A(k,ρ)A(\rho, \rho) = A(\rho, \rho) - A(\rho, k)A(k, \rho)A(ρ,ρ)=A(ρ,ρ)−A(ρ,k)A(k,ρ)
end
end