
多元微积分里二重极限往往比单变量极限难理解得多。问题不在于计算量而在于自变量可以从无限多个方向靠近目标点。单变量极限只需要看左右两边二元函数却要面对整张平面上的逼近路径。很多同学在纸上看到的只是公式lim_{(x,y)-(0,0)} ...很难想象为什么某条路径存在就能推翻极限也很容易把二重极限和累次极限混在一起。如果把这些函数画成三维曲面再把一条条逼近路径投影到曲面上很多结论会变得非常直观。下面会以几个经典函数为例用 Python 和 matplotlib 把二重极限的路径依赖、极限存在性与 epsilon-delta 定义可视化出来并给出可运行的代码和常见误区排查清单。这类内容经常出现在课程讲义或 PDF 课件里但静态图只能展示曲面形状看不出动点沿不同路径走向目标点时的高度变化。自己用代码把图做出来之后视角可以任意旋转路径可以随时增删动画可以反复播放。对学习多元微积分、准备考研数学或者需要用 Python 做数学可视化的人来说这种“先看图再理解定义”的方式比单纯记公式有效得多。1. 二重极限为什么比单变量极限难判断1.1 单变量极限的“左右”经验无法直接平移单变量函数y f(x)在x - a时的极限只需要检查两个方向从左边逼近和从右边逼近。只要左右极限相等极限就存在。这个结论能成立是因为数轴上的点只有一条线可走x 除了从左边来就是从右边来。二元函数完全不一样。(x, y)是平面上的点当它趋向(a, b)时可以沿着 x 轴、顺着 y 轴、走任意一条直线、绕一条抛物线、甚至沿一条螺旋线逼近。路径数量是无限的。二重极限如果存在必须保证所有这些路径对应的函数值极限都相同。这就是初学阶段最容易出错的地方有人习惯了单变量的“两个方向”于是把二重极限简化成“先令 x 趋近再令 y 趋近”也就是累次极限。这样做会忽略掉无数条曲线路径得到的结果自然不可靠。1.2 形式化定义去心邻域和 epsilon-delta二重极限的严格定义是lim_{(x,y)-(a,b)} f(x,y) L意思是对任意给定的ε 0总能找到一个δ 0使得当(x,y)满足0 sqrt((x-a)^2 (y-b)^2) δ时一定有|f(x,y) - L| ε这里的sqrt((x-a)^2 (y-b)^2)表示(x,y)到(a,b)的平面距离。整个条件说的是只要动点进入目标点周围一个足够小的去心圆盘曲面上对应点的高度就必须落进(L-ε, Lε)这条水平带子里。需要注意定义用的是“去心”邻域。也就是说函数在(a,b)这一点的值是否存在、等于多少都不影响极限。很多二元函数在目标点处没有定义但极限仍然可能存在最常见的就是分母为零的情况。1.3 二重极限、路径极限和累次极限不是一回事这三个概念经常被混用必须分开概念趋近方式说明二重极限(x,y)同时且沿任意路径趋向目标点真正的二元函数极限路径极限只沿某一条指定路径趋向目标点单变量极限只能作为反例证据累次极限先让一个变量趋近再让另一个变量趋近两次单变量极限与二重极限没有必然关系典型反例是f(x,y) xy/(x^2y^2)。先令x - 0得到 0再令y - 0累次极限为 0。但沿直线y x逼近原点时函数值恒为1/2所以二重极限并不存在。反过来有些函数二重极限存在但某个累次极限不存在。因此累次极限既不能证明二重极限存在也不能直接说明二重极限不存在。在中文教材里“双重极限”和“二重极限”有时混用有的地方又把“双重极限”用来指代累次极限。这里按英文里的 double limit 处理指的是二元函数的二重极限。2. 把曲面、等高线和逼近路径放在一张图里2.1 二元函数的图像是曲面极限关心的是“逼近某个点”二元函数z f(x,y)的图像是三维空间里的一个曲面。x 轴和 y 轴确定平面位置z 轴表示函数值。当(x,y)趋向目标点时曲面上的点(x, y, f(x,y))会在竖直方向上移动。二重极限关心的问题是动点从四面八方靠近目标点时曲面上的高度最终会不会落在一个确定的值附近。如果所有路径都通向同一个高度极限存在如果不同路径通向不同高度极限就不存在。可以想象成爬山从北坡、南坡、东坡、西坡以及各种斜向小路走最后如果都到达同一个山头顶部这个“山顶高度”就可以称为极限。如果一条路通向山顶另一条路却通向悬崖边上一块悬空的平台那就不能说存在唯一山顶高度。2.2 路径依赖曲面上的截线当选择一条平面路径x x(t), y y(t), t - t0时把它代入f(x,y)得到一元函数g(t) f(x(t), y(t))这就是沿该路径的极限。把这条路径画在曲面上会得到一条三维曲线。曲线的终点也就是当t趋向目标参数时曲面上点的位置是判断极限的关键。若两条路径对应的终点高度不同二重极限就不存在。这种“路径依赖”在曲面图上非常直观观察红色路径、蓝色路径、绿色路径最后分别贴到曲面的哪个高度。2.3 视觉判断极限是否存在的三条线索用图判断极限是否存在通常看三个信号直线路径。在目标点周围取不同方向沿这些方向看曲面截线的高度是否一致。曲线路径。直线路径不够时再取抛物线、双曲线、极坐标固定角度等路径看是否能找到不同高度。等高线。在等高线图里如果目标点附近出现多条颜色带汇聚到不同值或者等高线在目标附近突然断裂、交叉通常提示极限可能不存在。但必须明确图像只是辅助工具。曲面采样有分辨率数值计算有浮点误差视觉上看起来“像”极限存在不能替代数学证明。画图的真正价值是帮助找反例以及帮助理解定义里每个条件的几何含义。3. 用 Python 搭出可视化框架3.1 环境准备下面代码基于 Python 3.8 以上版本依赖 numpy 和 matplotlib。建议在独立虚拟环境里安装。python -m venv .venv source .venv/bin/activate pip install numpy matplotlib notebookWindows 激活虚拟环境的命令是.venv\Scripts\activate。如果是在 Jupyter 里做交互实验再安装 notebook 即可。安装完成后先运行一个最小检查import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D print(np.__version__, plt.matplotlib.__version__)只要不报错就说明环境可用。后面所有绘图代码都假设这三个模块已经导入。3.2 用 meshgrid 构造二维采样网格在 matplotlib 中画曲面需要先在 xy 平面上生成网格。np.meshgrid的作用是把一组 x 坐标和一组 y 坐标扩充成二维数组让f(X, Y)可以直接用数组计算。x np.linspace(-1.5, 1.5, 300) y np.linspace(-1.5, 1.5, 300) X, Y np.meshgrid(x, y) Z f(X, Y)X和Y的形状相同每个(X[i,j], Y[i,j])对应平面上一个采样点。网格越密曲面越平滑但计算量也越大。函数在某个点没有定义时Z中会出现nan或inf绘图时这些点会形成空洞。应对方法是在计算时屏蔽非有限值。3.3 一个通用的曲面和路径绘图函数为了后面多个例子复用先封装一个函数。它负责生成网格、绘制曲面、叠加等高线、再绘制若干条逼近路径。def plot_surface_with_paths(F, paths(), xlim(-1.5, 1.5), ylim(-1.5, 1.5), N300): x np.linspace(xlim[0], xlim[1], N) y np.linspace(ylim[0], ylim[1], N) X, Y np.meshgrid(x, y) with np.errstate(divideignore, invalidignore): Z F(X, Y) Z np.where(np.isfinite(Z), Z, np.nan) fig plt.figure(figsize(12, 8)) ax fig.add_subplot(111, projection3d) ax.plot_surface(X, Y, Z, cmapviridis, alpha0.85) ax.contourf(X, Y, Z, zdirz, offset-1.0, cmapviridis, alpha0.35) for px, py in paths: pz F(px, py) ax.plot(px, py, pz, linewidth2.5, labelfpath: y{py[-1]/px[-1]:.2f}x if px[-1] ! 0 else path) ax.set_xlabel(x) ax.set_ylabel(y) ax.set_zlabel(z) plt.tight_layout() return fig, ax这个函数不负责证明极限是否存在只负责把曲面上可能出现的“路径分叉”显示出来。offset-1.0是把等高线投影在z-1平面上实际使用时可以根据数据范围调整。路径标签这里写得比较粗糙只是个示例更精确的标签应该由调用处根据路径构造。3.4 运行方式和预期输出可以把代码保存成plot_limit.py运行python plot_limit.py。也可以放到 Jupyter 中逐段执行。预期输出是一个可以旋转视角的三维窗口曲面颜色沿 z 方向变化。保存图片时在绘图后加一行plt.savefig(limit_surface.png, dpi150)。matplotlib 常用函数整理如下函数用途关键参数np.meshgrid生成曲面所需的 X/Y 网格indexing,sparseax.plot_surface绘制三维曲面alpha,cmap,rstride,cstrideax.contourf绘制投影等高线zdir,offset,levelsax.plot在三维坐标系中绘制空间曲线linewidth,colorax.scatter标记动点位置s,color4. 四个经典函数从光滑曲面到路径依赖4.1 极限存在且函数连续f(x,y) x^2 y^2先看最简单的抛物面。它在原点处函数值为 0沿任何路径逼近原点高度都趋近 0。曲面连续、光滑没有孔洞也没有悬崖。def f_smooth(x, y): return x**2 y**2 paths [ (np.linspace(-1, 1, 300), 0.5 * np.linspace(-1, 1, 300)), (np.linspace(-1, 1, 300), -0.3 * np.linspace(-1, 1, 300)), ] fig, ax plot_surface_with_paths(f_smooth, paths, xlim(-1.5, 1.5), ylim(-1.5, 1.5)) plt.show()路径曲线在曲面上从高处滑向原点最终都汇聚到z0附近。这个例子虽然简单但适合作为理解二重极限的基准光滑连续曲面在所有方向上的终点高度一致。4.2 直线路径已经能推翻极限f(x,y) xy/(x^2y^2)这个函数在原点没有定义。用极坐标代换x r cosθ, y r sinθ得到f (r^2 cosθ sinθ) / r^2 cosθ sinθ函数值只与方向 θ 有关而与距离 r 无关。沿不同方向逼近原点会得到不同的常数。具体看沿直线y kx的情况f(x, kx) kx^2 / (x^2 k^2 x^2) k / (1 k^2)沿 x 轴即k0极限为 0。沿yx即k1极限为1/2。沿y-x即k-1极限为-1/2。曲面在原点附近会出现一个“扭转”的结构不同方向的截线终点停在不同的高度。可视化代码可以同时画出 k0、1、2、-1 这四条路径。def f_xy(x, y): return x * y / (x**2 y**2) t np.linspace(-1, 1, 300) mask np.abs(t) 1e-6 paths [] for k in [0, 1, 2, -1]: paths.append((t[mask], k * t[mask])) fig, ax plot_surface_with_paths(f_xy, paths, xlim(-1.2, 1.2), ylim(-1.2, 1.2), N200) plt.show()运行后会看到不同颜色路径的末端高度明显不同。这里有一个很关键的判断逻辑只要能找到两条路径它们的极限不相等二重极限就不存在不需要再检查其他路径。4.3 曲线路径才暴露问题f(x,y) x^2 y/(x^4 y^2)这个函数更具迷惑性。沿任意直线ykx逼近原点时f(x, kx) kx^3 / (x^4 k^2x^2) kx / (x^2 k^2)令x - 0结果都是 0。如果只检查直线路径很容易错误地认为二重极限存在且为 0。但取抛物线y x^2逼近原点的效果完全不同f(x, x^2) x^4 / (x^4 x^4) 1/2沿这条曲线函数值恒为1/2。因此二重极限不存在。这个例子说明直线路径全部相同只是必要条件不是充分条件。判断极限是否存在时必须考虑任意曲线路径。def f_curve(x, y): return x**2 * y / (x**4 y**2) t np.linspace(-0.8, 0.8, 300) mask np.abs(t) 1e-5 paths [ (t[mask], 0.6 * t[mask]), # 直线 (t[mask], (t[mask])**2), # 抛物线 ] fig, ax plot_surface_with_paths(f_curve, paths, xlim(-0.8, 0.8), ylim(-0.6, 0.8), N200) plt.show()曲面图上直线路径和抛物线路径最终的高度不会收敛到同一个点。这个例子很适合用来训练“不要只测直线”的意识。4.4 极限存在但函数没有定义f(x,y) x^2 y/(x^2y^2)它与 4.3 只差一个分母指数但结论完全不同。使用极坐标f (r^2 cos^2θ · r sinθ) / r^2 r cos^2θ sinθ由于|cos^2θ sinθ| 1当r - 0时函数值一致收敛到 0。因此lim_{(x,y)-(0,0)} x^2 y/(x^2y^2) 0函数在原点没有定义但极限存在。可以通过补充定义f(0,0)0让函数连续这种点称为可去间断点。def f_removable(x, y): return x**2 * y / (x**2 y**2) t np.linspace(-1, 1, 300) paths [ (t, 0.5 * t), (t, -0.5 * t), (t, t**2), ] fig, ax plot_surface_with_paths(f_removable, paths, xlim(-1.2, 1.2), ylim(-1.2, 1.2), N200) plt.show()曲面上原点处会有一个因未定义而产生的空洞但周围曲面像被“压平”了一样所有路径最后都趋向同一个高度。这个图与 4.3 的差别一目了然。5. 用动画逼近原点观察路径终点高度5.1 动画要表达什么静态曲面只能展示最终形态看不出“沿路径移动”的过程。动画可以更真实地模拟极限中的逼近过程一个红色动点沿着指定路径从远处缓慢滑向目标点并留下轨迹线。播放到最后一帧时轨迹终点所在的高度就是该路径的极限。对于路径依赖的函数把 k 改一下重新运行就能看到红色轨迹的终点改变高度。这个过程比静态图更能建立起“无限多条路径”的直觉。5.2 一段可运行的动画代码下面以f(x,y) xy/(x^2y^2)为例用FuncAnimation制作沿y 0.3x走向原点的动画。import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from mpl_toolkits.mplot3d import Axes3D x np.linspace(-1.5, 1.5, 250) y np.linspace(-1.5, 1.5, 250) X, Y np.meshgrid(x, y) def f(x, y): return x * y / (x**2 y**2) with np.errstate(divideignore, invalidignore): Z f(X, Y) Z np.where(np.isfinite(Z), Z, np.nan) fig plt.figure(figsize(9, 6)) ax fig.add_subplot(111, projection3d) ax.plot_surface(X, Y, Z, cmapviridis, alpha0.7) ax.set_zlim(-0.7, 0.7) steps np.linspace(1.2, 0.005, 150) line, ax.plot([], [], [], colorred, linewidth2) point, ax.plot([], [], [], o, colorred, markersize6) def update(i): xs steps[:i 1] ys 0.3 * xs zs f(xs, ys) line.set_data(xs, ys) line.set_3d_properties(zs) point.set_data([xs[-1]], [ys[-1]]) point.set_3d_properties([zs[-1]]) return line, point anim FuncAnimation(fig, update, frameslen(steps), interval40, blitFalse) anim.save(path_xy.gif, writerpillow, fps25)代码里把路径起点设在了x1.2终点停在x0.005避免直接落在奇点(0,0)。参数0.3就是路径斜率 k。如果把这个值改成1最后一帧的红色点会停在z0.5附近改成-1则停在z-0.5附近。5.3 动画结果如何辅助判断把 k0、0.3、1、-1 的动画分别保存逐帧对比最后一帧可以看到相同目标点对应的终点高度不同。这正是“路径依赖”的视觉证据。同时要注意动画本身没有严格证明二重极限不存在。它只是帮助找到“可能存在不同高度”的怀疑对象。真正下结论时还需要用路径代入完成符号计算。6. 回到 epsilon-delta图像和证明如何互相验证6.1 delta 邻域在图像上是“哪一部分”二重极限定义里δ控制的是 xy 平面上一个以目标点为圆心的去心圆盘。这个圆盘内的所有点经过f映射到 z 轴后必须全部落在(L-ε, Lε)的区间里。用图来理解先在曲面图上画两个水平平面z L-ε和z Lε。如果对于某个ε无论把 xy 平面上的圆盘半径缩得多小曲面局部总有一部分高度超出这两层平面之间那么这个ε就找不到对应的δ极限不成立。在实际画图中可以用ax.contourf观察等高线是否围绕目标点形成越来越小的闭合圈。如果等高线在某个方向一直不闭合说明对应方向上的函数值发散到不同范围。6.2 用极坐标证明存在性时的视觉对应极坐标代换是证明二重极限存在的常用手段。令x a r cosθ, y b r sinθ如果代入后能把函数写成f(x,y) L r^α · H(θ)并且H(θ)有界那么当r - 0时整个表达式收敛到 L。视觉上这等价于说在以目标点为圆心、半径为 r 的圆周上函数值的最大偏离会随着 r 缩小而趋近 0。f(x,y) x^2 y/(x^2y^2)就是典型例子。极坐标下得到r cos^2θ sinθ其中cos^2θ sinθ的绝对值不超过 1因此当r - 0时函数一致趋近 0。曲面图会显示一个“可去空洞”而不是撕裂。6.3 用路径法证明不存在的操作流程当怀疑某个二重极限不存在时按下面的步骤走先选简单路径。优先尝试 x 轴、y 轴、直线ykx。如果这些路径给出的极限不同立即下结论。如果直线路径高度相同再选曲线路径。重点尝试yx^2、yx^3、xy^2、极坐标固定 θ 等。找出两条极限不同或者某条路径极限不存在的路径后写清楚路径参数化和极限过程。如果找不到反例也不能断定极限存在需要用定义、夹逼、极坐标等方式证明。常见证明方法对比如下方法能证明关键注意路径法极限不存在只需要两条路径极限不同极坐标代换极限存在余项必须对 θ 一致有界夹逼定理极限存在需要构造合适的控制函数累次极限都不能直接证明只能作为直觉参考7. 常见误区与可视化排错清单7.1 概念误区累次极限和直线路径依赖第一个误区是把累次极限当作二重极限。f(x,y)xy/(x^2y^2)的累次极限都是 0但二重极限不存在。以后看到“先对 x 求极限再对 y 求极限”的写法要立刻意识到这只是一组特殊的路径顺序不能代表所有逼近路径。第二个误区是认为所有直线路径极限相同就代表二重极限存在。f(x,y)x^2 y/(x^4y^2)就是这个误区的标准反例。直线路径之外还有无穷多曲线路径。第三个误区是在用路径法时忽略了路径必须经过目标点。如果某条路径最终不趋向(a,b)那么它不能用来判断二重极限。7.2 可视化代码中的实际问题现象常见原因处理方式曲面出现白色孔洞或尖刺函数在目标点处未定义除零产生 inf/nan用np.where(np.isfinite(Z), Z, np.nan)屏蔽无效值等高线断裂采样范围过大或网格过稀缩小 xlim/ylim增加 N使用局部加密路径曲线突然中断参数 t 经过奇点z 出现 nan用 mask 排除零点或把路径分为两段动画卡顿每帧都重新绘制曲面先绘制曲面只更新轨迹线和动点图像旋转后看不出细节曲面遮挡严重降低alpha用等高线辅助调整视角7.3 数值抽检不能替代证明数值计算可以快速发现“可疑点”。比较稳妥的抽检方式是在极坐标下随机取很多组(r, θ)计算函数值看最大值和最小值是否有明显差距。rng np.random.default_rng(42) r 10.0 ** rng.uniform(-8, -1, 2000) theta rng.uniform(0, 2 * np.pi, 2000) x r * np.cos(theta) y r * np.sin(theta) vals f_xy(x, y) print(vals.min(), vals.max())如果随机抽检中出现了明显不同的函数值说明极限很可能不存在。但如果没有出现也只能说明在当前采样里没发现反例不能作为证明。数值结果适合用来生成猜测最终判断仍要回到符号推导。8. 从二重极限走向多元微积分的下一步8.1 建立“画图先行”的习惯学习二重极限最好的顺序是先画出曲面再观察路径最后回到定义或证明方法。一幅好的三维图能帮你在几秒内看到问题所在。比如曲面在目标点附近是否撕裂、是否扭曲、是否有多个“高度层”这些信号会直接影响证明策略。在实际教学中这个思路也有效。课堂演示可以用 Jupyter 现场改参数课程讲义则可以把图片导出成静态图嵌入 PDF。若要做成在线交互教材可以把 matplotlib 图替换成 Plotly或者使用 GeoGebra 生成可拖动的动图。8.2 可复用的检查清单每次分析一个二元函数极限可以按下面的清单逐项检查目标点是否在定义域内如果不在补充定义是否可行。先用 x 轴和 y 轴路径估算可能的极限值。再沿若干直线ykx计算路径极限。如果直线路径极限一致尝试曲线路径尤其是抛物线。用极坐标判断余项是否对 θ 一致趋于 0。必要时用夹逼定理构造控制函数。最后回到 epsilon-delta 定义确认逻辑上没有缝隙。这套清单同样适用于偏导数、方向导数和全微分的学习因为它们本质上都在处理“从不同方向逼近”的问题。8.3 扩展方向二重极限只是多元微分学的起点。理解路径依赖之后可以继续研究连续性与一致连续性以及极限存在对函数局部行为的影响。偏导数和方向导数的几何含义为什么偏导数存在推不出可微。全微分存在时切平面的可视化它与二重极限的关系。多元函数极值问题中的 Hessian 矩阵判定。建议打开 Python把上面几个函数跑一遍再把路径改成螺旋线x t cos(1/t), y t sin(1/t)观察曲面上的轨迹会发生什么。多数时候一个亲手画出来的反例比十遍口头解释都更让人记住。