理工大数值计算方法实验报告

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37437892/article/details/81056056

实验名称:方程求根

实验时间:2018.06.12

实验目的和要求:

1.了解方程求根的基本方法、基本原理、误差估计;

2.能够按照工程实际要求,选择适当的算法;

3.通过编写程序,进行算法设计和数值求解。

实验内容和原理:

 1. 熟悉使用二分法、牛顿法等方程求根的基本方法、基本原理、误差估计的相关理论。

2. 选择方程求解方法中的两种方法求方程:f(x)=x3+4x2-10=0在[1,2]内的一个实根,且要求满足精度|x*-xn|<0.5×10-5

如何对区间进行二分,并在二分后的左右两个区间中确定下一次求根搜索的区间?

假设区间端点为x1和x2,则通过计算区间的中点x0,即可将区间[x1, x2]二分为[x1, x0]和[x0, x2]。这时,为了确定下一次求根搜索的区间,必须判断方程的根在哪一个区间内,由上图可知方程的根所在区间的两个端点处的函数值的符号一定是相反的。也就是说,如果f(x0)与f(x1)是异号的,则根一定在左区间[x1, x0]内,否则根一定在右区间[x0, x2]内。

牛顿迭代法计算公式

设r是f(x) = 0的根,选取x0作为r初始近似值,过点(x0,f(x0))做曲线y = f(x)的切线L,L的方程为y = f(x0)+f'(x0)(x-x0),求出L与x轴交点的横坐标 x1 = x0-f(x0)/f'(x0),称x1为r的一次近似值。过点(x1,f(x1))做曲线y = f(x)的切线,并求该切线与x轴交点的横坐标 x2 = x1-f(x1)/f'(x1),称x2为r的二次近似值。重复以上过程,得r的近似值序列,其中x(n+1)=x(n)-f(x(n))/f'(x(n)),称为r的n+1次近似值,上式称为牛顿迭代

公式。解非线性方程f(x)=0的牛顿法是把非线性方程线性化的一种近似方法。把f(x)在x0点附近展开成泰勒级数 f(x) = f(x0)+(x-x0)f'(x0)+(x-x0)^2*f''(x0)/2! +… 取其线性部分,作为非线性方程f(x) = 0的近似方程,即泰勒展开的前两项,则有f(x0)+f'(x0)(x-x0)=0 设f'(x0)≠0则其解为x1=x0-f(x0)/f'(x0) 这样,得到牛顿法的一个迭代序列:x(n+1)=x(n)-f(x(n))/f'(x(n))。

 

主要仪器设备:

电脑、win10、DevC++运行环境

 

上机调试修改源程序:

牛顿法:

#include<iostream>

using  namespace std;

#include<cmath>

const double N=0.5*1e-5;

double get(double x){

       return (x-(x*x*x+4*x*x-10)/(3*x*x+8*x));

      

}

int main(){

  double a,b;

  a=1.5;

  int sum=0;

  while(1){

     sum++;

     b=get(a);

     printf("第%i次: %.12f\n",sum,b);

     if(abs(b-a)<N){

            printf("结果是:%.12f\n",b);

            break;

         }

       a=b;

  } 

}

 

二分法:

#include<iostream>

using namespace std;

const double N=0.5*0.00001;

double getData(double x){

       return x*x*x+4*x*x-10;

}

void solve(double a,double b){

       int sum=1;

       double xn,fa,fb,fx;

       while(1){

              xn=(a+b)/2.0;

              printf("第%i次:  [%.12f,%.12f]   中点值:%.12f\n",sum,a,b,xn);

              sum++;

              if((b-a)/2.0<N){

                printf("结果是:%.12f\n",xn);

                     break;

              }

               fa=getData(a);

               fb=getData(b);

               fx=getData(xn);

               if(fa*fx<0){

                    b=xn;

               }

              else if(fx*fb<0){

                     a=xn;

              }

       }

      

}

int main(){

       solve(1,2);

}

 

实验结果与分析

牛顿法:

二分法:

讨论、心得(可选):

从此求解非线性方程组有了新的可编程的方法,运算起来速度快,虽然存在舍入误差。编程技巧也理解的更加深刻了,能够轻松控制小数的精度和输出保留小数位数,学以致用,将数值方法正真和计算机联系起来,并且熟练运用。同时掌握了许多编程排除错误的技巧。同学之间相互讨论,互相帮助,将课堂知识理解的更加透彻。

    可采用自顶向下,逐步细化的编程方式。多将一些求表达式值的功能封装成一个函数,这样可扩展性就强了,而且思路清晰,完成效率高。

 

实验名称:线性方程组的直接解法

实验时间:2018.06.18

实验目的和要求:

1.了解Gauss消元法、LU分解法等线性方程组直接求解的基本方法、基本原理;

2.通过编写程序,进行算法设计和数值求解。

实验内容和原理:

利用高斯消元法、LU分解法分别求解下列方程组:

高斯消元法的基本思想是将原有线性方程组化为与之等价的三角形方程组,这个过程也称为消元过程,再通回代过程进行求解。消元过程使用公式为:

回代过程使用公式如下:

下面是关于使用LU分解求解方程组Ax = b:

当系数矩阵A完成了LU分解后,方程组Ax = b就可以化为L(Ux) = b,等价于求解两个方程组Ly = b和Ux = y;

具体计算公式为:

 

主要仪器设备:

电脑、win10、DevC++运行环境

 

上机调试修改源程序:

高斯消元法

#include<iostream>

using namespace std;

const int N=1000;

double value[N][N];

 int n;

void xiao(){

       for(int k=1;k<n;++k){

              for(int i=k+1;i<=n;++i){

                      //取系数

                      double l=value[i][k]/value[k][k];

                       value[i][k]=0;

                     for(int col=k+1;col<=n+1;++col)

                      value[i][col]-=value[k][col]*l;

              }

             

       }

}

void solve(){

       double ans[N];

       for(int i=n;i>=0;i--){

   //从最后一行开始;

   double sum=0;      

       for(int k=i+1;k<=n;++k){

              sum+=value[i][k]*ans[k];

       }

        ans[i] =(value[i][n+1]-sum)/value[i][i];

      

}

  for(int i=1;i<=n;++i)

 

   printf("第%i个x=%.6f\n",i,ans[i]);

  

}

int main(){

        

         cin>>n;

for(int i=1;i<=n;++i)

 for(int j=1;j<=n+1;++j)

 cin>>value[i][j];

   xiao();

   solve();

    

}

LU分解法

#include<iostream>

using namespace std;

const int N=998;

       double a[N][N];

       double b[N];

               double l[N][N],u[N][N];

       int n;

void solve(){

       double y[N],x[N];

       double b[N];

       cout<<"请输入n个b值"<<endl;

       for(int i=1;i<=n;++i)

        cin>>b[i];

        

       for(int i=1;i<=n;++i){

              double temp=0;

              for(int j=1;j<i;++j){

                     temp+=y[j]*l[i][j];

              }

              y[i]=b[i]-temp;

       }

      

       for(int i=n;i>=1;--i)

       {

              double temp=0;

              for(int j=n;j>i;--j ){

                     temp+=x[j]*u[i][j];

              }

              x[i]=(y[i]-temp)/u[i][i];

       }

       //输出y

       for(int i=1;i<=n;++i)

       printf("y%i=%.6f\n",i,y[i]);

      

              for(int i=1;i<=n;++i)

       printf("x%i=%.6f\n",i,x[i]);

}            

int main(){

      

       cin>>n;

       //输入

       for(int i=1;i<=n;++i)

        for(int j=1;j<=n;++j)

         cin>>a[i][j];

        

        //lu分解

        

        //处理第一行和第一列

        for(int j=1;j<=n;++j)

         u[1][j]=a[1][j];

        

        for(int i=2;i<=n;++i)

         l[i][1]=a[i][1]/u[1][1];

        

        //处理剩下的

        int k;

          for( k=2;k<=n;++k){

              for(int j=k;j<=n;++j){

                      

                      double t1=0;

              

              for(int i=1;i<=k-1;++i)

               t1+=l[k][i]*u[i][j];

               

               

               u[k][j]=a[k][j]-t1;

               

          }

         

          for(int i=k+1;i<=n;++i){

           

              double t2=0;

               for(int p=1;p<=k-1;++p)

                t2+=l[i][p]*u[p][k];

             l[i][k]=(a[i][k]-t2)/u[k][k];

          }

                  

           

        } 

        

        //输出lu

        //先将对角线元素置为1

        for(int i=1;i<=n;++i)

         l[i][i]=1;

        cout<<"L为:"<<endl;

         for(int i=1;i<=n;++i){

        

          for(int j=1;j<=n;++j)

           printf("%.6f ",l[i][j]);

                 cout<<endl;

}

 cout<<"U为:"<<endl;

       for(int i=1;i<=n;++i){

        

          for(int j=1;j<=n;++j)

           printf("%.6f ",u[i][j]);

           cout<<endl;

                

}

//回带

 solve();

        

}

 

实验结果与分析

高斯消元法:

LU分解法

讨论、心得(可选):

编写LU分解法的时候花了很长时间,主要是没有快速理清各层变量i、j、k的具体含义。懂了原理之后就能很快的编写程序了,但并不一定能一步成功,还需要后期的调试,和同学适当的讨论可以引发思维的碰撞,帮助我们快速理解和实践。

 

实验名称: 线性方程组的迭代解法

实验时间:2018.06.20

实验目的和要求:

1.了解雅可比迭代法、高斯-赛德尔迭代法等线性方程组迭代求解的基本方法、基本原理;

2.能够按照工程实际要求,选择适当的算法;

3.通过编写程序,进行算法设计和数值求解。

实验内容和原理:

使用雅可比迭代法或高斯-赛德尔迭代法对下列方程组进行求解。

雅可比迭代法的公式如下:

高斯-赛德尔迭代法的公式如下:

主要仪器设备:

电脑、win10、DevC++运行环境

 

上机调试修改源程序:

高斯-赛德尔迭代法:

#include<iostream>

#include<cmath>

using namespace std;

const int N=1000;

const double wucha=0.5*0.000001;

double x1[N],x2[N],x3[N];

double fanshu[N];

int main(){

       printf("第0组:%.6f %.6f %.6f \n",x1[0],x2[0],x3[0]);

       for(int i=1;;++i){

              x1[i]=              0.1*x2[i-1]+0.2*x3[i-1]+0.72;

              x2[i]=0.1*x1[i]+              +0.2*x3[i-1]+0.83;

              x3[i]=0.2*x1[i]+0.2*x2[i]                +0.84;   

             

              printf("第%i组:%.6f %.6f %.6f \n",i,x1[i],x2[i],x3[i]);

             

              //算误差   2范数

               

             

               fanshu[i]=abs(x1[i])+abs(x2[i])+abs(x3[i]);

               if(abs(fanshu[i]-fanshu[i-1])<wucha){

                    break;

               }

       }

      

}

实验结果与分析

高斯-赛德尔迭代法

讨论、心得(可选):

以后解线性方程组就可以少用克莱姆法则,用计算机迅速求解出来近似值,这种方法在实际生活的建模中非常实用。通过此次试验,不仅感受到了数值方法这门学科的奇妙用处,而且大大提升了我们快速编程能力,为今后的工作打下了坚实的基础。

 

实验名称:代数插值

实验时间:2018.06.20

实验目的和要求:

1.了解拉格朗日插值法或牛顿插值法的基本方法、基本原理;

2.通过编写程序,进行算法设计和数值求解。

实验内容和原理:

使用拉格朗日插值法或牛顿插值法求解:已知f(x)在6个点的函数值如下表所示,运用插值方法,求f(0.596)的近似值。

X

0.40

0.55

0.65

0.80

0.90

1.05

f(x)

0.41075

0.57815

0.69675

0.88811

1.02652

1.25386

拉格朗日基函数为:

拉格朗日插值多项式为:

主要仪器设备:

电脑、win10、DevC++运行环境

 

上机调试修改源程序:

拉格朗日插值法: 

#include<iostream>

using namespace std;

double x[100],y[100];

int n;

double get(double t){

       double ans=0;

       for(int i=1;i<=n;++i){

       double temp=1;

       for(int j=1;j<=n;++j){

                 if(j!=i)

                      temp*=(t-x[j]) /(x[i]-x[j]);

       }

         ans+=y[i]*temp;

}

   return ans;

}

int main(){

cin>>n;

for(int i=1;i<=n;++i)

cin>>x[i]>>y[i];

printf("%.6f\n",get(0.596));  

}

实验结果与分析

 

讨论、心得(可选):

拉格朗日插值多项式可以快速找出一条过所有插值节点的曲线,在生活中可以帮助我们快速预测或找出事物的相关规律。通过编程,更深层次的掌握了拉格朗日插值的由来,并且提高了自己编程的信心。体会到了用计算机解决问题方便、快捷。

 

实验名称:最小二乘法拟合多项式

实验时间:2018.06.22

实验目的和要求:

1.了解最小二乘法拟合多项式的基本方法、基本原理;

2.通过编写程序,进行算法设计和数值求解。

实验内容和原理:

给定数据点(xi ,yi),用最小二乘法拟合数据的多项式,并求平方误差。

xi

0

0.5

0.6

0.7

0.8

0.9

1.0

yi

1

1.75

1.96

2.19

2.44

2.71

3.00

最小二乘法(又称最小平方法)是一种数学优化技术。它通过最小化误差的平方和寻找数据的最佳函数匹配。

 

主要仪器设备:

电脑、win10、DevC++运行环境

 

上机调试修改源程序:

    #include<cstring>

#include <iostream>

#include <vector>

#include <cmath>

using namespace std;

//最小二乘拟合相关函数定义

double sum(vector<double> Vnum, int n);

double MutilSum(vector<double> Vx, vector<double> Vy, int n);

double RelatePow(vector<double> Vx, int n, int ex);

double RelateMutiXY(vector<double> Vx, vector<double> Vy, int n, int ex);

void EMatrix(vector<double> Vx, vector<double> Vy, int n, int ex, double coefficient[]);

void CalEquation(int exp, double coefficient[]);

double F(double c[],int l,int m);

double Em[6][4];

//主函数,这里将数据拟合成二次曲线

int main(int argc, char* argv[])

{

       double arry1[7]={0,0.5,0.6,0.7,0.8,0.9,1};

       double arry2[7]={1,1.75,1.96,2.19,2.44,2.71,3};

       double coefficient[7];

       memset(coefficient,0,sizeof(double)*7);

       vector<double> vx,vy;

       for (int i=0; i<7; i++)

       {

              vx.push_back(arry1[i]);

              vy.push_back(arry2[i]);

       }

       EMatrix(vx,vy,7,3,coefficient);

       printf("拟合方程为:y = %lf + %lfx + %lfx^2 \n",coefficient[1],coefficient[2],coefficient[3]);

       return 0;

}

//累加

double sum(vector<double> Vnum, int n)

{

       double dsum=0;

       for (int i=0; i<n; i++)

       {

              dsum+=Vnum[i];

       }

       return dsum;

}

//乘积和

double MutilSum(vector<double> Vx, vector<double> Vy, int n)

{

       double dMultiSum=0;

       for (int i=0; i<n; i++)

       {

              dMultiSum+=Vx[i]*Vy[i];

       }

       return dMultiSum;

}

//ex次方和

double RelatePow(vector<double> Vx, int n, int ex)

{

       double ReSum=0;

       for (int i=0; i<n; i++)

       {

              ReSum+=pow(Vx[i],ex);

       }

       return ReSum;

}

//x的ex次方与y的乘积的累加

double RelateMutiXY(vector<double> Vx, vector<double> Vy, int n, int ex)

{

       double dReMultiSum=0;

       for (int i=0; i<n; i++)

       {

              dReMultiSum+=pow(Vx[i],ex)*Vy[i];

       }

       return dReMultiSum;

}

//计算方程组的增广矩阵

void EMatrix(vector<double> Vx, vector<double> Vy, int n, int ex, double coefficient[])

{

       for (int i=1; i<=ex; i++)

       {

              for (int j=1; j<=ex; j++)

              {

                     Em[i][j]=RelatePow(Vx,n,i+j-2);

              }

              Em[i][ex+1]=RelateMutiXY(Vx,Vy,n,i-1);

       }

       Em[1][1]=n;

       CalEquation(ex,coefficient);

}

//求解方程

void CalEquation(int exp, double coefficient[])

{

       for(int k=1;k<exp;k++) //消元过程

       {

              for(int i=k+1;i<exp+1;i++)

              {

                     double p1=0;

                     if(Em[k][k]!=0)

                            p1=Em[i][k]/Em[k][k];

                     for(int j=k;j<exp+2;j++)

                            Em[i][j]=Em[i][j]-Em[k][j]*p1;

              }

       }

       coefficient[exp]=Em[exp][exp+1]/Em[exp][exp];

       for(int l=exp-1;l>=1;l--)   //回代求解

              coefficient[l]=(Em[l][exp+1]-F(coefficient,l+1,exp))/Em[l][l];

}

//供CalEquation函数调用

double F(double c[],int l,int m)

{

       double sum=0;

       for(int i=l;i<=m;i++)

              sum+=Em[l-1][i]*c[i];

       return sum;

}

 

实验结果与分析

讨论、心得(可选):

编程不是一日练成的,需要每天坚持,这样写代码才有感觉。编写一个大的系统往往很困难,但我们可以把它逐步细化、分解成一个个小的模块,然后精细的设计每一个小的模块,采用自顶向下,逐步细化的方法。遇到不懂的问题,查查等级高的博客,往往会有新思路和高效的解法。c/c++中包含很多库函数,不必要每个都记住,用的时候查询STL就行了。

在程序设计中,和同学多多交流,会有意想不到的收获,例如:两种方法,会有更优的解决方案。

现在越来越体会到数值计算方法和我们的生活很贴近,非常方便有用。


猜你喜欢

转载自blog.csdn.net/qq_37437892/article/details/81056056