跳轉到

09.1 多變數微積分#

本節目標#

  • 理解偏導數的數學意義,並用 diff(f, x) 對多變數函數求偏導
  • 學會求梯度、解聯立 \(f_x = f_y = 0\) 找出臨界點
  • 用 Hessian 矩陣判別極大值、極小值與鞍點
  • 以符號方式推導 Lagrange 乘數法,處理約束極值問題
  • 用嵌套 integrate 計算雙重積分
  • 動手實驗:畫出 3D 曲面與等高線圖

偏導數的概念#

對多變數函數 \(f(x, y)\) 而言,偏導數 \(\dfrac{\partial f}{\partial x}\) 是「把其他變數當常數,只對 \(x\) 微分」的結果。例如:

\[ f(x, y) = x^2 y + \sin(xy), \qquad \frac{\partial f}{\partial x} = 2xy + y\cos(xy) \]

在 SymPy 中,diff 的第二個之後的參數指定「對哪個變數」微分:

from sympy import symbols, diff, sin

x, y = symbols('x y')
f = x**2*y + sin(x*y)

print(diff(f, x))   # 2*x*y + y*cos(x*y)
print(diff(f, y))   # x**2 + x*cos(x*y)
print(diff(f, x, 2))        # 對 x 的二階偏導:2*y - y**2*sin(x*y)
print(diff(f, x, y))        # 混合偏導:2*x - x*y*sin(x*y) + cos(x*y)

梯度與極值:solve 聯立 f_x = f_y = 0#

梯度定義為所有偏導數組成的向量:

\[ \nabla f = \left( \frac{\partial f}{\partial x},\ \frac{\partial f}{\partial y} \right) \]

極值候補點(臨界點)就是梯度為零的地方。用 solve 解聯立方程 \(f_x = 0\) 且 \(f_y = 0\) :

from sympy import symbols, diff, solve

x, y = symbols('x y')
f = x**3 - 3*x + y**2   # 經典例子:有明顯的極值結構

fx = diff(f, x)   # 3*x**2 - 3
fy = diff(f, y)   # 2*y

crit = solve([fx, fy], [x, y])
print(crit)   # [(-1, 0), (1, 0)]

Hessian 判別#

在臨界點 \(P\) 處,二階行為由 Hessian 矩陣決定:

\[ H = \begin{pmatrix} f_{xx} & f_{xy} \\ f_{yx} & f_{yy} \end{pmatrix}, \qquad D = f_{xx} f_{yy} - f_{xy}^2 \]

判別規則(以兩變數為例):

  • \(D > 0\) 且 \(f_{xx} > 0\) :局部極小值
  • \(D > 0\) 且 \(f_{xx} < 0\) :局部極大值
  • \(D < 0\) :鞍點
  • \(D = 0\) :無法判斷
from sympy import symbols, diff, solve

x, y = symbols('x y')
f = x**3 - 3*x + y**2

fxx = diff(f, x, 2)
fyy = diff(f, y, 2)
fxy = diff(f, x, y)

crit = solve([diff(f, x), diff(f, y)], [x, y])
for px, py in crit:
    D = fxx*fyy - fxy**2
    Dv = D.subs({x: px, y: py})
    fv = fxx.subs({x: px, y: py})
    if Dv > 0:
        kind = "極小值" if fv > 0 else "極大值"
    elif Dv < 0:
        kind = "鞍點"
    else:
        kind = "無法判斷"
    print(f"({px}, {py}): D = {Dv}, f_xx = {fv} -> {kind}")
# (-1, 0): D = 12, f_xx = -6 -> 極大值
# (1, 0): D = 12, f_xx = 6 -> 極小值

也可以用矩陣形式直接建 Hessian 並求行列式:

from sympy import hessian

H = hessian(f, (x, y))
print(H.det())   # 與 fxx*fyy - fxy**2 相同

Lagrange 乘數法#

要在約束 \(g(x, y) = 0\) 下求 \(f(x, y)\) 的極值,引入乘數 \(\lambda\) ,解:

\[ \nabla f = \lambda \nabla g, \qquad g(x, y) = 0 \]

例子:在 \(x + y = 1\) 之下最大化 \(xy\) 。 這在幾何上是「周長固定的矩形,面積最大時是正方形」,我們用符號推導驗證:

from sympy import symbols, diff, solve, Rational

x, y, lam = symbols('x y lambda', real=True)
f = x*y
g = x + y - 1

# 聯立:f_x = lam*g_x, f_y = lam*g_y, g = 0
eqs = [
    diff(f, x) - lam*diff(g, x),   # y - lam
    diff(f, y) - lam*diff(g, y),   # x - lam
    g,
]
sol = solve(eqs, [x, y, lam], dict=True)
print(sol)   # [{x: 1/2, y: 1/2, lambda: 1/2}]

for s in sol:
    print("最大值 f =", f.subs(s))   # 1/4

結果: \(x = y = \dfrac{1}{2}\) , \(xy = \dfrac{1}{4}\) ,正好是正方形。SymPy 完全用符號運算得出這個結論,沒有任何數值近似。

雙重積分#

計算

\[ \int_0^1 \int_0^2 (x^2 + y^2)\, dx\, dy \]

在 SymPy 中就是嵌套呼叫 integrate,先對內層變數積分:

from sympy import symbols, integrate

x, y = symbols('x y')

inner = integrate(x**2 + y**2, (x, 0, 2))   # 先對 x:2*y**2 + 8/3
result = integrate(inner, (y, 0, 1))
print(result)   # 10/3

# 也可以一次寫完
print(integrate(x**2 + y**2, (x, 0, 2), (y, 0, 1)))   # 10/3

注意順序:(x, 0, 2), (y, 0, 1) 表示先對 \(x\) 積到 2、再對 \(y\) 積到 1(內層在前)。

# 驗證:用極座標算圓盤上的積分
from sympy import pi

r, t = symbols('r theta', positive=True)
expr = (r**2)   # x**2 + y**2 = r**2,面積元素為 r dr dt
print(integrate(expr*r, (r, 0, 1), (t, 0, 2*pi)))   # pi/2

動手實驗:3D 曲面與等高線#

把 \(f(x, y) = x^3 - 3x + y^2\) 的曲面與等高線畫出來,觀察極大、極小與鞍點的位置:

import numpy as np
import matplotlib.pyplot as plt

X, Y = np.meshgrid(np.linspace(-2.5, 2.5, 200),
                   np.linspace(-2.5, 2.5, 200))
F = X**3 - 3*X + Y**2

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

# 3D 曲面
ax1 = fig.add_subplot(1, 2, 1, projection='3d')
ax1.plot_surface(X, Y, F, cmap='viridis', alpha=0.9)
ax1.set_title('$f(x,y) = x^3 - 3x + y^2$')

# 等高線(標出臨界點)
ax2 = fig.add_subplot(1, 2, 2)
cs = ax2.contour(X, Y, F, levels=20)
ax2.clabel(cs, fontsize=8)
ax2.plot(-1, 0, 'r*', markersize=14, label='極大值')
ax2.plot(1, 0, 'g*', markersize=14, label='極小值')
ax2.legend()
ax2.set_title('等高線圖')

plt.tight_layout()
plt.show()

等高線圖中,局部極大值周圍的等高線呈「封閉圈且內圈數值較大」,鞍點處等高線則呈交叉狀。你也可以把本節的 Lagrange 例子畫出來: \(xy = c\) 的等高線與直線 \(x + y = 1\) 相切之處就是極值點。

# Lagrange 幾何直覺:xy = 1/4 與 x + y = 1 相切於 (1/2, 1/2)
xv = np.linspace(0, 1, 200)
Xv, Yv = np.meshgrid(xv, xv)
plt.contour(Xv, Yv, Xv*Yv, levels=[0.1, 0.2, 0.25, 0.3])
plt.plot(xv, 1 - xv, 'r-', label='$x + y = 1$')
plt.plot(0.5, 0.5, 'ko')
plt.legend()
plt.gca().set_aspect('equal')
plt.show()

小結與練習#

本節重點

  • diff(f, x) 求偏導數,diff(f, x, y) 求混合偏導
  • 極值候補點由 \(\nabla f = 0\) 求得,用 solve 解聯立方程
  • Hessian 行列式 \(D\) 配合 \(f_{xx}\) 判別極值類型;hessian(f, (x, y)) 可直接建矩陣
  • Lagrange 乘數法:解 \(\nabla f = \lambda \nabla g\) 與 \(g = 0\)
  • 雙重積分用嵌套 integrate,內層變數寫在前

練習

  1. 求 \(f(x, y) = x^2 + xy + y^2 - 3x\) 的所有臨界點並判別類型。
  2. 用 Lagrange 乘數法求:在 \(x^2 + y^2 = 1\) 之下, \(x + y\) 的最大值與最小值。
  3. 計算 \(\displaystyle\int_0^{\pi} \int_0^x \sin(x+y)\, dy\, dx\) ,並用數值積分(integral.as_sum() 或 mpmath)驗證。
  4. 實驗:把 3D 圖中的函數換成 \(f(x, y) = x^2 - y^2\) ,確認原點是鞍點。