在Manim中制作导数定义动画时,如何避免繁琐的手动计算?本文将介绍如何利用SymPy实现自动化符号运算,轻松呈现割线逼近切线的动态过程。

这个经典场景需要绘制曲线和割线,通过让割线上一个点无限逼近另一个点,最终形成切线。其核心在于计算割线斜率(f(x+h) - f(x))/h,并观察当h趋近于0时的变化。
虽然原理简单,但手动推导极限、逐帧计算坐标不仅耗时,还容易出错。以函数f(x)=x³-2x+1在x=1处的切线动画为例,我们需要解决以下问题:
手动处理这些步骤需要展开(1+h)³-2(1+h)+1等复杂表达式,对于更复杂的函数如f(x)=sin(x²)几乎不可行。这就是我们需要SymPy的原因——实现动态、精准的自动化符号计算。
SymPy能完美处理符号运算,其核心函数包括:
diff(f, x):自动求导函数limit(expr, h, 0):计算极限值以下代码展示了SymPy的强大功能:
from sympy import symbols, diff, limit# 定义符号变量
x = symbols('x')# 定义函数
f = x**3 - 2*x + 1# 自动求导
f_prime = diff(f, x)
print(f"导函数 f'(x) = {f_prime}") # 输出: 3*x**2 - 2# 计算x=1处的导数
slope_at_1 = f_prime.subs(x, 1)
print(f"x=1处斜率 = {slope_at_1}") # 输出: 1# 验证极限
secant_slope_expr = (f.subs(x, 1+h) - f.subs(x, 1)) / h
limit_slope = limit(secant_slope_expr, h, 0)
print(f"极限斜率 = {limit_slope}") # 输出: 1
将SymPy集成到Manim动画中,使用ValueTracker控制h值的变化:
from manim import *
from sympy import symbols, lambdify, diffclass DerivativeAnimation(Scene):
def construct(self):
# SymPy计算部分
x_sym = symbols("x")
f_sym = x_sym**3 - 2*x_sym + 1
f = lambdify(x_sym, f_sym, "numpy")
f_prime_sym = diff(f_sym, x_sym)
exact_k = float(f_prime_sym.subs(x_sym, 1)) # 精确导数 # Manim可视化
ax = Axes(x_range=[-2, 3], y_range=[-3, 5])
graph = ax.plot(f, color=YELLOW)
p_point = Dot(ax.c2p(1, f(1)), color=RED) # 动态割线
h_tracker = ValueTracker(1)
def get_secant_line():
h_val = h_tracker.get_value()
x_q = 1 + h_val
k = (f(x_q) - f(1)) / h_val
return ax.plot(
lambda x: k * (x - 1) + f(1),
color=GREEN, x_range=[0, x_q + 1]
) secant_line = always_redraw(get_secant_line) # 精确切线
tangent_line = ax.plot(
lambda x: exact_k * (x - 1) + f(1),
color=PURPLE, x_range=[-0.5, 2.5]
) # 动画流程
self.play(Create(ax), Create(graph), Create(p_point))
self.play(Create(secant_line))
self.play(
h_tracker.animate.set_value(0.001),
run_time=5,
rate_func=rate_functions.ease_in_out_quad
)
self.play(Create(tang