用Python和SymPy手把手推导费马原理:从反射定律到斯涅尔公式
用Python和SymPy手把手推导费马原理从反射定律到斯涅尔公式光总是选择最短时间的路径传播——这个看似简单的陈述背后隐藏着自然界最优雅的优化法则。费马原理不仅解释了光线为何会聪明地选择反射角等于入射角还揭示了光在不同介质间弯曲的数学本质。今天我们将用Python的SymPy库重新发现这些光学定律让代码成为我们探索物理世界的显微镜。1. 环境准备与基础概念在开始推导前我们需要配置Python环境和理解关键概念。建议使用Jupyter Notebook进行交互式操作它能实时显示符号运算结果。安装必要的库pip install sympy numpy matplotlib费马原理的核心是光程极值光在传播时总会选择使光程折射率与路径长度的乘积取极值的路径。这种表述比最短时间更普适因为它适用于折射率变化的介质。让我们先定义几个关键变量from sympy import * init_printing() # 启用美观的数学符号显示 # 定义符号变量 x, a, b, l symbols(x a b l, realTrue, positiveTrue) theta1, theta2 symbols(theta1 theta2, realTrue) n1, n2 symbols(n1 n2, positiveTrue) # 折射率 c symbols(c, positiveTrue) # 真空光速2. 反射定律的符号推导考虑光从点P反射到点Q的路径建立坐标系P点坐标(0, a)Q点坐标(l, -b)反射点坐标(x, 0)光程D为距离之和d1 sqrt(x**2 a**2) # P到反射点距离 d2 sqrt((l - x)**2 b**2) # 反射点到Q距离 D d1 d2根据费马原理对x求导并令导数为零dD_dx diff(D, x) solution solve(dD_dx, x)这个方程看似复杂但SymPy能完美处理。我们可以用三角函数关系简化数学关系Python表示sinθ₁ x/√(x²a²)sin_theta1 x / sqrt(x2 a2)sinθ₂ (l-x)/√((l-x)²b²)sin_theta2 (l - x) / sqrt((l - x)2 b2)将导数表达式转换为三角函数形式dD_dx_trig x/d1 - (l - x)/d2 # 等价于sinθ₁ - sinθ₂最终得到反射定律sinθ₁ sinθ₂ ⇒ θ₁ θ₂提示使用plot函数可以可视化不同x值对应的光程直观观察极值点import matplotlib.pyplot as plt import numpy as np # 示例参数 a_val, b_val, l_val 1, 1, 2 x_vals np.linspace(0, l_val, 100) D_vals [D.subs({a:a_val, b:b_val, l:l_val, x:x_val}) for x_val in x_vals] plt.plot(x_vals, D_vals) plt.xlabel(Reflection point x); plt.ylabel(Optical path D) plt.show()3. 折射定律的动力学推导当光穿过不同介质时速度变化导致路径弯曲。设介质1和2的折射率分别为n₁和n₂光速为v₁c/n₁和v₂c/n₂。传播时间T为v1, v2 c/n1, c/n2 T d1/v1 d2/v2对x求导并令导数为零dT_dx diff(T, x)同样转换为三角函数形式dT_dx_trig sin(theta1)/v1 - sin(theta2)/v2代入速度与折射率关系得到斯涅尔定律snell_law Eq(n1*sin(theta1), n2*sin(theta2))关键步骤对比表数学操作SymPy实现定义光程/时间D d1 d2或T d1/v1 d2/v2符号求导diff(D, x)解方程solve(dD_dx, x)三角替换x/d1 - sin(theta1)4. 高级应用与验证4.1 多介质界面的推广对于多层介质总光程是各段光程之和。我们可以定义通用光程函数def optical_path(lengths, refractive_indices): return sum(n*l for n, l in zip(refractive_indices, lengths))4.2 数值验证选择具体参数验证折射定律的正确性# 示例参数 params {n1: 1.0, n2: 1.5, theta1: pi/6} # 空气到玻璃入射角30° # 计算理论折射角 theta2_val solve(snell_law.subs(params), theta2)[0].evalf() print(f理论折射角: {theta2_val*180/pi:.2f}°) # 数值验证极值点 x_val l_val/3 # 假设反射点 T_val T.subs({**params, a:1, b:1, l:2, x:x_val, c:1}).evalf() print(f传播时间: {T_val})4.3 误差分析与敏感性研究折射率变化对折射角的影响n2_vals np.linspace(1.0, 2.0, 10) theta2_vals [solve(snell_law.subs({n1:1, n2:n2, theta1:pi/6}), theta2)[0] for n2 in n2_vals] plt.plot(n2_vals, [float(t*180/pi) for t in theta2_vals]) plt.xlabel(n2); plt.ylabel(Refraction angle (°)) plt.title(n11.0, θ130°时折射角随n2变化) plt.show()5. 工程实践中的注意事项在实际光学系统设计中有几个关键点需要考虑符号计算的局限性复杂几何可能需要数值方法多解情况需要物理约束性能优化技巧# 提前编译表达式 dD_dx_compiled lambdify((x, a, b, l), dD_dx, numpy) # 快速批量计算 x_array np.linspace(0, 1, 1000) results dD_dx_compiled(x_array, 1, 1, 2)常见问题排查确保所有变量已正确定义检查物理单位一致性验证极值性质最小vs最大光程光学设计中最容易犯的错误是忽略折射率的温度依赖性。在实际项目中我曾遇到实验室完美的透镜系统在户外使用时焦点偏移的问题后来发现是温度变化导致折射率改变。