epsilon_n_visualization.py
1from manim import *2import numpy as np3 4class EpsilonNVisualization(Scene):5 def construct(self):6 # Set default font7 Text.set_default(font="SimSun")8 9 # Title10 title = Text("极限的 ε-N 定义可视化", font="SimSun").scale(0.6) # 缩小标题字体11 title.to_edge(UP, buff=0.3)12 self.play(Write(title))13 self.wait(1)14 15 # Fade out title16 self.play(FadeOut(title))17 18 # Problem statement with mathematical notation19 problem = MathTex(r"\lim_{n \to \infty} \frac{3n^2}{n^2 - 3} = 3").scale(1.0) # 缩小题目字体20 problem.to_edge(UP, buff=0.3)21 22 self.play(Write(problem))23 self.wait(2)24 25 # Create axes for visualization26 axes = Axes(27 x_range=[0, 25, 5],28 y_range=[2, 4, 0.5],29 axis_config={"include_tip": True},30 x_length=8, # 缩小图像宽度31 y_length=4 # 缩小图像高度32 )33 axes.next_to(problem, DOWN, buff=0.5) # 下移一点点动画34 35 # Labels for axes36 x_label = MathTex("n").next_to(axes.x_axis, RIGHT, buff=0.3)37 y_label = MathTex("f(n)").next_to(axes.y_axis, UP, buff=0.3)38 39 self.play(Create(axes), Write(x_label), Write(y_label))40 self.wait(1)41 42 # Function to plot43 def func(x):44 return 3 * x**2 / (x**2 - 3)45 46 # 先显示数列散点图,按照n为正整数取值绘制点47 points = VGroup()48 x_values = range(2, 26) # 正整数n从2到2549 for x in x_values:50 y = func(x)51 point = Dot(axes.c2p(x, y), color=BLUE, radius=0.05)52 points.add(point)53 54 graph_label = MathTex(r"f(n) = \frac{3n^2}{n^2 - 3}").next_to(axes.c2p(20, func(20)), UP, buff=0.2).scale(0.7)55 graph_label.set_color(BLUE)56 57 # Plot the limit line y = 358 limit_line = axes.plot(lambda x: 3, x_range=[0, 25], color=GREEN)59 limit_label = MathTex("y = 3").next_to(limit_line.get_end(), DOWN, buff=0.2).scale(0.7)60 limit_label.set_color(GREEN)61 62 self.play(Create(points), Write(graph_label))63 self.play(Create(limit_line), Write(limit_label))64 self.wait(2)65 66 # 然后显示N的推导过程67 # 添加一般形式的推导过程68 derivation_title = Text("N 的一般形式推导过程:", font="SimSun").scale(0.4) # 缩小字体69 derivation_title.to_corner(UL, buff=0.3) # 调整位置70 derivation_title.shift(DOWN * 1.2) # 下移一点点71 72 derivation_step1 = MathTex(r"|f(n) - 3| = \left|\frac{9}{n^2 - 3}\right|").scale(0.4) # 缩小字体73 derivation_step2_part1 = Text("当", font="SimSun").scale(0.4)74 derivation_step2_part2 = MathTex(r"n > \sqrt{3}").scale(0.4)75 derivation_step2_part3 = Text("时", font="SimSun").scale(0.4)76 derivation_step2_part4 = MathTex(r"\frac{9}{n^2 - 3} < \varepsilon").scale(0.4)77 78 # 组合 derivation_step279 derivation_step2 = VGroup(derivation_step2_part1, derivation_step2_part2, derivation_step2_part3, derivation_step2_part4)80 derivation_step2.arrange(RIGHT, buff=0.1)81 82 derivation_step3 = MathTex(r"n^2 - 3 > \frac{9}{\varepsilon}").scale(0.4) # 缩小字体83 derivation_step4 = MathTex(r"n^2 > \frac{9}{\varepsilon} + 3").scale(0.4) # 缩小字体84 derivation_step5 = MathTex(r"n > \sqrt{\frac{9}{\varepsilon} + 3}").scale(0.4) # 缩小字体85 86 derivation_step6_part1 = Text("因此", font="SimSun").scale(0.4)87 derivation_step6_part2 = MathTex(r"N = \left\lceil \sqrt{\frac{9}{\varepsilon} + 3} \right\rceil").scale(0.4)88 89 # 组合 derivation_step690 derivation_step6 = VGroup(derivation_step6_part1, derivation_step6_part2)91 derivation_step6.arrange(RIGHT, buff=0.1)92 93 derivation_group = VGroup(derivation_title, derivation_step1, derivation_step2, derivation_step3, derivation_step4, derivation_step5, derivation_step6)94 derivation_group.arrange(DOWN, aligned_edge=LEFT, buff=0.1) # 减小行间距95 derivation_group.to_corner(UL, buff=0.3) # 调整位置96 derivation_group.shift(DOWN * 1.2) # 下移一点点97 98 self.play(Write(derivation_title))99 self.wait(1)100 self.play(Write(derivation_step1))101 self.wait(1)102 self.play(Write(derivation_step2_part1), Write(derivation_step2_part2), Write(derivation_step2_part3), Write(derivation_step2_part4))103 self.wait(1)104 self.play(Write(derivation_step3))105 self.wait(1)106 self.play(Write(derivation_step4))107 self.wait(1)108 self.play(Write(derivation_step5))109 self.wait(1)110 self.play(Write(derivation_step6_part1), Write(derivation_step6_part2))111 self.wait(2)112 113 # Show different epsilon values and corresponding N calculations114 epsilons = [0.5, 0.15]115 colors = [RED, ORANGE]116 highlight_colors = [YELLOW, PURPLE]117 118 for i, (epsilon, color, highlight_color) in enumerate(zip(epsilons, colors, highlight_colors)):119 # Calculate N for this epsilon120 N_float = np.sqrt(9/epsilon + 3)121 N = int(np.ceil(N_float)) # Take the ceiling of N122 123 # 将N具体数值的计算移到右上角124 if i == 0:125 # For first epsilon, show full calculation126 n_value_title = Text(f"当ε = {epsilon}时:", font="SimSun").scale(0.4) # 缩小字体127 n_value_title.to_corner(UR, buff=0.3) # 调整位置到右上角128 n_value_title.shift(DOWN * 0.5) # 下移一点点129 130 n_value_step1 = MathTex(f"N = \\left\\lceil \\sqrt{{\\frac{{9}}{{{epsilon}}} + 3}} \\right\\rceil").scale(0.5) # 缩小字体131 n_value_step2 = MathTex(f"N = \\left\\lceil {N_float:.2f} \\right\\rceil = {N}").scale(0.5) # 缩小字体132 133 n_value_group = VGroup(n_value_title, n_value_step1, n_value_step2)134 n_value_group.arrange(DOWN, aligned_edge=LEFT, buff=0.1) # 减小行间距135 n_value_group.to_corner(UR, buff=0.3) # 调整位置到右上角136 n_value_group.shift(DOWN * 0.5) # 下移一点点137 138 self.play(Write(n_value_title))139 self.play(Write(n_value_step1))140 self.wait(0.5)141 self.play(Write(n_value_step2))142 self.wait(1)143 144 n_value_group_prev = n_value_group145 else:146 # For second epsilon, just show the result147 n_result_part1 = Text("当", font="SimSun").scale(0.4)148 n_result_part2 = MathTex(r"\varepsilon").scale(0.4)149 n_result_part3 = Text(f"= {epsilon}时,N = {N}", font="SimSun").scale(0.4)150 151 n_result = VGroup(n_result_part1, n_result_part2, n_result_part3)152 n_result.arrange(RIGHT, buff=0.1)153 n_result.to_corner(UR, buff=0.3) # 调整位置到右上角154 n_result.shift(DOWN * 0.5) # 下移一点点155 156 self.play(Transform(n_value_group_prev, n_result))157 self.wait(2)158 159 # 最后显示y=3+epsilon和y=3-epsilon的直线160 upper_epsilon_line = axes.plot(lambda x: 3 + epsilon, x_range=[0, 25], color=color)161 lower_epsilon_line = axes.plot(lambda x: 3 - epsilon, x_range=[0, 25], color=color)162 163 upper_epsilon_label = MathTex(f"y = 3 + \\varepsilon = {3 + epsilon}").next_to(upper_epsilon_line.get_end(), UP, buff=0.1).scale(0.6)164 upper_epsilon_label.set_color(color)165 lower_epsilon_label = MathTex(f"y = 3 - \\varepsilon = {3 - epsilon}").next_to(lower_epsilon_line.get_end(), DOWN, buff=0.1).scale(0.6)166 lower_epsilon_label.set_color(color)167 168 if i == 0:169 # Show epsilon lines and labels170 self.play(Create(upper_epsilon_line), Create(lower_epsilon_line))171 self.play(Write(upper_epsilon_label), Write(lower_epsilon_label))172 self.wait(2)173 else:174 # Transform to new epsilon lines175 self.play(176 Transform(upper_epsilon_line_prev, upper_epsilon_line),177 Transform(lower_epsilon_line_prev, lower_epsilon_line),178 Transform(upper_epsilon_label_prev, upper_epsilon_label),179 Transform(lower_epsilon_label_prev, lower_epsilon_label)180 )181 self.wait(2)182 183 # Store references for next iteration184 upper_epsilon_line_prev = upper_epsilon_line185 lower_epsilon_line_prev = lower_epsilon_line186 upper_epsilon_label_prev = upper_epsilon_label187 lower_epsilon_label_prev = lower_epsilon_label188 189 # 2. Then show the absolute value |f(n) - 3|190 if i == 0:191 # Create a vertical brace between the epsilon lines in the middle of the graph, on the left side192 epsilon_brace = BraceBetweenPoints(193 axes.c2p(10, 3 + epsilon),194 axes.c2p(10, 3 - epsilon),195 color=color,196 direction=LEFT # Point brace to the left197 )198 epsilon_condition = MathTex(f"|f(n) - 3| < \\varepsilon = {epsilon}").scale(0.7)199 epsilon_condition.next_to(epsilon_brace, LEFT, buff=0.2)200 epsilon_condition.set_color(color)201 202 self.play(Create(epsilon_brace), Write(epsilon_condition))203 epsilon_brace_prev = epsilon_brace204 epsilon_condition_prev = epsilon_condition205 self.wait(2)206 else:207 # Update epsilon condition208 new_epsilon_brace = BraceBetweenPoints(209 axes.c2p(10, 3 + epsilon),210 axes.c2p(10, 3 - epsilon),211 color=color,212 direction=LEFT # Point brace to the left213 )214 new_epsilon_condition = MathTex(f"|f(n) - 3| < \\varepsilon = {epsilon}").scale(0.7)215 new_epsilon_condition.next_to(new_epsilon_brace, LEFT, buff=0.2)216 new_epsilon_condition.set_color(color)217 218 self.play(219 Transform(epsilon_brace_prev, new_epsilon_brace),220 Transform(epsilon_condition_prev, new_epsilon_condition)221 )222 self.wait(2)223 224 # Draw vertical line at N225 N_line = axes.get_vertical_line(226 axes.c2p(N, 0),227 line_config={"color": highlight_color, "stroke_width": 4}228 )229 N_label = MathTex(f"N = {N}").next_to(N_line, UP, buff=0.1).scale(0.7)230 N_label.set_color(highlight_color)231 232 # Create a dotted vertical line spanning the entire y-range for better visibility233 N_dotted_line = DashedLine(234 start=axes.c2p(N, axes.get_y_range()[0]),235 end=axes.c2p(N, axes.get_y_range()[1]),236 color=highlight_color,237 stroke_width=2,238 dash_length=0.1239 )240 241 if i == 0:242 # Show N line243 self.play(Create(N_dotted_line))244 self.play(Create(N_line), Write(N_label))245 self.wait(1)246 247 # Store references248 N_line_prev = N_line249 N_label_prev = N_label250 N_dotted_line_prev = N_dotted_line251 else:252 # Move N line to new position253 self.play(254 Transform(N_dotted_line_prev, N_dotted_line),255 Transform(N_line_prev, N_line),256 Transform(N_label_prev, N_label)257 )258 self.wait(1)259 260 # 高亮满足条件的点261 highlight_points = VGroup()262 for x in x_values:263 if x > N:264 y = func(x)265 point = Dot(axes.c2p(x, y), color=highlight_color, radius=0.07)266 highlight_points.add(point)267 268 if i == 0:269 # Show highlight270 self.play(Create(highlight_points))271 highlight_points_prev = highlight_points272 self.wait(2)273 else:274 # Update highlight275 self.play(Transform(highlight_points_prev, highlight_points))276 self.wait(2)277 278 # Show the relationship between epsilon and N279 relationship = MathTex(280 "\\varepsilon \\downarrow \\quad \\Rightarrow \\quad N \\uparrow",281 color=YELLOW282 ).scale(1.0) # 缩小字体283 relationship.to_edge(DOWN, buff=0.7) # 下移一点点284 285 self.play(Write(relationship))286 self.wait(2)287 288 # Final conclusion289 conclusion = Text(290 "当ε减小时,满足条件的N必须增大,这体现了极限的ε-N定义本质",291 font="SimSun"292 ).scale(0.5) # 缩小字体293 conclusion.next_to(relationship, DOWN, buff=0.3) # 调整位置和间距294 295 self.play(Write(conclusion))296 self.wait(3)297 298def main():299 scene = EpsilonNVisualization()300 scene.render()301 302if __name__ == "__main__":303 main() 讲解
这个视频围绕「数列极限的epsilon-N语言」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「极限的 ε-N 定义可视化」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「数列极限的epsilon-N语言」中的核心对象,尤其留意epsilon-N 定义、数列极限、数列收敛之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从epsilon-N 定义、数列极限、数列收敛这些概念进入,继续沿相邻问题观察同一类数学结构。