python實現矩陣的示例代碼
矩陣
使用python構建一個類,模擬矩陣,可以進行各種矩陣的計算,與各種方便的用法
init
from array import array
class Matrix:
def __init__(self, matrix: 'a list of one dimension', shape: 'a tuple of shape' = None,
dtype: 'data type code' = 'd'):
# matrix一個包含所有元素的列表,shape指定形狀,默認為列向量,dtype是數據類型
# 使用一個數組模擬矩陣,通過操作這個數組完成矩陣的運算
self.shape = (len(matrix), 1)
if shape:
self.shape = shape
self.array = array(dtype, matrix)getitem
由于矩陣是一個二維數組,應當支持諸如matrix[1, 2],matrix[1:3, 2],matrix[1:3, 2:4]之類的取值
所以我們需要使用slice類的indice方法實現__getitem__,并支持切片
def __getitem__(self, item: 'a index of two dimensions'):
# 使用slice類的indices方法,實現二維切片
rows, cols = item
# 下面對傳入的指針或者切片進行處理,使其可以統(tǒng)一處理
if isinstance(rows, slice):
rows = rows.indices(self.shape[0])
else:
rows = (rows, rows + 1)
if isinstance(cols, slice):
cols = cols.indices(self.shape[1])
else:
cols = (cols, cols + 1)
res = []
shape = (len(range(*rows)), len(range(*cols))) # 新矩陣的形狀
# 通過遍歷按照順序將元素加入新的矩陣
for row in range(*rows):
for col in range(*cols):
index = row * self.shape[1] + col
res.append(self.array[index])
if len(res) == 1:
# 若是數則返回數
return res[0]
# 若是矩陣則返回矩陣
return Matrix(res, shape)由于要支持切片,所以需要在方法中新建一個矩陣用于返回值
setitem
由于沒有序列協(xié)議的支持,我們需要自己實現__setitem__
def __setitem__(self, key: 'a index or slice of two dimensions', value):
# 使用slice類的indices方法,實現二維切片的廣播賦值
rows, cols = key
if isinstance(rows, slice):
rows = rows.indices(self.shape[0])
else:
rows = (rows, rows + 1)
if isinstance(cols, slice):
cols = cols.indices(self.shape[1])
else:
cols = (cols, cols + 1)
if isinstance(value, Matrix):
# 對于傳入的值是矩陣,則需要判斷形狀
if value.shape != (len(range(*rows)), len(range(*cols))):
raise ShapeError
# 使用x,y指針取出value中的值賦給矩陣
x = -1
for row in range(*rows):
x += 1
y = -1
for col in range(*cols):
y += 1
index = row * self.shape[1] + col
self.array[index] = value[x, y]
else:
for row in range(*rows):
for col in range(*cols):
index = row * self.shape[1] + col
self.array[index] = value若傳入的value是一個數,這里的邏輯基本與__getitem__相同,實現了廣播。
而若傳入的是一個矩陣,則需要判斷形狀,對對應的元素進行賦值,這是為了方便LU分解。
reshape
reshape用于改變形狀,對于上面的實現方法,只需要改變matrix.shape就可以了
注意改變前后的總元素數應當一致
def reshape(self, shape: 'a tuple of shape'):
if self.shape[0] * self.shape[1] != shape[0] * shape[1]:
raise ShapeError
self.shape = shaperepr
實現__repr__方法,較為美觀的打印矩陣
def __repr__(self):
shape = self.shape
_array = self.array
return "[" + ",\n".join(str(list(_array[i * shape[1]:(i + 1) * shape[1]])) for i in range(shape[0])) + "]"add 與 mul
對于加法與乘法的支持,這里的乘法是元素的乘法,不是矩陣的乘法
同樣的,實現廣播
? ? def __add__(self, other): ? ? ? ? shape = self.shape ? ? ? ? res = zeros(shape) ?# 創(chuàng)建一個新的零矩陣,用于返回 ? ? ? ? if isinstance(other, Matrix): ? ? ? ? ? ? # 實現同樣形狀的矩陣元素之間的加法 ? ? ? ? ? ? if self.shape != other.shape: ? ? ? ? ? ? ? ? # 如果矩陣的形狀對不上,就返回錯誤 ? ? ? ? ? ? ? ? raise ShapeError ? ? ? ? ? ? for i in range(shape[0]): ? ? ? ? ? ? ? ? for j in range(shape[1]): ? ? ? ? ? ? ? ? ? ? res[i, j] = self[i, j] + other[i, j] ? ? ? ? else: ? ? ? ? ? ? # 實現廣播 ? ? ? ? ? ? for i in range(shape[0]): ? ? ? ? ? ? ? ? for j in range(shape[1]): ? ? ? ? ? ? ? ? ? ? res[i, j] = self[i, j] + other ? ? ? ? return res ? ? def __mul__(self, other): ? ? ? ? shape = self.shape ? ? ? ? res = zeros(shape) ?# 創(chuàng)建一個新的零矩陣,用于返回 ? ? ? ? if isinstance(other, Matrix): ? ? ? ? ? ? # 實現同樣形狀的矩陣元素之間的乘法 ? ? ? ? ? ? if self.shape != other.shape: ? ? ? ? ? ? ? ? # 如果矩陣的形狀對不上,就返回錯誤 ? ? ? ? ? ? ? ? raise ShapeError ? ? ? ? ? ? for i in range(shape[0]): ? ? ? ? ? ? ? ? for j in range(shape[1]): ? ? ? ? ? ? ? ? ? ? res[i, j] = self[i, j] * other[i, j] ? ? ? ? else: ? ? ? ? ? ? # 實現廣播 ? ? ? ? ? ? for i in range(shape[0]): ? ? ? ? ? ? ? ? for j in range(shape[1]): ? ? ? ? ? ? ? ? ? ? res[i, j] = self[i, j] * other ? ? ? ? return res
matmul
matmul矩陣乘法,運算符為@
def __matmul__(self, other):
# 實現矩陣的乘法
if self.shape[1] != other.shape[0]:
# 對形狀進行判斷
raise ShapeError
if self.shape[0] == 1 and other.shape[1] == 1:
# 行向量與列向量的乘積,就是它們的數量積
length = self.shape[1]
return sum(self[0, i] * self[i, 0] for i in range(length))
res = []
shape = (self.shape[0], other.shape[1])
for i in range(shape[0]):
for j in range(shape[1]):
# 將兩個矩陣分別按行向量與列向量分塊,然后相乘
try:
# 由于切片返回的可能是數,而數不支持'@'運算符,所以使用異常處理語句
res.append(self[i, :] @ other[:, j])
except TypeError:
res.append(self[i, :] * other[:, j])
return Matrix(res, shape)將矩陣分成向量進行矩陣乘法
LU分解
用屬性self._lu,構建一個新的矩陣,作為LU分解表。self._lu并不會在初始化時創(chuàng)建,而是在需要用到LU分解表時計算。
同時,我們維護一個self.changed屬性,用來判斷在需要用到LU分解表時是否需要重新進行LU分解
? ? def __init__(self, matrix: 'a list of one dimension', shape: 'a tuple of shape' = None, ? ? ? ? ? ? ? ? ?dtype: 'data type code' = "d"): ? ? ? ? # matrix一個包含所有元素的列表,shape指定形狀默認為列向量,dtype是數據類型 ? ? ? ? # 使用一個數組模擬矩陣,通過操作這個數組完成矩陣的運算 ? ? ? ? self.shape = (len(matrix), 1) ? ? ? ? if shape: ? ? ? ? ? ? self.shape = shape ? ? ? ? self.array = array(dtype, matrix) ? ? ? ? self._changed = True ? ? ? ? self._primary = list(range(shape[0])) # 只進行行交換
顯然,當我們修改了矩陣的元素后就需要重新進行LU分解,重寫 setitem ,在開始時修改self.changed屬性
def __setitem__(self, key: 'a index or slice of two dimensions', value):
# 使用slice類的indices方法,實現二維切片的廣播賦值
self._change = True
...在lu分解中需要選擇主元,用屬性self._primary儲存當前矩陣的主元表,因此我們要重寫 getitem 與 setitem
... row = self._primary[row] # 通過主元表進行賦值,將行換為 index = row * self.shape[1] + col ...
下面,我們來實現LU分解
? ? def _primary_update(self, k): ? ? ? ? # 選擇絕對值最大的數作為主元 ? ? ? ? max_val = -777 ? ? ? ? max_index = 0 ? ? ? ? for i in range(k, self.shape[0]): ? ? ? ? ? ? x = abs(self[i, k]) ? ? ? ? ? ? if x > max_val: ? ? ? ? ? ? ? ? max_val = x ? ? ? ? ? ? ? ? max_index = i ? ? ? ? self._primary[k], self._primary[max_index] = self._primary[max_index], self._primary[k] ? ? def _lu_factorization(self): ? ? ? ? self._lu = Matrix(self.array, self.shape) # 新建一個矩陣儲存LU分解表 ? ? ? ? rows, cols = self.shape ? ? ? ? _lu = self._lu ? ? ? ? step = min(rows, cols) ? ? ? ? for k in range(step): ? ? ? ? ? ? if _lu[k, k] == 0: ? ? ? ? ? ? ? ? # 如果當前對角元素為0,就需要更換主元 ? ? ? ? ? ? ? ? _lu._primary_update(k) ? ? ? ? ? ? if _lu[k, k] == 0: ? ? ? ? ? ? ? ? # 如果更換主元之后仍然為0,就說明該列全為0,跳過 ? ? ? ? ? ? ? ? break ? ? ? ? ? ? x = 1 / _lu[k, k] ? ? ? ? ? ? _lu[k + 1:, k] *= x ? ? ? ? ? ? for i in range(k + 1, rows): ? ? ? ? ? ? ? ? for j in range(k + 1, cols): ? ? ? ? ? ? ? ? ? ? _lu[i, j] = _lu[i, j] - _lu[i, k] * _lu[k, j]
轉置
用一個方法實現轉置,而不是維護一個屬性
def trans(self):
shape = self.shape[::-1]
res = zeros(shape) # 創(chuàng)建一個零矩陣用于返回
for i in range(shape[0]):
for j in range(shape[1]):
res[i, j] = self[j, i]
return res利用LU分解求行列式
原矩陣的行列式就是L與U的對角元素的乘積
def det(self):
if self.shape[0] != self.shape[1]:
raise ShapeError
if self._changed:
self._lu_factorization()
self._changed = False
res = 1
for i in range(self.shape[0]):
res *= self[i, i]
return res利用LU分解解線性方程組
利用LU分解可以快速地解出線性方程組
def linear_equation(self, y):
# 利用LU分解表解方程
if not self.det():
# 不考慮扁平化的情況,即使可能有解
raise DetError
lu = self._lu
length = self.shape[1]
z = [0]*length # 先解 L @ z = y
for i in range(length):
z_i = y[i, 0]
for j in range(i):
z_i -= z[j] * lu[i, j]
z[i] = z_i
x = [0]*length # 再解 U @ x = z
for i in range(length - 1, -1, -1):
x_i = z[i]
for j in range(length - 1, i, -1):
x_i -= x[j] * lu[i, j]
x[i] = x_i / lu[i, i]
return Matrix(x, (length, 1))到此這篇關于python實現矩陣的示例代碼的文章就介紹到這了,更多相關python 矩陣內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用Python的Tornado框架實現一個Web端圖書展示頁面
Tornado是Python的一款高人氣Web開發(fā)框架,這里我們來展示使用Python的Tornado框架實現一個Web端圖書展示頁面的實例,通過該實例可以清楚地學習到Tornado的模板使用及整個Web程序的執(zhí)行流程.2016-07-07

