CE7453 Numerical Algorithms 期末高分超详细攻略 · 章节导航
本系列笔记覆盖 CE7453 期末考试所有核心模块,每章独立 markdown 文件,内容包括详细原理、公式推导、算法流程、例题全解、常见考点与英文关键词。适合零基础、考前冲刺、查漏补缺。
章节索引
-
Root Finding(方程求根)
- 二分法、牛顿法、割线法、误差与收敛性、典型例题
- 文件:
CE7453_期末高分速成.md
(含线性方程组)
-
Linear Systems(线性方程组)
- 高斯消元、LU分解、Jacobi/Gauss-Seidel、梯度下降、例题
- 文件:
CE7453_期末高分速成.md
-
Bezier/B-spline/Interpolation(曲线与插值)
- Bezier曲线、B-spline、de Casteljau与de Boor算法、插值多项式、例题
- 文件:
CE7453_03_Bezier_Bspline_Interpolation.md
-
Numerical Differentiation & Integration(数值微分与积分)
- 差分公式、梯形法、Simpson法、Romberg、Gaussian Quadrature、例题
- 文件:
CE7453_04_Numerical_Diff_Integration.md
-
Least Squares(最小二乘)
- 正规方程、QR分解、非线性最小二乘、GPS应用、例题
- 文件:
CE7453_05_Least_Squares.md
-
Eigenanalysis(特征值分析)
- 幂迭代、QR算法、Rayleigh商、PageRank、例题
- 文件:
CE7453_06_Eigenanalysis.md
-
Fourier Transform(傅里叶变换)
- DFT、FFT、三角插值、DCT、JPEG应用、例题
- 文件:
CE7453_07_Fourier_Transform.md
-
3D Data Registration(3D数据配准)
- ICP算法、SVD分解、点云配准、例题
- 文件:
CE7453_08_3D_Data_Registration.md
使用建议
- 每章内容均为独立 markdown 文件,可按需查阅或打印。
- 建议先看导航,按考试大纲查漏补缺。
- 例题部分适合考前手算练习,英文关键词便于查找原版资料。
- 如需某一算法的代码实现或更详细推导,可随时补充。
祝你期末高分!
CE7453 Numerical Algorithms 期末高分超详细攻略(第一章:Root Finding)
本章内容:方程求根(Root Finding)
适用对象:零基础/考前冲刺/快速查漏补缺
内容特色:详细原理、公式推导、算法流程、例题全解、英文关键词
1.1 基本概念与背景
-
什么是方程求根?
给定一个连续函数 ,我们想找到 使得 。这个 就叫做 的根(root)。 -
数学表示:
求解方程 的解 。 -
实际应用:
- 物理学:平衡点、临界值、交叉点计算
- 工程学:结构稳定性分析、电路设计
- 金融学:期权定价、收益率计算
- 计算机图形学:曲线/曲面求交
- 优化问题:目标函数的极值点(导数为零)
-
求根方法分类:
- 直接法:代数方程的解析解(如一元二次方程公式)
- 迭代法:通过迭代逼近根(本章重点)
- 混合法:结合多种方法的优点
1.2 主要算法与原理
1.2.1 二分法(Bisection Method)
-
原理:
如果连续函数 在区间 上满足 和 异号(即 ),根据中值定理,区间内必存在至少一个根。 -
算法流程:
- 检查 ,否则不能用二分法。
- 计算中点 。
- 判断 的符号:
- 如果 ,找到精确根,返回 。
- 如果 ,根在 ,令 。
- 如果 ,根在 ,令 。
- 重复2-3,直到区间足够小()或迭代次数达到上限。
-
收敛性分析:
- 每次迭代后,区间长度减半:
- 经过 次迭代后,区间长度:
- 要达到精度 ,需要迭代次数:
-
误差估计:
- 若 是第 次迭代的中点,则 ,其中 是真实根。
- 若要求解精确到小数点后 位,需满足
- 精度要求:若要求解精确到小数点后 位,即误差小于 ,则需要满足:
- 所需迭代次数:解上述不等式可得最小迭代次数 : 或更方便计算的形式:
-
优缺点:
- 优点:
- 一定收敛(全局收敛性,global convergence)
- 实现简单,稳定可靠
- 不需要导数信息
- 缺点:
- 收敛速度慢(线性收敛,收敛阶为1)
- 每次迭代只减少一位有效数字
- 只适用于区间端点异号的情况
- 无法处理重根
- 优点:
-
伪代码:
function Bisection(f, a, b, tol, max_iter) if f(a)*f(b) >= 0 then return "Error: 区间端点不异号" end if iter = 0 while (b-a)/2 > tol and iter < max_iter do c = (a+b)/2 if f(c) == 0 then return c // 找到精确根 else if f(a)*f(c) < 0 then b = c else a = c end if iter = iter + 1 end while return (a+b)/2 // 返回区间中点作为近似根 end function
-
公式:
-
英文关键词:bisection method, bracket, interval halving, binary search, convergence, tolerance, error bound
例题1:二分法求解
问题:用二分法求 在 内的根,精度要求为 。
分析: , 。 ,符合二分法使用条件。 初始区间 ,区间长度为 1。 要求精度 。 需要满足 ,即 。。 因为 , ,所以至少需要 次迭代。
迭代过程:
n | 区间 | 长度 | ||||
---|---|---|---|---|---|---|
0 | 0 | 1 | 0.5 | -0.375 | [0.5, 1] | 0.5 |
1 | 0.5 | 1 | 0.75 | 0.171875 | [0.5, 0.75] | 0.25 |
2 | 0.5 | 0.75 | 0.625 | -0.130859 | [0.625, 0.75] | 0.125 |
3 | 0.625 | 0.75 | 0.6875 | 0.012451 | [0.625, 0.6875] | 0.0625 |
4 | 0.625 | 0.6875 | 0.65625 | -0.061012 | [0.65625, 0.6875] | 0.03125 |
5 | 0.65625 | 0.6875 | 0.671875 | -0.024755 | [0.671875, 0.6875] | 0.015625 |
6 | 0.671875 | 0.6875 | 0.6796875 | -0.006270 | [0.6796875, 0.6875] | 0.0078125 |
结果:经过7次迭代,得到近似根 。此时区间长度为 ,满足精度要求。
Python代码示例:
def is_close(a, b, rtol=1e-5, atol=1e-8):
"""
检查两个数值是否足够接近,考虑浮点数精度问题
"""
return np.abs(a - b) <= (atol + rtol * np.abs(b))
def test_example1_bisection():
"""
验证例题1:用二分法求 f(x)=x^3+x-1 在 [0,1] 内的根,精度 10^-2
文档中的答案:x ≈ 0.6796875
"""
f = lambda x: x**3 + x - 1
# 二分法实现
def bisection_method(f, a, b, tol=1e-10, max_iter=100):
if f(a) * f(b) >= 0:
raise ValueError("区间端点函数值必须异号")
iter_count = 0
while (b - a) / 2 > tol and iter_count < max_iter:
c = (a + b) / 2
if f(c) == 0:
return c
elif f(a) * f(c) < 0:
b = c
else:
a = c
iter_count += 1
return (a + b) / 2
# 用自己实现的二分法计算
root_bisection = bisection_method(f, 0, 1, tol=1e-2)
# 用scipy的方法计算精确值
root_exact = optimize.root_scalar(f, bracket=[0, 1], method='brentq').root
# 文档中给出的答案
root_document = 0.6796875
# 验证自己实现的方法是否与文档答案一致
# 这里我们只展示计算过程,验证部分在test文件中执行
print(f"二分法计算结果: {root_bisection}")
print(f"文档答案: {root_document}")
print(f"Scipy精确解: {root_exact}")
print(f"函数在文档答案处的值: {f(root_document)}")
# 运行示例 (需要 import numpy as np 和 from scipy import optimize)
# test_example1_bisection()
1.2.2 牛顿法(Newton's Method)
-
原理:
利用函数的局部线性近似(泰勒展开)逐步逼近根。在当前点 处作切线,切线与 轴的交点作为下一个近似值 。 -
数学推导:
在 处展开 的一阶泰勒级数:令 ,解得:
这就是牛顿法的迭代公式:
-
几何解释:
每次迭代相当于用切线近似函数,切线与 轴的交点作为下一个近似值。 -
几何解释: 在点 处作函数 的切线,切线的方程为 。令 ,解出切线与 轴的交点横坐标,即为 。
-
算法流程:
- 选择初始值 (初值选择很重要!)
- 计算 和
- 计算
- 检查收敛条件:
- 若 (解的变化小)或
- 若 (函数值接近零)或
- 达到最大迭代次数 则停止;否则 ,回到步骤2
- 停止准则(Stopping Criteria):
迭代过程需要一个明确的停止条件,以避免无限循环并确保达到所需精度。常用准则包括:
- 解的绝对误差:。当连续两次迭代的解足够接近时停止。
- 解的相对误差: (当 )。适用于解的数量级未知或变化较大时。
- 函数值接近零:。当函数值足够接近零时停止。
- 达到最大迭代次数:设置一个迭代上限
max_iter
,防止算法因不收敛或收敛过慢而无限运行。 实际应用中通常组合使用这些准则。
-
收敛性分析:
- 若初值足够接近根,且 (非重根),则牛顿法具有二次收敛性(quadratic convergence)
- 误差关系:,其中 为常数
- 对于重根(),收敛阶降为线性
-
优缺点:
- 优点:
- 收敛速度快(通常为二次收敛)
- 每次迭代可使有效数字翻倍
- 适用于高精度计算
- 缺点:
- 需要计算导数
- 初值选择敏感,可能不收敛或收敛到非预期根
- 在导数接近零处可能不稳定
- 对重根收敛较慢
- 优点:
-
伪代码:
function Newton(f, f_prime, x0, tol_x, tol_f, max_iter) x = x0 iter = 0 while iter < max_iter do fx = f(x) if |fx| < tol_f then return x // 函数值足够接近零 end if fpx = f_prime(x) if fpx == 0 then return "Error: 导数为零,无法继续" end if x_new = x - fx/fpx if |x_new - x| < tol_x then return x_new // 解的变化足够小 end if x = x_new iter = iter + 1 end while return "Warning: 达到最大迭代次数" end function
-
英文关键词:Newton's method, Newton-Raphson method, tangent method, quadratic convergence, initial guess, derivative, root finding
例题2:牛顿法求解
问题:用牛顿法求 的正根,取初值 ,精度要求 。
分析: 迭代公式:
迭代过程:
结果:,满足精度要求。近似根 。 (实际根为 )
Python代码示例:
def test_example2_newton():
"""
验证例题2:用牛顿法求 f(x)=x^2-2 的根,x₀=1,精度 10^-4
文档中的答案:x ≈ 1.4142
"""
f = lambda x: x**2 - 2
df = lambda x: 2*x
# 牛顿法实现
def newton_method(f, df, x0, tol=1e-10, max_iter=100):
x = x0
for i in range(max_iter):
fx = f(x)
if abs(fx) < tol:
return x
dfx = df(x)
if dfx == 0:
raise ValueError("导数为零,牛顿法失效")
x_new = x - fx / dfx
if abs(x_new - x) < tol:
return x_new
x = x_new
return x
# 用自己实现的牛顿法计算
root_newton = newton_method(f, df, 1, tol=1e-4)
# 文档中给出的答案
root_document = 1.4142
# 精确值是根号2
root_exact = np.sqrt(2)
# 验证部分在test文件中执行
print(f"牛顿法计算结果: {root_newton}")
print(f"文档答案: {root_document}")
print(f"精确解: {root_exact}")
print(f"函数在文档答案处的值: {f(root_document)}")
# 运行示例 (需要 import numpy as np)
# test_example2_newton()
例题3:牛顿法求解
问题:用牛顿法求解 的根,取初始值 。
分析: 迭代公式:
迭代过程:
结果:迭代几次后,解趋于稳定。近似根 。
Python代码示例:
def test_example3_newton():
"""
验证例题3:用牛顿法求解 f(x) = e^x - x - 2 的根,初始值 x₀ = 1
文档中的答案:x ≈ 1.146
"""
f = lambda x: np.exp(x) - x - 2
df = lambda x: np.exp(x) - 1
# 使用上面的牛顿法实现
root_newton = newton_method(f, df, 1, tol=1e-4)
# 用scipy的方法计算精确值
root_exact = optimize.root_scalar(f, x0=1, fprime=df, method='newton').root
# 文档中给出的答案
root_document = 1.146
# 验证部分在test文件中执行
print(f"牛顿法计算结果: {root_newton}") # 实际计算会更精确
print(f"文档答案: {root_document}")
print(f"Scipy精确解: {root_exact}")
print(f"函数在文档答案处的值: {f(root_document)}")
# 运行示例 (需要 import numpy as np 和 from scipy import optimize, 以及上面的 newton_method 函数)
# test_example3_newton()
牛顿法求 的例题
问题:使用牛顿法计算 ,取初值 。
分析: 求 等价于求 的正根。 迭代公式:
迭代过程:
结果:迭代3次后,得到近似根 。
Python代码示例:
def test_sqrt3_newton():
"""
验证牛顿法求 sqrt(3) 的例题
文档中的答案:sqrt(3) ≈ 1.73205
"""
f = lambda x: x**2 - 3
df = lambda x: 2*x
# 牛顿法迭代公式的简化版本
def newton_sqrt3(x0, iterations=3):
x = x0
for _ in range(iterations):
x = 0.5 * (x + 3/x)
return x
# 用简化的牛顿法计算(文档中的迭代过程)
root_newton = newton_sqrt3(2, iterations=3)
# 文档中给出的答案
root_document = 1.73205
# 精确值是根号3
root_exact = np.sqrt(3)
# 验证部分在test文件中执行
print(f"简化牛顿法计算结果 (3次迭代): {root_newton}")
print(f"文档答案: {root_document}")
print(f"精确解: {root_exact}")
# 运行示例 (需要 import numpy as np)
# test_sqrt3_newton()
1.2.3 割线法(Secant Method)
-
原理:
牛顿法的变种,用差商近似导数,避免显式计算导数。使用前两次迭代点连线的斜率代替导数。 -
迭代公式:
-
数学推导:
用差商近似导数:代入牛顿法公式得到割线法公式。
-
几何解释: 割线法用连接点 和 的割线来近似函数 。割线与 轴的交点即为下一个近似值 。
-
算法流程:
- 选择两个初始点 和
- 计算
- 检查收敛条件,若满足则停止;否则 ,回到步骤2
-
收敛性分析:
- 收敛阶约为 (黄金分割比)
- 介于线性收敛和二次收敛之间,称为超线性收敛(superlinear convergence)
-
优缺点:
- 优点:
- 不需要计算导数
- 收敛速度优于二分法
- 每次迭代只需一次函数评估(牛顿法需要函数和导数)
- 缺点:
- 收敛速度低于牛顿法
- 需要两个初始点
- 可能出现分母接近零的情况(两点函数值接近)
- 收敛性不如二分法可靠
- 优点:
-
伪代码:
function Secant(f, x0, x1, tol, max_iter) iter = 0 while iter < max_iter do f0 = f(x0) f1 = f(x1) if |f1| < tol then return x1 end if if f1 == f0 then return "Error: 分母为零,无法继续" end if x_new = x1 - f1*(x1-x0)/(f1-f0) if |x_new - x1| < tol then return x_new end if x0 = x1 x1 = x_new iter = iter + 1 end while return "Warning: 达到最大迭代次数" end function
-
英文关键词:secant method, finite difference Newton, superlinear convergence, initial points
例题4:割线法求解
问题:用割线法求 的根,取初值 。
分析: 迭代公式:
迭代过程:
结果:迭代5次后,函数值非常接近零。近似根 。
Python代码示例:
def test_secant_example():
"""
验证割线法求 f(x)=x^3+x-1 的根的例题
文档中的答案:x ≈ 0.6822 (文档计算过程有微小差异,取0.682)
"""
f = lambda x: x**3 + x - 1
# 割线法实现
def secant_method(f, x0, x1, tol=1e-10, max_iter=100):
for i in range(max_iter):
f0, f1 = f(x0), f(x1)
if abs(f1) < tol:
return x1
if f1 == f0:
raise ValueError("割线法分母为零")
x_new = x1 - f1 * (x1 - x0) / (f1 - f0)
if abs(x_new - x1) < tol:
return x_new
x0, x1 = x1, x_new
return x1
# 用自己实现的割线法计算
root_secant = secant_method(f, 0, 1, tol=1e-4)
# 用scipy的方法计算精确值
root_exact = optimize.root_scalar(f, bracket=[0, 1], method='brentq').root
# 文档中给出的答案 (根据我们的计算是 0.682)
root_document = 0.682
# 验证部分在test文件中执行
print(f"割线法计算结果: {root_secant}")
print(f"文档答案 (按计算过程): {root_document}")
print(f"Scipy精确解: {root_exact}")
print(f"函数在文档答案处的值: {f(root_document)}")
# 运行示例 (需要 import numpy as np 和 from scipy import optimize)
# test_secant_example()
例题5:割线法求解
问题:用割线法求 的根,取初值 。
分析:
迭代过程:
结果:迭代几次后,解趋于稳定。近似根 。
Python代码示例:
def test_cos_x_example():
"""
验证割线法求 f(x) = cos(x) - x 的根的例题
文档中的答案:x ≈ 0.739
"""
f = lambda x: np.cos(x) - x
# 使用上面的割线法实现
root_secant = secant_method(f, 0.5, 1, tol=1e-4)
# 用scipy的方法计算精确值
root_exact = optimize.root_scalar(f, bracket=[0, 1], method='brentq').root
# 文档中给出的答案
root_document = 0.739
# 验证部分在test文件中执行
print(f"割线法计算结果: {root_secant}")
print(f"文档答案: {root_document}")
print(f"Scipy精确解: {root_exact}")
print(f"函数在文档答案处的值: {f(root_document)}")
# 运行示例 (需要 import numpy as np 和 from scipy import optimize, 以及上面的 secant_method 函数)
# test_cos_x_example()
1.2.4 Regula Falsi 法(假位法)
-
原理:
结合二分法的可靠性和割线法的快速收敛性。使用割线法确定下一个迭代点,但保持区间端点异号。 -
迭代公式:
与割线法相同,但每次迭代后保留使函数值异号的区间。 -
算法流程:
- 初始区间 满足
- 计算割线法的下一个点
- 若 ,则 ;否则
- 重复2-3直到收敛
-
优缺点:
- 优点:结合了二分法的可靠性和割线法的快速收敛
- 缺点:在某些情况下可能收敛缓慢
-
英文关键词:regula falsi, false position method, bracketing method, hybrid method
1.2.5 Brent 方法
-
原理:
结合二分法、割线法和反二次插值(inverse quadratic interpolation)的优点,是实际应用中最常用的求根方法之一。 -
特点:
- 保证收敛(如二分法)
- 在良好条件下快速收敛(如割线法)
- 使用三点插值进一步加速收敛
- MATLAB 的
fzero
函数和 Python 的scipy.optimize.brentq
使用此方法
-
英文关键词:Brent's method, inverse quadratic interpolation, guaranteed convergence, hybrid method
1.3 典型例题与详细解答
例题1:用二分法求 在 内的根,精度
解答步骤:
- 检查端点:, ,满足 ,可用二分法。
- 第一次迭代:
- ,根在
- 第二次迭代:
- ,根在
- 第三次迭代:
- ,根在
- 第四次迭代:
- ,根在
- 检查精度:,继续迭代
- 第五次迭代:
- ,根在
- 检查精度:,继续迭代
- 第六次迭代:
- ,根在
- 检查精度:,继续迭代
- 第七次迭代:
- ,根在
- 检查精度:,满足精度要求,停止迭代
答案:,精确根约为 ,误差在允许范围内。
例题2:用牛顿法求 的根,,精度
解答步骤:
- 计算 ,
- 第一次迭代:
- ,
- 第二次迭代:
- ,
- 第三次迭代:
- ,
- 第四次迭代:
- ,
- 检查精度:,满足精度要求
答案:,即 的近似值。
收敛性分析:
- 第一次迭代:误差约
- 第二次迭代:误差约 (误差平方级减小)
- 第三次迭代:误差约 (继续平方级减小)
- 体现了牛顿法的二次收敛特性
例题3:用牛顿法求解 的根,初始值
解答步骤:
- 计算 ,
- 第一次迭代:
- 第二次迭代:
- 第三次迭代:
- 已经足够接近零,可以停止迭代
答案:
验证:,,方程成立。
1.4 常见考点与易错点
-
初值选择:
- 牛顿法对初值敏感,选择不当可能导致不收敛或收敛到非预期根
- 二分法需要确保区间端点函数值异号
- 割线法需要两个初始点,且不能使函数值相等
-
收敛判断:
- 解的变化:
- 函数值接近零:
- 两种判断标准可能导致不同的停止时机
-
收敛速度比较:
- 二分法:线性收敛(收敛阶为1)
- 牛顿法:二次收敛(收敛阶为2)
- 割线法:超线性收敛(收敛阶约为1.618)
-
特殊情况处理:
- 重根问题:牛顿法对重根收敛较慢
- 导数为零:牛顿法可能失效
- 函数不连续:需要谨慎选择区间
-
误差分析:
- 截断误差:由算法本身引入
- 舍入误差:由计算机浮点运算引入
- 两种误差的平衡考虑
1.5 实际应用案例
1.5.1 计算机图形学中的曲线求交
在计算机图形学中,求解两条参数曲线 和 的交点,可以转化为求解方程组:
这是一个二元非线性方程组,可以使用牛顿法的多维扩展(牛顿-拉夫森法)求解。
1.5.2 机器学习中的优化问题
在机器学习中,寻找损失函数的最小值点,需要求解梯度为零的方程:
这可以使用牛顿法或其变种(如拟牛顿法)求解。
1.5.3 物理模拟中的平衡点
在物理系统模拟中,寻找系统的平衡状态,往往需要求解力平衡方程:
这类问题通常使用牛顿法或混合方法求解。
1.6 英文术语对照表
中文术语 | 英文术语 |
---|---|
方程求根 | Root Finding |
二分法 | Bisection Method |
牛顿法 | Newton's Method / Newton-Raphson Method |
割线法 | Secant Method |
假位法 | Regula Falsi / False Position Method |
收敛性 | Convergence |
收敛阶 | Order of Convergence |
线性收敛 | Linear Convergence |
二次收敛 | Quadratic Convergence |
超线性收敛 | Superlinear Convergence |
全局收敛 | Global Convergence |
局部收敛 | Local Convergence |
误差估计 | Error Estimation |
截断误差 | Truncation Error |
舍入误差 | Round-off Error |
迭代法 | Iterative Method |
直接法 | Direct Method |
混合法 | Hybrid Method |
CE7453 Numerical Algorithms 期末高分超详细攻略(第二章:Linear Systems)
本章内容:线性方程组(Linear Systems)
适用对象:零基础/考前冲刺/快速查漏补缺
内容特色:详细原理、公式推导、算法流程、例题全解、英文关键词
2.1 基本概念与背景
-
背景与重要性:
- 核心地位:线性方程组是现代科学与工程计算的基石。无论是模拟物理现象、设计复杂系统,还是分析海量数据,都离不开线性方程组的求解。
- 广泛应用:从桥梁结构的应力分析、电路网络的电流电压计算,到天气预报的数值模拟、机器学习模型的参数训练,再到经济系统的投入产出分析,线性方程组无处不在。
- 问题转化:许多看似非线性的复杂问题,在局部近似或迭代求解的过程中,往往会被转化为一系列线性方程组来处理,例如求解非线性方程组的牛顿法、偏微分方程的有限元/有限差分法等。
- 性能关键:求解线性方程组的算法效率(速度)和数值稳定性(精度、抗干扰能力)直接决定了相关应用的可行性和可靠性。特别是在处理大规模问题时,高效且稳健的线性求解器至关重要。
-
什么是线性方程组?
一组形如 的方程,其中 是 系数矩阵, 是 未知向量, 是 常数向量。 -
线性方程组的类型:
- 方程数 = 未知数():方阵系统,可能有唯一解、无穷多解或无解
- 方程数 > 未知数():超定系统,通常无精确解,可用最小二乘法求近似解
- 方程数 < 未知数():欠定系统,通常有无穷多解
-
解的存在性与唯一性:
- 若 ( 满秩),则有唯一解
- 若 ( 奇异),则有无穷多解或无解
- 若 无解,则称该方程组不相容(inconsistent)
-
实际应用举例:
- 工程结构分析 (Structural Engineering):在使用有限元方法 (Finite Element Method, FEM) 分析桥梁、建筑等结构受力时,需要求解大型稀疏线性方程组 ,其中 是结构刚度矩阵, 是节点位移向量, 是外加载荷向量。
- 电路分析 (Circuit Analysis):根据基尔霍夫定律 (Kirchhoff's Laws) 分析复杂电路时,节点电压法或网孔电流法会导出一组线性方程来确定各处的电压和电流。
- 计算流体力学 (Computational Fluid Dynamics, CFD):在模拟流体流动时,离散化的 Navier-Stokes 方程通常需要在每个时间步求解压力泊松方程等线性系统。
- 计算机图形学 (Computer Graphics):在进行三维建模、渲染和动画时,几何变换(旋转、缩放、平移)、光照模型(辐射度方法)、物理模拟(布料、流体)等都可能涉及线性方程组。
- 机器学习与数据科学 (Machine Learning & Data Science):线性回归、岭回归 (Ridge Regression)、支持向量机 (SVM) 的对偶问题、主成分分析 (PCA) 中的协方差矩阵特征分解等都与线性代数和线性方程组密切相关。最小二乘法是数据拟合和参数估计的基础。
- 经济学 (Economics):投入产出模型 (Input-Output Model) 分析国民经济各部门之间的相互依赖关系,其核心是求解一个线性方程组。
- 网络流分析 (Network Flow Analysis):在交通网络、通信网络或物流网络中,分析流量分配和优化路径常常需要解流量平衡方程,这也是线性方程组。
2.2 主要算法与原理
理论补充
- 高斯消元法的数学基础:
- 通过初等行变换(行交换、数乘、加法)将矩阵化为上三角形,等价于解线性方程组的消元过程。
- 消元过程本质是消去未知数,逐步简化方程组结构。
- 主元选择的数值意义:
- 主元过小会导致舍入误差放大,甚至数值不稳定。
- 部分主元消去法可显著提高算法的鲁棒性。
- 误差与稳定性分析:
- 舍入误差在消元和回代过程中逐步累积,尤其在主元较小时更为严重。
- 对病态矩阵(条件数大)应优先考虑主元策略或正交分解法。
2.2.1 高斯消元法(Gaussian Elimination)
-
基本原理:
通过初等行变换,将增广矩阵 转化为行阶梯形(上三角形),然后通过回代求解未知数。 -
算法流程:
- 前向消元(Forward Elimination):
- 对每一列 (从左到右)
- 选择主元(通常是当前列中最大的元素,称为部分主元消去法)
- 必要时进行行交换
- 用主元行消去下方各行的该列元素
- 回代(Back Substitution):
- 从最后一个未知数开始,逐个求解
- ,
- 前向消元(Forward Elimination):
-
数学表示:
- 消元过程:,其中 是乘数
- 增广矩阵变换:,其中 是上三角矩阵
-
部分主元消去法(Partial Pivoting):
- 在每一步消元前,选择当前列中绝对值最大的元素作为主元
- 通过行交换将主元移至对角线位置
- 目的:提高数值稳定性,减小舍入误差
-
计算复杂度:
- 前向消元:
- 回代:
- 总体:
-
伪代码:
function GaussianElimination(A, b) n = size(A, 1) // 矩阵A的行数 // 前向消元 for k = 1 to n-1 do // 部分主元选择 max_index = k for i = k+1 to n do if |A[i,k]| > |A[max_index,k]| then max_index = i end if end for // 行交换 if max_index != k then swap A[k,:] and A[max_index,:] swap b[k] and b[max_index] end if // 消元 for i = k+1 to n do m = A[i,k] / A[k,k] A[i,k] = 0 for j = k+1 to n do A[i,j] = A[i,j] - m * A[k,j] end for b[i] = b[i] - m * b[k] end for end for // 回代 x = new array of size n for i = n downto 1 do sum = 0 for j = i+1 to n do sum = sum + A[i,j] * x[j] end for x[i] = (b[i] - sum) / A[i,i] end for return x end function
误差与数值稳定性
-
舍入误差来源:有限精度下的加减乘除运算。
-
主元策略:通过行交换避免主元过小,减少误差放大。
-
病态系统:条件数大时,微小扰动会导致解大幅变化,应采用更稳定的算法(如QR分解)。
-
优缺点:
- 优点:
- 直接法,有限步可得精确解
- 实现简单,适用于一般线性方程组
- 缺点:
- 计算量大,不适合大规模稀疏矩阵
- 舍入误差可能累积
- 不利于并行计算
- 优点:
-
英文关键词:Gaussian elimination, row operation, pivoting, partial pivoting, upper triangular, back substitution, forward elimination, multiplier
例题1:高斯消元法解二元线性方程组 (1)
问题:用高斯消元法求解方程组:
解答步骤:
- 增广矩阵:
- 消元:目标是将第二行的第一个元素变为0。
- 计算乘数:
- 第二行减去第一行的 倍:
- 新第二行:
- 新第二行:
- 变换后的矩阵:
- 回代:
- 从第二行解出 :
- 将 代入第一行:
答案:
Python代码示例:
import numpy as np
def solve_gaussian_elimination(A_in, b_in):
"""使用高斯消元法解 Ax = b"""
A = np.array(A_in, dtype=float)
b = np.array(b_in, dtype=float)
n = len(b)
# 构建增广矩阵
Ab = np.hstack((A, b.reshape(-1, 1)))
# 前向消元
for k in range(n - 1):
# 简单主元选择(可选,这里为简化未实现)
# if np.abs(Ab[k, k]) < 1e-10:
# # 寻找下方绝对值最大的行进行交换
# max_idx = k + np.argmax(np.abs(Ab[k+1:, k])) + 1
# Ab[[k, max_idx]] = Ab[[max_idx, k]] # 行交换
if np.abs(Ab[k, k]) < 1e-10:
raise ValueError("主元为零或过小,无法进行消元")
for i in range(k + 1, n):
m = Ab[i, k] / Ab[k, k]
Ab[i, k:] = Ab[i, k:] - m * Ab[k, k:]
# 回代
x = np.zeros(n)
for i in range(n - 1, -1, -1):
if np.abs(Ab[i, i]) < 1e-10:
raise ValueError("主元为零或过小,无法回代")
sum_ax = np.dot(Ab[i, i+1:n], x[i+1:n])
x[i] = (Ab[i, n] - sum_ax) / Ab[i, i]
return x
# 例题1
A1 = [[2, 3], [5, 4]]
b1 = [8, 13]
x1 = solve_gaussian_elimination(A1, b1)
print(f"例题1 解: x = {x1[0]}, y = {x1[1]}")
# 验证 (可选)
# x_document = np.array([1, 2])
# assert np.allclose(x1, x_document)
例题2:高斯消元法解二元线性方程组 (2)
问题:用高斯消元法求解方程组:
解答步骤:
- 增广矩阵:
- 消元:
- 计算乘数:
- 第二行减去第一行的 倍:
- 新第二行:
- 新第二行:
- 变换后的矩阵:
- 回代:
- 从第二行解出 :
- 将 代入第一行:
答案:
Python代码示例:
# 使用上面定义的 solve_gaussian_elimination 函数
A2 = [[3, 2], [1, 4]]
b2 = [7, 5]
x2 = solve_gaussian_elimination(A2, b2)
print(f"例题2 解: x = {x2[0]}, y = {x2[1]}")
# 验证 (可选)
# x_document = np.array([1.8, 0.8])
# assert np.allclose(x2, x_document)
理论补充
- LU分解的本质:
- 将消元过程矩阵化, 记录消元乘数, 记录消元后的上三角结构。
- 形式适用于需要行交换的情况。
- 工程应用:
- 多次求解 ,只需分解一次 ,大幅提升效率。
- 与高斯消元的关系:
- LU分解是高斯消元的矩阵表达,便于理论分析和高效实现。
2.2.2 LU分解(LU Decomposition)
-
基本原理:
将系数矩阵 分解为下三角矩阵 和上三角矩阵 的乘积,即 。然后通过解 和 两个三角系统来求解原方程组。 -
与高斯消元的关系:
- LU分解本质上是高斯消元的矩阵形式
- 高斯消元中的乘数 构成了 矩阵的元素
- 消元后的上三角矩阵即为
-
分解过程:
- 不使用行交换时:
- 使用行交换时:,其中 是置换矩阵
-
求解流程:
- 分解
- 解 (前代)
- 解 (回代)
-
计算复杂度:
- 分解:
- 求解:
- 对于多个右端项 ,只需分解一次,每次求解只需
-
伪代码:
function LUDecomposition(A) n = size(A, 1) L = identity matrix of size n U = copy of A for k = 1 to n-1 do for i = k+1 to n do L[i,k] = U[i,k] / U[k,k] for j = k to n do U[i,j] = U[i,j] - L[i,k] * U[k,j] end for end for end for return L, U end function function SolveLU(L, U, b) n = size(L, 1) // 解 Ly = b y = new array of size n for i = 1 to n do sum = 0 for j = 1 to i-1 do sum = sum + L[i,j] * y[j] end for y[i] = b[i] - sum end for // 解 Ux = y x = new array of size n for i = n downto 1 do sum = 0 for j = i+1 to n do sum = sum + U[i,j] * x[j] end for x[i] = (y[i] - sum) / U[i,i] end for return x end function
误差与数值稳定性
-
LU分解对主元选择同样敏感,通常结合主元策略(如PLU分解)。
-
对于稀疏矩阵,需采用稀疏LU分解以节省存储和计算。
-
优缺点:
- 优点:
- 对于多个右端项 ,效率高
- 可用于计算行列式、矩阵求逆
- 分解与求解分离,结构清晰
- 缺点:
- 与高斯消元相同,不适合大规模稀疏矩阵
- 需要额外存储空间
- 优点:
-
英文关键词:LU decomposition, LU factorization, lower triangular, upper triangular, forward substitution, backward substitution, multiple right-hand sides
例题3:LU分解解线性方程组
问题:用LU分解求解方程组:
解答步骤:
-
LU分解:将 分解为 。
- 消元:
- 新第二行:
- 上三角矩阵
- 乘数 。下三角矩阵
- 验证:
- 消元:
-
求解 (前代):
- 第一行:
- 第二行:
-
求解 (回代):
- 第二行:
- 第一行:
答案:
Python代码示例:
import numpy as np
from scipy import linalg
def solve_using_lu(A_in, b_in):
"""使用 scipy 的 LU 分解求解 Ax = b"""
A = np.array(A_in, dtype=float)
b = np.array(b_in, dtype=float)
# LU 分解 (scipy.linalg.lu 返回 P, L, U)
P, L, U = linalg.lu(A)
# 注意 scipy 返回的 P 是置换矩阵,不是排列向量
# 需要计算 P @ b 或解 P^T L U x = b
# 这里我们解 Ly = P^T b, Ux = y
# 或者更常见的 Ly = Pb, Ux=y (需要理解P的作用)
# 为简单起见,假设没有行交换(P=I),或直接用 scipy 的 solve
# 实际应用中,如果需要自己实现,可以基于高斯消元过程记录 L 和 U
# 或者直接使用 scipy.linalg.solve
# 为了演示流程,我们假设 A = LU (无行交换)
# 手动分解 (仅适用于本例无行交换情况)
L_manual = np.array([[1., 0.], [2., 1.]])
U_manual = np.array([[4., 3.], [0., 1.]])
# 1. 解 Ly = b
y = linalg.solve_triangular(L_manual, b, lower=True)
# 2. 解 Ux = y
x = linalg.solve_triangular(U_manual, y, lower=False)
return x
# 例题3
A3 = [[4, 3], [8, 7]]
b3 = [24, 52]
x3 = solve_using_lu(A3, b3)
print(f"例题3 LU分解 解: x = {x3[0]}, y = {x3[1]}")
# 验证 (可选)
# x_document = np.array([3, 4])
# assert np.allclose(x3, x_document)
2.2.3 迭代法(Iterative Methods)
2.2.3.1 Jacobi 迭代法
-
基本原理:
将方程组 改写为 的形式,然后迭代求解。Jacobi 法中,每次迭代使用上一次迭代的所有分量值。 -
矩阵分解:
将 分解为 ,其中 是对角矩阵, 是严格下三角矩阵, 是严格上三角矩阵。 -
迭代公式:
- 分量形式:
- 矩阵形式:
-
收敛条件:
- 迭代矩阵 的谱半径
- 充分条件: 严格对角占优(每行对角元素的绝对值大于该行其他元素绝对值之和)
-
伪代码:
function Jacobi(A, b, x0, tol, max_iter) n = size(A, 1) x = x0 for iter = 1 to max_iter do x_new = new array of size n for i = 1 to n do sum = 0 for j = 1 to n do if j != i then sum = sum + A[i,j] * x[j] end if end for x_new[i] = (b[i] - sum) / A[i,i] end for if ||x_new - x|| < tol then return x_new end if x = x_new end for return "Warning: 达到最大迭代次数" end function
2.2.3.2 Gauss-Seidel 迭代法
-
基本原理:
与 Jacobi 法类似,但在计算第 个分量时,使用已经计算出的第 到第 个分量的新值。 -
迭代公式:
- 分量形式:
- 矩阵形式:
-
收敛条件:
- 迭代矩阵 的谱半径
- 充分条件: 严格对角占优或对称正定
-
伪代码:
function GaussSeidel(A, b, x0, tol, max_iter) n = size(A, 1) x = x0 for iter = 1 to max_iter do x_new = copy of x for i = 1 to n do sum = 0 for j = 1 to i-1 do sum = sum + A[i,j] * x_new[j] end for for j = i+1 to n do sum = sum + A[i,j] * x[j] end for x_new[i] = (b[i] - sum) / A[i,i] end for if ||x_new - x|| < tol then return x_new end if x = x_new end for return "Warning: 达到最大迭代次数" end function
2.2.3.3 Jacobi 与 Gauss-Seidel 比较
-
收敛速度:
- Gauss-Seidel 通常比 Jacobi 收敛更快
- Gauss-Seidel 每次迭代使用最新值,信息传播更快
-
存储需求:
- Jacobi 需要额外存储空间保存上一次迭代结果
- Gauss-Seidel 可以原地更新,节省存储
-
并行性:
- Jacobi 更适合并行计算
- Gauss-Seidel 由于依赖关系,并行化较困难
-
适用场景:
- 大规模稀疏矩阵
- 对角占优矩阵
- 初值接近真解的情况
-
英文关键词:Jacobi method, Gauss-Seidel method, iteration, convergence, diagonal dominance, spectral radius, iterative method, sparse matrix
例题4:Jacobi迭代法解线性方程组
问题:用Jacobi迭代法求解方程组,进行两次迭代: 初始值 。
解答步骤:
-
迭代公式:
-
初始值:
-
第一次迭代 (k=0):
-
第二次迭代 (k=1):
答案:迭代两次后,。
Python代码示例:
import numpy as np
def solve_jacobi(A_in, b_in, x0_in, iterations):
"""使用 Jacobi 迭代法求解 Ax = b"""
A = np.array(A_in, dtype=float)
b = np.array(b_in, dtype=float)
x = np.array(x0_in, dtype=float)
n = len(b)
x_new = np.zeros(n)
for k in range(iterations):
for i in range(n):
sigma = 0
for j in range(n):
if i != j:
sigma += A[i, j] * x[j]
if np.abs(A[i, i]) < 1e-10:
raise ValueError("对角元素过小")
x_new[i] = (b[i] - sigma) / A[i, i]
x = x_new.copy() # 更新 x 用于下一次迭代
print(f"迭代 {k+1}: x = {x}") # 打印每次迭代结果
return x
# 例题4
A4 = [[4, 1], [1, 3]]
b4 = [9, 7]
x0_4 = [0, 0]
iterations4 = 2
x4 = solve_jacobi(A4, b4, x0_4, iterations4)
print(f"例题4 Jacobi 解 (迭代 {iterations4} 次): x = {x4[0]:.2f}, y = {x4[1]:.2f}")
# 验证 (可选)
# x_document = np.array([1.67, 1.58])
# assert np.allclose(x4, x_document, rtol=1e-2)
2.2.3.2 Gauss-Seidel 迭代法
例题5:Gauss-Seidel迭代法解线性方程组
问题:用Gauss-Seidel迭代法求解例题4的方程组,进行两次迭代: 初始值 。
解答步骤:
-
迭代公式:
- (注意这里用的是最新的 )
-
初始值:
-
第一次迭代 (k=0):
-
第二次迭代 (k=1):
答案:迭代两次后,。(文档答案 可能是计算或舍入方式略有不同,但趋势一致)
Python代码示例:
import numpy as np
def solve_gauss_seidel(A_in, b_in, x0_in, iterations):
"""使用 Gauss-Seidel 迭代法求解 Ax = b"""
A = np.array(A_in, dtype=float)
b = np.array(b_in, dtype=float)
x = np.array(x0_in, dtype=float)
n = len(b)
for k in range(iterations):
x_old = x.copy() # 保存旧值用于比较(可选)
for i in range(n):
sigma = 0
for j in range(n):
if i != j:
# 注意这里用的是当前迭代中已经更新的值 x[j]
sigma += A[i, j] * x[j]
if np.abs(A[i, i]) < 1e-10:
raise ValueError("对角元素过小")
x[i] = (b[i] - sigma) / A[i, i]
print(f"迭代 {k+1}: x = {x}") # 打印每次迭代结果
# 收敛判断 (可选)
# if np.linalg.norm(x - x_old) < tol:
# break
return x
# 例题5
A5 = [[4, 1], [1, 3]]
b5 = [9, 7]
x0_5 = [0, 0]
iterations5 = 2
x5 = solve_gauss_seidel(A5, b5, x0_5, iterations5)
print(f"例题5 Gauss-Seidel 解 (迭代 {iterations5} 次): x = {x5[0]:.2f}, y = {x5[1]:.2f}")
# 验证 (可选)
# x_document = np.array([1.86, 1.71]) # 文档答案
# assert np.allclose(x5, x_document, rtol=1e-2)
2.3 实践案例与代码
2.3.1 高斯消元法解四阶线性方程组
问题:用高斯消元法求解以下方程组:
注意:此例题在原始文档中可能存在印刷错误,这里使用的是验证过的版本。
解答:这个过程比较繁琐,适合用代码实现。
答案:
Python代码示例:
# 使用上面定义的 solve_gaussian_elimination 函数
A6 = [
[2, 1, 3, 2],
[4, 3, 2, 1],
[1, 2, 4, 3],
[3, 4, 1, 2]
]
b6 = [21, 20, 29, 22]
x6 = solve_gaussian_elimination(A6, b6)
print(f"例题6 (4x4) 高斯消元 解: {x6}")
# 验证 (可选)
x_document = np.array([1, 2, 3, 4])
assert np.allclose(x6, x_document)
2.3.2 迭代法的收敛性判断
问题:判断Jacobi迭代法和Gauss-Seidel迭代法对于以下方程组是否收敛:
分析:
-
严格对角占优: 对于系数矩阵 :
- 第一行:,。。满足。
- 第二行:,。。满足。 因为矩阵 是严格对角占优的,所以Jacobi迭代法和Gauss-Seidel迭代法都收敛。
-
迭代矩阵的谱半径:
-
Jacobi迭代矩阵 , , 特征值 满足 。 。 谱半径 。所以Jacobi法收敛。
-
Gauss-Seidel迭代矩阵 特征值 满足 。 或 。 谱半径 。所以Gauss-Seidel法收敛。
-
答案:Jacobi迭代法和Gauss-Seidel迭代法都收敛。
Python代码示例:
import numpy as np
def check_convergence(A_in):
"""检查Jacobi和Gauss-Seidel迭代法的收敛性"""
A = np.array(A_in, dtype=float)
n = A.shape[0]
# 1. 检查严格对角占优
is_diag_dominant = True
for i in range(n):
diag = np.abs(A[i, i])
off_diag_sum = np.sum(np.abs(A[i, :])) - diag
if diag <= off_diag_sum:
is_diag_dominant = False
break
print(f"严格对角占优: {is_diag_dominant}")
# 2. 计算Jacobi迭代矩阵谱半径
D = np.diag(np.diag(A))
L_plus_U = A - D
try:
D_inv = np.linalg.inv(D)
T_J = -D_inv @ L_plus_U
rho_J = np.max(np.abs(np.linalg.eigvals(T_J)))
print(f"Jacobi 谱半径: {rho_J:.4f}, 收敛: {rho_J < 1}")
except np.linalg.LinAlgError:
print("Jacobi: D 不可逆")
rho_J = float('inf')
# 3. 计算Gauss-Seidel迭代矩阵谱半径
D_plus_L = np.tril(A)
U = np.triu(A, k=1)
try:
D_plus_L_inv = np.linalg.inv(D_plus_L)
T_G = -D_plus_L_inv @ U
rho_G = np.max(np.abs(np.linalg.eigvals(T_G)))
print(f"Gauss-Seidel 谱半径: {rho_G:.4f}, 收敛: {rho_G < 1}")
except np.linalg.LinAlgError:
print("Gauss-Seidel: D+L 不可逆")
rho_G = float('inf')
return is_diag_dominant, rho_J < 1, rho_G < 1
# 例题7
A7 = [[10, 2], [3, 15]]
check_convergence(A7)
CE7453 Numerical Algorithms 期末高分超详细攻略(第三章:Bezier/B-spline/Interpolation)
本章内容:Bezier 曲线、B-spline 曲线、插值方法
适用对象:零基础/考前冲刺/快速查漏补缺
内容特色:详细原理、公式推导、算法流程、例题全解、英文关键词
1. 基本概念与背景
- Bezier 曲线:由一组控制点定义的参数曲线,广泛用于计算机图形、字体、动画等。
- B-spline 曲线:Bezier 曲线的推广,支持更多控制点和更高阶的连续性,局部性更好。
- 插值(Interpolation):通过已知数据点,构造通过这些点的函数(曲线/多项式)。
2. Bezier 曲线
2.1 定义与公式
- n阶 Bezier 曲线公式:
其中 为控制点,, 为 Bernstein 基函数:
2.2 主要性质
- 端点插值(Endpoint interpolation):,
- 共线性(Co-tangency):曲线在端点处与首末两段控制多边形共线
- 凸包性(Convex hull property):曲线始终在控制多边形的凸包内
- 仿射不变性(Affine invariance):对控制点做仿射变换,曲线同样变换
2.3 de Casteljau 算法(数值稳定的递归算法)
算法流程
- 设 个控制点 ,参数
- 递归计算:
- 最终 即为
步骤解释
- 每一层递归都在相邻点之间做线性插值(Lerp),逐步逼近曲线上的点。
- 该算法数值稳定,适合实际计算。
例题
已知控制点 , , ,求 时的 Bezier 曲线点。
解答:
- , ,
- 答:
Python代码示例 (de Casteljau):
import numpy as np
def de_casteljau(control_points, t):
"""使用 de Casteljau 算法计算 Bezier 曲线上一点"""
points = np.array(control_points, dtype=float)
n = len(points) - 1 # 曲线阶数
# P_i^(0) = P_i
current_points = points.copy()
for r in range(1, n + 1): # 从 r=1 到 n
new_points = []
for i in range(n - r + 1): # P_i^(r) 的 i 从 0 到 n-r
p_r_i = (1 - t) * current_points[i] + t * current_points[i+1]
new_points.append(p_r_i)
current_points = np.array(new_points)
# print(f"r={r}, points={current_points}") # 可选:打印中间步骤
return current_points[0] # P_0^(n)
# 例题数据
P_bezier = [[0, 0], [1, 2], [3, 3]]
t_bezier = 0.5
# 计算
point_on_curve = de_casteljau(P_bezier, t_bezier)
print(f"例题 (Bezier de Casteljau): P({t_bezier}) = {point_on_curve}")
# 验证 (可选)
# expected_point = np.array([1.25, 1.75])
# assert np.allclose(point_on_curve, expected_point)
3. B-spline 曲线
3.1 定义与公式
-
B-spline 曲线公式:
其中 为 阶 B-spline 基函数, 为 de Boor 控制点, 为参数。 -
B-spline 基函数递归定义:
3.2 主要性质
- 局部性(Local support):每个控制点只影响相邻 个区间
- 高阶连续性:可实现 连续
- 可调节点向量(Knot vector):灵活控制曲线形状
3.3 de Boor 算法
算法流程
- 给定节点向量 ,控制点 ,阶数 ,参数
- 找到 所在区间
- 递归线性插值,最终得到
例题
已知三次 B-spline 曲线,控制点 , , , ,节点向量 ,求 处的曲线点。
解答:
- 节点向量为 ,阶数 (因为节点向量长度为 )。
- 参数 落在区间 ,但由于节点向量重复,实际有效区间需要考虑 属于 ,因此 。
- 影响 的控制点为 (因为 到 ,即控制点索引 到 )。
- 计算基函数 :
- 先计算 :
- (因为 不在 )
- (因为 不在 )
- (因为 不在 )
- (因为 在 )
- (因为 不在 )
- 递归计算 :
- 递归计算 :
- (类似计算)
- 递归计算 :
- 先计算 :
- 最终曲线点
- 答:
Python代码示例 (手动计算基函数):
import numpy as np
# 例题数据
control_points_bspline = np.array([[0, 0], [1, 2], [3, 3], [4, 0]])
knots_bspline = np.array([0,0,0,0,1,2,2,2,2])
u_bspline = 1
# 按照文档手动计算的基函数值
N14_at_1 = 0.25
N24_at_1 = 0.5
N34_at_1 = 0.25
# 影响 u=1 的控制点是 P1, P2, P3 (索引1, 2, 3)
point_on_bspline = N14_at_1 * control_points_bspline[1] + \
N24_at_1 * control_points_bspline[2] + \
N34_at_1 * control_points_bspline[3]
print(f"例题 (B-spline 手动基函数): r({u_bspline}) = {point_on_bspline}")
# 验证 (可选)
# expected_point = np.array([2.75, 2.0])
# assert np.allclose(point_on_bspline, expected_point)
# 注意: 实际应用中会使用递归函数计算基函数或直接使用 scipy.interpolate
# from scipy.interpolate import BSpline
# k = 3 # B样条阶数 (degree), k=order-1. Order k=4 for cubic.
# tck = (knots_bspline, control_points_bspline.T, k)
# point_scipy = BSpline(*tck)(u_bspline)
# print(f"例题 (B-spline Scipy): r({u_bspline}) = {point_scipy}")
新增例题:计算给定控制点 ,, 的二次Bezier曲线在 处的点
解答步骤:
- 使用de Casteljau算法:
- 第一层:
- 第二层:
- 结果:曲线点为 。
Python代码示例 (使用上面的 de_casteljau 函数):
# 新增例题数据
P_bezier_add = [[0, 0], [1, 1], [2, 0]]
t_bezier_add = 0.5
# 计算
point_on_curve_add = de_casteljau(P_bezier_add, t_bezier_add)
print(f"新增例题 (Bezier de Casteljau): P({t_bezier_add}) = {point_on_curve_add}")
# 验证 (可选)
# expected_point = np.array([1, 0.5])
# assert np.allclose(point_on_curve_add, expected_point)
4. 插值方法(Interpolation)
4.1 Lagrange 插值
- 公式:
- 优缺点:公式显式,适合手算,但高阶时数值不稳定。
例题
已知点 , , ,用 Lagrange 插值求 处的函数值。
解答:
- 基函数:
- 插值多项式:
- 代入 :
- 答:
Python代码示例 (Lagrange 插值):
import numpy as np
from scipy import interpolate # 用于验证
def lagrange_basis(x_data, i, x):
"""计算第 i 个 Lagrange 基函数 l_i(x)"""
n = len(x_data)
li = 1.0
for j in range(n):
if i != j:
li *= (x - x_data[j]) / (x_data[i] - x_data[j])
return li
def lagrange_interpolation(x_data, y_data, x_interp):
"""使用 Lagrange 插值计算 x_interp 处的函数值"""
n = len(x_data)
result = 0.0
for i in range(n):
result += y_data[i] * lagrange_basis(x_data, i, x_interp)
return result
# 例题数据
x_lagrange = np.array([0, 1, 2])
y_lagrange = np.array([1, 2, 0])
x_interp_lagrange = 1.5
# 计算
interp_value_lagrange = lagrange_interpolation(x_lagrange, y_lagrange, x_interp_lagrange)
print(f"例题 (Lagrange 插值): L({x_interp_lagrange}) = {interp_value_lagrange}")
# 验证 (可选)
# expected_value = 1.375
# assert np.isclose(interp_value_lagrange, expected_value)
# lagrange_poly = interpolate.lagrange(x_lagrange, y_lagrange)
# scipy_value = lagrange_poly(x_interp_lagrange)
# assert np.isclose(scipy_value, expected_value)
4.2 Newton 插值
- 公式:
其中 为差商(divided differences)
例题
已知点 , , ,用 Newton 插值求 处的函数值。
解答:
- 差商表:
- 插值多项式:
- 代入 :
- 答:
Python代码示例 (Newton 插值):
import numpy as np
def divided_differences(x_data, y_data):
"""计算差商表,返回 Newton 多项式系数"""
n = len(x_data)
coef = np.zeros([n, n])
coef[:, 0] = y_data # 第一列是 y 值
for j in range(1, n): # 列
for i in range(n - j): # 行
coef[i, j] = (coef[i + 1, j - 1] - coef[i, j - 1]) / (x_data[i + j] - x_data[i])
return coef[0, :] # 返回第一行作为系数 a0, a1, a2, ...
def newton_polynomial(coefficients, x_data, x):
"""根据差商系数计算 Newton 插值多项式在 x 处的值"""
n = len(coefficients)
result = coefficients[0]
term = 1.0
for i in range(1, n):
term *= (x - x_data[i - 1]) # 计算 (x-x0), (x-x0)(x-x1), ...
result += coefficients[i] * term
return result
# 例题数据
x_newton = np.array([0, 1, 2])
y_newton = np.array([1, 2, 0])
x_interp_newton = 1.5
# 计算
coeffs_newton = divided_differences(x_newton, y_newton)
interp_value_newton = newton_polynomial(coeffs_newton, x_newton, x_interp_newton)
print(f"例题 (Newton 插值) 差商系数: {coeffs_newton}")
print(f"例题 (Newton 插值): N({x_interp_newton}) = {interp_value_newton}")
# 验证 (可选)
# expected_value = 1.375
# assert np.isclose(interp_value_newton, expected_value)
4.3 样条插值(Spline Interpolation)
- 三次样条:分段三次多项式,保证 连续,常用于平滑曲线拟合。
例题
已知点 , , ,构造自然三次样条插值(边界条件为二阶导数为0)。
解答:
- 设区间 和 上的三次多项式为:
- 条件:
- 插值条件:, , ,
- 一阶导数连续:
- 二阶导数连续:
- 自然边界条件:,
- 解方程组(详细计算略):
- 答:自然三次样条插值多项式如上。
Python代码示例 (使用 Scipy):
import numpy as np
from scipy.interpolate import CubicSpline
# 例题数据
x_spline = np.array([0, 1, 2])
y_spline = np.array([1, 2, 0])
# 计算自然三次样条 (bc_type='natural')
cs = CubicSpline(x_spline, y_spline, bc_type='natural')
# 打印样条系数 (可选)
# print("样条系数 (分段):")
# for i in range(len(cs.c[0, :])):
# print(f" 区间 {i}: {cs.c[:, i]}")
# 在 x=1.5 处插值 (虽然例题只要求构造)
x_interp_spline = 1.5
interp_value_spline = cs(x_interp_spline)
print(f"例题 (自然三次样条插值): S({x_interp_spline}) = {interp_value_spline}")
# 验证 x=0, 1, 2 处的插值结果
print(f"验证: S(0)={cs(0)}, S(1)={cs(1)}, S(2)={cs(2)}")
assert np.isclose(cs(0), 1)
assert np.isclose(cs(1), 2)
assert np.isclose(cs(2), 0)
# 验证边界条件 S''(0)=0, S''(2)=0
print(f"验证: S''(0)={cs(0, 2)}, S''(2)={cs(2, 2)}")
assert np.isclose(cs(0, 2), 0)
assert np.isclose(cs(2, 2), 0)
5. 常见考点与易错点
- Bezier 曲线的端点、切线、凸包等性质
- de Casteljau 算法步骤与递归思想
- B-spline 节点向量与控制点影响范围
- 插值多项式的数值稳定性与误差
- 英文术语拼写与公式记忆
6. 英文关键词与表达
- Bezier curve, control point, Bernstein polynomial, de Casteljau algorithm, subdivision, convex hull, affine invariance
- B-spline, knot vector, de Boor point, local support, continuity, degree, order
- interpolation, Lagrange interpolation, Newton divided difference, spline, cubic spline
如需更详细例题推导或某一算法的代码实现,可随时补充!
CE7453 Numerical Algorithms 期末高分超详细攻略(第四章:Numerical Differentiation & Integration)
本章内容:数值微分、数值积分
适用对象:零基础/考前冲刺/快速查漏补缺
内容特色:详细原理、公式推导、算法流程、例题全解、英文关键词
1. 基本概念与背景
- 数值微分(Numerical Differentiation):用离散点近似计算函数的导数,适用于函数表达式复杂或仅有数据点的情况。
- 数值积分(Numerical Integration):用有限和近似计算定积分,适用于无法解析积分或仅有数据点的情况。
- 实际应用:工程仿真、信号处理、物理建模、数据分析等。
2. 数值微分(Numerical Differentiation)
2.1 差分公式(Finite Difference Formulas)
2.1.1 2点前向差分(2-point forward difference)
- 公式:
- 误差:,截断误差较大
2.1.2 3点中心差分(3-point centered difference)
- 公式:
- 误差:,更精确
2.1.3 5点中心差分(5-point centered difference)
- 公式:
- 误差:,高精度
2.1.4 Richardson 外推(Richardson Extrapolation)
- 思想:用不同步长的近似结果组合,消除主误差项,提高精度。
2.2 算法流程(以3点中心差分为例)
- 选定步长 (过大误差大,过小舍入误差大)
- 计算 和
- 按公式求导数近似值
- 可用不同 验证结果稳定性
2.3 典型例题
例题1:已知 ,用 ,求 处的导数近似值(用前向差分法)
解答:
- 精确值 ,误差约为
例题2:已知 ,用 ,求 处的导数近似值(用中心差分法)
解答:
- 精确值 ,误差约为
例题3:已知 ,在 处用 ,分别用2点前向差分、3点中心差分和5点中心差分计算导数近似值,并与精确值比较。
解答:
- 精确导数:,在 处,
- 2点前向差分:
- 误差:
- 3点中心差分:
- (如上)
- 误差:
- 5点中心差分:
- (如上)
- (如上)
- 误差:
- 比较:5点中心差分精度最高,误差最小;2点前向差分误差最大。
2.4 实际应用与注意事项
- 应用场景:数值微分常用于速度、加速度计算(如运动学分析),以及优化算法中的梯度计算。
- 注意事项:
- 步长 选择需平衡截断误差和舍入误差,建议多次尝试不同 值。
- 对于噪声数据,数值微分会放大噪声,需先平滑处理数据。
3. 数值积分(Numerical Integration)
3.1 Newton-Cotes 公式
3.1.1 梯形法(Trapezoidal Rule)
-
公式:
其中 -
复合梯形法:将区间分为 段,累加每段梯形面积
3.1.2 Simpson 法(Simpson's Rule)
-
公式:
-
复合Simpson法:区间分偶数段,累加每段Simpson近似
3.1.3 Romberg 积分(Romberg Integration)
- 思想:用梯形法结果递推外推,提高精度。基于 Richardson 外推,通过多次梯形法计算,逐步消除误差项。
3.1.4 高斯求积(Gaussian Quadrature)
- 思想:选取最优节点和权重,使多项式积分精度最高。对于 点高斯求积,对最高 次多项式精确。
3.2 算法流程(以复合梯形法为例)
- 将 分为 段,步长
- 计算端点
- 计算中间点 ,
- 按公式累加求和
3.3 典型例题
例题1:用复合梯形法计算 ,分4段
解答:
- 精确值 ,误差约为
例题2:用Simpson法计算积分 ,分2段
解答步骤:
- ,点:
- 精确值 ,误差为0。
例题3:用Romberg积分计算 ,取初始 ,进行两次外推。
解答步骤:
- 该积分精确值为 ,用于验证误差。
- 第一步:梯形法,不同分割:
- ,,
- ,,
- ,,
- , ,
- 第二步:Romberg外推:
- 第一次外推:
- 第二次外推:
- 继续外推:
- 结果:,与 较接近,误差约为 。
例题4:用三点高斯求积法计算
解答步骤:
- 三点高斯求积法使用的节点:,,
- 对应的权重:,,
- 函数值计算:
- 求积结果:
- 精确值为 ,误差约为 (约1.3%)
3.4 实际应用与注意事项
- 应用场景:数值积分用于计算面积、体积、概率密度积分、物理量累积(如功、能量)。
- 注意事项:
- 增加分割数 可提高精度,但计算量增加。
- 对于周期性函数或振荡函数,高斯求积可能更有效。
- Romberg 积分适合快速收敛,但对函数平滑性要求较高。
4. 常见考点与易错点
- 步长 选取不当导致误差大
- 舍入误差与截断误差权衡
- 复合公式累加时端点权重
- 高斯求积节点和权重记忆
- 英文术语拼写与公式记忆
5. 英文关键词与表达
- numerical differentiation, finite difference, truncation error, round-off error, Richardson extrapolation, step size
- numerical integration, Newton-Cotes, trapezoidal rule, Simpson's rule, Romberg integration, Gaussian quadrature, composite rule, node, weight
6. 代码实现示例
下面提供各种数值微分和数值积分方法的Python实现示例。
6.1 数值微分代码实现
import numpy as np
import matplotlib.pyplot as plt
def forward_difference(f, x, h=0.01):
"""
使用前向差分法计算导数
参数:
f: 函数
x: 计算导数的点
h: 步长
返回:
导数近似值
"""
return (f(x + h) - f(x)) / h
def central_difference(f, x, h=0.01):
"""
使用中心差分法计算导数
参数:
f: 函数
x: 计算导数的点
h: 步长
返回:
导数近似值
"""
return (f(x + h) - f(x - h)) / (2 * h)
def five_point_difference(f, x, h=0.01):
"""
使用五点中心差分法计算导数
参数:
f: 函数
x: 计算导数的点
h: 步长
返回:
导数近似值
"""
return (f(x - 2*h) - 8*f(x - h) + 8*f(x + h) - f(x + 2*h)) / (12 * h)
def richardson_extrapolation(f, x, h=0.1, k=2):
"""
使用Richardson外推法提高导数计算精度
参数:
f: 函数
x: 计算导数的点
h: 初始步长
k: 外推次数
返回:
改进后的导数近似值
"""
# 计算中心差分
D1 = central_difference(f, x, h)
D2 = central_difference(f, x, h/2)
# 外推公式: D = D2 + (D2 - D1)/(4^k - 1)
return D2 + (D2 - D1) / (4**k - 1)
# 测试示例
if __name__ == "__main__":
# 定义测试函数和其精确导数
f = lambda x: np.exp(2*x)
df_exact = lambda x: 2 * np.exp(2*x)
# 测试点
x0 = 1.0
# 计算导数并与精确值比较
h = 0.1
fd = forward_difference(f, x0, h)
cd = central_difference(f, x0, h)
fpd = five_point_difference(f, x0, h)
rd = richardson_extrapolation(f, x0, h)
exact = df_exact(x0)
print(f"函数 f(x) = e^(2x) 在 x = {x0} 处的导数:")
print(f"精确值: {exact:.6f}")
print(f"前向差分 (h={h}): {fd:.6f}, 相对误差: {abs(fd-exact)/exact:.6f}")
print(f"中心差分 (h={h}): {cd:.6f}, 相对误差: {abs(cd-exact)/exact:.6f}")
print(f"五点差分 (h={h}): {fpd:.6f}, 相对误差: {abs(fpd-exact)/exact:.6f}")
print(f"Richardson外推: {rd:.6f}, 相对误差: {abs(rd-exact)/exact:.6f}")
# 绘制不同步长下的误差
h_values = np.logspace(-8, -1, 20)
fd_errors = [abs(forward_difference(f, x0, h) - exact)/exact for h in h_values]
cd_errors = [abs(central_difference(f, x0, h) - exact)/exact for h in h_values]
fpd_errors = [abs(five_point_difference(f, x0, h) - exact)/exact for h in h_values]
plt.figure(figsize=(10, 6))
plt.loglog(h_values, fd_errors, 'o-', label='前向差分')
plt.loglog(h_values, cd_errors, 's-', label='中心差分')
plt.loglog(h_values, fpd_errors, '^-', label='五点差分')
plt.grid(True)
plt.xlabel('步长 h')
plt.ylabel('相对误差')
plt.title('不同数值微分方法在不同步长下的相对误差')
plt.legend()
plt.show()
6.2 数值积分代码实现
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
def trapezoidal_rule(f, a, b, n=100):
"""
复合梯形法则计算定积分
参数:
f: 被积函数
a, b: 积分区间
n: 分段数
返回:
积分近似值
"""
h = (b - a) / n
x = np.linspace(a, b, n+1)
y = f(x)
# 复合梯形法则公式
integral = (h/2) * (y[0] + 2*np.sum(y[1:-1]) + y[-1])
return integral
def simpson_rule(f, a, b, n=100):
"""
复合Simpson法则计算定积分
参数:
f: 被积函数
a, b: 积分区间
n: 分段数 (必须为偶数)
返回:
积分近似值
"""
if n % 2 != 0:
n += 1 # 确保n为偶数
h = (b - a) / n
x = np.linspace(a, b, n+1)
y = f(x)
# 复合Simpson法则公式
integral = (h/3) * (y[0] + 4*np.sum(y[1:-1:2]) + 2*np.sum(y[2:-1:2]) + y[-1])
return integral
def romberg_integration(f, a, b, max_iter=5):
"""
Romberg积分法
参数:
f: 被积函数
a, b: 积分区间
max_iter: 最大迭代次数
返回:
积分近似值
"""
R = np.zeros((max_iter, max_iter))
# 初始计算 - 使用复合梯形法则
h = b - a
R[0, 0] = (h/2) * (f(a) + f(b))
for i in range(1, max_iter):
# 计算下一级梯形法值
h = h / 2
n = 2**i
sum_f = 0
for k in range(1, n, 2):
sum_f += f(a + k*h)
R[i, 0] = R[i-1, 0]/2 + h*sum_f
# Richardson外推
for j in range(1, i+1):
R[i, j] = R[i, j-1] + (R[i, j-1] - R[i-1, j-1]) / (4**j - 1)
return R[max_iter-1, max_iter-1]
def gaussian_quadrature(f, a, b, n=5):
"""
高斯求积法计算定积分
参数:
f: 被积函数
a, b: 积分区间
n: 高斯点数 (最多5点)
返回:
积分近似值
"""
# 高斯点和权重 (标准区间 [-1, 1])
if n == 1:
x_points = np.array([0])
weights = np.array([2])
elif n == 2:
x_points = np.array([-1/np.sqrt(3), 1/np.sqrt(3)])
weights = np.array([1, 1])
elif n == 3:
x_points = np.array([-np.sqrt(3/5), 0, np.sqrt(3/5)])
weights = np.array([5/9, 8/9, 5/9])
elif n == 4:
x0 = np.sqrt((3-2*np.sqrt(6/5))/7)
x1 = np.sqrt((3+2*np.sqrt(6/5))/7)
x_points = np.array([-x1, -x0, x0, x1])
w0 = (18+np.sqrt(30))/36
w1 = (18-np.sqrt(30))/36
weights = np.array([w1, w0, w0, w1])
elif n == 5:
x0 = 0
x1 = np.sqrt(5-2*np.sqrt(10/7))/3
x2 = np.sqrt(5+2*np.sqrt(10/7))/3
x_points = np.array([-x2, -x1, x0, x1, x2])
w0 = 128/225
w1 = (322+13*np.sqrt(70))/900
w2 = (322-13*np.sqrt(70))/900
weights = np.array([w2, w1, w0, w1, w2])
else:
raise ValueError("目前仅支持1到5个高斯点")
# 转换到积分区间 [a, b]
scaled_f = lambda x: f((b-a)/2 * x + (a+b)/2) * (b-a)/2
# 计算积分
integral = np.sum(weights * scaled_f(x_points))
return integral
# 测试示例
if __name__ == "__main__":
# 测试函数
f1 = lambda x: x**2
f2 = lambda x: np.exp(x)
f3 = lambda x: 4/(1+x**2) # 积分结果为π
f4 = lambda x: np.sqrt(1-x**2) # 半圆面积,积分结果为π/2
# 积分区间
a1, b1 = 0, 1 # 用于f1, f2
a3, b3 = 0, 1 # 用于f3
a4, b4 = -1, 1 # 用于f4
# 精确结果
exact1 = 1/3 # ∫(0→1) x^2 dx = 1/3
exact2 = np.exp(1) - 1 # ∫(0→1) e^x dx = e - 1
exact3 = np.pi # ∫(0→1) 4/(1+x^2) dx = π
exact4 = np.pi/2 # ∫(-1→1) sqrt(1-x^2) dx = π/2
# 不同方法计算结果
n_intervals = 4 # 分段数
print("函数 f(x) = x^2 在区间 [0, 1] 上的积分:")
trap1 = trapezoidal_rule(f1, a1, b1, n_intervals)
simp1 = simpson_rule(f1, a1, b1, n_intervals)
romb1 = romberg_integration(f1, a1, b1, 3)
gauss1 = gaussian_quadrature(f1, a1, b1, 3)
print(f"精确值: {exact1}")
print(f"梯形法则 (n={n_intervals}): {trap1:.6f}, 相对误差: {abs(trap1-exact1)/exact1:.6f}")
print(f"Simpson法则 (n={n_intervals}): {simp1:.6f}, 相对误差: {abs(simp1-exact1)/exact1:.6f}")
print(f"Romberg积分: {romb1:.6f}, 相对误差: {abs(romb1-exact1)/exact1:.6f}")
print(f"高斯求积法 (3点): {gauss1:.6f}, 相对误差: {abs(gauss1-exact1)/exact1:.6f}")
print("\n函数 f(x) = 4/(1+x^2) 在区间 [0, 1] 上的积分 (结果应接近π):")
trap3 = trapezoidal_rule(f3, a3, b3, n_intervals)
simp3 = simpson_rule(f3, a3, b3, n_intervals)
romb3 = romberg_integration(f3, a3, b3, 3)
gauss3 = gaussian_quadrature(f3, a3, b3, 3)
print(f"精确值: {exact3}")
print(f"梯形法则 (n={n_intervals}): {trap3:.6f}, 相对误差: {abs(trap3-exact3)/exact3:.6f}")
print(f"Simpson法则 (n={n_intervals}): {simp3:.6f}, 相对误差: {abs(simp3-exact3)/exact3:.6f}")
print(f"Romberg积分: {romb3:.6f}, 相对误差: {abs(romb3-exact3)/exact3:.6f}")
print(f"高斯求积法 (3点): {gauss3:.6f}, 相对误差: {abs(gauss3-exact3)/exact3:.6f}")
# 绘制不同分段数下的误差比较
n_values = [2, 4, 8, 16, 32, 64, 128]
trap_errors = [abs(trapezoidal_rule(f2, a1, b1, n) - exact2)/exact2 for n in n_values]
simp_errors = [abs(simpson_rule(f2, a1, b1, n) - exact2)/exact2 for n in n_values]
plt.figure(figsize=(10, 6))
plt.loglog(n_values, trap_errors, 'o-', label='梯形法则')
plt.loglog(n_values, simp_errors, 's-', label='Simpson法则')
plt.grid(True)
plt.xlabel('分段数 n')
plt.ylabel('相对误差')
plt.title('不同数值积分方法在不同分段数下的相对误差 (f(x) = e^x)')
plt.legend()
plt.show()
6.3 应用实例:微分方程数值解法
import numpy as np
import matplotlib.pyplot as plt
def euler_method(f, t0, y0, h, n_steps):
"""
欧拉法求解常微分方程 dy/dt = f(t, y)
参数:
f: 函数,表示 dy/dt = f(t, y)
t0: 初始时间
y0: 初始值
h: 步长
n_steps: 步数
返回:
t_values: 时间点数组
y_values: 对应的函数值数组
"""
t_values = np.zeros(n_steps + 1)
y_values = np.zeros(n_steps + 1)
t_values[0] = t0
y_values[0] = y0
for i in range(n_steps):
t_values[i+1] = t_values[i] + h
y_values[i+1] = y_values[i] + h * f(t_values[i], y_values[i])
return t_values, y_values
def runge_kutta_4(f, t0, y0, h, n_steps):
"""
四阶Runge-Kutta方法求解常微分方程 dy/dt = f(t, y)
参数:
f: 函数,表示 dy/dt = f(t, y)
t0: 初始时间
y0: 初始值
h: 步长
n_steps: 步数
返回:
t_values: 时间点数组
y_values: 对应的函数值数组
"""
t_values = np.zeros(n_steps + 1)
y_values = np.zeros(n_steps + 1)
t_values[0] = t0
y_values[0] = y0
for i in range(n_steps):
t = t_values[i]
y = y_values[i]
k1 = f(t, y)
k2 = f(t + h/2, y + h*k1/2)
k3 = f(t + h/2, y + h*k2/2)
k4 = f(t + h, y + h*k3)
t_values[i+1] = t + h
y_values[i+1] = y + h * (k1 + 2*k2 + 2*k3 + k4) / 6
return t_values, y_values
# 示例:求解微分方程 dy/dt = -y + t (精确解为 y = t - 1 + 2e^(-t))
if __name__ == "__main__":
# 定义微分方程 dy/dt = f(t, y)
f = lambda t, y: -y + t
# 定义精确解
exact_solution = lambda t: t - 1 + 2 * np.exp(-t)
# 初始条件和求解参数
t0, y0 = 0, 1 # 初始条件
h = 0.1 # 步长
n_steps = 50 # 步数
# 使用欧拉法求解
t_euler, y_euler = euler_method(f, t0, y0, h, n_steps)
# 使用Runge-Kutta法求解
t_rk4, y_rk4 = runge_kutta_4(f, t0, y0, h, n_steps)
# 计算精确解
t_exact = np.linspace(t0, t0 + h*n_steps, 200)
y_exact = exact_solution(t_exact)
# 绘制结果比较
plt.figure(figsize=(12, 6))
plt.plot(t_exact, y_exact, 'k-', label='精确解')
plt.plot(t_euler, y_euler, 'bo-', label='欧拉法')
plt.plot(t_rk4, y_rk4, 'rs-', label='四阶Runge-Kutta法')
plt.grid(True)
plt.xlabel('t')
plt.ylabel('y')
plt.title('常微分方程 dy/dt = -y + t 的数值解 (h = ' + str(h) + ')')
plt.legend()
# 计算并显示误差
y_exact_at_points = exact_solution(t_euler)
euler_error = np.abs(y_euler - y_exact_at_points)
rk4_error = np.abs(y_rk4 - y_exact_at_points)
plt.figure(figsize=(12, 6))
plt.semilogy(t_euler, euler_error, 'bo-', label='欧拉法误差')
plt.semilogy(t_euler, rk4_error, 'rs-', label='四阶Runge-Kutta法误差')
plt.grid(True)
plt.xlabel('t')
plt.ylabel('误差 (对数尺度)')
plt.title('数值解误差比较')
plt.legend()
plt.show()
如需更详细例题推导或某一算法的代码实现,可随时补充!
CE7453 Numerical Algorithms 期末高分超详细攻略(第五章:Least Squares)
本章内容:最小二乘法、正规方程、QR分解、非线性最小二乘、GPS应用
适用对象:零基础/考前冲刺/快速查漏补缺
内容特色:详细原理、公式推导、算法流程、例题全解、英文关键词
1. 基本概念与背景
- 最小二乘法(Least Squares):用于拟合数据、解超定方程组(方程数多于未知数),使误差平方和最小。
- 实际应用:数据拟合、回归分析、信号处理、GPS定位、机器学习等。
2. 线性最小二乘法
2.1 问题描述
- 给定 个方程 , 为 矩阵(),通常无精确解。
- 目标:找到 使 最小。
2.2 正规方程(Normal Equation)
- 推导: 对 求导,令导数为0,得
- 解法:解 阶方程组
2.3 QR分解法(QR Decomposition)
- 原理:将 分解为 , 为正交矩阵, 为上三角矩阵。
- 步骤:
- ,回代求解
- 优点:数值稳定性高,适合大规模问题
2.4 典型例题
例题1:用最小二乘法拟合直线 ,已知数据点
详细解答:
-
问题建模:目标是找到参数 和 ,使得直线 尽可能接近给定的数据点。每个数据点对应一个方程:
- 对于 :
- 对于 :
- 对于 :
-
矩阵形式:将上述方程组写成矩阵形式 : 这里 是 矩阵, 是待求参数向量, 是观测值向量。
-
正规方程:由于 不是方阵,无法直接求逆,我们使用正规方程 :
- 计算 :
- 计算 :
- 计算 :
-
求解线性方程组:解 ,即: 使用高斯消元法或矩阵求逆:
- 矩阵行列式:
- 求逆矩阵:
- 计算 : 因此,,。
-
结果:拟合直线为 。可以通过计算残差 验证拟合效果。
3. 非线性最小二乘(Nonlinear Least Squares)
3.1 问题描述
- 拟合模型 , 为参数, 非线性
- 目标:最小化
3.2 Gauss-Newton 方法
- 思想:用泰勒展开线性化,迭代求解
- 步骤:
- 初始猜测
- 计算残差
- 计算雅可比矩阵
- 解正规方程
- 更新
- 迭代至收敛
3.3 典型例题
例题2:用最小二乘法拟合二次曲线 ,数据点
详细解答:
-
问题建模:虽然二次曲线是非线性函数,但对于参数 来说,模型是线性的,因此可以用线性最小二乘法直接求解。每个数据点对应一个方程:
- 对于 :
- 对于 :
- 对于 :
- 对于 :
-
矩阵形式:将方程组写成矩阵形式 :
-
正规方程:计算 和 :
-
求解线性方程组:解 ,即: 使用数值方法或矩阵求逆,得到:
- (几乎为0)
-
结果:拟合曲线为 。这表明数据点更适合用直线而非二次曲线进行拟合。可以通过计算残差验证拟合效果。
例题3:用非线性最小二乘法拟合指数模型 ,数据点
详细解答:
-
问题建模:目标是找到参数 和 ,使得 拟合数据点。模型为非线性,无法直接用线性最小二乘法求解,因此使用Gauss-Newton迭代方法。
-
目标函数:最小化残差平方和:
-
初始猜测:选择初始值 (可以根据数据趋势粗略估计)。
-
迭代步骤:
- 计算残差:对于当前参数 ,计算每个数据点的残差 。
- :
- :
- :
- 计算雅可比矩阵:,其中 ,,。
- 对于 :,
- 对于 :,
- 对于 :,
- 雅可比矩阵:
- 解正规方程:计算 和 ,解 ,得到参数更新量 。
- 更新参数:,。
- 重复迭代:重复上述步骤,直到参数收敛或残差足够小。
- 计算残差:对于当前参数 ,计算每个数据点的残差 。
-
结果:经过多次迭代,参数收敛到 ,,拟合模型为 ,接近真实指数增长趋势。
4. GPS定位中的最小二乘
- 原理:通过测量与多颗卫星的距离,建立超定方程组,利用最小二乘法估算接收机位置。
- 模型:,为未知位置,为卫星位置,为测距
- 步骤:线性化后用Gauss-Newton迭代
4.1 典型例题
例题4:假设接收机与三颗卫星的距离测量如下,估算接收机位置 :
- 卫星1:位置 ,距离
- 卫星2:位置 ,距离
- 卫星3:位置 ,距离
详细解答:
-
问题建模:接收机位置为 ,与各卫星的距离方程为:
-
非线性方程组:上述方程是非线性的,直接求解较复杂,因此使用Gauss-Newton方法进行迭代求解。
-
初始猜测:根据几何图形,接收机可能位于三颗卫星的中心附近,初始猜测为 。
-
线性化与迭代:
- 定义残差函数:
- 计算雅可比矩阵(偏导数):
- ,
- ,
- ,
- 在初始点 处计算残差和雅可比矩阵,解正规方程更新参数。
- 经过多次迭代,位置收敛到 。
- 定义残差函数:
-
结果:接收机位置为 ,可以通过代入原方程验证各卫星到该位置的距离是否接近5。
5. 常见考点与易错点
- 正规方程推导与矩阵乘法
- QR分解步骤与正交化方法(Gram-Schmidt)
- 非线性最小二乘的迭代过程
- 残差(residual)、条件数(condition number)理解
- 英文术语拼写与公式记忆
6. 英文关键词与表达
- least squares, overdetermined system, normal equation, QR decomposition, orthogonalization, Gram-Schmidt, residual, regression, nonlinear least squares, Gauss-Newton, GPS positioning, condition number
如需更详细例题推导或某一算法的代码实现,可随时补充!
CE7453 Numerical Algorithms 期末高分超详细攻略(第六章:Eigenanalysis)
本章内容:特征值与特征向量、幂迭代、QR算法、PageRank
适用对象:零基础/考前冲刺/快速查漏补缺
内容特色:详细原理、公式推导、算法流程、例题全解、英文关键词
1. 基本概念与背景
- 特征值与特征向量(Eigenvalue & Eigenvector):对于 矩阵 ,如果存在非零向量 和标量 使 ,则 为特征值, 为对应特征向量。
- 实际应用:PCA主成分分析、PageRank、振动分析、图像处理、机器学习等。
- 数学意义:特征值表示矩阵在某些方向上的缩放因子,特征向量表示这些方向。特征分解可以帮助理解矩阵的性质和行为。
特征值分解(Eigenvalue Decomposition): 如果矩阵 可对角化,则可以表示为 ,其中 是特征向量组成的矩阵, 是特征值组成对角矩阵。这种分解在求矩阵幂、解微分方程等场景中非常有用。
2. 幂迭代法(Power Iteration)
2.1 原理
- 用于求最大模特征值(dominant eigenvalue)及其对应的特征向量。
- 基本思想是通过反复用矩阵 作用于初始向量,逐步放大主特征分量(与最大特征值对应的分量),最终收敛到主特征向量。
- 数学依据:假设矩阵 有特征值 ,且 ,初始向量 可以表示为特征向量的线性组合 ,则 (当 很大时),即收敛到主特征向量方向。
2.2 算法流程
- 选择一个非零初始向量 (通常随机选择或全为1)。
- 归一化 ,以避免数值过大或过小。
- 迭代:计算 。
- 归一化 ,得到单位向量。
- 检查收敛性:若 (或达到最大迭代次数),则停止迭代。
- 计算特征值近似:(Rayleigh quotient,瑞利商)。
注意事项:
- 初始向量不能与主特征向量正交,否则无法收敛到最大特征值。
- 收敛速度取决于特征值之间的比值 ,比值越小,收敛越快。
2.3 例题
例题1:给定矩阵 ,初始向量 ,用幂迭代法求最大特征值及其特征向量。
详细解答:
- 初始化:,归一化后仍为 (因为 )。
- 第一次迭代:
- 计算 。
- 归一化:,所以 。
- 第二次迭代:
- 计算 。
- 归一化:,所以 。
- 第三次迭代:
- 计算 。
- 归一化:,所以 。
- 第四次迭代:
- 计算 。
- 归一化:,所以 。
- 第五次迭代:
- 计算 。
- 归一化:,所以 。
- 收敛检查:继续迭代,向量逐渐接近 (即 归一化后),说明收敛到主特征向量。
- 计算特征值:使用瑞利商,。
- 。
- 。
- 。
- 因此 。
答案:最大特征值约为 ,对应特征向量为 (未归一化形式)。
验证:直接计算特征值, 的特征方程为 ,解得 或 ,与幂迭代结果一致。
3. QR算法(QR Algorithm)
3.1 原理
- QR算法是一种求解矩阵所有特征值的迭代方法,特别适合对称矩阵。
- 基本思想:通过反复对矩阵进行QR分解(将矩阵分解为正交矩阵 和上三角矩阵 的乘积),并重新组合为 ,最终使矩阵收敛到对角形式(或近似对角形式),对角线元素即为特征值。
- 数学依据:QR算法本质上是幂法的扩展,同时对所有特征值进行迭代,收敛后矩阵变为上三角或对角矩阵。
3.2 算法流程
- 初始化:令 。
- 对当前矩阵 进行QR分解:(其中 是正交矩阵, 是上三角矩阵)。
- 计算新的矩阵:。
- 重复步骤2和3,直到 近似为对角矩阵(或达到最大迭代次数),对角线上的元素即为特征值的近似值。
注意事项:
- 对于对称矩阵,QR算法会收敛到对角矩阵;对于非对称矩阵,收敛到上三角矩阵(Schur形式)。
- 实际应用中常结合Hessenberg化(将矩阵化为上Hessenberg形式)和位移策略(shift)来加速收敛。
3.3 例题
例题2:使用QR算法计算矩阵 的特征值。
详细解答:
- 初始化:。
- 第一次迭代:
- 对 进行QR分解:
- 使用Gram-Schmidt正交化或其他方法,得到 ,(近似值)。
- 计算 。
- 对 进行QR分解:
- 第二次迭代:
- 对 进行QR分解,得到新的 和 。
- 计算 ,结果更接近对角矩阵。
- 继续迭代:经过多次迭代,矩阵逐渐收敛到 ,即特征值为 和 。
答案:特征值为 ,。
验证:直接计算特征方程 ,解得 或 ,与QR算法结果一致。
4. PageRank 算法(Google 搜索排序)
4.1 背景
- PageRank 是Google搜索引擎的核心算法之一,用于评估网页的重要性。
- 基本思想:网页的重要性取决于链接到该网页的其他网页数量和重要性,本质上是求网页链接矩阵的主特征向量。
- 数学模型:设 为转移概率矩阵(网页之间的链接概率),PageRank向量 满足 ,即 是矩阵 对应特征值 的特征向量。
4.2 算法流程
- 构造转移矩阵 : 表示从网页 链接到网页 的概率(若网页 有 个外链,则每个外链概率为 )。
- 选择初始向量 (通常为均匀分布,即每个网页初始重要性相等)。
- 迭代:。
- 归一化 ,确保向量元素之和为1。
- 收敛后, 的各个分量即为各网页的PageRank值(排名分数)。
阻尼因子(Damping Factor):
- 实际中引入阻尼因子 (通常为0.85),以模拟用户随机跳转行为,修正公式为 ,其中 为网页总数。
- 这保证了矩阵的收敛性,避免某些网页没有外链导致的问题。
4.3 例题
例题3:假设有3个网页,链接关系如下:网页1链接到2和3,网页2链接到3,网页3链接到1。计算各网页的PageRank值(忽略阻尼因子)。
详细解答:
- 构造转移矩阵 :
- 网页1有2个外链(到2和3),所以 ,,。
- 网页2有1个外链(到3),所以 ,,。
- 网页3有1个外链(到1),所以 ,,。
- 转移矩阵 。
- 初始化:。
- 第一次迭代:
- 。
- 归一化(可选,此处不归一化以观察收敛)。
- 第二次迭代:
- 。
- 继续迭代:经过多次迭代, 收敛到 (近似值)。
答案:网页1的PageRank值为 ,网页2的值为 ,网页3的值为 ,因此网页1和网页3最重要且同等重要。
5. 修正例题:幂迭代法
例题4(修正新增例题3):计算矩阵 的最大特征值及其特征向量。
详细解答:
- 初始化:,归一化后仍为 。
- 第一次迭代:
- 。
- 归一化:,。
- 第二次迭代:
- 。
- 归一化:,。
- 第三次迭代:
- 。
- 归一化:,。
- 继续迭代:向量逐渐收敛到 ,即未归一化形式为 或近似 。
- 计算特征值:,取 :
- 。
- 。
- 。
- 因此 。
答案:最大特征值约为 ,对应特征向量为 (近似值)。
验证:特征方程 ,解得 ,即 或 ,与幂迭代结果一致。
6. 常见考点与易错点
- 幂迭代法:初始向量不能与主特征向量正交,否则无法收敛到最大特征值;收敛速度受特征值比值影响。
- QR算法:只适合小规模矩阵或对称矩阵,实际应用中需结合位移策略;理解QR分解的正交性和收敛原理。
- 特征值/向量定义与实际意义:特征值分解在矩阵分析和应用中的重要性。
- PageRank:转移矩阵的构造方法,阻尼因子的作用,归一化的必要性。
- 英文术语拼写与公式记忆:确保考试中能准确写出专业术语和公式。
7. 英文关键词与表达
- eigenvalue, eigenvector, power iteration, Rayleigh quotient, QR algorithm, diagonalization, convergence, PageRank, principal component analysis (PCA), dominant eigenvalue, damping factor, transition matrix
如需更详细例题推导或某一算法的代码实现,可随时补充!
CE7453 Numerical Algorithms 期末高分超详细攻略(第七章:Fourier Transform)
本章内容:离散傅里叶变换(DFT)、快速傅里叶变换(FFT)、三角插值、JPEG应用
适用对象:零基础/考前冲刺/快速查漏补缺
内容特色:详细原理、公式推导、算法流程、例题全解、英文关键词
1. 基本概念与背景
- 傅里叶变换(Fourier Transform):将信号从时域变换到频域,分析信号的频率成分。
- 离散傅里叶变换(DFT):对有限长离散信号进行傅里叶变换,常用于数字信号处理、图像压缩等。
- 快速傅里叶变换(FFT):高效计算DFT的算法,大幅降低运算量。
- 实际应用:音频分析、图像处理(如JPEG压缩)、通信、工程仿真等。
2. 离散傅里叶变换(DFT)
2.1 公式与推导
- DFT公式:
其中, 是频域信号, 是时域信号, 是信号长度, 是虚数单位。 - IDFT公式(逆变换):
IDFT 将频域信号转换回时域信号。 - 性质:
- 可逆性:DFT 和 IDFT 互为逆运算,可以无损地从时域到频域再回到时域。
- 能量守恒:根据 Parseval 定理,信号在时域和频域的能量相等。
- 周期性:DFT 结果具有周期性,。
2.2 计算复杂度
- 直接计算 DFT 的复杂度为 ,因为对于每个 ,需要对所有 进行求和运算。
- 当信号长度 很大时,计算效率极低,因此需要更高效的算法(如 FFT)。
3. 快速傅里叶变换(FFT)
3.1 原理
- 分治思想:将长度为 的 DFT 分解为两个长度为 的 DFT,递归进行分解,直到长度为 1。
- 要求: 必须为 2 的幂,以便递归分解。
- FFT 通过减少重复计算,大幅降低复杂度。
3.2 算法流程(Cooley-Tukey FFT)
- 输入序列 。
- 若 ,直接返回 。
- 将序列分为偶数项 和奇数项 。
- 分别递归计算偶数和奇数部分的 DFT,得到 和 。
- 合并结果:
其中 、 分别为偶/奇部分的 DFT 结果, 是旋转因子。 - 复杂度从 降为 。
3.3 例题
例题1:计算 的 DFT。
详细解答:
- 给定 ,使用 DFT 公式 。
- 计算 :
- 计算 : 其中 ,,,所以:
- 计算 : 其中 ,,,所以:
- 计算 : 其中 ,,,所以:
- 最终结果:。
例题2:使用 FFT 计算 的 DFT。
解答步骤:
- ,满足 2 的幂要求。
- 第一步分解:分为偶数项 和奇数项 。
- 继续递归分解,直到长度为 1。
- 合并计算(由于篇幅限制,这里省略详细计算步骤,直接给出结果)。
- 结果:。
4. 三角插值与JPEG应用
4.1 三角插值(Trigonometric Interpolation)
- 定义:用三角函数(正弦、余弦)拟合周期数据,适用于周期性信号的分析和重构。
- 与 DFT 的关系:DFT 本质上就是三角插值系数的计算, 表示信号在不同频率上的分量。
- 应用:信号重构、数据平滑、周期性预测。
例题3:计算信号 的 DFT,并解释其频域意义。
详细解答:
- 使用 DFT 公式:。
- 计算 :
- 计算 :
- 计算 :
- 计算 :
- 结果:。
- 频域意义:信号的能量全部集中在直流分量(),表示信号没有周期性变化,是一个常数值信号。
4.2 JPEG 压缩中的 DCT
- 离散余弦变换(DCT):JPEG 采用 DCT,本质与 DFT 类似,但只使用实数部分(余弦函数),避免复数运算。
- DCT 公式(一维):
- 优势:
- DCT 能将图像能量集中在低频分量,便于去除高频噪声(人眼对高频细节不敏感)。
- 实数运算比 DFT 的复数运算更高效。
- JPEG 压缩流程:
- 将图像分成 8x8 像素块。
- 对每个块应用二维 DCT,得到频域系数。
- 量化:对高频系数进行压缩(丢弃小值或近似)。
- 编码:使用霍夫曼编码等方法进一步压缩数据。
- 解压时逆向操作:逆量化、逆 DCT 重构图像。
- DCT 与 DFT 的区别:
- DCT 仅用余弦函数,输出为实数;DFT 使用复指数,输出为复数。
- DCT 边界条件更适合图像处理(镜像对称),减少边界不连续性。
例题4:解释为什么 JPEG 压缩会丢失细节。
解答:
- JPEG 压缩通过量化步骤丢弃高频分量的小值系数,而高频分量对应图像的细节(如边缘、纹理)。
- 人眼对高频细节不敏感,因此丢弃这些分量可以在不明显影响视觉质量的情况下大幅减少数据量。
- 这种有损压缩导致细节丢失,尤其在高压缩比下,图像可能出现块状伪影(blocking artifacts)。
5. 常见考点与易错点
- DFT/IDFT 公式推导与正负号:注意指数中的正负号,DFT 为负号,IDFT 为正号。
- FFT 分治递归与合并步骤:理解如何分解序列和合并结果,旋转因子的作用。
- 频域与时域的物理意义:时域表示信号随时间变化,频域表示信号的频率成分和能量分布。
- DCT 与 DFT 的区别:DCT 仅用实数,适合图像压缩;DFT 处理复数,适合信号分析。
- 易错点:
- 计算 DFT 时漏掉旋转因子的复数性质。
- FFT 合并时未正确处理 偏移。
- 混淆 DCT 和 DFT 的应用场景。
- 英文术语拼写与公式记忆:熟悉常见术语拼写,考试中可能要求手写公式。
6. 英文关键词与表达
- discrete Fourier transform (DFT), fast Fourier transform (FFT), frequency domain, time domain, trigonometric interpolation, discrete cosine transform (DCT), JPEG compression, signal processing, spectrum, complex exponential, quantization, blocking artifacts
如需更详细例题推导或某一算法的代码实现,可随时补充!
CE7453 Numerical Algorithms 期末高分超详细攻略(第八章:3D Data Registration)
本章内容:3D数据配准、ICP算法、最优旋转/平移、SVD分解、实际应用
适用对象:零基础/考前冲刺/快速查漏补缺
内容特色:详细原理、公式推导、算法流程、例题全解、英文关键词
1. 基本概念与背景
- 3D数据配准(3D Data Registration):将多个3D点云或模型对齐到同一坐标系,常用于3D重建、医学成像、机器人导航等。
- 典型问题:给定两组点云,求最优刚性变换(旋转+平移)使它们尽量重合。
2. ICP算法(Iterative Closest Point)
2.1 原理
- 迭代式地将源点云与目标点云配准
- 每次迭代包括“最近点匹配”和“最优变换估计”两步
2.2 算法详细流程
- 初始化:设源点集 ,目标点集 ,初始变换
- 最近点匹配:对 中每个点 ,在 中找最近点
- 估计最优变换:求刚性变换 (旋转+平移),最小化配对点的均方误差
- 应用变换:用 更新
- 收敛判定:若误差变化小于阈值,停止;否则回到第2步
2.3 最优旋转/平移的SVD解法
- 设 和 已配对,先去中心化
- 计算协方差矩阵
- 对 做SVD分解
- 最优旋转 ,最优平移
3. 典型例题
例题1:已知 ,,求将 配准到 的最优刚性变换
解答:
- 计算质心:
- 的质心:
- 的质心:
- 去中心化:
- 去中心化后:
- 去中心化后:
- 计算协方差矩阵 :
- (对每个维度分别计算)
- 完整矩阵
- SVD分解:
- ,得到 ,(因为 是对角矩阵)
- 旋转矩阵 (单位矩阵)
- 计算平移向量:
- 结果:
- 最优旋转矩阵 为单位矩阵(无旋转)
- 最优平移向量
验证:将 的点应用变换后,,,与 完全重合。
例题2:给定点集 和 ,求将 配准到 的最优刚性变换
解答步骤:
- 计算质心:
- 的质心:
- 的质心:
- 去中心化:
- 去中心化后:
- 去中心化后:
- 计算协方差矩阵 :
- 对于每个点对,计算外积并求和
- SVD分解:
- ,得到 ,
- 旋转矩阵 (单位矩阵)
- 计算平移向量:
- 结果:
- 最优旋转矩阵 为单位矩阵(无旋转)
- 最优平移向量
验证:将 的点应用变换后,,,与 完全重合。
例题3(进阶):给定点集 和 ,求将 配准到 的最优刚性变换(涉及旋转)。
解答步骤:
- 计算质心:
- 的质心:
- 的质心:
- 去中心化:
- 去中心化后:
- 去中心化后:
- 计算协方差矩阵 :
- 对应点对:,,
- 计算每个点对的外积并求和,得到
- SVD分解:
- 对 进行 SVD 分解,得到 和
- 的特征值和特征向量计算后,(绕 z 轴旋转 90 度)
- 计算平移向量:
- 结果:
- 最优旋转矩阵
- 最优平移向量
验证:应用变换后, 的点将旋转并平移到与 接近重合的位置(由于点对匹配,此处为近似解)。
4. 常见考点与易错点
- 最近点匹配的高效实现(如k-d树)
- SVD分解步骤与旋转矩阵构造
- ICP收敛性与初始位置敏感性
- 多组点云全局配准与误差累积
- 英文术语拼写与公式记忆
5. 英文关键词与表达
- 3D data registration, point cloud, rigid transformation, rotation, translation, iterative closest point (ICP), singular value decomposition (SVD), covariance matrix, alignment, convergence
如需更详细例题推导或某一算法的代码实现,可随时补充!