可以先看最后一个视频,了解内容情况。
本文比较长,是个人对视频内容的一些总结及理解,可供参考。
代码是在jupiterlab里运行的,因为比较整合。
目录
向量
用numpy实现
矩阵
矩阵变换
用numpy实现
消元
p58 LU分解
p63 线性组合
生成空间
第九章
子空间
零空间
施密特正交编辑
P93 QR分解
SVD分解
下图贯穿视频
前面比较能理解,就记录了一下代码实现
import math
# from ._globals import is_zero, is_equal
EPSILON = 1e-8
def is_zero(x):
return abs(x) < EPSILON
def is_equal(a, b):
return abs(a - b) < EPSILON
class Vector:
def __init__(self, lst):
self._values = list(lst)
@classmethod
def zero(cls, dim):
"""返回一个dim维的零向量"""
return cls([0] * dim)
def __add__(self, another):
"""向量加法,返回结果向量"""
assert len(self) == len(another), \
"Error in adding. Length of vectors must be same."
return Vector([a + b for a, b in zip(self, another)])
def __sub__(self, another):
"""向量减法,返回结果向量"""
assert len(self) == len(another), \
"Error in subtracting. Length of vectors must be same."
return Vector([a - b for a, b in zip(self, another)])
def norm(self):
"""返回向量的模"""
return math.sqrt(sum(e**2 for e in self))
def normalize(self):
"""返回向量的单位向量"""
if is_zero(self.norm()):
raise ZeroDivisionError("Normalize error! norm is zero.")
return Vector(self._values) / self.norm()
def dot(self, another):
"""向量点乘,返回结果标量"""
assert len(self) == len(another), \
"Error in dot product. Length of vectors must be same."
return sum(a * b for a, b in zip(self, another))
def underlying_list(self):
"""返回向量的底层列表"""
return self._values[:]
def __mul__(self, k):
"""返回数量乘法的结果向量:self * k"""
return Vector([k * e for e in self])
def __rmul__(self, k):
"""返回数量乘法的结果向量:k * self"""
return self * k
def __truediv__(self, k):
"""返回数量除法的结果向量:self / k"""
return (1 / k) * self
def __pos__(self):
"""返回向量取正的结果向量"""
return 1 * self
def __neg__(self):
"""返回向量取负的结果向量"""
return -1 * self
def __eq__(self, other):
"""返回向量是否相等"""
other_list = other.underlying_list();
if(len(other_list) != len(self._values)):
return False
return all(is_equal(x, y) for x, y in zip(self._values, other_list))
def __neq__(self, other):
"""返回向量是否不等"""
return not(self == other)
def __iter__(self):
"""返回向量的迭代器"""
return self._values.__iter__()
def __getitem__(self, index):
"""取向量的第index个元素"""
return self._values[index]
def __len__(self):
"""返回向量长度(有多少个元素)"""
return len(self._values)
def __repr__(self):
return "Vector({})".format(self._values)
def __str__(self):
return "({})".format(", ".join(str(e) for e in self._values))
# from playLA.Vector import Vector
if __name__ == "__main__":
vec = Vector([5, 2])
print(vec) #
print("len(vec) = {}".format(len(vec)))
print("vec[0] = {}, vec[1] = {}".format(vec[0], vec[1]))
# (5, 2)
# len(vec) = 2
# vec[0] = 5, vec[1] = 2
vec2 = Vector([3, 1])
print("{} + {} = {}".format(vec, vec2, vec + vec2))
print("{} - {} = {}".format(vec, vec2, vec - vec2))
# (5, 2) + (3, 1) = (8, 3)
# (5, 2) - (3, 1) = (2, 1)
print("{} * {} = {}".format(vec, 3, vec * 3))
print("{} * {} = {}".format(3, vec, 3 * vec))
# (5, 2) * 3 = (15, 6)
# 3 * (5, 2) = (15, 6)
print("+{} = {}".format(vec, +vec))
print("-{} = {}".format(vec, -vec))
# +(5, 2) = (5, 2)
# -(5, 2) = (-5, -2)
zero2 = Vector.zero(2)
print(zero2)
print("{} + {} = {}".format(vec, zero2, vec + zero2))
# (0, 0)
# (5, 2) + (0, 0) = (5, 2)
print("norm({}) = {}".format(vec, vec.norm()))
print("norm({}) = {}".format(vec2, vec2.norm()))
print("norm({}) = {}".format(zero2, zero2.norm()))
# norm((5, 2)) = 5.385164807134504
# norm((3, 1)) = 3.1622776601683795
# norm((0, 0)) = 0.0
print("normalize {} is {}".format(vec, vec.normalize()))
print(vec.normalize().norm())
# normalize(5, 2) is (0.9284766908852593, 0.3713906763541037)
# 1.0
print("normalize {} is {}".format(vec2, vec2.normalize()))
print(vec2.normalize().norm())
# normalize(3, 1) is (0.9486832980505138, 0.31622776601683794)
# 0.9999999999999999
try:
zero2.normalize()
except ZeroDivisionError:
print("Cannot normalize zero vector {}.".format(zero2))
#Cannot normalize zero vector (0, 0).
print(vec.dot(vec2)) #17
vec3 = Vector([0, 0])
print("{} == {}? {}".format(zero2, vec3, vec3 == zero2))
print("{} == {}? {}".format(vec2, vec3, vec3 == vec2))
print("{} != {}? {}".format(vec2, vec3, vec3 != vec2))
# (0, 0) == (0, 0)? True
# (3, 1) == (0, 0)? False
# (3, 1) != (0, 0)? True
import numpy as np
if __name__ == "__main__":
print(np.__version__)
# np.array 基础
lst = [1, 2, 3]
lst[0] = "Linear Algebra"
print(lst)
vec = np.array([1, 2, 3])
print(vec)
# vec[0] = "Linear Algebra"
# vec[0] = 666
# print(vec)
# np.array的创建
print(np.zeros(5))
print(np.ones(5))
print(np.full(5, 666))
# np.array的基本属性
print(vec)
print("size =", vec.size)
print("size =", len(vec))
print(vec[0])
print(vec[-1])
print(vec[0: 2])
print(type(vec[0: 2]))
# np.array的基本运算
vec2 = np.array([4, 5, 6])
print("{} + {} = {}".format(vec, vec2, vec + vec2))
print("{} - {} = {}".format(vec, vec2, vec - vec2))
print("{} * {} = {}".format(2, vec, 2 * vec))
print("{} * {} = {}".format(vec, vec2, vec * vec2))
print("{}.dot({}) = {}".format(vec, vec2, vec.dot(vec2)))
print(np.linalg.norm(vec))
print(vec / np.linalg.norm(vec))
print(np.linalg.norm(vec / np.linalg.norm(vec)))
# zero3 = np.zeros(3)
# print(zero3 / np.linalg.norm(zero3))
# from .Vector import Vector
class Matrix:
# __init__(self, list2d):初始化方法,接受一个二维列表或二维向量列表作为参数,将其转换为内部的矩阵表示self._values。
# zero(cls, r, c):类方法,返回一个r行c列的零矩阵。
# identity(cls, n):类方法,返回一个n行n列的单位矩阵。
# T(self):返回矩阵的转置矩阵。
# __add__(self, another):重载加法运算符,返回两个矩阵的加法结果。
# __sub__(self, another):重载减法运算符,返回两个矩阵的减法结果。
# dot(self, another):矩阵乘法,接受一个向量或矩阵作为参数,返回乘法结果。
# __mul__(self, k):重载乘法运算符,返回矩阵与标量k的数量乘结果。
# __rmul__(self, k):重载乘法运算符,返回标量k与矩阵的数量乘结果。
# __truediv__(self, k):重载除法运算符,返回矩阵除以标量k的结果。
# __pos__(self):重载正号运算符,返回矩阵取正的结果。
# __neg__(self):重载负号运算符,返回矩阵取负的结果。
# row_vector(self, index):返回矩阵的第index个行向量。
# col_vector(self, index):返回矩阵的第index个列向量。
# __getitem__(self, pos):重载索引运算符,返回矩阵中pos位置的元素。
# size(self):返回矩阵的元素个数。
# row_num(self):返回矩阵的行数。
# col_num(self):返回矩阵的列数。
# shape(self):返回矩阵的形状,即行数和列数。
# __repr__(self)和__str__(self):返回矩阵的字符串表示。
def __init__(self, list2d):
if isinstance(list2d[0], list):
self._values = [row[:] for row in list2d]
elif isinstance(list2d[0], Vector):
self._values = [row.underlying_list() for row in list2d]
# 如果list2d的第一个元素是列表,则假设list2d是一个普通的二维列表,将其拷贝到self._values中。
# 如果list2d的第一个元素是Vector类型的对象,则假设list2d是一个包含Vector对象的二维列表,并将其中每个Vector对象的底层列表提取出来,并存储到self._values中。
@classmethod
def zero(cls, r, c):
"""返回一个r行c列的零矩阵"""
return cls([[0] * c for _ in range(r)])
@classmethod
def identity(cls, n):
"""返回一个n行n列的单位矩阵"""
m = [[0]*n for _ in range(n)]
for i in range(n):
m[i][i] = 1;
return cls(m)
def T(self):
"""返回矩阵的转置矩阵"""
return Matrix([[e for e in self.col_vector(i)]
for i in range(self.col_num())])
def __add__(self, another):
"""返回两个矩阵的加法结果"""
assert self.shape() == another.shape(), \
"Error in adding. Shape of matrix must be same."
# 确保两个矩阵具有相同的形状(行数和列数相同),如果形状不同,则会抛出一个错误提示.
return Matrix([[a + b for a, b in zip(self.row_vector(i), another.row_vector(i))]
for i in range(self.row_num())])
# 通过遍历两个矩阵的行向量,将对应位置的元素相加,生成一个新的矩阵作为加法的结果。
# 在列表表达式中,循环遍历可迭代对象中的每个元素,并将表达式的结果添加到生成的列表中。
# 例如,以下列表表达式将一个列表中的每个元素平方,并生成一个新的列表:
# 例如numbers = [1, 2, 3, 4, 5]
# squared_numbers = [x**2 for x in numbers]
# print(squared_numbers) # 输出: [1, 4, 9, 16, 25]
def __sub__(self, another):
"""返回两个矩阵的减法结果"""
assert self.shape() == another.shape(), \
"Error in subtracting. Shape of matrix must be same."
return Matrix([[a - b for a, b in zip(self.row_vector(i), another.row_vector(i))]
for i in range(self.row_num())])
#迭代两个矩阵的对应行向量,并将对应位置的元素相减得到结果矩阵。
def dot(self, another):
"""返回矩阵乘法的结果"""
if isinstance(another, Vector):
# 矩阵和向量的乘法
assert self.col_num() == len(another), \
"Error in Matrix-Vector Multiplication."
return Vector([self.row_vector(i).dot(another) for i in range(self.row_num())])
if isinstance(another, Matrix):
# 矩阵和矩阵的乘法
assert self.col_num() == another.row_num(), \
"Error in Matrix-Matrix Multiplication."
return Matrix([[self.row_vector(i).dot(another.col_vector(j)) for j in range(another.col_num())]
for i in range(self.row_num())])
# 如果another是Vector类型的对象,则执行矩阵和向量的乘法。
# 在这种情况下,首先断言当前矩阵的列数等于向量的长度,以确保乘法操作是有效的。
# 然后,通过迭代矩阵的每一行,调用对应行向量的dot方法与向量进行点积运算,并将结果存储在一个列表中,最后将该列表作为结果返回。
# 如果another是Matrix类型的对象,则执行矩阵和矩阵的乘法。
# 在这种情况下,首先断言当前矩阵的列数等于another矩阵的行数,以确保乘法操作是有效的。
# 然后,通过嵌套的列表推导式,迭代当前矩阵的每一行和another矩阵的每一列,并调用对应行向量和列向量的dot方法进行点积运算,得到乘法的结果矩阵。
def __mul__(self, k):
"""返回矩阵的数量乘结果: self * k"""
return Matrix([[e * k for e in self.row_vector(i)]
for i in range(self.row_num())])
# 通过列表推导式迭代每个元素,将当前矩阵的每个元素乘以k,得到结果矩阵。
def __rmul__(self, k):
"""返回矩阵的数量乘结果: k * self"""
return self * k
# 矩阵对象的右乘法,用于处理形如k * self的乘法运算。它直接调用了当前矩阵对象的__mul__方法,将k作为参数传递给它。
def __truediv__(self, k):
"""返回数量除法的结果矩阵:self / k"""
return (1 / k) * self
# 将矩阵的每个元素除以k来得到结果矩阵。
def __pos__(self):
"""返回矩阵取正的结果"""
return 1 * self
# 矩阵对象的取正运算,即返回矩阵本身。
def __neg__(self):
"""返回矩阵取负的结果"""
return -1 * self
# 矩阵对象的取负运算,即将矩阵的每个元素取相反数得到结果矩阵。
def row_vector(self, index):
"""返回矩阵的第index个行向量"""
return Vector(self._values[index])
def col_vector(self, index):
"""返回矩阵的第index个列向量"""
return Vector([row[index] for row in self._values])
def __getitem__(self, pos):
"""返回矩阵pos位置的元素 pos元组"""
r, c = pos
return self._values[r][c]
def size(self):
"""返回矩阵的元素个数"""
r, c = self.shape()
return r * c
def row_num(self):
"""返回矩阵的行数"""
return self.shape()[0]
__len__ = row_num
def col_num(self):
"""返回矩阵的列数"""
return self.shape()[1]
def shape(self):
"""返回矩阵的形状: (行数, 列数)"""
return len(self._values), len(self._values[0])
def __repr__(self):
return "Matrix({})".format(self._values)
__str__ = __repr__
# from playLA.Vector import Vector
# from playLA.Matrix import Matrix
if __name__ == "__main__":
matrix = Matrix([[1, 2], [3, 4]])
print(matrix) #Matrix([[1, 2], [3, 4]])
print("matrix.shape = {}".format(matrix.shape())) #matrix.shape = (2, 2)
print("matrix.size = {}".format(matrix.size())) #matrix.size = 4
print("len(matrix) = {}".format(len(matrix))) #len(matrix) = 2
print("matrix[0][0] = {}".format(matrix[0, 0])) #matrix[0][0] = 1
matrix2 = Matrix([[5, 6], [7, 8]])
print(matrix2)
print("add: {}".format(matrix + matrix2)) #add: Matrix([[6, 8], [10, 12]])
print("subtract: {}".format(matrix - matrix2)) #subtract: Matrix([[-4, -4], [-4, -4]])
print("scalar-mul: {}".format(2 * matrix)) #scalar-mul: Matrix([[2, 4], [6, 8]])
print("scalar-mul: {}".format(matrix * 2)) #scalar-mul: Matrix([[2, 4], [6, 8]])
print("zero_2_3: {}".format(Matrix.zero(2, 3))) #zero_2_3: Matrix([[0, 0, 0], [0, 0, 0]])
T = Matrix([[1.5, 0], [0, 2]])
p = Vector([5, 3])
print("T.dot(p) = {}".format(T.dot(p))) #T.dot(p) = (7.5, 6)
P = Matrix([[0, 4, 5], [0, 0, 3]])
print("T.dot(P) = {}".format(T.dot(P))) #T.dot(P) = Matrix([[0.0, 6.0, 7.5], [0, 0, 6]])
print("A.dot(B) = {}".format(matrix.dot(matrix2)))#A.dot(B) = Matrix([[19, 22], [43, 50]])
print("B.dot(A) = {}".format(matrix2.dot(matrix)))#B.dot(A) = Matrix([[23, 34], [31, 46]])
print("P.T = {}".format(P.T())) #P.T = Matrix([[0, 0], [4, 0], [5, 3]])
I = Matrix.identity(2) #单位矩阵
print(I) #Matrix([[1, 0], [0, 1]])
print("A.dot(I) = {}".format(matrix.dot(I))) #A.dot(I) = Matrix([[1, 2], [3, 4]])
print("I.dot(A) = {}".format(I.dot(matrix))) #I.dot(A) = Matrix([[1, 2], [3, 4]])
import matplotlib.pyplot as plt
# from playLA.Matrix import Matrix
# from playLA.Vector import Vector
import math
if __name__ == "__main__":
points = [[0, 0], [0, 5], [3, 5], [3, 4], [1, 4], [1, 3], [2, 3], [2, 2], [1, 2], [1, 0]]
x = [point[0] for point in points]
y = [point[1] for point in points]
plt.figure(figsize=(5, 5)) #窗口
plt.xlim(-10, 10)
plt.ylim(-10, 10)
plt.plot(x, y)
# plt.show()
P = Matrix(points)
# T = Matrix([[2, 0], [0, 1.5]]) #缩放
# T = Matrix([[1, 0], [0, -1]]) #x翻转
# T = Matrix([[-1, 0], [0, 1]]) #y翻转
# T = Matrix([[-1, 0], [0, -1]]) #原点对称
# T = Matrix([[1, 0.5], [0, 1]]) #x错切
T = Matrix([[1, 0], [0.5, 1]]) #y错切
# theta = math.pi / 3
# T = Matrix([[math.cos(theta), math.sin(theta)], [-math.sin(theta), math.cos(theta)]])
# 逆时针旋转90度
# theta = math.pi / -2
# T = Matrix([[math.cos(theta), math.sin(theta)], [-math.sin(theta), math.cos(theta)]])
# 根据矩阵表示空间的法则,直接写出的逆时针旋转90度的变换矩阵
# T = Matrix([[0, -1], [1, 0]])
P2 = T.dot(P.T()) #转置
plt.plot([P2.col_vector(i)[0] for i in range(P2.col_num())],
[P2.col_vector(i)[1] for i in range(P2.col_num())])
plt.show()
import numpy as np
if __name__ == "__main__":
# 矩阵的创建
A = np.array([[1, 2], [3, 4]])
print(A)
# [[1 2]
# [3 4]]
# 矩阵的属性
print(A.shape) #(2, 2)
print(A.T)
# [[1 3]
# [2 4]]
# 获取矩阵的元素
print(A[1, 1]) #4
print(A[0])
print(A[:, 0])
print(A[1, :])
# 矩阵的基本运算
B = np.array([[5, 6], [7, 8]])
print(A + B)
print(A - B)
print(10 * A)
print(A * 10)
print(A * B)
print(A.dot(B))
p = np.array([10, 100])
print(A + p)
print(A + 1)
print(A.dot(p))
# 单位矩阵
I = np.identity(2)
print(I)
print(A.dot(I))
print(I.dot(A))
# 逆矩阵
invA = np.linalg.inv(A)
print(invA)
# [[-2. 1.]
# [1.5 - 0.5]]
print(invA.dot(A))
# [[1.0000000e+00 4.4408921e-16]
# [0.0000000e+00 1.0000000e+00]]
print(A.dot(invA))
# [[1.00000000e+00 1.11022302e-16]
# [0.00000000e+00 1.00000000e+00]]
# 只有方针才可逆
# C = np.array([[1, 2, 3], [4, 5, 6]])
# np.linalg.inv(C)
# from .Matrix import Matrix
# from .Vector import Vector
# from ._globals import is_zero
class LinearSystem:
def __init__(self, A, b = None):
assert b is None or A.row_num() == len(b), "row number of A must be equal to the length of b"
self._m = A.row_num()#行
self._n = A.col_num()#列
if b is None:
self.Ab = [A.row_vector(i) for i in range(self._m)]
if isinstance(b, Vector):
self.Ab = [Vector(A.row_vector(i).underlying_list() + [b[i]])
for i in range(self._m)]
if isinstance(b, Matrix):
self.Ab = [Vector(A.row_vector(i).underlying_list() + b.row_vector(i).underlying_list())
for i in range(self._m)]
self.pivots = [] # 记录了pivots列表,用于存储主元的列索引。
# 初始化线性方程组对象。
# A是一个矩阵对象,表示线性方程组的系数矩阵;
# b是一个向量或矩阵对象,表示线性方程组的常数项(可选).
# 根据A和b的类型,将其转换为增广矩阵Ab,其中每个行向量是 A的行向量和b的对应行向量的连接。
def _max_row(self, index_i, index_j, n):
best, ret = abs(self.Ab[index_i][index_j]), index_i
for i in range(index_i + 1, n):
if abs(self.Ab[i][index_j]) > best:
best, ret = abs(self.Ab[i][index_j]), i
return ret
# 在指定范围内查找具有最大绝对值的元素的行索引。
# index_i 是起始行索引,index_j是列索引,n是结束行索引。返回具有最大绝对值元素的行索引。
def _forward(self):
i, k = 0, 0
while i < self._m and k < self._n:
# 看Ab[i][k]位置是否可以是主元
max_row = self._max_row(i, k, self._m)
self.Ab[i], self.Ab[max_row] = self.Ab[max_row], self.Ab[i]#交换
if is_zero(self.Ab[i][k]):
k += 1#下一列
else:
# 将主元归为一
self.Ab[i] = self.Ab[i] / self.Ab[i][k]
for j in range(i + 1, self._m):
self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i]
self.pivots.append(k)
i += 1
# 使用高斯消元法进行前向消元,将增广矩阵Ab转化为行最简形式。在过程中将主元归一化,并进行消元操作
def _backward(self):
n = len(self.pivots)
for i in range(n - 1, -1, -1):
k = self.pivots[i]
# Ab[i][k]为主元
for j in range(i - 1, -1, -1):
self.Ab[j] = self.Ab[j] - self.Ab[j][k] * self.Ab[i]
# 使用高斯约当消元法进行后向消元,进一步将增广矩阵 Ab 转化为行最简形式。
def gauss_jordan_elimination(self):
"""如果有解,返回True;如果没有解,返回False"""
self._forward()
self._backward()
for i in range(len(self.pivots), self._m):
if not is_zero(self.Ab[i][-1]):
return False
return True
# 调用 _forward 和 _backward 方法进行高斯约当消元,求解线性方程组。
# 如果方程组有解,返回 True;如果方程组无解,返回 False。
def fancy_print(self):
for i in range(self._m):
print(" ".join(str(self.Ab[i][j]) for j in range(self._n)), end=" ")
print("|", self.Ab[i][-1])
# 以更漂亮的格式打印增广矩阵 Ab,方便观察结果。
def inv(A):
if A.row_num() != A.col_num():
return None
n = A.row_num()
ls = LinearSystem(A, Matrix.identity(n))
if not ls.gauss_jordan_elimination():
return None
invA = [[row[i] for i in range(n, 2*n)] for row in ls.Ab]
return Matrix(invA)
# 计算矩阵 A 的逆矩阵。
# 首先检查 A 是否为方阵,如果不是,则返回 None。
# 创建一个线性方程组对象 ls,以 A 和单位矩阵作为参数,使用高斯约当消元法求解.
# 如果方程组无解,则返回 None.
# 从线性方程组对象的增广矩阵 Ab 中提取逆矩阵的部分,并返回一个新的矩阵对象 invA.
def rank(A):
ls = LinearSystem(A)
ls.gauss_jordan_elimination()
zero = Vector.zero(A.col_num())
return sum([row != zero for row in ls.Ab])
# 计算矩阵 A 的秩。
# 创建一个线性方程组对象 ls,以 A 作为参数,使用高斯约当消元法求解。
# 在消元过程中,统计非零行向量的数量,并返回该数量作为矩阵 A 的秩.
# from playLA.Matrix import Matrix
# from playLA.Vector import Vector
# from playLA.LinearSystem import LinearSystem
# from playLA.LinearSystem import inv, rank
if __name__ == "__main__":
A = Matrix([[1, 2, 4], [3, 7, 2], [2, 3, 3]])
b = Vector([7, -11, 1])
ls = LinearSystem(A, b)
ls.gauss_jordan_elimination()
ls.fancy_print()
print()
# [-1, -2, 3]
A2 = Matrix([[1, -3, 5], [2, -1, -3], [3, 1, 4]])
b2 = Vector([-9, 19, -13])
ls2 = LinearSystem(A2, b2)
ls2.gauss_jordan_elimination()
ls2.fancy_print()
print()
# [2, -3, -4]
A3 = Matrix([[1, 2, -2], [2, -3, 1], [3, -1, 3]])
b3 = Vector([6, -10, -16])
ls3 = LinearSystem(A3, b3)
ls3.gauss_jordan_elimination()
ls3.fancy_print()
print()
# [-2, 1, -3]
A4 = Matrix([[3, 1, -2], [5, -3, 10], [7, 4, 16]])
b4 = Vector([4, 32, 13])
ls4 = LinearSystem(A4, b4)
ls4.gauss_jordan_elimination()
ls4.fancy_print()
print()
# [3, -4, 0.5]
A5 = Matrix([[6, -3, 2], [5, 1, 12], [8, 5, 1]])
b5 = Vector([31, 36, 11])
ls5 = LinearSystem(A5, b5)
ls5.gauss_jordan_elimination()
ls5.fancy_print()
print()
# [3, -3, 2]
A6 = Matrix([[1, 1, 1], [1, -1, -1], [2, 1, 5]])
b6 = Vector([3, -1, 8])
ls6 = LinearSystem(A6, b6)
ls6.gauss_jordan_elimination()
ls6.fancy_print()
print()
# [1, 1, 1]
print("--------------------------------------------------------------")
A7 = Matrix([[1, -1, 2, 0, 3],
[-1, 1, 0, 2, -5],
[1, -1, 4, 2, 4],
[-2, 2, -5, -1, -3]])
b7 = Vector([1, 5, 13, -1])
ls7 = LinearSystem(A7, b7)
ls7.gauss_jordan_elimination()
ls7.fancy_print()
print()
# 1.0 -1.0 0.0 -2.0 0.0 | -15.0
# 0.0 0.0 1.0 1.0 0.0 | 5.0
# 0.0 0.0 0.0 0.0 1.0 | 2.0
# 0.0 0.0 0.0 0.0 0.0 | 2.220446049250313e-16
A8 = Matrix([[2, 2],
[2, 1],
[1, 2]])
b8 = Vector([3, 2.5, 7])
ls8 = LinearSystem(A8, b8)
if not ls8.gauss_jordan_elimination():
print("No Solution!")
ls8.fancy_print()
print()
# No Solution!
# 1.0 0.0 | 1.0
# -0.0 1.0 | 0.5
# 0.0 0.0 | 5.0
A = Matrix([[1, 2], [3, 4]])
invA = inv(A)
print(invA)
#-2 1
# 1 -0.5
print(A.dot(invA))
# 1 0
# 0 1
print(invA.dot(A))
# 1 0
# 0 1
print()
print("rank A8 = {}".format(rank(A8))) #rank A8 = 2
print("rank A7 = {}".format(rank(A7))) #rank A7 = 3
print("rank A6 = {}".format(rank(A6))) #rank A6 = 3
强烈推荐5-9 总结:看待矩阵的四个重要视角_哔哩哔哩_bilibili
数据 系统 变换 空间(真的很透彻)
强烈推荐6-6 直观理解线性方程组解的结构(1)_哔哩哔哩_bilibili
视频里所有的直观理解都很好
下面是从p58开始的一些我不大熟悉的地方
LU分解目的是提高效率
代码
#from .Matrix import Matrix
#from .Vector import Vector
#from ._globals import is_zero
def lu(matrix):
assert matrix.row_num() == matrix.col_num(), "matrix must be a square matrix"
# 检查matrix是否为方阵,如果不是,则抛出断言错误
n = matrix.row_num()
A = [matrix.row_vector(i) for i in range(n)]
# 创建一个与 matrix 大小相同的矩阵A,其中每个行向量都是matrix的行向量。
L = [[1.0 if i == j else 0.0 for i in range(n)] for j in range(n)]
# 创建一个与matrix大小相同的单位下三角矩阵 L,其中主对角线上的元素为1,其他元素为0。
for i in range(n):
# 看A[i][i]位置是否可以是主元.对于每个对角线上的元素 A[i][i],检查是否为零。如果为零,表示主元为零,无法进行LU分解,返回 None(上三角为空,下三角为空)。
if is_zero(A[i][i]):
return None, None
else:
for j in range(i + 1, n):
p = A[j][i] / A[i][i]
A[j] = A[j] - p * A[i]
L[j][i] = p
# 否则,对于当前行i的下方的每一行j,计算倍数p,使得A[j][i] = p * A[i][i],并将其存储在矩阵L的相应位置
# 然后,通过从第i行中减去倍数p乘以第 i行,将矩阵 A进行消元操作。
return Matrix(L), Matrix([A[i].underlying_list() for i in range(n)])
# 返回矩阵 L 和经过消元后的矩阵 A
#from playLA.Matrix import Matrix
#from playLA.LU import lu
if __name__ == "__main__":
A1 = Matrix([[1, 2, 3], [4, 5, 6], [3, -3, 5]])
L1, U1 = lu(A1)
print(L1) #Matrix([[1.0, 0.0, 0.0], [4.0, 1.0, 0.0], [3.0, 3.0, 1.0]])
print(U1) #上三角up Matrix([[1, 2, 3], [0.0, -3.0, -6.0], [0.0, 0.0, 14.0]])
print(L1.dot(U1)) #验证 Matrix([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [3.0, -3.0, 5.0]])
print()
A2 = Matrix([[1, 4, 5, 3], [5, 22, 27, 11], [6, 19, 27, 31], [5, 28, 35, -8]])
L2, U2 = lu(A2)
print(L2)
print(U2)
print(L2.dot(U2))
print()
A3 = Matrix([[1, 2, 3], [3, 7, 14], [4, 13, 38]])
L3, U3 = lu(A3)
print(L3)
print(U3)
print(L3.dot(U3))
print()
PLU分解,更多
p65直观理解(推荐)
第八章总结8-8 本章小结:形成自己的知识图谱_哔哩哔哩_bilibili
# from .Matrix import Matrix
# from .LinearSystem import rank
def gram_schmidt_process(basis):#对给定的向量组 basis 进行Gram-Schmidt正交化过程
matrix = Matrix(basis)#将向量组转换为矩阵
assert rank(matrix) == len(basis)
# 检查矩阵的秩是否等于向量组的长度,如果不相等,则抛出断言错误,表示向量组线性相关,无法进行正交化。
res = [basis[0]]#存储正交化后的向量。第一个向量作为初始向量 p,将其添加到 res 中
for i in range(1, len(basis)):
p = basis[i]
for r in res:
p = p - basis[i].dot(r) / r.dot(r) * r
#将其与已经正交化的向量进行投影,然后将投影后的部分从 basis[i] 中减去
res.append(p) #将正交化后的向量 p 添加到 res 中。
return res;
def qr(A):#计算矩阵 A 的QR分解
assert A.row_num() == A.col_num(), "A must be square"
#首先检查 A 是否为方阵,如果不是,则抛出断言错误。
basis = [A.col_vector(i) for i in range(A.col_num())];
#列表 basis,其中每个元素为 A 的列向量
P = gram_schmidt_process(basis)#得到正交化后的向量组 P。
Q = Matrix([v / v.norm() for v in P]).T()
#将向量组 P 中的每个向量除以其自身的范数,得到单位向量组 Q。
R = Q.T().dot(A)
#将 Q 转置后与 A 进行矩阵乘法,得到上三角矩阵 R。
return Q, R # Q 是正交矩阵,R 是上三角矩阵。
# from playLA.Vector import Vector
# from playLA.GramSchmidtProcess import gram_schmidt_process
# from itertools import product
if __name__ == "__main__":
basis1 = [Vector([2, 1]), Vector([1, 1])]
res1 = gram_schmidt_process(basis1)
for row in res1:
print(row)
#(2, 1)
# (-0.19999999999999996, 0.4)
# 标准正交基
res1 = [row / row.norm() for row in res1]
for row in res1:
print(row)
#(0.8944271909999159, 0.4472135954999579)
#(-0.44721359549995787, 0.894427190999916)
print(res1[0].dot(res1[1])) #1.1102230246251565e-16(0)
print()
basis2 = [Vector([2, 3]), Vector([4, 5])]
res2 = gram_schmidt_process(basis2)
res2 = [row / row.norm() for row in res2]
for row in res2:
print(row)
print(res2[0].dot(res2[1]))
print()
#(0.5547001962252291, 0.8320502943378437)
# (0.8320502943378439, -0.5547001962252287)
# 4.996003610813204e-16
basis3 = [Vector([1, 0, 1]), Vector([3, 1, 1]), Vector([-1, -1, -1])]
res3 = gram_schmidt_process(basis3)
res3 = [row / row.norm() for row in res3]
for row in res3:
print(row)
print(sum(res3[i].dot(res3[j]) for i, j in product(range(3), repeat=2) if i != j))
print()
#(0.7071067811865475, 0.0, 0.7071067811865475)
# (0.5773502691896258, 0.5773502691896258, -0.5773502691896258)
# (0.40824829046386296, -0.816496580927726, -0.40824829046386296)
# -1.1102230246251565e-16
basis4 = [Vector([1, 1, 5, 2]), Vector([-3, 3, 4, -2]), Vector([-1, -2, 2, 5])]
res4 = gram_schmidt_process(basis4)
res4 = [row / row.norm() for row in res4]
for row in res4:
print(row)
print(sum(res4[i].dot(res4[j]) for i, j in product(range(3), repeat=2) if i != j))
print()
#(0.1796053020267749, 0.1796053020267749, 0.8980265101338746, 0.3592106040535498)
# (-0.6447334317039531, 0.4554538921211412, 0.2602593669263664, -0.5560086475245101)
# (-0.7426488253165523, -0.32682633521100585, -0.019776923309897908, 0.584179888538524 )
# 5.551115123125783e-17
basis5 = [Vector([1, 2, 3, 4]), Vector([2, 1, 1, 0]), Vector([3, 0, -1, 3])]
res5 = gram_schmidt_process(basis5)
res5 = [row / row.norm() for row in res5]
for row in res5:
print(row)
print(sum(res5[i].dot(res5[j]) for i, j in product(range(3), repeat=2) if i != j))
print()
# (0.18257418583505536, 0.3651483716701107, 0.5477225575051661, 0.7302967433402214)
# (0.8454337760700726, 0.2552252908890785, 0.1435642261251067, -0.44664425905588745)
# (0.4725854085508103, -0.2953658803442565, -0.6498049367573643, 0.5168902906024487)
# -2.7755575615628914e-16
# from playLA.Matrix import Matrix
# from playLA.GramSchmidtProcess import qr
if __name__ == "__main__":
A1 = Matrix([[1, 1, 2],
[1, 1, 0],
[1, 0, 0]])
Q1, R1 = qr(A1)
print(Q1)
print(R1)
print(Q1.dot(R1))
print()
# Matrix([[0.5773502691896258, 0.408248290463863, 0.7071067811865475],
# [0.5773502691896258, 0.408248290463863, -0.7071067811865475],
# [0.5773502691896258, -0.8164965809277259, 0.0]])
# Matrix([[1.7320508075688776, 1.1547005383792517, 1.1547005383792517],
# [1.1102230246251565e-16, 0.816496580927726, 0.816496580927726], [0.0, 0.0, 1.414213562373095]])
# Matrix(
# [[1.0000000000000002, 1.0000000000000002, 2.0],
# [1.0000000000000002, 1.0000000000000002, 4.440892098500626e-16],
# [1.0000000000000002, 2.220446049250313e-16, 2.220446049250313e-16]])
A2 = Matrix([[2, -1, -1],
[2, 0, 2],
[2, -1, 3]])
Q2, R2 = qr(A2)
print(Q2)
print(R2)
print(Q2.dot(R2))
# Matrix([[0.5773502691896258, -0.408248290463863, -0.7071067811865475], [0.5773502691896258, 0.8164965809277259, 1.1775693440128314e-16], [0.5773502691896258, -0.408248290463863, 0.7071067811865476]])
# Matrix([[3.4641016151377553, -1.1547005383792517, 2.3094010767585034], [-2.220446049250313e-16, 0.816496580927726, 0.8164965809277258], [4.440892098500626e-16, -1.1102230246251565e-16, 2.8284271247461907]])
# Matrix([[2.0, -1.0000000000000002, -1.0], [2.0000000000000004, -2.220446049250313e-16, 2.0000000000000004], [2.000000000000001, -1.0000000000000002, 3.000000000000001]])
P94总结10-8 本章小结和更多和投影相关的话题_哔哩哔哩_bilibili
最小二乘法
图形学+计算机视觉
行列式(按行排列)
import numpy as np
from numpy.linalg import eig
if __name__ == "__main__":
A1 = np.array([[4, -2],
[1, 1]]);
eigenvalues1, eigenvectors1 = eig(A1);
print(eigenvalues1)
print(eigenvectors1)
print()
#[3. 2.]
# [[0.89442719 0.70710678]
# [0.4472136 0.70710678]]
# 关于y=x翻转
A2 = np.array([[0, 1],
[1, 0]]);
eigenvalues2, eigenvectors2 = eig(A2);
print(eigenvalues2)
print(eigenvectors2)
print()
# [1. - 1.]
# [[0.70710678 - 0.70710678]
# [0.70710678 0.70710678]]
# 旋转90度
A3 = np.array([[0, -1],
[1, 0]]);
eigenvalues3, eigenvectors3 = eig(A3);
print(eigenvalues3)
print(eigenvectors3)
print()
# [0. + 1.j 0. - 1.j]
# [[0.70710678 + 0.j 0.70710678 - 0.j]
# [0. - 0.70710678j 0. + 0.70710678j]]
# 单位矩阵
A4 = np.array([[1, 0],
[0, 1]]);
eigenvalues4, eigenvectors4 = eig(A4);
print(eigenvalues4)
print(eigenvectors4)
print()
# [1. 1.]
# [[1. 0.]
# [0. 1.]]
# 几何重数为1
A5 = np.array([[3, 1],
[0, 3]]);
eigenvalues5, eigenvectors5 = eig(A5);
print(eigenvalues5)
print(eigenvectors5)
print()
# [3. 3.]
# [[1.00000000e+00 - 1.00000000e+00]
# [0.00000000e+00 6.66133815e-16]]
import numpy as np
from numpy.linalg import eig, inv
# from playLA.LinearSystem import rank
# from playLA.Matrix import Matrix
def diagonalize(A):
assert A.ndim == 2 #二维
assert A.shape[0] == A.shape[1] #长宽相等
eigenvalues, eigenvectors = eig(A) #特征值和特征向量
P = eigenvectors
if rank(Matrix(P.tolist())) != A.shape[0]:
print("Matrix can not be diagonalized!")
return None, None, None
#rank函数来检查特征向量矩阵P的秩是否等于矩阵A的维度。
# 如果不相等,说明矩阵A不可对角化,打印出相应的提示信息并返回None。
D = np.diag(eigenvalues)
#对角矩阵D,其中对角线上的元素为特征值数组eigenvalues的值。
Pinv = inv(P) #P的逆矩阵,保存在变量Pinv中.
return P, D, Pinv
if __name__ == "__main__":
A1 = np.array([[4, -2],
[1, 1]])
P1, D1, Pinv1 = diagonalize(A1)
print(P1)
#[[0.89442719 0.70710678]
# [0.4472136 0.70710678]]
print(D1)
#[[3. 0.]
#[0. 2.]]
print(Pinv1)
#[[ 2.23606798 -2.23606798]
#[-1.41421356 2.82842712]]
print(P1.dot(D1).dot(Pinv1))
#[[ 4. -2.]
#[ 1. 1.]]
print()
A2 = np.array([[3, 1],
[0, 3]])
P2, D2, Pinv2 = diagonalize(A2)
print(P2)
print(D2)
print(Pinv2)
print()
#Matrix can not be diagonalized!
# None
# None
# None
import numpy as np
from scipy.linalg import svd
if __name__ == "__main__":
A = np.array([[1, 2],
[3, 4],
[5, 6]])
U, s, VT = svd(A)
print(U)
#[[-0.2298477 0.88346102 0.40824829]
# [-0.52474482 0.24078249 -0.81649658]
# [-0.81964194 -0.40189603 0.40824829]]
print(s)
#[9.52551809 0.51430058]
print(VT)
#[[-0.61962948 -0.78489445]
# [-0.78489445 0.61962948]]
print()
Sigma = np.zeros(A.shape) #0
for i in range(len(s)):
Sigma[i][i] = s[i] #对角线
print(Sigma)
#[[9.52551809 0. ]
# [0. 0.51430058]
# [0. 0. ]]
print(U.dot(Sigma).dot(VT))
#[[1. 2.]
# [3. 4.]
# [5. 6.]]