In [1]:
# ANS-1
import numpy as np
def solve_linear_system_custom(A_in, b_in):
"""
使用不带行交换的高斯消元法求解 Ax=b,并返回 LU 分解。
这种方法严格对应手动计算的步骤。
"""
# 转换为 float 类型以避免整数除法截断
A = A_in.copy().astype(float)
b = b_in.copy().astype(float)
n = len(b)
# 初始化 L 和 U
# L 初始化为单位矩阵
L = np.eye(n)
# --- 1. 高斯消元过程 (Gaussian Elimination) ---
# 目标:将 A 转换为上三角矩阵 U,并记录乘数到 L
# 对应书中 Algorithm 12
for k in range(n-1): # 遍历每一列 k (作为主元列)
pivot = A[k, k]
if abs(pivot) < 1e-12:
raise ValueError("主元为 0,需要行交换(本代码仅演示不交换的情况)")
for i in range(k+1, n): # 遍历主元下方的每一行 i
# 计算乘数 m
multiplier = A[i, k] / pivot
# 将乘数存入 L 矩阵的对应位置
L[i, k] = multiplier
# 更新第 i 行:Row_i = Row_i - m * Row_k
A[i, k:] = A[i, k:] - multiplier * A[k, k:]
# 同步更新右端项 b (这就相当于把 b 放在增广矩阵里一起变换)
# b[i] = b[i] - m * b[k]
# 注意:实际上我们在 LU 分解思路中通常分步做,
# 但为了直观展示手动过程,我们可以把 b 视为增广列的一部分进行变换
b[i] = b[i] - multiplier * b[k]
# 此时,A 已经被变换成了上三角矩阵 U
U = A
# --- 2. 回代求解 (Back Substitution) ---
# 对应书中 Example 9 [cite: 78-83]
x = np.zeros(n)
for i in range(n-1, -1, -1):
# x[i] = (b[i] - sum(U[i,j]*x[j])) / U[i,i]
sum_ax = sum(U[i, j] * x[j] for j in range(i+1, n))
x[i] = (b[i] - sum_ax) / U[i, i]
return x, L, U
# --- 主程序 ---
print("--- 问题 1 解答 (求解) ---")
# 定义题目中的矩阵 A 和向量 b
A_input = np.array([
[1, 1, 2],
[4, 3, 0],
[0, 2, 2]
])
b_input = np.array([2, 5, 6])
print("--- 原始输入 ---")
print(f"A =\n{A_input}")
print(f"b = {b_input}\n")
# 调用求解函数
x_sol, L_mat, U_mat = solve_linear_system_custom(A_input, b_input)
print("--- 计算结果 ---")
print(f"解向量 x = {x_sol}")
print("(这与我们手工计算的 [-1, 3, 0] 完全一致)\n")
print("--- LU 分解结果 ---")
print(f"L (下三角矩阵) =\n{L_mat}")
print(f"U (上三角矩阵) =\n{U_mat}")
print("\n--- 验证 ---")
print(f"验证 L * U 是否等于 A:\n{np.dot(L_mat, U_mat)}")
print(f"验证 A * x 是否等于 b:\n{np.dot(A_input, x_sol)}")
print("\n" + "="*50 + "\n")
--- 问题 1 解答 (求解) --- --- 原始输入 --- A = [[1 1 2] [4 3 0] [0 2 2]] b = [2 5 6] --- 计算结果 --- 解向量 x = [-1. 3. -0.] (这与我们手工计算的 [-1, 3, 0] 完全一致) --- LU 分解结果 --- L (下三角矩阵) = [[ 1. 0. 0.] [ 4. 1. 0.] [ 0. -2. 1.]] U (上三角矩阵) = [[ 1. 1. 2.] [ 0. -1. -8.] [ 0. 0. -14.]] --- 验证 --- 验证 L * U 是否等于 A: [[1. 1. 2.] [4. 3. 0.] [0. 2. 2.]] 验证 A * x 是否等于 b: [2. 5. 6.] ==================================================
In [2]:
# ANS-2
import numpy as np
def gaussian_elimination_pivot(A_in, b_in, tol=1e-10):
"""
带列主元选择的高斯消元法。
如果主元绝对值小于 tol,则在下方寻找最大元素进行行交换。
"""
# 转换为 float 类型,防止整数除法
A = A_in.copy().astype(float)
b = b_in.copy().astype(float)
n = len(b)
# --- 1. 消元过程 (Forward Elimination) ---
for k in range(n-1):
# === 列主元选择 (Pivoting) ===
# 检查当前主元 A[k,k] 是否过小
if abs(A[k, k]) < tol:
# 在当前列 k 的下方 (k+1 到 n) 寻找绝对值最大的元素
# np.argmax 返回的是相对于切片的索引,所以要加上 k
max_index_rel = np.argmax(np.abs(A[k:, k]))
max_index = max_index_rel + k
# 如果找到的最大值还是很小,说明矩阵奇异
if abs(A[max_index, k]) < tol:
raise ValueError("矩阵是奇异的 (Singular Matrix)")
# 交换行 (Swap rows)
# 注意:要在 A 和 b 上同时进行交换
A[[k, max_index]] = A[[max_index, k]]
b[[k, max_index]] = b[[max_index, k]]
# print(f" [Log] Step {k}: Swapped row {k} with row {max_index}")
# === 标准消元步骤 ===
for i in range(k+1, n):
multiplier = A[i, k] / A[k, k]
A[i, k:] = A[i, k:] - multiplier * A[k, k:]
b[i] = b[i] - multiplier * b[k]
# --- 2. 回代过程 (Back Substitution) ---
x = np.zeros(n)
for i in range(n-1, -1, -1):
sum_ax = sum(A[i, j] * x[j] for j in range(i+1, n))
x[i] = (b[i] - sum_ax) / A[i, i]
return x
# === 题目数据 ===
A = np.array([
[0, 1, 4],
[2, 4, 6],
[5, 6, 0]
])
b = np.array([8, 15, 5])
print("--- 问题 2 解答 ---")
# 1. 使用我们手写的带 Pivoting 的高斯消元
try:
x_custom = gaussian_elimination_pivot(A, b)
print(f"自定义代码结果 (x): {x_custom}")
except Exception as e:
print(f"出错: {e}")
# 2. 使用 Python 内置的 'backslash' (numpy.linalg.solve)
# numpy 的 solve 内部使用 LAPACK 库,默认带有 pivoting
x_numpy = np.linalg.solve(A, b)
print(f"Numpy solve 结果 : {x_numpy}")
# 验证两者是否一致
if np.allclose(x_custom, x_numpy):
print("验证成功:自定义代码与内置函数结果一致。")
else:
print("验证失败:结果不一致。")
print("\n" + "="*50 + "\n")
--- 问题 2 解答 --- 自定义代码结果 (x): [-11. 10. -0.5] Numpy solve 结果 : [-11. 10. -0.5] 验证成功:自定义代码与内置函数结果一致。 ==================================================
In [3]:
# ANS-3
import time
import scipy.sparse as sparse
import scipy.sparse.linalg as splinalg
print("--- 问题 3 解答 (性能测试) ---")
# 定义测试的矩阵大小 n
ns = [10, 100, 1000, 2000, 4000, 8000]
print(f"{'N':<10} | {'Dense Time (s)':<15} | {'Sparse Time (s)':<15} | {'Speedup (x)':<10}")
print("-" * 60)
for n in ns:
# 构造右端项 b (全为 1)
b = np.ones(n)
# === (a) 稠密矩阵 (Dense Matrix) ===
# 使用 np.diag 构建矩阵
# 主对角线 -2,上方偏移1为 1,下方偏移-1为 1
main_diag = -2 * np.ones(n)
upper_diag = np.ones(n-1)
lower_diag = np.ones(n-1)
A_dense = np.diag(main_diag, 0) + np.diag(upper_diag,
1) + np.diag(lower_diag, -1)
# 计时:稠密求解
start_dense = time.time()
x_dense = np.linalg.solve(A_dense, b)
end_dense = time.time()
time_dense = end_dense - start_dense
# === (b) 稀疏矩阵 (Sparse Matrix) ===
# 使用 scipy.sparse.diags 构建 (类似 MATLAB 的 spdiags)
# data 存放对角线数据,offsets 存放对应的偏移量
data = [main_diag, upper_diag, lower_diag]
offsets = [0, 1, -1]
# format='csr' (Compressed Sparse Row) 是求解线性方程组常用的格式
A_sparse = sparse.diags(data, offsets, shape=(n, n), format='csr')
# 计时:稀疏求解
start_sparse = time.time()
# 使用 scipy 的稀疏求解器
x_sparse = splinalg.spsolve(A_sparse, b)
end_sparse = time.time()
time_sparse = end_sparse - start_sparse
# 计算加速比
speedup = time_dense / time_sparse if time_sparse > 0 else 0
print(f"{n:<10} | {time_dense:<15.6f} | {time_sparse:<15.6f} | {speedup:<10.1f}")
print("\n结论:随着 n 的增加,稠密矩阵求解时间呈指数级增长 (约 O(n^3)),")
print("而稀疏矩阵求解非常快,因为通过忽略零元素,它避免了大量的无效计算。")
print("对于 n=8000,加速比通常会达到几百甚至上千倍。")
print("\n" + "="*50 + "\n")
--- 问题 3 解答 (性能测试) --- N | Dense Time (s) | Sparse Time (s) | Speedup (x) ------------------------------------------------------------ 10 | 0.000080 | 0.000186 | 0.4 100 | 0.000153 | 0.000150 | 1.0 1000 | 0.021276 | 0.000856 | 24.8 2000 | 0.116385 | 0.000460 | 253.1 4000 | 0.886252 | 0.000883 | 1003.8 8000 | 6.578838 | 0.001818 | 3618.8 结论:随着 n 的增加,稠密矩阵求解时间呈指数级增长 (约 O(n^3)), 而稀疏矩阵求解非常快,因为通过忽略零元素,它避免了大量的无效计算。 对于 n=8000,加速比通常会达到几百甚至上千倍。 ==================================================
In [4]:
# ANS-4
print("--- 问题 4 解答 (化学配平) ---")
# 已知 y
y = 100.0
# 构建线性方程组 Ax = b
# 未知数向量为 [x, z, w]
# 行 1: C原子平衡 -> 7x - 7z = 0
# 行 2: N原子平衡 -> 3z = y
# 行 3: O原子平衡 -> 6z + w = 3y
A_chem = np.array([
[7, -7, 0], # x, z, w 的系数
[0, 3, 0],
[0, 6, 1]
])
b_chem = np.array([
0, # RHS for C
y, # RHS for N (1*y)
3 * y # RHS for O (3*y)
])
# 求解
try:
solution = np.linalg.solve(A_chem, b_chem)
x_sol, z_sol, w_sol = solution
print(f"当输入 Nitric Acid (y) = {y} 时,计算结果如下:")
print(f"x (Toluene) = {x_sol:.4f}")
print(f"z (TNT) = {z_sol:.4f}")
print(f"w (Water) = {w_sol:.4f}")
# --- 验证步骤 ---
# 我们使用了 C, N, O 的方程求解。现在用 H 的方程来验证结果是否一致。
# H方程: 8x + y = 5z + 2w
# 即: LHS = 8x + y, RHS = 5z + 2w
lhs_h = 8 * x_sol + y
rhs_h = 5 * z_sol + 2 * w_sol
print(f"\n验证 H (氢) 原子守恒:")
print(f"反应前 H 数量: {lhs_h:.4f}")
print(f"反应后 H 数量: {rhs_h:.4f}")
if np.isclose(lhs_h, rhs_h):
print("验证通过!方程组是相容的。")
else:
print("验证失败!")
except Exception as e:
print(f"求解失败: {e}")
--- 问题 4 解答 (化学配平) --- 当输入 Nitric Acid (y) = 100.0 时,计算结果如下: x (Toluene) = 33.3333 z (TNT) = 33.3333 w (Water) = 100.0000 验证 H (氢) 原子守恒: 反应前 H 数量: 366.6667 反应后 H 数量: 366.6667 验证通过!方程组是相容的。