文章目錄
數值代數
Gauss消元法
對於線性方程組
其矩陣形式爲,其中
所謂消去法,即逐次小區未知數的方法,將化爲與原式等價的三角方程組。若化爲如下形式
則易得求解公式
在Julia中可寫爲
# Gauss消去法
# M爲n×n+1矩陣,對應Ax=b,M[:,end]爲b
function GaussElimination(M)
M = copy(M)
n,_ = size(M)
# 消元過程
for i in 1:n-1
vMinus = M[i,:]/M[i,i] #當前行循環所要消去的變量
for j in i+1:n
M[j,:] -= vMinus*M[j,i]
end
end
# 回代過程
x = zeros(n)
for i in n:-1:1
sumValue = sum([M[i,j]*x[j] for j in i:n])
x[i] = (M[i,end]-sumValue)/M[i,i]
end
return x
end
驗證
julia> A = rand(10,10);
julia> x = rand(10);
julia> b = A*x;
julia> x1 = GaussElimination(hcat(A,b));#hcat函數爲矩陣拼接
julia> x1-x
10-element Array{Float64,1}:
3.3306690738754696e-15
-1.021405182655144e-14
5.495603971894525e-15
5.2735593669694936e-15
-5.10702591327572e-15
-8.604228440844963e-15
5.329070518200751e-15
1.0380585280245214e-14
-1.5836670082952642e-14
8.659739592076221e-15
矩陣的三角分解
在Gauss消元的過程中,我們構造了一個上三角矩陣用於回代,在這個過程中,每一步操作均類似於矩陣的初等變換,而初等初等變換又可以等效爲矩陣乘法,所以這些變換矩陣之間的乘積又構成了另一個矩陣。
即高斯消元過程爲
其中,爲上三角陣,則,令,則有。
通過Julia實現爲
# 生成單位矩陣
function eye(n)
M = zeros(n,n)
for i in 1:n
M[i,i] = 1
end
return M
end
# 矩陣的LU分解
function LU(M)
U = copy(M)
n,_ = size(M)
L = eye(n)
eleMatrix = eye(n)
# 初等變換過程
for i in 1:n-1
eleMatrix[i+1:end,i] = [-U[j,i]/U[i,i] for j in i+1:n]
U = eleMatrix*U
L = L/eleMatrix
eleMatrix[i+1:end,i] = [0 for j in i+1:n]
end
return L,U
end
驗證
julia> M = rand(4,4)
julia> L,U = LU(M);
julia> L
4×4 Array{Float64,2}:
1.0 0.0 0.0 0.0
0.0795445 1.0 0.0 0.0
0.686459 1.03633 1.0 0.0
0.327521 1.27914 0.659519 1.0
julia> U
4×4 Array{Float64,2}:
0.591548 0.102808 0.599782 0.25122
0.0 0.51307 0.902331 0.481771
0.0 0.0 -0.691109 0.258695
0.0 0.0 -5.55112e-17 -0.170634
julia> L*U-M
4×4 Array{Float64,2}:
0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0
0.0 1.11022e-16 0.0 0.0
Gauss 主元素消去法
Gauss消元公式表明,項不可爲0,否則將出現除零錯誤。非但如此,如果是一個小量,由於計算機程序在運行過程中,其位數有限,所以可能會造成較大誤差。
julia> A =rand(4,4)*10;
julia> A[1,1] = 1e-10;
julia> x = rand(4)*10;
julia> b = A*x;
julia> x1 = GaussElimination(hcat(A,b));
julia> x1-x
4-element Array{Float64,1}:
0.00014665020004134277
-0.00015594355145509553
1.9684886276571945e-6
1.596867372199995e-5
可以看到,其最大誤差已經達到了0.0001。
爲了避免這種誤差,最直觀的想法是,在Gauss消元的過程中,使得較大元素的值儘量靠前。對於增廣矩陣而言,若在中有最大元素,則可以將的第行,第列與第一行、第一列進行交換。對於交換後的矩陣,則選取除了第一行第一列之外的子矩陣繼續進行最大值的選取與交換,直至進行到最後一行。
其Julia實現爲
# M爲n×n+1增廣矩陣,對應Ax=b,M[:,end]爲b
# 輸出爲最大元調整後的矩陣和未知數的變化順序
function allPrinciple(M)
M = copy(M)
n,_ = size(M)
J = zeros(n-1) #保存x的交換信息
for i = 1:n-1
pos = findmax(M[i:end,i:end-1])[2]
ni = pos[1]+i-1; nj = pos[2]+i-1 #最大值所在位置
J[i] = nj
# 交換行
rowi = M[i,:]
M[i,:] = M[ni,:]
M[ni,:] = rowi
# 交換列
colj = M[:,i]
M[:,i] = M[:,nj]
M[:,nj] = colj
end
return M,J
end
驗證
julia> include("almatrix.jl");
julia> A = rand(4,4)*10;
julia> A[1,1] = 1e-10;
julia> x = rand(4)*10;
julia> b = A*x;
julia> M = hcat(A,b)
4×5 Array{Float64,2}:
1.0e-10 3.15827 6.67401 6.19187 91.509
1.33812 5.68155 1.95993 9.98016 91.6819
9.29672 1.16334 1.35398 2.7712 46.9376
5.32787 0.445123 3.25703 1.22906 41.3079
julia> M1,J=allPrinciple(M);
julia> M1
4×5 Array{Float64,2}:
9.98016 1.33812 1.95993 5.68155 91.6819
2.7712 9.29672 1.35398 1.16334 46.9376
6.19187 1.0e-10 6.67401 3.15827 91.509
1.22906 5.32787 3.25703 0.445123 41.3079
可見已經實現了最大元交換,接下來可將換元過程插入到Gauss消去法中,
#全主元素消去法
function allElimitation(M)
M,J = allPrinciple(M) #最大主元替換
x = GaussElimination(M) #高斯消去法
for i in length(J):-1:1 #未知數換位
print(J[i])
xi = x[J[i]]
x[J[i]] = x[i]
x[i] = xi
end
return x
end
```julia
驗證(接上面代碼)
```javascript
julia> include("almatrix.jl");
julia> x1 = GaussElimination(M);
julia> x2 = allElimitation(M);
julia> x1-x
4-element Array{Float64,1}:
-0.00024191569586218264
-0.0018563311857411335
-0.00014663572230411148
0.0011049087452414952
julia> x2-x
4-element Array{Float64,1}:
6.661338147750939e-16
2.1316282072803006e-14
-8.881784197001252e-16
-1.1546319456101628e-14
可見主元素消去法相對於原始的Gauss消去法提高了不少精度。
Gauss-Jordan消去法
Gauss消去法是一種從上到下的消元過程,即始終消去對角線下方的元素,最終使得係數矩陣變成上三角的形式。Gauss-Jordan消去法則還要消去對角線上方的元素,使得係數矩陣變成對角矩陣。
# Gauss-Jordan消去法
# M爲n×n+1增廣矩陣,對應Ax=b,M[:,end]爲b
function GaussJordan(M,maxFlag=true)
if maxFlag
M,J = allPrinciple(M) #最大主元替換
end
n,_ = size(M)
# Gauss消元
for i in 1:n-1
vMinus = M[i,:]/M[i,i] #當前行循環所要消去的變量
for j in i+1:n
M[j,:] -= vMinus*M[j,i]
end
end
# Jordan消元
for i in n:-1:2
vMinus = M[i,:]/M[i,i]
for j in 1:i-1
M[j,:] -= vMinus*M[j,i]
end
end
# 回代
x = [M[i,end]/M[i,i] for i in 1:n]
if maxFlag
for i in length(J):-1:1 #未知數換位
print(J[i])
xi = x[J[i]]
x[J[i]] = x[i]
x[i] = xi
end
end
return M,x
end
追趕法
在某些偏微分方程的求解算法中,在初值條件給定的情況下會產生一個特殊的方程求解問題,其係數矩陣爲三對角矩陣,其形式如下
此時,對A進行分解可得
易得其變換關係爲
根據上式可得到追趕法的求解過程
Step1 計算
step2 計算
step3 計算,即其追趕過程
其Julia實現爲
# 追趕法,輸入M爲三對角矩陣,f爲向量
function chasing(M,f)
n,_ = size(M)
b = [M[i,i] for i in 1:n]
c = [M[i,i+1] for i in 1:n-1]
a = vcat(1,[M[i+1,i] for i in 1:n-1])
# 根據遞推公式求beta
beta = zeros(n-1)
beta[1] = c[1]/b[1]
for i in 2:n-1
beta[i] = c[i]/(b[i]-a[i]*beta[i-1])
end
# 求解y
y = zeros(n)
y[1] = f[1]/b[1]
for i in 2:n
y[i] = (f[i]-a[i]*y[i-1])/(b[i]-a[i]*beta[i-1])
end
# x的追趕過程
x = zeros(n)
x[n] = y[n]
for i in n-1:-1:1
x[i] = y[i]-beta[i]*x[i+1]
end
return x
end
驗證
julia> M = zeros(5,5);
julia> for i in 1:5
M[i,i] = rand()*10
M[i,i+1] = rand()*3
M[i+1,i] = rand()*3
end #此處會報錯哦
julia> x = rand(5);
julia> f = M*x;
julia> x1 = chasing(M,f);
julia> x1-x
5-element Array{Float64,1}:
0.0
-2.220446049250313e-16
0.0
-1.1102230246251565e-16
0.0
Jocabi迭代法
對於方程組
若爲非奇異矩陣且任意項不爲零,則將分裂爲,其中爲對角矩陣,分別爲對焦爲0的下三角和上三角矩陣。
將方程組中的對角項提出,則方程組變爲
或將矩陣表達式寫爲
記爲
其中
通過迭代法求上式即可得到迭代公式
寫成矩陣形式即爲
其Julia實現爲
## Jocobi迭代法
## n爲迭代次數
function Jacobi(M,b,n=10)
nDim,_ = size(M)
D = zeros(nDim,nDim)
for i in 1:nDim
D[i,i] = M[i,i]
end
B = D\(D-M)
f = D\b
x = f #初始化
for i = 1:n
x = B*x+f
end
return x
end
Gauss-Seidel迭代
在Jacobi迭代中,被看成一個整體參與迭代,在每一次迭代的過程中,同一代的其內部的變量並無相互影響。
然而,如果迭代收斂,那麼新一代的一定比舊一代的更接近真實值,所以,如果將迭代過程拆分,讓新一代的計算結果實時取代舊一代的結果,那麼應該有助於加速收斂過程,此即Gauss-Seidel迭代。
如果寫出迭代公式的展開式可能會更加直觀
則Gauss-Seidel迭代可寫爲
矩陣特徵值與特徵向量
Gerschgorin定理
設,則的每一個特徵值必屬於下述圓盤之中
證明 設爲內任意一個特徵值,爲對應的特徵向量,則有
記以及,則
又因,則
冪法和反冪法
冪法時一種迭代算法,所以其基本表示形式必然爲,即任取一非零向量,通過矩陣構造一向量序列
若令爲相應的特徵向量,則
若令,則,則。即經過無窮多次迭代之後
此即矩陣的最大特徵值所對應的特徵向量,相鄰兩次迭代特徵向量之商即爲最大特徵值。這種方法即爲,與之相應地,由於其逆矩陣的特徵值變爲原矩陣的倒數,則對逆矩陣使用冪法,即可得到最小特徵向量,此方法即爲
# 乘冪法
function Power(M,n)
arr = rand(size(M)[1])
for i in 1:n-1
arr = M*arr
arr = arr/maximum(arr) #對向量進行歸一化
end
Arr = M*arr
return Arr,Arr./arr
end
驗證
julia> include("almatrix.jl");
julia> M = rand(5,5);
julia> Power(M,20)[2]
5-element Array{Float64,1}:
2.6429750659456106 #此即最大特徵值
2.6429750659461253
2.6429750659454907
2.6429750659466134
2.642975065945339
由於向量之間點除很難得到相同的值,所以我們選擇其中一點來觀察乘冪法的收斂速度。
令爲的第個分量,爲第次迭代誤差的第個分量,則
根據其誤差分量的特徵可知,當主特徵值遠大於其他特徵值時,將具備更快的收斂速度。若,則收斂速度將會很慢。所以,如果能夠使得與其他特徵值的差距變大,則可增快收斂速度。
對此,可以構造矩陣,則其特徵值爲,則的收斂速度大於,因爲
通過Julia寫爲
# 加速之後的冪法
function acPower(M,n,p)
B = M .- eye(size(M)[1])*p
arr,val = Power(B,n)
return arr,val.+p
end
測試
julia> x = rand(5,5);
julia> x[5,5] = 7;
julia> x[4,4] = 8;
julia> x[3,3] = 8.5;
julia> x[2,2] = 9.5;
julia> x[1,1] = 10;
julia> acPower(x,20,0)[2]
5-element Array{Float64,1}:
11.119295564775351
11.079539551635982
11.058127553729367
11.095918606361074
11.05404344237109
julia> acPower(x,15,5)[2]
5-element Array{Float64,1}:
11.090309381320136
11.094079685109394
11.09547046795793
11.094168499705209
11.094043676186722
可見,在p值選5的情況下,迭代15次的結果優於未加速時迭代20次的結果。(因爲這五個值都是主特徵向量,所以這五個值越相近說明越接近真實值。)
Schmidt正交分解
設是上的個線性無關的向量,令,則爲單位向量,再令,則。
同理,得到對任意向量組的正交化過程
這個過程可以表示爲矩陣形式
其中,Q爲正交陣,R爲上三角陣,上三角陣的對角元素即爲其特徵值。其Julia實現爲
# Schmidt正交化法,輸入爲矩陣,按每一列求其歸一化向量
function Schmidt(M)
n,_ = size(M)
N = zeros(n,n)
for i = 1:n
B = M[:,i]
for j in 1:i-1
B -= M[:,i]'*N[:,j]*N[:,j]
end
N[:,i] = B/sqrt(sum([B[j]^2 for j in 1:n]))
end
return N
end
驗證
julia> include("almatrix.jl");
julia> x = rand(5,5);
julia> y = Schmidt(x);
julia> for i in 1:5
println(y[:,1]'*y[:,i])
end
1.0000000000000002
-1.1102230246251565e-16
-3.3306690738754696e-16
-9.645062526431047e-16
-8.326672684688674e-16
Jacobi旋轉法
Jacobi法的基本思想是構造簡單、特殊的正交矩陣序列,使得
逐漸接近對角型,從而得到A的特徵值。
對於矩陣
其中,對角上的分別在第列,則該矩陣被稱爲平面旋轉矩陣。
設矩陣是對稱矩陣,記,對做一系列旋轉相似變換,得,則只在第行列的元素不同,且有如下關係
可以看到,我們可可以通過適當選取值,使得,通過不斷選取不同的可以逐漸使得矩陣中越來越多的元素爲零。
此時,需要滿足
# Jacobi旋轉法
function JacobiRot(M)
n,_=size(M)
E = eye(n)
for i = 1:n
for j = 1:n
p2Tan = -2*M[i,j]/M[i,i]-M[j,j]
p2Cos = 1/sqrt(1+p2Tan^2)
p2Sin = p2Tan*p2Cos
pCos = sqrt(p2Cos+1)/2
pSin = p2Sin/pCos/2
cii = M[i,i]*pCos^2+M[j,j]*pSin^2-M[i,j]*p2Sin
cjj = cii+2*M[i,j]*p2Sin
cij = (M[i,i]-M[j,j])*p2Sin/2+M[i,j]*p2Cos
ci = [M[i,k]*pCos-M[j,k]*pSin for k in 1:n]
cj = [M[j,k]*pCos+M[i,k]*pSin for k in 1:n]
ci[i] = cii;ci[j]=cij
cj[i] = cij;cj[j]=cjj
M[i,:] = ci;M[:,i] = ci
M[j,:] = cj;M[:,j] = cj
end
end
return M
end
此外還滿足如下關係
,引入記號
則有
這說明每次Jacobi迭代雖然會使得某個元素變爲0,但也可能會讓某些爲0的元素變爲非0,在使用Jacobi旋轉法時,也應該遵循最大元優先的原則。