让数学公式自动推导

我们在做数学公式推导的视频时,比如,想展示一个因式分解: <math xmlns="http://www.w3.org/1998/Math/MathML"> x 2 + 2 x + 1 x^2+2x+1 </math>x2+2x+1 变成 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x + 1 ) 2 (x+1)^2 </math>(x+1)2 的,或者反过来因式展开,都需要手动计算分解或展开后的结果。

然后再做一个切换动画。

python 复制代码
expr1 = MathTex("x^2 + 2x + 1")
expr2 = MathTex("(x + 1)^2")
self.play(Transform(expr1, expr2))

简单的公式可以手动去做,如果公式稍微复杂呢?比如:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> x 3 − 6 x 2 + 11 x − 6 x^3-6x^2+11x-6 </math>x3−6x2+11x−6 可分解成: <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x − 1 ) ( x − 2 ) ( x − 3 ) (x-1)(x-2)(x-3) </math>(x−1)(x−2)(x−3)
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 x 3 + 4 x 2 − 30 x 2x^3 + 4x^2-30x </math>2x3+4x2−30x 可分解成: <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 x ( x − 3 ) ( x + 5 ) 2x(x-3)(x+5) </math>2x(x−3)(x+5)
  • ... ...

这几个公式就不是那么容易分解了,我们制作公式变换的数学动画,或者绘制函数图像时,肯定不想把时间花在这些数学推导上。

那么,把 SymPy 引进了 Manim 的动画制作中------ 一切都变了。

1. 痛点还原:手动推导

假设我们要展示从 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x + 2 ) ( x − 3 ) (x+2)(x-3) </math>(x+2)(x−3) 展开到 <math xmlns="http://www.w3.org/1998/Math/MathML"> x 2 − x − 6 x^2-x-6 </math>x2−x−6 的过程。

传统做法会这样写:

python 复制代码
# 原始因式
expr_origin = MathTex("(x + 2)(x - 3)")

# 手动计算中间步骤(心算 or 草稿纸)
# ... ...

# 合并同类项
expr_result = MathTex("x^2 - x - 6")

# 然后动画切换切换...
self.play(Transform(expr_origin, expr_result))

问题显而易见:

  • 容易算错(特别是系数复杂时)
  • 无法自动化(换个新公式就得重写代码)
  • 缺乏动态生成的灵活性

我们真正需要的 是一个能自动处理代数变形,并且把结果无缝喂给 Manim 的管道。

2. SymPy 登场:代数变形的"发动机"

SymPyPython 的符号计算库,简单说就是能让计算机帮你推导数学公式

核心武器:expand()factor()

python 复制代码
from sympy import symbols, expand, factor, latex

x = symbols("x")

# 原始表达式:因式形式
expr_factored = (x + 2) * (x - 3)

# 一键展开!
expr_expanded = expand(expr_factored)
print(expr_expanded)  # 输出:x**2 - x - 6

# 再一键因式分解!
expr_back = factor(expr_expanded)
print(expr_back)  # 输出:(x - 3)*(x + 2)

expand() 方法可以帮助我们将因式展开;

factor() 方法则是分解因式。

SymPy 不仅能返回计算结果,还能生成 LaTeX 字符串 ,这正是 Manim 渲染公式需要的格式:

python 复制代码
from sympy import latex

latex_code = latex(expr_expanded)
print(latex_code)  # 输出:x^{2} - x - 6

有了这个"发动机",我们只需要告诉 SymPy 起点和终点,中间的所有代数变形它都能自动计算。

3. Manim 联动实战:让公式自动推导

下面通过一个曲线绘制的示例来演示 SymPy 如何让 Manim 动画更加简单。

为了能够绘制曲线的方法能够通用,我们封装几个函数:

python 复制代码
from sympy import symbols, sympify, solve, Eq, latex, lambdify

def plot_function_curve(axes, equation_str, x_range, plot_range):
    """
    绘制函数曲线

    Args:
        axes: 坐标系对象
        equation_str: 方程字符串,如 "x**2 - x - 6"
        x_range: 绘图 x 范围,如 [-3.5, 4.5]
        plot_range: 用于 sympy 求解的 x 范围

    Returns:
        graph: 曲线对象
        graph_label: 曲线标签
    """
    x = symbols("x")
    expr = sympify(equation_str)

    # 创建可调用函数
    f = lambdify(x, expr, modules="numpy")

    # 绘制曲线
    graph = axes.plot(f, x_range=x_range, color=YELLOW, stroke_width=3)

    # 创建标签
    graph_label = axes.get_graph_label(
        graph, label=f"f(x)={latex(expr)}", x_val=x_range[1], direction=UP * 3
    )

    return graph, graph_label

def find_roots(equation_str):
    """
    使用 SymPy 求解方程的根

    Args:
        equation_str: 方程字符串,如 "x**2 - x - 6"

    Returns:
        roots: 实数根列表
    """
    x = symbols("x")
    expr = sympify(equation_str)

    # 求解方程 f(x) = 0
    solutions = solve(Eq(expr, 0), x)

    # 只保留实数根
    roots = []
    for sol in solutions:
        if sol.is_real:
            roots.append(float(sol))

    return roots


def create_root_markers(axes, roots):
    """
    创建零点标记和标签

    Args:
        axes: 坐标系对象
        roots: 根的列表

    Returns:
        root_dots: 零点标记组
        root_labels: 零点标签组
    """
    root_dots = VGroup()
    root_labels = VGroup()

    for rx in roots:
        # 在零点处画点
        dot = Dot(axes.c2p(rx, 0), color=RED, radius=0.1)
        # 添加坐标标签
        label = MathTex(f"({rx}, 0)", font_size=30, color=RED).next_to(dot, UP * 0.5)
        root_dots.add(dot)
        root_labels.add(label)

    return root_dots, root_labels

其中:

  1. plot_function_curve:根据曲线方程的字符串(比如"x**2 - x - 6")来绘制曲线
  2. find_roots:求解曲线方程的根,也就是和曲线和 X 轴的交点
  3. create_root_markers:把曲线方程的根标记出来

有了上面封装的函数,绘制一个曲线的动画代码就非常简洁了。

python 复制代码
class PlotQuadraticFunction(Scene):
    def construct(self):
        # 1. 创建坐标系
        axes = Axes(
            x_range=[-4, 5, 1],  # x 轴范围:-4 到 5,步长 1
            y_range=[-8, 10, 2],  # y 轴范围:-8 到 10,步长 2
            x_length=8,
            y_length=6,
            axis_config={"color": BLUE, "include_numbers": True, "font_size": 24},
            x_axis_config={"numbers_to_include": range(-4, 6)},
            y_axis_config={"numbers_to_include": range(-8, 11, 2)},
        )
        axes_labels = axes.get_axis_labels(x_label="x", y_label="f(x)")

        # 定义方程
        equation = "x**2 - x - 6"

        # 绘制函数图像
        graph, graph_label = plot_function_curve(
            axes, equation, x_range=[-3.5, 4.5], plot_range=[-4, 5]
        )

        # 使用 SymPy 求解零点
        roots = find_roots(equation)

        # 创建零点标记
        root_dots, root_labels = create_root_markers(axes, roots)

        # 动画展示
        self.play(Create(axes), Write(axes_labels))
        self.wait()

        self.play(Create(graph), Write(graph_label))
        self.wait()

        self.play(
            LaggedStart(
                *[Create(dot) for dot in root_dots],
                *[Write(label) for label in root_labels],
                lag_ratio=0.3,
            )
        )
        self.wait()

上面的代码看着长,其实关键的就一行:equation = "x**2 - x - 6"

其他部分都是渲染坐标系,创建各种元素的动画等等。

生成的效果如下:

使用 SymPy 中核心的几个方法如下:

  • sympify:比如,expr = sympify(equation_str)将曲线方程的字符串转换成 SymPy 中的符号公式
  • lambdifySymPy 中的符号公式转换为 Python 函数(供 Manim 绘制曲线用)
  • latex:比如,latex(expr),将SymPy 中的符号公式转换为 Manim 中显示公式的 Latex 格式
  • solvesolve(Eq(expr, 0), x),求解方程的根,也就是动画中标记的与 X 轴的交点

只从上面示例中的代码,可能还看不出来上 SymPyManim 结合的威力。

这时候,如何我们还想绘制另一个曲线, <math xmlns="http://www.w3.org/1998/Math/MathML"> x 3 − 6 x 2 + 11 x − 6 x^3-6x^2+11x-6 </math>x3−6x2+11x−6,那么,只要改一行代码就行了。

equation = "x**2 - x - 6" 改成 "x**3 - 6*(x**2) +11*x - 6"

这里显示的有点重叠,我们可以根据曲线显示的情况,调整下坐标系或曲线参数的范围。

python 复制代码
axes = Axes(
    x_range=[-2, 5, 1], 
    # ...
    x_axis_config={"numbers_to_include": range(-1, 6)},
    # ...
)

# 绘制函数图像
graph, graph_label = plot_function_curve(
    axes, equation, x_range=[-1, 4.5], plot_range=[-1, 5] # 调整了这里的范围
)

4. 小结

本文主要内容如下:

  • 痛点认知:手动推导公式既低效又容易出错。
  • SymPy 核心技能expand() 展开、factor() 因式分解、latex() 转换。
  • Manim 联动套路SymPy 算 → LaTeX 转 → Manim 渲染。

任何代数方程的因式变化曲线函数 都可以自动化生成,我们可以把精力集中在动画节奏和视觉设计上。

相关推荐
m0_733565461 小时前
如何指定PHP版本运行phpMyAdmin_多版本共存配置
jvm·数据库·python
love530love2 小时前
ComfyUI MediaPipe 猴子补丁终极完善版:补全上下文管理与姿态检测兼容
人工智能·windows·python·comfyui·protobuf·mediapipe
小小编程路2 小时前
新手快速学 Python 极简速成指南
开发语言·c++·python
小马过河R2 小时前
RAG检索优化策略:系统性四层框架解析
人工智能·python·算法·ai·llm·rag·问答
yzx9910132 小时前
脚本定制从入门到实践:打造你的专属浏览器助手
python
AI技术控2 小时前
论文解读:AE-TCN-SA——基于自编码器、TCN 与自注意力机制的锂电池内短路诊断方法
人工智能·python·深度学习·算法·机器学习·自然语言处理
向日的葵0062 小时前
阿里云OSS从0到1实战:为宠物收养系统打造图片上传功能
python·阿里云·云计算·pillow·fastapi·宠物
川冰ICE3 小时前
Python爬虫实战⑳|Pandas时间序列,趋势分析一网打尽
爬虫·python·pandas
金融大 k3 小时前
多市场行情时间戳对齐:UTC 存储的夏令时陷阱与数据库设计方案
python·websocket·行情数据