sequence_convergence.py
1from manim import *2import numpy as np3 4class SequenceConvergence(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, 20, 5],15 y_range=[-0.5, 2, 0.5],16 axis_config={"color": GREY},17 x_length=10,18 y_length=619 ).shift(DOWN * 0.2)20 21 axes_labels = axes.get_axis_labels(22 x_label="n",23 y_label="a_n"24 )25 26 self.play(Create(axes), Write(axes_labels))27 self.wait(1)28 29 # 创建收敛数列 an = 1 + 1/n30 def converge_seq(n):31 return 1 + 1/n if n > 0 else 232 33 converge_points = [axes.c2p(n, converge_seq(n)) for n in range(1, 21)]34 converge_dots = VGroup(*[Dot(point, color=BLUE) for point in converge_points])35 36 # 创建连线37 converge_lines = VMobject(color=BLUE_A, stroke_width=2)38 converge_lines.set_points_smoothly(converge_points)39 40 converge_label = MathTex(41 r"a_n",42 color=BLUE43 ).scale(0.8).to_edge(UP, buff=1.5).to_edge(RIGHT, buff=1.0)44 45 # 创建振荡数列 bn = (-1)^n/n + 146 def oscillate_seq(n):47 return (-1)**n/n + 1 if n > 0 else 1.548 49 oscillate_points = [axes.c2p(n, oscillate_seq(n)) for n in range(1, 21)]50 oscillate_dots = VGroup(*[Dot(point, color=RED) for point in oscillate_points])51 52 oscillate_lines = VMobject(color=RED_A, stroke_width=2)53 oscillate_lines.set_points_as_corners(oscillate_points)54 55 oscillate_label = MathTex(56 r"b_n",57 color=RED58 ).scale(0.8).next_to(converge_label, DOWN, buff=0.3)59 60 # 显示收敛数列61 self.play(Write(converge_label))62 for i in range(len(converge_dots)):63 self.play(64 Create(converge_dots[i]),65 Create(converge_lines) if i == len(converge_dots)-1 else Wait(),66 run_time=0.267 )68 self.wait(1)69 70 # 显示振荡数列71 self.play(Write(oscillate_label))72 for i in range(len(oscillate_dots)):73 self.play(74 Create(oscillate_dots[i]),75 Create(oscillate_lines) if i == len(oscillate_dots)-1 else Wait(),76 run_time=0.277 )78 self.wait(1)79 80 # 添加极限线81 limit_line = DashedLine(82 axes.c2p(0, 1),83 axes.c2p(20, 1),84 color=YELLOW85 )86 limit_label = MathTex(87 r"\lim_{n \to \infty} a_n = \lim_{n \to \infty} b_n = a", 88 color=YELLOW89 ).next_to(limit_line, RIGHT, buff=0.2).shift(DOWN * 1 + LEFT * 5)90 91 self.play(92 Create(limit_line),93 Write(limit_label)94 )95 self.wait(1)96 97 # 创建动态ε带和N标注的更新函数98 def update_epsilon_band(epsilon_val):99 # 更新ε带100 upper_line = DashedLine(101 axes.c2p(0, 1 + epsilon_val),102 axes.c2p(20, 1 + epsilon_val),103 color=GREEN_A104 )105 middle_line = DashedLine( # 添加中间线106 axes.c2p(0, 1),107 axes.c2p(20, 1),108 color=YELLOW,109 dash_length=0.1110 )111 lower_line = DashedLine(112 axes.c2p(0, 1 - epsilon_val),113 axes.c2p(20, 1 - epsilon_val),114 color=GREEN_A115 )116 117 # 添加标注118 upper_label = MathTex(r"a+\varepsilon", color=GREEN_A).next_to(119 axes.c2p(0, 1 + epsilon_val), LEFT120 )121 middle_label = MathTex(r"a", color=YELLOW).next_to(122 axes.c2p(0, 1), LEFT123 )124 lower_label = MathTex(r"a-\varepsilon", color=GREEN_A).next_to(125 axes.c2p(0, 1 - epsilon_val), LEFT126 )127 128 epsilon_brace = BraceBetweenPoints(129 axes.c2p(19, 1 + epsilon_val),130 axes.c2p(19, 1 - epsilon_val),131 color=GREEN_A,132 direction=RIGHT133 )134 epsilon_text = MathTex(r"2\varepsilon", color=GREEN_A).next_to(epsilon_brace, RIGHT)135 136 # 计算对应的N值137 # 对于an = 1 + 1/n,要使|an - 1| < ε,需要n > 1/ε138 N_value = max(5, int(1/epsilon_val))139 140 # 添加N处的垂直虚线141 N_line = DashedLine(142 axes.c2p(N_value, 0),143 axes.c2p(N_value, 2), # 延伸到足够高144 color=GREEN,145 dash_length=0.1146 )147 148 # 简化N标签149 N_arrow = Arrow(150 axes.c2p(N_value, -0.3),151 axes.c2p(N_value, 0),152 color=GREEN,153 buff=0154 )155 N_label = MathTex(r"N", color=GREEN).next_to(N_arrow, DOWN)156 157 return VGroup(158 upper_line, middle_line, lower_line,159 upper_label, middle_label, lower_label,160 epsilon_brace, epsilon_text,161 N_line, N_arrow, N_label162 )163 164 # 创建动画序列165 epsilon_values = [0.5, 0.3, 0.2, 0.1]166 epsilon_bands = []167 168 # 显示初始ε带169 current_band = update_epsilon_band(epsilon_values[0])170 self.play(Create(current_band))171 self.wait(1)172 173 # 动态更新ε带和N174 for i in range(1, len(epsilon_values)):175 new_band = update_epsilon_band(epsilon_values[i])176 self.play(177 ReplacementTransform(current_band, new_band),178 run_time=2179 )180 current_band = new_band181 self.wait(1)182 183 # 修改解释文本的位置和大小184 explanation = Text(185 "数列收敛的定义:\n" + 186 "∀ε>0, ∃N>0, 当n>N时," +187 "|an-a| < ε" +188 "其中a是数列的极限。" +189 "注意:ε越小,N越大",190 font="SimSun",191 font_size=22, # 稍微减小字体192 line_spacing=1.1 # 稍微减小行距193 ).next_to(194 axes, # 只相对于坐标系定位,不包括current_band195 DOWN,196 buff=0.3 # 减小间距197 ).align_to(axes, LEFT).shift(UP * 0.5) # 整体上移一点198 199 # 创建背景矩形,确保文字清晰可见200 explanation_bg = BackgroundRectangle(201 explanation,202 color=BLACK,203 fill_opacity=0.8,204 buff=0.2205 )206 207 # 先显示背景,再显示文字208 self.play(209 FadeIn(explanation_bg),210 Write(explanation)211 )212 self.wait(2)213def main():214 import os215 os.system("manim -pqh sequence_convergence.py SequenceConvergence")216 217if __name__ == "__main__":218 main() 讲解
这个视频围绕「数列的收敛性演示」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「数列的收敛性演示」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「数列的收敛性演示」中的核心对象,尤其留意数列收敛、epsilon-N 定义、函数图像之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从数列收敛、epsilon-N 定义、函数图像这些概念进入,继续沿相邻问题观察同一类数学结构。