赞
踩
熟悉求解线性方程组的有关理论和方法;会编写Gauss消去法、LU分解法、Jacobi迭代法、Gauss-Seidel迭代法、超松弛(SOR)迭代法及共轭梯度法的程序;通过实际计算,进一步了解各种方法的优缺点,选择合适的数值方法。
根据线性方程组公式(1)可得,系数矩阵
表1 实验1的结果
方法 | 计算误差( | |
---|---|---|
Gauss消去法 | 0 | |
LU分解法 | 0 |
根据公式(2),建立线性方程组标准方程
Jacobi迭代法的迭代公式为
Gauss-Seidel迭代法的迭代公式为
表2 问题2的Gauss-Seidel迭代法计算结果
迭代次数 | 允许误差 | 实际误差 |
---|---|---|
598 | 1E-4 | 9.4250E-5 |
以上2种方法求解该问题时,发现Jacobi迭代法不收敛,Gauss-Seidel迭代法收敛,在允许精度为1E-4的情况下,迭代次数为598次,可见Gauss-Seidel迭代法在求解该线性方程组时,收敛速度不佳。
在Gauss-Seidel迭代法的基础上,为获得更快的收敛效果,在修正量前乘以一个修正系数
共轭梯度法与之前的迭代法不同,它属于不定常迭代法,用于求解对称正对线性方程组,其本质上是一种变分方法。
共轭梯度法的迭代方程为
表3 问题1,2,3的共轭迭代法计算结果
问题序号 | 迭代次数 | 计算误差 | 允许误差 |
---|---|---|---|
1 | 2 | 7.9422E-5 | |
2 | 11 | 8.8953E-5 | 1E-4 |
3 | 4 | 1.9327E-5 |
由表3所得,共轭迭代法在问题(1)(2)(3)中的线性方程组求解上具有较好的收敛效果,尤其对问题(2)中的大型稀疏矩阵上,其收敛效果明显高于Gauss-Seidel迭代法。
线性方程组的直接求法,Gauss消去法和LU分解法在维度较小的非病态问题上,具有良好的效果。这两种算法理论上的复杂度为
线性方程组的迭代求法,Jacobi迭代法、Gauss-Seidel迭代法、超松弛(SOR)迭代法和共轭梯度法针对大型稀疏矩阵(如问题(2)的84阶线性方程组)表现出不同的效果。Jacobi迭代法的不收敛、Gauss-Seidel迭代法的极大的迭代次数以及共轭梯度法较优的计算结果,表明了这些迭代法不同的计算特性。SOR迭代法的松弛因子
基于以上实验结果,我们得到以下结论:
1. 直接求法在一般问题,维数较小下(系数矩阵的秩小于100),非常实用。
2. 迭代法的收敛性在不同问题反映出的现象差异很大。
3. 采用Jacobi迭代法、Gauss-Seidel迭代法求解大型稀疏矩阵,收敛性难以保证,而共轭迭代法收敛极好。
4. 超松弛迭代法的收敛性受松弛因子影响,并可能存在最优的松弛因子,使其收敛速度最快。
所有程序都在MATLAB(R2012b)平台上实现。
源代码 |
---|
function [rankA, rankB, N, X] = CNumbericGauss(A, b)
% 调用格式: [rankA, rankB, N, X] = CNumbericGauss(A, b)
%
% 作者: 王瑞
% 时间: 2015.10.27 17:12 - 20.12
% 版本: Version 1.0
%
% 任务: 高斯消去法求解线性方程组的解 Ax = b, 换主元的高斯消去法, 取最邻近非 0 首项
%
% 输入: A = 系数矩阵, 方阵
% b = 常系数向量, 行向量
%
% 输出: rankA = 系数矩阵 A 的秩
% rankB = 增广矩阵 B 的秩, 其中 B = [A|b]
% N = 方程组未知量个数
% X = 方程组的解向量
%
B = [A b];
rankA = rank(A);
rankB = rank(B);
N = length(b);
X = zeros(size(b));
if rankA ~= rankB
disp('None: 矩阵 A 的秩不等于 [A b] 的秩, 无解!');
return ;
end
if rankA < N
disp(['Waring: 矩阵的秩小于' num2str(N) ',存在无穷多解。']);
return;
end
if N == 1
disp('别闹,用手算的。');
return;
end
%% Gauss 消去法
if rankA == N
disp(['Good: 矩阵的秩为' num2str(N) ',存在唯一解。']);
for i = 1 : N
if abs(B(i, i)) <= eps
B(i, i) = 0;
[flag, j] = max(abs(B(i:N, i)));
j = j + i -1;
temp = B(i, :);
B(i, :) = B(j, :);
B(j, :) = temp;
if abs(flag) <= eps % 无非 0 首项判定
disp('这里出现了一个 Error,首项极小!');
return;
end
end
B(i, :) = B(i, :)/B(i, i);
for j = i+1 : N % Gauss 消去
B(j, :) = B(j, :) - B(j, i)*B(i, :);
end
end
for i = N : -1 : 1 % Gauss 回代求解
if i == N
X(i) = B(i, N+1)/B(i, N);
else
X(i) = (B(i, N+1) - B(i, i+1:N)*X(i+1:N)) / B(i, i);
end
end
end
end
源代码 |
---|
function [rankA, rankB, N, X] = CNumbericLU(A, b)
% 调用格式: [rankA, rankB, N, X] = CNumbericLU(A, b)
%
% 作者: 王瑞
% 时间: 2015.10.27 20:14 - 21:37
% 版本: Version 1.0
%
% 任务: 选主元的三角分解法求解线性方程组的解 Ax = b, LU 法
%
% 输入: A = 系数矩阵, 方阵
% b = 常系数向量, 行向量
%
% 输出: rankA = 系数矩阵 A 的秩
% rankB = 增广矩阵 B 的秩, 其中 B = [A|b]
% N = 方程组未知量个数
% X = 方程组的解向量
%
B = [A b];
rankA = rank(A);
rankB = rank(B);
N = length(b);
X = zeros(size(b));
if rankA ~= rankB
disp('None: 矩阵 A 的秩不等于 [A b] 的秩, 无解!');
return ;
elseif rankA ~= N
disp(['Waring: 矩阵的秩小于' num2str(N) ',存在无穷多解。']);
return;
end
if rankA == 1
disp('别闹,用手算的。');
return;
end
disp(['Good: 矩阵的秩为' num2str(N) ',存在唯一解。']);
%% 求 L U P [核心]
[L, U, P] = lu(A);
b = P*b;
%% 求 y
y = zeros(size(b));
for i = 1 : N
if i == 1
y(1) = b(1);
else
y(i) = b(i) - L(i, 1:i-1)*y(1:i-1);
end
end
%% 求 X
for i = N : -1 : 1
if i == N
X(i) = y(i) / U(i, i);
else
X(i) = (y(i) - U(i, i+1:N)*X(i+1:N)) / U(i, i);
end
end
end
源代码 |
---|
function [rankA, rankB, N, X, ite, tol] = CNumbericJacobiIteration(A, b, TOL, ITE, initX)
% 调用格式: [rankA, rankB, N, X, ite, tol] = CNumbericJacobiIteration(A, b, initX, TOL, ITE, initX)
% [rankA, rankB, N, X, ite, tol] = CNumbericJacobiIteration(A, b, initX, TOL, ITE)
%
% 作者: 王瑞
% 时间: 2015.10.27 21:40; 2015.10.28 13:37 - 15:00
% 版本: Version 1.0
%
% 任务: Jacobi迭代法求解线性方程组的解 Ax = b
% 构建 x(k+1) = Bx(k) + f
% B = E - D^-1*A = D^-1*(L+U), f = D^-1*b;
%
% 输入: A = 系数矩阵, 方阵
% b = 常系数向量, 行向量
% initX = 初始解
% ITE = 迭代次数上限
% TOL = 解的精度(范数)
%
% 输出: rankA = 系数矩阵 A 的秩
% rankB = 增广矩阵 B 的秩, 其中 B = [A|b]
% N = 方程组未知量个数
% X = 方程组的解向量
% ite = 求解的迭代次数
% tol = 实际误差
%
if nargin == 5
X = initX;
elseif nargin == 4
X = zeros(size(b));
end
B = [A, b];
rankA = rank(A);
rankB = rank(B);
N = length(b);
ite = 0;
tol = inf;
if rankA ~= rankB
disp('None: 矩阵 A 的秩不等于 [A b] 的秩, 无解!');
return ;
elseif rankA ~= N
disp(['Waring: 矩阵的秩小于' num2str(N) '[' num2str(rankA) ']'',存在无穷多解。']);
end
if rankA == 1
disp('别闹,用手算的。');
return;
end
if rankA == N
disp(['Good: 矩阵的秩为' num2str(N) ',存在唯一解。']);
end
%% B0, f
D = diag(A);
L = (-1) * tril(A, -1);
U = (-1) * triu(A, 1);
invD = diag(1./D);
B0 = invD*(L+U);
f = invD*b;
R = max(abs(eig(B0))); % 谱半径,收敛判定
if R >= 1
disp(['Error: 谱半径 R = ' num2str(R) ',大于等于 1,算法不收敛。']);
return;
end
%% Jacobi 迭代
while ite < ITE
ite = ite + 1;
X = B0*X + f;
tol = norm(b - A*X) / norm(b);
if tol < TOL
disp('Excatly: 求得解。');
break;
end
end
if ite > ITE
disp(['Message: 在 ' num2str(ITE) ' 次迭代过程中,无法求得解,'...
'建议增大总的迭代次数 或者 分析算法的收敛性。']);
end
end
源代码 |
---|
function [rankA, rankB, N, X, ite, tol] = CNumbericGaussSeidelIteration(A, b, TOL, ITE, initX)
% 调用格式: [rankA, rankB, N, X, ite, tol] = CNumbericGaussSeidelIteration(A, b, TOL, ITE, initX)
% [rankA, rankB, N, X, ite, tol] = CNumbericGaussSeidelIteration(A, b, TOL, ITE)
%
% 作者: 王瑞
% 时间: 2015.10.28 14:15 - 15:00
% 版本: Version 1.0
%
% 任务: Gauss-Seidel迭代法求解线性方程组的解 Ax = b
% 构建 x(k+1) = Gx(k) + f
% G = (D - L)^-1*U, f = (D - L)^-1*b
%
% 输入: A = 系数矩阵, 方阵
% b = 常系数向量, 行向量
% initX = 初始解
% ITE = 迭代次数上限
% TOL = 解的精度(范数)
%
% 输出: rankA = 系数矩阵 A 的秩
% rankB = 增广矩阵 B 的秩, 其中 B = [A|b]
% N = 方程组未知量个数
% X = 方程组的解向量
% ite = 求解的迭代次数
% tol = 实际误差
%
if nargin == 5
X = initX;
elseif nargin == 4
X = zeros(size(b));
end
B = [A, b];
rankA = rank(A);
rankB = rank(B);
N = length(b);
ite = 0;
tol = inf;
if rankA ~= rankB
disp('None: 矩阵 A 的秩不等于 [A b] 的秩, 无解!');
return ;
elseif rankA ~= N
disp(['Waring: 矩阵的秩小于' num2str(N) '[' num2str(rankA) ']'',存在无穷多解。']);
end
if rankA == 1
disp('别闹,用手算的。');
return;
end
if rankA == N
disp(['Good: 矩阵的秩为' num2str(N) ',存在唯一解。']);
end
%% G, f
D = diag(diag(A));
L = (-1) * tril(A, -1);
U = (-1) * triu(A, 1);
invDL = eye(length(b))/(D - L);
G = invDL*U;
f = invDL*b;
R = max(abs(eig(G))); % 谱半径,收敛判定
if R >= 1
disp(['Error: 谱半径 R = ' num2str(R) ',大于等于 1,算法不收敛。']);
return;
end
%% Gauss-Seidel 迭代
while ite < ITE
ite = ite + 1;
Xk = X;
X = G*Xk + f;
tol = norm(b - A*X) / norm(b);
if tol < TOL
disp('Excatly: 求得解。');
break;
end
end
if ite > ITE
disp(['Message: 在 ' num2str(ITE) ' 次迭代过程中,无法求得解,'...
'建议增大总的迭代次数 或者 分析算法的收敛性。']);
end
end
源代码 |
---|
function [rankA, rankB, N, X, ite, tol] = CNumbericSORIteration(A, b, Omega, TOL, ITE, initX)
% 调用格式: [rankA, rankB, N, X, ite, tol] = CNumbericSORIteration(A, b, Omega, TOL, ITE, initX)
% [rankA, rankB, N, X, ite, tol] = CNumbericSORIteration(A, b, Omega, TOL, ITE)
%
% 作者: 王瑞
% 时间: 2015.10.28 16:00 - 16:38
% 版本: Version 1.0
%
% 任务: 超松弛(SOR)迭代法求解线性方程组的解 Ax = b
% 构建 x(k+1) = Gx(k) + f
% G = (D-Omege*L)^-1*[(1-Omega)*D+Omega*U],
% f = Omega*(D-Omega*L)^-1*b
%
% 输入: A = 系数矩阵, 方阵
% b = 常系数向量, 行向量
% initX = 初始解
% Omege = 松弛因子 (0, 2)
% ITE = 迭代次数上限
% TOL = 解的精度(范数)
%
% 输出: rankA = 系数矩阵 A 的秩
% rankB = 增广矩阵 B 的秩, 其中 B = [A|b]
% N = 方程组未知量个数
% X = 方程组的解向量
% ite = 求解的迭代次数
% tol = 实际误差
%
if nargin == 5
X = zeros(size(b));
elseif nargin == 6
X = initX;
end
B = [A, b];
rankA = rank(A);
rankB = rank(B);
N = length(b);
ite = 0;
tol = inf;
if rankA ~= rankB
disp('None: 矩阵 A 的秩不等于 [A b] 的秩, 无解!');
return ;
elseif rankA ~= N
disp(['Waring: 矩阵的秩小于' num2str(N) '[' num2str(rankA) ']'',存在无穷多解。']);
end
if rankA == 1
disp('别闹,用手算的。');
return;
end
if rankA == N
disp(['Good: 矩阵的秩为' num2str(N) ',存在唯一解。']);
end
%% D, L, U
D = diag(diag(A));
L = (-1) * tril(A, -1);
U = (-1) * triu(A, 1);
%% G, f
G = (D - Omega*L)\((1-Omega)*D + Omega*U);
f = (D - Omega*L)\(Omega*b);
R = max(abs(eig(G))); % 谱半径,收敛判定
if R >= 1
disp(['Error: 谱半径 R = ' num2str(R) ',大于等于 1,算法不收敛。']);
return;
end
disp(['R = ' num2str(R)]);
%% SOR 迭代
while ite < ITE
ite = ite + 1;
X = G*X + f;
tol = norm(b - A*X) / norm(b);
if tol < TOL
disp('Excatly: 求得解。');
break;
end
end
if ite > ITE
disp(['Message: 在 ' num2str(ITE) ' 次迭代过程中,无法求得解,'...
'建议增大总的迭代次数 或者 分析算法的收敛性。']);
end
end
源代码 |
---|
function [rankA, rankB, N, X, ite, tol] = CNumbericCGMIteration(A, b, TOL, ITE, initX)
% 调用格式: [rankA, rankB, N, X, ite, tol] = CNumbericCGMIteration(A, b, TOL, ITE, initX)
% [rankA, rankB, N, X, ite, tol] = CNumbericCGMIteration(A, b, TOL, ITE)
%
% 作者: 王瑞
% 时间: 2015.10.28 16:54 - 19:21
% 版本: Version 1.0
%
% 任务: 共轭梯度法(Conjugate Gradient Method, CGM)迭代法求解线性方程组的解 Ax = b
% 适用于系数矩阵为对称阵的线性方程组(函数内包含矩阵对称化 b = A'*b, A = A'*A)
% 构建 x(k+1) = x(k) + alpha(k)*p(k)
% 等同 MATLAB 内置函数 cgs
%
% 输入: A = 系数矩阵, 方阵
% b = 常系数向量, 行向量
% ITE = 迭代次数上限
% TOL = 解的精度(范数)
% initX = 初始解
%
% 输出: rankA = 系数矩阵 A 的秩
% rankB = 增广矩阵 B 的秩, 其中 B = [A|b]
% N = 方程组未知量个数
% X = 方程组的解向量
% ite = 求解的迭代次数
% tol = 实际误差
%
if nargin == 5
X = initX;
elseif nargin == 4
X = zeros(size(b));
end
%% 解的判定
B = [A, b];
rankA = rank(A);
rankB = rank(B);
N = length(b);
ite = 0;
tol = inf;
if rankA ~= rankB
disp('None: 矩阵 A 的秩不等于 [A b] 的秩, 无解!');
return ;
elseif rankA ~= N
disp(['Waring: 矩阵的秩小于' num2str(N) '[' num2str(rankA) ']'',存在无穷多解。']);
end
if rankA == 1
disp('别闹,用手算的。');
return;
end
if rankA == N
disp(['Good: 矩阵的秩为' num2str(N) ',存在唯一解。']);
end
% 系数矩阵对称,放大 A' 倍
if rank(A-A') ~= 0
b = A'*b;
A = A'*A;
end
% GCM 迭代
r = b - A*X;
while ite < ITE
err = r'*r;
ite = ite + 1;
if ite == 1
p = r;
else
beta = err / errold;
p = r + beta*p;
end
Ap = A*p;
alpha = err / ((Ap)'*p);
X = X + alpha*p;
r = r - alpha*Ap;
errold = err;
tol = norm(r) / norm(b);
if tol < TOL
disp('Excatly: 求得解。');
break;
end
end
if ite > ITE
disp(['Message: 在 ' num2str(ITE) ' 次迭代过程中,无法求得解,'...
'建议增大总的迭代次数 或者 分析算法的收敛性。']);
end
end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。