uniform_continuity.py
1from manim import *2import numpy as np3 4class UniformContinuityDemo(Scene):5 def construct(self):6 # 添加标题7 title = Text("函数的一致连续性", font="SimSun", font_size=36)8 title.to_edge(UP)9 self.play(Write(title))10 self.wait(1)11 12 # 创建坐标系13 axes = Axes(14 x_range=[0, 3],15 y_range=[0, 2],16 axis_config={"color": GREY},17 x_length=10,18 y_length=619 ).add_coordinates()20 21 # 添加坐标轴标签22 labels = axes.get_axis_labels(x_label="x", y_label="y")23 24 self.play(Create(axes), Write(labels))25 self.wait(1)26 27 # 创建函数图像 f(x) = sin(x)28 def f(x): return np.sin(x)29 graph = axes.plot(f, color=BLUE)30 graph_label = MathTex(r"f(x)=\sin(x)").next_to(graph, UR)31 32 self.play(Create(graph), Write(graph_label))33 self.wait(1)34 35 # 创建两个点和它们的垂直线36 x1, x2 = 0.5, 1.237 point1 = Dot(axes.c2p(x1, f(x1)), color=RED)38 point2 = Dot(axes.c2p(x2, f(x2)), color=RED)39 40 v_line1 = axes.get_vertical_line(axes.c2p(x1, f(x1)), color=YELLOW)41 v_line2 = axes.get_vertical_line(axes.c2p(x2, f(x2)), color=YELLOW)42 43 # 添加x轴上的点44 x1_dot = Dot(axes.c2p(x1, 0), color=RED)45 x2_dot = Dot(axes.c2p(x2, 0), color=RED)46 47 # 创建水平和垂直距离的标注48 x_brace = BraceBetweenPoints(49 axes.c2p(x1, 0),50 axes.c2p(x2, 0),51 color=GREEN,52 direction=DOWN53 )54 x_text = MathTex(r"|x_1-x_2|<\delta", color=GREEN).next_to(x_brace, DOWN)55 56 y_brace = BraceBetweenPoints(57 axes.c2p(x2, f(x1)),58 axes.c2p(x2, f(x2)),59 color=BLUE,60 direction=RIGHT61 )62 y_text = MathTex(r"|f(x_1)-f(x_2)|<\epsilon", color=BLUE).next_to(y_brace, RIGHT)63 64 # 添加两点之间的连线和距离标注65 line_between_points = DashedLine(66 axes.c2p(x1, f(x1)),67 axes.c2p(x2, f(x2)),68 color=YELLOW69 )70 71 # 计算两点之间的实际距离72 distance_text = MathTex(73 r"\sqrt{(x_1-x_2)^2 + (f(x_1)-f(x_2))^2}",74 color=YELLOW,75 font_size=2476 ).next_to(line_between_points.get_center(), UP, buff=0.2)77 78 # 显示点和线79 self.play(80 Create(point1),81 Create(point2),82 Create(x1_dot),83 Create(x2_dot),84 Create(v_line1),85 Create(v_line2),86 Create(line_between_points),87 Write(distance_text)88 )89 self.wait(1)90 91 # 显示距离标注92 self.play(93 Create(x_brace),94 Write(x_text)95 )96 self.play(97 Create(y_brace),98 Write(y_text)99 )100 self.wait(1)101 102 # 创建一致连续性的定义103 definition = VGroup(104 Text("一致连续性定义:", font="SimSun", font_size=24),105 MathTex(r"\forall \epsilon > 0, \exists \delta > 0, \forall x_1,x_2 \in D:"),106 MathTex(r"|x_1-x_2|<\delta \Rightarrow |f(x_1)-f(x_2)|<\epsilon")107 ).arrange(DOWN, aligned_edge=LEFT).to_corner(DL)108 109 self.play(Write(definition))110 self.wait(1)111 112 # 动画演示:移动点并保持距离关系113 def update_points(t):114 new_x1 = 0.5 + 0.8*t # 减小移动距离115 new_x2 = 1.2 + 0.8*t - 0.9*t # 增加收敛速度,确保最终在ε带内116 117 # 更新点的位置118 point1.move_to(axes.c2p(new_x1, f(new_x1)))119 point2.move_to(axes.c2p(new_x2, f(new_x2)))120 x1_dot.move_to(axes.c2p(new_x1, 0))121 x2_dot.move_to(axes.c2p(new_x2, 0))122 123 # 更新垂直线124 v_line1.become(axes.get_vertical_line(axes.c2p(new_x1, f(new_x1)), color=YELLOW))125 v_line2.become(axes.get_vertical_line(axes.c2p(new_x2, f(new_x2)), color=YELLOW))126 127 # 更新距离标注位置128 distance_text.next_to(line_between_points.get_center(), UP, buff=0.2)129 130 # 更新距离标注131 x_brace.become(BraceBetweenPoints(132 axes.c2p(new_x1, 0),133 axes.c2p(new_x2, 0),134 color=GREEN,135 direction=DOWN136 ))137 y_brace.become(BraceBetweenPoints(138 axes.c2p(new_x2, f(new_x1)),139 axes.c2p(new_x2, f(new_x2)),140 color=BLUE,141 direction=RIGHT142 ))143 144 # 创建动画145 self.play(146 UpdateFromAlphaFunc(147 VGroup(148 point1, point2, x1_dot, x2_dot, 149 v_line1, v_line2, x_brace, y_brace,150 line_between_points, distance_text151 ),152 lambda mob, alpha: update_points(1.5 * alpha)153 ),154 run_time=3,155 rate_func=there_and_back156 )157 self.wait(2)158 159class UniformContinuityComparison(Scene):160 def construct(self):161 # 添加标题162 title = Text("一致连续与非一致连续函数对比", font="SimSun", font_size=36)163 title.to_edge(UP)164 self.play(Write(title))165 self.wait(1)166 167 # 创建两个子坐标系168 left_axes = Axes(169 x_range=[0, 3],170 y_range=[0, 2],171 axis_config={"color": GREY},172 x_length=5,173 y_length=4174 ).shift(LEFT * 3.5)175 176 right_axes = Axes(177 x_range=[0.2, 3],178 y_range=[0, 5],179 axis_config={"color": GREY},180 x_length=5,181 y_length=4182 ).shift(RIGHT * 3.5)183 184 # 添加坐标轴标签185 left_labels = left_axes.get_axis_labels(x_label="x", y_label="y")186 right_labels = right_axes.get_axis_labels(x_label="x", y_label="y")187 188 self.play(189 Create(left_axes), Write(left_labels),190 Create(right_axes), Write(right_labels)191 )192 self.wait(1)193 194 # 创建函数图像195 def f1(x): return np.sin(x) # 一致连续函数196 def f2(x): return 1/x # 非一致连续函数197 198 graph1 = left_axes.plot(f1, color=BLUE)199 graph2 = right_axes.plot(f2, color=RED, x_range=[0.2, 3])200 201 graph1_label = MathTex(r"f(x)=\sin(x)", color=BLUE).next_to(left_axes, UP)202 graph2_label = MathTex(r"g(x)=\frac{1}{x}", color=RED).next_to(right_axes, UP)203 204 self.play(205 Create(graph1), Write(graph1_label),206 Create(graph2), Write(graph2_label)207 )208 self.wait(1)209 210 # 在左图(sin函数)上创建两个点和垂直线211 x1, x2 = 0.5, 1.2212 left_point1 = Dot(left_axes.c2p(x1, f1(x1)), color=BLUE)213 left_point2 = Dot(left_axes.c2p(x2, f1(x2)), color=BLUE)214 215 # 添加垂直辅助线216 left_v_line1 = left_axes.get_vertical_line(left_axes.c2p(x1, f1(x1)), color=YELLOW)217 left_v_line2 = left_axes.get_vertical_line(left_axes.c2p(x2, f1(x2)), color=YELLOW)218 219 # 修改左图的标注220 left_delta = BraceBetweenPoints(221 left_axes.c2p(x1, 0),222 left_axes.c2p(x2, 0),223 color=GREEN,224 direction=DOWN225 )226 left_delta_text = MathTex(r"\delta", color=GREEN).next_to(left_delta, DOWN)227 228 # 修改垂直距离标注,使用函数值差异229 left_v_brace = BraceBetweenPoints(230 left_axes.c2p(x2, f1(x1)),231 left_axes.c2p(x2, f1(x2)),232 color=BLUE,233 direction=RIGHT234 )235 left_v_text = MathTex(r"f(x_1)-f(x_2)", color=BLUE, font_size=24).next_to(236 left_v_brace, RIGHT, buff=0.1237 )238 239 # 在右图(1/x函数)上创建两个点和垂直线240 x3, x4 = 0.5, 0.8241 right_point1 = Dot(right_axes.c2p(x3, f2(x3)), color=RED)242 right_point2 = Dot(right_axes.c2p(x4, f2(x4)), color=RED)243 244 # 添加垂直辅助线245 right_v_line1 = right_axes.get_vertical_line(right_axes.c2p(x3, f2(x3)), color=YELLOW)246 right_v_line2 = right_axes.get_vertical_line(right_axes.c2p(x4, f2(x4)), color=YELLOW)247 248 # 修改右图的标注249 right_delta = BraceBetweenPoints(250 right_axes.c2p(x3, 0),251 right_axes.c2p(x4, 0),252 color=GREEN,253 direction=DOWN254 )255 right_delta_text = MathTex(r"\delta", color=GREEN).next_to(right_delta, DOWN)256 257 # 修改垂直距离标注,使用函数值差异258 right_v_brace = BraceBetweenPoints(259 right_axes.c2p(x4, f2(x3)),260 right_axes.c2p(x4, f2(x4)),261 color=BLUE,262 direction=RIGHT263 )264 right_v_text = MathTex(r"g(x_1)-g(x_2)", color=BLUE, font_size=24).next_to(265 right_v_brace, RIGHT, buff=0.1266 )267 268 # 显示点、线和标注269 self.play(270 Create(VGroup(271 left_point1, left_point2, 272 left_v_line1, left_v_line2273 )),274 Create(VGroup(275 right_point1, right_point2,276 right_v_line1, right_v_line2277 ))278 )279 self.play(280 Create(left_delta), Write(left_delta_text),281 Create(left_v_brace), Write(left_v_text),282 Create(right_delta), Write(right_delta_text),283 Create(right_v_brace), Write(right_v_text)284 )285 self.wait(1)286 287 # 添加固定的ε水平线(左图)288 epsilon_value = 0.2 # 设置一个固定的ε值289 left_epsilon_line1 = DashedLine(290 left_axes.c2p(0, 0.8), # 降低水平线的位置291 left_axes.c2p(3, 0.8),292 color=BLUE_A293 )294 left_epsilon_line2 = DashedLine(295 left_axes.c2p(0, 0.8 + epsilon_value),296 left_axes.c2p(3, 0.8 + epsilon_value),297 color=BLUE_A298 )299 left_epsilon_brace = BraceBetweenPoints(300 left_axes.c2p(2.8, 0.8),301 left_axes.c2p(2.8, 0.8 + epsilon_value),302 color=BLUE_A,303 direction=RIGHT304 )305 left_epsilon_text = MathTex(r"\epsilon", color=BLUE_A).next_to(left_epsilon_brace, RIGHT)306 307 # 添加固定的ε水平线(右图)308 right_epsilon_line1 = DashedLine(309 right_axes.c2p(0.2, 1.5), # 调整右图水平线位置310 right_axes.c2p(3, 1.5),311 color=BLUE_A312 )313 right_epsilon_line2 = DashedLine(314 right_axes.c2p(0.2, 1.5 + epsilon_value),315 right_axes.c2p(3, 1.5 + epsilon_value),316 color=BLUE_A317 )318 right_epsilon_brace = BraceBetweenPoints(319 right_axes.c2p(2.8, 1.5),320 right_axes.c2p(2.8, 1.5 + epsilon_value),321 color=BLUE_A,322 direction=RIGHT323 )324 right_epsilon_text = MathTex(r"\epsilon", color=BLUE_A).next_to(right_epsilon_brace, RIGHT)325 326 # 显示ε线和标注327 self.play(328 Create(left_epsilon_line1), Create(left_epsilon_line2),329 Create(left_epsilon_brace), Write(left_epsilon_text),330 Create(right_epsilon_line1), Create(right_epsilon_line2),331 Create(right_epsilon_brace), Write(right_epsilon_text)332 )333 self.wait(1)334 335 # 修改动画更新函数336 def update_points(t):337 # 更新左侧点(sin函数)- 让两点最终重合338 new_x1 = 0.5 + 0.8*t # 第一个点向右移动339 new_x2 = new_x1 + 0.7*(1-t) # 第二个点逐渐靠近第一个点,最后重合340 341 left_point1.move_to(left_axes.c2p(new_x1, f1(new_x1)))342 left_point2.move_to(left_axes.c2p(new_x2, f1(new_x2)))343 344 # 更新左侧垂直线和δ标注345 left_v_line1.become(left_axes.get_vertical_line(left_axes.c2p(new_x1, f1(new_x1)), color=YELLOW))346 left_v_line2.become(left_axes.get_vertical_line(left_axes.c2p(new_x2, f1(new_x2)), color=YELLOW))347 348 left_delta.become(BraceBetweenPoints(349 left_axes.c2p(new_x1, 0),350 left_axes.c2p(new_x2, 0),351 color=GREEN,352 direction=DOWN353 ))354 left_delta_text.next_to(left_delta, DOWN)355 356 # 更新左侧垂直花括号和标注357 left_v_brace.become(BraceBetweenPoints(358 left_axes.c2p(new_x2, f1(new_x1)),359 left_axes.c2p(new_x2, f1(new_x2)),360 color=BLUE,361 direction=RIGHT362 ))363 left_v_text.next_to(left_v_brace, RIGHT, buff=0.1)364 365 # 更新右侧点(1/x函数)366 new_x3 = 0.5 - 0.25*t # 第一个点向左移动367 new_x4 = 0.8 - 0.45*t # 第二个点移动更快,使得两点逐渐靠近368 369 # 确保x值不会太接近0370 new_x3 = max(0.2, new_x3)371 new_x4 = max(0.25, new_x4)372 373 right_point1.move_to(right_axes.c2p(new_x3, f2(new_x3)))374 right_point2.move_to(right_axes.c2p(new_x4, f2(new_x4)))375 376 # 更新右侧垂直线377 right_v_line1.become(right_axes.get_vertical_line(right_axes.c2p(new_x3, f2(new_x3)), color=YELLOW))378 right_v_line2.become(right_axes.get_vertical_line(right_axes.c2p(new_x4, f2(new_x4)), color=YELLOW))379 380 # 更新右侧δ标注381 right_delta.become(BraceBetweenPoints(382 right_axes.c2p(new_x3, 0),383 right_axes.c2p(new_x4, 0),384 color=GREEN,385 direction=DOWN386 ))387 right_delta_text.next_to(right_delta, DOWN)388 389 # 更新右侧垂直花括号和函数值差标注390 right_v_brace.become(BraceBetweenPoints(391 right_axes.c2p(new_x4, f2(new_x3)),392 right_axes.c2p(new_x4, f2(new_x4)),393 color=BLUE,394 direction=RIGHT395 ))396 right_v_text.next_to(right_v_brace, RIGHT, buff=0.1)397 398 # 更新右侧水平虚线位置399 min_y = min(f2(new_x3), f2(new_x4)) # 获取较小的y值400 right_epsilon_line1.become(DashedLine(401 right_axes.c2p(0.2, min_y),402 right_axes.c2p(3, min_y),403 color=BLUE_A404 ))405 right_epsilon_line2.become(DashedLine(406 right_axes.c2p(0.2, min_y + epsilon_value),407 right_axes.c2p(3, min_y + epsilon_value),408 color=BLUE_A409 ))410 right_epsilon_brace.become(BraceBetweenPoints(411 right_axes.c2p(2.8, min_y),412 right_axes.c2p(2.8, min_y + epsilon_value),413 color=BLUE_A,414 direction=RIGHT415 ))416 right_epsilon_text.next_to(right_epsilon_brace, RIGHT)417 418 # 修改动画组,添加右侧水平虚线到更新组419 self.play(420 UpdateFromAlphaFunc(421 VGroup(422 left_point1, left_point2,423 left_v_line1, left_v_line2,424 left_delta, left_delta_text,425 left_v_brace, left_v_text,426 right_point1, right_point2,427 right_v_line1, right_v_line2,428 right_delta, right_delta_text,429 right_v_brace, right_v_text,430 right_epsilon_line1, right_epsilon_line2, # 添加右侧水平虚线431 right_epsilon_brace, right_epsilon_text # 添加右侧epsilon标注432 ),433 lambda mob, alpha: update_points(alpha)434 ),435 run_time=4,436 rate_func=linear437 )438 439 # 修改结论文字和位置440 conclusion = Text(441 "一致连续函数中,对于任意给定的ε,总能找到足够小的δ,\n" + 442 "使得任意两点距离小于δ时,函数值之差小于ε;而非一致连续函数在某些区域无法做到这一点。",443 font="SimSun",444 font_size=18, # 减小字体445 line_spacing=1.2446 ).next_to(447 VGroup(left_axes, right_axes), # 相对于两个坐标系448 DOWN, # 在坐标系下方449 buff=1.0 # 增加与坐标系的距离450 )451 452 self.play(Write(conclusion))453 self.wait(2) 讲解
这个视频围绕「一致连续与非一致连续函数对比」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「函数的一致连续性」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「一致连续与非一致连续函数对比」中的核心对象,尤其留意一致连续、函数极限、epsilon-delta 定义之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从一致连续、函数极限、epsilon-delta 定义这些概念进入,继续沿相邻问题观察同一类数学结构。