前言
在實際項目的一些矩陣運算模塊中,往往需要對線性方程組進行求解以得到最終結果。
然而,你無法讓計算機去使用克萊默法則或者高斯消元法這樣的純數學方法來進行求解。
計算機解決這個問題的方法是迭代法。本文將介紹三種最為經典的迭代法並用經典C++源代碼實現之。
迭代法簡介
從解的某個近似值出發,通過構造一個無窮序列去逼近精確解的方法。
雅克比迭代法
計算流程:
1. 初始化系數矩陣等計算環境
2. 設置精度控制和迭代次數控制變量
3. 采用如下式子進行迭代計算:
4. 循環執行 3,若(條件a)當前滿足迭代精度控制的解分量數等於解向量維數或者(條件b)迭代次數達到最大次數則跳出循環,進入到 5。
5. 若是因為條件a跳出循環則迭代成功,打印解向量;若是因為條件b退出循環則表示在指定的迭代次數內方程組未求解完。
說明:最經典原始的一種解方程組的迭代法。
高斯迭代法
計算流程:
1. 初始化系數矩陣等計算環境
2. 設置精度控制和迭代次數控制變量
3. 采用如下式子進行迭代計算:
4. 循環執行 3,若(條件a)當前滿足迭代精度控制的解分量數等於解向量維數或者(條件b)迭代次數達到最大次數則跳出循環,進入到 5。
5. 若是因為條件a跳出循環則迭代成功,打印解向量;若是因為條件b退出循環則表示在指定的迭代次數內方程組未求解完。
說明:相對於雅克比迭代法,該方法不需要一個額外的輔助向量保存上一次迭代計算的結果,節省了空間。
超松弛迭代法
計算流程:
1. 初始化系數矩陣等計算環境
2. 設置精度控制和迭代次數控制變量以及松弛因子omiga
3. 采用如下式子進行迭代計算:
4. 循環執行 3,若(條件a)當前滿足迭代精度控制的解分量數等於解向量維數或者(條件b)迭代次數達到最大次數則跳出循環,進入到 5。
5. 若是因為條件a跳出循環則迭代成功,打印解向量;若是因為條件b退出循環則表示在指定的迭代次數內方程組未求解完。
說明:
1. 該方法同樣不需要一個額外的輔助向量保存上一次迭代計算的結果。
2. 需要設置一個w因子參數,一般取0-2。當小於1時該方法為低松弛迭代法,高於1時為超松弛迭代法。經驗上來看一般取1.4-1.6來實現超松弛迭代。
源代碼實現
第一部分:迭代類聲明 (Iterator.h)
1 // 頭文件保護符 2 #ifndef Iterator_H 3 #define Iterator_H 4 5 // 迭代計算類 6 class CIterator 7 { 8 public: 9 // 構造函數 10 CIterator (); 11 // 析構函數 12 ~CIterator (); 13 // 設置計算環境 (如系數矩陣等) 14 bool SetEnvironment (); 15 // 雅克比迭代法計算方程解 16 bool JacobianCalculate (); 17 // 高斯迭代法計算方程解 18 bool GaussCalculate (); 19 // 超松弛迭代法計算方程解 20 bool RelaxationCalculate (double omiga); 21 // 獲取系數矩陣 22 double ** GetMatrixA (); 23 // 獲取系數矩陣的階 24 int GetOrder (); 25 // 獲取方程組右值向量 26 double * GetVectorB (); 27 // 獲取方程解向量 28 double * GetSolution (); 29 30 private: 31 double **matrixA; // 系數矩陣 32 int order; // 系數矩陣的階 33 double *vectorB; // 方程組右值向量 34 double *solution; // 解向量 35 }; 36 37 #endif
第二部分:迭代類實現 (Iterator.cpp)
1 #include "Iterator.h" 2 #include <iostream> 3 #include <iomanip> 4 5 using namespace std; 6 7 // 構造函數 8 CIterator :: CIterator () 9 { 10 matrixA = NULL; 11 order = 0; 12 vectorB = NULL; 13 solution = NULL; 14 } 15 16 // 析構函數 17 CIterator :: ~CIterator () 18 { 19 // 釋放內存空間 20 if (!vectorB) { 21 delete [] vectorB; 22 vectorB = NULL; 23 } 24 if (!solution) { 25 delete [] solution; 26 solution = NULL; 27 } 28 if (matrixA!=NULL) { 29 for (int i=0; i<order; i++) { 30 delete [] matrixA[i]; 31 matrixA[i] = NULL; 32 } 33 delete [] matrixA; 34 matrixA = NULL; 35 } 36 } 37 38 // 設置計算環境 (如系數矩陣等) 39 bool CIterator :: SetEnvironment () 40 { 41 cout << "系數矩陣階數: "; 42 cin >> order; 43 cout << endl; 44 45 matrixA = new double *[order]; 46 for (int i=0; i<order; i++) 47 matrixA[i] = new double [order]; 48 49 for (int i=0; i<order; i++) { 50 cout << "第 " << i << " 行系數: "; 51 for (int j=0; j<order; j++) 52 cin >> matrixA[i][j]; 53 } 54 cout << endl; 55 56 vectorB = new double [order]; 57 cout << "b 向量: "; 58 for (int i=0; i<order; i++) 59 cin >> vectorB[i]; 60 cout << endl; 61 62 solution = new double [order]; 63 64 cout << "運算環境搭建完畢" << endl << endl; 65 66 return true; 67 } 68 69 // 雅克比迭代法計算方程解 70 bool CIterator :: JacobianCalculate () 71 { 72 cout << "下面使用雅克比迭代法計算該線性方程組" << endl << endl; 73 74 // 設置迭代精度控制值 75 cout << "迭代精度控制值: "; 76 double bias; 77 cin >> bias; 78 cout << endl; 79 80 // 設置迭代次數控制值 81 cout << "迭代次數控制值: "; 82 int itMax; 83 cin >> itMax; 84 cout << endl; 85 86 // 當前滿足迭代精度控制的解分量數 87 int meetPrecisionCount = 0; 88 89 // 輔助向量,存放上一次迭代的解向量。 90 double * auxiliary = new double [order]; 91 92 // 初始化解向量 93 for (int i=0; i<order; i++) { 94 auxiliary[i] = 0; 95 solution[i] = 1; 96 } 97 98 // 當前迭代次數 99 int itCur = 0; 100 101 // 若當前滿足迭代精度控制的解分量數等於解向量維數或者迭代次數達到最大次數則跳出循環 102 while (meetPrecisionCount<order && itCur<itMax) { 103 104 // 當前滿足迭代精度控制的解分量數清零 105 meetPrecisionCount = 0; 106 107 // 將當前解向量存入輔助向量 108 for (int i=0; i<order; i++) 109 auxiliary[i] = solution[i]; 110 111 // 計算新的解向量 112 for (int i=0; i<order; i++) { 113 114 // 暫存當前解分量 115 double temp = solution[i]; 116 117 // 清零當前解分量 118 solution[i] = 0; 119 120 // 雅克比迭代計算 121 for (int j=0; j<i; j++) { 122 solution[i] += matrixA[i][j]*auxiliary[j]; 123 } 124 for (int j=i+1; j<order; j++) { 125 solution[i] += matrixA[i][j]*auxiliary[j]; 126 } 127 solution[i] = (vectorB[i]-solution[i])/matrixA[i][i]; 128 129 // 更新當前滿足迭代精度控制的解分量數 130 if (fabs(temp-solution[i])<bias) 131 meetPrecisionCount++; 132 } 133 134 // 當前迭代次數遞增 135 itCur++; 136 } 137 138 // 若在規定的迭代次數內未完成計算任務,則返回錯誤。 139 if (itCur == itMax) return false; 140 141 return true; 142 } 143 144 // 高斯迭代法計算方程解 145 bool CIterator :: GaussCalculate () 146 { 147 cout << "下面使用高斯迭代法計算該線性方程組" << endl << endl; 148 149 // 設置迭代精度控制值 150 cout << "迭代精度控制值: "; 151 double bias; 152 cin >> bias; 153 cout << endl; 154 155 // 設置迭代次數控制值 156 cout << "迭代次數控制值: "; 157 int itMax; 158 cin >> itMax; 159 cout << endl; 160 161 // 當前滿足迭代精度控制的解分量數 162 int meetPrecisionCount = 0; 163 164 // 初始化解向量 165 for (int i=0; i<order; i++) { 166 solution[i] = 0; 167 } 168 169 // 當前迭代次數 170 int itCur = 0; 171 172 // 若當前滿足迭代精度控制的解分量數等於解向量維數或者迭代次數達到最大次數則跳出循環 173 while (meetPrecisionCount<order && itCur<itMax) { 174 175 // 當前滿足迭代精度控制的解分量數清零 176 meetPrecisionCount = 0; 177 178 // 計算新的解向量 179 for (int i=0; i<order; i++) { 180 181 // 暫存當前解分量 182 double temp = solution[i]; 183 184 // 清零當前解分量 185 solution[i] = 0; 186 187 // 高斯迭代計算 188 for (int j=0; j<i; j++) { 189 solution[i] += matrixA[i][j]*solution[j]; 190 } 191 for (int j=i+1; j<order; j++) { 192 solution[i] += matrixA[i][j]*solution[j]; 193 } 194 solution[i] = (vectorB[i]-solution[i])/matrixA[i][i]; 195 196 // 更新當前滿足迭代精度控制的解分量數 197 if (fabs(temp-solution[i])<bias) 198 meetPrecisionCount++; 199 } 200 201 // 當前迭代次數遞增 202 itCur++; 203 } 204 205 // 若在規定的迭代次數內未完成計算任務,則返回錯誤。 206 if (itCur == itMax) return false; 207 208 return true; 209 } 210 211 // 超松弛迭代法計算方程解 212 bool CIterator :: RelaxationCalculate (double omiga) 213 { 214 cout << "下面使用超松弛迭代法計算該線性方程組" << endl << endl; 215 216 // 設置迭代精度控制值 217 cout << "迭代精度控制值: "; 218 double bias; 219 cin >> bias; 220 cout << endl; 221 222 // 設置迭代次數控制值 223 cout << "迭代次數控制值: "; 224 int itMax; 225 cin >> itMax; 226 cout << endl; 227 228 // 當前滿足迭代精度控制的解分量數 229 int meetPrecisionCount = 0; 230 231 // 當前滿足迭代精度控制的解分量數 232 for (int i=0; i<order; i++) { 233 solution[i] = 0; 234 } 235 236 // 當前迭代次數 237 int itCur = 0; 238 239 // 若當前滿足迭代精度控制的解分量數等於解向量維數或者迭代次數達到最大次數則跳出循環 240 while (meetPrecisionCount<order && itCur<itMax) { 241 242 // 當前滿足迭代精度控制的解分量數清零 243 meetPrecisionCount = 0; 244 245 // 計算新的解向量 246 for (int i=0; i<order; i++) { 247 248 // 暫存當前解分量 249 double temp = solution[i]; 250 251 // 清零當前解分量 252 solution[i] = 0; 253 254 // 超松弛迭代計算 255 for (int j=0; j<i; j++) { 256 solution[i] += matrixA[i][j]*solution[j]; 257 } 258 for (int j=i+1; j<order; j++) { 259 solution[i] += matrixA[i][j]*solution[j]; 260 } 261 solution[i] = omiga*(vectorB[i]-solution[i])/matrixA[i][i] + (1-omiga)*temp; 262 263 // 更新當前滿足迭代精度控制的解分量數 264 if (fabs(temp-solution[i])<bias) 265 meetPrecisionCount++; 266 } 267 268 // 當前迭代次數遞增 269 itCur++; 270 } 271 272 // 若在規定的迭代次數內未完成計算任務,則返回錯誤。 273 if (itCur == itMax) return false; 274 275 return true; 276 } 277 278 // 獲取系數矩陣 279 double ** CIterator :: GetMatrixA () 280 { 281 return this->matrixA; 282 } 283 284 // 獲取系數矩陣的階 285 int CIterator :: GetOrder () 286 { 287 return this->order; 288 } 289 290 // 獲取方程組右值向量 291 double * CIterator :: GetVectorB () 292 { 293 return this->vectorB; 294 } 295 296 // 獲取方程解向量 297 double * CIterator :: GetSolution () 298 { 299 return this->solution; 300 }
第三部分:主函數 (main.cpp)
1 // 迭代計算類 2 #include "Iterator.h" 3 #include <iostream> 4 5 using namespace std; 6 7 // 打印方程解 8 void printResults (CIterator iterator); 9 10 int main() 11 { 12 // 定義迭代類對象 13 CIterator iterator; 14 15 // 設置計算環境 (如系數矩陣等) 16 iterator.SetEnvironment(); 17 18 // 雅克比迭代法計算方程解 19 if (!iterator.JacobianCalculate()) { 20 cout << "規定次數內未完成迭代計算" << endl; 21 return EXIT_FAILURE; 22 } 23 24 // 高斯迭代法計算方程解 25 /* 26 if (!iterator.GaussCalculate()) { 27 cout << "規定次數內未完成迭代計算" << endl; 28 return EXIT_FAILURE; 29 } 30 */ 31 32 // 超松弛迭代法計算方程解 33 /* 34 double omiga = 1.5; // 松弛迭代系數 35 if (!iterator.RelaxationCalculate(omiga)) { 36 cout << "規定次數內未完成迭代計算" << endl; 37 return EXIT_FAILURE; 38 } 39 */ 40 41 // 打印方程解 42 printResults (iterator); 43 44 return EXIT_SUCCESS; 45 } 46 47 // 打印方程解 48 void printResults (CIterator iterator) 49 { 50 cout << "計算成功,打印方程解: " << endl; 51 for (int i=0; i<iterator.GetOrder(); i++) 52 cout << "x" << i << " = " << iterator.GetSolution()[i] << endl; 53 54 cout << endl; 55 }
程序演示
使用超松弛迭代法解如下方程組:
將項目主函數中雅克比和高斯迭代計算的部分注釋掉,運行:
說明
不是每個方程組都能迭代得到解,我們通常將系數矩陣轉換為對角占優矩陣(右向量部分也要跟着轉)。滿足此條件的方程組的解向量大都能用這幾種迭代法算出來。