3.1 - 6 迭代法

基本思想

对线性方程组 Ax=b ,当 A 为低阶稠密矩阵时,选主元Gauss消去/Doolittle分解均为有效方法。
工程应用中 A 常为高阶稀疏矩阵,适用迭代法求解:利好计算机存储与性能。

通法

  • Ax=b 改写为 x=B0x+f
  • 任取初始值,如 x(0)=(0,0,0)T ,将其带入得到方程组解 x(1)=B0x(0)+f
  • 依次得: x(2)=B0x(1)+f, x(2)=B0x(1)+f, , x(k)=B0x(k1)+f, 
  • 即得向量序列 x(0),x(1),,x(k) ,迭代公式 x(k)=B0x(k1)+f
  • 迭代次数较高时,向量序列 x(k) 有可能收敛至逼近精确解的值(不一定)。

根据 x=Bx+f 变形方式的不同,存在多种迭代算法。

定义1
对于给定方程组 x=Bx+f ,用公式 xk+1=Bx(k)+f, (k=0,1,2,3,) 逐步代入求近似解的方法称为迭代法(或称为一阶定常迭代法,这里 Bk 无关)。
如果 limkx(k)=x ,即向量序列收敛至精确解序列,则称此迭代法收敛,否则称发散
由于需要研究 {x(k)} 的收敛性,引进误差向量 ε(k+1)=x(k+1)x 。易得到: ε(k+1)=Bε(k) ,递推得: ε(k)=Bε(k1)==Bkε(0)

Jacobi迭代

通法细节
A 分裂成 A=MN ,其中 M 为可选择的非奇异矩阵,使 Mx=d 易解,一般选择为 A 的某种近似,称 M分裂矩阵
则原方程 Ax=b 转化为 Mx=Nx+b ,即求解: x=M1Nx+M1b
可构造一阶定常迭代法: {x(0)x(k+1)=Bx(k)+f (k=0,1,)B=M1N=M1(MA)=IM1A,f=M1bB=IM1A 为迭代法的迭代矩阵,选取 M 阵就得到 Ax=b 的各种迭代法。

Jacobbi迭代
aii0(i=1,2,,n) ,并将 A 写成三部分: A=(a11a22ann)(0a210an1,1an1,20an,1an,2an,n10)(0a12a1,n1a1,n0a2,n1a2,n0an1,n0)DLU 由此,当 aii0(i=1,2,,n) 时,选取 MA 的对角元素部分,即选取 M=DA=DN ,即得到 Ax=bJacobi迭代法{x(0)x(k+1)=Bx(k)+f (k=0,1,) B=D1(L+U)Jf=D1bJ 为Jacobi迭代法的迭代矩阵

Jacobi迭代法的分量计算公式: 记 x(k)=(x1(k),x2(k),,xn(k)) ,则有: Dxk+1=(L+U)x(k)+baiixik+1=j=1i1aijxj(k)j=i+1naijxj(k)+bi(i=1,2,,n)B=IM1A, f=M1b 因此,解 Ax=b 的Jacobi迭代法的计算公式为: {x(0)=(x1(0),,xi(0),,xn(0))Txi(k+1)=(bij=1,jinaijxj(k))/aiii=1,2,,nk=0,1,  矩阵表示为: Ax=b(DLU)x=bDx=(L+U)x+bDx=(a11a22ann)(x1x2xn)=(0a12a1,n1a1,na210a2,n1a2,nan1,1an1,20an1,nan,1an,2an,n10)(x1x2xn)+b=(L+U)x+b

Gauss-Seidel迭代法

改进Jacobi迭代法:选取分裂矩阵 MA 的下三角部分,即 M=DLA=MU ,得到: {x(0)x(k+1)=Bx(k)+f(k=0,1,)B=I(DL1)A=(DL)1UG,f=(DL)1bG 为Gauss-Seidel迭代法的迭代矩阵。

分量计算公式Dx(k+1)=Dx(k)(Lx(k+1)+UxkDx(k)+b){x1(k+1)=a12a11x2(k)a13a11x3(k)a1na11xn(k)+b2a22x2(k+1)=a21a22x2(k+1)a23a22x3(k)a2na22xn(k)+b2a22x3(k+1)=a31a33x2(k+1)a32a33x3(k+1)a3na33xn(k)+b3a33xn(k+1)=an1annx2(k+1)an2annx3(k+1)an,n1annxn(k+1)+bnann xik+1=bnj=1i1aijxj(k+1)j=i+1naijxj(k)aii

代码:Jacobi迭代

//
// Created by xa on 2021-03-01.
//
#include <iostream>
#include <vector>

using std::vector;

vector<vector<double>> A;
vector<double> b;
vector<double> x;
vector<double> next_x;

int size = 0;

void iterate(vector<vector<double>> A_, vector<double> b_);

int main(){
    vector<vector<double>> A_{{10, 3, 1}, {2, -10, 3}, {1, 3, 10}};
    vector<double> b_{14, -5, 14};
    iterate(A_, b_);
}

void iterate(vector<vector<double>> A_, vector<double> b_) {
    A.assign(A_.begin(), A_.end());
    b.assign(b_.begin(), b_.end());
    size = A.size();

    vector<double> x(size);
    vector<double> next_x(size);

    int k = 0;
    while (true) {
        for (int i = 0; i < size; ++i) {
            double tmp = b[i];
            for (int j = 0; j < size; ++j)
                if (j != i) tmp -= A[i][j] * x[j];
            next_x[i] = tmp / A[i][i];
        }
        for (int i = 0; i < size; ++i) {
            x[i] = next_x[i];
            std::cout << x[i] << ' ';
        }
        std::cout << " 第"<< k << "次" << std::endl;
        if (k > 100) break;
        ++k;
    }
}

代码:Gauss-Seidel迭代

/***区别***/
//Jacobi
for (int j = 0; j < size; ++j)
    if (j != i) tmp -= A[i][j] * x[j];
//Gauss-Seidel
for (int j = 0; j < size; ++j) {
    if (j < i) tmp -= A[i][j] * next_x[j];
    if (j > i) tmp -= A[i][j] * x[j];
}
//
// Created by xa on 2021-03-01.
//
#include <iostream>
#include <vector>

using std::vector;

vector<vector<double>> A;
vector<double> b;
vector<double> x;
vector<double> next_x;

int size = 0;

void iterate(vector<vector<double>> A_, vector<double> b_);

int main(){
    vector<vector<double>> A_{{10, 3, 1}, {2, -10, 3}, {1, 3, 10}};
    vector<double> b_{14, -5, 14};
    iterate(A_, b_);
}

void iterate(vector<vector<double>> A_, vector<double> b_) {
    A.assign(A_.begin(), A_.end());
    b.assign(b_.begin(), b_.end());
    size = A.size();

    vector<double> x(size);
    vector<double> next_x(size);

    int k = 0;
    while (true) {
        for (int i = 0; i < size; ++i) {
            double tmp = b[i];
            for (int j = 0; j < size; ++j) {
                if (j < i) tmp -= A[i][j] * next_x[j];
                if (j > i) tmp -= A[i][j] * x[j];
            }

            next_x[i] = tmp / A[i][i];
        }
        for (int i = 0; i < size; ++i) {
            x[i] = next_x[i];
            std::cout << x[i] << ' ';
        }
        std::cout << " 第"<< k << "次" << std::endl;
        if (k > 100) break;
        ++k;
    }
}