解线性方程组(二)——Jacobi迭代法求解(C++)

news2024/11/27 7:37:41

迭代法

相比于直接法求解,迭代法使用多次迭代来逐渐逼近解,其精度比不上直接法,但是其速度会比直接法快很多,计算精度可控,特别适用于求解系数矩阵为大型稀疏矩阵的方程组。

Jacobi迭代法

假设有方程组如下:
{ a 11 x 1 + a 12 x 2 + ⋯ + a 1 n x n = b 1 a 21 x 1 + a 22 x 2 + ⋯ + a 2 n x n = b 2 ⋯ ⋯ ⋯ a n 1 x 1 + a n 2 x 2 + ⋯ + a n n x n = b n \begin{cases} a_{11}x_1+a_{12}x_2+\cdots+a_{1n}x_n=b_1\\ a_{21}x_1+a_{22}x_2+\cdots+a_{2n}x_n=b_2\\ \cdots \qquad \qquad\cdots \qquad \qquad \cdots \\ a_{n1}x_1+a_{n2}x_2+\cdots+a_{nn}x_n=b_n\\ \end{cases} a11x1+a12x2++a1nxn=b1a21x1+a22x2++a2nxn=b2an1x1+an2x2++annxn=bn
将其转换为矩阵形式
A x ⃗ = b ⃗ A\vec{x}=\vec{b} Ax =b
[ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋮ ⋮ ⋱ ⋮ a m 1 a m 2 ⋯ a m n ] [ x 1 x 2 ⋮ x n ] = [ b 1 b 2 ⋮ b n ] \begin{bmatrix} {a_{11}}&{a_{12}}&{\cdots}&{a_{1n}}\\ {a_{21}}&{a_{22}}&{\cdots}&{a_{2n}}\\ {\vdots}&{\vdots}&{\ddots}&{\vdots}\\ {a_{m1}}&{a_{m2}}&{\cdots}&{a_{mn}}\\ \end{bmatrix} \begin{bmatrix} {x_{1}}\\ {x_{2}}\\ {\vdots}\\ {x_{n}}\\ \end{bmatrix}= \begin{bmatrix} {b_{1}}\\ {b_{2}}\\ {\vdots}\\ {b_n} \end{bmatrix} a11a21am1a12a22am2a1na2namn x1x2xn = b1b2bn
对于是否可以使用Jacobi迭代法,需要满足以下条件之一:

  1. A为行对角优阵,即 ∣ a i i ∣ > ∑ j ≠ i ∣ a i j ∣ ( i = 1 , 2 , ⋯   , n ) |a_{ii}|>\sum_{j \neq i}|a_{ij}|(i=1,2,\cdots,n) aii>j=iaij(i=1,2,,n)
  2. A为行列角优阵,即 ∣ a j j ∣ > ∑ j ≠ i ∣ a i j ∣ ( j = 1 , 2 , ⋯   , n ) |a_{jj}|>\sum_{j \neq i}|a_{ij}|(j=1,2,\cdots,n) ajj>j=iaij(j=1,2,,n)
  3. A的元素满足 ∑ i ≠ j ∣ a i j ∣ ∣ a i i ∣ < 1 ( j , 1 , 2 , ⋯   , n ) \sum_{i \neq j}\frac{|a_{ij}|}{|aii|}<1(j,1,2,\cdots,n) i=jaiiaij<1(j,1,2,,n)
    若矩阵A满足上述条件之一,则可以使用Jacobi迭代法求解方程组。
    首先将上述的方程组转为如下形式:
    { x 1 = 1 a 11 ( − a 12 x 2 − ⋯ − a 1 n x n + b 1 ) x 2 = 1 a 22 ( − a 21 x 1 − ⋯ − a 2 n x n + b 2 ) ⋯ ⋯ ⋯ x n = 1 a n n ( − a n 1 x 1 − ⋯ − a n n − 1 x n − 1 + b n ) \begin{cases} x_1=\frac{1}{a_{11}}(-a_{12}x_2-\cdots -a_{1n}x_n+b_1)\\ x_2=\frac{1}{a_{22}}(-a_{21}x_1-\cdots -a_{2n}x_n+b_2)\\ \cdots \qquad \qquad\cdots \qquad \qquad \cdots \\ x_n=\frac{1}{a_{nn}}(-a_{n1}x_1-\cdots -a_{nn-1}x_{n-1}+b_n)\\ \end{cases} x1=a111(a12x2a1nxn+b1)x2=a221(a21x1a2nxn+b2)xn=ann1(an1x1ann1xn1+bn)
    写成矩阵形式可以得到Jacobi迭代式:
    ( D + L + u ) x ⃗ = b ⃗ D x ⃗ = − ( L + U ) x ⃗ + b ⃗ x ⃗ ( k + 1 ) = − D − 1 ( L + U ) x ⃗ ( k ) + D − 1 b ⃗ (D+L+u)\vec{x}=\vec{b}\\ D\vec{x}=-(L+U)\vec{x}+\vec{b}\\ \vec{x}^{(k+1)}=-D^{-1}(L+U)\vec{x}^{(k)}+D^{-1}\vec{b} (D+L+u)x =b Dx =(L+U)x +b x (k+1)=D1(L+U)x (k)+D1b
    其中 D D D为对角矩阵, L L L为下三角矩阵- D D D U U U为上三角矩阵- U U U D + L + U D+L+U D+L+U为矩阵A。
    在这里插入图片描述

代码实现

由于这个过程涉及大量的矩阵操作,整个算法分为两个源文件:Matrix.cpp实现矩阵操作,main.cpp实现Jacobi迭代法。
首先是Matrix.cpp的代码,其中矩阵求逆的原理参考:

#include <Matrix.h>
#include <iostream>
#include <cmath>
//矩阵与向量相乘,输入矩阵A,向量b,运算结果result和维数n
void matrix_multiply_vector(double **A,double *b,double * result,int n)
{
    for(int i=0;i<n;i++)
    {
        result[i]=0.0;
        for(int j=0;j<n;j++)
        {
            result[i]+=A[i][j]*b[j];
        }
    }
}
//矩阵乘法
void matrix_multiply_matrix(double **A,double **B,double **result,int n)
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            result[i][j]=0.0;
            for(int k=0;k<n;k++)
            {
                result[i][j]+=A[i][k]*B[k][j];
            }
        }
    }
}
//矩阵加减法
void matrix_add_matrix(double **A,double **B,double **result,int n,bool isAdd)
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(isAdd)
            {
                result[i][j]=A[i][j]+B[i][j];
            }
            else{
                result[i][j]=A[i][j]-B[i][j];
            }
            
        }
    }
}
//向量的加减法
void vactor_add_vector(double *A,double *B,double *result,int n,bool isAdd)
{
    for(int i=0;i<n;i++)
    {
        if(isAdd)
        {
            result[i]=A[i]+B[i];
        }
        else{
            result[i]=A[i]-B[i];
        }
    }
}
//判断向量误差范围,只要符合精度即可
bool vector_equal(double *A,double *B,int n,double error)
{
    for(int i=0;i<n;i++)
    {
        if(fabs(A[i]-B[i])>error)
        {
            return false;
        }
    }
    return true;
}
//向量赋值
void vector_copy(double *A,double *B,int n)
{
    for(int i=0;i<n;i++)
    {
        B[i]=A[i];
    }
}
//矩阵初始化
void matrix_init(double **A,int n)
{
    for(int i=0;i<n;i++)
    {
        A[i]=new double [n];
        for(int j=0;j<n;j++)
        {
            A[i][j]=0.0;
        }
    }
}
//判断矩阵A是否有收敛性
bool astringency(double **A,int n)
{
    double abs_row_sum=0.0;
    double abs_col_sum=0.0;
    double the_third_condition=0.0;
    bool RowOptimalMatrix=true;
    bool ColOptimalMatrix=true;
    for(int i=0;i<n;i++)//判断是不是行对角优阵
    {
        abs_row_sum=0.0;
        for(int j=0;j<n;j++)
        {
            if(i!=j)
            {
                abs_row_sum+=fabs(A[i][j]);
            }
        }
        if(abs_row_sum>A[i][i])//证明不是行对角优阵
        {
            RowOptimalMatrix=false;
            break;
        }
    }
    for(int j=0;j<n;j++)//判断是不是列对角优阵
    {
        abs_col_sum=0.0;
        for(int i=0;i<n;i++)
        {
            if(i!=j)
            {
                abs_col_sum+=fabs(A[i][j]);
            }
        }
        if(abs_col_sum>A[j][j])
        {
            ColOptimalMatrix=false;
            break;
        }
    }
    return ColOptimalMatrix or RowOptimalMatrix;
}
//矩阵交换某两行
void matrix_swap_row(double **A,int i,int j,int n)
{
    double temp;
    for(int k=0;k<n;k++)
    {
        temp=A[i][k];
        A[i][k]=A[j][k];
        A[j][k]=temp;
    }
}
//矩阵第i行=矩阵第i行-矩阵第j行*a
void matrix_minus_inner(double **A,double a,int i,int j,int n)
{
    for(int k=0;k<n;k++)
    {
        A[i][k]-=a*A[j][k];
    }
}
//矩阵求逆
void matrix_inverse(double **A,double **A_inverse,int n)
{
    double **A_E=new double*[2*n];
    //构建增广矩阵
    for(int i=0;i<n;i++)
    {
        A_E[i]=new double [n*2];
        for(int j=0;j<n*2;j++)
        {
            if(j<n)
            {
                A_E[i][j]=A[i][j];
            }
            else if((j-n)==i){
                A_E[i][j]=1;
            }
            else{
                A_E[i][j]=0;
            }
        }
    }
    //首先将矩阵化为上三角矩阵
    for(int i=0;i<n;i++)
    {
        if(A_E[i][i]==0)
        {
            for(int k=i+1;k<n;k++)
            {
                if(A_E[k][i]!=0)
                {
                    matrix_swap_row(A_E,i,k,n*2);
                    break;
                }
            }
        }
        for(int j=i+1;j<n;j++)
        {
            matrix_minus_inner(A_E,A_E[j][i]/A_E[i][i],j,i,2*n);
        }
    }
    //判断矩阵是否可逆
    for(int i=0;i<n;i++)
    {
        if(A_E[i][i]==0)
        {
            std::cout<<"矩阵不可逆"<<std::endl;
            exit(0);
        }
    }
    //将上三角转换为对角矩阵
    for(int j=1;j<n;j++)
    {
        for(int i=0;i<j;i++)
        {
            matrix_minus_inner(A_E,A_E[i][j]/A_E[j][j],i,j,2*n);
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=n;j<2*n;j++)
        {
            A_inverse[i][j-n]=A_E[i][j]/A_E[i][i];
        }
    }
}

main.cpp文件内容如下:

//Jacobi迭代法求解线性方程组
/*
5x1+2x2-2x3=1
x1+4x2+x3=2
x1-2x2+4x3=-1
*/
#include<iostream>
#include<cmath>
#include<Matrix.h>//自定义头文件
using namespace std;
int main()
{
    int n;
    cout<<"Enter the matrix dimension A: ";
    cin>>n;//输入数组维度
    double **A=new double *[n];
    cout<<"Enter the coefficient matrix:"<<endl;
    for(int i=0;i<n;i++)
    {
        A[i]=new double[n];
        for(int j=0;j<n;j++)
        {
            cin>>A[i][j];//每次输入一个数字都用空格隔开,输入样例
            //1 2 3\enter
            //4 5 6\enter
            //7 8 9\enter
        }
    }
    double *b=new double[n];
    cout<<"Input vectors b: ";
    for(int i=0;i<n;i++)
    {
        cin>>b[i];//输入方程组右边的向量,1 2 3\enter
    }
    bool isAstringency=astringency(A,n);//判断系数矩阵A是否具有收敛性
    if(isAstringency)
    {
        cout<<"矩阵A符合收敛性"<<endl;
    }
    else{
        exit(0);
        cout<<"矩阵A不符合收敛性"<<endl;
    }
    double *x=new double[n];//解向量X
    double *x_last=new double[n];//上一次的x
    for(int i=0;i<n;i++)
    {
        x[i]=0.0;//初始化x
    }
    double **A_L_U=new double*[n];//L+U
    double **A_D_inverse=new double*[n];//D的逆
    for(int i=0;i<n;i++)
    {
        A_D_inverse[i]=new double [n];
        A_L_U[i]=new double [n];
        for(int j=0;j<n;j++)
        {
            if(i==j)
            {
                A_L_U[i][j]=0.0;
                A_D_inverse[i][j]=1.0/A[i][j];//对角矩阵的逆为其倒数
            }
            else{
                A_L_U[i][j]=A[i][j];
                A_D_inverse[i][j]=0.0;
            }
        }
    }
    double **B=new double *[n];//公式前半段的矩阵
    matrix_init(B,n);
    matrix_multiply_matrix(A_D_inverse,A_L_U,B,n);//求D^(-1)(L+U)
    double *f=new double[n];
    matrix_multiply_vector(A_D_inverse,b,f,n);//求取D^-1 * b
    double *temp1=new double[n];
    do{
        vector_copy(x,x_last,n);
        matrix_multiply_vector(B,x_last,temp1,n);//计算公式前半段
        vactor_add_vector(f,temp1,x,n,false);
    }while(vector_equal(x,x_last,n,1e-6)==false);//判断向量在误差范围内相等
    cout<<"运行结果为:"<<endl;
    for(int i=0;i<n;i++)
    {
        cout<<x[i]<<" ";
    }
    system("pause");
    return 0;
}

结果分析

代码运行结果如下:
在这里插入图片描述

当下一次的迭代结果与上一次的迭代结果的最大相差值小于1e-6时,认为迭代已经收敛,输出结果即可(当然也可以换成其它结束迭代方法,如:判断两个向量之差的二范数)。
与直接使用克拉默法则计算准确的解以及matlab计算结果比较,不难发现其 x 1 x_1 x1 x 3 x_3 x3均不为0,只是是一个在我们设定的误差范围内接近0的数,符合迭代法的解的性质,只能在设定的误差范围内得到一个近似的解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1450906.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】实现Date类的各种运算符重载

上一篇文章只实现了operator操作符重载&#xff0c;由于运算符较多&#xff0c;该篇文章单独实现剩余所有的运算符重载。继续以Date类为例&#xff0c;实现运算符重载&#xff1a; 1.Date.h #pragma once#include <iostream> #include <assert.h>using namespace …

WebSocket | 基于TCP的全双工通信网络协议

文章目录 1、介绍2、示例2.1、分析2.2、代码开发2.3、功能测试 ​&#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&#xff0c;初步涉猎Python人工智能开…

qt-C++笔记之打印所有发生的事件

qt-C笔记之打印所有发生的事件 code review! 文章目录 qt-C笔记之打印所有发生的事件1.ChatGPT问答使用 QApplication 的 notify 方法使用 QObject 的 event 方法 2.使用 QObject 的 event 方法3.使用 QApplication 的 notify 方法 1.ChatGPT问答 在Qt C中&#xff0c;若要打…

小米米家智能摄像头mp4多碎片手工恢复案例

小米米家智能摄像头mp4多碎片手工恢复案例 智能摄像头品牌中小米算是绝对的大厂&#xff0c;其采用的方案也是比较成熟比较典型的&#xff1a;日志截图1分钟1个文件。小米米家的智能摄像头之前处理过很多&#xff0c;这次来讲一个比较特殊的案例。 故障存储: 32G TF卡 fat…

HiveSQL——统计当前时间段的有客人在住的房间数量

注&#xff1a;参考文章&#xff1a; HiveSQL一天一个小技巧&#xff1a;如何统计当前时间点状态情况【辅助变量累计变换思路】_sql查询统计某状态出现的次数及累计时间-CSDN博客文章浏览阅读2k次&#xff0c;点赞6次&#xff0c;收藏8次。本文总结了一种当前时间点状态统计的…

Vue 进阶系列丨实现简易VueRouter

‍‍Vue 进阶系列教程将在本号持续发布&#xff0c;一起查漏补缺学个痛快&#xff01;若您有遇到其它相关问题&#xff0c;非常欢迎在评论中留言讨论&#xff0c;达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧&#xff01; 2013年7月28日&#xff0c;尤雨溪第一次在 G…

springboot集成elk实现日志采集可视化

一、安装ELK 安装ELK组件请参考我这篇博客&#xff1a;windows下安装ELK(踩坑记录)_windows上安装elk教程-CSDN博客 这里不再重复赘述。 二、编写logstash配置 ELK组件均安装好并成功启动&#xff0c;进入到logstash组件下的config文件夹&#xff0c;创建logstash.conf配置…

Three.JS教程5 threejs中的材质

Three.JS教程5 threejs中的材质 一、什么是Three.js材质&#xff1f;二、Three.js的材质类型1. 材质类型2. 材质的共用属性&#xff08;1&#xff09;.alphaHash : Boolean&#xff08;2&#xff09;.alphaTest : Float&#xff08;3&#xff09;.alphaToCoverage : Boolean&am…

使用 Mermaid 创建流程图,序列图,甘特图

使用 Mermaid 创建流程图和图表 Mermaid 是一个流行的 JavaScript 库&#xff0c;用于创建流程图、序列图、甘特图和其他各种图表。它的简洁语法使得创建图表变得非常简单&#xff0c;无需复杂的绘图工具或专业的编程技能。在本文中&#xff0c;我们将讲解如何使用 Mermaid 来创…

卷积神经网络的基本结构

卷积神经网络的基本结构 与传统的全连接神经网络一样&#xff0c;卷积神经网络依然是一个层级网络&#xff0c;只不过层的功能和形式发生了变化。 典型的CNN结构包括&#xff1a; 数据输入层&#xff08;Input Layer&#xff09;卷积层&#xff08;Convolutional Layer&#x…

Android 9.0 禁用adb shell input输入功能

1.前言 在9.0的系统rom产品开发中,在进行一些定制开发中,对于一些adb shell功能需要通过属性来控制禁止使用input 等输入功能,比如adb shell input keyevent 响应输入事件等,所以就需要 熟悉adb shell input的输入事件流程,然后来禁用adb shell input的输入事件功能,接…

函数求导法则【高数笔记】

【分类】 1. 四则运算求导 2. 复合运算求导 3. 整体思想求导 #整体思想求导本质是运用复合运算求导&#xff0c;只不过是对复合运算求导的一种精炼 #无论是具体函数还是抽象函数求导&#xff0c;方法是一致的 【四则运算求导】 加&#xff0c;减&#xff0c;乘&#xff0c;除&a…

openEuler 22.03 LTS 上源码安装 PostgreSQL 15

安装PostgreSQL 15 1 安装必要的依赖 #yum install -y readline-devel zlib-devel gcc2、下载源码 # wget https://ftp.postgresql.org/pub/source/v15.6/postgresql-15.6.tar.gz # tar -xzvf postgresql-15.6.tar.gz3 配置 # cd postgresql-15.6/ # ./configure4 编译安装…

JVM-JVM中对象的结构

对象内存布局 对象里的三个区&#xff1a; 对象头&#xff08;Header&#xff09;&#xff1a;Java对象头占8byte。如果是数组则占12byte。因为JVM里数组size需要使用4byte存储。 标记字段MarkWord&#xff1a; 用于存储对象自身的运行时数据&#xff0c;它是synchronized实现轻…

图像识别基础之模板匹配

principle 图像匹配 本质&#xff1a;图像的相似度很高(矩阵的相似度很高) code /*\brief 我的图像匹配函数&#xff0c;获取差方和均值最小的矩阵作为结果\param srcPicFile:用以匹配的图像文件\param templatePicFile:模板图像文件\param destPicFile:输出的检测结果文件…

【读书笔记】ICS设备及应用攻击(一)

工控系统通常是由互联设备所构成的大型复杂系统&#xff0c;这些设备包括类似于人机界面&#xff08;HMI&#xff09;、PLC、传感器、执行器以及其他使用协商好的协议进行相互通信的设备。所有交互背后的驱动力都是软件&#xff0c;软件为工控系统中几乎所有部分的运行提供支撑…

鸿蒙开发-HarmonyOS UI架构

初步布局Index 当我们新建一个工程之后&#xff0c;首先会进入Index页。我们先简单的做一个文章列表的显示 class Article {title?: stringdesc?: stringlink?: string }Entry Component struct Index {State articles: Article[] []build() {Row() {Scroll() {Column() …

Python是垃圾?千万不要再学Python了?

“人生苦短&#xff0c;快学Python”这句话&#xff0c;相信大家都有看到过&#xff0c;但是有细心留意过&#xff0c;就会发现Python其实在网上的评价褒贬不一&#xff0c;有好评&#xff0c;也有差评。这就会给那些不懂Python却想要学Python的一些人造成困惑&#xff0c;我到…

主从延迟如何解决

最近项目上线&#xff0c;遇到了主从问题。按理说公司基建不至于出现这种问题&#xff0c;但就是出现了。可能因为用的不是原生的MySQL吧。主从延迟会给前端和服务端带来很多问题&#xff0c;需要花费时间用工程手段来解决&#xff0c;我认为这是很不合理的。 举几个因为主从延…

数字IC实践项目(9)— Tang Nano 20K: I2C OLED Driver

Tang Nano 20K: I2C OLED Driver 写在前面的话硬件模块RTL电路和相关资源报告SSD1306 OLED 驱动芯片SSD1306 I2C协议接口OLED 驱动模块RTL综合实现 总结 写在前面的话 之前在逛淘宝的时候偶然发现了Tang Nano 20K&#xff0c;十分感慨国产FPGA替代方案的进步之快&#xff1b;被…