迭代法求線性方程的解
1. 實驗目標
1.設計簡單迭代、高斯、SOR算法
2.對比不同方法的計算效率
2. 程序設計
本實驗用C#編寫,簡單實現了雅可比、高斯-賽德爾迭代法、SOR迭代法等方法三種方法。三種方法抽象爲ISolution接口,即有迭代次數ItNum、迭代終止條件Diff和解法Solve。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IterEquation
{
///<summary>
///解法接口
///</summary>
interface ISolution
{
///<summary>
///誤差
///</summary>
List<double>Error {get; }
///<summary>
///迭代次數
///</summary>
int ItNum { get; set; }
///<summary>
///終止誤差
///</summary>
double Diff { get; set; }
///<summary>
///解法
///</summary>
///<param name="A"></param>
///<param name="B"></param>
///<returns></returns>
double[] Solve(double[,]A,double[] B,double[]x0);
}
}
雅可比迭代法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IterEquation
{
///<summary>
///雅可比迭代方法
///</summary>
class Jacobi:ISolution
{
private List<double> _error = newList<double>();
public List<double> Error
{
get
{
return _error;
}
}
private int _itNum =10;
public int ItNum
{
get
{
return _itNum;
}
set
{
_itNum = value;
}
}
private double _diff= 0.0001;
public double Diff
{
get
{
return _diff;
}
set
{
_diff = value;
}
}
public double[]Solve(double[,] A, double[]B,double[] x0)
{
int it = 0;
while (it < _itNum)
{
double[] x = newdouble[B.Length];
for (inti = 0; i < x.Length; i++)
{
doublesum = 0;
for(int j = 0; j < x.Length; j++)
{
if (i != j)
{
sum += A[i, j] *x0[j];
}
}
x[i] = (B[i] - sum) / A[i,i];
}
double diff = Util.Diff(x0,x);
_error.Add(diff);
//誤差判斷
if (diff < _diff)
break;
x0 = x;
it++;
}
return x0;
}
}
}
2.高斯賽德爾迭代法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IterEquation
{
///<summary>
///高斯塞德爾求解法
/// GaussSeidel
///</summary>
class GaussSeidel:ISolution
{
private List<double> _error = newList<double>();
public List<double> Error
{
get
{
return _error;
}
}
private int _itNum =10;
public int ItNum
{
get
{
return _itNum;
}
set
{
_itNum = value;
}
}
private double _diff= 0.0001;
public double Diff
{
get
{
return _diff;
}
set
{
_diff = value;
}
}
public double[]Solve(double[,] A, double[]B, double[] x0)
{
int it = 0;
while (it < _itNum)
{
double diff = 0;
for (inti = 0; i < x0.Length; i++)
{
doublesum = 0;
for(int j = 0; j < x0.Length; j++)
{
if(i != j)
{
sum += A[i, j] *x0[j];
}
}
doublenewVal = (B[i] - sum) / A[i, i];
diff += Math.Sqrt((newVal - x0[i])*(newVal - x0[i]));
x0[i] = (B[i] - sum) / A[i,i];
}
_error.Add(diff);
if (diff < _diff)
break;
it++;
}
return x0;
}
}
}
3. SOR法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IterEquation
{
///<summary>
///鬆弛法
///</summary>
class SOR:ISolution
{
private List<double> _error = newList<double>();
public List<double> Error
{
get
{
return _error;
}
}
private int _itNum =10;
public int ItNum
{
get
{
return _itNum;
}
set
{
_itNum = value;
}
}
private double _diff= 0.0001;
public double Diff
{
get
{
return _diff;
}
set
{
_diff = value;
}
}
///<summary>
///鬆弛係數
///</summary>
private double _omega= 1;
public double Omega
{
get { return_omega; }
set { _omega = value;}
}
public double[]Solve(double[,] A, double[]B, double[] x0)
{
int it = 0;
while (it < _itNum)
{
double diff = 0;
for (inti = 0; i < x0.Length; i++)
{
doublesum = 0;
for(int j = 0; j < x0.Length; j++)
{
if (i != j)
{
sum += A[i, j] *x0[j];
}
}
doublenewVal = x0[i]+_omega*((B[i] - sum) / A[i, i] - x0[i]);
diff += Math.Sqrt((newVal - x0[i]) * (newVal - x0[i]));
x0[i] = newVal
;
}
_error.Add(diff);
if (diff < _diff)
break;
it++;
}
return x0;
}
}
}
4. 窗體程序
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.IO;
namespace IterEquation
{
public partial class SolutionForm: Form
{
double[,] A;
double[] B;
double[] x0;
public SolutionForm()
{
InitializeComponent();
}
private voidJacobiButton_Click(object sender, EventArgs e)
{
Jacobi jsolu = new Jacobi();
jsolu.ItNum = Int32.Parse(itnum.Text);
jsolu.Diff = Double.Parse(error.Text);
double[] x = jsolu.Solve(A, B, x0);
axisDraw(jsolu.Error, "雅可比");
resUpdate(x);
}
private voidGaussSeidel_Click(object sender, EventArgs e)
{
GaussSeidel gsolu =new GaussSeidel();
gsolu.ItNum = Int32.Parse(itnum.Text);
gsolu.Diff = Double.Parse(error.Text);
double[] x = gsolu.Solve(A, B, x0);
axisDraw(gsolu.Error, "高斯-塞德爾");
resUpdate(x);
}
private voidSOR_Click(object sender, EventArgs e)
{
SOR gsolu = newSOR();
gsolu.ItNum = Int32.Parse(itnum.Text);
gsolu.Diff = Double.Parse(error.Text);
gsolu.Omega = Double.Parse(omega.Text);
double[] x = gsolu.Solve(A, B, x0);
axisDraw(gsolu.Error, "SOR");
resUpdate(x);
}
///<summary>
///繪製座標系
///</summary>
///<param name="x"></param>
///<param name="name"></param>
private voidaxisDraw(List<double>x,String name)
{
String newName = name;
int k = 1;
while (axis.Series.FindByName(newName)!=null)
{
newName = name + k;
k++;
}
axis.Series.Add(new Series(newName));
for(inti = 0;i < x.Count;i++)
{
axis.Series[newName].Points.AddXY(i, x[i]);
}
axis.Series[newName].ChartType = SeriesChartType.Line;
}
///<summary>
///結果更新
///</summary>
private voidresUpdate(double[] res)
{
String lableS = "";
for(inti = 0;i < res.Length;i++)
{
lableS += "x" + i +"=" + res[i] + "\n";
}
resLable.Text = lableS;
}
private voidClearInput_Click(object sender, EventArgs e)
{
axis.Series.Clear();
}
private voidequation_Click(object sender, EventArgs e)
{
ReadData();
}
private voidReadData()
{
try
{
StreamReader objReader =new StreamReader("equationData.txt");
string sLine = "";
objReader.ReadLine();
sLine = objReader.ReadLine();
int numOfX = Int32.Parse(sLine);
A = new double[numOfX,numOfX];
B = new double[numOfX];
x0 = new double[numOfX];
//輸入A
String[] s;
for (inti = 0; i < numOfX; i++)
{
sLine =objReader.ReadLine();
s = sLine.Split(',');
for(int j = 0; j < numOfX; j++)
{
A[i, j] = Double.Parse(s[j]);
}
}
//輸入B
objReader.ReadLine();
sLine = objReader.ReadLine();
s = sLine.Split(',');
for (inti = 0; i < numOfX; i++)
{
B[i] = Double.Parse(s[i]);
}
objReader.ReadLine();
sLine = objReader.ReadLine();
s = sLine.Split(',');
for (inti = 0; i < numOfX; i++)
{
x0[i] = Double.Parse(s[i]);
}
objReader.Close();
//表示
String InputText ="";
for (inti = 0; i < numOfX; i++)
{
for(int j = 0; j < numOfX; j++)
{
if (A[i, j] < 0)
{
InputText += A[i,j] + "x" + j;
}
else
{
if (i == 0)
{
InputText +=A[i, j] + "x" + j;
}
else
{
InputText += "+" + A[i, j] + "x"+ j;
}
}
}
//輸入等於號
InputText += "=" + B[i] +"\n";
}
//輸入初值
InputText += "\n";
for (inti = 0; i < numOfX; i++)
{
InputText += "x"+ i +"=" + x0[i] + ",";
}
inputEqual.Text = InputText;
}
catch (IOExceptioner)
{
Console.WriteLine("An IO exception has been thrown!");
Console.WriteLine(er.ToString());
Console.ReadLine();
return;
}
}
private voidSolutionForm_Load(object sender, EventArgs e)
{
ReadData();
}
}
}
5. 實驗分析
1.迭代法收斂效率對比
本文采用方程組在初值X1=X2=X3=0的條件下迭代10次進行測試,其中鬆弛條件1.2,其真值分別爲X1=11,X2=12,X3=13其收斂情況如下圖所示。
其中雅可比的解爲,X1=10.999364458,X2=11.999362259,X3=12.999244634,高斯賽德爾的解爲X1 = 10.999826484685, X2 = 11.9999893656629,X3 =12.99944028263, SOR的解爲X1=11.000021026236,X2=12.0000737550456,X3=12.9999719110308。
2.鬆弛係數值設置範圍
可以在此方程條件下看到高斯-賽德爾迭代法比雅可比迭代法迭代收斂更快,同時SOR法加快了高斯賽德爾迭代法的收斂速度。
針對SOR鬆弛係數的設置問題,從理論上來證明鬆弛係數應在0~2之間,部分方程必須小於等於1。在前面的條件下,分別取鬆弛度0.5,1,1.5,2四組數據對SOR進行求解如下圖所示。
我們可以看到結果正如推論所示,在前三個值條件下是收斂的,且在1的時候(即退化爲高斯-賽得爾迭代法)時收斂效果較好。當鬆弛係數等於2時,迭代不收斂。