多元微积分可视化

条件极值问题

围绕条件极值问题,观察条件极值、拉格朗日乘子、等高线之间的关系与推理路径。

打开原视频

conditional_extrema_demo.py
1from manim import *2import numpy as np3 4class ConditionalExtremaDemo(ThreeDScene):5    def construct(self):6        # 设置3D场景7        self.set_camera_orientation(phi=70 * DEGREES, theta=30 * DEGREES)8        9        # 创建标题10        title = Text("条件极值问题", font="PingFang SC", font_size=32)11        title.to_corner(UL, buff=0.5)12        self.add_fixed_in_frame_mobjects(title)13        self.play(Write(title))14        self.wait()15 16        # 创建3D坐标系17        axes = ThreeDAxes(18            x_range=[-3, 3, 1],19            y_range=[-3, 3, 1],20            z_range=[-3, 3, 1],21            axis_config={"color": GRAY}22        )23        self.add(axes)24        self.wait()25 26        # 定义目标函数 f(x,y) = x^2 + y^227        def f(x, y):28            return x**2 + y**229 30        # 定义约束条件 g(x,y) = x^2 + y^2/2 - 1 = 031        def g(x, y):32            return x**2 + y**2/2 - 133 34        # 创建目标函数的曲面35        surface = Surface(36            lambda u, v: np.array([37                u,38                v,39                f(u, v)40            ]),41            u_range=[-2, 2],42            v_range=[-2, 2],43            checkerboard_colors=[BLUE_D, BLUE_E],44            resolution=(30, 30)45        )46        self.play(Create(surface))47        self.wait()48 49        # 创建约束条件的椭圆50        constraint_ellipse = ParametricFunction(51            lambda t: np.array([52                np.cos(t),53                np.sqrt(2) * np.sin(t),54                055            ]),56            t_range=[0, 2*PI],57            color=YELLOW58        )59        self.play(Create(constraint_ellipse))60        self.wait()61 62        # 显示目标函数和约束条件63        equations = VGroup(64            Text("目标函数:", font="PingFang SC", color=WHITE, font_size=24),65            MathTex(r"f(x,y) = x^2 + y^2", color=WHITE, font_size=24),66            Text("约束条件:", font="PingFang SC", color=YELLOW, font_size=24),67            MathTex(r"g(x,y) = x^2 + \frac{y^2}{2} - 1 = 0", color=YELLOW, font_size=24)68        )69        equations.arrange(DOWN, aligned_edge=LEFT, buff=0.2)70        equations.to_corner(UR, buff=0.5)71        self.add_fixed_in_frame_mobjects(equations)72        self.play(Write(equations))73        self.wait()74 75        # 创建等高线76        contour_levels = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5]77        contours = VGroup()78        for level in contour_levels:79            contour = ParametricFunction(80                lambda t: np.array([81                    np.sqrt(level) * np.cos(t),82                    np.sqrt(level) * np.sin(t),83                    084                ]),85                t_range=[0, 2*PI],86                color=BLUE_D87            )88            contours.add(contour)89        90        # 添加等高线说明91        contour_explanation = Text(92            "等高线",93            font="PingFang SC",94            color=BLUE_D,95            font_size=2096        )97        contour_explanation.next_to(contours, RIGHT, buff=0.5)98        self.add_fixed_in_frame_mobjects(contour_explanation)99        self.play(Write(contour_explanation))100        self.wait()101        102        self.play(Create(contours))103        self.wait()104 105        # 显示几个非极值点及其梯度106        non_extremum_points = [107            (-0.8, 0.4),  # 第一个点,在左下角108            (0.3, 1.2),  # 第二个点109            (-0.6, 0.8)  # 第三个点110        ]111 112        for x, y in non_extremum_points:113            # 创建点114            point = Dot3D(point=np.array([x, y, 0]), color=RED, radius=0.08)115            self.play(Create(point))116 117            # 计算梯度向量118            grad_f_vec = np.array([2*x, 2*y, 0])  # ∇f 的方向119            grad_g_vec = np.array([2*x, y, 0])    # ∇g 的方向120            121            # 归一化并缩放梯度向量122            scale = 1.0123            grad_f_vec = scale * grad_f_vec / np.linalg.norm(grad_f_vec)124            grad_g_vec = scale * grad_g_vec / np.linalg.norm(grad_g_vec)125 126            # 创建梯度箭头127            grad_f_arrow = Arrow(128                start=np.array([x, y, 0]),129                end=np.array([x + grad_f_vec[0], y + grad_f_vec[1], 0]),130                color=BLUE,131                buff=0.1,132                stroke_width=6,133                max_tip_length_to_length_ratio=0.2134            )135            grad_g_arrow = Arrow(136                start=np.array([x, y, 0]),137                end=np.array([x + grad_g_vec[0], y + grad_g_vec[1], 0]),138                color=YELLOW,139                buff=0.1,140                stroke_width=6,141                max_tip_length_to_length_ratio=0.2142            )143            self.play(Create(grad_f_arrow), Create(grad_g_arrow))144            self.wait()145 146        # 添加梯度方向的解释说明(移到左侧)147        gradient_explanation = VGroup(148            Text("蓝色箭头:目标函数f的梯度∇f", font="PingFang SC", color=BLUE, font_size=20),149            Text("黄色箭头:约束条件g的梯度∇g", font="PingFang SC", color=YELLOW, font_size=20)150        )151        gradient_explanation.arrange(DOWN, aligned_edge=LEFT, buff=0.2)152        gradient_explanation.to_corner(UL, buff=0.5)153        gradient_explanation.shift(DOWN * 1.5)  # 向下移动一点,避免与标题重叠154        self.add_fixed_in_frame_mobjects(gradient_explanation)155        self.play(Write(gradient_explanation))156        self.wait(2)157 158        # 显示梯度条件159        gradient_condition = VGroup(160            MathTex(r"\nabla f = \lambda \nabla g", color=GREEN, font_size=24),161            MathTex(r"(2x, 2y) = \lambda(2x, y)", color=GREEN, font_size=24)162        )163        gradient_condition.arrange(DOWN, aligned_edge=LEFT, buff=0.2)164        gradient_condition.next_to(equations, DOWN, buff=0.3)165        self.add_fixed_in_frame_mobjects(gradient_condition)166        self.play(Write(gradient_condition))167        self.wait()168 169        # 显示方程组170        system_eq = VGroup(171            MathTex(r"2x - 2\lambda x = 0", color=WHITE, font_size=24),172            MathTex(r"2y - \lambda y = 0", color=WHITE, font_size=24),173            MathTex(r"x^2 + \frac{y^2}{2} - 1 = 0", color=WHITE, font_size=24)174        )175        system_eq.arrange(DOWN, aligned_edge=LEFT, buff=0.2)176        system_eq.next_to(gradient_condition, DOWN, buff=0.3)177        self.add_fixed_in_frame_mobjects(system_eq)178        self.play(Write(system_eq))179        self.wait()180 181        # 显示解182        solution = VGroup(183            MathTex(r"x = 0, y = \pm\sqrt{2}", color=RED, font_size=24),184            MathTex(r"x = \pm 1, y = 0", color=RED, font_size=24)185        )186        solution.arrange(DOWN, aligned_edge=LEFT, buff=0.2)187        solution.next_to(system_eq, DOWN, buff=0.3)188        self.add_fixed_in_frame_mobjects(solution)189        self.play(Write(solution))190        self.wait()191 192        # 显示极值点193        extremum_points = [194            (0, np.sqrt(2)),    # 最大值点195            (0, -np.sqrt(2)),   # 最大值点196            (1, 0),             # 最小值点197            (-1, 0)             # 最小值点198        ]199        200        for x, y in extremum_points:201            point = Dot3D(point=np.array([x, y, 0]), color=RED, radius=0.1)202            self.play(Create(point))203            204            # 在极值点处显示梯度向量205            grad_f = Arrow(206                start=np.array([x, y, 0]),207                end=np.array([x + 2*x, y + 2*y, 0]),208                color=BLUE,209                buff=0.1,210                stroke_width=6,211                max_tip_length_to_length_ratio=0.2212            )213            grad_g = Arrow(214                start=np.array([x, y, 0]),215                end=np.array([x + 2*x, y + y, 0]),216                color=YELLOW,217                buff=0.1,218                stroke_width=6,219                max_tip_length_to_length_ratio=0.2220            )221            self.play(222                Create(grad_f),223                Create(grad_g),224                Flash(point, color=RED, flash_radius=0.5)225            )226            self.wait()227 228        # 显示极值点坐标229        extremum_coords = VGroup(230            VGroup(231                MathTex(r"(0, \sqrt{2})", color=RED, font_size=24),232                MathTex(r"(0, -\sqrt{2})", color=RED, font_size=24)233            ),234            VGroup(235                MathTex(r"(1, 0)", color=RED, font_size=24),236                MathTex(r"(-1, 0)", color=RED, font_size=24)237            )238        )239        # 设置每列内部的对齐方式240        extremum_coords[0].arrange(DOWN, aligned_edge=LEFT, buff=0.2)241        extremum_coords[1].arrange(DOWN, aligned_edge=LEFT, buff=0.2)242        # 设置两列之间的间距243        extremum_coords.arrange(RIGHT, aligned_edge=UP, buff=0.5)244        extremum_coords.next_to(solution, DOWN, buff=0.3)245        extremum_coords.shift(LEFT * 0.5)  # 向左移动一点246        self.add_fixed_in_frame_mobjects(extremum_coords)247        self.play(Write(extremum_coords))248        self.wait()249 250        # 添加极值点处的说明251        extremum_explanation = VGroup(252            Text("在极值点处,两个梯度方向相同", font="PingFang SC", color=RED, font_size=20),253            Text("(1,0)和(-1,0)是最小值点", font="PingFang SC", color=RED, font_size=20),254            Text("(0,±√2)是最大值点", font="PingFang SC", color=RED, font_size=20)255        )256        extremum_explanation.arrange(DOWN, aligned_edge=LEFT, buff=0.2)257        extremum_explanation.next_to(extremum_coords, DOWN, buff=0.3)258        extremum_explanation.shift(LEFT * 0.5)  # 向左移动一点259        self.add_fixed_in_frame_mobjects(extremum_explanation)260        self.play(Write(extremum_explanation))261        self.wait(2)262 263        # 总结(移到最底部)264        summary = VGroup(265            Text(266                "在极值点处,目标函数的梯度与约束条件的梯度平行",267                font="PingFang SC",268                color=WHITE,269                font_size=24270            ),271            Text(272                "这就是拉格朗日乘数法的几何意义",273                font="PingFang SC",274                color=WHITE,275                font_size=24276            )277        )278        summary.arrange(DOWN, aligned_edge=LEFT, buff=0.2)279        summary.to_edge(DOWN, buff=0.5)280        self.add_fixed_in_frame_mobjects(summary)281        self.play(Write(summary))282        self.wait(2)283 284        # 旋转视角以更好地观察285        self.move_camera(phi=45 * DEGREES, theta=60 * DEGREES, run_time=2)286        self.wait()287 288        # 开始环绕旋转289        self.begin_ambient_camera_rotation(rate=0.2)290        self.wait(4)291        self.stop_ambient_camera_rotation()292        self.wait()293 294        # 淡出所有元素295        self.play(296            *[FadeOut(mob) for mob in self.mobjects],297            run_time=2298        )

讲解

这个视频围绕「条件极值问题」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。

开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。

画面中出现的文字「条件极值问题」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。

随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。

核心公式可以写成:

f(x,y)=x2+y2f(x,y) = x^2 + y^2

这类公式可以和画面中的符号一一对应。

观察路径

观察路径可以分三步:先锁定「条件极值问题」中的核心对象,尤其留意条件极值、拉格朗日乘子、等高线之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。

本页可以从条件极值、拉格朗日乘子、等高线、多元函数极值这些概念进入,继续沿相邻问题观察同一类数学结构。