📚 飞哥文档库

🍃 MongoDB NoSQL 驱动 | 全文搜索 | 灵活查询

📁 其他 agent22 👁️ 1 次浏览 | 🕐 2026-02-22 04:47

Agent 22 - Python科学计算实战(第一性原理版)

费曼原则:如果你不能用代码实现它,你就没有真正理解它。


核心哲学:代码即证明

数学概念往往是抽象的,但Python让抽象变得可触摸、可验证、可可视化。本教程遵循"第一性原理"思维:

  1. 用代码验证数学:不写数学伪代码,写真实可运行的Python
  2. 让抽象变具体:每个概念都有可视化呈现
  3. 从数值到符号:从数值实验过渡到严格数学证明

第一层:Python科研环境

1.1 环境搭建

Anaconda安装与配置

# 安装Anaconda后,创建隔离的数学计算环境
conda create -n math python=3.11
conda activate math

# 安装核心科学计算库
conda install numpy scipy sympy matplotlib pandas jupyter
pip install plotly ipywidgets

Jupyter Notebook/Lab配置

# 在~/.jupyter/jupyter_notebook_config.py中添加
import numpy as np
import matplotlib.pyplot as plt
from sympy import *
from scipy import *

# 自动导入设置
%matplotlib inline
%config InlineBackend.figure_format = 'retina'

VS Code科研配置

// .vscode/settings.json
{
    "python.analysis.typeCheckingMode": "basic",
    "jupyter.askForKernelRestart": false,
    "editor.formatOnSave": true,
    "python.analysis.autoImportCompletions": true
}

1.2 核心库速览

功能 典型应用
NumPy 数组与矩阵运算 向量化计算、线性代数
SciPy 科学计算 积分、优化、微分方程
SymPy 符号计算 解析解、公式推导
Matplotlib 可视化 函数图像、数据分析
Plotly 交互式可视化 3D图形、动态演示

NumPy基础(向量化编程入门)

import numpy as np

# 创建数组
x = np.linspace(0, 2*np.pi, 1000)  # 1000个点均匀分布在[0, 2π]
y = np.sin(x)                       # 向量化计算,无需循环

# 矩阵运算
A = np.array([[1, 2], [3, 4]])
B = np.linalg.inv(A)                # 矩阵求逆
eigenvalues = np.linalg.eigvals(A)  # 特征值

# 性能对比:向量化 vs 循环
def compute_loop(n):
    """Python循环版本(慢)"""
    result = []
    for i in range(n):
        result.append(np.sin(i/n))
    return result

def compute_vectorized(n):
    """NumPy向量化版本(快)"""
    x = np.arange(n) / n
    return np.sin(x)

# 向量化通常快100倍以上

第二层:高数概念验证

2.1 极限与连续

2.1.1 数列收敛可视化

费曼视角:极限不是一个遥远的目标,而是一个越来越近的过程。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

def visualize_sequence_convergence():
    """
    可视化数列 a_n = 1/n 收敛到 0
    费曼解释:想象一只蚂蚁每次走一半,它最终会无限接近但永不触碰
    """
    fig, ax = plt.subplots(figsize=(10, 6))

    # 生成数列 a_n = 1/n
    n = np.arange(1, 101)
    a_n = 1 / n

    # 绘制收敛过程
    ax.scatter(n, a_n, c=n, cmap='viridis', s=50, alpha=0.7)
    ax.axhline(y=0, color='red', linestyle='--', label='极限 L=0')
    ax.set_xlabel('n', fontsize=12)
    ax.set_ylabel('$a_n = \\frac{1}{n}$', fontsize=12)
    ax.set_title('数列收敛可视化:当 n→∞ 时,a_n→0', fontsize=14)
    ax.legend()
    ax.grid(True, alpha=0.3)

    # 添加收敛带
    epsilon = 0.05
    ax.axhspan(-epsilon, epsilon, alpha=0.2, color='green', label=f'ε={epsilon}')
    N_epsilon = int(np.ceil(1/epsilon))
    ax.axvline(x=N_epsilon, color='green', linestyle=':', alpha=0.7)
    ax.text(N_epsilon+2, 0.5, f'N={N_epsilon}', fontsize=10)

    return fig

# 更多收敛数列示例
def demonstrate_convergence_types():
    """
    展示不同类型的收敛
    """
    n = np.arange(1, 51)

    sequences = {
        '1/n (单调收敛)': 1/n,
        '(-1)^n/n (振荡收敛)': (-1)**n / n,
        '1 - 1/n (单调递增)': 1 - 1/n,
        'n/(n+1) (有理函数)': n/(n+1),
        '(1+1/n)^n → e': (1 + 1/n)**n
    }

    fig, axes = plt.subplots(2, 3, figsize=(15, 10))
    axes = axes.flatten()

    for idx, (name, seq) in enumerate(sequences.items()):
        ax = axes[idx]
        ax.plot(n, seq, 'bo-', markersize=4, alpha=0.7)

        # 标注极限
        limit = seq[-1]
        ax.axhline(y=limit, color='red', linestyle='--', alpha=0.7, label=f'极限≈{limit:.4f}')
        ax.set_title(name)
        ax.set_xlabel('n')
        ax.set_ylabel('$a_n$')
        ax.legend()
        ax.grid(True, alpha=0.3)

    plt.tight_layout()
    return fig

2.1.2 ε-δ定义验证

def verify_epsilon_delta():
    """
    验证 lim(x→2) x² = 4 的 ε-δ 定义

    对于任意 ε > 0,存在 δ > 0,使得当 0 < |x-2| < δ 时,|x²-4| < ε

    费曼视角:无论多窄的"误差带"(ε),我总能找到足够小的"邻域"(δ)
    """
    fig, axes = plt.subplots(1, 2, figsize=(14, 6))

    # 左图:直观理解
    ax1 = axes[0]
    x = np.linspace(0, 4, 1000)
    y = x**2

    epsilon = 0.5
    delta = epsilon / 5  # 因为导数在x=2处为4,所以 δ ≈ ε/4

    ax1.plot(x, y, 'b-', linewidth=2, label='$f(x) = x^2$')
    ax1.axhline(y=4, color='gray', linestyle='-', alpha=0.5)
    ax1.axvline(x=2, color='gray', linestyle='-', alpha=0.5)

    # ε-带
    ax1.axhspan(4-epsilon, 4+epsilon, alpha=0.2, color='red', label=f'ε={epsilon}')
    # δ-带
    ax1.axvspan(2-delta, 2+delta, alpha=0.2, color='green', label=f'δ={delta:.3f}')

    ax1.set_xlabel('x')
    ax1.set_ylabel('f(x)')
    ax1.set_title('ε-δ 定义的直观理解')
    ax1.legend()
    ax1.grid(True, alpha=0.3)
    ax1.set_xlim([0, 4])
    ax1.set_ylim([0, 16])

    # 右图:验证函数
    ax2 = axes[1]
    epsilons = np.logspace(-3, 0, 50)  # 从0.001到1

    # 精确计算:|x²-4| < ε ⟹ |x-2||x+2| < ε
    # 在 x≈2 附近,|x+2|≈4,所以 δ = ε/4
    deltas = epsilons / 4

    ax2.loglog(epsilons, deltas, 'b-', linewidth=2, label='δ = ε/4')
    ax2.loglog(epsilons, epsilons, 'r--', alpha=0.5, label='δ = ε (参考线)')
    ax2.set_xlabel('ε')
    ax2.set_ylabel('δ')
    ax2.set_title('ε-δ 对应关系')
    ax2.legend()
    ax2.grid(True, alpha=0.3)

    return fig

def find_delta_for_epsilon(f, c, L, epsilon, x_range=(0, 5)):
    """
    数值方法:给定ε,寻找合适的δ

    Args:
        f: 目标函数
        c: 趋近点
        L: 极限值
        epsilon: 误差限
        x_range: 搜索范围

    Returns:
        delta: 找到的δ值
    """
    x = np.linspace(x_range[0], x_range[1], 10000)
    y = f(x)

    # 找到满足 |f(x)-L| < ε 的 x 区间
    mask = np.abs(y - L) < epsilon
    valid_x = x[mask]

    if len(valid_x) == 0:
        return None

    # 找到包含 c 的最大连通区间
    left = np.min(valid_x[valid_x < c]) if np.any(valid_x < c) else c
    right = np.max(valid_x[valid_x > c]) if np.any(valid_x > c) else c

    delta = min(c - left, right - c)
    return delta

# 使用示例
# epsilon = 0.01
# delta = find_delta_for_epsilon(lambda x: x**2, 2, 4, epsilon)
# print(f"当 ε={epsilon} 时,取 δ={delta:.6f} 可保证 |x²-4| < {epsilon}")

2.1.3 函数连续性检测

def check_continuity(f, x, tol=1e-6):
    """
    数值检测函数在点x处的连续性

    原理:lim(h→0) f(x+h) = f(x)
    """
    h_values = np.logspace(-10, -1, 100)
    left_limits = [f(x - h) for h in h_values]
    right_limits = [f(x + h) for h in h_values]

    f_x = f(x)

    left_continuous = np.allclose(left_limits[::-1], f_x, atol=tol)
    right_continuous = np.allclose(right_limits, f_x, atol=tol)

    return {
        'left_continuous': left_continuous,
        'right_continuous': right_continuous,
        'continuous': left_continuous and right_continuous,
        'left_limit': left_limits[-1] if left_limits else None,
        'right_limit': right_limits[-1] if right_limits else None,
        'function_value': f_x
    }

# 示例:检测各类不连续点
def visualize_discontinuities():
    """
    可视化不同类型的不连续性
    """
    fig, axes = plt.subplots(2, 2, figsize=(12, 10))

    x = np.linspace(-2, 2, 1000)

    # 1. 可去间断点
    ax1 = axes[0, 0]
    x1 = x[x != 1]
    y1 = (x1**2 - 1) / (x1 - 1)  # = x+1 当 x≠1
    ax1.plot(x1, y1, 'b-', linewidth=2)
    ax1.plot(1, 2, 'ro', markersize=10, fillstyle='none', markeredgewidth=2, label='可去间断点')
    ax1.set_title('可去间断点: $f(x) = \\frac{x^2-1}{x-1}$')
    ax1.grid(True, alpha=0.3)
    ax1.legend()

    # 2. 跳跃间断点
    ax2 = axes[0, 1]
    y2 = np.where(x < 0, -1, 1)
    ax2.plot(x[x < 0], y2[x < 0], 'b-', linewidth=2)
    ax2.plot(x[x >= 0], y2[x >= 0], 'b-', linewidth=2)
    ax2.plot(0, 0, 'ro', markersize=10, fillstyle='none', markeredgewidth=2)
    ax2.set_title('跳跃间断点: 符号函数')
    ax2.set_ylim([-2, 2])
    ax2.grid(True, alpha=0.3)

    # 3. 无穷间断点
    ax3 = axes[1, 0]
    x3 = x[x != 0]
    y3 = 1 / x3
    ax3.plot(x3[x3 < 0], y3[x3 < 0], 'b-', linewidth=2)
    ax3.plot(x3[x3 > 0], y3[x3 > 0], 'b-', linewidth=2)
    ax3.axvline(x=0, color='red', linestyle='--', label='无穷间断点')
    ax3.set_title('无穷间断点: $f(x) = \\frac{1}{x}$')
    ax3.set_ylim([-10, 10])
    ax3.grid(True, alpha=0.3)
    ax3.legend()

    # 4. 振荡间断点
    ax4 = axes[1, 1]
    x4 = x[x != 0]
    y4 = np.sin(1/x4)
    ax4.plot(x4, y4, 'b-', linewidth=0.5)
    ax4.axvline(x=0, color='red', linestyle='--', label='振荡间断点')
    ax4.set_title('振荡间断点: $f(x) = \\sin(\\frac{1}{x})$')
    ax4.set_ylim([-1.5, 1.5])
    ax4.grid(True, alpha=0.3)
    ax4.legend()

    plt.tight_layout()
    return fig

2.2 导数与微分

2.2.1 数值微分方法

import numpy as np

class NumericalDifferentiation:
    """
    数值微分:当解析解难求时,用有限差分近似导数

    费曼理解:导数是"变化率的放大镜"
    """

    @staticmethod
    def forward_diff(f, x, h=1e-5):
        """前向差分: f'(x) ≈ [f(x+h) - f(x)] / h
        误差: O(h)
        """
        return (f(x + h) - f(x)) / h

    @staticmethod
    def backward_diff(f, x, h=1e-5):
        """后向差分: f'(x) ≈ [f(x) - f(x-h)] / h
        误差: O(h)
        """
        return (f(x) - f(x - h)) / h

    @staticmethod
    def central_diff(f, x, h=1e-5):
        """中心差分: f'(x) ≈ [f(x+h) - f(x-h)] / (2h)
        误差: O(h²) —— 更精确!
        """
        return (f(x + h) - f(x - h)) / (2 * h)

    @staticmethod
    def five_point_stencil(f, x, h=1e-5):
        """五点公式: f'(x) ≈ [-f(x+2h) + 8f(x+h) - 8f(x-h) + f(x-2h)] / (12h)
        误差: O(h⁴) —— 高精度
        """
        return (-f(x + 2*h) + 8*f(x + h) - 8*f(x - h) + f(x - 2*h)) / (12 * h)

    @staticmethod
    def second_derivative(f, x, h=1e-5):
        """二阶导数: f''(x) ≈ [f(x+h) - 2f(x) + f(x-h)] / h²
        物理意义:加速度、曲率
        """
        return (f(x + h) - 2*f(x) + f(x - h)) / (h**2)

# 精度验证示例
def verify_numerical_differentiation():
    """
    用已知导数的函数验证数值微分的精度
    """
    # 测试函数:f(x) = e^x * sin(x)
    # 精确导数:f'(x) = e^x * (sin(x) + cos(x))

    def f(x):
        return np.exp(x) * np.sin(x)

    def df_exact(x):
        return np.exp(x) * (np.sin(x) + np.cos(x))

    x_test = 1.0
    exact = df_exact(x_test)

    # 测试不同步长
    h_values = np.logspace(-10, -1, 50)

    methods = {
        'Forward': NumericalDifferentiation.forward_diff,
        'Backward': NumericalDifferentiation.backward_diff,
        'Central': NumericalDifferentiation.central_diff,
        'Five-point': NumericalDifferentiation.five_point_stencil
    }

    errors = {name: [] for name in methods}

    for h in h_values:
        for name, method in methods.items():
            approx = method(f, x_test, h)
            error = abs(approx - exact)
            errors[name].append(error)

    # 可视化
    fig, ax = plt.subplots(figsize=(10, 6))
    for name, errs in errors.items():
        ax.loglog(h_values, errs, 'o-', label=name, markersize=3)

    ax.axhline(y=np.finfo(float).eps, color='k', linestyle='--', alpha=0.5, label='Machine ε')
    ax.set_xlabel('Step size h')
    ax.set_ylabel('Absolute Error')
    ax.set_title('数值微分方法的误差比较')
    ax.legend()
    ax.grid(True, alpha=0.3)

    return fig

2.2.2 梯度计算与自动微分

import numpy as np

def gradient_2d(f, x, y, h=1e-5):
    """
    计算二元函数的梯度 ∇f = [∂f/∂x, ∂f/∂y]

    费曼几何解释:梯度指向函数增长最快的方向
    """
    df_dx = (f(x + h, y) - f(x - h, y)) / (2 * h)
    df_dy = (f(x, y + h) - f(x, y - h)) / (2 * h)
    return np.array([df_dx, df_dy])

def visualize_gradient_field():
    """
    可视化梯度场:
    - 等高线表示函数值
    - 箭头表示梯度方向(垂直于等高线,指向高处)
    """
    fig, axes = plt.subplots(1, 2, figsize=(14, 6))

    # 创建网格
    x = np.linspace(-3, 3, 50)
    y = np.linspace(-3, 3, 50)
    X, Y = np.meshgrid(x, y)

    # 测试函数1:f(x,y) = x² + y² (碗形)
    Z1 = X**2 + Y**2
    ax1 = axes[0]
    contour1 = ax1.contour(X, Y, Z1, levels=15, cmap='viridis')
    ax1.clabel(contour1, inline=True, fontsize=8)

    # 计算并绘制梯度场
    skip = 3
    dZ1_dx = 2 * X
    dZ1_dy = 2 * Y
    ax1.quiver(X[::skip, ::skip], Y[::skip, ::skip], 
               dZ1_dx[::skip, ::skip], dZ1_dy[::skip, ::skip],
               scale=50, color='red', alpha=0.7)
    ax1.set_title('$f(x,y) = x^2 + y^2$ 的梯度场')
    ax1.set_xlabel('x')
    ax1.set_ylabel('y')
    ax1.set_aspect('equal')

    # 测试函数2:f(x,y) = sin(x) * cos(y) (波纹形)
    Z2 = np.sin(X) * np.cos(Y)
    ax2 = axes[1]
    contour2 = ax2.contour(X, Y, Z2, levels=15, cmap='viridis')
    ax2.clabel(contour2, inline=True, fontsize=8)

    dZ2_dx = np.cos(X) * np.cos(Y)
    dZ2_dy = -np.sin(X) * np.sin(Y)
    ax2.quiver(X[::skip, ::skip], Y[::skip, ::skip],
               dZ2_dx[::skip, ::skip], dZ2_dy[::skip, ::skip],
               scale=20, color='red', alpha=0.7)
    ax2.set_title('$f(x,y) = \\sin(x)\\cos(y)$ 的梯度场')
    ax2.set_xlabel('x')
    ax2.set_ylabel('y')
    ax2.set_aspect('equal')

    plt.tight_layout()
    return fig

2.2.3 泰勒展开验证

import numpy as np
import sympy as sp

def taylor_series_visualization():
    """
    可视化泰勒展开:用多项式逼近任意函数

    费曼洞察:泰勒展开就像用"本地信息"(函数值和各阶导数)
    重建"全局行为"
    """
    fig, axes = plt.subplots(2, 2, figsize=(14, 12))

    x = np.linspace(-np.pi, np.pi, 500)

    # 1. sin(x) 在 x=0 处的泰勒展开
    ax1 = axes[0, 0]
    ax1.plot(x, np.sin(x), 'k-', linewidth=2, label='$\\sin(x)$')

    colors = plt.cm.viridis(np.linspace(0, 1, 5))
    for n, color in zip([1, 3, 5, 7, 9], colors):
        # 泰勒展开: sin(x) ≈ Σ (-1)^k * x^(2k+1) / (2k+1)!
        taylor = sum([(-1)**k * x**(2*k+1) / np.math.factorial(2*k+1) 
                      for k in range((n+1)//2)])
        ax1.plot(x, taylor, '--', color=color, alpha=0.8, 
                label=f'n={n}')

    ax1.set_title('$\\sin(x)$ 的泰勒展开(在 x=0 处)')
    ax1.set_ylim([-2, 2])
    ax1.legend()
    ax1.grid(True, alpha=0.3)
    ax1.axhline(y=0, color='k', linewidth=0.5)
    ax1.axvline(x=0, color='k', linewidth=0.5)

    # 2. e^x 的泰勒展开
    ax2 = axes[0, 1]
    ax2.plot(x, np.exp(x), 'k-', linewidth=2, label='$e^x$')

    for n, color in zip([1, 2, 3, 4, 5], colors):
        taylor = sum([x**k / np.math.factorial(k) for k in range(n+1)])
        ax2.plot(x, taylor, '--', color=color, alpha=0.8,
                label=f'n={n}')

    ax2.set_title('$e^x$ 的泰勒展开')
    ax2.set_ylim([-2, 10])
    ax2.legend()
    ax2.grid(True, alpha=0.3)
    ax2.axhline(y=0, color='k', linewidth=0.5)
    ax2.axvline(x=0, color='k', linewidth=0.5)

    # 3. 收敛半径可视化(以 1/(1-x) 为例)
    ax3 = axes[1, 0]
    x3 = np.linspace(-2, 2, 500)
    x3_valid = x3[np.abs(x3) < 1]

    ax3.plot(x3_valid, 1/(1-x3_valid), 'k-', linewidth=2, label='$\\frac{1}{1-x}$')

    for n, color in zip([3, 5, 10, 20], colors):
        taylor = sum([x3**k for k in range(n+1)])  # 几何级数
        ax3.plot(x3, taylor, '--', color=color, alpha=0.8,
                label=f'n={n}')

    ax3.axvline(x=1, color='red', linestyle=':', alpha=0.7, label='收敛边界 |x|=1')
    ax3.axvline(x=-1, color='red', linestyle=':', alpha=0.7)
    ax3.set_title('收敛半径示例:$\\frac{1}{1-x}$')
    ax3.set_ylim([-5, 10])
    ax3.legend()
    ax3.grid(True, alpha=0.3)
    ax3.axhline(y=0, color='k', linewidth=0.5)
    ax3.axvline(x=0, color='k', linewidth=0.5)

    # 4. 误差分析
    ax4 = axes[1, 1]
    x0 = 1.0  # 在 x=1 处评估 sin(x) 的泰勒展开误差
    n_values = range(1, 20)

    exact = np.sin(x0)
    errors = []
    for n in n_values:
        approx = sum([(-1)**k * x0**(2*k+1) / np.math.factorial(2*k+1) 
                      for k in range((n+1)//2)])
        errors.append(abs(approx - exact))

    ax4.semilogy(n_values, errors, 'bo-', markersize=6)
    ax4.set_xlabel('泰勒展开的阶数 n')
    ax4.set_ylabel('绝对误差')
    ax4.set_title('泰勒展开的收敛速度')
    ax4.grid(True, alpha=0.3)
    ax4.axhline(y=np.finfo(float).eps, color='red', linestyle='--', 
                label='机器精度')
    ax4.legend()

    plt.tight_layout()
    return fig

# 使用 SymPy 进行符号泰勒展开
def symbolic_taylor_expansion():
    """
    使用 SymPy 进行精确的符号泰勒展开
    """
    x = sp.Symbol('x')

    functions = {
        'sin(x)': sp.sin(x),
        'cos(x)': sp.cos(x),
        'e^x': sp.exp(x),
        'ln(1+x)': sp.log(1+x)
    }

    results = {}
    for name, func in functions.items():
        # 在 x=0 处展开到 6 阶
        taylor = sp.series(func, x, 0, 7).removeO()
        results[name] = taylor
        print(f"{name} ≈ {taylor}")

    return results

2.3 积分计算

2.3.1 数值积分方法

import numpy as np
from scipy import integrate

class NumericalIntegration:
    """
    数值积分:当原函数难求时,用数值方法近似定积分

    费曼理解:积分是"无穷小累加的艺术"
    """

    @staticmethod
    def rectangle_rule(f, a, b, n=1000):
        """
        矩形法(左端点):∫[a,b]f(x)dx ≈ h * Σ f(x_i)
        误差: O(1/n)
        """
        h = (b - a) / n
        x = np.linspace(a, b - h, n)  # 左端点
        return h * np.sum(f(x))

    @staticmethod
    def trapezoidal_rule(f, a, b, n=1000):
        """
        梯形法:∫[a,b]f(x)dx ≈ h * [f(a)/2 + Σ f(x_i) + f(b)/2]
        误差: O(1/n²)
        """
        h = (b - a) / n
        x = np.linspace(a, b, n + 1)
        y = f(x)
        return h * (0.5 * y[0] + np.sum(y[1:-1]) + 0.5 * y[-1])

    @staticmethod
    def simpson_rule(f, a, b, n=1000):
        """
        Simpson法则(抛物线近似):
        ∫[a,b]f(x)dx ≈ h/3 * [f(a) + 4Σf(x_odd) + 2Σf(x_even) + f(b)]
        误差: O(1/n⁴) —— 高精度!
        注意:n 必须是偶数
        """
        if n % 2 == 1:
            n += 1
        h = (b - a) / n
        x = np.linspace(a, b, n + 1)
        y = f(x)

        result = h/3 * (y[0] + 
                       4 * np.sum(y[1:-1:2]) + 
                       2 * np.sum(y[2:-2:2]) + 
                       y[-1])
        return result

    @staticmethod
    def gaussian_quadrature(f, a, b, n=5):
        """
        高斯-勒让德求积:使用最优节点和权重
        对于多项式,2n-1 阶以下精确
        """
        # 使用 scipy 的实现
        result, error = integrate.quad(f, a, b)
        return result

# 误差比较实验
def compare_integration_methods():
    """
    比较不同数值积分方法的精度和收敛速度
    """
    # 测试函数:∫[0,π] sin(x) dx = 2
    def f(x):
        return np.sin(x)

    exact = 2.0
    a, b = 0, np.pi

    n_values = [10, 20, 50, 100, 200, 500, 1000, 2000]

    methods = {
        'Rectangle': NumericalIntegration.rectangle_rule,
        'Trapezoidal': NumericalIntegration.trapezoidal_rule,
        'Simpson': NumericalIntegration.simpson_rule
    }

    errors = {name: [] for name in methods}

    for n in n_values:
        for name, method in methods.items():
            approx = method(f, a, b, n)
            error = abs(approx - exact)
            errors[name].append(error)

    # 可视化
    fig, ax = plt.subplots(figsize=(10, 6))
    for name, errs in errors.items():
        ax.loglog(n_values, errs, 'o-', label=name, markersize=6)

    # 参考线
    n_array = np.array(n_values)
    ax.loglog(n_array, 1/n_array, 'k--', alpha=0.5, label='O(1/n)')
    ax.loglog(n_array, 1/n_array**2, 'k:', alpha=0.5, label='O(1/n²)')
    ax.loglog(n_array, 1/n_array**4, 'k-.', alpha=0.5, label='O(1/n⁴)')

    ax.set_xlabel('Number of subintervals n')
    ax.set_ylabel('Absolute Error')
    ax.set_title('数值积分方法的收敛速度比较')
    ax.legend()
    ax.grid(True, alpha=0.3)

    return fig

2.3.2 符号积分验证

import sympy as sp

def symbolic_integration_examples():
    """
    使用 SymPy 进行精确符号积分

    验证数值结果的准确性
    """
    x = sp.Symbol('x')

    # 基本积分
    integrals = [
        ('多项式', x**3 + 2*x**2 - x + 1),
        ('三角函数', sp.sin(x)**2),
        ('指数函数', sp.exp(x) * sp.sin(x)),
        ('有理函数', 1/(1 + x**2)),
        ('根式函数', sp.sqrt(1 - x**2)),
    ]

    results = []
    for name, func in integrals:
        # 不定积分
        indefinite = sp.integrate(func, x)
        # 验证:求导后应恢复原函数
        verified = sp.simplify(sp.diff(indefinite, x) - func) == 0

        results.append({
            'name': name,
            'function': func,
            'integral': indefinite,
            'verified': verified
        })

        print(f"\n{name}:")
        print(f"  ∫ {func} dx = {indefinite}")
        print(f"  验证通过: {verified}")

    return results

def definite_integral_examples():
    """
    定积分示例:计算面积、体积、质心等
    """
    x = sp.Symbol('x')

    examples = [
        ('圆面积', sp.sqrt(1 - x**2), -1, 1, sp.pi/2),
        ('高斯积分', sp.exp(-x**2), -sp.oo, sp.oo, sp.sqrt(sp.pi)),
        ('反常积分', 1/x**2, 1, sp.oo, 1),
    ]

    for name, func, a, b, expected in examples:
        result = sp.integrate(func, (x, a, b))
        print(f"\n{name}: ∫[{a},{b}] {func} dx = {result}")
        if expected is not None:
            print(f"  期望值: {expected}")
            print(f"  相等: {sp.simplify(result - expected) == 0}")

2.3.3 反常积分处理

import numpy as np
from scipy import integrate

def improper_integration():
    """
    处理反常积分(无穷区间或无界函数)

    费曼洞察:反常积分是"可驯服的无穷"
    """

    # 1. 无穷区间积分
    def infinite_interval_examples():
        examples = [
            ('高斯积分', lambda x: np.exp(-x**2), 0, np.inf, np.sqrt(np.pi)/2),
            ('p-积分 (p>1)', lambda x: 1/x**2, 1, np.inf, 1),
            ('指数衰减', lambda x: np.exp(-x), 0, np.inf, 1),
        ]

        results = []
        for name, f, a, b, expected in examples:
            result, error = integrate.quad(f, a, b)
            results.append({
                'name': name,
                'numerical': result,
                'expected': expected,
                'error': error,
                'diff': abs(result - expected)
            })
        return results

    # 2. 瑕积分(无界函数)
    def singular_integration():
        examples = [
            ('瑕积分', lambda x: 1/np.sqrt(x), 0, 1, 2),
            ('对数积分', lambda x: -np.log(x), 0, 1, 1),
        ]

        results = []
        for name, f, a, b, expected in examples:
            # scipy 可以自动处理某些奇点
            result, error = integrate.quad(f, a, b)
            results.append({
                'name': name,
                'numerical': result,
                'expected': expected
            })
        return results

    # 3. 柯西主值
    def cauchy_principal_value():
        """
        计算 1/x 在 -1 到 1 的柯西主值
        P.V. ∫[-1,1] 1/x dx = 0(奇函数对称)
        """
        # 使用 scipy 的特殊参数
        result, error, info = integrate.quad(lambda x: 1/x, -1, 1,
                                             limit=100,
                                             weight='cauchy', wvar=0)
        return result

    return {
        'infinite': infinite_interval_examples(),
        'singular': singular_integration(),
        'cauchy_pv': cauchy_principal_value()
    }

第三层:实战项目

项目1:函数可视化系统

# project1_function_visualizer.py

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
import sympy as sp

class FunctionVisualizer:
    """
    交互式函数可视化系统

    功能:
    - 绘制函数图像
    - 显示导数与原函数关系
    - 泰勒展开动画逼近
    """

    def __init__(self):
        self.x_range = (-10, 10)
        self.fig = None
        self.axes = None

    def interactive_plot(self, func_str='sin(x)'):
        """
        创建交互式函数绘图界面
        """
        x = np.linspace(self.x_range[0], self.x_range[1], 1000)

        # 将字符串转换为函数
        x_sym = sp.Symbol('x')
        func_sym = sp.sympify(func_str)
        f = sp.lambdify(x_sym, func_sym, 'numpy')

        fig, ax = plt.subplots(figsize=(12, 6))
        plt.subplots_adjust(bottom=0.25)

        y = f(x)
        line, = ax.plot(x, y, 'b-', linewidth=2, label=f'y = {func_str}')
        ax.axhline(y=0, color='k', linewidth=0.5)
        ax.axvline(x=0, color='k', linewidth=0.5)
        ax.grid(True, alpha=0.3)
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_title('函数可视化')
        ax.legend()

        # 添加导数显示
        df_sym = sp.diff(func_sym, x_sym)
        df = sp.lambdify(x_sym, df_sym, 'numpy')

        # 创建滑块控制显示范围
        axcolor = 'lightgoldenrodyellow'
        ax_xmin = plt.axes([0.15, 0.1, 0.3, 0.03], facecolor=axcolor)
        ax_xmax = plt.axes([0.6, 0.1, 0.3, 0.03], facecolor=axcolor)

        sxmin = Slider(ax_xmin, 'X min', -20, 0, valinit=self.x_range[0])
        sxmax = Slider(ax_xmax, 'X max', 0, 20, valinit=self.x_range[1])

        def update(val):
            xmin = sxmin.val
            xmax = sxmax.val
            x_new = np.linspace(xmin, xmax, 1000)
            y_new = f(x_new)
            line.set_xdata(x_new)
            line.set_ydata(y_new)
            ax.set_xlim([xmin, xmax])
            ax.relim()
            ax.autoscale_view()
            fig.canvas.draw_idle()

        sxmin.on_changed(update)
        sxmax.on_changed(update)

        return fig

    def derivative_relationship(self, func_str='x**3 - 3*x'):
        """
        可视化导数与原函数的关系
        - 函数上升时,导数为正
        - 函数下降时,导数为负
        - 极值点,导数为零
        """
        x_sym = sp.Symbol('x')
        func_sym = sp.sympify(func_str)
        df_sym = sp.diff(func_sym, x_sym)

        f = sp.lambdify(x_sym, func_sym, 'numpy')
        df = sp.lambdify(x_sym, df_sym, 'numpy')

        x = np.linspace(-3, 3, 500)
        y = f(x)
        dy = df(x)

        fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)

        # 原函数
        ax1 = axes[0]
        ax1.plot(x, y, 'b-', linewidth=2, label=f'$f(x) = {sp.latex(func_sym)}$')
        ax1.axhline(y=0, color='k', linewidth=0.5)
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        ax1.set_ylabel('f(x)')
        ax1.set_title('原函数')

        # 导数
        ax2 = axes[1]
        ax2.plot(x, dy, 'r-', linewidth=2, label=f"$f'(x) = {sp.latex(df_sym)}$")
        ax2.axhline(y=0, color='k', linewidth=0.5)
        ax2.fill_between(x, 0, dy, where=(dy > 0), alpha=0.3, color='green', label='f 上升')
        ax2.fill_between(x, 0, dy, where=(dy < 0), alpha=0.3, color='red', label='f 下降')
        ax2.legend()
        ax2.grid(True, alpha=0.3)
        ax2.set_ylabel("f'(x)")
        ax2.set_title('导数')

        # 对应关系
        ax3 = axes[2]
        ax3.plot(x, y, 'b-', linewidth=2, alpha=0.7, label='f(x)')
        # 标记导数为零的点(极值点)
        zero_crossings = x[:-1][np.diff(np.sign(dy)) != 0]
        for zc in zero_crossings:
            ax3.axvline(x=zc, color='purple', linestyle='--', alpha=0.5)
            ax3.plot(zc, f(zc), 'ro', markersize=10)
        ax3.legend()
        ax3.grid(True, alpha=0.3)
        ax3.set_ylabel('f(x)')
        ax3.set_xlabel('x')
        ax3.set_title('极值点(导数为零)')

        plt.tight_layout()
        return fig

    def taylor_animation(self, func_str='sin(x)', center=0, max_degree=20):
        """
        泰勒展开逼近动画
        """
        x_sym = sp.Symbol('x')
        func_sym = sp.sympify(func_str)

        x = np.linspace(-np.pi, np.pi, 500)
        y_exact = sp.lambdify(x_sym, func_sym, 'numpy')(x)

        fig, ax = plt.subplots(figsize=(12, 6))
        line_exact, = ax.plot(x, y_exact, 'k-', linewidth=2, label='Exact')
        line_taylor, = ax.plot([], [], 'r--', linewidth=2, label='Taylor')

        ax.set_xlim([-np.pi, np.pi])
        ax.set_ylim([-2, 2])
        ax.axhline(y=0, color='k', linewidth=0.5)
        ax.axvline(x=0, color='k', linewidth=0.5)
        ax.grid(True, alpha=0.3)
        ax.legend()
        ax.set_title(f'Taylor Series Approximation of {func_str} at x={center}')

        # 预计算各阶泰勒展开
        taylor_funcs = []
        for n in range(1, max_degree + 1):
            taylor_expr = sp.series(func_sym, x_sym, center, n+1).removeO()
            taylor_func = sp.lambdify(x_sym, taylor_expr, 'numpy')
            taylor_funcs.append(taylor_func)

        def animate(frame):
            y_taylor = taylor_funcs[frame](x)
            line_taylor.set_data(x, y_taylor)
            ax.set_title(f'Taylor Series (n={frame+1})')
            return line_taylor,

        from matplotlib.animation import FuncAnimation
        anim = FuncAnimation(fig, animate, frames=max_degree, interval=500, blit=True)

        return fig, anim

# 使用示例
if __name__ == '__main__':
    viz = FunctionVisualizer()
    viz.interactive_plot('sin(x)')
    plt.show()

项目2:数值积分器

# project2_numerical_integrator.py

import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
import sympy as sp

class NumericalIntegrator:
    """
    通用数值积分器

    功能:
    - 多种积分方法实现
    - 误差分析和自适应步长
    - 可视化积分过程
    """

    METHODS = {
        'rectangle': '矩形法',
        'trapezoidal': '梯形法',
        'simpson': 'Simpson法',
        'romberg': 'Romberg加速',
        'gaussian': '高斯求积'
    }

    def integrate(self, f, a, b, method='simpson', n=1000, **kwargs):
        """
        通用积分接口

        Args:
            f: 被积函数
            a, b: 积分区间
            method: 积分方法
            n: 子区间数
        """
        if method == 'rectangle':
            return self._rectangle(f, a, b, n)
        elif method == 'trapezoidal':
            return self._trapezoidal(f, a, b, n)
        elif method == 'simpson':
            return self._simpson(f, a, b, n)
        elif method == 'romberg':
            return self._romberg(f, a, b, **kwargs)
        elif method == 'gaussian':
            return self._gaussian(f, a, b, **kwargs)
        else:
            raise ValueError(f"Unknown method: {method}")

    def _rectangle(self, f, a, b, n):
        """左矩形法"""
        h = (b - a) / n
        x = np.linspace(a, b - h, n)
        return h * np.sum(f(x))

    def _trapezoidal(self, f, a, b, n):
        """梯形法"""
        h = (b - a) / n
        x = np.linspace(a, b, n + 1)
        y = f(x)
        return h * (0.5 * y[0] + np.sum(y[1:-1]) + 0.5 * y[-1])

    def _simpson(self, f, a, b, n):
        """Simpson 1/3 法则"""
        if n % 2 == 1:
            n += 1
        h = (b - a) / n
        x = np.linspace(a, b, n + 1)
        y = f(x)
        return h/3 * (y[0] + 4*np.sum(y[1:-1:2]) + 2*np.sum(y[2:-2:2]) + y[-1])

    def _romberg(self, f, a, b, max_iter=10, tol=1e-10):
        """
        Romberg 积分:Richardson 外推加速

        原理:通过不同步长的结果外推,消除低阶误差项
        """
        R = np.zeros((max_iter, max_iter))

        # 初始梯形法
        n = 1
        h = b - a
        R[0, 0] = h * (f(a) + f(b)) / 2

        for i in range(1, max_iter):
            # 细分步长
            n *= 2
            h /= 2

            # 计算新的梯形估计
            x_new = np.linspace(a + h, b - h, n // 2)
            R[i, 0] = R[i-1, 0] / 2 + h * np.sum(f(x_new))

            # Richardson 外推
            for j in range(1, i + 1):
                R[i, j] = (4**j * R[i, j-1] - R[i-1, j-1]) / (4**j - 1)

            # 检查收敛
            if abs(R[i, i] - R[i-1, i-1]) < tol:
                return R[i, i]

        return R[max_iter-1, max_iter-1]

    def _gaussian(self, f, a, b, n=5):
        """高斯-勒让德求积"""
        # 使用 scipy
        result, _ = integrate.quad(f, a, b, limit=n)
        return result

    def adaptive_simpson(self, f, a, b, tol=1e-6, max_depth=20):
        """
        自适应 Simpson 积分

        只在误差大的区域细分,提高效率
        """
        def adaptive_recursion(f, a, b, eps, whole, fa, fb, fm, depth):
            m = (a + b) / 2
            lm = (a + m) / 2
            rm = (m + b) / 2
            flm = f(lm)
            frm = f(rm)

            left = (fa + 4*flm + fm) * (m - a) / 6
            right = (fm + 4*frm + fb) * (b - m) / 6
            delta = left + right - whole

            if depth >= max_depth or abs(delta) <= 15 * eps:
                return left + right + delta / 15

            return (adaptive_recursion(f, a, m, eps/2, left, fa, fm, flm, depth+1) +
                   adaptive_recursion(f, m, b, eps/2, right, fm, fb, frm, depth+1))

        fa, fb = f(a), f(b)
        m = (a + b) / 2
        fm = f(m)
        whole = (fa + 4*fm + fb) * (b - a) / 6

        return adaptive_recursion(f, a, b, tol, whole, fa, fb, fm, 0)

    def visualize_integration(self, f, a, b, n=10, method='trapezoidal'):
        """
        可视化积分过程
        """
        fig, ax = plt.subplots(figsize=(12, 6))

        # 绘制函数
        x_fine = np.linspace(a, b, 1000)
        ax.plot(x_fine, f(x_fine), 'b-', linewidth=2, label='f(x)')

        # 绘制积分近似
        x_coarse = np.linspace(a, b, n + 1)
        y_coarse = f(x_coarse)

        if method == 'rectangle':
            for i in range(n):
                rect = plt.Rectangle((x_coarse[i], 0), 
                                     x_coarse[i+1] - x_coarse[i], 
                                     y_coarse[i],
                                     fill=True, alpha=0.3, color='red',
                                     edgecolor='red')
                ax.add_patch(rect)
        elif method == 'trapezoidal':
            for i in range(n):
                x_trap = [x_coarse[i], x_coarse[i], x_coarse[i+1], x_coarse[i+1]]
                y_trap = [0, y_coarse[i], y_coarse[i+1], 0]
                ax.fill(x_trap, y_trap, alpha=0.3, color='red', edgecolor='red')
        elif method == 'simpson':
            # Simpson 使用抛物线近似
            for i in range(0, n, 2):
                if i + 2 <= n:
                    x_seg = np.linspace(x_coarse[i], x_coarse[i+2], 50)
                    # 抛物线插值
                    coeffs = np.polyfit([x_coarse[i], x_coarse[i+1], x_coarse[i+2]],
                                       [y_coarse[i], y_coarse[i+1], y_coarse[i+2]], 2)
                    y_parabola = np.polyval(coeffs, x_seg)
                    ax.fill_between(x_seg, 0, y_parabola, alpha=0.3, color='red')

        ax.axhline(y=0, color='k', linewidth=0.5)
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_title(f'{self.METHODS[method]} 可视化 (n={n})')
        ax.legend()
        ax.grid(True, alpha=0.3)

        return fig

    def error_analysis(self, f, a, b, exact_value):
        """
        不同方法的误差分析
        """
        n_values = np.logspace(1, 4, 20, dtype=int)

        results = {}
        for method in ['rectangle', 'trapezoidal', 'simpson']:
            errors = []
            for n in n_values:
                approx = self.integrate(f, a, b, method, n)
                error = abs(approx - exact_value)
                errors.append(error)
            results[method] = errors

        # 绘制误差图
        fig, ax = plt.subplots(figsize=(10, 6))
        for method, errors in results.items():
            ax.loglog(n_values, errors, 'o-', label=self.METHODS[method], markersize=4)

        # 参考线
        ax.loglog(n_values, 1/n_values, 'k--', alpha=0.5, label='O(1/n)')
        ax.loglog(n_values, 1/n_values**2, 'k:', alpha=0.5, label='O(1/n²)')
        ax.loglog(n_values, 1/n_values**4, 'k-.', alpha=0.5, label='O(1/n⁴)')

        ax.set_xlabel('Number of subintervals')
        ax.set_ylabel('Absolute Error')
        ax.set_title('Integration Methods Error Comparison')
        ax.legend()
        ax.grid(True, alpha=0.3)

        return fig

# 使用示例
if __name__ == '__main__':
    integrator = NumericalIntegrator()

    # 测试函数:高斯函数积分
    def gaussian(x):
        return np.exp(-x**2)

    exact = np.sqrt(np.pi)  # ∫[-∞,∞] e^(-x²) dx = √π

    # 比较不同方法
    for method in ['rectangle', 'trapezoidal', 'simpson', 'romberg']:
        result = integrator.integrate(gaussian, -5, 5, method, n=1000)
        print(f"{method}: {result:.10f}, error: {abs(result - exact):.2e}")

项目3:微分方程求解器

# project3_differential_equations.py

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint, solve_ivp

class ODESolver:
    """
    常微分方程数值求解器

    功能:
    - 实现经典数值方法(欧拉、龙格-库塔)
    - 绘制相平面图
    - 比较不同方法的精度和稳定性
    """

    def __init__(self):
        self.methods = {
            'euler': self.euler_method,
            'improved_euler': self.improved_euler,
            'rk4': self.rk4_method,
            'rk45': self.rk45_scipy
        }

    def euler_method(self, f, y0, t):
        """
        显式欧拉方法(一阶)

        y_{n+1} = y_n + h * f(t_n, y_n)

        简单但不稳定,仅用于教学
        """
        y = np.zeros((len(t), len(y0) if hasattr(y0, '__len__') else 1))
        y[0] = y0

        for i in range(len(t) - 1):
            h = t[i+1] - t[i]
            y[i+1] = y[i] + h * np.array(f(t[i], y[i]))

        return y.squeeze()

    def improved_euler(self, f, y0, t):
        """
        改进欧拉方法(Heun方法,二阶)

        k1 = f(t_n, y_n)
        k2 = f(t_n + h, y_n + h*k1)
        y_{n+1} = y_n + h/2 * (k1 + k2)
        """
        y = np.zeros((len(t), len(y0) if hasattr(y0, '__len__') else 1))
        y[0] = y0

        for i in range(len(t) - 1):
            h = t[i+1] - t[i]
            k1 = np.array(f(t[i], y[i]))
            k2 = np.array(f(t[i+1], y[i] + h*k1))
            y[i+1] = y[i] + h/2 * (k1 + k2)

        return y.squeeze()

    def rk4_method(self, f, y0, t):
        """
        经典龙格-库塔方法(四阶,RK4)

        k1 = f(t_n, y_n)
        k2 = f(t_n + h/2, y_n + h/2*k1)
        k3 = f(t_n + h/2, y_n + h/2*k2)
        k4 = f(t_n + h, y_n + h*k3)
        y_{n+1} = y_n + h/6 * (k1 + 2k2 + 2k3 + k4)

        精度高,是实际应用中最常用的方法
        """
        y = np.zeros((len(t), len(y0) if hasattr(y0, '__len__') else 1))
        y[0] = y0

        for i in range(len(t) - 1):
            h = t[i+1] - t[i]
            k1 = np.array(f(t[i], y[i]))
            k2 = np.array(f(t[i] + h/2, y[i] + h/2*k1))
            k3 = np.array(f(t[i] + h/2, y[i] + h/2*k2))
            k4 = np.array(f(t[i+1], y[i] + h*k3))
            y[i+1] = y[i] + h/6 * (k1 + 2*k2 + 2*k3 + k4)

        return y.squeeze()

    def rk45_scipy(self, f, y0, t_span, **kwargs):
        """
        使用 scipy 的自适应 RK45 方法
        """
        sol = solve_ivp(f, t_span, [y0] if np.isscalar(y0) else y0, 
                       method='RK45', dense_output=True, **kwargs)
        t_dense = np.linspace(t_span[0], t_span[1], 1000)
        y_dense = sol.sol(t_dense)
        return t_dense, y_dense.T

    def solve(self, f, y0, t, method='rk4'):
        """
        通用求解接口
        """
        if method in self.methods:
            return self.methods[method](f, y0, t)
        else:
            raise ValueError(f"Unknown method: {method}")

    def harmonic_oscillator(self, omega=1.0, method='rk4'):
        """
        简谐振子:y'' + ω²y = 0

        转化为方程组:
        y1' = y2
        y2' = -ω²*y1

        解析解:y(t) = A*cos(ωt) + B*sin(ωt)
        """
        def f(t, y):
            return [y[1], -omega**2 * y[0]]

        y0 = [1.0, 0.0]  # 初始位移1,初始速度0
        t = np.linspace(0, 4*np.pi/omega, 1000)

        solution = self.solve(f, y0, t, method)

        # 解析解
        exact = np.cos(omega * t)

        fig, axes = plt.subplots(2, 1, figsize=(12, 8))

        # 时域图
        ax1 = axes[0]
        ax1.plot(t, solution[:, 0], 'b-', label='Numerical')
        ax1.plot(t, exact, 'r--', alpha=0.7, label='Exact: cos(t)')
        ax1.set_xlabel('t')
        ax1.set_ylabel('y')
        ax1.set_title('Harmonic Oscillator Solution')
        ax1.legend()
        ax1.grid(True, alpha=0.3)

        # 相平面图(速度-位移)
        ax2 = axes[1]
        ax2.plot(solution[:, 0], solution[:, 1], 'b-', linewidth=1)
        ax2.set_xlabel('Position (y)')
        ax2.set_ylabel('Velocity (y\')')
        ax2.set_title('Phase Portrait')
        ax2.grid(True, alpha=0.3)
        ax2.set_aspect('equal')

        plt.tight_layout()
        return fig

    def damped_pendulum(self, gamma=0.1, omega0=1.0):
        """
        阻尼单摆:θ'' + γθ' + ω₀²sin(θ) = 0
        """
        def f(t, y):
            theta, theta_dot = y
            return [theta_dot, -gamma*theta_dot - omega0**2*np.sin(theta)]

        # 不同初始条件的相平面图
        fig, ax = plt.subplots(figsize=(10, 8))

        initial_conditions = np.linspace(-2*np.pi, 2*np.pi, 20)
        colors = plt.cm.viridis(np.linspace(0, 1, len(initial_conditions)))

        t = np.linspace(0, 20, 2000)

        for theta0, color in zip(initial_conditions, colors):
            y0 = [theta0, 0.0]
            sol = self.solve(f, y0, t, 'rk4')
            ax.plot(sol[:, 0], sol[:, 1], color=color, alpha=0.7, linewidth=0.8)

        ax.set_xlabel('θ (Angle)')
        ax.set_ylabel('θ\' (Angular Velocity)')
        ax.set_title('Damped Pendulum Phase Portrait')
        ax.grid(True, alpha=0.3)
        ax.axhline(y=0, color='k', linewidth=0.5)
        ax.axvline(x=0, color='k', linewidth=0.5)

        return fig

    def lorenz_attractor(self, sigma=10, rho=28, beta=8/3):
        """
        Lorenz吸引子(混沌系统示例)

        dx/dt = σ(y - x)
        dy/dt = x(ρ - z) - y
        dz/dt = xy - βz
        """
        def f(t, state):
            x, y, z = state
            return [
                sigma * (y - x),
                x * (rho - z) - y,
                x * y - beta * z
            ]

        # 微小差异的初始条件
        t_span = (0, 50)
        dt = 0.01
        t = np.arange(t_span[0], t_span[1], dt)

        fig = plt.figure(figsize=(14, 5))

        # 3D轨迹
        ax1 = fig.add_subplot(131, projection='3d')
        colors = ['red', 'blue']

        for i, delta in enumerate([0, 1e-5]):
            y0 = [1.0 + delta, 1.0, 1.0]
            sol = self.solve(f, y0, t, 'rk4')
            ax1.plot(sol[:, 0], sol[:, 1], sol[:, 2], 
                    color=colors[i], alpha=0.7, linewidth=0.5,
                    label=f'δ={delta}')

        ax1.set_xlabel('X')
        ax1.set_ylabel('Y')
        ax1.set_zlabel('Z')
        ax1.set_title('Lorenz Attractor (Chaos)')
        ax1.legend()

        # X随时间变化
        ax2 = fig.add_subplot(132)
        sol1 = self.solve(f, [1.0, 1.0, 1.0], t, 'rk4')
        sol2 = self.solve(f, [1.00001, 1.0, 1.0], t, 'rk4')

        ax2.plot(t, sol1[:, 0], 'r-', alpha=0.7, label='x(0)=1.0')
        ax2.plot(t, sol2[:, 0], 'b-', alpha=0.7, label='x(0)=1.00001')
        ax2.set_xlabel('t')
        ax2.set_ylabel('x')
        ax2.set_title('Sensitive Dependence on Initial Conditions')
        ax2.legend()
        ax2.grid(True, alpha=0.3)

        # 误差增长
        ax3 = fig.add_subplot(133)
        error = np.abs(sol1[:, 0] - sol2[:, 0])
        ax3.semilogy(t, error, 'g-', linewidth=1)
        ax3.set_xlabel('t')
        ax3.set_ylabel('|x₁ - x₂|')
        ax3.set_title('Error Growth (Exponential)')
        ax3.grid(True, alpha=0.3)

        plt.tight_layout()
        return fig

    def compare_methods_accuracy(self):
        """
        比较不同数值方法的精度
        """
        # 测试方程:y' = -y, y(0) = 1
        # 解析解:y(t) = e^(-t)

        def f(t, y):
            return -y

        y0 = 1.0
        t_end = 5
        exact = np.exp(-t_end)

        h_values = np.logspace(-3, -1, 20)

        errors = {method: [] for method in ['euler', 'improved_euler', 'rk4']}

        for h in h_values:
            t = np.arange(0, t_end + h, h)

            for method in errors.keys():
                sol = self.solve(f, y0, t, method)
                error = abs(sol[-1] - exact)
                errors[method].append(error)

        fig, ax = plt.subplots(figsize=(10, 6))
        for method, errs in errors.items():
            ax.loglog(h_values, errs, 'o-', label=method, markersize=4)

        ax.loglog(h_values, h_values, 'k--', alpha=0.5, label='O(h)')
        ax.loglog(h_values, h_values**2, 'k:', alpha=0.5, label='O(h²)')
        ax.loglog(h_values, h_values**4, 'k-.', alpha=0.5, label='O(h⁴)')

        ax.set_xlabel('Step size h')
        ax.set_ylabel('Error at t=5')
        ax.set_title('ODE Solver Accuracy Comparison')
        ax.legend()
        ax.grid(True, alpha=0.3)

        return fig

# 使用示例
if __name__ == '__main__':
    solver = ODESolver()
    solver.harmonic_oscillator()
    plt.show()

项目4:优化问题求解器

# project4_optimization.py

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize, minimize_scalar
from mpl_toolkits.mplot3d import Axes3D

class OptimizationSolver:
    """
    数值优化求解器

    实现:
    - 梯度下降法
    - 牛顿法
    - 约束优化
    - 可视化收敛过程
    """

    def gradient_descent(self, f, grad_f, x0, lr=0.1, tol=1e-6, max_iter=1000):
        """
        梯度下降法

        x_{k+1} = x_k - lr * ∇f(x_k)

        最直观的优化方法,沿着最陡下降方向移动
        """
        x = np.array(x0, dtype=float)
        trajectory = [x.copy()]

        for i in range(max_iter):
            gradient = grad_f(x)
            x_new = x - lr * gradient

            if np.linalg.norm(x_new - x) < tol:
                break

            x = x_new
            trajectory.append(x.copy())

        return x, np.array(trajectory)

    def newton_method(self, f, grad_f, hess_f, x0, tol=1e-6, max_iter=100):
        """
        牛顿法

        x_{k+1} = x_k - H⁻¹(x_k) * ∇f(x_k)

        利用二阶信息(Hessian),收敛更快但需要计算逆矩阵
        """
        x = np.array(x0, dtype=float)
        trajectory = [x.copy()]

        for i in range(max_iter):
            gradient = grad_f(x)
            hessian = hess_f(x)

            # 求解线性方程组 H * d = -∇f
            direction = np.linalg.solve(hessian, -gradient)
            x_new = x + direction

            if np.linalg.norm(x_new - x) < tol:
                break

            x = x_new
            trajectory.append(x.copy())

        return x, np.array(trajectory)

    def newton_line_search(self, f, grad_f, hess_f, x0, tol=1e-6, max_iter=100):
        """
        带线搜索的牛顿法
        """
        x = np.array(x0, dtype=float)
        trajectory = [x.copy()]

        for i in range(max_iter):
            gradient = grad_f(x)
            hessian = hess_f(x)

            direction = np.linalg.solve(hessian, -gradient)

            # Armijo 线搜索
            alpha = 1.0
            c = 0.5
            while f(x + alpha * direction) > f(x) + c * alpha * np.dot(gradient, direction):
                alpha *= 0.5

            x_new = x + alpha * direction

            if np.linalg.norm(x_new - x) < tol:
                break

            x = x_new
            trajectory.append(x.copy())

        return x, np.array(trajectory)

    def visualize_2d_optimization(self, f, grad_f, x_range, y_range, method='gd'):
        """
        可视化2D优化过程
        """
        fig = plt.figure(figsize=(14, 6))

        # 创建网格
        x = np.linspace(x_range[0], x_range[1], 100)
        y = np.linspace(y_range[0], y_range[1], 100)
        X, Y = np.meshgrid(x, y)
        Z = np.array([[f(np.array([xi, yi])) for xi in x] for yi in y])

        # 3D表面图
        ax1 = fig.add_subplot(121, projection='3d')
        surf = ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.7)
        ax1.set_xlabel('x')
        ax1.set_ylabel('y')
        ax1.set_zlabel('f(x,y)')
        ax1.set_title('Objective Function')

        # 等高线图 + 优化轨迹
        ax2 = fig.add_subplot(122)
        contour = ax2.contour(X, Y, Z, levels=20, cmap='viridis')
        ax2.clabel(contour, inline=True, fontsize=8)

        # 运行优化
        x0 = np.array([-1.5, 1.5])

        if method == 'gd':
            x_opt, traj = self.gradient_descent(f, grad_f, x0, lr=0.1)
            title = 'Gradient Descent'
        elif method == 'newton':
            # 需要Hessian
            def hess_f(x):
                return np.array([
                    [2 + 4*x[0]**2 - 2*(1-x[0]**2), -2*x[0]*x[1]],
                    [-2*x[0]*x[1], 2]
                ])
            x_opt, traj = self.newton_method(f, grad_f, hess_f, x0)
            title = 'Newton Method'

        # 绘制轨迹
        ax2.plot(traj[:, 0], traj[:, 1], 'r.-', markersize=8, linewidth=1.5)
        ax2.plot(traj[0, 0], traj[0, 1], 'go', markersize=12, label='Start')
        ax2.plot(traj[-1, 0], traj[-1, 1], 'r*', markersize=15, label='End')

        ax2.set_xlabel('x')
        ax2.set_ylabel('y')
        ax2.set_title(f'{title} Trajectory ({len(traj)} iterations)')
        ax2.legend()
        ax2.set_aspect('equal')

        plt.tight_layout()
        return fig

    def rosenbrock_function(self):
        """
        Rosenbrock函数(优化测试经典函数)
        f(x,y) = (1-x)² + 100(y-x²)²

        全局最小值在 (1, 1),位于狭长山谷中
        """
        def f(x):
            return (1 - x[0])**2 + 100 * (x[1] - x[0]**2)**2

        def grad_f(x):
            return np.array([
                -2*(1-x[0]) - 400*x[0]*(x[1]-x[0]**2),
                200*(x[1]-x[0]**2)
            ])

        def hess_f(x):
            return np.array([
                [2 + 800*x[0]**2 - 400*(x[1]-x[0]**2), -400*x[0]],
                [-400*x[0], 200]
            ])

        return f, grad_f, hess_f

    def constrained_optimization_demo(self):
        """
        约束优化示例

        最小化 f(x,y) = x² + y²
        约束: x + y >= 1
        """
        def objective(x):
            return x[0]**2 + x[1]**2

        # 不等式约束: g(x) >= 0
        def constraint(x):
            return x[0] + x[1] - 1

        # 使用 scipy 求解
        x0 = [0.5, 0.5]
        cons = {'type': 'ineq', 'fun': constraint}
        result = minimize(objective, x0, method='SLSQP', constraints=cons)

        # 可视化
        fig, ax = plt.subplots(figsize=(10, 8))

        # 可行域
        x = np.linspace(-1, 2, 100)
        y = np.linspace(-1, 2, 100)
        X, Y = np.meshgrid(x, y)
        Z = X**2 + Y**2

        # 等高线
        contour = ax.contour(X, Y, Z, levels=15, cmap='viridis')
        ax.clabel(contour, inline=True, fontsize=8)

        # 约束边界
        x_boundary = np.linspace(-1, 2, 100)
        y_boundary = 1 - x_boundary
        ax.plot(x_boundary, y_boundary, 'r-', linewidth=2, label='Constraint: x+y=1')
        ax.fill_between(x_boundary, -1, y_boundary, where=(y_boundary > -1), 
                        alpha=0.2, color='red', label='Infeasible region')

        # 最优解
        ax.plot(result.x[0], result.x[1], 'r*', markersize=20, label=f'Optimum {result.x.round(4)}')
        ax.plot(x0[0], x0[1], 'go', markersize=12, label='Start')

        ax.set_xlim([-1, 2])
        ax.set_ylim([-1, 2])
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_title('Constrained Optimization')
        ax.legend()
        ax.grid(True, alpha=0.3)
        ax.set_aspect('equal')

        return fig

    def compare_convergence(self):
        """
        比较梯度下降和牛顿法的收敛速度
        """
        f, grad_f, hess_f = self.rosenbrock_function()
        x0 = np.array([-1.2, 1.0])

        # 梯度下降
        _, traj_gd = self.gradient_descent(f, grad_f, x0, lr=0.001, max_iter=5000)
        errors_gd = [f(x) for x in traj_gd]

        # 牛顿法
        _, traj_newton = self.newton_method(f, grad_f, hess_f, x0)
        errors_newton = [f(x) for x in traj_newton]

        fig, ax = plt.subplots(figsize=(10, 6))
        ax.semilogy(errors_gd, 'b-', label=f'Gradient Descent ({len(traj_gd)} iters)')
        ax.semilogy(errors_newton, 'r-', label=f'Newton Method ({len(traj_newton)} iters)')
        ax.set_xlabel('Iteration')
        ax.set_ylabel('Objective Value (log scale)')
        ax.set_title('Convergence Speed Comparison on Rosenbrock Function')
        ax.legend()
        ax.grid(True, alpha=0.3)

        return fig

# 使用示例
if __name__ == '__main__':
    opt = OptimizationSolver()

    # Rosenbrock函数优化
    f, grad_f, hess_f = opt.rosenbrock_function()
    opt.visualize_2d_optimization(f, grad_f, [-2, 2], [-1, 3], method='gd')
    plt.show()

项目5:傅里叶分析工具

# project5_fourier_analysis.py

import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, ifft, fftfreq, fftshift
from scipy.signal import butter, filtfilt

class FourierAnalyzer:
    """
    傅里叶分析工具箱

    功能:
    - DFT/FFT 实现
    - 频谱分析
    - 滤波器设计
    - 信号重构
    """

    def __init__(self, sample_rate=1000):
        self.fs = sample_rate  # 采样率 (Hz)
        self.dt = 1 / sample_rate

    def dft_slow(self, x):
        """
        慢速 DFT(用于教学理解)

        X[k] = Σ x[n] * e^(-j*2π*k*n/N)

        复杂度 O(N²),实际使用 FFT
        """
        N = len(x)
        X = np.zeros(N, dtype=complex)

        for k in range(N):
            for n in range(N):
                X[k] += x[n] * np.exp(-2j * np.pi * k * n / N)

        return X

    def analyze_signal(self, signal, t=None):
        """
        完整信号分析

        Returns:
            时域、频域、功率谱密度
        """
        N = len(signal)

        if t is None:
            t = np.arange(N) * self.dt

        # FFT
        X = fft(signal)

        # 频率轴
        freqs = fftfreq(N, self.dt)

        # 功率谱密度
        psd = np.abs(X)**2 / (self.fs * N)
        psd[1:-1] *= 2  # 双边谱转单边谱(除直流和奈奎斯特频率)

        # 相位谱
        phase = np.angle(X)

        return {
            'time': t,
            'signal': signal,
            'frequencies': freqs[:N//2],
            'amplitude': np.abs(X)[:N//2] * 2 / N,
            'power': psd[:N//2],
            'phase': phase[:N//2]
        }

    def visualize_analysis(self, signal, t=None):
        """
        可视化信号分析结果
        """
        result = self.analyze_signal(signal, t)

        fig, axes = plt.subplots(2, 2, figsize=(14, 10))

        # 时域波形
        ax1 = axes[0, 0]
        ax1.plot(result['time'], result['signal'], 'b-', linewidth=1)
        ax1.set_xlabel('Time (s)')
        ax1.set_ylabel('Amplitude')
        ax1.set_title('Time Domain Signal')
        ax1.grid(True, alpha=0.3)

        # 幅频谱
        ax2 = axes[0, 1]
        ax2.stem(result['frequencies'], result['amplitude'], basefmt=' ')
        ax2.set_xlabel('Frequency (Hz)')
        ax2.set_ylabel('Amplitude')
        ax2.set_title('Amplitude Spectrum')
        ax2.grid(True, alpha=0.3)

        # 功率谱密度
        ax3 = axes[1, 0]
        ax3.semilogy(result['frequencies'], result['power'], 'g-', linewidth=1)
        ax3.set_xlabel('Frequency (Hz)')
        ax3.set_ylabel('Power/Frequency (dB/Hz)')
        ax3.set_title('Power Spectral Density')
        ax3.grid(True, alpha=0.3)

        # 相位谱
        ax4 = axes[1, 1]
        ax4.plot(result['frequencies'], np.unwrap(result['phase']), 'r-', linewidth=1)
        ax4.set_xlabel('Frequency (Hz)')
        ax4.set_ylabel('Phase (rad)')
        ax4.set_title('Phase Spectrum')
        ax4.grid(True, alpha=0.3)

        plt.tight_layout()
        return fig, result

    def filter_design(self, filter_type='lowpass', cutoff=100, order=4):
        """
        Butterworth 滤波器设计

        Args:
            filter_type: 'lowpass', 'highpass', 'bandpass', 'bandstop'
            cutoff: 截止频率 (Hz),带通/带阻时为 [low, high]
            order: 滤波器阶数
        """
        nyquist = self.fs / 2

        if isinstance(cutoff, (list, tuple)):
            normalized = [c / nyquist for c in cutoff]
        else:
            normalized = cutoff / nyquist

        b, a = butter(order, normalized, btype=filter_type)
        return b, a

    def apply_filter(self, signal, b, a):
        """
        应用滤波器(零相位滤波,避免相位失真)
        """
        return filtfilt(b, a, signal)

    def denoising_demo(self):
        """
        信号去噪演示
        """
        # 生成含噪声信号
        t = np.linspace(0, 1, self.fs, endpoint=False)

        # 原始信号:多个正弦波
        signal_clean = (np.sin(2 * np.pi * 50 * t) +   # 50 Hz
                       0.5 * np.sin(2 * np.pi * 120 * t) +  # 120 Hz
                       0.3 * np.sin(2 * np.pi * 200 * t))   # 200 Hz

        # 添加噪声
        noise = 0.5 * np.random.randn(len(t))
        signal_noisy = signal_clean + noise

        # 分析
        fig, axes = plt.subplots(3, 2, figsize=(14, 12))

        # 时域
        ax1 = axes[0, 0]
        ax1.plot(t[:200], signal_clean[:200], 'b-', label='Clean')
        ax1.plot(t[:200], signal_noisy[:200], 'r-', alpha=0.5, label='Noisy')
        ax1.legend()
        ax1.set_title('Time Domain (First 200 samples)')
        ax1.grid(True, alpha=0.3)

        # 频域
        freqs = fftfreq(len(t), self.dt)[:len(t)//2]
        psd_clean = np.abs(fft(signal_clean))[:len(t)//2]**2
        psd_noisy = np.abs(fft(signal_noisy))[:len(t)//2]**2

        ax2 = axes[0, 1]
        ax2.semilogy(freqs, psd_clean, 'b-', label='Clean')
        ax2.semilogy(freqs, psd_noisy, 'r-', alpha=0.5, label='Noisy')
        ax2.set_xlim([0, 300])
        ax2.legend()
        ax2.set_title('Power Spectrum')
        ax2.grid(True, alpha=0.3)

        # 低通滤波
        b, a = self.filter_design('lowpass', cutoff=150, order=6)
        signal_filtered = self.apply_filter(signal_noisy, b, a)

        ax3 = axes[1, 0]
        ax3.plot(t[:200], signal_clean[:200], 'b-', label='Clean', linewidth=2)
        ax3.plot(t[:200], signal_filtered[:200], 'g-', label='Filtered', alpha=0.8)
        ax3.legend()
        ax3.set_title('Lowpass Filtered')
        ax3.grid(True, alpha=0.3)

        psd_filtered = np.abs(fft(signal_filtered))[:len(t)//2]**2
        ax4 = axes[1, 1]
        ax4.semilogy(freqs, psd_clean, 'b-', label='Clean')
        ax4.semilogy(freqs, psd_filtered, 'g-', label='Filtered')
        ax4.set_xlim([0, 300])
        ax4.legend()
        ax4.set_title('Filtered Spectrum')
        ax4.grid(True, alpha=0.3)

        # 带通滤波(只保留 100-150 Hz)
        b_bp, a_bp = self.filter_design('bandpass', cutoff=[80, 160], order=6)
        signal_bandpass = self.apply_filter(signal_noisy, b_bp, a_bp)

        ax5 = axes[2, 0]
        ax5.plot(t[:200], signal_bandpass[:200], 'purple', label='Bandpass')
        ax5.legend()
        ax5.set_title('Bandpass Filter (80-160 Hz)')
        ax5.grid(True, alpha=0.3)

        psd_bp = np.abs(fft(signal_bandpass))[:len(t)//2]**2
        ax6 = axes[2, 1]
        ax6.semilogy(freqs, psd_bp, 'purple', label='Bandpass')
        ax6.set_xlim([0, 300])
        ax6.legend()
        ax6.set_title('Bandpass Spectrum')
        ax6.grid(True, alpha=0.3)

        plt.tight_layout()
        return fig

    def reconstruction_demo(self):
        """
        信号重构演示:从频域回到时域
        """
        # 方波的傅里叶级数重构
        t = np.linspace(-np.pi, np.pi, 1000)

        fig, axes = plt.subplots(2, 3, figsize=(15, 8))
        axes = axes.flatten()

        n_harmonics = [1, 3, 5, 10, 20, 50]

        for idx, n in enumerate(n_harmonics):
            ax = axes[idx]

            # 方波傅里叶级数:Σ (4/π) * sin((2k+1)x)/(2k+1)
            reconstruction = np.zeros_like(t)
            for k in range(n):
                harmonic = 2*k + 1
                reconstruction += (4/np.pi) * np.sin(harmonic * t) / harmonic

            ax.plot(t, np.sign(np.sin(t)), 'k--', linewidth=1, label='Square wave', alpha=0.5)
            ax.plot(t, reconstruction, 'b-', linewidth=1.5, label=f'{n} harmonics')
            ax.set_ylim([-1.5, 1.5])
            ax.set_title(f'Fourier Series Reconstruction (n={n})')
            ax.legend()
            ax.grid(True, alpha=0.3)

        plt.tight_layout()
        return fig

    def spectrogram(self, signal, window_size=256, overlap=128):
        """
        短时傅里叶变换(时频分析)
        """
        hop = window_size - overlap
        n_windows = (len(signal) - window_size) // hop + 1

        freqs = fftfreq(window_size, self.dt)[:window_size//2]
        times = np.arange(n_windows) * hop * self.dt

        spectrogram = np.zeros((len(freqs), n_windows))

        window = np.hanning(window_size)

        for i in range(n_windows):
            start = i * hop
            segment = signal[start:start+window_size] * window
            spectrum = np.abs(fft(segment))[:window_size//2]
            spectrogram[:, i] = spectrum

        return times, freqs, spectrogram

    def visualize_spectrogram(self, signal, t=None):
        """
        可视化时频谱
        """
        times, freqs, spec = self.spectrogram(signal)

        fig, ax = plt.subplots(figsize=(12, 6))

        im = ax.pcolormesh(times, freqs, 20*np.log10(spec + 1e-10), 
                          shading='gouraud', cmap='viridis')
        ax.set_ylabel('Frequency (Hz)')
        ax.set_xlabel('Time (s)')
        ax.set_title('Spectrogram (STFT)')
        ax.set_ylim([0, self.fs/2])
        plt.colorbar(im, ax=ax, label='Magnitude (dB)')

        return fig

# 使用示例
if __name__ == '__main__':
    analyzer = FourierAnalyzer(sample_rate=1000)

    # 生成测试信号
    t = np.linspace(0, 1, 1000)
    signal = np.sin(2 * np.pi * 50 * t) + 0.5 * np.sin(2 * np.pi * 120 * t)

    analyzer.visualize_analysis(signal, t)
    plt.show()

代码规范与最佳实践

4.1 向量化编程

import numpy as np

# ❌ 慢:Python循环
def slow_sum(arr):
    result = 0
    for x in arr:
        result += x
    return result

# ✅ 快:NumPy向量化
def fast_sum(arr):
    return np.sum(arr)

# ❌ 慢:逐元素操作
result = []
for i in range(len(x)):
    result.append(np.sin(x[i]) * np.cos(x[i]))

# ✅ 快:向量化操作
result = np.sin(x) * np.cos(x)

# ✅ 广播机制
# (100, 1) + (1, 50) → (100, 50)
A = np.random.randn(100, 1)
B = np.random.randn(1, 50)
C = A + B  # 自动广播

4.2 函数封装

class MathVisualizer:
    """
    数学可视化工具类

    提供常见数学概念的可视化方法
    """

    def __init__(self, figsize=(10, 6), style='seaborn-v0_8'):
        """
        初始化可视化器

        Args:
            figsize: 默认图像尺寸
            style: Matplotlib样式
        """
        self.figsize = figsize
        plt.style.use(style)

    def plot_function(self, f, x_range, **kwargs):
        """
        绘制函数图像

        Args:
            f: 可调用函数
            x_range: (xmin, xmax) 元组
            **kwargs: 传递给plt.plot的参数
        """
        x = np.linspace(x_range[0], x_range[1], 1000)
        y = f(x)

        fig, ax = plt.subplots(figsize=self.figsize)
        ax.plot(x, y, **kwargs)
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.grid(True, alpha=0.3)
        return fig, ax

4.3 文档字符串规范

def numerical_derivative(f, x, h=1e-5, method='central'):
    """
    计算函数的数值导数

    使用有限差分方法近似计算函数在某点的导数。
    中心差分法的误差为 O(h²),优于前向差分的 O(h)。

    Parameters
    ----------
    f : callable
        要求导的函数,接受标量输入返回标量输出
    x : float
        求导点
    h : float, optional
        差分步长,默认为 1e-5
        过小会导致浮点误差,过大会增加截断误差
    method : str, optional
        差分方法:'forward', 'backward', 'central'
        默认为 'central'(精度最高)

    Returns
    -------
    float
        导数的数值近似值

    Raises
    ------
    ValueError
        当 method 不是支持的值时

    Examples
    --------
    >>> f = lambda x: x**2
    >>> numerical_derivative(f, 2.0)
    4.000000000000001  # 接近理论值 4

    >>> numerical_derivative(np.sin, 0.0)
    1.0  # cos(0) = 1

    References
    ----------
    .. [1] 数值分析,李庆扬等,第5章
    """
    if method == 'forward':
        return (f(x + h) - f(x)) / h
    elif method == 'backward':
        return (f(x) - f(x - h)) / h
    elif method == 'central':
        return (f(x + h) - f(x - h)) / (2 * h)
    else:
        raise ValueError(f"Unknown method: {method}")

4.4 单元测试

import unittest
import numpy as np
from numpy.testing import assert_almost_equal, assert_allclose

class TestNumericalMethods(unittest.TestCase):
    """
    数值方法单元测试
    """

    def setUp(self):
        """测试前的准备工作"""
        self.tolerance = 1e-6

    def test_numerical_derivative_polynomial(self):
        """测试多项式函数的数值微分"""
        f = lambda x: 3*x**3 + 2*x**2 + x + 1
        df_exact = lambda x: 9*x**2 + 4*x + 1

        x_test = 2.0
        from numerical_methods import numerical_derivative
        df_numerical = numerical_derivative(f, x_test)

        assert_almost_equal(df_numerical, df_exact(x_test), decimal=5)

    def test_numerical_derivative_trig(self):
        """测试三角函数的数值微分"""
        f = np.sin
        df_exact = np.cos

        x_test = np.pi / 4
        from numerical_methods import numerical_derivative
        df_numerical = numerical_derivative(f, x_test)

        assert_almost_equal(df_numerical, df_exact(x_test), decimal=8)

    def test_numerical_integration(self):
        """测试数值积分"""
        from numerical_methods import NumericalIntegration

        integrator = NumericalIntegration()

        # ∫[0,1] x² dx = 1/3
        f = lambda x: x**2
        result = integrator.simpson(f, 0, 1, n=100)

        assert_almost_equal(result, 1/3, decimal=8)

if __name__ == '__main__':
    unittest.main()

附录:快速参考表

常用NumPy操作

操作 代码
创建等差数列 np.linspace(a, b, n)
创建等比数列 np.logspace(a, b, n)
矩阵乘法 A @ Bnp.dot(A, B)
元素乘法 A * B
矩阵求逆 np.linalg.inv(A)
特征值 np.linalg.eigvals(A)
条件筛选 A[A > 0]
广播 A[:, None] + B[None, :]

常用SciPy模块

from scipy import integrate   # 积分
from scipy import optimize    # 优化
from scipy import linalg      # 线性代数
from scipy import interpolate # 插值
from scipy import fft         # 傅里叶变换
from scipy import signal      # 信号处理
from scipy import stats       # 统计

常用SymPy操作

import sympy as sp

x, y, z = sp.symbols('x y z')
f = sp.sin(x)**2 + sp.cos(x)**2  # 符号表达式
sp.simplify(f)                    # 化简 → 1
sp.diff(f, x)                     # 求导
sp.integrate(f, x)                # 积分
sp.series(f, x, 0, 5)             # 泰勒展开
sp.solve(x**2 - 1, x)             # 解方程 → [-1, 1]
sp.lambdify(x, f, 'numpy')        # 转为Python函数

总结

核心原则

  1. 代码验证数学:用数值实验验证理论结果
  2. 可视化优先:一张图胜过千言万语
  3. 模块化设计:封装可复用的数学工具
  4. 测试驱动:确保数值方法的正确性

学习路径

  1. 第1周:熟悉Python科学计算环境
  2. 第2-3周:实现基本数值方法(微分、积分)
  3. 第4-5周:完成实战项目
  4. 第6周:优化代码,添加文档和测试

扩展阅读


"用代码验证数学,让抽象变具体。这是第一性原理的力量。" —— 费曼精神