用Python可视化多元函数微分:3D图形理解梯度、切平面与方向导数

张开发
2026/4/19 9:34:10 15 分钟阅读

分享文章

用Python可视化多元函数微分:3D图形理解梯度、切平面与方向导数
用Python可视化多元函数微分3D图形理解梯度、切平面与方向导数多元函数微分学是高等数学中的核心内容但对于许多学习者来说那些抽象的定理和公式往往难以直观理解。想象一下当你面对可微性条件或方向导数这样的概念时是否曾希望有一个更直观的方式来掌握它们这正是Python科学可视化能够大显身手的地方。传统教材通常通过静态的二维图示来讲解这些概念但多元函数本质上是三维甚至更高维的。借助Matplotlib、Plotly等Python库我们可以创建交互式的3D可视化让抽象的数学概念变得触手可及。本文将带你从零开始通过代码实现以下目标绘制二元函数曲面并实现交互式旋转查看动态展示偏导数的几何意义沿坐标轴方向的切线可视化可微函数的切平面及其拟合效果直观理解梯度向量的指向与大小变化探索不同方向的方向导数变化规律1. 环境准备与基础可视化1.1 安装必要的Python库在开始之前确保你的Python环境已安装以下科学计算和可视化库pip install numpy matplotlib plotly scipy对于更高级的3D可视化PyVista也是一个不错的选择pip install pyvista1.2 创建基础函数曲面让我们以一个经典的二元函数为例z x² - y²双曲抛物面。这个函数在原点附近有丰富的微分特性非常适合我们的演示目的。import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # 定义函数 def func(x, y): return x**2 - y**2 # 创建网格数据 x np.linspace(-3, 3, 100) y np.linspace(-3, 3, 100) X, Y np.meshgrid(x, y) Z func(X, Y) # 绘制3D曲面 fig plt.figure(figsize(10, 8)) ax fig.add_subplot(111, projection3d) surf ax.plot_surface(X, Y, Z, cmapviridis, alpha0.8) ax.set_xlabel(X轴) ax.set_ylabel(Y轴) ax.set_zlabel(Z轴) plt.title(双曲抛物面 z x² - y²) plt.colorbar(surf) plt.tight_layout() plt.show()这段代码会生成一个可旋转的3D曲面图你可以通过鼠标拖动从不同角度观察这个曲面。这种交互式体验已经比教科书上的静态图片前进了一大步。提示在Jupyter Notebook中使用%matplotlib widget可以获得更流畅的交互体验。2. 偏导数与切线的可视化2.1 计算偏导数对于我们的函数z x² - y²偏导数很容易计算∂z/∂x 2x∂z/∂y -2y让我们在点P(1, 0.5)处计算这些偏导数# 计算点P(1, 0.5)处的偏导数 x_p, y_p 1.0, 0.5 z_p func(x_p, y_p) dz_dx 2 * x_p # ∂z/∂x 2x dz_dy -2 * y_p # ∂z/∂y -2y print(f在点({x_p}, {y_p})处:) print(f∂z/∂x {dz_dx}) print(f∂z/∂y {dz_dy})2.2 可视化偏导数的几何意义偏导数∂z/∂x表示当y固定时z随x的变化率几何上对应于曲面在x方向的切线斜率。我们可以用切线来直观展示这一点# 绘制x方向的切线 t np.linspace(-1, 1, 50) x_tangent_x x_p t y_tangent_x y_p * np.ones_like(t) z_tangent_x z_p dz_dx * t # 绘制y方向的切线 x_tangent_y x_p * np.ones_like(t) y_tangent_y y_p t z_tangent_y z_p dz_dy * t # 绘制曲面和切线 fig plt.figure(figsize(12, 8)) ax fig.add_subplot(111, projection3d) ax.plot_surface(X, Y, Z, cmapviridis, alpha0.5) ax.scatter([x_p], [y_p], [z_p], colorred, s50) # 点P ax.plot(x_tangent_x, y_tangent_x, z_tangent_x, colorblue, linewidth3, labelx方向切线) ax.plot(x_tangent_y, y_tangent_y, z_tangent_y, colorgreen, linewidth3, labely方向切线) ax.legend() plt.title(偏导数的几何意义坐标轴方向的切线) plt.tight_layout() plt.show()从图中可以清楚地看到两条切线分别沿着x和y方向贴在曲面上它们的斜率正好对应于两个偏导数的值。3. 可微性与切平面3.1 切平面的数学表达对于可微函数切平面方程为 z f(x₀,y₀) fₓ(x₀,y₀)(x-x₀) fᵧ(x₀,y₀)(y-y₀)在我们的例子中点P(1,0.5)处的切平面方程为 z (1² - 0.5²) 2*(x-1) -1*(y-0.5) 0.75 2(x-1) - (y-0.5)3.2 可视化切平面让我们用代码实现切平面的绘制并与原曲面进行比较# 定义切平面函数 def tangent_plane(x, y, x0, y0, func): dz_dx 2 * x0 # 我们的函数关于x的偏导 dz_dy -2 * y0 # 关于y的偏导 return func(x0, y0) dz_dx*(x-x0) dz_dy*(y-y0) # 计算切平面 Z_tangent tangent_plane(X, Y, x_p, y_p, func) # 绘制比较 fig plt.figure(figsize(14, 6)) # 子图1曲面和切平面 ax1 fig.add_subplot(121, projection3d) ax1.plot_surface(X, Y, Z, cmapviridis, alpha0.7) ax1.plot_surface(X, Y, Z_tangent, colororange, alpha0.5) ax1.scatter([x_p], [y_p], [z_p], colorred, s50) ax1.set_title(曲面与切平面) # 子图2误差分析曲面与切平面的差值 ax2 fig.add_subplot(122, projection3d) error Z - Z_tangent ax2.plot_surface(X, Y, error, cmapcoolwarm, alpha0.7) ax2.scatter([x_p], [y_p], [0], colorred, s50) ax2.set_title(切平面近似误差) plt.tight_layout() plt.show()从误差图中可以看到在点P附近误差非常小接近0随着远离P点误差逐渐增大。这正是可微性的直观体现——在足够小的邻域内切平面可以很好地近似原曲面。4. 梯度与方向导数4.1 梯度的计算与可视化梯度是一个向量其分量是函数的偏导数 ∇f (∂f/∂x, ∂f/∂y)在我们的例子中∇f (2x, -2y)。在点P(1,0.5)处∇f (2, -1)。让我们可视化梯度向量# 计算梯度 grad_x 2 * x_p grad_y -2 * y_p # 绘制梯度向量 fig plt.figure(figsize(10, 8)) ax fig.add_subplot(111, projection3d) # 绘制曲面 ax.plot_surface(X, Y, Z, cmapviridis, alpha0.5) # 绘制点P ax.scatter([x_p], [y_p], [z_p], colorred, s50) # 绘制梯度向量投影到xy平面 ax.quiver(x_p, y_p, z_p, grad_x, grad_y, 0, colorblue, length0.5, normalizeFalse, arrow_length_ratio0.1, linewidth3, label梯度向量投影) # 绘制梯度向量三维 scale 0.2 # 缩放因子使箭头大小合适 ax.quiver(x_p, y_p, z_p, grad_x*scale, grad_y*scale, np.sqrt(grad_x**2 grad_y**2)*scale, colorgreen, arrow_length_ratio0.1, linewidth3, label梯度向量(3D)) ax.legend() plt.title(梯度向量可视化) plt.tight_layout() plt.show()梯度向量指示了函数在该点处增长最快的方向。从图中可以看到蓝色箭头是梯度在xy平面的投影绿色箭头则展示了梯度在三维空间中的指向。4.2 方向导数的计算与比较方向导数是函数在某点沿特定方向的变化率。计算公式为 Dᵤf ∇f · u fₓcosθ fᵧsinθ 其中u (cosθ, sinθ)是单位方向向量。让我们比较几个不同方向的方向导数# 方向导数计算函数 def directional_derivative(grad_x, grad_y, theta): theta_rad np.deg2rad(theta) u_x np.cos(theta_rad) # 方向向量的x分量 u_y np.sin(theta_rad) # 方向向量的y分量 return grad_x * u_x grad_y * u_y # 计算不同方向的方向导数 angles np.arange(0, 360, 30) # 每30度一个方向 dd_values [directional_derivative(grad_x, grad_y, angle) for angle in angles] # 可视化方向导数 fig plt.figure(figsize(10, 6)) ax fig.add_subplot(111, polarTrue) ax.plot(np.deg2rad(angles), dd_values, b-, linewidth2) ax.fill(np.deg2rad(angles), dd_values, b, alpha0.2) ax.set_theta_zero_location(E) # 0度朝右 ax.set_title(不同方向的方向导数, pad20) plt.show()极坐标图清晰地展示了方向导数如何随方向变化。最大值出现在梯度方向约-26.6度因为我们的梯度是(2,-1)最小值出现在相反方向。5. 交互式可视化进阶5.1 使用Plotly创建交互式图形Matplotlib的3D功能有一定局限Plotly提供了更强大的交互式3D可视化import plotly.graph_objects as go # 创建Plotly图形 fig go.Figure() # 添加曲面 fig.add_trace(go.Surface(xX, yY, zZ, colorscaleViridis, opacity0.8)) # 添加切平面 fig.add_trace(go.Surface(xX, yY, zZ_tangent, colorscaleReds, opacity0.5)) # 添加点P fig.add_trace(go.Scatter3d(x[x_p], y[y_p], z[z_p], modemarkers, markerdict(size5, colorred))) # 添加梯度向量 fig.add_trace(go.Cone(x[x_p], y[y_p], z[z_p], u[grad_x*0.2], v[grad_y*0.2], w[np.sqrt(grad_x**2 grad_y**2)*0.2], sizemodescaled, sizeref0.5, anchortail, colorscaleBlues)) fig.update_layout(title交互式3D可视化曲面、切平面与梯度, scenedict(aspectmodecube)) fig.show()Plotly生成的图形可以自由旋转、缩放并能通过悬停查看坐标值大大增强了探索体验。5.2 创建动态演示为了更生动地展示这些概念我们可以创建动画来展示切平面如何随点移动而变化from matplotlib.animation import FuncAnimation from IPython.display import HTML # 准备动画数据 points [(1, 0.5), (0, 1), (-1, -0.5), (1.5, -1)] # 要演示的几个点 # 设置图形 fig plt.figure(figsize(10, 8)) ax fig.add_subplot(111, projection3d) ax.plot_surface(X, Y, Z, cmapviridis, alpha0.7) ax.set_xlim(-3, 3) ax.set_ylim(-3, 3) ax.set_zlim(-5, 5) # 初始化元素 point ax.scatter([], [], [], colorred, s50) tangent_plane ax.plot_surface(np.empty_like(X), np.empty_like(Y), np.empty_like(Z), colororange, alpha0.5) def init(): point._offsets3d ([], [], []) return point, tangent_plane def update(frame): x_p, y_p points[frame] z_p func(x_p, y_p) # 更新点位置 point._offsets3d ([x_p], [y_p], [z_p]) # 计算并更新切平面 Z_tangent tangent_plane(X, Y, x_p, y_p, func) tangent_plane._facecolors tangent_plane._edgecolors plt.cm.Oranges(np.ones_like(Z)) tangent_plane._verts3d X, Y, Z_tangent ax.set_title(f点({x_p:.1f}, {y_p:.1f})处的切平面) return point, tangent_plane ani FuncAnimation(fig, update, frameslen(points), init_funcinit, blitFalse) plt.close() HTML(ani.to_html5_video())这段代码会生成一个动画展示当点在曲面上移动时切平面如何相应地调整以保持与曲面的最佳局部近似。6. 应用实例极值分析与海塞矩阵6.1 寻找极值点对于函数z x² - y²我们可以通过求解梯度为零的点来找到临界点from scipy.optimize import minimize # 寻找最小值虽然这个函数没有全局极值但可以演示过程 result minimize(lambda x: func(x[0], x[1]), x0[1, 1]) print(f找到的临界点: ({result.x[0]:.4f}, {result.x[1]:.4f})) print(f函数值: {result.fun:.4f}) print(f梯度: {result.jac if hasattr(result, jac) else 未提供})对于我们的简单函数显然在(0,0)处梯度为零让我们分析这个点。6.2 海塞矩阵与极值判定海塞矩阵是由二阶偏导数组成的矩阵 H [[∂²f/∂x², ∂²f/∂x∂y], [∂²f/∂y∂x, ∂²f/∂y²]]对于z x² - y² ∂²f/∂x² 2 ∂²f/∂y² -2 ∂²f/∂x∂y ∂²f/∂y∂x 0所以海塞矩阵为 [[2, 0], [0, -2]]这个矩阵的特征值为2和-2一正一负说明(0,0)是一个鞍点既不是局部最大值也不是局部最小值。6.3 可视化极值分析让我们可视化临界点附近的函数行为# 在原点附近创建更精细的网格 x_fine np.linspace(-1, 1, 50) y_fine np.linspace(-1, 1, 50) X_fine, Y_fine np.meshgrid(x_fine, y_fine) Z_fine func(X_fine, Y_fine) # 计算特征值和特征向量 eigenvalues np.linalg.eigvals([[2, 0], [0, -2]]) print(f海塞矩阵的特征值: {eigenvalues}) # 绘制临界点附近 fig plt.figure(figsize(12, 6)) # 子图13D视图 ax1 fig.add_subplot(121, projection3d) ax1.plot_surface(X_fine, Y_fine, Z_fine, cmapcoolwarm, alpha0.8) ax1.scatter([0], [0], [0], colorred, s50) ax1.set_title(临界点(0,0)附近的函数行为) # 子图2等高线图 ax2 fig.add_subplot(122) contour ax2.contour(X_fine, Y_fine, Z_fine, levels20, cmapcoolwarm) ax2.scatter([0], [0], colorred, s50) ax2.set_title(等高线图展示鞍点特征) plt.colorbar(contour) plt.tight_layout() plt.show()从等高线图中可以清楚地看到在x方向对应正特征值函数表现为凸函数而在y方向对应负特征值表现为凹函数这正是鞍点的典型特征。

更多文章