学校课程要求
当然上述代码各大网站已经漫天飞了,随便搜几个回来自己整合一下轻松就完成了,放几个我参考的文章:
雅各比迭代法: https://blog.csdn.net/xiaowei_cqu/article/details/8585703
Gausss-Seidel迭代法: https://blog.csdn.net/qq_27508477/article/details/83152993
高斯消元法: https://blog.csdn.net/qq_26025363/article/details/53027926
但是整合起来的代码风格各异,c风格的c++代码、各种幻数、冗余的表达等等全部挤在一个cpp文件里,看得人眼花缭乱、头晕目眩。于是借此机会正好可以熟悉练习一下之前阅读《c++程序设计语言》----Bjarne Stroustrup,4th edition 学到的面向对象的程序排布和模块化编程,正好最近也在做一个项目需要用到这些好的方法。
我先定义了两个基类 IterativeMethod 和 GaussEli 分别用来存放两种迭代法(Jacobi和Gauss-Seidel)和高斯消元法的算法代码并留出接口用来输入矩阵和精度参数,然后在main函数的文件定义了类 deploy 来进行方程的参数输入、存放和方程解的调用,类 deploy继承自上述两个类 IterativeMethod 和 GaussEli ,所以可以直接调用基类的算法,从自己变量中储存的数据中输入算法所需参数,并输出结果。
下面上c++代码:
1 //"FieldPractice.cpp" 2 //This file includes function main, the program starts here 3 #include"GaussEli.h" 4 #include"IterativeMethod.h" 5 #include<cstdlib> 6 #include<vector> 7 //Class that deploys all the methods 8 class deploy:IterativeMethod,GaussEli { 9 public: 10 //Set up basic matrix 11 deploy(double deli):delicacy(deli) { 12 cout << "input the number of unknows n:"; 13 cin >> n; 14 cout << endl; 15 vector<vector<double> > A1(n, vector<double>(n, 0)); 16 A = A1; 17 vector<double> B1(n, 0); 18 B = B1; 19 cout << "input the matrix:" << endl; 20 for (int i = 0; i < n; i++) { 21 for (int j = 0; j < n; j++) { 22 cin >>A[i][j]; 23 } 24 } 25 cout << endl; 26 cout << "input b vector:" << endl; 27 for (int k = 0; k < n; k++) { 28 cin >> B[k]; 29 } 30 cout << endl; 31 cout << "formula to break:" << endl; 32 for (int a = 0; a < n; a++) { 33 for (int b = 0; b < n; b++) { 34 cout << A[a][b] << " "; 35 } 36 cout << " " << B[a] << endl; 37 } 38 cout << endl; 39 }; 40 41 void jacobi() { 42 Jacobi(A, B, n, delicacy); 43 } 44 void gauss() { 45 Gauss(A, B, n, delicacy); 46 } 47 void gauss_direct() { 48 Gaussin(A, B, n, delicacy); 49 } 50 private: 51 int n; 52 double delicacy; 53 vector<vector<double> > A; 54 vector<double> B; 55 }; 56 57 int main() { 58 //Input accuracy factor 59 deploy dm(pow(10,-7)); 60 //Iterative method 61 dm.jacobi(); 62 dm.gauss(); 63 //Direct method 64 dm.gauss_direct(); 65 return 0; 66 }
1 //"GaussEli.h" 2 //This file contains the declaration of the class DirectMethod 3 #pragma once 4 #include<vector> 5 #include<iostream> 6 #include<cmath> 7 #define Length 10 8 using namespace std; 9 class GaussEli { 10 public: 11 GaussEli() {}; 12 ~GaussEli() {}; 13 void Gaussin(vector<vector<double> > A, vector<double> B, int n, double epslong); 14 private: 15 16 };
1 #pragma once 2 //"GaussEli.cpp" 3 //This file contains the definition of "GaussEli.h" 4 #include"GaussEli.h" 5 #include<iostream> 6 using namespace std; 7 8 void GaussEli::Gaussin(vector<vector<double> > a, vector<double> b, int n, double epslong) 9 { 10 cout << endl << endl << "Gaussin Elimination:" << endl; 11 //判断能否用高斯消元法,如果矩阵主对角线上有0元素存在是不能用的 12 for (int i = 0; i < n; i++) 13 if (a[i][i] == 0) 14 { 15 cout << "can't use gaussin method" << endl; 16 return; 17 } 18 19 int i, j, k; 20 vector<double> c(n,0); //存储初等行变换的系数,用于行的相减 21 //消元的整个过程如下,总共n-1次消元过程。 22 for (k = 0; k < n - 1; k++) 23 { 24 //求出第K次初等行变换的系数 25 for (i = k + 1; i < n; i++) 26 c[i] = a[i][k] / a[k][k]; 27 28 //第K次的消元计算 29 for (i = k + 1; i < n; i++) 30 { 31 for (j = 0; j < n; j++) 32 { 33 a[i][j] = a[i][j] - c[i] * a[k][j]; 34 } 35 b[i] = b[i] - c[i] * b[k]; 36 } 37 } 38 39 //解的存储数组 40 vector<double> x(n, 0); 41 //先计算出最后一个未知数; 42 x[n - 1] = b[n - 1] / a[n - 1][n - 1]; 43 //求出每个未知数的值 44 for (i = n - 2; i >= 0; i--) 45 { 46 double sum = 0; 47 for (j = i + 1; j < n; j++) 48 { 49 sum += a[i][j] * x[j]; 50 } 51 x[i] = (b[i] - sum) / a[i][i]; 52 } 53 54 cout << " the solution of the equations is:" << endl; 55 cout << endl; 56 for (i = 0; i < n; i++) 57 cout << "x" << i + 1 << "=" << x[i] << endl; 58 59 }
1 #pragma once 2 //"IterativeMethod.h" 3 //This file contains the declaration of the class IterativeMethod 4 #include<iomanip> 5 #include<string> 6 #include<vector> 7 #include<iostream> 8 #include<cmath> 9 using namespace std; 10 class IterativeMethod { 11 public: 12 IterativeMethod() {}; 13 ~IterativeMethod() {}; 14 void Jacobi(vector<vector<double> > A, vector<double> B, int n, double epslong); 15 void Gauss(vector<vector<double> > A, vector<double> B, int n, double epslong); 16 private: 17 double MaxOfList(vector<double> x); 18 };
1 //"IterativeMethod.cpp" 2 //This file contains the definition of "ItearativeMethod.h" 3 #include"IterativeMethod.h" 4 5 //函数求数组中的最大值 6 double IterativeMethod::MaxOfList(vector<double>x) { 7 double max = x[0]; 8 int n = x.size(); 9 for (int i = 0; i < n; i++) 10 if (x[i] > max) max = x[i]; 11 return max; 12 } 13 14 //雅可比迭代公式 15 void IterativeMethod::Jacobi(vector<vector<double> > A, vector<double> B, int n, double epslong) { 16 vector<double> X(n, 0); 17 vector<double> Y(n, 0); 18 vector<double> D(n, 0); 19 cout << "Jacobi:" << endl; 20 int k = 0; //记录循环次数 21 do { 22 X = Y; 23 for (int i = 0; i < n; i++) { 24 double tem = 0; 25 for (int j = 0; j < n; j++) { 26 if (i != j) tem += A[i][j] * X[j]; 27 } 28 Y[i] = (B[i] - tem) / A[i][i]; 29 cout << left << setw(8) << Y[i] << " "; 30 } 31 cout << endl; 32 k++; 33 if (k > 100) { 34 cout << "Failure" << endl; 35 return; 36 } 37 38 for (int a = 0; a < n; a++) { 39 D[a] = X[a] - Y[a]; 40 } 41 } while (MaxOfList(D) > epslong || MaxOfList(D) < -epslong); 42 43 return; 44 } 45 //Gauss-Sediel method 46 void IterativeMethod::Gauss(vector<vector<double> > A, vector<double> B, int n, double epslong) { 47 vector<double> X(n, 0); 48 vector<double> X1(n, 0); 49 vector<double> Y(n, 0); 50 vector<double> D(n, 0); 51 cout << "Gauss:" << endl; 52 int k = 0; //记录循环次数 53 do { 54 X1 = Y; 55 for (int i = 0; i < n; i++) { 56 double tem = 0; 57 for (int j = 0; j < n; j++) { 58 if (i != j) tem += A[i][j] * X[j]; 59 } 60 Y[i] = (B[i] - tem) / A[i][i]; 61 X = Y; 62 cout << left << setw(8) << Y[i] << " "; 63 } 64 cout << endl; 65 k++; 66 if (k > 100) { 67 cout << "Failure!" << endl; 68 return; 69 } 70 71 for (int a = 0; a < n; a++) { 72 D[a] = X1[a] - Y[a]; 73 } 74 } while (MaxOfList(D) > epslong || MaxOfList(D) < -epslong); 75 76 return; 77 }
这样的写法比起原来有几个优点:一是分离了算法和方程矩阵的储存,如果后续想加入新的算法也不用重新写一遍数据输入或者重新输入数据,直接用deploy类再调用新的算法类即可;二是将方程矩阵相关的数据存储和输入全部封装到一个类型中,并且输入的过程直接写在 deploy 类的构造函数,遵循了RAII原则(资源获取即初始化),可以避免一些潜在的错误发生;三是如果程序想要求解多个方程而不是一个,可以直接生成新的 deploy 实体即可,避免了按照代码原来的写法还要繁琐的定义一大堆变量的情况;还有模块化编程在debug时候的便利性我就不在此赘述了。
其实上述程序还有可以进一步改进的空间,应该在类 deploy 中增加变量以储存方程的解,并通过调用算法函数的返回值获得解,再进一步进行输出。换句话说,程序的输出即方程的解可以储存在派生类中,而不是代表算法的基类,算法应该只进行计算而其他部分应该留给别的模块来执行,这样更能体现模块化编程的思想。不过这也只是课程作业的一个随笔,顺手记录,余下的部分就交给有兴趣的读者自行完成了。