工程计算——奇异值分解法&迭代法

本次整理主要参考工程数学计算方法(第二版)_张诚坚_何南忠_覃婷婷_高教社MATLAB数值计算实战_占海明_机械工业出版社两本书籍。

由于迭代法具有:

  • 存储单元少
  • 程序简单
  • 计算速度快
    的优点,因此在实际大规模科学工程计算中迭代法用的更多。

例题引入

给定线性方程组
( 1 2 3 4 5 − 2 3 4 5 6 − 3 − 4 5 6 7 − 4 − 5 − 6 7 8 − 5 − 6 − 7 − 8 9 ) ( x 1 x 2 x 3 x 4 x 5 ) = ( 55 66 63 36 − 25 ) \begin{pmatrix} 1 &2&3&4&5\\ -2&3&4&5&6\\ -3&-4&5&6&7\\ -4&-5&-6&7&8\\ -5&-6&-7&-8&9 \end{pmatrix} \begin{pmatrix} x_1\\x_2\\x_3\\x_4\\x_5 \end{pmatrix} = \begin{pmatrix} 55\\66\\63\\36\\-25 \end{pmatrix} 1234523456345674567856789x1x2x3x4x5=5566633625
试以 ∥ X ( k + 1 ) − X ( k ) ∥ \begin{Vmatrix}X^{(k+1)}-X^{(k)}\end{Vmatrix} X(k+1)X(k) < < < 1 0 − 5 10^{-5} 105作为终止准则,分别利用

  • Jacobi迭代法
  • Gauss-Seidel迭代法
  • 具有最佳松弛因子的JOR迭代法
  • SOR迭代法

求解该方程组,并比较这些方法的计算时间。

以上呈现的是书本上的原始题目,但通过求解该线性方程组的条件数 cond ( A ) \text{cond} (A) cond(A)可以得到 cond ( A ) ≫ 1 \text{cond} (A)\gg 1 cond(A)1,也即:该线性方程组是病态的,这是不争的事实。因此,如果真的按照题目要求解题,那么求出的结果一定是发散的,这是没有太大的实际意义的,也更无必要通过这个例子去比较这些方法的计算时间了。对于求解病态的线性方程组我们可以使用——奇异值分解法(SVD)
关于条件数的求解可见工程计算——求解线性方程组
我们先来介绍SVD分解法:

SVD分解法

对于 n n n阶实矩阵 A A A,存在 n n n阶正交阵 U 、 V U、V UV和对角阵 S S S,使得 A = U S V T A=USV^{T} A=USVT
结合:
A = U S V T ⇒ A − 1 = V S − 1 U T A x = b A=USV^{T}\Rightarrow A^{-1} = VS^{-1}U^{T}\\Ax=b A=USVTA1=VS1UTAx=b
得到:
x = A − 1 b = V S − 1 U T b              ( 1 ) x = A^{-1}b = VS^{-1}U^{T}b\ \ \ \ \ \ \ \ \ \ \ \ (1) x=A1b=VS1UTb            (1)
假设:
U = [ u 1 , u 2 , u 3 , . . . , u n ] V = [ v 1 , v 2 , v 3 , . . . , v n ] S = d i a g ( σ 1 , σ 2 , . . . , σ n ) U = [u_1,u_2,u_3,...,u_n]\\V=[v_1,v_2,v_3,...,v_n]\\S = diag(\sigma_1,\sigma_2,...,\sigma_n) U=[u1,u2,u3,...,un]V=[v1,v2,v3,...,vn]S=diag(σ1,σ2,...,σn)
结合 ( 1 ) (1) (1)得到:
x = ∑ i = 1 n u i T b σ i v i x=\sum\limits_{i=1}^n\frac{u_i^{T}b}{\sigma_i}v_i x=i=1nσiuiTbvi
因为,当 σ i \sigma_i σi接近于 0 0 0时,一方面会对计算 x x x产生较大的误差;另一方面,由 A = U S V T = ∑ i = 1 n σ i u i v i T A = USV^{T}=\sum\limits_{i=1}^{n}\sigma_iu_iv_i^{T} A=USVT=i=1nσiuiviT可知,当 σ i \sigma_i σi 接近于0时,后面的项对矩阵 A A A的贡献率非常小。取一正的阈值(假设为 ϵ \epsilon ϵ)当 ∣ σ i ∣ < ϵ |\sigma_i|<\epsilon σi<ϵ 时,其相应的项就不再计算,即 A ≈ ∑ ∣ σ i ∣ ≥ ϵ σ i u i v i T A\approx \sum\limits_{|\sigma_i|\ge\epsilon}\sigma_iu_iv_i^{T} AσiϵσiuiviT
由此我们可以进一步得到: x ≈ ∑ ∣ σ i ∣ ≥ ϵ u i T b σ i v i x \approx \sum\limits_{|\sigma_i|\ge \epsilon }\frac{u_i^{T}b}{\sigma_i}v_i xσiϵσiuiTbvi
根据上式编写MATLAB程序:

function varargout = svd_solve(A,b,ep)
%SVD_SOLVE 奇异值分解法求解病态方程组
n = length(b);                  % 线性方程组的阶数
if nargin < 3 
    ep = 1e-10;                 % 默认精度
end
[U,S,V] = svd(A);               %奇异值分解
%MATLAB中提供了svd函数实现矩阵的奇异值分解,该函数的调用格式即为
%[U,S,V] = svd(A)
%其中U、V为n阶正交阵,V为对角阵
sigma = diag(S);                %提取对角线元素
x = zeros(n,1);                 %预置零向量存储迭代解
k = 1;                          %计数器
while 1
    if abs(sigma(k))<ep         %若不满足条件
        A = A-sigma(k)*U(:,k)*V(:,k)';%从A中剔除
    else 
        x = x+(U(:,k)'*b)/sigma(k)*V(:,k);% 累加
    end
    k = k+1;                    %计数器累加
    if k > n
        break                   % 判断次数大于方程组的阶数退出循环
    end
end
[varargout{1:2}] = deal(x,...
    A);

对于该题求解:
主函数可为:

A = [1 2 3 4 5 ;-2 3 4 5 6;-3 -4 5 6 7;-4 -5 -6 7 8;-5 -6 -7 -8 9];
b = [55;66;63;36;-25];
n = length(b);
x0 = zeros(n,1);
ep = 1e-5;
maxiter = 100;
[x,exitflag,iter,xs,err]= jacobic(A,b,x0,ep,maxiter);
if exitflag == 1
    disp("该线性方程组迭代收敛,其对应的解为:");
    x
    disp("迭代次数为:");
    iter
    disp("迭代序列为:");
    xs
    disp("近似相对误差为:")
else
    disp("该线性方程组迭代发散,改为使用奇异值分解法分解求解。");
    disp("奇异值分解法下:")
    [x,A]= svd_solve(A,b,ep);
    disp("利用奇异值分解法得到的该线性方程组的解为:");
    x
    disp("利用奇异值分解法得到的该线性方程组对应的近似系数矩阵为:");
    A
end

结果:

该线性方程组迭代发散,改为使用奇异值分解法分解求解。
奇异值分解法下:
利用奇异值分解法得到的该线性方程组的解为:

x =

    1.0000
    2.0000
    3.0000
    4.0000
    5.0000

利用奇异值分解法得到的该线性方程组对应的近似系数矩阵为:

A =

     1     2     3     4     5
    -2     3     4     5     6
    -3    -4     5     6     7
    -4    -5    -6     7     8
    -5    -6    -7    -8     9

迭代法

接下来介绍迭代法(iteration method)
迭代法核心思想

Jacobi迭代法

B J = − D − 1 ( L + U ) F J = D − 1 b B_{J}=-D^{-1}(L+U)\\F_{J}=D^{-1}b BJ=D1(L+U)FJ=D1b

function varargout = jacobic(A,b,x0,ep,maxiter)
%JACOBI 分量形式Jacobi迭代法求解线性方程组
n = length(b);                %线性方程组的阶数
if nargin < 5
    maxiter = 500;            %默认最大迭代次数
end
if nargin < 4
    ep = 1e-8;                %默认精度
end
if nargin < 3 || isempty(x0)
    x0 = zeros(n,1);           %默认迭代初值
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%以上为初始化过程相当于:
% maxiter = 500;
% ep = 1e-8;
% x0 = zeros(n,1);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
x0 = x0(:);                    %将初始迭代解向量变成列向量
x = zeros(n,1);                %预置零向量存储迭代解
iter = 1 ;                      %迭代次数初始值
exitflag = 1;                  %迭代收敛标志: 1-收敛;0-发散
xs(iter,:) = x0;               %存储每次迭代的值
err(iter,1) = nan;             %初始误差置为nan
while exitflag
    for k=1:n                  %Jacobi迭代
        x(k) = (b(k)-A(k,[1:k-1,k+1:n])*x0([1:k-1,k+1:n]))/A(k,k);
    end
    xs(iter+1,:) = x;          %将迭代解存储在xs矩阵中
    err(iter+1,1) = norm(x-x0);%计算误差并存储
    if err(iter+1,1)<=ep       %前后迭代值误差在允许范围内
        break;                 %退出循环
    end
    x0 = x;                    %更新迭代初始值
    iter = iter + 1;           %迭代次数累加
    if iter > maxiter  %若迭代次数大于最大迭代次数,则认为迭代发散,即根不可靠
        exitflag = 0;          %置exitflag值为0,即发散
    end
end
[varargout{1:5}] = deal(x,...  %第1个输出参数为线性方程组的解
    exitflag,...               %第2个输出参数为迭代收敛的标志
    iter,...                   %第3个输出参数为迭代次数
    xs,...                     %第4个输出参数为迭代序列
    err(2:end));               %第5个输出参数为近似相对误差

Gauss-Seidel迭代法

B G S = − ( D + L ) − 1 U F G S = ( D + L ) − 1 b B_{GS} = -(D+L)^{-1}U\\F_{GS} = (D+L)^{-1}b BGS=(D+L)1UFGS=(D+L)1b

function varargout = seidelc(A,b,x0,ep,maxiter)
%SEIDEL 分量形式Gauss-Seidel迭代法求解线性方程组
n = length(b);                %线性方程组的阶数
if nargin < 5
    maxiter = 500;            %默认最大迭代次数
end
if nargin < 4
    ep = 1e-8;                %默认精度
end
if nargin < 3 || isempty(x0)
    x0 = zeros(n,1);           %默认迭代初值
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%以上为初始化过程相当于:
% maxiter = 500;
% ep = 1e-8;
% x0 = zeros(n,1);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
x0 = x0(:);                    %将初始迭代解向量变成列向量
x = zeros(n,1);                %预置零向量存储迭代解
iter = 1 ;                      %迭代次数初始值
exitflag = 1;                  %迭代收敛标志: 1-收敛;0-发散
xs(iter,:) = x0;               %存储每次迭代的值
err(iter,1) = nan;             %初始误差置为nan
while exitflag
    for k=1:n                  %Gauss-Seidel迭代
        x(k) = (b(k)-A(k,[1:k-1,k+1:n])*x1([1:k-1,k+1:n]))/A(k,k);
        x1(k) = x(k);
    end
    xs(iter+1,:) = x;          %将迭代解存储在xs矩阵中
    err(iter+1,1) = norm(x-x0);%计算误差并存储
    if err(iter+1,1)<=ep       %前后迭代值误差在允许范围内
        break                  %退出循环
    end
    x0 = x;                    %更新迭代初始值
    iter = iter + 1;           %迭代次数累加
    if iter > maxiter  %若迭代次数大于最大迭代次数,则认为迭代发散,即根不可靠
        exitflag = 0;          %置exitflag值为0,即发散
    end
end
[varargout{1:5}] = deal(x,...  %第1个输出参数为线性方程组的解
    exitflag,...               %第2个输出参数为迭代收敛的标志
    iter,...                   %第3个输出参数为迭代次数
    xs,...                     %第4个输出参数为迭代序列
    err(2:end));               %第5个输出参数为近似相对误差

SOR迭代法

B S O R = ( D + ω L ) − 1 [ ( 1 − ω ) D − ω U ] F S O R = ω ( D + ω L ) − 1 b B_{SOR} = (D+\omega L)^{-1}[(1-\omega )D-\omega U]\\F_{SOR} = \omega (D + \omega L)^{-1}b BSOR=(D+ωL)1[(1ω)DωU]FSOR=ω(D+ωL)1b

function varargout = sorc(A,b,w,x0,ep,maxiter)
%SORC 分量形式SOR法求解线性方程组
n = length(b);                %线性方程组的阶数
if nargin < 5
    maxiter = 500;            %默认最大迭代次数
end
if nargin < 4
    ep = 1e-8;                %默认精度
end
if nargin < 3 || isempty(x0)
    x0 = zeros(n,1);           %默认迭代初值
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%以上为初始化过程相当于:
% maxiter = 500;
% ep = 1e-8;
% x0 = zeros(n,1);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
x0 = x0(:);                    %将初始迭代解向量变成列向量
x = zeros(n,1);                %预置零向量存储迭代解
iter = 1 ;                      %迭代次数初始值
exitflag = 1;                  %迭代收敛标志: 1-收敛;0-发散
xs(iter,:) = x0;               %存储每次迭代的值
err(iter,1) = nan;             %初始误差置为nan
while exitflag
    x1 = x0(:);
    for k=1:n                  %Gauss-Seidel迭代
        x(k) = (1-w)*x0(k)+...
            w*(b(k)-A(k,[1:k-1,k+1:n])*x1([1:k-1,k+1]))/A(k,k);
        x1(k) = x(k);
    end
    xs(iter+1,:) = x;          %将迭代解存储在xs矩阵中
    err(iter+1,1) = norm(x-x0);%计算误差并存储
    if err(iter+1,1)<=ep       %前后迭代值误差在允许范围内
        break                  %退出循环
    end
    x0 = x;                    %更新迭代初始值
    iter = iter + 1;           %迭代次数累加
    if iter > maxiter  %若迭代次数大于最大迭代次数,则认为迭代发散,即根不可靠
        exitflag = 0;          %置exitflag值为0,即发散
    end
end
[varargout{1:5}] = deal(x,...  %第1个输出参数为线性方程组的解
    exitflag,...               %第2个输出参数为迭代收敛的标志
    iter,...                   %第3个输出参数为迭代次数
    xs,...                     %第4个输出参数为迭代序列
    err(2:end));               %第5个输出参数为近似相对误差
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 奇异值分解(Singular Value Decomposition,SVD)是一种非常有用的矩阵分解,可以将一个矩阵分解为三个矩阵的乘积,分别是U、Σ和V的转置。 具体实现奇异值分解的算有很多种,其中一种较为常用的是基于Jacobi迭代的算。下面是一个简单的C语言实现奇异值分解的示例代码: ```c #include <stdio.h> #include <math.h> // 定义矩阵的行数和列数 #define M 3 #define N 3 // 执行奇异值分解的函数 void svd_decomposition(float matrix[M][N], float U[M][M], float sigma[M][N], float V[N][N]) { // 先对矩阵进行转置 float matrix_t[N][M]; for(int i=0; i<N; i++){ for(int j=0; j<M; j++){ matrix_t[i][j] = matrix[j][i]; } } // 计算矩阵的乘积 matrix * matrix_t,并保存结果在 sigma 矩阵中 float product[M][N]; for(int i=0; i<M; i++){ for(int j=0; j<N; j++){ product[i][j] = 0; for(int k=0; k<N; k++){ product[i][j] += matrix[i][k] * matrix_t[k][j]; } } } // 对 product 矩阵进行奇异值分解,得到 U、sigma 和 V 的转置 // 这里省略了具体的奇异值分解 // 打印结果 printf("U 矩阵:\n"); for(int i=0; i<M; i++){ for(int j=0; j<M; j++){ printf("%.2f ", U[i][j]); } printf("\n"); } printf("sigma 矩阵:\n"); for(int i=0; i<M; i++){ for(int j=0; j<N; j++){ printf("%.2f ", sigma[i][j]); } printf("\n"); } printf("V 矩阵:\n"); for(int i=0; i<N; i++){ for(int j=0; j<N; j++){ printf("%.2f ", V[i][j]); } printf("\n"); } } int main() { // 示例矩阵 float matrix[M][N] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; // 定义 U、sigma 和 V 矩阵 float U[M][M], sigma[M][N], V[N][N]; // 执行奇异值分解 svd_decomposition(matrix, U, sigma, V); return 0; } ``` 以上示例代码实现了奇异值分解的关键步骤,包括矩阵的转置、矩阵乘奇异值分解。需要注意的是,这里只是简单地演示了奇异值分解的实现思路,实际应用中可能需要根据具体的需求优化代码的性能和稳定性。 ### 回答2: 奇异值分解(Singular Value Decomposition,简称SVD)是一种常用的矩阵分解,它可以将一个复杂的矩阵分解为三个简单的矩阵相乘的形式。SVD分解有很多应用领域,比如降维、推荐系统、图像处理等。 要用C语言实现奇异值分解,首先需要理解SVD的原理和数学公式。以下是实现步骤的概括: 1. 读取需要分解的矩阵,可以使用二维数组来表示矩阵。 2. 对矩阵进行奇异值分解,使用迭代或其他数值计算计算矩阵的奇异值、左奇异向量和右奇异向量。 3. 根据计算得到的奇异值和奇异向量,将原始矩阵分解为三个矩阵相乘的形式。 4. 可以根据需要选择保留的奇异值个数,进而实现矩阵降维。 5. 具体应用时,可以根据需要对矩阵进行重构、推荐算等。 在C语言中实现SVD需要适当的数学库和算支持。可以使用已有的数学库,如LAPACK(Linear Algebra PACKage)等。这些库提供了一些矩阵运算函数和数值计算,可以帮助我们完成SVD计算过程。 整体而言,C语言实现奇异值分解需要一定的数学背景和编程能力,需要了解奇异值分解的原理和数学公式,并使用合适的数学库和算实现计算过程。 ### 回答3: 奇异值分解(Singular Value Decomposition,SVD) 是一种重要的矩阵分解,可以将一个矩阵分解为三个简化的矩阵之积,其中包括一个左奇异矩阵、一个奇异值矩阵和一个右奇异矩阵。SVD 在很多应用中都有广泛的应用,比如推荐系统、图像处理和自然语言处理等领域。 要在 C 语言中实现奇异值分解,可以按照以下步骤进行: 1. 导入所需的库,比如数值计算库和线性代数库。 2. 定义需要分解的矩阵,并将其读入内存。 3. 利用数值计算库提供的函数,计算矩阵的奇异值分解。这些函数通常包括计算特征值和特征向量以及矩阵相乘的功能。 4. 将计算得到的奇异值矩阵和左右奇异矩阵保存到内存中,以备后续使用。 5. 进行进一步的数据处理和分析。比如根据需要,选择保留较大奇异值,并相应地截断左奇异矩阵和右奇异矩阵。 最后,需要考虑的是,为了提高计算效率,还可以将 C 语言中的循环或者递归等常用技巧应用于奇异值分解的实现过程中。 总之,奇异值分解是一种重要的数学工具,在 C 语言中实现奇异值分解可以通过调用相关的数值计算库来完成。这样就能得到矩阵的奇异值、左奇异矩阵和右奇异矩阵,为进一步的数据分析和处理提供了基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值