多元微积分可视化

二元函数极限演示

围绕二元函数极限演示,观察二元函数极限、路径极限、函数极限之间的关系与推理路径。

打开原视频

manim_function_limit_comparison.py
1#!/usr/bin/env python2# -*- coding: utf-8 -*-3 4"""5这个程序用Manim创建动画,对比一元函数和二元函数的概念及其极限6作者: Claude7"""8 9from manim import *10import numpy as np11 12# 配置中文支持13config.tex_template.add_to_preamble(r"\usepackage[UTF8]{ctex}")14 15# 定义全局变量A代表极限值116A = 117 18class OneVariableFunction(Scene):19    """演示一元函数及其极限"""20    21    def construct(self):22        # 标题23        title = Text("一元函数极限", font="SimSun")24        self.play(Write(title))25        self.wait()26        self.play(title.animate.scale(0.6).to_edge(UP))27        28        # 创建坐标系29        axes = Axes(30            x_range=[-5, 5, 1],31            y_range=[-1.5, 1.5, 0.5],32            axis_config={"include_tip": False},33        ).scale(0.8)34        35        # 坐标轴标签36        x_label = Text("x", font="SimSun").next_to(axes.x_axis.get_end(), RIGHT)37        y_label = Text("f(x)", font="SimSun").next_to(axes.y_axis.get_end(), UP)38        axes_labels = VGroup(x_label, y_label)39        40        # 函数标题41        func_title = Text("f(x) = sin(x)/x", font="SimSun", font_size=24)42        func_title.next_to(axes, UP)43        44        # 函数定义45        def f(x):46            return np.sin(x) / x if x != 0 else A47        48        # 创建函数图像49        graph = axes.plot(lambda x: f(x) if x != 0 else None, 50                          discontinuities=[0], color=BLUE)51        52        # 单点极限值53        limit_point = Dot(axes.c2p(0, A), color=RED)54        limit_label = Text("极限值 = A", font="SimSun", font_size=20, color=RED)55        limit_label.next_to(limit_point, UR, buff=0.1)56        57        # 显示所有元素58        self.play(59            Create(axes),60            Write(axes_labels),61            Write(func_title)62        )63        self.play(Create(graph))64        self.wait()65        66        # 动画演示点的趋近67        approach_dot = Dot(axes.c2p(3, f(3)), color=YELLOW)68        x_tracker = ValueTracker(3)69        70        approach_dot.add_updater(71            lambda d: d.move_to(axes.c2p(x_tracker.get_value(), 72                                         f(x_tracker.get_value())))73        )74        75        approach_arrow = Arrow(start=axes.c2p(3, 0), 76                              end=axes.c2p(3, f(3)), 77                              color=YELLOW, buff=0)78        approach_arrow.add_updater(79            lambda a: a.put_start_and_end_on(80                axes.c2p(x_tracker.get_value(), 0),81                axes.c2p(x_tracker.get_value(), f(x_tracker.get_value()))82            )83        )84        85        value_label = Text("", font="SimSun", font_size=18)86        value_label.add_updater(87            lambda t: t.become(88                Text(f"x = {x_tracker.get_value():.3f}\nf(x) = {f(x_tracker.get_value()):.3f}", 89                     font="SimSun", font_size=18)90            ).next_to(approach_dot, UR, buff=0.1)91        )92        93        self.play(Create(approach_dot), Create(approach_arrow), Create(value_label))94        95        # 从正向趋近96        self.play(x_tracker.animate.set_value(0.001), run_time=3)97        self.wait()98        99        # 从负向趋近100        self.play(x_tracker.animate.set_value(-3), run_time=1)101        self.play(x_tracker.animate.set_value(-0.001), run_time=3)102        self.wait()103        104        # 显示极限点和标签105        self.play(Create(limit_point), Write(limit_label))106        self.wait()107        108        # 清除更新器109        approach_dot.clear_updaters()110        approach_arrow.clear_updaters()111        value_label.clear_updaters()112        113        # 一元函数极限概念114        concept = Text(115            "一元函数极限:当x趋近于某个值a时,函数值f(x)趋近于的极限值L\n"116            "记为:lim(x→a) f(x) = L", 117            font="SimSun", font_size=24118        )119        concept.to_edge(DOWN, buff=0.5)120        121        self.play(Write(concept))122        self.wait(2)123        124        # 清场准备转入二元函数125        self.play(126            *[FadeOut(mob) for mob in self.mobjects]127        )128 129class TwoVariableFunction(ThreeDScene):130    """演示二元函数及其极限"""131    132    def construct(self):133        # 设置3D场景134        self.set_camera_orientation(phi=70 * DEGREES, theta=-30 * DEGREES)135        136        # 标题137        title = Text("二元函数极限", font="SimSun")138        title_mobject = self.add_fixed_in_frame_mobjects(title)139        title.to_corner(UL)140        self.play(Write(title))141        142        # 函数信息143        func_info = Text("f(x,y) = sin(√(x²+y²))/√(x²+y²)", font="SimSun", font_size=24)144        func_info.next_to(title, DOWN, aligned_edge=LEFT)145        self.add_fixed_in_frame_mobjects(func_info)146        self.play(Write(func_info))147        148        # 创建3D坐标系149        axes = ThreeDAxes(150            x_range=[-5, 5, 1],151            y_range=[-5, 5, 1],152            z_range=[-1, 1.5, 0.5],153        ).scale(0.7)154        155        # 添加轴标签156        x_label = Text("x", font="SimSun").next_to(axes.x_axis.get_end(), RIGHT)157        y_label = Text("y", font="SimSun").next_to(axes.y_axis.get_end(), RIGHT)158        z_label = Text("f(x,y)", font="SimSun").next_to(axes.z_axis.get_end(), UP)159        160        # 固定标签在帧中161        self.add_fixed_in_frame_mobjects(x_label, y_label, z_label)162        163        # 创建坐标系164        self.play(Create(axes))165        self.play(Write(VGroup(x_label, y_label, z_label)))166        167        # 函数定义168        def f(x, y):169            r = np.sqrt(x**2 + y**2)170            if r == 0:171                return A172            return np.sin(r) / r173        174        # 创建曲面175        surface = Surface(176            lambda u, v: axes.c2p(u, v, f(u, v)),177            u_range=[-4, 4],178            v_range=[-4, 4],179            resolution=(20, 20),180            checkerboard_colors=[BLUE_D, BLUE_E],181            fill_opacity=0.7182        )183        184        # 显示曲面185        self.play(Create(surface), run_time=2)186        self.wait(0.5)187        188        # 添加说明文字189        approach_text_3d = Text("自变量沿多条路径趋向于原点", font="SimSun", font_size=24)190        approach_text_3d.next_to(title, DOWN, buff=0.5)191        self.add_fixed_in_frame_mobjects(approach_text_3d)192        self.play(Write(approach_text_3d))193        self.wait(0.5)194        self.play(FadeOut(approach_text_3d))195        196        # 创建原点197        origin = Dot3D(axes.c2p(0, 0, A), color=RED)198        199        # 极限点200        limit_point = Dot3D(axes.c2p(0, 0, A), color=RED)201        limit_label = Text("极限值 = A", font="SimSun", color=RED)202        limit_label.next_to(limit_point, UP)203        self.add_fixed_in_frame_mobjects(limit_label)204        205        self.play(Create(limit_point))206        self.play(Write(limit_label))207        208        # 修改描述文本209        paths_description = Text("不同路径趋向同一极限值", font="SimSun", font_size=28, color=BLUE)210        paths_description.to_edge(UP)211        self.add_fixed_in_frame_mobjects(paths_description)212        self.play(Write(paths_description))213        214        # 添加并显示不同路径215        paths = VGroup()216        217        # 沿x轴趋近218        x_path = ParametricFunction(219            lambda t: axes.c2p(t, 0, f(t, 0)),220            t_range=[4, 0.01, -0.01],221            color=YELLOW222        )223        paths.add(x_path)224        225        # 沿y轴趋近226        y_path = ParametricFunction(227            lambda t: axes.c2p(0, t, f(0, t)),228            t_range=[4, 0.01, -0.01],229            color=GREEN230        )231        paths.add(y_path)232        233        # 沿直线y=x趋近234        xy_path = ParametricFunction(235            lambda t: axes.c2p(t, t, f(t, t)),236            t_range=[4, 0.01, -0.01],237            color=PURPLE238        )239        paths.add(xy_path)240        241        # 沿螺旋线趋近242        spiral_path = ParametricFunction(243            lambda t: axes.c2p(244                t * np.cos(2 * t), 245                t * np.sin(2 * t), 246                f(t * np.cos(2 * t), t * np.sin(2 * t))247            ),248            t_range=[4, 0.01, -0.01],249            color=ORANGE250        )251        paths.add(spiral_path)252        253        # 逐个显示点254        self.play(255            Create(x_path),256            Create(y_path),257            Create(xy_path),258            Create(spiral_path),259        )260        261        # 显示所有路径262        self.play(Create(paths), run_time=2)263        self.wait(1)264        265        # 显示极限点和标签266        limit_point_3d = Dot3D(axes.c2p(0, 0, A), color=RED, radius=0.1)267        limit_label_3d = Text("极限值=A", font="SimSun", font_size=20, color=RED)268        limit_label_3d.next_to(limit_point_3d, UP, buff=0.2)269        270        self.play(Create(limit_point_3d))271        self.add_fixed_in_frame_mobjects(limit_label_3d)272        self.play(Write(limit_label_3d))273        274        # 二元函数极限概念275        concept = Text(276            "二元函数极限:当点(x,y)沿任意路径趋近于点(a,b)时,\n"277            "若函数值f(x,y)都趋近于同一个值L,则L为函数在该点的极限\n"278            "记为:lim(x,y)→(a,b) f(x,y) = L", 279            font="SimSun", font_size=24280        )281        concept.to_edge(DOWN, buff=0.5)282        self.add_fixed_in_frame_mobjects(concept)283        284        self.play(Write(concept))285        self.wait(2)286 287class LimitDoesNotExist(ThreeDScene):288    """演示二元函数极限不存在的情况"""289    290    def construct(self):291        # 设置3D场景292        self.set_camera_orientation(phi=70 * DEGREES, theta=-30 * DEGREES)293        294        # 标题295        title = Text("二元函数极限不存在示例", font="SimSun")296        self.add_fixed_in_frame_mobjects(title)297        title.to_corner(UL)298        self.play(Write(title))299        300        # 函数信息301        func_info = Text("f(x,y) = xy/(x²+y²)", font="SimSun", font_size=24)302        func_info.next_to(title, DOWN, aligned_edge=LEFT)303        self.add_fixed_in_frame_mobjects(func_info)304        self.play(Write(func_info))305        306        # 创建3D坐标系307        axes = ThreeDAxes(308            x_range=[-5, 5, 1],309            y_range=[-5, 5, 1],310            z_range=[-1, 1, 0.5],311        ).scale(0.7)312        313        # 添加轴标签314        x_label = Text("x", font="SimSun").next_to(axes.x_axis.get_end(), RIGHT)315        y_label = Text("y", font="SimSun").next_to(axes.y_axis.get_end(), RIGHT)316        317        # 修改z轴标签位置,不再放在z轴的顶部318        z_label = Text("f(x,y)", font="SimSun", color=BLUE_B)319        z_label.to_edge(LEFT).shift(DOWN * 2)  # 放在左侧边缘位置,不会阻碍视线320        321        # 固定标签在帧中322        self.add_fixed_in_frame_mobjects(x_label, y_label, z_label)323        324        # 创建坐标系325        self.play(Create(axes))326        self.play(Write(VGroup(x_label, y_label, z_label)))327        328        # 函数定义329        def f(x, y):330            r_squared = x**2 + y**2331            if r_squared == 0:332                return A  # 函数在原点未定义,但这里设为A便于可视化333            return (x * y) / r_squared334        335        # 创建表面,去除原点周围以避免奇点336        resolution = 20  # 减小以提高性能337        surface = Surface(338            lambda u, v: axes.c2p(339                u, v, f(u, v)340            ),341            u_range=[-4, 4],342            v_range=[-4, 4],343            resolution=(resolution, resolution),344            checkerboard_colors=[BLUE_D, BLUE_E],345            fill_opacity=0.7346        )347        348        # 添加表面349        self.play(Create(surface), run_time=2)350        self.wait()351        352        # 添加从不同方向趋近的路径353        paths = VGroup()354        355        # 沿x轴趋近356        x_path = ParametricFunction(357            lambda t: axes.c2p(t, 0, f(t, 0)),358            t_range=[4, 0.01, -0.01],359            color=YELLOW360        )361        paths.add(x_path)362        x_path_label = Text("x轴路径: 极限值 = A", font="SimSun", font_size=20, color=YELLOW)363        x_path_label.to_edge(LEFT).shift(UP * 1.5)364        self.add_fixed_in_frame_mobjects(x_path_label)365        366        # 沿y轴趋近367        y_path = ParametricFunction(368            lambda t: axes.c2p(0, t, f(0, t)),369            t_range=[4, 0.01, -0.01],370            color=GREEN371        )372        paths.add(y_path)373        y_path_label = Text("y轴路径: 极限值 = A", font="SimSun", font_size=20, color=GREEN)374        y_path_label.next_to(x_path_label, DOWN, aligned_edge=LEFT)375        self.add_fixed_in_frame_mobjects(y_path_label)376        377        # 沿y=x直线趋近378        xy_path = ParametricFunction(379            lambda t: axes.c2p(t, t, f(t, t)),380            t_range=[4, 0.01, -0.01],381            color=PURPLE382        )383        paths.add(xy_path)384        xy_path_label = Text("y=x路径: 极限值 = A", font="SimSun", font_size=20, color=PURPLE)385        xy_path_label.next_to(y_path_label, DOWN, aligned_edge=LEFT)386        self.add_fixed_in_frame_mobjects(xy_path_label)387        388        # 沿y=-x直线趋近389        xy_neg_path = ParametricFunction(390            lambda t: axes.c2p(t, -t, f(t, -t)),391            t_range=[4, 0.01, -0.01],392            color=ORANGE393        )394        paths.add(xy_neg_path)395        xy_neg_path_label = Text("y=-x路径: 极限值 = A", font="SimSun", font_size=20, color=ORANGE)396        xy_neg_path_label.next_to(xy_path_label, DOWN, aligned_edge=LEFT)397        self.add_fixed_in_frame_mobjects(xy_neg_path_label)398        399        # 添加路径400        self.play(Create(paths), run_time=3)401        402        # 显示路径标签403        self.play(404            Write(x_path_label),405            Write(y_path_label),406            Write(xy_path_label),407            Write(xy_neg_path_label)408        )409        410        # 说明文本411        description = Text(412            "二元函数极限不存在情况:\n"413            "从不同方向趋近原点,得到不同的极限值", 414            font="SimSun", font_size=22, color=RED415        )416        description.to_edge(DOWN, buff=0.5)417        self.add_fixed_in_frame_mobjects(description)418        419        self.play(Write(description))420        self.wait(1)421        422        # 旋转相机以更好地观察表面423        self.begin_ambient_camera_rotation(rate=0.1)424        self.wait(5)425        self.stop_ambient_camera_rotation()426        427        # 添加说明:不同路径得到不同极限值428        explanation_2 = Text(429            "不同路径趋近原点时得到不同的极限值",430            font="SimSun", color=YELLOW431        ).scale(0.5)432        explanation_2.next_to(axes, DOWN)433        self.play(Write(explanation_2), run_time=1)434        435        # 定义结论文本436        conclusion_2 = Text(437            "不同路径得到不同极限值,因此极限不存在",438            font="SimSun", color=RED439        ).scale(0.7)440        conclusion_2.to_edge(UP, buff=2)441        442        # 点沿着路径移动到原点443        self.play(FadeOut(description), Write(conclusion_2))444        self.wait(2)445 446class FunctionLimitComparison(Scene):447    """整合一元函数与二元函数极限的完整比较"""448    449    def construct(self):450        # 主标题 - 使用Text而非Title以避免TeX编译错误451        title = Text("函数极限概念对比", font="SimSun", font_size=36)452        title.to_edge(UP)453        self.play(Write(title))454        self.wait()455        456        # 说明文本457        intro_text = Text(458            "本动画将分三部分展示:\n"459            "1. 一元函数极限\n"460            "2. 二元函数极限\n"461            "3. 二元函数极限不存在的情况", 462            font="SimSun", 463            font_size=28464        )465        intro_text.next_to(title, DOWN, buff=0.5)466        467        self.play(Write(intro_text))468        self.wait(2)469        470        # 第一部分提示471        part1_text = Text("第一部分:一元函数极限", font="SimSun", font_size=36, color=BLUE)472        self.play(473            FadeOut(intro_text),474            ReplacementTransform(title, part1_text)475        )476        self.wait()477        self.play(FadeOut(part1_text))478        479        # 一元函数极限场景480        # 主标题481        title = Text("一元函数极限", font="SimSun")482        self.play(Write(title))483        self.wait()484        self.play(title.animate.scale(0.6).to_edge(UP))485        486        # 说明文本487        intro = Text("一元函数 f(x) = sin(x)/x", font="SimSun", font_size=24)488        intro.next_to(title, DOWN)489        self.play(Write(intro))490        self.wait(2)491        492        # 清场准备展示一元函数493        self.play(494            FadeOut(intro),495            title.animate.scale(0.8).to_corner(UL)496        )497        498        # 创建坐标系499        axes = Axes(500            x_range=[-5, 5, 1],501            y_range=[-1.5, 1.5, 0.5],502            axis_config={"include_tip": False},503        ).scale(0.8)504        505        # 函数标题506        func_eq = Text("f(x) = sin(x)/x", font="SimSun", font_size=28)507        func_eq.next_to(axes, UP, buff=0.2)508        509        # 函数定义510        def f1(x):511            return np.sin(x) / x if x != 0 else A512        513        # 创建函数图像514        graph = axes.plot(lambda x: f1(x) if x != 0 else None, 515                          discontinuities=[0], color=BLUE)516        517        # 单点极限值518        limit_point = Dot(axes.c2p(0, A), color=RED)519        limit_label = Text("极限值 = A", font="SimSun", font_size=20, color=RED)520        limit_label.next_to(limit_point, UR, buff=0.1)521        522        # 显示元素523        self.play(524            Create(axes),525            Write(func_eq)526        )527        self.play(Create(graph))528        529        # 创建用于演示的两个点,分别从正向和负向趋近530        pos_x_start = 3.0531        neg_x_start = -3.0532        pos_dot = Dot(axes.c2p(pos_x_start, f1(pos_x_start)), color=YELLOW)533        neg_dot = Dot(axes.c2p(neg_x_start, f1(neg_x_start)), color=GREEN)534        535        # 使用ParametricFunction创建点的轨迹路径536        pos_path = ParametricFunction(537            lambda t: axes.c2p(538                pos_x_start * (1 - t) + 0.01 * t,  # 从pos_x_start到0.01539                f1(pos_x_start * (1 - t) + 0.01 * t)540            ),541            t_range=[0, 1],542            color=YELLOW_A,543            stroke_opacity=0.6544        )545        546        neg_path = ParametricFunction(547            lambda t: axes.c2p(548                neg_x_start * (1 - t) - 0.01 * t,  # 从neg_x_start到-0.01549                f1(neg_x_start * (1 - t) - 0.01 * t)550            ),551            t_range=[0, 1],552            color=GREEN_A,553            stroke_opacity=0.6554        )555        556        # 添加表示趋近方向的标签557        pos_direction = Text("x→0+", font="SimSun", font_size=20, color=YELLOW)558        pos_direction.next_to(pos_dot, UR, buff=0.1)559        560        neg_direction = Text("x→0-", font="SimSun", font_size=20, color=GREEN)561        neg_direction.next_to(neg_dot, UL, buff=0.1)562        563        self.add_fixed_in_frame_mobjects(pos_direction, neg_direction)564        565        # 显示点和轨迹566        self.play(Create(pos_dot), Create(neg_dot))567        self.play(Create(pos_path), Create(neg_path), run_time=1)568        self.play(Write(pos_direction), Write(neg_direction))569        self.wait()570        571        # 沿着曲线移动点(使用单个动画,而不是分两个阶段)572        self.play(573            MoveAlongPath(pos_dot, pos_path),574            MoveAlongPath(neg_dot, neg_path),575            # 中途更新标签位置576            UpdateFromFunc(577                pos_direction,578                lambda m: m.next_to(axes.c2p(579                    pos_x_start * (1 - self.renderer.time / 5) + 0.01 * self.renderer.time / 5,580                    f1(pos_x_start * (1 - self.renderer.time / 5) + 0.01 * self.renderer.time / 5)581                ), UR, buff=0.1)582            ),583            UpdateFromFunc(584                neg_direction,585                lambda m: m.next_to(axes.c2p(586                    neg_x_start * (1 - self.renderer.time / 5) - 0.01 * self.renderer.time / 5,587                    f1(neg_x_start * (1 - self.renderer.time / 5) - 0.01 * self.renderer.time / 5)588                ), UL, buff=0.1)589            ),590            run_time=5591        )592        593        self.wait()594        595        # 显示极限点和标签(确保这段代码不会被跳过)596        self.play(Create(limit_point))597        self.add_fixed_in_frame_mobjects(limit_label)598        self.play(Write(limit_label))599        self.wait(1)  # 确保足够的暂停时间,让用户看清极限值标签600 601        # 使用Text创建极限符号表示(避免LaTeX错误)602        lim_text = Text("lim", font_size=28)603        x_to_0 = Text("x→0", font_size=22)604        x_to_0.next_to(lim_text, DOWN, buff=0.05, aligned_edge=LEFT)605        f_x_text = Text("f(x) = A", font_size=28)606        f_x_text.next_to(lim_text, RIGHT, buff=0.2)607        608        limit_group = VGroup(lim_text, x_to_0, f_x_text)609        limit_group.shift(DOWN * 2)610        611        self.add_fixed_in_frame_mobjects(limit_group)612        self.play(Write(lim_text), Write(x_to_0), Write(f_x_text))613        self.wait(2)614        615        # 清场,准备第二部分616        self.play(617            *[FadeOut(mob) for mob in self.mobjects if mob != lim_text],618            FadeOut(title),619            FadeOut(limit_label),620            FadeOut(func_eq),621            FadeOut(pos_direction),622            FadeOut(neg_direction),623            FadeOut(pos_dot),624            FadeOut(neg_dot),625            FadeOut(pos_path),626            FadeOut(neg_path)627        )628        629        # 第二部分提示630        part2_text = Text("第二部分:二元函数极限", font="SimSun", font_size=36, color=GREEN)631        self.play(Write(part2_text))632        self.wait()633        self.play(FadeOut(part2_text))634        635        # 显示提示信息636        tip_text = Text(637            "注意:接下来是三维场景演示\n"638            "请运行以下命令观看:\n"639            "manim -pql manim_function_limit_comparison.py TwoVariableFunction",640            font="SimSun", 641            font_size=28642        )643        644        self.play(Write(tip_text))645        self.wait(3)646        647        # 第三部分提示648        self.play(FadeOut(tip_text))649        part3_text = Text("第三部分:二元函数极限不存在的情况", font="SimSun", font_size=36, color=RED)650        self.play(Write(part3_text))651        self.wait()652        self.play(FadeOut(part3_text))653        654        # 显示提示信息655        tip_text2 = Text(656            "注意:接下来是三维场景演示\n"657            "请运行以下命令观看:\n"658            "manim -pql manim_function_limit_comparison.py LimitDoesNotExist",659            font="SimSun", 660            font_size=28661        )662        663        self.play(Write(tip_text2))664        self.wait(3)665        666        # 最后的总结667        self.play(FadeOut(tip_text2))668        summary = Text(669            "本演示展示了函数极限的概念区别:\n\n"670            "一元函数:极限存在当且仅当从左右两个方向趋近的值相同\n\n"671            "二元函数:极限存在当且仅当从任意方向趋近的值都相同",672            font="SimSun", 673            font_size=28674        )675        676        self.play(Write(summary))677        self.wait(3)678        679        # 结束680        end_text = Text(681            "动画演示结束,谢谢观看",682            font="SimSun", 683            font_size=36,684            color=BLUE685        )686        687        self.play(688            FadeOut(summary),689            Write(end_text)690        )691        self.wait(2)692 693class CompleteFunctionLimitDemo(ThreeDScene):694    """完整演示:依次展示一元函数极限、二元函数极限和二元函数极限不存在的情况"""695    696    def construct(self):697        # 添加总标题,修改为"二元函数极限演示"698        main_title = Text("二元函数极限演示", font="SimSun", font_size=36, color=BLUE)699        main_title.to_edge(UP)700        701        self.add_fixed_in_frame_mobjects(main_title)702        self.play(Write(main_title))703        self.wait(1)704        # 显示标题后淡出705        self.play(FadeOut(main_title))706        self.wait(0.5)707        708        # 设置为2D视角用于一元函数部分709        self.set_camera_orientation(phi=0, theta=-90 * DEGREES)710        711        # 添加一元函数小标题712        one_var_title = Text("一元函数极限", font="SimSun", font_size=28, color=BLUE)713        one_var_title.to_edge(UP)714        self.add_fixed_in_frame_mobjects(one_var_title)715        self.play(Write(one_var_title))716        self.wait(0.5)717        718        # 创建坐标系719        axes = Axes(720            x_range=[-5, 5, 1],721            y_range=[-1.5, 1.5, 0.5],722            axis_config={"include_tip": False},723        ).scale(0.8)724        725        # 函数定义726        def f1(x):727            return np.sin(x) / x if x != 0 else A728        729        # 创建函数图像730        function_graph = axes.plot(lambda x: f1(x) if x != 0 else None, 731                                   discontinuities=[0], color=BLUE)732        733        self.play(734            Create(axes),735            Create(function_graph)736        )737        738        # 添加文字说明739        approach_text = Text("自变量从两个方向趋向于0点", font="SimSun", font_size=24)740        approach_text.next_to(one_var_title, DOWN, buff=0.5)741        self.add_fixed_in_frame_mobjects(approach_text)742        self.play(Write(approach_text))743        self.wait(0.5)744        self.play(FadeOut(approach_text))745        746        # 创建用于演示的两个点,分别从正向和负向趋近747        pos_x_start = 2.0748        neg_x_start = -2.0749        750        # 使用ParametricFunction创建点的轨迹路径751        pos_path = ParametricFunction(752            lambda t: axes.c2p(753                pos_x_start * (1 - t) + 0.01 * t,  # 从pos_x_start到0.01754                f1(pos_x_start * (1 - t) + 0.01 * t)755            ),756            t_range=[0, 1],757            color=YELLOW_A,758            stroke_opacity=0.6759        )760        761        neg_path = ParametricFunction(762            lambda t: axes.c2p(763                neg_x_start * (1 - t) - 0.01 * t,  # 从neg_x_start到-0.01764                f1(neg_x_start * (1 - t) - 0.01 * t)765            ),766            t_range=[0, 1],767            color=GREEN_A,768            stroke_opacity=0.6769        )770        771        self.play(Create(pos_path), Create(neg_path))772        773        # 创建移动点和标签774        pos_point = Dot(axes.c2p(pos_x_start, f1(pos_x_start)), color=YELLOW)775        neg_point = Dot(axes.c2p(neg_x_start, f1(neg_x_start)), color=GREEN)776        777        pos_label = Text("x→0+", font="SimSun", font_size=20).next_to(pos_point, UR, buff=0.1)778        neg_label = Text("x→0-", font="SimSun", font_size=20).next_to(neg_point, UL, buff=0.1)779        780        self.add_fixed_in_frame_mobjects(pos_label, neg_label)781        self.play(Create(pos_point), Create(neg_point))782        self.play(Write(pos_label), Write(neg_label))783        784        # 移动点到原点785        self.play(786            MoveAlongPath(pos_point, pos_path),787            MoveAlongPath(neg_point, neg_path),788            run_time=3789        )790        self.wait(0.5)791        792        # 移动点到达后显示极限点和标签793        limit_point = Dot(axes.c2p(0, A), color=RED)794        limit_label = Text("极限值=A", font="SimSun", font_size=20, color=RED)795        limit_label.next_to(limit_point, UR, buff=0.1)796        797        self.play(Create(limit_point))798        self.add_fixed_in_frame_mobjects(limit_label)799        self.play(Write(limit_label))800        self.wait(1)801        802        # 使用Text创建极限符号表示(避免LaTeX错误)803        lim_text = Text("lim", font_size=28)804        x_to_0 = Text("x→0", font_size=22)805        x_to_0.next_to(lim_text, DOWN, buff=0.05, aligned_edge=LEFT)806        f_x_text = Text("f(x) = A", font_size=28)807        f_x_text.next_to(lim_text, RIGHT, buff=0.2)808        809        limit_group = VGroup(lim_text, x_to_0, f_x_text)810        limit_group.shift(DOWN * 2)811        812        self.add_fixed_in_frame_mobjects(limit_group)813        self.play(Write(lim_text), Write(x_to_0), Write(f_x_text))814        self.wait(2)815        816        # 清除一元函数部分817        self.clear()818        819        # 开始二元函数部分820        two_var_title = Text("二元函数极限", font="SimSun", font_size=28, color=BLUE)821        two_var_title.to_edge(UP)822        self.add_fixed_in_frame_mobjects(two_var_title)823        self.play(Write(two_var_title))824        self.wait(0.5)825        826        # 添加二元函数极限结论到标题下方827        conclusion_2 = Text("二元函数极限存在:从任意路径趋近得到相同极限值", font="SimSun", font_size=20, color=BLUE)828        conclusion_2.next_to(two_var_title, DOWN, buff=0.3)829        self.add_fixed_in_frame_mobjects(conclusion_2)830        self.play(Write(conclusion_2))831        self.wait(0.5)832        833        # 设置3D相机角度834        self.set_camera_orientation(phi=75 * DEGREES, theta=-60 * DEGREES)835        836        # 创建3D坐标轴837        axes_3d = ThreeDAxes(838            x_range=[-5, 5, 1],839            y_range=[-5, 5, 1],840            z_range=[-1, 5, 1],841            x_length=6,842            y_length=6,843            z_length=4,844        ).move_to(ORIGIN)845        846        # 显示3D坐标系847        self.play(Create(axes_3d))848        849        # 函数定义,计算曲面上的点850        def f(x, y):851            r = np.sqrt(x**2 + y**2)852            if r == 0:853                return A854            return np.sin(r) / r855        856        # 创建曲面857        surface = Surface(858            lambda u, v: axes_3d.c2p(u, v, f(u, v)),859            u_range=[-4, 4],860            v_range=[-4, 4],861            resolution=(20, 20),862            checkerboard_colors=[BLUE_D, BLUE_E],863            fill_opacity=0.7864        )865        866        # 显示曲面867        self.play(Create(surface), run_time=2)868        self.wait(0.5)869        870        # 添加说明文字871        approach_text_3d = Text("自变量沿多条路径趋向于原点", font="SimSun", font_size=24)872        approach_text_3d.next_to(two_var_title, DOWN, buff=0.5)873        self.add_fixed_in_frame_mobjects(approach_text_3d)874        self.play(Write(approach_text_3d))875        self.wait(0.5)876        self.play(FadeOut(approach_text_3d))877        878        # 创建原点879        origin = Dot3D(axes_3d.c2p(0, 0, A), color=RED)880        self.play(Create(origin))881        self.wait(0.5)882        883        # 添加从不同方向趋近的路径884        paths = VGroup()885        886        # 沿x轴趋近887        x_path = ParametricFunction(888            lambda t: axes_3d.c2p(t, 0, f(t, 0)),889            t_range=[4, 0.01, -0.01],890            color=YELLOW891        )892        paths.add(x_path)893        x_point = Sphere(radius=0.1, color=YELLOW).move_to(axes_3d.c2p(4, 0, f(4, 0)))894 895        # 沿y轴趋近896        y_path = ParametricFunction(897            lambda t: axes_3d.c2p(0, t, f(0, t)),898            t_range=[4, 0.01, -0.01],899            color=GREEN900        )901        paths.add(y_path)902        y_point = Sphere(radius=0.1, color=GREEN).move_to(axes_3d.c2p(0, 4, f(0, 4)))903 904        # 沿直线y=x趋近905        xy_path = ParametricFunction(906            lambda t: axes_3d.c2p(t, t, f(t, t)),907            t_range=[4, 0.01, -0.01],908            color=PURPLE909        )910        paths.add(xy_path)911        xy_point = Sphere(radius=0.1, color=PURPLE).move_to(axes_3d.c2p(3, 3, f(3, 3)))912 913        # 沿直线y=-x趋近914        xy_neg_path = ParametricFunction(915            lambda t: axes_3d.c2p(t, -t, f(t, -t)),916            t_range=[4, 0.01, -0.01],917            color=TEAL918        )919        paths.add(xy_neg_path)920        xy_neg_point = Sphere(radius=0.1, color=TEAL).move_to(axes_3d.c2p(3, -3, f(3, -3)))921 922        # 沿螺旋线趋近923        spiral_path = ParametricFunction(924            lambda t: axes_3d.c2p(925                t * np.cos(2 * t), 926                t * np.sin(2 * t), 927                f(t * np.cos(2 * t), t * np.sin(2 * t))928            ),929            t_range=[4, 0.01, -0.01],930            color=ORANGE931        )932        paths.add(spiral_path)933        spiral_point = Sphere(radius=0.1, color=ORANGE).move_to(934            axes_3d.c2p(4 * np.cos(8), 4 * np.sin(8), f(4 * np.cos(8), 4 * np.sin(8)))935        )936 937        # 逐个显示点938        self.play(939            Create(x_point),940            Create(y_point),941            Create(xy_point),942            Create(xy_neg_point),943            Create(spiral_point),944        )945        946        # 显示所有路径947        self.play(Create(paths), run_time=2)948        self.wait(1)949        950        # 显示极限点和标签951        limit_point_3d = Dot3D(axes_3d.c2p(0, 0, A), color=RED, radius=0.1)952        limit_label_3d = Text("极限值=A", font="SimSun", font_size=20, color=RED)953        limit_label_3d.next_to(limit_point_3d, UP, buff=0.2)954        955        # 添加进一步说明956        limit_explanation = Text("无论从哪个方向趋近,极限值都相同", font="SimSun", font_size=22, color=GREEN)957        limit_explanation.to_edge(DOWN, buff=1)958        959        self.play(Create(limit_point_3d))960        self.add_fixed_in_frame_mobjects(limit_label_3d, limit_explanation)961        self.play(Write(limit_label_3d))962        self.play(Write(limit_explanation))963        self.wait(1)964        965        # 使用Text创建极限符号表示(避免LaTeX错误)966        lim_text_2 = Text("lim", font_size=28)967        xy_to_00 = Text("(x,y)→(0,0)", font_size=22)968        xy_to_00.next_to(lim_text_2, DOWN, buff=0.05, aligned_edge=LEFT)969        f_xy_text_2 = Text("f(x,y) = A", font_size=28)970        f_xy_text_2.next_to(lim_text_2, RIGHT, buff=0.5)  # 增加右侧缓冲区,从0.2改为0.5971        972        limit_group_2 = VGroup(lim_text_2, xy_to_00, f_xy_text_2)973        limit_group_2.shift(DOWN * 3).scale(0.9)  # 缩小整体大小并调整位置974        975        self.add_fixed_in_frame_mobjects(limit_group_2)976        self.play(Write(lim_text_2), Write(xy_to_00), Write(f_xy_text_2))977        self.wait(2)978        979        # 清除场景980        self.clear()981        982        # 开始二元函数极限不存在的情况983        no_limit_title = Text("二元函数极限不存在的情况", font="SimSun", font_size=28, color=RED)984        no_limit_title.to_edge(UP)985        self.add_fixed_in_frame_mobjects(no_limit_title)986        self.play(Write(no_limit_title))987        self.wait(0.5)988        989        # 添加二元函数极限不存在结论到标题下方990        conclusion_3 = Text("不同路径趋近得到不同极限值,因此极限不存在", font="SimSun", font_size=20, color=RED)991        conclusion_3.next_to(no_limit_title, DOWN, buff=0.3)992        self.add_fixed_in_frame_mobjects(conclusion_3)993        self.play(Write(conclusion_3))994        self.wait(0.5)995        996        # 设置3D相机角度997        self.set_camera_orientation(phi=75 * DEGREES, theta=-60 * DEGREES)998        999        # 创建新的3D坐标轴1000        axes_3d_2 = ThreeDAxes(1001            x_range=[-5, 5, 1],1002            y_range=[-5, 5, 1],1003            z_range=[-1, 1, 0.5],1004            x_length=6,1005            y_length=6,1006            z_length=4,1007        ).move_to(ORIGIN)1008        1009        # 显示3D坐标系1010        self.play(Create(axes_3d_2))1011        1012        # 创建函数 z = xy/(x^2+y^2) 的曲面1013        def f2(x, y):1014            r_squared = x**2 + y**21015            if r_squared < 0.001:  # 避免除以零1016                return 0  # 返回0而不是A,因为在这个例子中沿x轴和y轴趋近的极限值是01017            return (x * y) / r_squared1018        1019        # 创建曲面1020        surface_2 = Surface(1021            lambda u, v: axes_3d_2.c2p(u, v, f2(u, v)),1022            u_range=[-2, 2],1023            v_range=[-2, 2],1024            resolution=(30, 30),1025            fill_opacity=0.7,1026            checkerboard_colors=[BLUE_D, BLUE_E],1027        )1028        1029        # 创建函数公式1030        func_formula = Text("z=f(x,y)=xy/(x²+y²)", font="SimSun", font_size=24, color=BLUE)1031        func_formula.to_edge(UP, buff=1.5)1032        1033        self.play(Create(surface_2))1034        self.add_fixed_in_frame_mobjects(func_formula)1035        self.play(Write(func_formula))1036        1037        # 创建原点1038        origin = Dot3D(axes_3d_2.c2p(0, 0, 0), color=RED)1039        self.play(Create(origin))1040        self.wait(0.5)1041        1042        # 显示解释文本1043        explain_text = Text("观察不同路径趋近原点时的极限值", font="SimSun", font_size=20)1044        explain_text.to_edge(UP, buff=2)1045        self.add_fixed_in_frame_mobjects(explain_text)1046        self.play(Write(explain_text))1047        self.wait(1)1048        self.play(FadeOut(explain_text))1049        1050        # 创建不同类型的路径及其标签1051        paths_3d = VGroup()1052        path_points = []1053        path_labels = []1054        1055        # 1. 沿x轴趋近 - 极限值 = A1056        x_path = ParametricFunction(1057            lambda t: axes_3d_2.c2p(t, 0, f2(t, 0)),1058            t_range=[2, 0.1, -0.05],1059            color=YELLOW1060        )1061        paths_3d.add(x_path)1062        x_point = Sphere(radius=0.08, color=YELLOW).move_to(axes_3d_2.c2p(2, 0, f2(2, 0)))1063        x_label = Text("沿x轴路径趋近:极限值为A", font="SimSun", font_size=18, color=YELLOW)1064        x_label.to_corner(UL).shift(DOWN * 2)1065        self.add_fixed_in_frame_mobjects(x_label)1066        path_points.append(x_point)1067        path_labels.append(x_label)1068        1069        # 2. 沿y轴趋近 - 极限值 = A1070        y_path = ParametricFunction(1071            lambda t: axes_3d_2.c2p(0, t, f2(0, t)),1072            t_range=[2, 0.1, -0.05],1073            color=GREEN1074        )1075        paths_3d.add(y_path)1076        y_point = Sphere(radius=0.08, color=GREEN).move_to(axes_3d_2.c2p(0, 2, f2(0, 2)))1077        y_label = Text("沿y轴路径趋近:极限值为A", font="SimSun", font_size=18, color=GREEN)1078        y_label.next_to(x_label, DOWN, aligned_edge=LEFT)1079        self.add_fixed_in_frame_mobjects(y_label)1080        path_points.append(y_point)1081        path_labels.append(y_label)1082        1083        # 3. 沿直线y=x路径趋近 - 极限值 = A1084        xy_path = ParametricFunction(1085            lambda t: axes_3d_2.c2p(t, t, f2(t, t)),1086            t_range=[2, 0.1, -0.05],1087            color=PURPLE1088        )1089        paths_3d.add(xy_path)1090        xy_point = Sphere(radius=0.08, color=PURPLE).move_to(axes_3d_2.c2p(2, 2, f2(2, 2)))1091        xy_label = Text("沿y=x路径趋近:极限值为A", font="SimSun", font_size=18, color=PURPLE)1092        xy_label.next_to(y_label, DOWN, aligned_edge=LEFT)1093        self.add_fixed_in_frame_mobjects(xy_label)1094        path_points.append(xy_point)1095        path_labels.append(xy_label)1096        1097        # 4. 沿直线y=-x路径趋近 - 极限值 = A1098        xy_neg_path = ParametricFunction(1099            lambda t: axes_3d_2.c2p(t, -t, f2(t, -t)),1100            t_range=[2, 0.1, -0.05],1101            color=TEAL1102        )1103        paths_3d.add(xy_neg_path)1104        xy_neg_point = Sphere(radius=0.08, color=TEAL).move_to(axes_3d_2.c2p(2, -2, f2(2, -2)))1105        xy_neg_label = Text("沿y=-x路径趋近:极限值为A", font="SimSun", font_size=18, color=TEAL)1106        xy_neg_label.next_to(xy_label, DOWN, aligned_edge=LEFT)1107        self.add_fixed_in_frame_mobjects(xy_neg_label)1108        path_points.append(xy_neg_point)1109        path_labels.append(xy_neg_label)1110 1111        # 创建极限点1112        limit_points = [1113            Dot3D(axes_3d_2.c2p(0, 0, 0), color=YELLOW, radius=0.08),  # x轴路径极限点:(0,0,0)1114            Dot3D(axes_3d_2.c2p(0, 0, 0), color=GREEN, radius=0.08),   # y轴路径极限点:(0,0,0)1115            Dot3D(axes_3d_2.c2p(0, 0, 0.5), color=PURPLE, radius=0.08), # y=x路径极限点:(0,0,0.5)1116            Dot3D(axes_3d_2.c2p(0, 0, -0.5), color=TEAL, radius=0.08),  # y=-x路径极限点:(0,0,-0.5)1117        ]1118        1119        # 显示极限点1120        for limit_point in limit_points:1121            self.play(Create(limit_point), run_time=0.5)1122        1123        # 然后逐个演示点沿着路径移动到各自的极限值1124        for i, (point, limit_point) in enumerate(zip(path_points, limit_points)):1125            # 对于每个点,创建一个路径从点的当前位置到对应的极限值1126            self.play(1127                point.animate.move_to(limit_point.get_center()),1128                run_time=21129            )1130            self.wait(0.5)1131        1132        # 使用Text创建极限符号表示(避免LaTeX错误)1133        lim_text_3 = Text("lim", font_size=28)1134        xy_to_00_3 = Text("(x,y)→(0,0)", font_size=22)1135        xy_to_00_3.next_to(lim_text_3, DOWN, buff=0.05, aligned_edge=LEFT)1136        f_xy_text_3 = Text("xy/(x²+y²) 不存在", font_size=28)1137        f_xy_text_3.next_to(lim_text_3, RIGHT, buff=0.2)1138        1139        limit_group_3 = VGroup(lim_text_3, xy_to_00_3, f_xy_text_3)1140        limit_group_3.to_edge(DOWN, buff=1)1141        1142        self.add_fixed_in_frame_mobjects(limit_group_3)1143        self.play(Write(lim_text_3), Write(xy_to_00_3), Write(f_xy_text_3))1144        self.wait(2)1145 1146if __name__ == "__main__":1147    # 运行完整演示1148    # manim -pql manim_function_limit_comparison.py CompleteFunctionLimitDemo1149    pass

讲解

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

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

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

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

观察路径

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

本页可以从二元函数极限、路径极限、函数极限、多元函数这些概念进入,继续沿相邻问题观察同一类数学结构。