Matrix Operations -- Transpose +Determinant + Adjugate+ Inverse + Gram-Schimidt +LUP + QR + Eigen
Matrix Operations
這些函式操作的實現裡面,我沒有用一個Class抽象矩陣之後,再做操作實現.
關鍵還是希望減少閱讀"困難".
如果你看到這篇筆記貼,希望不管感興趣哪種操作實現,都可以很快的從Python內建資料結構入手,不用關心抽象.把注意力都集中在演算法實現上面.
目前很多隻是演算法實現,還有時間複雜度高的值得優化的地方...
但是能用...
特比特別注意,這裡有些函式實現的時候,我呼叫了,transpose這個轉置的實現.
因為數學領域裡面,總是喜歡用列向量 column vector,然後實際上Python實現的時候是很不方面對列向量做計算的.於是我有時候會使用轉置,使之變成行向量,來進行計算會方便的多.
當你遇到坑爹又裝逼的矩陣運算的表怕,一切都是紙老虎!
目前實現的主要矩陣操作:
Transpose +Determinant + Adjugate+ Inverse + Gram-Schimidt +LUP decomnposition + QR decomposition
Determinant : 行列式求值
對於行列式求值,我實現了一個函式determinant(),用來求給定行列式的.
def determinant(self, matrix) : row = len(matrix) col = len(matrix[0]) if row == 1 : return matrix elif row == 2 : return matrix[0][0]*matrix[1][1] - matrix[1][0] * matrix[0][1] ret_val = 0 for i in range(0, col) : tmp_mat = [[0 for x in range(0, col-1)] for y in range(0, row-1)] for m in range(0, row-1) : n = 0 while n < col-1 : if n < i : tmp_mat[m][n] = matrix[m+1][n] else : tmp_mat[m][n] = matrix[m+1][n+1] n += 1 ret_val += ((-1)**(i)) * matrix[0][i] * \ self.determinant(tmp_mat) return ret_val
Python 的numpy模組有內嵌的行列式求值函式det()
Dot Product:點積
騷年啊!醒醒啊!你腫麼可以把點積和內積弄混!!!
The Gram-Schimidt Process : 正交化
這裡有一篇關於討論Gram-Schimidt的文章,我覺得蠻好的:
老實說,這裡被坑了好久...看到一個資料上面
MIT的lecture
提供了一下演算法.但是又沒講請促.我還以為q*是求伴隨矩陣.坑我好久...我還去寫了伴隨矩陣的實現,
簡直氣憤...別看下面這個實現.越看越不相信自己...建議可以看我的Python實現...
我自己根據公式給出的Python實現:
def gram_schimidt(self, A) :
if A is None :
return
A_T = self.transpose(A)
row = len(A_T)
col = len(A_T[0])
V = [[0 for i in range(0, col)] for j in range(0, row)]
for i in range(0, row) :
tmp_mat = [0 for x in range(0, col)]
for j in range(0, col) :
tmp = A_T[i][j]
for k in range(0, i) :
factor = (1.0*self.dot_product(A_T[i], V[k])) / \
self.dot_product(V[k], V[k])
tmp -= factor*V[k][j]
V[i][j] = tmp
V = self.transpose(V)
return V
Adjugate:伴隨矩陣
測試結果:
Inverse : 矩陣求逆
基於行列變換去做矩陣求逆
這裡我實現了函式inverse(matrix),用來求解,給定引數matrix
最後我測試的時候讓逆矩陣和原來的矩陣做矩陣乘法.最後得到單位矩陣.經檢驗,求逆演算法是正確的.
def inverse(self, mat) :
if mat is None :
return
# make sure that this matrix that you inputed is invertible
if self.determinant(mat) == 0 :
print "ATTENTION! The determinant of matrix is ZERO"
print "This matrix is uninvertible"
return
row = len(mat)
col = len(mat[0])
#matrix = copy.copy(mat)
matrix = copy.deepcopy(mat)
# matrix = [[0 for i in range(0, col)] for j in range(0, row)]
# for i in range(0, row) :
# for j in range(0, col) :
# matrix[i][j] = mat[i][j]
for i in range(0, row) :
for j in range(0, col) :
if i == j :
matrix[i] += [1]
else :
matrix[i] += [0]
row = len(matrix)
col = len(matrix[0])
for i in range(0, row) :
if matrix[i][i] is 0 :
for k in range(i+1, row) :
if matrix[k][i] is not 0 :
break
if k is not i+1 :
for j in range(0, col) :
matrix[i][j], matrix[k][j] = matrix[k][j], matrix[i][j]
for k in range(i+1, row) :
if matrix[k][i] is not 0 :
times = (1.0*matrix[k][i])/matrix[i][i]
for j in range(i, col) :
matrix[k][j] /= times
matrix[k][j] -= matrix[i][j]
for i in range(0, row) :
for j in range(i+1, col/2) :
if matrix[i][j] is not 0 :
times = matrix[i][j]/matrix[j][j]
for k in range(j, col) :
matrix[i][k] -= times * matrix[j][k]
for i in range(0, row) :
times = matrix[i][i]
for j in range(0, col) :
matrix[i][j] /= times
output = [[0 for i in range(0, col/2)] for j in range(0, row)]
for i in range(0, row) :
for j in range(0, col/2) :
output[i][j] = matrix[i][j+col/2]
return output
QR decomposition :
這個在我原來看來裝逼度很高的矩陣分解也就是個紙老虎
首先對其進行Gram-schimidt正交化處理,然後就簡答鳥,看下面這個運算demo
向量組v由正交化過程求得,然後每個列向量除以自己的模值即可得到矩陣Q
利用Q是正交陣的特點,求得R矩陣
哥們兒,別忘記鳥,計算機求解的都是數值解,數值解不一定等於解析解~!
def qr_decomposition(self, A) :
if A is None :
return
orthogonal_mat = self.transpose(self.gram_schimidt(A))
row = len(orthogonal_mat)
col = len(orthogonal_mat[0])
Q = [[0 for i in range(0, col)] for j in range(0, row)]
for i in range(0, row) :
mag = self.magnitude(orthogonal_mat[i])
for j in range(0, col) :
Q[i][j] = orthogonal_mat[i][j]/mag
R = self.multiply(Q, A)
Q = self.transpose(Q)
return (Q, R)
關於LUP的理論分析去看CLRS吧.... 這裡僅貼出自己的實現.
終於~ LUP不再那麼神乎其神...
Eigen value and eigen vector :
看這裡:
---------------
Python 實現:
"""
Programmer : EOF
Code date : 2015.02.28
Code file : matrix.py
Code description :
@transpose : return a matrix @A_T which is tranposed matrix of @A
@lu_decomposition : return matrix @L and @U which are decomposed from matrix @A
@plu_decomposition : return matrix @P, @L and @U which are decomposed from matrix @A
"""
import copy
import numpy
class mat() :
def transpose(self, A) :
row = len(A)
col = len(A[0])
A_T = [[0 for i in range(0, row)] for j in range(0, col)]
for i in range(0, row) :
for j in range(0, col) :
A_T[j][i] = A[i][j]
return A_T
def add(self, A, B) :
if A is None or B is None :
return
row_A = len(A)
col_A = len(A[0])
row_B = len(B)
col_B = len(B[0])
if row_A != row_B or col_A != col_B :
print "Are you kidding me? The matrixes that you inputed ",\
"have different size. It's illegal to add these matries together"
return
output = [[0 for i in range(0, col_A)] for j in range(0, row_A)]
for i in range(0, row_A) :
for j in range(0, col_A) :
output[i][j] = A[i][j] + B[i][j]
return output
def sub(self, A, B) :
if A is None or B is None :
return
row_A = len(A)
col_A = len(A[0])
row_B = len(B)
col_B = len(B[0])
if row_A != row_B or col_A != col_B :
print "Are you kidding me? The matrixes that you inputed ",\
"have different size. It's illegal to sub these matries together"
return
output = [[0 for i in range(0, col_A)] for j in range(0, row_A)]
for i in range(0, row_A) :
for j in range(0, col_A) :
output[i][j] = A[i][j] - B[i][j]
return output
def multiply(self, A, B) :
if A is None or B is None :
return
if len(A[0]) is not len(B) :
print "The size of the two inputed matrix is illegally ",\
"to do multiplication in matrix"
return
row_A = len(A)
col_A = len(A[0])
col_B = len(B[0])
output = [[0 for i in range(0, col_B)] for j in range(0, row_A)]
for i in range(0, row_A) :
for j in range(0, col_B) :
sum_val = 0
for k in range(0, col_A) :
sum_val += A[i][k] * B[k][j]
output[i][j] = sum_val
return output
def dot_product(self, A, B) :
if A is None or B is None :
return
len_A = len(A)
len_B = len(B)
if len_A != len_B :
print "It's Illegal to do dot product with the two matrixes",
"which have different size"
return
sum_val = 0
for i in range(0, len_A) :
sum_val += A[i]*B[i]
return sum_val
def magnitude(self, A) :
ret_val = self.dot_product(A, A)
return numpy.sqrt(ret_val)
def gram_schimidt(self, A) :
if A is None :
return
A_T = self.transpose(A)
row = len(A_T)
col = len(A_T[0])
V = [[0 for i in range(0, col)] for j in range(0, row)]
for i in range(0, row) :
tmp_mat = [0 for x in range(0, col)]
for j in range(0, col) :
tmp = A_T[i][j]
for k in range(0, i) :
factor = (1.0*self.dot_product(A_T[i], V[k])) / \
self.dot_product(V[k], V[k])
tmp -= factor*V[k][j]
V[i][j] = tmp
V = self.transpose(V)
return V
def adjugate(self, A) :
row = len(A)
col = len(A[0])
output = [[0 for x in range(0, col)] for y in range(0, row)]
for i in range(0, row) :
for j in range(0, col) :
tmp_mat = [[0 for x in range(0, col-1)] for y in range(0, row-1)]
for m in range(0, row) :
for n in range(0, col) :
if m < i and n < j :
tmp_mat[m][n] = A[m][n]
elif m > i and n < j :
tmp_mat[m-1][n] = A[m][n]
elif m < i and n > j :
tmp_mat[m][n-1] = A[m][n]
elif m > i and n > j :
tmp_mat[m-1][n-1] = A[m][n]
det_val = self.determinant(tmp_mat)
det_val *= (-1)**(i+j)
output[i][j] = det_val
output = self.transpose(output)
return output
def qr_decomposition(self, A) :
if A is None :
return
orthogonal_mat = self.transpose(self.gram_schimidt(A))
row = len(orthogonal_mat)
col = len(orthogonal_mat[0])
Q = [[0 for i in range(0, col)] for j in range(0, row)]
for i in range(0, row) :
mag = self.magnitude(orthogonal_mat[i])
for j in range(0, col) :
Q[i][j] = orthogonal_mat[i][j]/mag
R = self.multiply(Q, A)
Q = self.transpose(Q)
return (Q, R)
def lup_solve(self, L, U, pi, b) :
n = len(self.L)
x = [0 for i in range(0, n)]
y = [0 for i in range(0, n)]
for i in range(0, n) :
tmp = 0
for j in range(0, i-1) :
tmp += L[i][j] * y[j]
y[i] = b[ pi[i] ] - tmp
for i in range(n-1, -1, -1) :
tmp = 0
for j in range(i+1, n) :
tmp += U[i][j] * x[j]
x[i] = (y[i] - tmp)/u[i][i]
return x
def lu_decomposition(self, A) :
n = len(A)
a = A
self.L = [[0 for i in range(0, n)] for j in range(0, n)]
self.U = [[0 for i in range(0, n)] for j in range(0, n)]
for k in range(0, n) :
self.U[k][k] = a[k][k]
for i in range(k+1, n) :
self.L[i][k] = a[i][k]/self.U[k][k]
self.U[k][i] = a[k][i]
for i in range(k+1, n) :
for j in range(k+1, n) :
a[i][j] = a[i][j] - self.L[i][k]*self.U[k][j]
return (self.L, self.U)
def lup_decomposition(self, A) :
n = len(A)
a = A
pi = [i for i in range(0, n)]
for k in range(0, n) :
p = 0
for i in range(k, n) :
if abs(a[i][k]) > p :
p = abs(a[i][k])
k_prime = i
if p is 0 :
print "singular matrix"
return
pi[k], pi[k_prime] = pi[k_prime], pi[k]
for i in range(0, n) :
a[k][i], a[k_prime][i] = a[k_prime][i], a[k][i]
for i in range(k+1, n) :
a[i][k] /= (a[k][k] * 1.0)
for j in range(k+1, n) :
a[i][j] -= a[i][k] * a[k][j]
self.P = [[ 0 for i in range(0, len(pi))] for j in range(0, len(pi))]
for i in range(0, len(pi)) :
self.P[i][ pi[i] ] = 1
self.L = [[0 for i in range(0, n)] for j in range(0, n)]
self.U = [[0 for i in range(0, n)] for j in range(0, n)]
row = len(self.L)
col = len(self.L[0])
for i in range(0, row) :
for j in range(0, col) :
if j < i :
self.L[i][j] = a[i][j]
elif j == i :
self.L[i][j] = 1
self.U[i][j] = a[i][j]
else :
self.U[i][j] = a[i][j]
return (self.P, self.L, self.U)
def determinant(self, matrix) :
row = len(matrix)
col = len(matrix[0])
if row == 1 :
return matrix
elif row == 2 :
return matrix[0][0]*matrix[1][1] - matrix[1][0] * matrix[0][1]
ret_val = 0
for i in range(0, col) :
tmp_mat = [[0 for x in range(0, col-1)] for y in range(0, row-1)]
for m in range(0, row-1) :
n = 0
while n < col-1 :
if n < i :
tmp_mat[m][n] = matrix[m+1][n]
else :
tmp_mat[m][n] = matrix[m+1][n+1]
n += 1
ret_val += ((-1)**(i)) * matrix[0][i] * \
self.determinant(tmp_mat)
return ret_val
def inverse(self, mat) :
if mat is None :
return
# make sure that this matrix that you inputed is invertible
if self.determinant(mat) == 0 :
print "ATTENTION! The determinant of matrix is ZERO"
print "This matrix is uninvertible"
return
row = len(mat)
col = len(mat[0])
#matrix = copy.copy(mat)
matrix = copy.deepcopy(mat)
# matrix = [[0 for i in range(0, col)] for j in range(0, row)]
# for i in range(0, row) :
# for j in range(0, col) :
# matrix[i][j] = mat[i][j]
for i in range(0, row) :
for j in range(0, col) :
if i == j :
matrix[i] += [1]
else :
matrix[i] += [0]
row = len(matrix)
col = len(matrix[0])
for i in range(0, row) :
if matrix[i][i] is 0 :
for k in range(i+1, row) :
if matrix[k][i] is not 0 :
break
if k is not i+1 :
for j in range(0, col) :
matrix[i][j], matrix[k][j] = matrix[k][j], matrix[i][j]
for k in range(i+1, row) :
if matrix[k][i] is not 0 :
times = (1.0*matrix[k][i])/matrix[i][i]
for j in range(i, col) :
matrix[k][j] /= times
matrix[k][j] -= matrix[i][j]
for i in range(0, row) :
for j in range(i+1, col/2) :
if matrix[i][j] is not 0 :
times = matrix[i][j]/matrix[j][j]
for k in range(j, col) :
matrix[i][k] -= times * matrix[j][k]
for i in range(0, row) :
times = matrix[i][i]
for j in range(0, col) :
matrix[i][j] /= times
output = [[0 for i in range(0, col/2)] for j in range(0, row)]
for i in range(0, row) :
for j in range(0, col/2) :
output[i][j] = matrix[i][j+col/2]
return output
def eig(self, A) :
if A is None :
return
tmp_mat = copy.deepcopy(A)
for i in range(0, 20) :
(Q, R) = self.qr_decomposition(tmp_mat)
tmp_mat = self.multiply(R, Q)
row = len(tmp_mat)
col = len(tmp_mat[0])
for i in range(0, row) :
for j in range(0, col) :
if i != j :
tmp_mat[i][j] = 0
eig_vec = self.inverse(self.sub(A, tmp_mat))
return (tmp_mat, eig_vec)
def show(self, matrix) :
if matrix is None :
return
row = len(matrix)
col = len(matrix[0])
for i in range(0, row) :
for j in range(0, col) :
print "\t%1.2f" % matrix[i][j],
print
print "\n\n"
#-----------------for testing------------------------------------
#matrix = [[2,3,1,5], [6,13,5,19], [2,19,10,23],[4,10,11,31]]
matrix = [[2,0,2,0.6], [3,3,4,-2], [5,5,4,2], [-1,-2,3.4,-1]]
m = mat()
print "The determinant of matrix @mat_1 :"
mat_1 = [[1, 0, 0], [0, 2, 0], [0, 0, 3]]
m.show(mat_1)
print m.determinant(mat_1)
print "The determinant of matrix @mat_2 :"
mat_2 = [[1, 5, 0], [2, 4, -1], [0, -2, 0]]
m.show(mat_2)
print m.determinant(mat_2)
print "The input matrix is \n"
m.show(matrix)
print "After transpose\n"
m.show(m.transpose(matrix))
(P, L, U) = m.lup_decomposition(matrix)
print "After PLU decomposition, we got matrixes"
print "P:"
m.show(P)
print "L:"
m.show(L)
print "U:"
m.show(U)
inv_mat = m.inverse(matrix)
print "The inverse matrix of the inputed matrix is "
m.show(inv_mat)
print "Output"
m.show(m.multiply(inv_mat, matrix))
mat_1 = [[1,2,3],[4,5,6],[7,8,9]]
print "The inputed matrix"
m.show(mat_1)
print "The adjugate matrix of the inputed matrix is "
m.show(m.adjugate(mat_1))
mat_1 = [[1,0,0],[1,1,0],[1,1,1],[1,1,1]]
print "The inputed matrix"
m.show(mat_1)
print "The gram chimidt matrix of the inputed matrix is "
m.show(m.gram_schimidt(mat_1))
print "The inputed matrix"
m.show(mat_1)
print "The adjugate matrix of the inputed matrix is "
(Q, R) = m.qr_decomposition(mat_1)
m.show(Q)
m.show(R)
#mat_1 = [[2, 3], [3, -6]]
mat_1 = [[2, 1], [1, 2]]
print "The inputed matrix"
m.show(mat_1)
print "The eig value matrix of the inputed matrix is "
(eig_val, eig_vector) = m.eig(mat_1)
m.show(eig_val)
m.show(eig_vector)
waiting for updates ... ...
2015.02.28 更新了行列式求值的實現.determinant()
2015.03.01 更新了矩陣求逆的實現.inverse()
2015.03.03 更新了矩陣求伴隨矩陣的實現adjugate()
2015.03.05 更新了矩陣Gram-Schimit()正交化的實現.
2015.03.05 更新了矩陣的QR分解的實現
2015.03.05 更新了矩陣的特徵值和特徵向量的實現eig()
再美好也經不住遺忘,再悲傷也抵不過時光
攝於二零一五年大年初二