defgaussian_elimination(A,b): n = len(A) assertlen(b) == n, "A and b size not match" for l in A: assertlen(l) == n, "Require square matrix" for i inrange(n-1): ##搜索最大值主元 for j inrange(i+1,n): ifabs(A[j][i]) > abs(A[i][i]): A[j], A[i] = A[i], A[j] b[j], b[i] = b[i], b[j] ##消元步骤 for j inrange(i+1,n): r = A[j][i]/A[i][i] A[j][i] = 0.0 for k inrange(i+1,n): A[j][k] -= r*A[i][k] b[j] -= r*b[i] ##回代方程求出x x = [0.0for _ inrange(n)] for i inrange(n-1,-1,-1): for j inrange(i+1,n): b[i] -= A[i][j]*x[j] x[i] = b[i]/A[i][i] return x
defjacobi_iter(A,b, max_iter:int = 100, tol: float = 1e-5): ##D^-1(b-(L+U)x) -> D^-1(b-Ax)+x n = len(A) assertlen(b) == n, "A and b size not match" for l in A: assertlen(l) == n, "Require square matrix" x = [0.0for _ inrange(n)] for _ inrange(max_iter): xk = x.copy() max_d = float("inf") for i inrange(n): s = 0.0 for j inrange(n): s += A[i][j]*x[j] delta = (b[i] - s)/A[i][i] max_d = max(max_d, abs(delta)) xk[i] += delta x = xk if max_d < tol: break return x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
defgauss_seidel_iter(A,b, max_iter:int = 100, tol: float = 1e-5): ##D^-1(b-(L+U)x) -> D^-1(b-Ax)+x n = len(A) assertlen(b) == n, "A and b size not match" for l in A: assertlen(l) == n, "Require square matrix" x = [0.0for _ inrange(n)] for _ inrange(max_iter): max_d = float("inf") for i inrange(n): s = 0.0 for j inrange(n): s += A[i][j]*x[j] delta = (b[i] - s)/A[i][i] max_d = max(max_d, abs(delta)) x[i] += delta if max_d < tol: break return x
import math defqr_factorization_gs(A): m = len(A) n = len(A[0]) if m > 0else0 for l in A: assertlen(l) == n, "Not a matrix" R = [[0.0for _i inrange(n)] for _j inrange(n)] Q = [[0.0for _i inrange(n)] for _j inrange(m)] for j inrange(n): y = [A[k][j] for k inrange(m)] for i inrange(j): for k inrange(m): R[i][j] += Q[k][i]*y[k] for k inrange(m): y[k] -= R[i][j]*Q[k][i] R[j][j] = math.sqrt(sum([yk*yk for yk in y])) for k inrange(m): Q[k][j] = y[k] / R[j][j] return Q, R
import math ##矩阵乘法 defmat_mul(a,b): m_a,n_a = len(a),len(a[0]) n_b,k_b = len(b),len(b[0]) assert n_a == n_b, "Matrix size must match" c = [[0.0for _j inrange(k_b)] for _i inrange(m_a)] for i inrange(m_a): for j inrange(k_b): for k inrange(n_a): c[i][j] += a[i][k]*b[k][j] return c
defqr_factorization_householder(A): m = len(A) n = len(A[0]) if m > 0else0 for l in A: assertlen(l) == n, "Not a matrix" Q = [[1.0if i == j else0.0for i inrange(m)] for j inrange(m)] for i inrange(min(m-1,n)): v0 = math.sqrt(sum([A[k][i]*A[k][i] for k inrange(i,m)])) v = [-A[k][i] for k inrange(i,m)] v[0] += -math.copysign(v0,v[0]) vn2 = sum([p*p for p in v]) H = [[1.0if i == j else0.0for i inrange(m)] for j inrange(m)] for ib inrange(i,m): for jb inrange(i,m): H[ib][jb] -= 2*v[ib-i]*v[jb-i]/vn2 Q = mat_mul(Q,H) A = mat_mul(H,A) return Q, A
幂迭代法(Power Iteration)是用于求解最大特征值(如果存在)及其对应特征向量的一种数值方法,它的思路非常简单:对于给定的一个任意矢量,用待求解矩阵的n次方左乘,当n足够大时其结果即为最大特征值对应的特征矢量,求解对应特征值可以通过瑞利商 与其对应的还有反幂迭代法(Inverse Power Iteration),即对求逆的矩阵进行幂迭代计算其最小特征值。
defqr_eig(A, max_iter: int = 100): m = len(A) for l in A: assertlen(l) == m, "Require square matrix" Q = [[1.0if i == j else0.0for i inrange(m)] for j inrange(m)] Q_res = Q R = A for _ inrange(max_iter): Q, R = qr_factorization_householder(mat_mul(R,Q)) Q_res = mat_mul(Q_res,Q) mat_eig = mat_mul(R,Q) eig = [mat_eig[i][i] for i inrange(m)] return eig, Q_res
在这个基础上可以改进得到求解任意方阵特征值的Shifted QR Algorithm,在QR迭代前为矩阵特征值增加一个偏置,使得调整后的矩阵特征值接近0,在迭代结束后去掉这个偏置,这样可以加快迭代收敛。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
defqr_shifted_eig(A, tol : float = 1e-8): m = len(A) for l in A: assertlen(l) == m, "Require square matrix" eig = [0.0for _ inrange(m)] for i inrange(m-1,0,-1): whilemax([abs(A[i][j]) for j inrange(i)]) > tol: mu = A[i][i] M1 = [[A[j][k] - (mu if j == k else0.0) for k inrange(i+1)] for j inrange(i+1)] Q, R = qr_factorization_householder(M1) A = mat_mul(R,Q) for k inrange(i+1): A[k][k] += mu eig[i] = A[i][i] A = [[A[j][k] for k inrange(i)] for j inrange(i)] eig[0] = A[0][0] return eig
defjacobi_eig(A, max_iter: int = 100,tol : float = 1e-8): m = len(A) for l in A: assertlen(l) == m, "Require square matrix" V = [[1.0if i == j else0.0for i inrange(m)] for j inrange(m)] for _ inrange(max_iter): p, q, max_val = 0, 1, abs(A[0][1]) for i inrange(m): for j inrange(i+1,m): ifabs(A[i][j]) > max_val: p, q, max_val = i, j, abs(A[p][q]) if max_val < tol: break theta = 0.5 * math.atan2(2*A[p][q], A[p][p] - A[q][q]) c, s = math.cos(theta), math.sin(theta)
## 矩阵J比较稀疏,如果要优化效率乘法可以拆开来直接计算 J = [[1.0if i == j else0.0for i inrange(m)] for j inrange(m)] J[p][p], J[p][q] = c, -s J[q][p], J[q][q] = s, c
J_T = [[1.0if i == j else0.0for i inrange(m)] for j inrange(m)] J_T[p][p], J_T[p][q] = c, s J_T[q][p], J_T[q][q] = -s, c A = mat_mul(mat_mul(J_T,A),J) V = mat_mul(V,J) eig = [A[i][i] for i inrange(m)] return eig, V
import math defjacobi_svd(A, max_iter: int = 100,tol : float = 1e-8): m = len(A) n = len(A[0]) if m > 0else0 for l in A: assertlen(l) == n, "Not a matrix" V = [[1.0if i == j else0.0for i inrange(n)] for j inrange(n)] a_col2 = [0.0for _ inrange(n)] for _ inrange(max_iter): converged = True for i inrange(n-1): for j inrange(i+1, n): a_col2[i] = sum([A[k][i]*A[k][i] for k inrange(m)]) a_col2[j] = sum([A[k][j]*A[k][j] for k inrange(m)]) ai_aj = sum([A[k][j]*A[k][i] for k inrange(m)]) ifabs(ai_aj) > tol * math.sqrt(a_col2[i] * a_col2[j]): converged = False theta = 0.5 * math.atan2(2*ai_aj, a_col2[j] - a_col2[i]) c, s = math.cos(theta), math.sin(theta) for k inrange(m): A[k][i], A[k][j] = c*A[k][i] - s*A[k][j], s*A[k][i] + c*A[k][j] V[k][i], V[k][j] = c*V[k][i] - s*V[k][j], s*V[k][i] + c*V[k][j] if converged: break s = [math.sqrt(a_col2[i]) for i inrange(n)] U = [[A[i][j]/s[j] for i inrange(n)] for j inrange(m)] return U, s, V