数学分析基础可视化

一致连续与非一致连续函数对比

围绕一致连续与非一致连续函数对比,观察一致连续、函数极限、epsilon-delta 定义之间的关系与推理路径。

打开原视频

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)

讲解

这个视频围绕「一致连续与非一致连续函数对比」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。

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

画面中出现的文字「函数的一致连续性」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。

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

核心公式可以写成:

f(x)=sin(x)f(x)=\sin(x)

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

观察路径

观察路径可以分三步:先锁定「一致连续与非一致连续函数对比」中的核心对象,尤其留意一致连续、函数极限、epsilon-delta 定义之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。

本页可以从一致连续、函数极限、epsilon-delta 定义这些概念进入,继续沿相邻问题观察同一类数学结构。