視覺slam十四講課后習題ch3-6


題目回顧:

一般解線性方程Ax=b有哪幾種做法?你能在Eigen中實現嗎?

解: 線性方程組Ax = b的解法 :
1、直接法:(1,2,3,4,5)
2、迭代法:如Jacobi迭代法(6)
其中只有2 3方法不要求方程組個數與變量個數相等

下面簡略說明下Jacobi迭代算法:
由迭代法求解線性方程組的基本思想是將聯立方程組的求解歸結為重復計算一組彼此獨立的線性表達式,這就使問題得到了簡化,類似簡單迭代法轉換方程組中每個方程式可得到雅可比迭代式
迭代法求解方程組有一定的局限性,比如下面Jacobi函數程序實現的過程中,會判斷迭代矩陣的譜半徑是不是小於1,如果小於1表示Jacobi迭代法收斂,如果求不出來迭代矩陣即D矩陣不可逆的話,無法通過收斂的充要條件來判斷是不是收斂,此時可以試着迭代多次,看看輸出結果是否收斂,此時Jacobi迭代算法並不一定收斂,只能試着做下,下面的程序實現過程僅僅處理了充要條件,迭代法同時有十分明顯的優點——算法簡單,因而編制程序比較容易,所以在實際求解問題中仍有非常大利用價值。

具體代碼實現 如下:

  1 #include<iostream>
  2 #include<ctime>
  3 #include <cmath>
  4 #include <complex>
  5 /*                線性方程組Ax = b的解法 ( 直接法(1,2,3,4,5)+迭代法(6) )  其中只有2 3方法不要求方程組個數與變量個數相等   */
  6 
  7 //包含Eigen頭文件
  8 //#include <Eigen/Dense>
  9 #include<Eigen/Core>
 10 #include<Eigen/Geometry>
 11 #include <Eigen/Eigenvalues>
 12 
 13 //下面這兩個宏的數值一樣的時候 方法1 4 5 6才能正常工作
 14 #define MATRIX_SIZE 3   //方程組個數
 15 #define MATRIX_SIZE_ 3  //變量個數
 16 //using namespace std;
 17 typedef  Eigen::Matrix<double,MATRIX_SIZE, MATRIX_SIZE_>  Mat_A;
 18 typedef  Eigen::Matrix<double ,MATRIX_SIZE,1 >              Mat_B;
 19 
 20 //Jacobi迭代法的一步求和計算
 21 double Jacobi_sum(Mat_A   &A,Mat_B   &x_k,int i);
 22 
 23 //迭代不收斂的話 解向量是0
 24 Mat_B Jacobi(Mat_A   &A,Mat_B   &b,  int &iteration_num, double &accuracy );
 25 
 26 int main(int argc,char **argv)
 27 {
 28     //設置輸出小數點后3位
 29     std::cout.precision(3);
 30     //設置變量
 31     Eigen::Matrix<double,MATRIX_SIZE, MATRIX_SIZE_> matrix_NN = Eigen::MatrixXd::Random(MATRIX_SIZE,MATRIX_SIZE_);
 32     Eigen::Matrix<double ,MATRIX_SIZE,1 > v_Nd = Eigen::MatrixXd::Random(MATRIX_SIZE,1);
 33 
 34     //測試用例
 35     matrix_NN << 10,3,1,2,-10,3,1,3,10;
 36     v_Nd <<14,-5,14;
 37 
 38     //設置解變量
 39     Eigen::Matrix<double,MATRIX_SIZE_,1>x;
 40 
 41     //時間變量
 42     clock_t tim_stt = clock();
 43 
 44 /*1、求逆法      很可能沒有解 僅僅針對方陣才能計算*/
 45 #if (MATRIX_SIZE == MATRIX_SIZE_)
 46     x = matrix_NN.inverse() * v_Nd;
 47     std::cout<<"直接法所用時間和解為:"<< 1000*(clock() - tim_stt)/(double)CLOCKS_PER_SEC
 48         <<"MS"<< std::endl << x.transpose() << std::endl;
 49 #else
 50     std::cout<<"直接法不能解!(提示:直接法中方程組的個數必須與變量個數相同,需要設置MATRIX_SIZE == MATRIX_SIZE_)"<<std::endl;
 51 #endif
 52 
 53 /*2、QR分解解方程組 適合非方陣和方陣 當方程組有解時的出的是真解,若方程組無解得出的是近似解*/
 54     tim_stt = clock();
 55     x = matrix_NN.colPivHouseholderQr().solve(v_Nd);
 56     std::cout<<"QR分解所用時間和解為:"<<1000*(clock() - tim_stt)/(double)CLOCKS_PER_SEC
 57         << "MS" << std::endl << x.transpose() << std::endl;
 58 
 59 /*3、最小二乘法 適合非方陣和方陣,方程組有解時得出真解,否則是最小二乘解(在求解過程中可以用QR分解 分解最小二成的系數矩陣) */
 60     tim_stt = clock();
 61     x = (matrix_NN.transpose() * matrix_NN ).inverse() * (matrix_NN.transpose() * v_Nd);
 62     std::cout<<"最小二乘法所用時間和解為:"<< 1000*(clock() - tim_stt)/(double)CLOCKS_PER_SEC
 63         << "MS" << std::endl  << x.transpose() << std::endl;
 64 
 65 /*4、LU分解方法    只能為方陣(滿足分解的條件才行)    */
 66 #if (MATRIX_SIZE == MATRIX_SIZE_)
 67     tim_stt = clock();
 68     x = matrix_NN.lu().solve(v_Nd);
 69     std::cout<< "LU分解方法所用時間和解為:" << 1000*(clock() - tim_stt)/(double)CLOCKS_PER_SEC
 70         << "MS" << std::endl << x.transpose() << std::endl;
 71 #else
 72     std::cout<<"LU分解法不能解!(提示:直接法中方程組的個數必須與變量個數相同,需要設置MATRIX_SIZE == MATRIX_SIZE_)"<<std::endl;
 73 #endif
 74 
 75 /*5、Cholesky 分解方法  只能為方陣 (結果與其他的方法差好多)*/
 76 #if (MATRIX_SIZE == MATRIX_SIZE_)
 77     tim_stt = clock();
 78     x = matrix_NN.llt().solve(v_Nd);
 79     std::cout<< "Cholesky 分解方法所用時間和解為:" << 1000*(clock() - tim_stt)/(double)CLOCKS_PER_SEC
 80         << "MS"<< std::endl<< x.transpose()<<std::endl;
 81 #else
 82     std::cout<< "Cholesky法不能解!(提示:直接法中方程組的個數必須與變量個數相同,需要設置MATRIX_SIZE == MATRIX_SIZE_)"<<std::endl;
 83 #endif
 84 
 85 /*6、Jacobi迭代法   */
 86 #if (MATRIX_SIZE == MATRIX_SIZE_)
 87     int Iteration_num = 10 ;
 88     double Accuracy =0.01;
 89     tim_stt = clock();
 90     x= Jacobi(matrix_NN,v_Nd,Iteration_num,Accuracy);
 91     std::cout<< "Jacobi 迭代法所用時間和解為:" << 1000*(clock() - tim_stt)/(double)CLOCKS_PER_SEC
 92         << "MS"<< std::endl<< x.transpose()<<std::endl;
 93 #else
 94     std::cout<<"LU分解法不能解!(提示:直接法中方程組的個數必須與變量個數相同,需要設置MATRIX_SIZE == MATRIX_SIZE_)"<<std::endl;
 95 #endif
 96 
 97     return 0;
 98 }
 99 
100 //迭代不收斂的話 解向量是0
101 Mat_B Jacobi(Mat_A  &A,Mat_B  &b, int &iteration_num, double &accuracy ) {
102     Mat_B x_k = Eigen::MatrixXd::Zero(MATRIX_SIZE_,1);//迭代的初始值
103     Mat_B x_k1;         //迭代一次的解向量
104     int k,i;            //i,k是迭代算法的循環次數的臨時變量
105     double temp;        //每迭代一次解向量的每一維變化的模值
106     double R=0;         //迭代一次后,解向量每一維變化的模的最大值
107     int isFlag = 0;     //迭代要求的次數后,是否滿足精度要求
108 
109     //判斷Jacobi是否收斂
110     Mat_A D;            //D矩陣
111     Mat_A L_U;          //L+U
112     Mat_A temp2 = A;    //臨時矩陣獲得A矩陣除去對角線后的矩陣
113     Mat_A B;            //Jacobi算法的迭代矩陣
114     Eigen::MatrixXcd EV;//獲取矩陣特征值
115     double maxev=0.0;   //最大模的特征值
116     int flag = 0;       //判斷迭代算法是否收斂的標志 1表示Jacobi算法不一定能收斂到真值
117 
118     std::cout<<std::endl<<"歡迎進入Jacobi迭代算法!"<<std::endl;
119     //對A矩陣進行分解 求取迭代矩陣 再次求取譜半徑 判斷Jacobi迭代算法是否收斂
120     for(int l=0 ;l < MATRIX_SIZE;l++){
121         D(l,l) = A(l,l);
122         temp2(l,l) = 0;
123         if(D(l,l) == 0){
124             std::cout<<"迭代矩陣不可求"<<std::endl;
125             flag =1;
126             break;
127         }
128     }
129     L_U = -temp2;
130     B = D.inverse()*L_U;
131 
132     //求取特征值
133     Eigen::EigenSolver<Mat_A>es(B);
134     EV = es.eigenvalues();
135 //    cout<<"迭代矩陣特征值為:"<<EV << endl;
136 
137     //求取矩陣的特征值 然後獲取模最大的特徵值 即爲譜半徑
138     for(int index = 0;index< MATRIX_SIZE;index++){
139         maxev = ( maxev > __complex_abs(EV(index)) )?maxev:(__complex_abs(EV(index)));
140     }
141     std::cout<< "Jacobi迭代矩陣的譜半徑為:"<< maxev<<std::endl;
142 
143     //譜半徑大於1 迭代法則發散
144     if(maxev >= 1){
145         std::cout<<"Jacobi迭代算法不收斂!"<<std::endl;
146         flag =1;
147     }
148 
149     //迭代法收斂則進行迭代的計算
150     if (flag == 0 ){
151         std::cout<<"Jacobi迭代算法譜半徑小於1,該算法收斂"<<std::endl;
152         std::cout<<"Jacobi迭代法迭代次數和精度: "<< std::endl << iteration_num<<" "<<accuracy<<std::endl;
153 
154         //迭代計算
155         for( k = 0 ;k < iteration_num ; k++ ){
156             for(i = 0;i< MATRIX_SIZE_ ; i++){
157                 x_k1(i) = x_k(i) + ( b(i) - Jacobi_sum(A,x_k,i) )/A(i,i);
158                 temp = fabs( x_k1(i) - x_k(i) );
159                 if( fabs( x_k1(i) - x_k(i) ) > R )
160                     R = temp;
161             }
162 
163             //判斷進度是否達到精度要求 達到進度要求后 自動退出
164             if( R < accuracy ){
165                 std::cout <<"Jacobi迭代算法迭代"<< k << "次達到精度要求."<< std::endl;
166                 isFlag = 1;
167                 break;
168             }
169 
170             //清零R,交換迭代解
171             R = 0;
172             x_k = x_k1;
173         }
174         if( !isFlag )
175             std::cout << std::endl <<"迭代"<<iteration_num<<"次后仍然未達到精度要求,若不滿意該解,請再次運行加大循環次數!"<< std::endl;
176         return x_k1;
177     }
178     //否則返回0
179     return  x_k;
180 }
181 
182 //Jacobi迭代法的一步求和計算
183 double Jacobi_sum(Mat_A  &A,Mat_B &x_k,int i) {
184     double sum;
185     for(int j = 0; j< MATRIX_SIZE_;j++){
186         sum += A(i,j)*x_k(j);
187     }
188     return sum;
189 }

 

歡迎大家關注我的微信公眾號「佛系師兄」,里面有關於 Ceres 以及 OpenCV 庫等一些技術文章。

比如

反復研究好幾遍,我才發現關於 CMake 變量還可以這樣理解!

更多好的文章會優先在里面不定期分享!打開微信客戶端,掃描下方二維碼即可關注!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM