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 ) 讲解
这个视频围绕「条件极值问题」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「条件极值问题」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「条件极值问题」中的核心对象,尤其留意条件极值、拉格朗日乘子、等高线之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从条件极值、拉格朗日乘子、等高线、多元函数极值这些概念进入,继续沿相邻问题观察同一类数学结构。