mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
4827 lines
151 KiB
Python
4827 lines
151 KiB
Python
from big_ol_pile_of_manim_imports import *
|
|
from functools import reduce
|
|
|
|
# revert_to_original_skipping_status
|
|
|
|
def chi_func(n):
|
|
if n%2 == 0:
|
|
return 0
|
|
if n%4 == 1:
|
|
return 1
|
|
else:
|
|
return -1
|
|
|
|
class LatticePointScene(Scene):
|
|
CONFIG = {
|
|
"y_radius" : 6,
|
|
"x_radius" : None,
|
|
"plane_center" : ORIGIN,
|
|
"max_lattice_point_radius" : 6,
|
|
"dot_radius" : 0.075,
|
|
"secondary_line_ratio" : 0,
|
|
"plane_color" : BLUE_E,
|
|
"dot_color" : YELLOW,
|
|
"dot_drawing_stroke_color" : PINK,
|
|
"circle_color" : MAROON_D,
|
|
"radial_line_color" : RED,
|
|
}
|
|
def setup(self):
|
|
if self.x_radius is None:
|
|
self.x_radius = self.y_radius*FRAME_X_RADIUS/FRAME_Y_RADIUS
|
|
plane = ComplexPlane(
|
|
y_radius = self.y_radius,
|
|
x_radius = self.x_radius,
|
|
secondary_line_ratio = self.secondary_line_ratio,
|
|
radius = self.plane_color
|
|
)
|
|
plane.set_height(FRAME_HEIGHT)
|
|
plane.shift(self.plane_center)
|
|
self.add(plane)
|
|
self.plane = plane
|
|
|
|
self.setup_lattice_points()
|
|
|
|
def setup_lattice_points(self):
|
|
M = self.max_lattice_point_radius
|
|
int_range = list(range(-M, M+1))
|
|
self.lattice_points = VGroup()
|
|
for x, y in it.product(*[int_range]*2):
|
|
r_squared = x**2 + y**2
|
|
if r_squared > M**2:
|
|
continue
|
|
dot = Dot(
|
|
self.plane.coords_to_point(x, y),
|
|
color = self.dot_color,
|
|
radius = self.dot_radius,
|
|
)
|
|
dot.r_squared = r_squared
|
|
self.lattice_points.add(dot)
|
|
self.lattice_points.sort(
|
|
lambda p : get_norm(p - self.plane_center)
|
|
)
|
|
|
|
def get_circle(self, radius = None, color = None):
|
|
if radius is None:
|
|
radius = self.max_lattice_point_radius
|
|
if color is None:
|
|
color = self.circle_color
|
|
radius *= self.plane.get_space_unit_to_y_unit()
|
|
circle = Circle(
|
|
color = color,
|
|
radius = radius,
|
|
)
|
|
circle.move_to(self.plane.get_center())
|
|
return circle
|
|
|
|
def get_radial_line_with_label(self, radius = None, color = None):
|
|
if radius is None:
|
|
radius = self.max_lattice_point_radius
|
|
if color is None:
|
|
color = self.radial_line_color
|
|
radial_line = Line(
|
|
self.plane_center,
|
|
self.plane.coords_to_point(radius, 0),
|
|
color = color
|
|
)
|
|
r_squared = int(np.round(radius**2))
|
|
root_label = TexMobject("\\sqrt{%d}"%r_squared)
|
|
root_label.add_background_rectangle()
|
|
root_label.next_to(radial_line, UP, SMALL_BUFF)
|
|
|
|
return radial_line, root_label
|
|
|
|
def get_lattice_points_on_r_squared_circle(self, r_squared):
|
|
points = VGroup(*[dot for dot in self.lattice_points if dot.r_squared == r_squared])
|
|
points.sort(
|
|
lambda p : angle_of_vector(p-self.plane_center)%(2*np.pi)
|
|
)
|
|
return points
|
|
|
|
def draw_lattice_points(self, points = None, run_time = 4):
|
|
if points is None:
|
|
points = self.lattice_points
|
|
self.play(*[
|
|
DrawBorderThenFill(
|
|
dot,
|
|
stroke_width = 4,
|
|
stroke_color = self.dot_drawing_stroke_color,
|
|
run_time = run_time,
|
|
rate_func = squish_rate_func(
|
|
double_smooth, a, a + 0.25
|
|
),
|
|
)
|
|
for dot, a in zip(
|
|
points,
|
|
np.linspace(0, 0.75, len(points))
|
|
)
|
|
])
|
|
|
|
def add_axis_labels(self, spacing = 2):
|
|
x_max = int(self.plane.point_to_coords(FRAME_X_RADIUS*RIGHT)[0])
|
|
y_max = int(self.plane.point_to_coords(FRAME_Y_RADIUS*UP)[1])
|
|
x_range = list(range(spacing, x_max, spacing))
|
|
y_range = list(range(spacing, y_max, spacing))
|
|
for r in x_range, y_range:
|
|
r += [-n for n in r]
|
|
tick = Line(ORIGIN, MED_SMALL_BUFF*UP)
|
|
x_ticks = VGroup(*[
|
|
tick.copy().move_to(self.plane.coords_to_point(x, 0))
|
|
for x in x_range
|
|
])
|
|
tick.rotate(-np.pi/2)
|
|
y_ticks = VGroup(*[
|
|
tick.copy().move_to(self.plane.coords_to_point(0, y))
|
|
for y in y_range
|
|
])
|
|
x_labels = VGroup(*[
|
|
TexMobject(str(x))
|
|
for x in x_range
|
|
])
|
|
y_labels = VGroup(*[
|
|
TexMobject(str(y) + "i")
|
|
for y in y_range
|
|
])
|
|
|
|
for labels, ticks in (x_labels, x_ticks), (y_labels, y_ticks):
|
|
labels.scale(0.6)
|
|
for tex_mob, tick in zip(labels, ticks):
|
|
tex_mob.add_background_rectangle()
|
|
tex_mob.next_to(
|
|
tick,
|
|
tick.get_start() - tick.get_end(),
|
|
SMALL_BUFF
|
|
)
|
|
self.add(x_ticks, y_ticks, x_labels, y_labels)
|
|
digest_locals(self, [
|
|
"x_ticks", "y_ticks",
|
|
"x_labels", "y_labels",
|
|
])
|
|
|
|
def point_to_int_coords(self, point):
|
|
x, y = self.plane.point_to_coords(point)[:2]
|
|
return (int(np.round(x)), int(np.round(y)))
|
|
|
|
def dot_to_int_coords(self, dot):
|
|
return self.point_to_int_coords(dot.get_center())
|
|
|
|
|
|
######
|
|
|
|
class Introduction(PiCreatureScene):
|
|
def construct(self):
|
|
self.introduce_three_objects()
|
|
self.show_screen()
|
|
|
|
def introduce_three_objects(self):
|
|
primes = self.get_primes()
|
|
primes.to_corner(UP+RIGHT)
|
|
primes.shift(DOWN)
|
|
plane = self.get_complex_numbers()
|
|
plane.shift(2*LEFT)
|
|
pi_group = self.get_pi_group()
|
|
pi_group.next_to(primes, DOWN, buff = MED_LARGE_BUFF)
|
|
pi_group.shift_onto_screen()
|
|
|
|
morty = self.get_primary_pi_creature()
|
|
video = VideoIcon()
|
|
video.set_color(TEAL)
|
|
video.next_to(morty.get_corner(UP+LEFT), UP)
|
|
|
|
self.play(
|
|
morty.change_mode, "raise_right_hand",
|
|
DrawBorderThenFill(video)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Write(primes, run_time = 2),
|
|
morty.change_mode, "happy",
|
|
video.set_height, FRAME_WIDTH,
|
|
video.center,
|
|
video.set_fill, None, 0
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Write(plane, run_time = 2),
|
|
morty.change, "raise_right_hand"
|
|
)
|
|
self.wait()
|
|
self.remove(morty)
|
|
morty = morty.copy()
|
|
self.add(morty)
|
|
self.play(
|
|
ReplacementTransform(
|
|
morty.body,
|
|
pi_group.get_part_by_tex("pi"),
|
|
run_time = 1
|
|
),
|
|
FadeOut(VGroup(morty.eyes, morty.mouth)),
|
|
Write(VGroup(*pi_group[1:]))
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
plane.set_width, pi_group.get_width(),
|
|
plane.next_to, pi_group, DOWN, MED_LARGE_BUFF
|
|
)
|
|
|
|
def show_screen(self):
|
|
screen = ScreenRectangle(height = 4.3)
|
|
screen.to_edge(LEFT)
|
|
titles = VGroup(
|
|
TextMobject("From zeta video"),
|
|
TextMobject("Coming up")
|
|
)
|
|
for title in titles:
|
|
title.next_to(screen, UP)
|
|
title.set_color(YELLOW)
|
|
self.play(
|
|
ShowCreation(screen),
|
|
FadeIn(titles[0])
|
|
)
|
|
self.show_frame()
|
|
self.wait(2)
|
|
self.play(Transform(*titles))
|
|
self.wait(3)
|
|
|
|
def get_primes(self):
|
|
return TexMobject("2, 3, 5, 7, 11, 13, \\dots")
|
|
|
|
def get_complex_numbers(self):
|
|
plane = ComplexPlane(
|
|
x_radius = 3,
|
|
y_radius = 2.5,
|
|
)
|
|
plane.add_coordinates()
|
|
point = plane.number_to_point(complex(1, 2))
|
|
dot = Dot(point, radius = YELLOW)
|
|
label = TexMobject("1 + 2i")
|
|
label.add_background_rectangle()
|
|
label.next_to(dot, UP+RIGHT, buff = SMALL_BUFF)
|
|
label.set_color(YELLOW)
|
|
plane.label = label
|
|
plane.add(dot, label)
|
|
return plane
|
|
|
|
def get_pi_group(self):
|
|
result = TexMobject("\\pi", "=", "%.8f\\dots"%np.pi)
|
|
pi = result.get_part_by_tex("pi")
|
|
pi.scale(2, about_point = pi.get_right())
|
|
pi.set_color(MAROON_B)
|
|
return result
|
|
|
|
class ShowSum(TeacherStudentsScene):
|
|
CONFIG = {
|
|
"num_terms_to_add" : 40,
|
|
}
|
|
def construct(self):
|
|
self.say_words()
|
|
self.show_sum()
|
|
|
|
def say_words(self):
|
|
self.teacher_says("This won't be easy")
|
|
self.change_student_modes(
|
|
"hooray", "sassy", "angry"
|
|
)
|
|
self.wait(2)
|
|
|
|
def show_sum(self):
|
|
line = UnitInterval()
|
|
line.add_numbers(0, 1)
|
|
# line.shift(UP)
|
|
sum_point = line.number_to_point(np.pi/4)
|
|
|
|
numbers = [0] + [
|
|
((-1)**n)/(2.0*n + 1)
|
|
for n in range(self.num_terms_to_add)
|
|
]
|
|
partial_sums = np.cumsum(numbers)
|
|
points = list(map(line.number_to_point, partial_sums))
|
|
arrows = [
|
|
Arrow(
|
|
p1, p2,
|
|
tip_length = 0.2*min(1, get_norm(p1-p2)),
|
|
buff = 0
|
|
)
|
|
for p1, p2 in zip(points, points[1:])
|
|
]
|
|
dot = Dot(points[0])
|
|
|
|
sum_mob = TexMobject(
|
|
"1", "-\\frac{1}{3}",
|
|
"+\\frac{1}{5}", "-\\frac{1}{7}",
|
|
"+\\frac{1}{9}", "-\\frac{1}{11}",
|
|
"+\\cdots"
|
|
)
|
|
sum_mob.to_corner(UP+RIGHT)
|
|
lhs = TexMobject(
|
|
"\\frac{\\pi}{4}", "=",
|
|
)
|
|
lhs.next_to(sum_mob, LEFT)
|
|
lhs.set_color_by_tex("pi", YELLOW)
|
|
sum_arrow = Arrow(
|
|
lhs.get_part_by_tex("pi").get_bottom(),
|
|
sum_point
|
|
)
|
|
fading_terms = [
|
|
TexMobject(sign + "\\frac{1}{%d}"%(2*n + 1))
|
|
for n, sign in zip(
|
|
list(range(self.num_terms_to_add)),
|
|
it.cycle("+-")
|
|
)
|
|
]
|
|
for fading_term, arrow in zip(fading_terms, arrows):
|
|
fading_term.next_to(arrow, UP)
|
|
|
|
terms = it.chain(sum_mob, it.repeat(None))
|
|
last_arrows = it.chain([None], arrows)
|
|
last_fading_terms = it.chain([None], fading_terms)
|
|
|
|
self.change_student_modes(
|
|
*["pondering"]*3,
|
|
look_at_arg = line,
|
|
added_anims = [
|
|
FadeIn(VGroup(line, dot)),
|
|
FadeIn(lhs),
|
|
RemovePiCreatureBubble(
|
|
self.teacher,
|
|
target_mode = "raise_right_hand"
|
|
)
|
|
]
|
|
|
|
)
|
|
run_time = 1
|
|
for term, arrow, last_arrow, fading_term, last_fading_term in zip(
|
|
terms, arrows, last_arrows, fading_terms, last_fading_terms
|
|
):
|
|
anims = []
|
|
if term:
|
|
anims.append(Write(term))
|
|
if last_arrow:
|
|
anims.append(FadeOut(last_arrow))
|
|
if last_fading_term:
|
|
anims.append(FadeOut(last_fading_term))
|
|
dot_movement = ApplyMethod(dot.move_to, arrow.get_end())
|
|
anims.append(ShowCreation(arrow))
|
|
anims.append(dot_movement)
|
|
anims.append(FadeIn(fading_term))
|
|
self.play(*anims, run_time = run_time)
|
|
if term:
|
|
self.wait()
|
|
else:
|
|
run_time *= 0.8
|
|
self.play(
|
|
FadeOut(arrow),
|
|
FadeOut(fading_term),
|
|
dot.move_to, sum_point
|
|
)
|
|
self.play(ShowCreation(sum_arrow))
|
|
self.wait()
|
|
self.change_student_modes("erm", "confused", "maybe")
|
|
self.play(self.teacher.change_mode, "happy")
|
|
self.wait(2)
|
|
|
|
class FermatsDreamExcerptWrapper(Scene):
|
|
def construct(self):
|
|
words = TextMobject(
|
|
"From ``Fermat's dream'' by Kato, Kurokawa and Saito"
|
|
)
|
|
words.scale(0.8)
|
|
words.to_edge(UP)
|
|
self.add(words)
|
|
self.wait()
|
|
|
|
class ShowCalculus(PiCreatureScene):
|
|
def construct(self):
|
|
frac_sum = TexMobject(
|
|
"1 - \\frac{1}{3} + \\frac{1}{5} - \\frac{1}{7} + \\cdots",
|
|
)
|
|
int1 = TexMobject(
|
|
"= \\int_0^1 (1 - x^2 + x^4 - \\dots )\\,dx"
|
|
)
|
|
int2 = TexMobject(
|
|
"= \\int_0^1 \\frac{1}{1+x^2}\\,dx"
|
|
)
|
|
arctan = TexMobject("= \\tan^{-1}(1)")
|
|
pi_fourths = TexMobject("= \\frac{\\pi}{4}")
|
|
|
|
frac_sum.to_corner(UP+LEFT)
|
|
frac_sum.shift(RIGHT)
|
|
rhs_group = VGroup(int1, int2, arctan, pi_fourths)
|
|
rhs_group.arrange(
|
|
DOWN, buff = MED_LARGE_BUFF,
|
|
aligned_edge = LEFT
|
|
)
|
|
rhs_group.shift(
|
|
frac_sum.get_right() + MED_SMALL_BUFF*RIGHT \
|
|
-int1[0].get_left()
|
|
)
|
|
|
|
self.add(frac_sum)
|
|
modes = it.chain(["plain"], it.cycle(["confused"]))
|
|
for rhs, mode in zip(rhs_group, modes):
|
|
self.play(
|
|
FadeIn(rhs),
|
|
self.pi_creature.change, mode
|
|
)
|
|
self.wait()
|
|
self.change_mode("maybe")
|
|
self.wait()
|
|
self.look_at(rhs_group[-1])
|
|
self.wait()
|
|
self.pi_creature_says(
|
|
"Where's the \\\\ circle?",
|
|
bubble_kwargs = {"width" : 4, "height" : 3},
|
|
target_mode = "maybe"
|
|
)
|
|
self.look_at(rhs_group[0])
|
|
self.wait()
|
|
|
|
def create_pi_creature(self):
|
|
return Randolph(color = BLUE_C).to_corner(DOWN+LEFT)
|
|
|
|
class CertainRegularityInPrimes(LatticePointScene):
|
|
CONFIG = {
|
|
"y_radius" : 8,
|
|
"x_radius" : 20,
|
|
"max_lattice_point_radius" : 8,
|
|
"plane_center" : 2.5*RIGHT,
|
|
"primes" : [5, 13, 17, 29, 37, 41, 53],
|
|
"include_pi_formula" : True,
|
|
}
|
|
def construct(self):
|
|
if self.include_pi_formula:
|
|
self.add_pi_formula()
|
|
self.walk_through_primes()
|
|
|
|
def add_pi_formula(self):
|
|
formula = TexMobject(
|
|
"\\frac{\\pi}{4}", "=",
|
|
"1", "-", "\\frac{1}{3}",
|
|
"+", "\\frac{1}{5}", "-", "\\frac{1}{7}",
|
|
"+\\cdots"
|
|
)
|
|
formula.set_color_by_tex("pi", YELLOW)
|
|
formula.add_background_rectangle()
|
|
formula.to_corner(UP+LEFT, buff = MED_SMALL_BUFF)
|
|
self.add_foreground_mobject(formula)
|
|
|
|
def walk_through_primes(self):
|
|
primes = self.primes
|
|
lines_and_labels = [
|
|
self.get_radial_line_with_label(np.sqrt(p))
|
|
for p in primes
|
|
]
|
|
lines, labels = list(zip(*lines_and_labels))
|
|
circles = [
|
|
self.get_circle(np.sqrt(p))
|
|
for p in primes
|
|
]
|
|
dots_list = [
|
|
self.get_lattice_points_on_r_squared_circle(p)
|
|
for p in primes
|
|
]
|
|
groups = [
|
|
VGroup(*mobs)
|
|
for mobs in zip(lines, labels, circles, dots_list)
|
|
]
|
|
|
|
curr_group = groups[0]
|
|
self.play(Write(curr_group, run_time = 2))
|
|
self.wait()
|
|
for group in groups[1:]:
|
|
self.play(Transform(curr_group, group))
|
|
self.wait(2)
|
|
|
|
class Outline(PiCreatureScene):
|
|
def construct(self):
|
|
self.generate_list()
|
|
self.wonder_at_pi()
|
|
self.count_lattice_points()
|
|
self.write_steps_2_and_3()
|
|
self.show_chi()
|
|
self.show_complicated_formula()
|
|
self.show_last_step()
|
|
|
|
def generate_list(self):
|
|
steps = VGroup(
|
|
TextMobject("1. Count lattice points"),
|
|
TexMobject("2. \\text{ Things like }17 = ", "4", "^2 + ", "1", "^2"),
|
|
TexMobject("3. \\text{ Things like }17 = (", "4", " + ", "i", ")(", "4", " - ", "i", ")"),
|
|
TextMobject("4. Introduce $\\chi$"),
|
|
TextMobject("5. Shift perspective"),
|
|
)
|
|
for step in steps[1:3]:
|
|
step.set_color_by_tex("1", RED, substring = False)
|
|
step.set_color_by_tex("i", RED, substring = False)
|
|
step.set_color_by_tex("4", GREEN, substring = False)
|
|
steps.arrange(
|
|
DOWN,
|
|
buff = MED_LARGE_BUFF,
|
|
aligned_edge = LEFT
|
|
)
|
|
steps.to_corner(UP+LEFT)
|
|
|
|
self.steps = steps
|
|
|
|
def wonder_at_pi(self):
|
|
question = TexMobject("\\pi", "=???")
|
|
pi = question.get_part_by_tex("pi")
|
|
pi.scale(2, about_point = pi.get_right())
|
|
pi.set_color(YELLOW)
|
|
question.next_to(self.pi_creature.body, LEFT, aligned_edge = UP)
|
|
self.think(
|
|
"Who am I really?",
|
|
look_at_arg = question,
|
|
added_anims = [
|
|
FadeIn(question)
|
|
]
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
RemovePiCreatureBubble(self.pi_creature),
|
|
question.to_corner, UP+RIGHT
|
|
)
|
|
|
|
self.question = question
|
|
self.pi = question.get_part_by_tex("pi")
|
|
|
|
def count_lattice_points(self):
|
|
step = self.steps[0]
|
|
plane = NumberPlane(
|
|
x_radius = 10, y_radius = 10,
|
|
secondary_line_ratio = 0,
|
|
color = BLUE_E,
|
|
)
|
|
plane.set_height(6)
|
|
plane.next_to(step, DOWN)
|
|
plane.to_edge(LEFT)
|
|
circle = Circle(
|
|
color = YELLOW,
|
|
radius = get_norm(
|
|
plane.coords_to_point(10, 0) - \
|
|
plane.coords_to_point(0, 0)
|
|
)
|
|
)
|
|
plane_center = plane.coords_to_point(0, 0)
|
|
circle.move_to(plane_center)
|
|
lattice_points = VGroup(*[
|
|
Dot(
|
|
plane.coords_to_point(a, b),
|
|
radius = 0.05,
|
|
color = PINK,
|
|
)
|
|
for a in range(-10, 11)
|
|
for b in range(-10, 11)
|
|
if a**2 + b**2 <= 10**2
|
|
])
|
|
lattice_points.sort(
|
|
lambda p : get_norm(p - plane_center)
|
|
)
|
|
lattice_group = VGroup(plane, circle, lattice_points)
|
|
|
|
self.play(ShowCreation(circle))
|
|
self.play(Write(plane, run_time = 2), Animation(circle))
|
|
self.play(
|
|
*[
|
|
DrawBorderThenFill(
|
|
dot,
|
|
stroke_width = 4,
|
|
stroke_color = YELLOW,
|
|
run_time = 4,
|
|
rate_func = squish_rate_func(
|
|
double_smooth, a, a + 0.25
|
|
)
|
|
)
|
|
for dot, a in zip(
|
|
lattice_points,
|
|
np.linspace(0, 0.75, len(lattice_points))
|
|
)
|
|
]
|
|
)
|
|
self.play(
|
|
FadeIn(step)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
lattice_group.set_height, 2.5,
|
|
lattice_group.next_to, self.question, DOWN,
|
|
lattice_group.to_edge, RIGHT
|
|
)
|
|
|
|
def write_steps_2_and_3(self):
|
|
for step in self.steps[1:3]:
|
|
self.play(FadeIn(step))
|
|
self.wait(2)
|
|
self.wait()
|
|
|
|
def show_chi(self):
|
|
input_range = list(range(1, 7))
|
|
chis = VGroup(*[
|
|
TexMobject("\\chi(%d)"%n)
|
|
for n in input_range
|
|
])
|
|
chis.arrange(RIGHT, buff = LARGE_BUFF)
|
|
chis.set_stroke(WHITE, width = 1)
|
|
numerators = VGroup()
|
|
arrows = VGroup()
|
|
for chi, n in zip(chis, input_range):
|
|
arrow = TexMobject("\\Downarrow")
|
|
arrow.next_to(chi, DOWN, SMALL_BUFF)
|
|
arrows.add(arrow)
|
|
value = TexMobject(str(chi_func(n)))
|
|
value.set_color_by_tex("1", BLUE)
|
|
value.set_color_by_tex("-1", GREEN)
|
|
value.next_to(arrow, DOWN)
|
|
numerators.add(value)
|
|
group = VGroup(chis, arrows, numerators)
|
|
group.set_width(1.3*FRAME_X_RADIUS)
|
|
group.to_corner(DOWN+LEFT)
|
|
|
|
self.play(FadeIn(self.steps[3]))
|
|
self.play(*[
|
|
FadeIn(
|
|
mob,
|
|
run_time = 3,
|
|
lag_ratio = 0.5
|
|
)
|
|
for mob in [chis, arrows, numerators]
|
|
])
|
|
self.change_mode("pondering")
|
|
self.wait()
|
|
|
|
self.chis = chis
|
|
self.arrows = arrows
|
|
self.numerators = numerators
|
|
|
|
def show_complicated_formula(self):
|
|
rhs = TexMobject(
|
|
" = \\lim_{N \\to \\infty}",
|
|
" \\frac{4}{N}",
|
|
"\\sum_{n = 1}^N",
|
|
"\\sum_{d | n} \\chi(d)",
|
|
)
|
|
pi = self.pi
|
|
self.add(pi.copy())
|
|
pi.generate_target()
|
|
pi.target.next_to(self.steps[3], RIGHT, MED_LARGE_BUFF)
|
|
pi.target.shift(MED_LARGE_BUFF*DOWN)
|
|
rhs.next_to(pi.target, RIGHT)
|
|
|
|
self.play(
|
|
MoveToTarget(pi),
|
|
Write(rhs)
|
|
)
|
|
self.change_mode("confused")
|
|
self.wait(2)
|
|
|
|
self.complicated_formula = rhs
|
|
|
|
def show_last_step(self):
|
|
expression = TexMobject(
|
|
"=", "\\frac{\\quad}{1}",
|
|
*it.chain(*[
|
|
["+", "\\frac{\\quad}{%d}"%d]
|
|
for d in range(2, len(self.numerators)+1)
|
|
] + [["+ \\cdots"]])
|
|
)
|
|
over_four = TexMobject("\\quad \\over 4")
|
|
over_four.to_corner(DOWN+LEFT)
|
|
over_four.shift(UP)
|
|
pi = self.pi
|
|
pi.generate_target()
|
|
pi.target.scale(0.75)
|
|
pi.target.next_to(over_four, UP)
|
|
expression.next_to(over_four, RIGHT, align_using_submobjects = True)
|
|
self.numerators.generate_target()
|
|
for num, denom in zip(self.numerators.target, expression[1::2]):
|
|
num.scale(1.2)
|
|
num.next_to(denom, UP, MED_SMALL_BUFF)
|
|
|
|
self.play(
|
|
MoveToTarget(self.numerators),
|
|
MoveToTarget(pi),
|
|
Write(over_four),
|
|
FadeOut(self.chis),
|
|
FadeOut(self.arrows),
|
|
FadeOut(self.complicated_formula),
|
|
)
|
|
self.play(
|
|
Write(expression),
|
|
self.pi_creature.change_mode, "pondering"
|
|
)
|
|
self.wait(3)
|
|
|
|
########
|
|
def create_pi_creature(self):
|
|
return Randolph(color = BLUE_C).flip().to_corner(DOWN+RIGHT)
|
|
|
|
class CountLatticePoints(LatticePointScene):
|
|
CONFIG = {
|
|
"y_radius" : 11,
|
|
"max_lattice_point_radius" : 10,
|
|
"dot_radius" : 0.05,
|
|
"example_coords" : (7, 5),
|
|
}
|
|
def construct(self):
|
|
self.introduce_lattice_point()
|
|
self.draw_lattice_points_in_circle()
|
|
self.turn_points_int_units_of_area()
|
|
self.write_pi_R_squared()
|
|
self.allude_to_alternate_counting_method()
|
|
|
|
|
|
def introduce_lattice_point(self):
|
|
x, y = self.example_coords
|
|
example_dot = Dot(
|
|
self.plane.coords_to_point(x, y),
|
|
color = self.dot_color,
|
|
radius = 1.5*self.dot_radius,
|
|
)
|
|
label = TexMobject(str(self.example_coords))
|
|
label.add_background_rectangle()
|
|
label.next_to(example_dot, UP+RIGHT, buff = 0)
|
|
h_line = Line(
|
|
ORIGIN, self.plane.coords_to_point(x, 0),
|
|
color = GREEN
|
|
)
|
|
v_line = Line(
|
|
h_line.get_end(), self.plane.coords_to_point(x, y),
|
|
color = RED
|
|
)
|
|
lines = VGroup(h_line, v_line)
|
|
|
|
dots = self.lattice_points.copy()
|
|
random.shuffle(dots.submobjects)
|
|
|
|
self.play(*[
|
|
ApplyMethod(
|
|
dot.set_fill, None, 0,
|
|
run_time = 3,
|
|
rate_func = squish_rate_func(
|
|
lambda t : 1 - there_and_back(t),
|
|
a, a + 0.5
|
|
),
|
|
remover = True
|
|
)
|
|
for dot, a in zip(dots, np.linspace(0, 0.5, len(dots)))
|
|
])
|
|
self.play(
|
|
Write(label),
|
|
ShowCreation(lines),
|
|
DrawBorderThenFill(example_dot),
|
|
run_time = 2,
|
|
)
|
|
self.wait(2)
|
|
self.play(*list(map(FadeOut, [label, lines, example_dot])))
|
|
|
|
def draw_lattice_points_in_circle(self):
|
|
circle = self.get_circle()
|
|
radius = Line(ORIGIN, circle.get_right())
|
|
radius.set_color(RED)
|
|
brace = Brace(radius, DOWN, buff = SMALL_BUFF)
|
|
radius_label = brace.get_text(
|
|
str(self.max_lattice_point_radius),
|
|
buff = SMALL_BUFF
|
|
)
|
|
radius_label.add_background_rectangle()
|
|
brace.add(radius_label)
|
|
|
|
self.play(
|
|
ShowCreation(circle),
|
|
Rotating(radius, about_point = ORIGIN),
|
|
run_time = 2,
|
|
rate_func = smooth,
|
|
)
|
|
self.play(FadeIn(brace))
|
|
self.add_foreground_mobject(brace)
|
|
self.draw_lattice_points()
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [brace, radius])))
|
|
|
|
self.circle = circle
|
|
|
|
def turn_points_int_units_of_area(self):
|
|
square = Square(fill_opacity = 0.9)
|
|
unit_line = Line(
|
|
self.plane.coords_to_point(0, 0),
|
|
self.plane.coords_to_point(1, 0),
|
|
)
|
|
square.set_width(unit_line.get_width())
|
|
squares = VGroup(*[
|
|
square.copy().move_to(point)
|
|
for point in self.lattice_points
|
|
])
|
|
squares.set_color_by_gradient(BLUE_E, GREEN_E)
|
|
squares.set_stroke(WHITE, 1)
|
|
point_copies = self.lattice_points.copy()
|
|
|
|
self.play(
|
|
ReplacementTransform(
|
|
point_copies, squares,
|
|
run_time = 3,
|
|
lag_ratio = 0.5,
|
|
lag_factor = 4,
|
|
),
|
|
Animation(self.lattice_points)
|
|
)
|
|
self.wait()
|
|
self.play(FadeOut(squares), Animation(self.lattice_points))
|
|
|
|
def write_pi_R_squared(self):
|
|
equations = VGroup(*[
|
|
VGroup(
|
|
TextMobject(
|
|
"\\# Lattice points\\\\",
|
|
"within radius ", R,
|
|
alignment = ""
|
|
),
|
|
TexMobject(
|
|
"\\approx \\pi", "(", R, ")^2"
|
|
)
|
|
).arrange(RIGHT)
|
|
for R in ("10", "1{,}000{,}000", "R")
|
|
])
|
|
radius_10_eq, radius_million_eq, radius_R_eq = equations
|
|
for eq in equations:
|
|
for tex_mob in eq:
|
|
tex_mob.set_color_by_tex("0", BLUE)
|
|
radius_10_eq.to_corner(UP+LEFT)
|
|
radius_million_eq.next_to(radius_10_eq, DOWN, LARGE_BUFF)
|
|
radius_million_eq.to_edge(LEFT)
|
|
brace = Brace(radius_million_eq, DOWN)
|
|
brace.add(brace.get_text("More accurate"))
|
|
brace.set_color(YELLOW)
|
|
|
|
background = FullScreenFadeRectangle(opacity = 0.9)
|
|
|
|
self.play(
|
|
FadeIn(background),
|
|
Write(radius_10_eq)
|
|
)
|
|
self.wait(2)
|
|
self.play(ReplacementTransform(
|
|
radius_10_eq.copy(),
|
|
radius_million_eq
|
|
))
|
|
self.play(FadeIn(brace))
|
|
self.wait(3)
|
|
|
|
self.radius_10_eq = radius_10_eq
|
|
self.million_group = VGroup(radius_million_eq, brace)
|
|
self.radius_R_eq = radius_R_eq
|
|
|
|
def allude_to_alternate_counting_method(self):
|
|
alt_count = TextMobject(
|
|
"(...something else...)", "$R^2$", "=",
|
|
arg_separator = ""
|
|
)
|
|
alt_count.to_corner(UP+LEFT)
|
|
alt_count.set_color_by_tex("something", MAROON_B)
|
|
self.radius_R_eq.next_to(alt_count, RIGHT)
|
|
|
|
final_group = VGroup(
|
|
alt_count.get_part_by_tex("something"),
|
|
self.radius_R_eq[1].get_part_by_tex("pi"),
|
|
).copy()
|
|
|
|
self.play(
|
|
FadeOut(self.million_group),
|
|
Write(alt_count),
|
|
ReplacementTransform(
|
|
self.radius_10_eq,
|
|
self.radius_R_eq
|
|
)
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
final_group.arrange, RIGHT,
|
|
final_group.next_to, ORIGIN, UP
|
|
)
|
|
rect = BackgroundRectangle(final_group)
|
|
self.play(FadeIn(rect), Animation(final_group))
|
|
self.wait(2)
|
|
|
|
class SoYouPlay(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says(
|
|
"So you play!",
|
|
run_time = 2
|
|
)
|
|
self.change_student_modes("happy", "thinking", "hesitant")
|
|
self.wait()
|
|
self.look_at(Dot().to_corner(UP+LEFT))
|
|
self.wait(3)
|
|
|
|
class CountThroughRings(LatticePointScene):
|
|
CONFIG = {
|
|
"example_coords" : (3, 2),
|
|
"num_rings_to_show_explicitly" : 7,
|
|
"x_radius" : 15,
|
|
|
|
"plane_center" : 2*RIGHT,
|
|
"max_lattice_point_radius" : 5,
|
|
}
|
|
def construct(self):
|
|
self.add_lattice_points()
|
|
self.preview_rings()
|
|
self.isolate_single_ring()
|
|
self.show_specific_lattice_point_distance()
|
|
self.count_through_rings()
|
|
|
|
def add_lattice_points(self):
|
|
big_circle = self.get_circle()
|
|
self.add(big_circle)
|
|
self.add(self.lattice_points)
|
|
self.big_circle = big_circle
|
|
|
|
def preview_rings(self):
|
|
radii = list(set([
|
|
np.sqrt(p.r_squared) for p in self.lattice_points
|
|
]))
|
|
radii.sort()
|
|
circles = VGroup(*[
|
|
self.get_circle(radius = r)
|
|
for r in radii
|
|
])
|
|
circles.set_stroke(width = 2)
|
|
|
|
self.add_foreground_mobject(self.lattice_points)
|
|
self.play(FadeIn(circles))
|
|
self.play(OldLaggedStart(
|
|
ApplyMethod,
|
|
circles,
|
|
arg_creator = lambda m : (m.set_stroke, PINK, 4),
|
|
rate_func = there_and_back,
|
|
))
|
|
self.wait()
|
|
self.remove_foreground_mobject(self.lattice_points)
|
|
|
|
digest_locals(self, ["circles", "radii"])
|
|
|
|
def isolate_single_ring(self):
|
|
x, y = self.example_coords
|
|
example_circle = self.circles[
|
|
self.radii.index(np.sqrt(x**2 + y**2))
|
|
]
|
|
self.circles.remove(example_circle)
|
|
points_on_example_circle = self.get_lattice_points_on_r_squared_circle(
|
|
x**2 + y**2
|
|
).copy()
|
|
|
|
self.play(
|
|
FadeOut(self.circles),
|
|
self.lattice_points.set_fill, GREY, 0.5,
|
|
Animation(points_on_example_circle)
|
|
)
|
|
self.wait()
|
|
|
|
digest_locals(self, ["points_on_example_circle", "example_circle"])
|
|
|
|
def show_specific_lattice_point_distance(self):
|
|
x, y = self.example_coords
|
|
dot = Dot(
|
|
self.plane.coords_to_point(x, y),
|
|
color = self.dot_color,
|
|
radius = self.dot_radius
|
|
)
|
|
label = TexMobject("(a, b)")
|
|
num_label = TexMobject(str(self.example_coords))
|
|
for mob in label, num_label:
|
|
mob.add_background_rectangle()
|
|
mob.next_to(dot, UP + RIGHT, SMALL_BUFF)
|
|
a, b = label[1][1].copy(), label[1][3].copy()
|
|
|
|
x_spot = self.plane.coords_to_point(x, 0)
|
|
radial_line = Line(self.plane_center, dot)
|
|
h_line = Line(self.plane_center, x_spot)
|
|
h_line.set_color(GREEN)
|
|
v_line = Line(x_spot, dot)
|
|
v_line.set_color(RED)
|
|
|
|
distance = TexMobject("\\sqrt{a^2 + b^2}")
|
|
distance_num = TexMobject("\\sqrt{%d}"%(x**2 + y**2))
|
|
for mob in distance, distance_num:
|
|
mob.scale(0.75)
|
|
mob.add_background_rectangle()
|
|
mob.next_to(radial_line.get_center(), UP, SMALL_BUFF)
|
|
mob.rotate(
|
|
radial_line.get_angle(),
|
|
about_point = mob.get_bottom()
|
|
)
|
|
|
|
self.play(Write(label))
|
|
self.play(
|
|
ApplyMethod(a.next_to, h_line, DOWN, SMALL_BUFF),
|
|
ApplyMethod(b.next_to, v_line, RIGHT, SMALL_BUFF),
|
|
ShowCreation(h_line),
|
|
ShowCreation(v_line),
|
|
)
|
|
self.play(ShowCreation(radial_line))
|
|
self.play(Write(distance))
|
|
self.wait(2)
|
|
|
|
a_num, b_num = [
|
|
TexMobject(str(coord))[0]
|
|
for coord in self.example_coords
|
|
]
|
|
a_num.move_to(a, UP)
|
|
b_num.move_to(b, LEFT)
|
|
self.play(
|
|
Transform(label, num_label),
|
|
Transform(a, a_num),
|
|
Transform(b, b_num),
|
|
)
|
|
self.wait()
|
|
self.play(Transform(distance, distance_num))
|
|
self.wait(3)
|
|
self.play(*list(map(FadeOut, [
|
|
self.example_circle, self.points_on_example_circle,
|
|
distance, a, b,
|
|
radial_line, h_line, v_line,
|
|
label
|
|
])))
|
|
|
|
def count_through_rings(self):
|
|
counts = [
|
|
len(self.get_lattice_points_on_r_squared_circle(N))
|
|
for N in range(self.max_lattice_point_radius**2 + 1)
|
|
]
|
|
left_list = VGroup(*[
|
|
TexMobject(
|
|
"\\sqrt{%d} \\Rightarrow"%n, str(count)
|
|
)
|
|
for n, count in zip(
|
|
list(range(self.num_rings_to_show_explicitly)),
|
|
counts
|
|
)
|
|
])
|
|
left_counts = VGroup()
|
|
left_roots = VGroup()
|
|
for mob in left_list:
|
|
mob[1].set_color(YELLOW)
|
|
left_counts.add(VGroup(mob[1]))
|
|
mob.add_background_rectangle()
|
|
left_roots.add(VGroup(mob[0], mob[1][0]))
|
|
|
|
left_list.arrange(
|
|
DOWN,
|
|
buff = MED_LARGE_BUFF,
|
|
aligned_edge = LEFT,
|
|
)
|
|
left_list.to_corner(UP + LEFT)
|
|
|
|
top_list = VGroup(*[
|
|
TexMobject("%d, "%count)
|
|
for count in counts
|
|
])
|
|
top_list.set_color(YELLOW)
|
|
top_list.arrange(RIGHT, aligned_edge = DOWN)
|
|
top_list.set_width(FRAME_WIDTH - MED_LARGE_BUFF)
|
|
top_list.to_edge(UP, buff = SMALL_BUFF)
|
|
top_rect = BackgroundRectangle(top_list)
|
|
|
|
for r_squared, count_mob, root in zip(it.count(), left_counts, left_roots):
|
|
self.show_ring_count(
|
|
r_squared,
|
|
count_mob,
|
|
added_anims = [FadeIn(root)]
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
FadeOut(left_roots),
|
|
FadeIn(top_rect),
|
|
*[
|
|
ReplacementTransform(
|
|
lc, VGroup(tc),
|
|
path_arc = np.pi/2
|
|
)
|
|
for lc, tc in zip(left_counts, top_list)
|
|
]
|
|
)
|
|
for r_squared in range(len(left_counts), self.max_lattice_point_radius**2 + 1):
|
|
self.show_ring_count(
|
|
r_squared, top_list[r_squared],
|
|
)
|
|
self.wait(3)
|
|
|
|
|
|
def show_ring_count(
|
|
self, radius_squared, target,
|
|
added_anims = None,
|
|
run_time = 1
|
|
):
|
|
added_anims = added_anims or []
|
|
radius = np.sqrt(radius_squared)
|
|
points = self.get_lattice_points_on_r_squared_circle(radius_squared)
|
|
points.save_state()
|
|
circle = self.get_circle(radius)
|
|
radial_line = Line(
|
|
self.plane_center, self.plane.coords_to_point(radius, 0),
|
|
color = RED
|
|
)
|
|
root = TexMobject("\\sqrt{%d}"%radius_squared)
|
|
root.add_background_rectangle()
|
|
root.set_width(
|
|
min(0.7*radial_line.get_width(), root.get_width())
|
|
)
|
|
root.next_to(radial_line, DOWN, SMALL_BUFF)
|
|
if not hasattr(self, "little_circle"):
|
|
self.little_circle = circle
|
|
if not hasattr(self, "radial_line"):
|
|
self.radial_line = radial_line
|
|
if not hasattr(self, "root"):
|
|
self.root = root
|
|
if hasattr(self, "last_points"):
|
|
added_anims += [self.last_points.restore]
|
|
self.last_points = points
|
|
|
|
if radius_squared == 0:
|
|
points.set_fill(YELLOW, 1)
|
|
self.play(
|
|
DrawBorderThenFill(points, stroke_color = PINK),
|
|
*added_anims,
|
|
run_time = run_time
|
|
)
|
|
self.play(ReplacementTransform(
|
|
points.copy(), target
|
|
))
|
|
return
|
|
points.set_fill(YELLOW, 1)
|
|
self.play(
|
|
Transform(self.little_circle, circle),
|
|
Transform(self.radial_line, radial_line),
|
|
Transform(self.root, root),
|
|
DrawBorderThenFill(
|
|
points,
|
|
stroke_width = 4,
|
|
stroke_color = PINK,
|
|
),
|
|
*added_anims,
|
|
run_time = run_time
|
|
)
|
|
self.wait(run_time)
|
|
if len(points) > 0:
|
|
mover = points.copy()
|
|
else:
|
|
mover = VectorizedPoint(self.plane_center)
|
|
self.play(ReplacementTransform(mover, target, run_time = run_time))
|
|
|
|
class LookAtExampleRing(LatticePointScene):
|
|
CONFIG = {
|
|
"dot_radius" : 0.1,
|
|
"plane_center" : 2*LEFT,
|
|
"x_radius" : 17,
|
|
"y_radius" : 7,
|
|
}
|
|
def construct(self):
|
|
self.analyze_25()
|
|
self.analyze_11()
|
|
|
|
def analyze_25(self):
|
|
x_color = GREEN
|
|
y_color = RED
|
|
circle = self.get_circle(radius = 5)
|
|
points = self.get_lattice_points_on_r_squared_circle(25)
|
|
radius, root_label = self.get_radial_line_with_label(5)
|
|
coords_list = [(5, 0), (4, 3), (3, 4), (0, 5), (-3, 4), (-4, 3)]
|
|
labels = [
|
|
TexMobject("(", str(x), ",", str(y), ")")
|
|
for x, y in coords_list
|
|
]
|
|
for label in labels:
|
|
label.x = label[1]
|
|
label.y = label[3]
|
|
label.x.set_color(x_color)
|
|
label.y.set_color(y_color)
|
|
label.add_background_rectangle()
|
|
|
|
for label, point in zip(labels, points):
|
|
x_coord = (point.get_center() - self.plane_center)[0]
|
|
vect = UP+RIGHT if x_coord >= 0 else UP+LEFT
|
|
label.next_to(point, vect, SMALL_BUFF)
|
|
label.point = point
|
|
|
|
def special_str(n):
|
|
return "(%d)"%n if n < 0 else str(n)
|
|
|
|
sums_of_squares = [
|
|
TexMobject(
|
|
special_str(x), "^2", "+",
|
|
special_str(y), "^2", "= 25"
|
|
)
|
|
for x, y in coords_list
|
|
]
|
|
for tex_mob in sums_of_squares:
|
|
tex_mob.x = tex_mob[0]
|
|
tex_mob.y = tex_mob[3]
|
|
tex_mob.x.set_color(x_color)
|
|
tex_mob.y.set_color(y_color)
|
|
tex_mob.add_background_rectangle()
|
|
tex_mob.to_corner(UP+RIGHT)
|
|
|
|
self.play(
|
|
ShowCreation(radius),
|
|
Write(root_label)
|
|
)
|
|
self.play(
|
|
ShowCreation(circle),
|
|
Rotating(
|
|
radius,
|
|
about_point = self.plane_center,
|
|
rate_func = smooth,
|
|
),
|
|
FadeIn(points, lag_ratio = 0.5),
|
|
run_time = 2,
|
|
)
|
|
self.wait()
|
|
|
|
curr_label = labels[0]
|
|
curr_sum_of_squares = sums_of_squares[0]
|
|
self.play(
|
|
Write(curr_label),
|
|
curr_label.point.set_color, PINK
|
|
)
|
|
x, y = curr_label.x.copy(), curr_label.y.copy()
|
|
self.play(
|
|
Transform(x, curr_sum_of_squares.x),
|
|
Transform(y, curr_sum_of_squares.y),
|
|
)
|
|
self.play(
|
|
Write(curr_sum_of_squares),
|
|
Animation(VGroup(x, y))
|
|
)
|
|
self.remove(x, y)
|
|
self.wait()
|
|
|
|
for label, sum_of_squares in zip(labels, sums_of_squares)[1:]:
|
|
self.play(
|
|
ReplacementTransform(curr_label, label),
|
|
label.point.set_color, PINK,
|
|
curr_label.point.set_color, self.dot_color
|
|
)
|
|
curr_label = label
|
|
self.play(
|
|
ReplacementTransform(
|
|
curr_sum_of_squares, sum_of_squares
|
|
)
|
|
)
|
|
curr_sum_of_squares = sum_of_squares
|
|
self.wait()
|
|
|
|
points.save_state()
|
|
points.generate_target()
|
|
for i, point in enumerate(points.target):
|
|
point.move_to(
|
|
self.plane.coords_to_point(i%3, i//3)
|
|
)
|
|
points.target.next_to(circle, RIGHT)
|
|
|
|
self.play(MoveToTarget(
|
|
points,
|
|
run_time = 2,
|
|
))
|
|
self.wait()
|
|
self.play(points.restore, run_time = 2)
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [
|
|
curr_label, curr_sum_of_squares,
|
|
circle, points,
|
|
radius, root_label
|
|
])))
|
|
|
|
def analyze_11(self):
|
|
R = np.sqrt(11)
|
|
circle = self.get_circle(radius = R)
|
|
radius, root_label = self.get_radial_line_with_label(R)
|
|
equation = TexMobject("11 \\ne ", "a", "^2", "+", "b", "^2")
|
|
equation.set_color_by_tex("a", GREEN)
|
|
equation.set_color_by_tex("b", RED)
|
|
equation.add_background_rectangle()
|
|
equation.to_corner(UP+RIGHT)
|
|
|
|
self.play(
|
|
Write(root_label),
|
|
ShowCreation(radius),
|
|
run_time = 1
|
|
)
|
|
self.play(
|
|
ShowCreation(circle),
|
|
Rotating(
|
|
radius,
|
|
about_point = self.plane_center,
|
|
rate_func = smooth,
|
|
),
|
|
run_time = 2,
|
|
)
|
|
self.wait()
|
|
self.play(Write(equation))
|
|
self.wait(3)
|
|
|
|
class Given2DThinkComplex(TeacherStudentsScene):
|
|
def construct(self):
|
|
tex = TextMobject("2D $\\Leftrightarrow$ Complex numbers")
|
|
plane = ComplexPlane(
|
|
x_radius = 0.6*FRAME_X_RADIUS,
|
|
y_radius = 0.6*FRAME_Y_RADIUS,
|
|
)
|
|
plane.add_coordinates()
|
|
plane.set_height(FRAME_Y_RADIUS)
|
|
plane.to_corner(UP+LEFT)
|
|
|
|
self.teacher_says(tex)
|
|
self.change_student_modes("pondering", "confused", "erm")
|
|
self.wait()
|
|
self.play(
|
|
Write(plane),
|
|
RemovePiCreatureBubble(
|
|
self.teacher,
|
|
target_mode = "raise_right_hand"
|
|
)
|
|
)
|
|
self.change_student_modes(
|
|
*["thinking"]*3,
|
|
look_at_arg = plane
|
|
)
|
|
self.wait(3)
|
|
|
|
class IntroduceComplexConjugate(LatticePointScene):
|
|
CONFIG = {
|
|
"y_radius" : 20,
|
|
"x_radius" : 30,
|
|
"plane_scale_factor" : 1.7,
|
|
"plane_center" : 2*LEFT,
|
|
"example_coords" : (3, 4),
|
|
"x_color" : GREEN,
|
|
"y_color" : RED,
|
|
}
|
|
def construct(self):
|
|
self.resize_plane()
|
|
self.write_points_with_complex_coords()
|
|
self.introduce_complex_conjugate()
|
|
self.show_confusion()
|
|
self.expand_algebraically()
|
|
self.discuss_geometry()
|
|
self.show_geometrically()
|
|
|
|
def resize_plane(self):
|
|
self.plane.scale(
|
|
self.plane_scale_factor,
|
|
about_point = self.plane_center
|
|
)
|
|
self.plane.set_stroke(width = 1)
|
|
self.plane.axes.set_stroke(width = 3)
|
|
|
|
def write_points_with_complex_coords(self):
|
|
x, y = self.example_coords
|
|
x_color = self.x_color
|
|
y_color = self.y_color
|
|
|
|
point = self.plane.coords_to_point(x, y)
|
|
dot = Dot(point, color = self.dot_color)
|
|
x_point = self.plane.coords_to_point(x, 0)
|
|
h_arrow = Arrow(self.plane_center, x_point, buff = 0)
|
|
v_arrow = Arrow(x_point, point, buff = 0)
|
|
h_arrow.set_color(x_color)
|
|
v_arrow.set_color(y_color)
|
|
x_coord = TexMobject(str(x))
|
|
x_coord.next_to(h_arrow, DOWN, SMALL_BUFF)
|
|
x_coord.set_color(x_color)
|
|
x_coord.add_background_rectangle()
|
|
y_coord = TexMobject(str(y))
|
|
imag_y_coord = TexMobject(str(y) + "i")
|
|
for coord in y_coord, imag_y_coord:
|
|
coord.next_to(v_arrow, RIGHT, SMALL_BUFF)
|
|
coord.set_color(y_color)
|
|
coord.add_background_rectangle()
|
|
|
|
tuple_label = TexMobject(str((x, y)))
|
|
tuple_label[1].set_color(x_color)
|
|
tuple_label[3].set_color(y_color)
|
|
complex_label = TexMobject("%d+%di"%(x, y))
|
|
complex_label[0].set_color(x_color)
|
|
complex_label[2].set_color(y_color)
|
|
for label in tuple_label, complex_label:
|
|
label.add_background_rectangle()
|
|
label.next_to(dot, UP+RIGHT, buff = 0)
|
|
|
|
y_range = list(range(-9, 10, 3))
|
|
ticks = VGroup(*[
|
|
Line(
|
|
ORIGIN, MED_SMALL_BUFF*RIGHT
|
|
).move_to(self.plane.coords_to_point(0, y))
|
|
for y in y_range
|
|
])
|
|
imag_coords = VGroup()
|
|
for y, tick in zip(y_range, ticks):
|
|
if y == 0:
|
|
continue
|
|
if y == 1:
|
|
tex = "i"
|
|
elif y == -1:
|
|
tex = "-i"
|
|
else:
|
|
tex = "%di"%y
|
|
imag_coord = TexMobject(tex)
|
|
imag_coord.scale(0.75)
|
|
imag_coord.add_background_rectangle()
|
|
imag_coord.next_to(tick, LEFT, SMALL_BUFF)
|
|
imag_coords.add(imag_coord)
|
|
|
|
self.add(dot)
|
|
self.play(
|
|
ShowCreation(h_arrow),
|
|
Write(x_coord)
|
|
)
|
|
self.play(
|
|
ShowCreation(v_arrow),
|
|
Write(y_coord)
|
|
)
|
|
self.play(FadeIn(tuple_label))
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [tuple_label, y_coord])))
|
|
self.play(*list(map(FadeIn, [complex_label, imag_y_coord])))
|
|
self.play(*list(map(Write, [imag_coords, ticks])))
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [
|
|
v_arrow, h_arrow,
|
|
x_coord, imag_y_coord,
|
|
])))
|
|
|
|
self.complex_label = complex_label
|
|
self.example_dot = dot
|
|
|
|
def introduce_complex_conjugate(self):
|
|
x, y = self.example_coords
|
|
equation = VGroup(
|
|
TexMobject("25 = ", str(x), "^2", "+", str(y), "^2", "="),
|
|
TexMobject("(", str(x), "+", str(y), "i", ")"),
|
|
TexMobject("(", str(x), "-", str(y), "i", ")"),
|
|
)
|
|
equation.arrange(
|
|
RIGHT, buff = SMALL_BUFF,
|
|
)
|
|
VGroup(*equation[-2:]).shift(0.5*SMALL_BUFF*DOWN)
|
|
equation.scale(0.9)
|
|
equation.to_corner(UP+RIGHT, buff = MED_SMALL_BUFF)
|
|
equation.shift(MED_LARGE_BUFF*DOWN)
|
|
for tex_mob in equation:
|
|
tex_mob.set_color_by_tex(str(x), self.x_color)
|
|
tex_mob.set_color_by_tex(str(y), self.y_color)
|
|
tex_mob.add_background_rectangle()
|
|
|
|
dot = Dot(
|
|
self.plane.coords_to_point(x, -y),
|
|
color = self.dot_color
|
|
)
|
|
label = TexMobject("%d-%di"%(x, y))
|
|
label[0].set_color(self.x_color)
|
|
label[2].set_color(self.y_color)
|
|
label.add_background_rectangle()
|
|
label.next_to(dot, DOWN+RIGHT, buff = 0)
|
|
|
|
brace = Brace(equation[-1], DOWN)
|
|
conjugate_words = TextMobject("Complex \\\\ conjugate")
|
|
conjugate_words.scale(0.8)
|
|
conjugate_words.add_background_rectangle()
|
|
conjugate_words.next_to(brace, DOWN)
|
|
|
|
self.play(FadeIn(
|
|
equation,
|
|
run_time = 3,
|
|
lag_ratio = 0.5
|
|
))
|
|
self.wait(2)
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(conjugate_words, run_time = 2)
|
|
)
|
|
self.wait()
|
|
self.play(*[
|
|
ReplacementTransform(m1.copy(), m2)
|
|
for m1, m2 in [
|
|
(self.example_dot, dot),
|
|
(self.complex_label, label),
|
|
]
|
|
])
|
|
self.wait(2)
|
|
|
|
self.conjugate_label = VGroup(brace, conjugate_words)
|
|
self.equation = equation
|
|
self.conjugate_dot = dot
|
|
|
|
def show_confusion(self):
|
|
randy = Randolph(color = BLUE_C).to_corner(DOWN+LEFT)
|
|
morty = Mortimer().to_edge(DOWN)
|
|
randy.make_eye_contact(morty)
|
|
|
|
self.play(*list(map(FadeIn, [randy, morty])))
|
|
self.play(PiCreatureSays(
|
|
randy, "Wait \\dots why?",
|
|
target_mode = "confused",
|
|
))
|
|
self.play(Blink(randy))
|
|
self.wait(2)
|
|
self.play(
|
|
RemovePiCreatureBubble(
|
|
randy, target_mode = "erm",
|
|
),
|
|
PiCreatureSays(
|
|
morty, "Now it's a \\\\ factoring problem!",
|
|
target_mode = "hooray",
|
|
bubble_kwargs = {"width" : 5, "height" : 3}
|
|
)
|
|
)
|
|
self.play(
|
|
morty.look_at, self.equation,
|
|
randy.look_at, self.equation,
|
|
)
|
|
self.play(Blink(morty))
|
|
self.play(randy.change_mode, "pondering")
|
|
self.play(RemovePiCreatureBubble(morty))
|
|
self.play(*list(map(FadeOut, [randy, morty])))
|
|
|
|
def expand_algebraically(self):
|
|
x, y = self.example_coords
|
|
expansion = VGroup(
|
|
TexMobject(str(x), "^2"),
|
|
TexMobject("-", "(", str(y), "i", ")^2")
|
|
)
|
|
expansion.arrange(RIGHT, buff = SMALL_BUFF)
|
|
expansion.next_to(
|
|
VGroup(*self.equation[-2:]),
|
|
DOWN, LARGE_BUFF
|
|
)
|
|
alt_y_term = TexMobject("+", str(y), "^2")
|
|
alt_y_term.move_to(expansion[1], LEFT)
|
|
for tex_mob in list(expansion) + [alt_y_term]:
|
|
tex_mob.set_color_by_tex(str(x), self.x_color)
|
|
tex_mob.set_color_by_tex(str(y), self.y_color)
|
|
tex_mob.rect = BackgroundRectangle(tex_mob)
|
|
|
|
x1 = self.equation[-2][1][1]
|
|
x2 = self.equation[-1][1][1]
|
|
y1 = VGroup(*self.equation[-2][1][3:5])
|
|
y2 = VGroup(*self.equation[-1][1][2:5])
|
|
vect = MED_LARGE_BUFF*UP
|
|
|
|
self.play(FadeOut(self.conjugate_label))
|
|
group = VGroup(x1, x2)
|
|
self.play(group.shift, -vect)
|
|
self.play(
|
|
FadeIn(expansion[0].rect),
|
|
ReplacementTransform(group.copy(), expansion[0]),
|
|
)
|
|
self.play(group.shift, vect)
|
|
group = VGroup(x1, y2)
|
|
self.play(group.shift, -vect)
|
|
self.wait()
|
|
self.play(group.shift, vect)
|
|
group = VGroup(x2, y1)
|
|
self.play(group.shift, -vect)
|
|
self.wait()
|
|
self.play(group.shift, vect)
|
|
group = VGroup(*it.chain(y1, y2))
|
|
self.play(group.shift, -vect)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(expansion[1].rect),
|
|
ReplacementTransform(group.copy(), expansion[1]),
|
|
)
|
|
self.play(group.shift, vect)
|
|
self.wait(2)
|
|
self.play(
|
|
Transform(expansion[1].rect, alt_y_term.rect),
|
|
Transform(expansion[1], alt_y_term),
|
|
)
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [
|
|
expansion[0].rect,
|
|
expansion[1].rect,
|
|
expansion
|
|
])))
|
|
|
|
def discuss_geometry(self):
|
|
randy = Randolph(color = BLUE_C)
|
|
randy.scale(0.8)
|
|
randy.to_corner(DOWN+LEFT)
|
|
morty = Mortimer()
|
|
morty.set_height(randy.get_height())
|
|
morty.next_to(randy, RIGHT)
|
|
randy.make_eye_contact(morty)
|
|
screen = ScreenRectangle(height = 3.5)
|
|
screen.to_corner(DOWN+RIGHT, buff = MED_SMALL_BUFF)
|
|
|
|
self.play(*list(map(FadeIn, [randy, morty])))
|
|
self.play(PiCreatureSays(
|
|
morty, "More geometry!",
|
|
target_mode = "hooray",
|
|
run_time = 2,
|
|
bubble_kwargs = {"height" : 2, "width" : 4}
|
|
))
|
|
self.play(Blink(randy))
|
|
self.play(
|
|
RemovePiCreatureBubble(
|
|
morty, target_mode = "plain",
|
|
),
|
|
PiCreatureSays(
|
|
randy, "???",
|
|
target_mode = "maybe",
|
|
bubble_kwargs = {"width" : 3, "height" : 2}
|
|
)
|
|
)
|
|
self.play(
|
|
ShowCreation(screen),
|
|
morty.look_at, screen,
|
|
randy.look_at, screen,
|
|
)
|
|
self.play(Blink(morty))
|
|
self.play(RemovePiCreatureBubble(randy, target_mode = "pondering"))
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [randy, morty, screen])))
|
|
|
|
def show_geometrically(self):
|
|
dots = [self.example_dot, self.conjugate_dot]
|
|
top_dot, low_dot = dots
|
|
for dot in dots:
|
|
dot.line = Line(
|
|
self.plane_center, dot.get_center(),
|
|
color = BLUE
|
|
)
|
|
dot.angle = dot.line.get_angle()
|
|
dot.arc = Arc(
|
|
dot.angle,
|
|
radius = 0.75,
|
|
color = YELLOW
|
|
)
|
|
dot.arc.shift(self.plane_center)
|
|
dot.arc.add_tip(tip_length = 0.2)
|
|
dot.rotate_word = TextMobject("Rotate")
|
|
dot.rotate_word.scale(0.5)
|
|
dot.rotate_word.next_to(dot.arc, RIGHT, SMALL_BUFF)
|
|
dot.magnitude_word = TextMobject("Length 5")
|
|
dot.magnitude_word.scale(0.6)
|
|
dot.magnitude_word.next_to(
|
|
ORIGIN,
|
|
np.sign(dot.get_center()[1])*UP,
|
|
buff = SMALL_BUFF
|
|
)
|
|
dot.magnitude_word.add_background_rectangle()
|
|
dot.magnitude_word.rotate(dot.angle)
|
|
dot.magnitude_word.shift(dot.line.get_center())
|
|
twenty_five_label = TexMobject("25")
|
|
twenty_five_label.add_background_rectangle()
|
|
twenty_five_label.next_to(
|
|
self.plane.coords_to_point(25, 0),
|
|
DOWN
|
|
)
|
|
|
|
self.play(ShowCreation(top_dot.line))
|
|
mover = VGroup(
|
|
top_dot.line.copy().set_color(PINK),
|
|
top_dot.copy()
|
|
)
|
|
self.play(FadeIn(
|
|
top_dot.magnitude_word,
|
|
lag_ratio = 0.5
|
|
))
|
|
self.wait()
|
|
self.play(ShowCreation(top_dot.arc))
|
|
self.wait(2)
|
|
self.play(ShowCreation(low_dot.line))
|
|
self.play(
|
|
ReplacementTransform(
|
|
top_dot.arc,
|
|
low_dot.arc
|
|
),
|
|
FadeIn(low_dot.rotate_word)
|
|
)
|
|
self.play(
|
|
Rotate(
|
|
mover, low_dot.angle,
|
|
about_point = self.plane_center
|
|
),
|
|
run_time = 2
|
|
)
|
|
self.play(
|
|
FadeOut(low_dot.arc),
|
|
FadeOut(low_dot.rotate_word),
|
|
FadeIn(low_dot.magnitude_word),
|
|
)
|
|
self.play(
|
|
mover[0].scale_about_point, 5, self.plane_center,
|
|
mover[1].move_to, self.plane.coords_to_point(25, 0),
|
|
run_time = 2
|
|
)
|
|
self.wait()
|
|
self.play(Write(twenty_five_label))
|
|
self.wait(3)
|
|
|
|
class NameGaussianIntegers(LatticePointScene):
|
|
CONFIG = {
|
|
"max_lattice_point_radius" : 15,
|
|
"dot_radius" : 0.05,
|
|
"plane_center" : 2*LEFT,
|
|
"x_radius" : 15,
|
|
}
|
|
def construct(self):
|
|
self.add_axis_labels()
|
|
self.add_a_plus_bi()
|
|
self.draw_lattice_points()
|
|
self.add_name()
|
|
self.restrict_to_one_circle()
|
|
self.show_question_algebraically()
|
|
|
|
def add_a_plus_bi(self):
|
|
label = TexMobject(
|
|
"a", "+", "b", "i"
|
|
)
|
|
a = label.get_part_by_tex("a")
|
|
b = label.get_part_by_tex("b")
|
|
a.set_color(GREEN)
|
|
b.set_color(RED)
|
|
label.add_background_rectangle()
|
|
label.to_corner(UP+RIGHT)
|
|
integers = TextMobject("Integers")
|
|
integers.next_to(label, DOWN, LARGE_BUFF)
|
|
integers.add_background_rectangle()
|
|
arrows = VGroup(*[
|
|
Arrow(integers.get_top(), mob, tip_length = 0.15)
|
|
for mob in (a, b)
|
|
])
|
|
self.add_foreground_mobjects(label, integers, arrows)
|
|
|
|
self.a_plus_bi = label
|
|
self.integers_label = VGroup(integers, arrows)
|
|
|
|
def add_name(self):
|
|
gauss_name = TextMobject(
|
|
"Carl Friedrich Gauss"
|
|
)
|
|
gauss_name.add_background_rectangle()
|
|
gauss_name.next_to(ORIGIN, UP, MED_LARGE_BUFF)
|
|
gauss_name.to_edge(LEFT)
|
|
|
|
gaussian_integers = TextMobject("``Gaussian integers'': ")
|
|
gaussian_integers.scale(0.9)
|
|
gaussian_integers.next_to(self.a_plus_bi, LEFT)
|
|
gaussian_integers.add_background_rectangle()
|
|
|
|
self.play(FadeIn(gaussian_integers))
|
|
self.add_foreground_mobject(gaussian_integers)
|
|
self.play(FadeIn(
|
|
gauss_name,
|
|
run_time = 2,
|
|
lag_ratio = 0.5
|
|
))
|
|
self.wait(3)
|
|
self.play(FadeOut(gauss_name))
|
|
|
|
self.gaussian_integers = gaussian_integers
|
|
|
|
def restrict_to_one_circle(self):
|
|
dots = self.get_lattice_points_on_r_squared_circle(25).copy()
|
|
for dot in dots:
|
|
dot.scale_in_place(2)
|
|
circle = self.get_circle(5)
|
|
radius, root_label = self.get_radial_line_with_label(5)
|
|
|
|
self.play(
|
|
FadeOut(self.lattice_points),
|
|
ShowCreation(circle),
|
|
Rotating(
|
|
radius,
|
|
run_time = 1, rate_func = smooth,
|
|
about_point = self.plane_center
|
|
),
|
|
*list(map(GrowFromCenter, dots))
|
|
)
|
|
self.play(Write(root_label))
|
|
self.wait()
|
|
|
|
self.circle_dots = dots
|
|
|
|
def show_question_algebraically(self):
|
|
for i, dot in enumerate(self.circle_dots):
|
|
x, y = self.dot_to_int_coords(dot)
|
|
x_str = str(x)
|
|
y_str = str(y) if y >= 0 else "(%d)"%y
|
|
label = TexMobject(x_str, "+", y_str, "i")
|
|
label.scale(0.8)
|
|
label.next_to(
|
|
dot,
|
|
dot.get_center()-self.plane_center + SMALL_BUFF*(UP+RIGHT),
|
|
buff = 0,
|
|
)
|
|
label.add_background_rectangle()
|
|
dot.label = label
|
|
|
|
equation = TexMobject(
|
|
"25 = "
|
|
"(", x_str, "+", y_str, "i", ")",
|
|
"(", x_str, "-", y_str, "i", ")",
|
|
)
|
|
equation.scale(0.9)
|
|
equation.add_background_rectangle()
|
|
equation.to_corner(UP + RIGHT)
|
|
dot.equation = equation
|
|
|
|
for mob in label, equation:
|
|
mob.set_color_by_tex(x_str, GREEN, substring = False)
|
|
mob.set_color_by_tex(y_str, RED, substring = False)
|
|
|
|
dot.line_pair = VGroup(*[
|
|
Line(
|
|
self.plane_center,
|
|
self.plane.coords_to_point(x, u*y),
|
|
color = PINK,
|
|
)
|
|
for u in (1, -1)
|
|
])
|
|
dot.conjugate_dot = self.circle_dots[-i]
|
|
|
|
self.play(*list(map(FadeOut, [
|
|
self.a_plus_bi, self.integers_label,
|
|
self.gaussian_integers,
|
|
])))
|
|
|
|
last_dot = None
|
|
for dot in self.circle_dots:
|
|
anims = [
|
|
dot.set_color, PINK,
|
|
dot.conjugate_dot.set_color, PINK,
|
|
]
|
|
if last_dot is None:
|
|
anims += [
|
|
FadeIn(dot.equation),
|
|
FadeIn(dot.label),
|
|
]
|
|
anims += list(map(ShowCreation, dot.line_pair))
|
|
else:
|
|
anims += [
|
|
last_dot.set_color, self.dot_color,
|
|
last_dot.conjugate_dot.set_color, self.dot_color,
|
|
ReplacementTransform(last_dot.equation, dot.equation),
|
|
ReplacementTransform(last_dot.label, dot.label),
|
|
ReplacementTransform(last_dot.line_pair, dot.line_pair),
|
|
]
|
|
self.play(*anims)
|
|
self.wait()
|
|
last_dot = dot
|
|
|
|
class FactorOrdinaryNumber(TeacherStudentsScene):
|
|
def construct(self):
|
|
equation = TexMobject(
|
|
"2{,}250", "=", "2 \\cdot 3^2 \\cdot 5^3"
|
|
)
|
|
equation.next_to(self.get_pi_creatures(), UP, LARGE_BUFF)
|
|
number = equation[0]
|
|
alt_rhs_list = list(it.starmap(TexMobject, [
|
|
("\\ne", "2^2 \\cdot 563"),
|
|
("\\ne", "2^2 \\cdot 3 \\cdot 11 \\cdot 17"),
|
|
("\\ne", "2 \\cdot 7^2 \\cdot 23"),
|
|
("=", "(-2) \\cdot (-3) \\cdot (3) \\cdot 5^3"),
|
|
("=", "2 \\cdot (-3) \\cdot (3) \\cdot (-5) \\cdot 5^2"),
|
|
]))
|
|
for alt_rhs in alt_rhs_list:
|
|
if "\\ne" in alt_rhs.get_tex_string():
|
|
alt_rhs.set_color(RED)
|
|
else:
|
|
alt_rhs.set_color(GREEN)
|
|
alt_rhs.move_to(equation.get_right())
|
|
number.save_state()
|
|
number.next_to(self.teacher, UP+LEFT)
|
|
title = TextMobject("Almost", "Unique factorization")
|
|
title.set_color_by_tex("Almost", YELLOW)
|
|
title.to_edge(UP)
|
|
|
|
self.play(
|
|
self.teacher.change_mode, "raise_right_hand",
|
|
Write(number)
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
number.restore,
|
|
Write(VGroup(*equation[1:])),
|
|
Write(title[1])
|
|
)
|
|
self.change_student_modes(
|
|
*["pondering"]*3,
|
|
look_at_arg = equation,
|
|
added_anims = [self.teacher.change_mode, "happy"]
|
|
)
|
|
self.wait()
|
|
last_alt_rhs = None
|
|
for alt_rhs in alt_rhs_list:
|
|
equation.generate_target()
|
|
equation.target.next_to(alt_rhs, LEFT)
|
|
anims = [MoveToTarget(equation)]
|
|
if last_alt_rhs:
|
|
anims += [ReplacementTransform(last_alt_rhs, alt_rhs)]
|
|
else:
|
|
anims += [FadeIn(alt_rhs)]
|
|
self.play(*anims)
|
|
if alt_rhs is alt_rhs_list[-2]:
|
|
self.change_student_modes(
|
|
*["sassy"]*3,
|
|
look_at_arg = alt_rhs,
|
|
added_anims = [Write(title[0])]
|
|
)
|
|
self.wait(2)
|
|
last_alt_rhs = alt_rhs
|
|
|
|
self.play(
|
|
FadeOut(VGroup(equation, alt_rhs)),
|
|
PiCreatureSays(
|
|
self.teacher,
|
|
"It's similar for \\\\ Gaussian integers",
|
|
bubble_kwargs = {"height" : 3.5}
|
|
)
|
|
)
|
|
self.change_student_modes(*["happy"]*3)
|
|
self.wait(3)
|
|
|
|
class IntroduceGaussianPrimes(LatticePointScene, PiCreatureScene):
|
|
CONFIG = {
|
|
"plane_center" : LEFT,
|
|
"x_radius" : 13,
|
|
}
|
|
def create_pi_creature(self):
|
|
morty = Mortimer().flip()
|
|
morty.scale(0.7)
|
|
morty.next_to(ORIGIN, UP, buff = 0)
|
|
morty.to_edge(LEFT)
|
|
return morty
|
|
|
|
def setup(self):
|
|
LatticePointScene.setup(self)
|
|
PiCreatureScene.setup(self)
|
|
self.remove(self.pi_creature)
|
|
|
|
def construct(self):
|
|
self.plane.set_stroke(width = 2)
|
|
morty = self.pi_creature
|
|
dots = [
|
|
Dot(self.plane.coords_to_point(*coords))
|
|
for coords in [
|
|
(5, 0),
|
|
(2, 1), (2, -1),
|
|
(-1, 2), (-1, -2),
|
|
(-2, -1), (-2, 1),
|
|
]
|
|
]
|
|
five_dot = dots[0]
|
|
five_dot.set_color(YELLOW)
|
|
p_dots = VGroup(*dots[1:])
|
|
p1_dot, p2_dot, p3_dot, p4_dot, p5_dot, p6_dot = p_dots
|
|
VGroup(p1_dot, p3_dot, p5_dot).set_color(PINK)
|
|
VGroup(p2_dot, p4_dot, p6_dot).set_color(RED)
|
|
|
|
labels = [
|
|
TexMobject(tex).add_background_rectangle()
|
|
for tex in ("5", "2+i", "2-i", "-1+2i", "-1-2i", "-2-i", "-2+i")
|
|
]
|
|
five_label, p1_label, p2_label, p3_label, p4_label, p5_label, p6_label = labels
|
|
vects = [
|
|
DOWN,
|
|
UP+RIGHT, DOWN+RIGHT,
|
|
UP+LEFT, DOWN+LEFT,
|
|
DOWN+LEFT, UP+LEFT,
|
|
]
|
|
for dot, label, vect in zip(dots, labels, vects):
|
|
label.next_to(dot, vect, SMALL_BUFF)
|
|
|
|
arc_angle = 0.8*np.pi
|
|
times_i_arc = Arrow(
|
|
p1_dot.get_top(), p3_dot.get_top(),
|
|
path_arc = arc_angle
|
|
)
|
|
times_neg_i_arc = Arrow(
|
|
p2_dot.get_bottom(), p4_dot.get_bottom(),
|
|
path_arc = -arc_angle
|
|
)
|
|
times_i = TexMobject("\\times i")
|
|
times_i.add_background_rectangle()
|
|
times_i.next_to(
|
|
times_i_arc.point_from_proportion(0.5),
|
|
UP
|
|
)
|
|
times_neg_i = TexMobject("\\times (-i)")
|
|
times_neg_i.add_background_rectangle()
|
|
times_neg_i.next_to(
|
|
times_neg_i_arc.point_from_proportion(0.5),
|
|
DOWN
|
|
)
|
|
VGroup(
|
|
times_i, times_neg_i, times_i_arc, times_neg_i_arc
|
|
).set_color(MAROON_B)
|
|
|
|
gaussian_prime = TextMobject("$\\Rightarrow$ ``Gaussian prime''")
|
|
gaussian_prime.add_background_rectangle()
|
|
gaussian_prime.scale(0.9)
|
|
gaussian_prime.next_to(p1_label, RIGHT)
|
|
|
|
factorization = TexMobject(
|
|
"5", "= (2+i)(2-i)"
|
|
)
|
|
factorization.to_corner(UP+RIGHT)
|
|
factorization.shift(1.5*LEFT)
|
|
factorization.add_background_rectangle()
|
|
neg_alt_factorization = TexMobject("=(-2-i)(-2+i)")
|
|
i_alt_factorization = TexMobject("=(-1+2i)(-1-2i)")
|
|
for alt_factorization in neg_alt_factorization, i_alt_factorization:
|
|
alt_factorization.next_to(
|
|
factorization.get_part_by_tex("="), DOWN,
|
|
aligned_edge = LEFT
|
|
)
|
|
alt_factorization.add_background_rectangle()
|
|
|
|
for dot in dots:
|
|
dot.add(Line(
|
|
self.plane_center,
|
|
dot.get_center(),
|
|
color = dot.get_color()
|
|
))
|
|
|
|
self.add(factorization)
|
|
self.play(
|
|
DrawBorderThenFill(five_dot),
|
|
FadeIn(five_label)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
ReplacementTransform(
|
|
VGroup(five_dot).copy(),
|
|
VGroup(p1_dot, p2_dot)
|
|
)
|
|
)
|
|
self.play(*list(map(Write, [p1_label, p2_label])))
|
|
self.wait()
|
|
self.play(Write(gaussian_prime))
|
|
self.wait()
|
|
|
|
#Show morty
|
|
self.play(FadeIn(morty))
|
|
self.play(PiCreatureSays(
|
|
morty, "\\emph{Almost} unique",
|
|
bubble_kwargs = {"height" : 2, "width" : 5},
|
|
))
|
|
self.wait()
|
|
self.play(RemovePiCreatureBubble(morty, target_mode = "pondering"))
|
|
|
|
#Show neg_alternate expression
|
|
movers = [p1_dot, p2_dot, p1_label, p2_label]
|
|
for mover in movers:
|
|
mover.save_state()
|
|
self.play(
|
|
Transform(p1_dot, p5_dot),
|
|
Transform(p1_label, p5_label),
|
|
)
|
|
self.play(
|
|
Transform(p2_dot, p6_dot),
|
|
Transform(p2_label, p6_label),
|
|
)
|
|
self.play(Write(neg_alt_factorization))
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(neg_alt_factorization),
|
|
*[m.restore for m in movers]
|
|
)
|
|
self.wait()
|
|
|
|
##Show i_alternate expression
|
|
self.play(
|
|
ShowCreation(times_i_arc),
|
|
FadeIn(times_i),
|
|
*[
|
|
ReplacementTransform(
|
|
mob1.copy(), mob2,
|
|
path_arc = np.pi/2
|
|
)
|
|
for mob1, mob2 in [
|
|
(p1_dot, p3_dot),
|
|
(p1_label, p3_label),
|
|
]
|
|
]
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
ShowCreation(times_neg_i_arc),
|
|
FadeIn(times_neg_i),
|
|
*[
|
|
ReplacementTransform(
|
|
mob1.copy(), mob2,
|
|
path_arc = -np.pi/2
|
|
)
|
|
for mob1, mob2 in [
|
|
(p2_dot, p4_dot),
|
|
(p2_label, p4_label),
|
|
]
|
|
]
|
|
)
|
|
self.wait()
|
|
self.play(Write(i_alt_factorization))
|
|
self.change_mode("hesitant")
|
|
self.wait(3)
|
|
|
|
class FromIntegerFactorsToGaussianFactors(TeacherStudentsScene):
|
|
def construct(self):
|
|
expression = TexMobject(
|
|
"30", "=", "2", "\\cdot", "3", "\\cdot", "5"
|
|
)
|
|
expression.shift(2*UP)
|
|
two = expression.get_part_by_tex("2")
|
|
five = expression.get_part_by_tex("5")
|
|
two.set_color(BLUE)
|
|
five.set_color(GREEN)
|
|
two.factors = TexMobject("(1+i)", "(1-i)")
|
|
five.factors = TexMobject("(2+i)", "(2-i)")
|
|
for mob, vect in (two, DOWN), (five, UP):
|
|
mob.factors.next_to(mob, vect, LARGE_BUFF)
|
|
mob.factors.set_color(mob.get_color())
|
|
mob.arrows = VGroup(*[
|
|
Arrow(
|
|
mob.get_edge_center(vect),
|
|
factor.get_edge_center(-vect),
|
|
color = mob.get_color(),
|
|
tip_length = 0.15
|
|
)
|
|
for factor in mob.factors
|
|
])
|
|
|
|
self.add(expression)
|
|
for mob in two, five:
|
|
self.play(
|
|
ReplacementTransform(
|
|
mob.copy(),
|
|
mob.factors
|
|
),
|
|
*list(map(ShowCreation, mob.arrows))
|
|
)
|
|
self.wait()
|
|
self.play(*[
|
|
ApplyMethod(pi.change, "pondering", expression)
|
|
for pi in self.get_pi_creatures()
|
|
])
|
|
self.wait(5)
|
|
group = VGroup(
|
|
expression,
|
|
two.arrows, two.factors,
|
|
five.arrows, five.factors,
|
|
)
|
|
self.teacher_says(
|
|
"Now for a \\\\ surprising fact...",
|
|
added_anims = [FadeOut(group)]
|
|
)
|
|
self.wait(2)
|
|
|
|
class FactorizationPattern(Scene):
|
|
def construct(self):
|
|
self.force_skipping()
|
|
|
|
self.add_number_line()
|
|
self.show_one_mod_four_primes()
|
|
self.show_three_mod_four_primes()
|
|
self.ask_why_this_is_true()
|
|
self.show_two()
|
|
|
|
def add_number_line(self):
|
|
line = NumberLine(
|
|
x_min = 0,
|
|
x_max = 36,
|
|
unit_size = 0.4,
|
|
numbers_to_show = list(range(0, 33, 4)),
|
|
numbers_with_elongated_ticks = list(range(0, 33, 4)),
|
|
)
|
|
line.shift(2*DOWN)
|
|
line.to_edge(LEFT)
|
|
line.add_numbers()
|
|
|
|
self.add(line)
|
|
self.number_line = line
|
|
|
|
def show_one_mod_four_primes(self):
|
|
primes = [5, 13, 17, 29]
|
|
dots = VGroup(*[
|
|
Dot(self.number_line.number_to_point(prime))
|
|
for prime in primes
|
|
])
|
|
dots.set_color(GREEN)
|
|
prime_mobs = VGroup(*list(map(TexMobject, list(map(str, primes)))))
|
|
arrows = VGroup()
|
|
for prime_mob, dot in zip(prime_mobs, dots):
|
|
prime_mob.next_to(dot, UP, LARGE_BUFF)
|
|
prime_mob.set_color(dot.get_color())
|
|
arrow = Arrow(prime_mob, dot, buff = SMALL_BUFF)
|
|
arrow.set_color(dot.get_color())
|
|
arrows.add(arrow)
|
|
|
|
factorizations = VGroup(*[
|
|
TexMobject("=(%d+%si)(%d-%si)"%(x, y_str, x, y_str))
|
|
for x, y in [(2, 1), (3, 2), (4, 1), (5, 2)]
|
|
for y_str in [str(y) if y is not 1 else ""]
|
|
])
|
|
factorizations.arrange(DOWN, aligned_edge = LEFT)
|
|
factorizations.to_corner(UP+LEFT)
|
|
factorizations.shift(RIGHT)
|
|
movers = VGroup()
|
|
for p_mob, factorization in zip(prime_mobs, factorizations):
|
|
mover = p_mob.copy()
|
|
mover.generate_target()
|
|
mover.target.next_to(factorization, LEFT)
|
|
movers.add(mover)
|
|
v_dots = TexMobject("\\vdots")
|
|
v_dots.next_to(factorizations[-1][0], DOWN)
|
|
factorization.add(v_dots)
|
|
|
|
self.play(*it.chain(
|
|
list(map(Write, prime_mobs)),
|
|
list(map(ShowCreation, arrows)),
|
|
list(map(DrawBorderThenFill, dots)),
|
|
))
|
|
self.wait()
|
|
self.play(*[
|
|
MoveToTarget(
|
|
mover,
|
|
run_time = 2,
|
|
path_arc = np.pi/2,
|
|
)
|
|
for mover in movers
|
|
])
|
|
self.play(FadeIn(
|
|
factorizations,
|
|
run_time = 2,
|
|
lag_ratio = 0.5
|
|
))
|
|
self.wait(4)
|
|
self.play(*list(map(FadeOut, [movers, factorizations])))
|
|
|
|
def show_three_mod_four_primes(self):
|
|
primes = [3, 7, 11, 19, 23, 31]
|
|
dots = VGroup(*[
|
|
Dot(self.number_line.number_to_point(prime))
|
|
for prime in primes
|
|
])
|
|
dots.set_color(RED)
|
|
prime_mobs = VGroup(*list(map(TexMobject, list(map(str, primes)))))
|
|
arrows = VGroup()
|
|
for prime_mob, dot in zip(prime_mobs, dots):
|
|
prime_mob.next_to(dot, UP, LARGE_BUFF)
|
|
prime_mob.set_color(dot.get_color())
|
|
arrow = Arrow(prime_mob, dot, buff = SMALL_BUFF)
|
|
arrow.set_color(dot.get_color())
|
|
arrows.add(arrow)
|
|
|
|
words = TextMobject("Already Gaussian primes")
|
|
words.to_corner(UP+LEFT)
|
|
word_arrows = VGroup(*[
|
|
Line(
|
|
words.get_bottom(), p_mob.get_top(),
|
|
color = p_mob.get_color(),
|
|
buff = MED_SMALL_BUFF
|
|
)
|
|
for p_mob in prime_mobs
|
|
])
|
|
|
|
self.play(*it.chain(
|
|
list(map(Write, prime_mobs)),
|
|
list(map(ShowCreation, arrows)),
|
|
list(map(DrawBorderThenFill, dots)),
|
|
))
|
|
self.wait()
|
|
self.play(
|
|
Write(words),
|
|
*list(map(ShowCreation, word_arrows))
|
|
)
|
|
self.wait(4)
|
|
self.play(*list(map(FadeOut, [words, word_arrows])))
|
|
|
|
def ask_why_this_is_true(self):
|
|
randy = Randolph(color = BLUE_C)
|
|
randy.scale(0.7)
|
|
randy.to_edge(LEFT)
|
|
randy.shift(0.8*UP)
|
|
|
|
links_text = TextMobject("(See links in description)")
|
|
links_text.scale(0.7)
|
|
links_text.to_corner(UP+RIGHT)
|
|
links_text.shift(DOWN)
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(PiCreatureBubbleIntroduction(
|
|
randy, "Wait...why?",
|
|
bubble_class = ThoughtBubble,
|
|
bubble_kwargs = {"height" : 2, "width" : 3},
|
|
target_mode = "confused",
|
|
look_at_arg = self.number_line,
|
|
))
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
self.play(FadeIn(links_text))
|
|
self.wait(2)
|
|
self.play(*list(map(FadeOut, [
|
|
randy, randy.bubble, randy.bubble.content,
|
|
links_text
|
|
])))
|
|
|
|
def show_two(self):
|
|
two_dot = Dot(self.number_line.number_to_point(2))
|
|
two = TexMobject("2")
|
|
two.next_to(two_dot, UP, LARGE_BUFF)
|
|
arrow = Arrow(two, two_dot, buff = SMALL_BUFF)
|
|
VGroup(two_dot, two, arrow).set_color(YELLOW)
|
|
|
|
mover = two.copy()
|
|
mover.generate_target()
|
|
mover.target.to_corner(UP+LEFT)
|
|
factorization = TexMobject("=", "(1+i)", "(1-i)")
|
|
factorization.next_to(mover.target, RIGHT)
|
|
factors = VGroup(*factorization[1:])
|
|
|
|
time_i_arrow = Arrow(
|
|
factors[1].get_bottom(),
|
|
factors[0].get_bottom(),
|
|
path_arc = -np.pi
|
|
)
|
|
times_i = TexMobject("\\times i")
|
|
# times_i.scale(1.5)
|
|
times_i.next_to(time_i_arrow, DOWN)
|
|
times_i.set_color(time_i_arrow.get_color())
|
|
words = TextMobject("You'll see why this matters...")
|
|
words.next_to(times_i, DOWN)
|
|
words.shift_onto_screen()
|
|
|
|
self.play(
|
|
Write(two),
|
|
ShowCreation(arrow),
|
|
DrawBorderThenFill(two_dot)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
MoveToTarget(mover),
|
|
Write(factorization)
|
|
)
|
|
|
|
self.revert_to_original_skipping_status()
|
|
self.wait(2)
|
|
self.play(ShowCreation(time_i_arrow))
|
|
self.play(Write(times_i))
|
|
self.wait(2)
|
|
self.play(FadeIn(words))
|
|
self.wait(2)
|
|
|
|
class RingsWithOneModFourPrimes(CertainRegularityInPrimes):
|
|
CONFIG = {
|
|
"plane_center" : ORIGIN,
|
|
"primes" : [5, 13, 17, 29, 37, 41, 53],
|
|
"include_pi_formula" : False,
|
|
}
|
|
|
|
class RingsWithThreeModFourPrimes(CertainRegularityInPrimes):
|
|
CONFIG = {
|
|
"plane_center" : ORIGIN,
|
|
"primes" : [3, 7, 11, 19, 23, 31, 43],
|
|
"include_pi_formula" : False,
|
|
}
|
|
|
|
class FactorTwo(LatticePointScene):
|
|
CONFIG = {
|
|
"y_radius" : 3,
|
|
}
|
|
def construct(self):
|
|
two_dot = Dot(self.plane.coords_to_point(2, 0))
|
|
two_dot.set_color(YELLOW)
|
|
factor_dots = VGroup(*[
|
|
Dot(self.plane.coords_to_point(1, u))
|
|
for u in (1, -1)
|
|
])
|
|
two_label = TexMobject("2").next_to(two_dot, DOWN)
|
|
two_label.set_color(YELLOW)
|
|
two_label.add_background_rectangle()
|
|
factor_labels = VGroup(*[
|
|
TexMobject(tex).add_background_rectangle().next_to(dot, vect)
|
|
for tex, dot, vect in zip(
|
|
["1+i", "1-i"], factor_dots, [UP, DOWN]
|
|
)
|
|
])
|
|
VGroup(factor_labels, factor_dots).set_color(MAROON_B)
|
|
|
|
for dot in it.chain(factor_dots, [two_dot]):
|
|
line = Line(self.plane_center, dot.get_center())
|
|
line.set_color(dot.get_color())
|
|
dot.add(line)
|
|
|
|
self.play(
|
|
ShowCreation(two_dot),
|
|
Write(two_label),
|
|
)
|
|
self.play(*[
|
|
ReplacementTransform(
|
|
VGroup(mob1.copy()), mob2
|
|
)
|
|
for mob1, mob2 in [
|
|
(two_label, factor_labels),
|
|
(two_dot, factor_dots),
|
|
]
|
|
])
|
|
self.wait(2)
|
|
dot_copy = factor_dots[1].copy()
|
|
dot_copy.set_color(RED)
|
|
for angle in np.pi/2, -np.pi/2:
|
|
self.play(Rotate(dot_copy, angle, run_time = 2))
|
|
self.wait(2)
|
|
|
|
class CountThroughRingsCopy(CountThroughRings):
|
|
pass
|
|
|
|
class NameGaussianIntegersCopy(NameGaussianIntegers):
|
|
pass
|
|
|
|
class IntroduceRecipe(Scene):
|
|
CONFIG = {
|
|
"N_string" : "25",
|
|
"integer_factors" : [5, 5],
|
|
"gaussian_factors" : [
|
|
complex(2, 1), complex(2, -1),
|
|
complex(2, 1), complex(2, -1),
|
|
],
|
|
"x_color" : GREEN,
|
|
"y_color" : RED,
|
|
"N_color" : WHITE,
|
|
"i_positive_color" : BLUE,
|
|
"i_negative_color" : YELLOW,
|
|
"i_zero_color" : MAROON_B,
|
|
"T_chart_width" : 8,
|
|
"T_chart_height" : 6,
|
|
}
|
|
def construct(self):
|
|
self.add_title()
|
|
self.show_ordinary_factorization()
|
|
self.subfactor_ordinary_factorization()
|
|
self.organize_factors_into_columns()
|
|
self.mention_conjugate_rule()
|
|
self.take_product_of_columns()
|
|
self.mark_left_product_as_result()
|
|
self.swap_factors()
|
|
|
|
def add_title(self):
|
|
title = TexMobject(
|
|
"\\text{Recipe for }",
|
|
"a", "+", "b", "i",
|
|
"\\text{ satisfying }",
|
|
"(", "a", "+", "b", "i", ")",
|
|
"(", "a", "-", "b", "i", ")",
|
|
"=", self.N_string
|
|
)
|
|
strings = ("a", "b", self.N_string)
|
|
colors = (self.x_color, self.y_color, self.N_color)
|
|
for tex, color in zip(strings, colors):
|
|
title.set_color_by_tex(tex, color, substring = False)
|
|
title.to_edge(UP, buff = MED_SMALL_BUFF)
|
|
h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
|
|
h_line.next_to(title, DOWN)
|
|
self.add(title, h_line)
|
|
N_mob = title.get_part_by_tex(self.N_string)
|
|
digest_locals(self, ["title", "h_line", "N_mob"])
|
|
|
|
def show_ordinary_factorization(self):
|
|
N_mob = self.N_mob.copy()
|
|
N_mob.generate_target()
|
|
N_mob.target.next_to(self.h_line, DOWN)
|
|
N_mob.target.to_edge(LEFT)
|
|
|
|
factors = self.integer_factors
|
|
symbols = ["="] + ["\\cdot"]*(len(factors)-1)
|
|
factorization = TexMobject(*it.chain(*list(zip(
|
|
symbols, list(map(str, factors))
|
|
))))
|
|
factorization.next_to(N_mob.target, RIGHT)
|
|
|
|
self.play(MoveToTarget(
|
|
N_mob,
|
|
run_time = 2,
|
|
path_arc = -np.pi/6
|
|
))
|
|
self.play(Write(factorization))
|
|
self.wait()
|
|
|
|
self.factored_N_mob = N_mob
|
|
self.integer_factorization = factorization
|
|
|
|
def subfactor_ordinary_factorization(self):
|
|
factors = self.gaussian_factors
|
|
factorization = TexMobject(
|
|
"=", *list(map(self.complex_number_to_tex, factors))
|
|
)
|
|
max_width = FRAME_WIDTH - 2
|
|
if factorization.get_width() > max_width:
|
|
factorization.set_width(max_width)
|
|
factorization.next_to(
|
|
self.integer_factorization, DOWN,
|
|
aligned_edge = LEFT
|
|
)
|
|
for factor, mob in zip(factors, factorization[1:]):
|
|
mob.underlying_number = factor
|
|
y = complex(factor).imag
|
|
if y == 0:
|
|
mob.set_color(self.i_zero_color)
|
|
elif y > 0:
|
|
mob.set_color(self.i_positive_color)
|
|
elif y < 0:
|
|
mob.set_color(self.i_negative_color)
|
|
movers = VGroup()
|
|
mover = self.integer_factorization[0].copy()
|
|
mover.target = factorization[0]
|
|
movers.add(mover)
|
|
index = 0
|
|
for prime_mob in self.integer_factorization[1::2]:
|
|
gauss_prime = factors[index]
|
|
gauss_prime_mob = factorization[index+1]
|
|
mover = prime_mob.copy()
|
|
mover.target = gauss_prime_mob
|
|
movers.add(mover)
|
|
if abs(complex(gauss_prime).imag) > 0:
|
|
index += 1
|
|
mover = prime_mob.copy()
|
|
mover.target = factorization[index+1]
|
|
movers.add(mover)
|
|
index += 1
|
|
|
|
self.play(OldLaggedStart(
|
|
MoveToTarget,
|
|
movers,
|
|
replace_mobject_with_target_in_scene = True
|
|
))
|
|
self.wait()
|
|
|
|
self.gaussian_factorization = factorization
|
|
|
|
def organize_factors_into_columns(self):
|
|
T_chart = self.get_T_chart()
|
|
factors = self.gaussian_factorization.copy()[1:]
|
|
left_factors, right_factors = self.get_left_and_right_factors()
|
|
for group in left_factors, right_factors:
|
|
group.generate_target()
|
|
group.target.arrange(DOWN)
|
|
left_factors.target.next_to(T_chart.left_h_line, DOWN)
|
|
right_factors.target.next_to(T_chart.right_h_line, DOWN)
|
|
|
|
self.play(ShowCreation(T_chart))
|
|
self.wait()
|
|
self.play(MoveToTarget(left_factors))
|
|
self.play(MoveToTarget(right_factors))
|
|
self.wait()
|
|
|
|
digest_locals(self, ["left_factors", "right_factors"])
|
|
|
|
def mention_conjugate_rule(self):
|
|
left_factors = self.left_factors
|
|
right_factors = self.right_factors
|
|
double_arrows = VGroup()
|
|
for lf, rf in zip(left_factors.target, right_factors.target):
|
|
arrow = DoubleArrow(
|
|
lf, rf,
|
|
buff = SMALL_BUFF,
|
|
tip_length = SMALL_BUFF,
|
|
color = GREEN
|
|
)
|
|
word = TextMobject("Conjugates")
|
|
word.scale(0.75)
|
|
word.add_background_rectangle()
|
|
word.next_to(arrow, DOWN, SMALL_BUFF)
|
|
arrow.add(word)
|
|
double_arrows.add(arrow)
|
|
main_arrow = double_arrows[0]
|
|
|
|
self.play(Write(main_arrow, run_time = 1))
|
|
self.wait()
|
|
for new_arrow in double_arrows[1:]:
|
|
self.play(Transform(main_arrow, new_arrow))
|
|
self.wait()
|
|
self.wait()
|
|
self.play(FadeOut(main_arrow))
|
|
|
|
def take_product_of_columns(self):
|
|
arrows = self.get_product_multiplication_lines()
|
|
products = self.get_product_mobjects()
|
|
factor_groups = [self.left_factors, self.right_factors]
|
|
|
|
for arrow, product, group in zip(arrows, products, factor_groups):
|
|
self.play(ShowCreation(arrow))
|
|
self.play(ReplacementTransform(
|
|
group.copy(), VGroup(product)
|
|
))
|
|
self.wait()
|
|
self.wait(3)
|
|
|
|
def mark_left_product_as_result(self):
|
|
rect = self.get_result_surrounding_rect()
|
|
words = TextMobject("Output", " of recipe")
|
|
words.next_to(rect, DOWN, buff = MED_LARGE_BUFF)
|
|
words.to_edge(LEFT)
|
|
arrow = Arrow(words.get_top(), rect.get_left())
|
|
|
|
self.play(ShowCreation(rect))
|
|
self.play(
|
|
Write(words, run_time = 2),
|
|
ShowCreation(arrow)
|
|
)
|
|
self.wait(3)
|
|
self.play(*list(map(FadeOut, [words, arrow])))
|
|
|
|
self.output_label_group = VGroup(words, arrow)
|
|
|
|
def swap_factors(self):
|
|
for i in range(len(self.left_factors)):
|
|
self.swap_factors_at_index(i)
|
|
self.wait()
|
|
|
|
#########
|
|
|
|
def get_left_and_right_factors(self):
|
|
factors = self.gaussian_factorization.copy()[1:]
|
|
return VGroup(*factors[::2]), VGroup(*factors[1::2])
|
|
|
|
def get_T_chart(self):
|
|
T_chart = VGroup()
|
|
h_lines = VGroup(*[
|
|
Line(ORIGIN, self.T_chart_width*RIGHT/2.0)
|
|
for x in range(2)
|
|
])
|
|
h_lines.arrange(RIGHT, buff = 0)
|
|
h_lines.shift(UP)
|
|
v_line = Line(self.T_chart_height*UP, ORIGIN)
|
|
v_line.move_to(h_lines.get_center(), UP)
|
|
|
|
T_chart.left_h_line, T_chart.right_h_line = h_lines
|
|
T_chart.v_line = v_line
|
|
T_chart.digest_mobject_attrs()
|
|
|
|
return T_chart
|
|
|
|
def complex_number_to_tex(self, z):
|
|
z = complex(z)
|
|
x, y = z.real, z.imag
|
|
if y == 0:
|
|
return "(%d)"%x
|
|
y_sign_tex = "+" if y >= 0 else "-"
|
|
if abs(y) == 1:
|
|
y_str = y_sign_tex + "i"
|
|
else:
|
|
y_str = y_sign_tex + "%di"%abs(y)
|
|
return "(%d%s)"%(x, y_str)
|
|
|
|
def get_product_multiplication_lines(self):
|
|
lines = VGroup()
|
|
for factors in self.left_factors, self.right_factors:
|
|
line = Line(ORIGIN, 3*RIGHT)
|
|
line.next_to(factors, DOWN, SMALL_BUFF)
|
|
times = TexMobject("\\times")
|
|
times.next_to(line.get_left(), UP+RIGHT, SMALL_BUFF)
|
|
line.add(times)
|
|
lines.add(line)
|
|
self.multiplication_lines = lines
|
|
return lines
|
|
|
|
def get_product_mobjects(self):
|
|
factor_groups = [self.left_factors, self.right_factors]
|
|
product_mobjects = VGroup()
|
|
for factors, line in zip(factor_groups, self.multiplication_lines):
|
|
product = reduce(op.mul, [
|
|
factor.underlying_number
|
|
for factor in factors
|
|
])
|
|
color = average_color(*[
|
|
factor.get_color()
|
|
for factor in factors
|
|
])
|
|
product_mob = TexMobject(
|
|
self.complex_number_to_tex(product)
|
|
)
|
|
product_mob.set_color(color)
|
|
product_mob.next_to(line, DOWN)
|
|
product_mobjects.add(product_mob)
|
|
self.product_mobjects = product_mobjects
|
|
return product_mobjects
|
|
|
|
def swap_factors_at_index(self, index):
|
|
factor_groups = self.left_factors, self.right_factors
|
|
factors_to_swap = [group[index] for group in factor_groups]
|
|
self.play(*[
|
|
ApplyMethod(
|
|
factors_to_swap[i].move_to, factors_to_swap[1-i],
|
|
path_arc = np.pi/2,
|
|
)
|
|
for i in range(2)
|
|
])
|
|
for i, group in enumerate(factor_groups):
|
|
group.submobjects[index] = factors_to_swap[1-i]
|
|
self.play(FadeOut(self.product_mobjects))
|
|
self.get_product_mobjects()
|
|
rect = self.result_surrounding_rect
|
|
new_rect = self.get_result_surrounding_rect()
|
|
self.play(*[
|
|
ReplacementTransform(group.copy(), VGroup(product))
|
|
for group, product in zip(
|
|
factor_groups, self.product_mobjects,
|
|
)
|
|
]+[
|
|
ReplacementTransform(rect, new_rect)
|
|
])
|
|
self.wait()
|
|
|
|
def get_result_surrounding_rect(self, product = None):
|
|
if product is None:
|
|
product = self.product_mobjects[0]
|
|
rect = SurroundingRectangle(product)
|
|
self.result_surrounding_rect = rect
|
|
return rect
|
|
|
|
def write_last_step(self):
|
|
output_words, arrow = self.output_label_group
|
|
final_step = TextMobject(
|
|
"Multiply by $1$, $i$, $-1$ or $-i$"
|
|
)
|
|
final_step.scale(0.9)
|
|
final_step.next_to(arrow.get_start(), DOWN, SMALL_BUFF)
|
|
final_step.shift_onto_screen()
|
|
|
|
anims = [Write(final_step)]
|
|
if arrow not in self.get_mobjects():
|
|
# arrow = Arrow(
|
|
# final_step.get_top(),
|
|
# self.result_surrounding_rect.get_left()
|
|
# )
|
|
anims += [ShowCreation(arrow)]
|
|
self.play(*anims)
|
|
self.wait(2)
|
|
|
|
class StateThreeChoices(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says(
|
|
"$5^2$ gives 3 choices."
|
|
)
|
|
self.wait(3)
|
|
|
|
class ThreeOutputsAsLatticePoints(LatticePointScene):
|
|
CONFIG = {
|
|
"coords_list" : [(3, 4), (5, 0), (3, -4)],
|
|
"dot_radius" : 0.1,
|
|
"colors" : [YELLOW, GREEN, PINK, MAROON_B],
|
|
}
|
|
def construct(self):
|
|
self.add_circle()
|
|
self.add_dots_and_labels()
|
|
|
|
def add_circle(self):
|
|
radius = np.sqrt(self.radius_squared)
|
|
circle = self.get_circle(radius)
|
|
radial_line, root_label = self.get_radial_line_with_label(radius)
|
|
self.add(radial_line, root_label, circle)
|
|
self.add_foreground_mobject(root_label)
|
|
|
|
def add_dots_and_labels(self):
|
|
dots = VGroup(*[
|
|
Dot(
|
|
self.plane.coords_to_point(*coords),
|
|
radius = self.dot_radius,
|
|
color = self.colors[0],
|
|
)
|
|
for coords in self.coords_list
|
|
])
|
|
labels = VGroup()
|
|
for x, y in self.coords_list:
|
|
if y == 0:
|
|
y_str = ""
|
|
vect = DOWN+RIGHT
|
|
elif y > 1:
|
|
y_str = "+%di"%y
|
|
vect = UP+RIGHT
|
|
else:
|
|
y_str = "%di"%y
|
|
vect = DOWN+RIGHT
|
|
label = TexMobject("%d%s"%(x, y_str))
|
|
label.add_background_rectangle()
|
|
point = self.plane.coords_to_point(x, y)
|
|
label.next_to(point, vect)
|
|
labels.add(label)
|
|
|
|
for dot, label in zip(dots, labels):
|
|
self.play(
|
|
FadeIn(label),
|
|
DrawBorderThenFill(
|
|
dot,
|
|
stroke_color = PINK,
|
|
stroke_width = 4
|
|
)
|
|
)
|
|
self.wait(2)
|
|
|
|
self.original_dots = dots
|
|
|
|
class LooksLikeYoureMissingSome(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"Looks like you're \\\\ missing a few",
|
|
target_mode = "sassy",
|
|
student_index = 0,
|
|
)
|
|
self.play(self.teacher.change, "guilty")
|
|
self.wait(3)
|
|
|
|
class ShowAlternateFactorizationOfTwentyFive(IntroduceRecipe):
|
|
CONFIG = {
|
|
"gaussian_factors" : [
|
|
complex(-1, 2), complex(-1, -2),
|
|
complex(2, 1), complex(2, -1),
|
|
],
|
|
}
|
|
def construct(self):
|
|
self.add_title()
|
|
self.show_ordinary_factorization()
|
|
self.subfactor_ordinary_factorization()
|
|
self.organize_factors_into_columns()
|
|
self.take_product_of_columns()
|
|
self.mark_left_product_as_result()
|
|
self.swap_factors()
|
|
|
|
class WriteAlternateLastStep(IntroduceRecipe):
|
|
def construct(self):
|
|
self.force_skipping()
|
|
self.add_title()
|
|
self.show_ordinary_factorization()
|
|
self.subfactor_ordinary_factorization()
|
|
self.organize_factors_into_columns()
|
|
self.take_product_of_columns()
|
|
self.mark_left_product_as_result()
|
|
self.revert_to_original_skipping_status()
|
|
|
|
self.cross_out_output_words()
|
|
self.write_last_step()
|
|
|
|
def cross_out_output_words(self):
|
|
output_words, arrow = self.output_label_group
|
|
cross = TexMobject("\\times")
|
|
cross.replace(output_words, stretch = True)
|
|
cross.set_color(RED)
|
|
|
|
self.add(output_words, arrow)
|
|
self.play(Write(cross))
|
|
output_words.add(cross)
|
|
self.play(output_words.to_edge, DOWN)
|
|
|
|
class ThreeOutputsAsLatticePointsContinued(ThreeOutputsAsLatticePoints):
|
|
def construct(self):
|
|
self.force_skipping()
|
|
ThreeOutputsAsLatticePoints.construct(self)
|
|
self.revert_to_original_skipping_status()
|
|
|
|
self.show_multiplication_by_units()
|
|
|
|
def show_multiplication_by_units(self):
|
|
original_dots = self.original_dots
|
|
lines = VGroup()
|
|
for dot in original_dots:
|
|
line = Line(self.plane_center, dot.get_center())
|
|
line.set_stroke(dot.get_color(), 6)
|
|
lines.add(line)
|
|
dot.add(line)
|
|
words_group = VGroup(*[
|
|
TextMobject("Multiply by $%s$"%s)
|
|
for s in ("1", "i", "-1", "-i")
|
|
])
|
|
for words, color in zip(words_group, self.colors):
|
|
words.add_background_rectangle()
|
|
words.set_color(color)
|
|
words_group.arrange(DOWN, aligned_edge = LEFT)
|
|
words_group.to_corner(UP+LEFT, buff = MED_SMALL_BUFF)
|
|
angles = [np.pi/2, np.pi, -np.pi/2]
|
|
|
|
self.play(
|
|
FadeIn(words_group[0]),
|
|
*list(map(ShowCreation, lines))
|
|
)
|
|
for words, angle, color in zip(words_group[1:], angles, self.colors[1:]):
|
|
self.play(FadeIn(words))
|
|
dots_copy = original_dots.copy()
|
|
self.play(
|
|
dots_copy.rotate, angle,
|
|
dots_copy.set_color, color,
|
|
path_arc = angle
|
|
)
|
|
self.wait()
|
|
self.wait(2)
|
|
|
|
class RecipeFor125(IntroduceRecipe):
|
|
CONFIG = {
|
|
"N_string" : "125",
|
|
"integer_factors" : [5, 5, 5],
|
|
"gaussian_factors" : [
|
|
complex(2, -1), complex(2, 1),
|
|
complex(2, -1), complex(2, 1),
|
|
complex(2, -1), complex(2, 1),
|
|
],
|
|
}
|
|
def construct(self):
|
|
self.force_skipping()
|
|
|
|
self.add_title()
|
|
self.show_ordinary_factorization()
|
|
self.subfactor_ordinary_factorization()
|
|
|
|
self.revert_to_original_skipping_status()
|
|
self.organize_factors_into_columns()
|
|
# self.take_product_of_columns()
|
|
# self.mark_left_product_as_result()
|
|
# self.swap_factors()
|
|
# self.write_last_step()
|
|
|
|
class StateFourChoices(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says(
|
|
"$5^3$ gives 4 choices."
|
|
)
|
|
self.wait(3)
|
|
|
|
class Show125Circle(ThreeOutputsAsLatticePointsContinued):
|
|
CONFIG = {
|
|
"radius_squared" : 125,
|
|
"coords_list" : [(2, 11), (10, 5), (10, -5), (2, -11)],
|
|
"y_radius" : 15,
|
|
}
|
|
def construct(self):
|
|
self.draw_circle()
|
|
self.add_dots_and_labels()
|
|
self.show_multiplication_by_units()
|
|
self.ask_about_two()
|
|
|
|
def draw_circle(self):
|
|
self.plane.scale(2)
|
|
radius = np.sqrt(self.radius_squared)
|
|
circle = self.get_circle(radius)
|
|
radial_line, root_label = self.get_radial_line_with_label(radius)
|
|
|
|
self.play(
|
|
Write(root_label),
|
|
ShowCreation(radial_line)
|
|
)
|
|
self.add_foreground_mobject(root_label)
|
|
self.play(
|
|
Rotating(
|
|
radial_line,
|
|
rate_func = smooth,
|
|
about_point = self.plane_center
|
|
),
|
|
ShowCreation(circle),
|
|
run_time = 2,
|
|
)
|
|
group = VGroup(
|
|
self.plane, radial_line, circle, root_label
|
|
)
|
|
self.play(group.scale, 0.5)
|
|
|
|
class RecipeFor375(IntroduceRecipe):
|
|
CONFIG = {
|
|
"N_string" : "375",
|
|
"integer_factors" : [3, 5, 5, 5],
|
|
"gaussian_factors" : [
|
|
3,
|
|
complex(2, 1), complex(2, -1),
|
|
complex(2, 1), complex(2, -1),
|
|
complex(2, 1), complex(2, -1),
|
|
],
|
|
}
|
|
def construct(self):
|
|
self.add_title()
|
|
self.show_ordinary_factorization()
|
|
self.subfactor_ordinary_factorization()
|
|
self.organize_factors_into_columns()
|
|
self.express_trouble_with_three()
|
|
self.take_product_of_columns()
|
|
|
|
def express_trouble_with_three(self):
|
|
morty = Mortimer().flip().to_corner(DOWN+LEFT)
|
|
three = self.gaussian_factorization[1].copy()
|
|
three.generate_target()
|
|
three.target.next_to(morty, UP, MED_LARGE_BUFF)
|
|
|
|
self.play(FadeIn(morty))
|
|
self.play(
|
|
MoveToTarget(three),
|
|
morty.change, "angry", three.target
|
|
)
|
|
self.play(Blink(morty))
|
|
self.wait()
|
|
for factors in self.left_factors, self.right_factors:
|
|
self.play(
|
|
three.next_to, factors, DOWN,
|
|
morty.change, "sassy", factors.get_bottom()
|
|
)
|
|
self.wait()
|
|
self.right_factors.add(three)
|
|
self.play(morty.change_mode, "pondering")
|
|
|
|
####
|
|
|
|
def get_left_and_right_factors(self):
|
|
factors = self.gaussian_factorization.copy()[1:]
|
|
return VGroup(*factors[1::2]), VGroup(*factors[2::2])
|
|
|
|
class Show375Circle(LatticePointScene):
|
|
CONFIG = {
|
|
"y_radius" : 20,
|
|
}
|
|
def construct(self):
|
|
radius = np.sqrt(375)
|
|
circle = self.get_circle(radius)
|
|
radial_line, root_label = self.get_radial_line_with_label(radius)
|
|
|
|
self.play(
|
|
ShowCreation(radial_line),
|
|
Write(root_label, run_time = 1)
|
|
)
|
|
self.add_foreground_mobject(root_label)
|
|
self.play(
|
|
Rotating(
|
|
radial_line,
|
|
rate_func = smooth,
|
|
about_point = self.plane_center
|
|
),
|
|
ShowCreation(circle),
|
|
run_time = 2,
|
|
)
|
|
group = VGroup(
|
|
self.plane, radial_line, root_label, circle
|
|
)
|
|
self.wait(2)
|
|
|
|
class RecipeFor1125(IntroduceRecipe):
|
|
CONFIG = {
|
|
"N_string" : "1125",
|
|
"integer_factors" : [3, 3, 5, 5, 5],
|
|
"gaussian_factors" : [
|
|
3, 3,
|
|
complex(2, 1), complex(2, -1),
|
|
complex(2, 1), complex(2, -1),
|
|
complex(2, 1), complex(2, -1),
|
|
],
|
|
}
|
|
def construct(self):
|
|
self.add_title()
|
|
self.show_ordinary_factorization()
|
|
self.subfactor_ordinary_factorization()
|
|
self.organize_factors_into_columns()
|
|
self.mention_conjugate_rule()
|
|
self.take_product_of_columns()
|
|
self.mark_left_product_as_result()
|
|
self.swap_factors()
|
|
self.write_last_step()
|
|
|
|
def write_last_step(self):
|
|
words = TextMobject(
|
|
"Multiply by \\\\ ",
|
|
"$1$, $i$, $-1$ or $-i$"
|
|
)
|
|
words.scale(0.7)
|
|
words.to_corner(DOWN+LEFT)
|
|
product = self.product_mobjects[0]
|
|
|
|
self.play(Write(words))
|
|
self.wait()
|
|
|
|
class Show125CircleSimple(LatticePointScene):
|
|
CONFIG = {
|
|
"radius_squared" : 125,
|
|
"y_radius" : 12,
|
|
"max_lattice_point_radius" : 12,
|
|
}
|
|
def construct(self):
|
|
self.plane.set_stroke(width = 1)
|
|
radius = np.sqrt(self.radius_squared)
|
|
circle = self.get_circle(radius)
|
|
radial_line, root_label = self.get_radial_line_with_label(radius)
|
|
dots = self.get_lattice_points_on_r_squared_circle(self.radius_squared)
|
|
|
|
self.play(
|
|
ShowCreation(radial_line),
|
|
Write(root_label, run_time = 1)
|
|
)
|
|
self.add_foreground_mobject(root_label)
|
|
self.play(
|
|
Rotating(
|
|
radial_line,
|
|
rate_func = smooth,
|
|
about_point = self.plane_center
|
|
),
|
|
ShowCreation(circle),
|
|
OldLaggedStart(
|
|
DrawBorderThenFill,
|
|
dots,
|
|
stroke_width = 4,
|
|
stroke_color = PINK,
|
|
),
|
|
run_time = 2,
|
|
)
|
|
self.wait(2)
|
|
|
|
class Show1125Circle(Show125CircleSimple):
|
|
CONFIG = {
|
|
"radius_squraed" : 1125,
|
|
"y_radius" : 35,
|
|
"max_lattice_point_radius" : 35,
|
|
}
|
|
|
|
class SummarizeCountingRule(Show125Circle):
|
|
CONFIG = {
|
|
"dot_radius" : 0.075,
|
|
"N_str" : "N",
|
|
"rect_opacity" : 1,
|
|
}
|
|
def construct(self):
|
|
self.add_count_words()
|
|
self.draw_circle()
|
|
self.add_full_screen_rect()
|
|
self.talk_through_rules()
|
|
self.ask_about_two()
|
|
|
|
def add_count_words(self):
|
|
words = TextMobject(
|
|
"\\# Lattice points \\\\ on $\\sqrt{%s}$ circle"%self.N_str
|
|
)
|
|
words.to_corner(UP+LEFT)
|
|
words.add_background_rectangle()
|
|
self.add(words)
|
|
self.count_words = words
|
|
|
|
def draw_circle(self):
|
|
radius = np.sqrt(self.radius_squared)
|
|
circle = self.get_circle(radius)
|
|
radial_line, num_root_label = self.get_radial_line_with_label(radius)
|
|
root_label = TexMobject("\\sqrt{%s}"%self.N_str)
|
|
root_label.next_to(radial_line, UP, SMALL_BUFF)
|
|
dots = VGroup(*[
|
|
Dot(
|
|
self.plane.coords_to_point(*coords),
|
|
radius = self.dot_radius,
|
|
color = self.dot_color
|
|
)
|
|
for coords in self.coords_list
|
|
])
|
|
for angle in np.pi/2, np.pi, -np.pi/2:
|
|
dots.add(*dots.copy().rotate(angle))
|
|
|
|
self.play(
|
|
Write(root_label),
|
|
ShowCreation(radial_line)
|
|
)
|
|
self.play(
|
|
Rotating(
|
|
radial_line,
|
|
rate_func = smooth,
|
|
about_point = self.plane_center
|
|
),
|
|
ShowCreation(circle),
|
|
run_time = 2,
|
|
)
|
|
self.play(OldLaggedStart(
|
|
DrawBorderThenFill,
|
|
dots,
|
|
stroke_width = 4,
|
|
stroke_color = PINK
|
|
))
|
|
self.wait(2)
|
|
|
|
def add_full_screen_rect(self):
|
|
rect = FullScreenFadeRectangle(
|
|
fill_opacity = self.rect_opacity
|
|
)
|
|
self.play(
|
|
FadeIn(rect),
|
|
Animation(self.count_words)
|
|
)
|
|
|
|
def talk_through_rules(self):
|
|
factorization = TexMobject(
|
|
"N =",
|
|
"3", "^4", "\\cdot",
|
|
"5", "^3", "\\cdot",
|
|
"13", "^2"
|
|
)
|
|
factorization.next_to(ORIGIN, RIGHT)
|
|
factorization.to_edge(UP)
|
|
|
|
three, five, thirteen = [
|
|
factorization.get_part_by_tex(str(n), substring = False)
|
|
for n in (3, 5, 13)
|
|
]
|
|
three_power = factorization.get_part_by_tex("^4")
|
|
five_power = factorization.get_part_by_tex("^3")
|
|
thirteen_power = factorization.get_part_by_tex("^2")
|
|
alt_three_power = five_power.copy().move_to(three_power)
|
|
|
|
three_brace = Brace(VGroup(*factorization[1:3]), DOWN)
|
|
five_brace = Brace(VGroup(*factorization[3:6]), DOWN)
|
|
thirteen_brace = Brace(VGroup(*factorization[6:9]), DOWN)
|
|
|
|
three_choices = three_brace.get_tex("(", "1", ")")
|
|
five_choices = five_brace.get_tex(
|
|
"(", "3", "+", "1", ")"
|
|
)
|
|
thirteen_choices = thirteen_brace.get_tex(
|
|
"(", "2", "+", "1", ")"
|
|
)
|
|
all_choices = VGroup(three_choices, five_choices, thirteen_choices)
|
|
for choices in all_choices:
|
|
choices.scale(0.75, about_point = choices.get_top())
|
|
thirteen_choices.next_to(five_choices, RIGHT)
|
|
three_choices.next_to(five_choices, LEFT)
|
|
alt_three_choices = TexMobject("(", "0", ")")
|
|
alt_three_choices.scale(0.75)
|
|
alt_three_choices.move_to(three_choices, RIGHT)
|
|
|
|
|
|
self.play(FadeIn(factorization))
|
|
self.wait()
|
|
self.play(
|
|
five.set_color, GREEN,
|
|
thirteen.set_color, GREEN,
|
|
FadeIn(five_brace),
|
|
FadeIn(thirteen_brace),
|
|
)
|
|
self.wait()
|
|
for choices, power in (five_choices, five_power), (thirteen_choices, thirteen_power):
|
|
self.play(
|
|
Write(VGroup(choices[0], *choices[2:])),
|
|
ReplacementTransform(
|
|
power.copy(), choices[1]
|
|
)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
three.set_color, RED,
|
|
FadeIn(three_brace)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Write(VGroup(three_choices[0], three_choices[2])),
|
|
ReplacementTransform(
|
|
three_power.copy(), three_choices[1]
|
|
)
|
|
)
|
|
self.wait()
|
|
|
|
movers = three_power, three_choices
|
|
for mob in movers:
|
|
mob.save_state()
|
|
self.play(
|
|
Transform(
|
|
three_power, alt_three_power,
|
|
path_arc = np.pi
|
|
),
|
|
Transform(three_choices, alt_three_choices)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
*[mob.restore for mob in movers],
|
|
path_arc = -np.pi
|
|
)
|
|
self.wait()
|
|
|
|
equals_four = TexMobject("=", "4")
|
|
four = equals_four.get_part_by_tex("4")
|
|
four.set_color(YELLOW)
|
|
final_choice_words = TextMobject(
|
|
"Mutiply", "by $1$, $i$, $-1$ or $-i$"
|
|
)
|
|
final_choice_words.set_color(YELLOW)
|
|
final_choice_words.next_to(four, DOWN, LARGE_BUFF, LEFT)
|
|
final_choice_words.to_edge(RIGHT)
|
|
final_choice_arrow = Arrow(
|
|
final_choice_words[0].get_top(),
|
|
four.get_bottom(),
|
|
buff = SMALL_BUFF
|
|
)
|
|
|
|
choices_copy = all_choices.copy()
|
|
choices_copy.generate_target()
|
|
|
|
choices_copy.target.scale(1./0.75)
|
|
choices_copy.target.arrange(RIGHT, buff = SMALL_BUFF)
|
|
choices_copy.target.next_to(equals_four, RIGHT, SMALL_BUFF)
|
|
choices_copy.target.shift(0.25*SMALL_BUFF*DOWN)
|
|
self.play(
|
|
self.count_words.next_to, equals_four, LEFT,
|
|
MoveToTarget(choices_copy),
|
|
FadeIn(equals_four)
|
|
)
|
|
self.play(*list(map(FadeIn, [final_choice_words, final_choice_arrow])))
|
|
self.wait()
|
|
|
|
def ask_about_two(self):
|
|
randy = Randolph(color = BLUE_C)
|
|
randy.scale(0.7)
|
|
randy.to_edge(LEFT)
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(PiCreatureBubbleIntroduction(
|
|
randy, "What about \\\\ factors of 2?",
|
|
bubble_class = ThoughtBubble,
|
|
bubble_kwargs = {"height" : 3, "width" : 3},
|
|
target_mode = "confused",
|
|
look_at_arg = self.count_words
|
|
))
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
|
|
class ThisIsTheHardestPart(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.change_student_modes("horrified", "confused", "pleading")
|
|
self.teacher_says("This is the \\\\ hardest part")
|
|
self.change_student_modes("thinking", "happy", "pondering")
|
|
self.wait(2)
|
|
|
|
class RecipeFor10(IntroduceRecipe):
|
|
CONFIG = {
|
|
"N_string" : "10",
|
|
"integer_factors" : [2, 5],
|
|
"gaussian_factors" : [
|
|
complex(1, 1), complex(1, -1),
|
|
complex(2, 1), complex(2, -1),
|
|
],
|
|
}
|
|
def construct(self):
|
|
self.add_title()
|
|
self.show_ordinary_factorization()
|
|
self.subfactor_ordinary_factorization()
|
|
self.organize_factors_into_columns()
|
|
self.take_product_of_columns()
|
|
self.mark_left_product_as_result()
|
|
self.swap_two_factors()
|
|
self.write_last_step()
|
|
|
|
def swap_two_factors(self):
|
|
left = self.left_factors[0]
|
|
right = self.right_factors[0]
|
|
arrow = Arrow(right, left, buff = SMALL_BUFF)
|
|
times_i = TexMobject("\\times i")
|
|
times_i.next_to(arrow, DOWN, 0)
|
|
times_i.add_background_rectangle()
|
|
curr_product = self.product_mobjects[0].copy()
|
|
|
|
for x in range(2):
|
|
self.swap_factors_at_index(0)
|
|
self.play(
|
|
ShowCreation(arrow),
|
|
Write(times_i, run_time = 1)
|
|
)
|
|
self.wait()
|
|
self.play(curr_product.to_edge, LEFT)
|
|
self.swap_factors_at_index(0)
|
|
new_arrow = Arrow(
|
|
self.result_surrounding_rect, curr_product,
|
|
buff = SMALL_BUFF
|
|
)
|
|
self.play(
|
|
Transform(arrow, new_arrow),
|
|
MaintainPositionRelativeTo(times_i, arrow)
|
|
)
|
|
self.wait(2)
|
|
self.play(*list(map(FadeOut, [arrow, times_i, curr_product])))
|
|
|
|
class FactorsOfTwoNeitherHelpNorHurt(TeacherStudentsScene):
|
|
def construct(self):
|
|
words = TextMobject(
|
|
"Factors of", "$2^k$", "neither \\\\ help nor hurt"
|
|
)
|
|
words.set_color_by_tex("2", YELLOW)
|
|
self.teacher_says(words)
|
|
self.change_student_modes(*["pondering"]*3)
|
|
self.wait(3)
|
|
|
|
class EffectOfPowersOfTwo(LatticePointScene):
|
|
CONFIG = {
|
|
"y_radius" : 9,
|
|
"max_lattice_point_radius" : 9,
|
|
"square_radii" : [5, 10, 20, 40, 80],
|
|
}
|
|
def construct(self):
|
|
radii = list(map(np.sqrt, self.square_radii))
|
|
circles = list(map(self.get_circle, radii))
|
|
radial_lines, root_labels = list(zip(*list(map(
|
|
self.get_radial_line_with_label, radii
|
|
))))
|
|
dots_list = list(map(
|
|
self.get_lattice_points_on_r_squared_circle,
|
|
self.square_radii
|
|
))
|
|
groups = [
|
|
VGroup(*mobs)
|
|
for mobs in zip(radial_lines, circles, root_labels, dots_list)
|
|
]
|
|
group = groups[0]
|
|
|
|
self.add(group)
|
|
self.play(OldLaggedStart(
|
|
DrawBorderThenFill, dots_list[0],
|
|
stroke_width = 4,
|
|
stroke_color = PINK
|
|
))
|
|
self.wait()
|
|
for new_group in groups[1:]:
|
|
self.play(Transform(group, new_group))
|
|
self.wait(2)
|
|
|
|
class NumberTheoryAtItsBest(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says(
|
|
"Number theory at its best!",
|
|
target_mode = "hooray",
|
|
run_time = 2,
|
|
)
|
|
self.change_student_modes(*["hooray"]*3)
|
|
self.wait(3)
|
|
|
|
class IntroduceChi(FactorizationPattern):
|
|
CONFIG = {
|
|
"numbers_list" : [
|
|
list(range(i, 36, d))
|
|
for i, d in [(1, 4), (3, 4), (2, 2)]
|
|
],
|
|
"colors" : [GREEN, RED, YELLOW]
|
|
}
|
|
def construct(self):
|
|
self.add_number_line()
|
|
self.add_define_chi_label()
|
|
for index in range(3):
|
|
self.describe_values(index)
|
|
self.fade_out_labels()
|
|
self.cyclic_pattern()
|
|
self.write_multiplicative_label()
|
|
self.show_multiplicative()
|
|
|
|
|
|
def add_define_chi_label(self):
|
|
label = TextMobject("Define $\\chi(n)$:")
|
|
chi_expressions = VGroup(*[
|
|
self.get_chi_expression(numbers, color)
|
|
for numbers, color in zip(
|
|
self.numbers_list,
|
|
self.colors
|
|
)
|
|
])
|
|
chi_expressions.scale(0.9)
|
|
chi_expressions.arrange(
|
|
DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT
|
|
)
|
|
chi_expressions.to_corner(UP+RIGHT)
|
|
brace = Brace(chi_expressions, LEFT)
|
|
label.next_to(brace, LEFT)
|
|
|
|
self.play(
|
|
Write(label),
|
|
GrowFromCenter(brace)
|
|
)
|
|
self.define_chi_label = label
|
|
self.chi_expressions = chi_expressions
|
|
|
|
def describe_values(self, index):
|
|
numbers = self.numbers_list[index]
|
|
color = self.colors[index]
|
|
dots, arrows, labels = self.get_dots_arrows_and_labels(
|
|
numbers, color
|
|
)
|
|
chi_expression = self.chi_expressions[index]
|
|
|
|
self.introduce_dots_arrows_and_labels(dots, arrows, labels)
|
|
self.wait()
|
|
self.play(
|
|
Write(VGroup(*[
|
|
part
|
|
for part in chi_expression
|
|
if part not in chi_expression.inputs
|
|
])),
|
|
*[
|
|
ReplacementTransform(label.copy(), num_mob)
|
|
for label, num_mob in zip(
|
|
labels, chi_expression.inputs
|
|
)
|
|
])
|
|
self.wait()
|
|
|
|
def fade_out_labels(self):
|
|
self.play(*list(map(FadeOut, [
|
|
self.last_dots, self.last_arrows, self.last_labels,
|
|
self.number_line
|
|
])))
|
|
|
|
def cyclic_pattern(self):
|
|
input_range = list(range(1, 9))
|
|
chis = VGroup(*[
|
|
TexMobject("\\chi(%d)"%n)
|
|
for n in input_range
|
|
])
|
|
chis.arrange(RIGHT, buff = LARGE_BUFF)
|
|
chis.set_stroke(WHITE, width = 1)
|
|
numbers = VGroup()
|
|
arrows = VGroup()
|
|
for chi, n in zip(chis, input_range):
|
|
arrow = TexMobject("\\Uparrow")
|
|
arrow.next_to(chi, UP, SMALL_BUFF)
|
|
arrows.add(arrow)
|
|
value = TexMobject(str(chi_func(n)))
|
|
for tex, color in zip(["1", "-1", "0"], self.colors):
|
|
value.set_color_by_tex(tex, color)
|
|
value.next_to(arrow, UP)
|
|
numbers.add(value)
|
|
group = VGroup(chis, arrows, numbers)
|
|
group.set_width(FRAME_WIDTH - LARGE_BUFF)
|
|
group.to_edge(DOWN, buff = LARGE_BUFF)
|
|
|
|
self.play(*[
|
|
FadeIn(
|
|
mob,
|
|
run_time = 3,
|
|
lag_ratio = 0.5
|
|
)
|
|
for mob in [chis, arrows, numbers]
|
|
])
|
|
|
|
self.play(OldLaggedStart(
|
|
ApplyMethod,
|
|
numbers,
|
|
lambda m : (m.shift, MED_SMALL_BUFF*UP),
|
|
rate_func = there_and_back,
|
|
lag_ratio = 0.2,
|
|
run_time = 6
|
|
))
|
|
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [chis, arrows, numbers])))
|
|
|
|
def write_multiplicative_label(self):
|
|
morty = Mortimer()
|
|
morty.scale(0.7)
|
|
morty.to_corner(DOWN+RIGHT)
|
|
|
|
self.play(PiCreatureSays(
|
|
morty, "$\\chi$ is ``multiplicative''",
|
|
bubble_kwargs = {"height" : 2.5, "width" : 5}
|
|
))
|
|
self.play(Blink(morty))
|
|
self.morty = morty
|
|
|
|
def show_multiplicative(self):
|
|
pairs = [(3, 5), (5, 5), (2, 13), (3, 11)]
|
|
expressions = VGroup()
|
|
for x, y in pairs:
|
|
expression = TexMobject(
|
|
"\\chi(%d)"%x,
|
|
"\\cdot",
|
|
"\\chi(%d)"%y,
|
|
"=",
|
|
"\\chi(%d)"%(x*y)
|
|
)
|
|
braces = [
|
|
Brace(expression[i], UP)
|
|
for i in (0, 2, 4)
|
|
]
|
|
for brace, n in zip(braces, [x, y, x*y]):
|
|
output = chi_func(n)
|
|
label = brace.get_tex(str(output))
|
|
label.set_color(self.number_to_color(output))
|
|
brace.add(label)
|
|
expression.add(brace)
|
|
expressions.add(expression)
|
|
|
|
expressions.next_to(ORIGIN, LEFT)
|
|
expressions.shift(DOWN)
|
|
expression = expressions[0]
|
|
|
|
self.play(
|
|
FadeIn(expression),
|
|
self.morty.change, "pondering", expression
|
|
)
|
|
self.wait(2)
|
|
for new_expression in expressions[1:]:
|
|
self.play(Transform(expression, new_expression))
|
|
self.wait(2)
|
|
|
|
|
|
|
|
|
|
#########
|
|
|
|
def get_dots_arrows_and_labels(self, numbers, color):
|
|
dots = VGroup()
|
|
arrows = VGroup()
|
|
labels = VGroup()
|
|
for number in numbers:
|
|
point = self.number_line.number_to_point(number)
|
|
dot = Dot(point)
|
|
label = TexMobject(str(number))
|
|
label.scale(0.8)
|
|
label.next_to(dot, UP, LARGE_BUFF)
|
|
arrow = Arrow(label, dot, buff = SMALL_BUFF)
|
|
VGroup(dot, label, arrow).set_color(color)
|
|
dots.add(dot)
|
|
arrows.add(arrow)
|
|
labels.add(label)
|
|
return dots, arrows, labels
|
|
|
|
def introduce_dots_arrows_and_labels(self, dots, arrows, labels):
|
|
if hasattr(self, "last_dots"):
|
|
self.play(
|
|
ReplacementTransform(self.last_dots, dots),
|
|
ReplacementTransform(self.last_arrows, arrows),
|
|
ReplacementTransform(self.last_labels, labels),
|
|
)
|
|
else:
|
|
self.play(
|
|
Write(labels),
|
|
FadeIn(arrows, lag_ratio = 0.5),
|
|
OldLaggedStart(
|
|
DrawBorderThenFill, dots,
|
|
stroke_width = 4,
|
|
stroke_color = YELLOW
|
|
),
|
|
run_time = 2
|
|
)
|
|
self.last_dots = dots
|
|
self.last_arrows = arrows
|
|
self.last_labels = labels
|
|
|
|
def get_chi_expression(self, numbers, color, num_terms = 4):
|
|
truncated_numbers = numbers[:num_terms]
|
|
output = str(chi_func(numbers[0]))
|
|
result = TexMobject(*it.chain(*[
|
|
["\\chi(", str(n), ")", "="]
|
|
for n in truncated_numbers
|
|
] + [
|
|
["\\cdots =", output]
|
|
]))
|
|
result.inputs = VGroup()
|
|
for n in truncated_numbers:
|
|
num_mob = result.get_part_by_tex(str(n), substring = False)
|
|
num_mob.set_color(color)
|
|
result.inputs.add(num_mob)
|
|
result.set_color_by_tex(output, color, substring = False)
|
|
return result
|
|
|
|
def number_to_color(self, n):
|
|
output = chi_func(n)
|
|
if n == 1:
|
|
return self.colors[0]
|
|
elif n == -1:
|
|
return self.colors[1]
|
|
else:
|
|
return self.colors[2]
|
|
|
|
class WriteCountingRuleWithChi(SummarizeCountingRule):
|
|
CONFIG = {
|
|
"colors" : [GREEN, RED, YELLOW]
|
|
}
|
|
def construct(self):
|
|
self.add_count_words()
|
|
self.draw_circle()
|
|
self.add_full_screen_rect()
|
|
|
|
self.add_factorization_and_rule()
|
|
self.write_chi_expression()
|
|
self.walk_through_expression_terms()
|
|
self.circle_four()
|
|
|
|
def add_factorization_and_rule(self):
|
|
factorization = TexMobject(
|
|
"N", "=",
|
|
"2", "^2", "\\cdot",
|
|
"3", "^4", "\\cdot",
|
|
"5", "^3",
|
|
)
|
|
for tex, color in zip(["5", "3", "2"], self.colors):
|
|
factorization.set_color_by_tex(tex, color, substring = False)
|
|
factorization.to_edge(UP)
|
|
factorization.shift(LEFT)
|
|
|
|
count = VGroup(
|
|
TexMobject("=", "4"),
|
|
TexMobject("(", "1", ")"),
|
|
TexMobject("(", "1", ")"),
|
|
TexMobject("(", "3+1", ")"),
|
|
)
|
|
count.arrange(RIGHT, buff = SMALL_BUFF)
|
|
for i, color in zip([3, 2, 1], self.colors):
|
|
count[i][1].set_color(color)
|
|
count.next_to(
|
|
factorization.get_part_by_tex("="), DOWN,
|
|
buff = LARGE_BUFF,
|
|
aligned_edge = LEFT
|
|
)
|
|
|
|
self.play(
|
|
FadeIn(factorization),
|
|
self.count_words.next_to, count, LEFT
|
|
)
|
|
self.wait()
|
|
self.play(*[
|
|
ReplacementTransform(
|
|
VGroup(factorization.get_part_by_tex(
|
|
tex, substring = False
|
|
)).copy(),
|
|
part
|
|
)
|
|
for tex, part in zip(["=", "2", "3", "5"], count)
|
|
])
|
|
self.wait()
|
|
|
|
self.factorization = factorization
|
|
self.count = count
|
|
|
|
def write_chi_expression(self):
|
|
equals_four = TexMobject("=", "4")
|
|
expression = VGroup(equals_four)
|
|
for n, k, color in zip([2, 3, 5], [2, 4, 3], reversed(self.colors)):
|
|
args = ["(", "\\chi(", "1", ")", "+"]
|
|
for i in range(1, k+1):
|
|
args += ["\\chi(", str(n), "^%d"%i, ")", "+"]
|
|
args[-1] = ")"
|
|
factor = TexMobject(*args)
|
|
factor.set_color_by_tex(str(n), color, substring = False)
|
|
factor.set_color_by_tex("1", color, substring = False)
|
|
factor.scale(0.8)
|
|
expression.add(factor)
|
|
expression.arrange(
|
|
DOWN, buff = MED_SMALL_BUFF, aligned_edge = LEFT
|
|
)
|
|
equals_four.next_to(expression[1], LEFT, SMALL_BUFF)
|
|
expression.shift(
|
|
self.count[0].get_center() + LARGE_BUFF*DOWN -\
|
|
equals_four.get_center()
|
|
)
|
|
|
|
count_copy = self.count.copy()
|
|
self.play(*[
|
|
ApplyMethod(
|
|
c_part.move_to, e_part, LEFT,
|
|
path_arc = -np.pi/2,
|
|
run_time = 2
|
|
)
|
|
for c_part, e_part in zip(count_copy, expression)
|
|
])
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
count_copy, expression,
|
|
run_time = 2
|
|
))
|
|
self.wait()
|
|
|
|
self.chi_expression = expression
|
|
|
|
def walk_through_expression_terms(self):
|
|
rect = FullScreenFadeRectangle()
|
|
groups = [
|
|
VGroup(
|
|
self.chi_expression[index],
|
|
self.count[index],
|
|
self.factorization.get_part_by_tex(tex1, substring = False),
|
|
self.factorization.get_part_by_tex(tex2, substring = False),
|
|
)
|
|
for index, tex1, tex2 in [
|
|
(-1, "5", "^3"), (-2, "3", "^4"), (-3, "2", "^2")
|
|
]
|
|
]
|
|
evaluation_strings = [
|
|
"(1+1+1+1)",
|
|
"(1-1+1-1+1)",
|
|
"(1+0+0)",
|
|
]
|
|
|
|
for group, tex in zip(groups, evaluation_strings):
|
|
chi_sum, count, base, exp = group
|
|
brace = Brace(chi_sum, DOWN)
|
|
evaluation = brace.get_tex(*tex)
|
|
evaluation.set_color(base.get_color())
|
|
evaluation_rect = BackgroundRectangle(evaluation)
|
|
|
|
self.play(FadeIn(rect), Animation(group))
|
|
self.play(GrowFromCenter(brace))
|
|
self.play(
|
|
FadeIn(evaluation_rect),
|
|
ReplacementTransform(chi_sum.copy(), evaluation),
|
|
)
|
|
self.wait(2)
|
|
self.play(Indicate(count, color = PINK))
|
|
self.wait()
|
|
if base.get_tex_string() is "3":
|
|
new_exp = TexMobject("3")
|
|
new_exp.replace(exp)
|
|
count_num = count[1]
|
|
new_count = TexMobject("0")
|
|
new_count.replace(count_num, dim_to_match = 1)
|
|
new_count.set_color(count_num.get_color())
|
|
evaluation_point = VectorizedPoint(evaluation[-4].get_right())
|
|
chi_sum_point = VectorizedPoint(chi_sum[-7].get_right())
|
|
new_brace = Brace(VGroup(*chi_sum[:-6]), DOWN)
|
|
|
|
to_save = [brace, exp, evaluation, count_num, chi_sum]
|
|
for mob in to_save:
|
|
mob.save_state()
|
|
|
|
self.play(FocusOn(exp))
|
|
self.play(Transform(exp, new_exp))
|
|
self.play(
|
|
Transform(brace, new_brace),
|
|
Transform(
|
|
VGroup(*evaluation[-3:-1]),
|
|
evaluation_point
|
|
),
|
|
evaluation[-1].next_to, evaluation_point, RIGHT, SMALL_BUFF,
|
|
Transform(
|
|
VGroup(*chi_sum[-6:-1]),
|
|
chi_sum_point
|
|
),
|
|
chi_sum[-1].next_to, chi_sum_point, RIGHT, SMALL_BUFF
|
|
)
|
|
self.play(Transform(count_num, new_count))
|
|
self.play(Indicate(count_num, color = PINK))
|
|
self.wait()
|
|
self.play(*[mob.restore for mob in to_save])
|
|
|
|
self.play(
|
|
FadeOut(VGroup(
|
|
rect, brace, evaluation_rect, evaluation
|
|
)),
|
|
Animation(group)
|
|
)
|
|
|
|
def circle_four(self):
|
|
four = self.chi_expression[0][1]
|
|
rect = SurroundingRectangle(four)
|
|
|
|
self.revert_to_original_skipping_status()
|
|
self.play(ShowCreation(rect))
|
|
self.wait(3)
|
|
|
|
class WeAreGettingClose(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says("We're getting close...")
|
|
self.change_student_modes(*["hooray"]*3)
|
|
self.wait(2)
|
|
|
|
class ExpandCountWith45(SummarizeCountingRule):
|
|
CONFIG = {
|
|
"N_str" : "45",
|
|
"coords_list" : [(3, 6), (6, 3)],
|
|
"radius_squared" : 45,
|
|
"y_radius" : 7,
|
|
"rect_opacity" : 0.75,
|
|
}
|
|
def construct(self):
|
|
self.add_count_words()
|
|
self.draw_circle()
|
|
self.add_full_screen_rect()
|
|
self.add_factorization_and_count()
|
|
self.expand_expression()
|
|
self.show_divisor_sum()
|
|
|
|
|
|
def add_factorization_and_count(self):
|
|
factorization = TexMobject(
|
|
"45", "=", "3", "^2", "\\cdot", "5",
|
|
)
|
|
for tex, color in zip(["5", "3",], [GREEN, RED]):
|
|
factorization.set_color_by_tex(tex, color, substring = False)
|
|
factorization.to_edge(UP)
|
|
factorization.shift(1.7*LEFT)
|
|
|
|
equals_four = TexMobject("=", "4")
|
|
expression = VGroup(equals_four)
|
|
for n, k, color in zip([3, 5], [2, 1], [RED, GREEN]):
|
|
args = ["("]
|
|
["\\chi(1)", "+"]
|
|
for i in range(k+1):
|
|
if i == 0:
|
|
input_str = "1"
|
|
elif i == 1:
|
|
input_str = str(n)
|
|
else:
|
|
input_str = "%d^%d"%(n, i)
|
|
args += ["\\chi(%s)"%input_str, "+"]
|
|
args[-1] = ")"
|
|
factor = TexMobject(*args)
|
|
for part in factor[1::2]:
|
|
part[2].set_color(color)
|
|
factor.scale(0.8)
|
|
expression.add(factor)
|
|
expression.arrange(RIGHT, buff = SMALL_BUFF)
|
|
expression.next_to(
|
|
factorization[1], DOWN,
|
|
buff = LARGE_BUFF,
|
|
aligned_edge = LEFT,
|
|
)
|
|
braces = VGroup(*[
|
|
Brace(part, UP)
|
|
for part in expression[1:]
|
|
])
|
|
for brace, num, color in zip(braces, [1, 2], [RED, GREEN]):
|
|
num_mob = brace.get_tex(str(num), buff = SMALL_BUFF)
|
|
num_mob.set_color(color)
|
|
brace.add(num_mob)
|
|
|
|
self.play(
|
|
FadeIn(factorization),
|
|
self.count_words.next_to, expression, LEFT
|
|
)
|
|
self.wait()
|
|
self.play(*[
|
|
ReplacementTransform(
|
|
VGroup(factorization.get_part_by_tex(
|
|
tex, substring = False
|
|
)).copy(),
|
|
part
|
|
)
|
|
for tex, part in zip(["=", "3", "5"], expression)
|
|
])
|
|
self.play(FadeIn(braces))
|
|
self.wait()
|
|
|
|
self.chi_expression = expression
|
|
self.factorization = factorization
|
|
|
|
def expand_expression(self):
|
|
equals, four, lp, rp = list(map(TexMobject, [
|
|
"=", "4", "\\big(", "\\big)"
|
|
]))
|
|
expansion = VGroup(equals, four, lp)
|
|
chi_pairs = list(it.product(*[
|
|
factor[1::2]
|
|
for factor in self.chi_expression[1:]
|
|
]))
|
|
num_pairs = list(it.product([1, 3, 9], [1, 5]))
|
|
products = list(it.starmap(op.mul, num_pairs))
|
|
sorted_indices = np.argsort(products)
|
|
mover_groups = [VGroup(), VGroup()]
|
|
plusses = VGroup()
|
|
prime_pairs = VGroup()
|
|
for index in sorted_indices:
|
|
pair = chi_pairs[index]
|
|
prime_pair = VGroup()
|
|
for chi, movers in zip(pair, mover_groups):
|
|
mover = chi.copy()
|
|
mover.generate_target()
|
|
expansion.add(mover.target)
|
|
movers.add(mover)
|
|
prime_pair.add(mover.target[2])
|
|
prime_pairs.add(prime_pair)
|
|
if index != sorted_indices[-1]:
|
|
plus = TexMobject("+")
|
|
plusses.add(plus)
|
|
expansion.add(plus)
|
|
expansion.add(rp)
|
|
expansion.arrange(RIGHT, buff = SMALL_BUFF)
|
|
expansion.set_width(FRAME_WIDTH - LARGE_BUFF)
|
|
expansion.next_to(ORIGIN, UP)
|
|
rect = BackgroundRectangle(expansion)
|
|
rect.stretch_in_place(1.5, 1)
|
|
|
|
self.play(
|
|
FadeIn(rect),
|
|
*[
|
|
ReplacementTransform(
|
|
self.chi_expression[i][j].copy(),
|
|
mob
|
|
)
|
|
for i, j, mob in [
|
|
(0, 0, equals),
|
|
(0, 1, four),
|
|
(1, 0, lp),
|
|
(2, -1, rp),
|
|
]
|
|
]
|
|
)
|
|
for movers in mover_groups:
|
|
self.wait()
|
|
self.play(movers.next_to, rect, DOWN)
|
|
self.play(*list(map(MoveToTarget, movers)))
|
|
self.play(Write(plusses))
|
|
self.wait()
|
|
|
|
self.expansion = expansion
|
|
self.prime_pairs = prime_pairs
|
|
|
|
def show_divisor_sum(self):
|
|
equals, four, lp, rp = list(map(TexMobject, [
|
|
"=", "4", "\\big(", "\\big)"
|
|
]))
|
|
divisor_sum = VGroup(equals, four, lp)
|
|
|
|
num_pairs = list(it.product([1, 3, 9], [1, 5]))
|
|
products = list(it.starmap(op.mul, num_pairs))
|
|
products.sort()
|
|
color = BLACK
|
|
product_mobs = VGroup()
|
|
chi_mobs = VGroup()
|
|
for product in products:
|
|
chi_mob = TexMobject("\\chi(", str(product), ")")
|
|
product_mob = chi_mob.get_part_by_tex(str(product))
|
|
product_mob.set_color(color)
|
|
product_mobs.add(product_mob)
|
|
divisor_sum.add(chi_mob)
|
|
chi_mobs.add(chi_mob)
|
|
if product != products[-1]:
|
|
divisor_sum.add(TexMobject("+"))
|
|
divisor_sum.add(rp)
|
|
divisor_sum.arrange(RIGHT, buff = SMALL_BUFF)
|
|
divisor_sum.next_to(self.expansion, DOWN, MED_LARGE_BUFF)
|
|
rect = BackgroundRectangle(divisor_sum)
|
|
|
|
prime_pairs = self.prime_pairs.copy()
|
|
for prime_pair, product_mob in zip(prime_pairs, product_mobs):
|
|
prime_pair.target = product_mob.copy()
|
|
prime_pair.target.set_color(YELLOW)
|
|
|
|
braces = VGroup(*[Brace(m, DOWN) for m in chi_mobs])
|
|
for brace, product in zip(braces, products):
|
|
value = brace.get_tex(str(chi_func(product)))
|
|
brace.add(value)
|
|
|
|
self.play(
|
|
FadeIn(rect),
|
|
Write(divisor_sum, run_time = 2)
|
|
)
|
|
self.play(OldLaggedStart(
|
|
MoveToTarget, prime_pairs,
|
|
run_time = 4,
|
|
lag_ratio = 0.25,
|
|
))
|
|
self.remove(prime_pairs)
|
|
product_mobs.set_color(YELLOW)
|
|
self.wait(2)
|
|
self.play(OldLaggedStart(
|
|
ApplyMethod,
|
|
product_mobs,
|
|
lambda m : (m.shift, MED_LARGE_BUFF*DOWN),
|
|
rate_func = there_and_back
|
|
))
|
|
self.play(FadeIn(
|
|
braces,
|
|
run_time = 2,
|
|
lag_ratio = 0.5,
|
|
))
|
|
self.wait(2)
|
|
|
|
class CountLatticePointsInBigCircle(LatticePointScene):
|
|
CONFIG = {
|
|
"y_radius" : 2*11,
|
|
"max_lattice_point_radius" : 10,
|
|
"dot_radius" : 0.05
|
|
}
|
|
def construct(self):
|
|
self.resize_plane()
|
|
self.introduce_points()
|
|
self.show_rings()
|
|
self.ignore_center_dot()
|
|
|
|
def resize_plane(self):
|
|
self.plane.set_stroke(width = 2)
|
|
self.plane.scale(2)
|
|
self.lattice_points.scale(2)
|
|
for point in self.lattice_points:
|
|
point.scale_in_place(0.5)
|
|
|
|
def introduce_points(self):
|
|
circle = self.get_circle(radius = self.max_lattice_point_radius)
|
|
radius = Line(ORIGIN, circle.get_right())
|
|
radius.set_color(RED)
|
|
R = TexMobject("R").next_to(radius, UP)
|
|
R_rect = BackgroundRectangle(R)
|
|
R_group = VGroup(R_rect, R)
|
|
pi_R_squared = TexMobject("\\pi", "R", "^2")
|
|
pi_R_squared.next_to(ORIGIN, UP)
|
|
pi_R_squared.to_edge(RIGHT)
|
|
pi_R_squared_rect = BackgroundRectangle(pi_R_squared)
|
|
pi_R_squared_group = VGroup(pi_R_squared_rect, pi_R_squared)
|
|
|
|
self.play(*list(map(FadeIn, [circle, radius, R_group])))
|
|
self.add_foreground_mobject(R_group)
|
|
self.draw_lattice_points()
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(R_rect),
|
|
FadeIn(pi_R_squared_rect),
|
|
ReplacementTransform(R, pi_R_squared.get_part_by_tex("R")),
|
|
Write(VGroup(*[
|
|
part for part in pi_R_squared
|
|
if part is not pi_R_squared.get_part_by_tex("R")
|
|
]))
|
|
)
|
|
self.remove(R_group)
|
|
self.add_foreground_mobject(pi_R_squared_group)
|
|
self.wait()
|
|
|
|
self.circle = circle
|
|
self.radius = radius
|
|
|
|
def show_rings(self):
|
|
N_range = list(range(self.max_lattice_point_radius**2))
|
|
rings = VGroup(*[
|
|
self.get_circle(radius = np.sqrt(N))
|
|
for N in N_range
|
|
])
|
|
rings.set_color_by_gradient(TEAL, GREEN)
|
|
rings.set_stroke(width = 2)
|
|
dot_groups = VGroup(*[
|
|
self.get_lattice_points_on_r_squared_circle(N)
|
|
for N in N_range
|
|
])
|
|
radicals = self.get_radicals()
|
|
|
|
self.play(
|
|
OldLaggedStart(FadeIn, rings),
|
|
Animation(self.lattice_points),
|
|
OldLaggedStart(FadeIn, radicals),
|
|
run_time = 3
|
|
)
|
|
self.add_foreground_mobject(radicals)
|
|
self.play(
|
|
OldLaggedStart(
|
|
ApplyMethod,
|
|
dot_groups,
|
|
lambda m : (m.set_stroke, PINK, 5),
|
|
rate_func = there_and_back,
|
|
run_time = 4,
|
|
lag_ratio = 0.1,
|
|
),
|
|
)
|
|
self.wait()
|
|
|
|
self.rings = rings
|
|
|
|
def ignore_center_dot(self):
|
|
center_dot = self.lattice_points[0]
|
|
circle = Circle(color = RED)
|
|
circle.replace(center_dot)
|
|
circle.scale_in_place(2)
|
|
arrow = Arrow(ORIGIN, UP+RIGHT, color = RED)
|
|
arrow.next_to(circle, DOWN+LEFT, SMALL_BUFF)
|
|
new_max = 2*self.max_lattice_point_radius
|
|
new_dots = VGroup(*[
|
|
Dot(
|
|
self.plane.coords_to_point(x, y),
|
|
color = self.dot_color,
|
|
radius = self.dot_radius,
|
|
)
|
|
for x in range(-new_max, new_max+1)
|
|
for y in range(-new_max, new_max+1)
|
|
if (x**2 + y**2) > self.max_lattice_point_radius**2
|
|
if (x**2 + y**2) < new_max**2
|
|
])
|
|
new_dots.sort(get_norm)
|
|
|
|
self.play(*list(map(ShowCreation, [circle, arrow])))
|
|
self.play(*list(map(FadeOut, [circle, arrow])))
|
|
self.play(FadeOut(center_dot))
|
|
self.lattice_points.remove(center_dot)
|
|
self.wait()
|
|
self.play(*[
|
|
ApplyMethod(m.scale, 0.5)
|
|
for m in [
|
|
self.plane,
|
|
self.circle,
|
|
self.radius,
|
|
self.rings,
|
|
self.lattice_points
|
|
]
|
|
])
|
|
new_dots.scale(0.5)
|
|
self.play(FadeOut(self.rings))
|
|
self.play(
|
|
ApplyMethod(
|
|
VGroup(self.circle, self.radius).scale_in_place, 2,
|
|
rate_func=linear,
|
|
),
|
|
OldLaggedStart(
|
|
DrawBorderThenFill,
|
|
new_dots,
|
|
stroke_width = 4,
|
|
stroke_color = PINK,
|
|
lag_ratio = 0.2,
|
|
),
|
|
run_time = 4,
|
|
)
|
|
self.wait(2)
|
|
|
|
#####
|
|
|
|
@staticmethod
|
|
def get_radicals():
|
|
radicals = VGroup(*[
|
|
TexMobject("\\sqrt{%d}"%N)
|
|
for N in range(1, 13)
|
|
])
|
|
radicals.add(
|
|
TexMobject("\\vdots"),
|
|
TexMobject("\\sqrt{R^2}")
|
|
)
|
|
radicals.arrange(DOWN, buff = MED_SMALL_BUFF)
|
|
radicals.set_height(FRAME_HEIGHT - MED_LARGE_BUFF)
|
|
radicals.to_edge(DOWN, buff = MED_SMALL_BUFF)
|
|
radicals.to_edge(LEFT)
|
|
for radical in radicals:
|
|
radical.add_background_rectangle()
|
|
return radicals
|
|
|
|
class AddUpGrid(Scene):
|
|
def construct(self):
|
|
self.add_radicals()
|
|
self.add_row_lines()
|
|
self.add_chi_sums()
|
|
self.put_four_in_corner()
|
|
self.talk_through_rows()
|
|
self.organize_into_columns()
|
|
self.add_count_words()
|
|
self.collapse_columns()
|
|
self.factor_out_R()
|
|
self.show_chi_sum_values()
|
|
self.compare_to_pi_R_squared()
|
|
self.celebrate()
|
|
|
|
def add_radicals(self):
|
|
self.radicals = CountLatticePointsInBigCircle.get_radicals()
|
|
self.add(self.radicals)
|
|
|
|
def add_row_lines(self):
|
|
h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS - MED_LARGE_BUFF)
|
|
h_line.set_stroke(WHITE, 1)
|
|
row_lines = VGroup(*[
|
|
h_line.copy().next_to(
|
|
radical, DOWN,
|
|
buff = SMALL_BUFF,
|
|
aligned_edge = LEFT
|
|
)
|
|
for radical in self.radicals
|
|
])
|
|
row_lines[-2].shift(
|
|
row_lines[-1].get_left()[0]*RIGHT -\
|
|
row_lines[-2].get_left()[0]*RIGHT
|
|
)
|
|
|
|
self.play(OldLaggedStart(ShowCreation, row_lines))
|
|
self.wait()
|
|
|
|
self.row_lines = row_lines
|
|
|
|
def add_chi_sums(self):
|
|
chi_sums = VGroup()
|
|
chi_mobs = VGroup()
|
|
plusses = VGroup()
|
|
fours = VGroup()
|
|
parens = VGroup()
|
|
arrows = VGroup()
|
|
for N, radical in zip(list(range(1, 13)), self.radicals):
|
|
arrow, four, lp, rp = list(map(TexMobject, [
|
|
"\\Rightarrow", "4", "\\big(", "\\big)"
|
|
]))
|
|
fours.add(four)
|
|
parens.add(lp, rp)
|
|
arrows.add(arrow)
|
|
chi_sum = VGroup(arrow, four, lp)
|
|
for d in range(1, N+1):
|
|
if N%d != 0:
|
|
continue
|
|
chi_mob = TexMobject("\\chi(", str(d), ")")
|
|
chi_mob[1].set_color(YELLOW)
|
|
chi_mob.d = d
|
|
chi_mobs.add(chi_mob)
|
|
chi_sum.add(chi_mob)
|
|
if d != N:
|
|
plus = TexMobject("+")
|
|
plus.chi_mob = chi_mob
|
|
plusses.add(plus)
|
|
chi_sum.add(plus)
|
|
chi_sum.add(rp)
|
|
chi_sum.arrange(RIGHT, buff = SMALL_BUFF)
|
|
chi_sum.scale(0.7)
|
|
chi_sum.next_to(radical, RIGHT)
|
|
chi_sums.add(chi_sum)
|
|
radical.chi_sum = chi_sum
|
|
|
|
self.play(OldLaggedStart(
|
|
Write, chi_sums,
|
|
run_time = 5,
|
|
rate_func = lambda t : t,
|
|
))
|
|
self.wait()
|
|
|
|
digest_locals(self, [
|
|
"chi_sums", "chi_mobs", "plusses",
|
|
"fours", "parens", "arrows",
|
|
])
|
|
|
|
def put_four_in_corner(self):
|
|
corner_four = TexMobject("4")
|
|
corner_four.to_corner(DOWN+RIGHT, buff = MED_SMALL_BUFF)
|
|
rect = SurroundingRectangle(corner_four, color = BLUE)
|
|
corner_four.rect = rect
|
|
|
|
self.play(
|
|
ReplacementTransform(
|
|
self.fours, VGroup(corner_four),
|
|
run_time = 2,
|
|
),
|
|
FadeOut(self.parens)
|
|
)
|
|
self.play(ShowCreation(rect))
|
|
|
|
self.corner_four = corner_four
|
|
|
|
def talk_through_rows(self):
|
|
rect = Rectangle(
|
|
stroke_width = 0,
|
|
fill_color = BLUE_C,
|
|
fill_opacity = 0.3,
|
|
)
|
|
rect.stretch_to_fit_width(
|
|
VGroup(self.radicals, self.chi_mobs).get_width()
|
|
)
|
|
rect.stretch_to_fit_height(self.radicals[0].get_height())
|
|
|
|
composite_rects, prime_rects = [
|
|
VGroup(*[
|
|
rect.copy().move_to(self.radicals[N-1], LEFT)
|
|
for N in numbers
|
|
])
|
|
for numbers in ([6, 12], [2, 3, 5, 7, 11])
|
|
]
|
|
prime_rects.set_color(GREEN)
|
|
|
|
randy = Randolph().flip()
|
|
randy.next_to(self.chi_mobs, RIGHT)
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(randy.change_mode, "pleading")
|
|
self.play(
|
|
FadeIn(composite_rects),
|
|
randy.look_at, composite_rects.get_bottom()
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
FadeOut(composite_rects),
|
|
FadeIn(prime_rects),
|
|
randy.look_at, prime_rects.get_top(),
|
|
)
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [prime_rects, randy])))
|
|
|
|
def organize_into_columns(self):
|
|
left_x = self.arrows.get_right()[0] + SMALL_BUFF
|
|
spacing = self.chi_mobs[-1].get_width() + SMALL_BUFF
|
|
for chi_mob in self.chi_mobs:
|
|
y = chi_mob.get_left()[1]
|
|
x = left_x + (chi_mob.d - 1)*spacing
|
|
chi_mob.generate_target()
|
|
chi_mob.target.move_to(x*RIGHT + y*UP, LEFT)
|
|
for plus in self.plusses:
|
|
plus.generate_target()
|
|
plus.target.scale(0.5)
|
|
plus.target.next_to(
|
|
plus.chi_mob.target, RIGHT, SMALL_BUFF
|
|
)
|
|
|
|
self.play(*it.chain(
|
|
list(map(MoveToTarget, self.chi_mobs)),
|
|
list(map(MoveToTarget, self.plusses)),
|
|
), run_time = 2)
|
|
self.wait()
|
|
|
|
def add_count_words(self):
|
|
rect = Rectangle(
|
|
stroke_color = WHITE,
|
|
stroke_width = 2,
|
|
fill_color = average_color(BLUE_E, BLACK),
|
|
fill_opacity = 1,
|
|
height = 1.15,
|
|
width = FRAME_WIDTH - 2*MED_SMALL_BUFF,
|
|
)
|
|
rect.move_to(3*LEFT, LEFT)
|
|
rect.to_edge(UP, buff = SMALL_BUFF)
|
|
words = TextMobject("Total")
|
|
words.scale(0.8)
|
|
words.next_to(rect.get_left(), RIGHT, SMALL_BUFF)
|
|
approx = TexMobject("\\approx")
|
|
approx.scale(0.7)
|
|
approx.next_to(words, RIGHT, SMALL_BUFF)
|
|
words.add(approx)
|
|
|
|
self.play(*list(map(FadeIn, [rect, words])))
|
|
self.wait()
|
|
|
|
self.count_rect = rect
|
|
self.count_words = words
|
|
|
|
def collapse_columns(self):
|
|
chi_mob_columns = [VGroup() for i in range(12)]
|
|
for chi_mob in self.chi_mobs:
|
|
chi_mob_columns[chi_mob.d - 1].add(chi_mob)
|
|
|
|
full_sum = VGroup()
|
|
for d in range(1, 7):
|
|
R_args = ["{R^2"]
|
|
if d != 1:
|
|
R_args.append("\\over %d}"%d)
|
|
term = VGroup(
|
|
TexMobject(*R_args),
|
|
TexMobject("\\chi(", str(d), ")"),
|
|
TexMobject("+")
|
|
)
|
|
term.arrange(RIGHT, SMALL_BUFF)
|
|
term[1][1].set_color(YELLOW)
|
|
full_sum.add(term)
|
|
full_sum.arrange(RIGHT, SMALL_BUFF)
|
|
full_sum.scale(0.7)
|
|
full_sum.next_to(self.count_words, RIGHT, SMALL_BUFF)
|
|
|
|
for column, term in zip(chi_mob_columns, full_sum):
|
|
rect = SurroundingRectangle(column)
|
|
rect.stretch_to_fit_height(FRAME_HEIGHT)
|
|
rect.move_to(column, UP)
|
|
rect.set_stroke(width = 0)
|
|
rect.set_fill(YELLOW, 0.3)
|
|
|
|
self.play(FadeIn(rect))
|
|
self.wait()
|
|
self.play(
|
|
ReplacementTransform(
|
|
column.copy(),
|
|
VGroup(term[1]),
|
|
run_time = 2
|
|
),
|
|
Write(term[0]),
|
|
Write(term[2]),
|
|
)
|
|
self.wait()
|
|
if term is full_sum[2]:
|
|
vect = sum([
|
|
self.count_rect.get_left()[0],
|
|
FRAME_X_RADIUS,
|
|
-MED_SMALL_BUFF,
|
|
])*LEFT
|
|
self.play(*[
|
|
ApplyMethod(m.shift, vect)
|
|
for m in [
|
|
self.count_rect,
|
|
self.count_words,
|
|
]+list(full_sum[:3])
|
|
])
|
|
VGroup(*full_sum[3:]).shift(vect)
|
|
self.play(FadeOut(rect))
|
|
|
|
self.full_sum = full_sum
|
|
|
|
def factor_out_R(self):
|
|
self.corner_four.generate_target()
|
|
R_squared = TexMobject("R^2")
|
|
dots = TexMobject("\\cdots")
|
|
lp, rp = list(map(TexMobject, ["\\big(", "\\big)"]))
|
|
new_sum = VGroup(
|
|
self.corner_four.target, R_squared, lp
|
|
)
|
|
|
|
R_fracs, chi_terms, plusses = full_sum_parts = [
|
|
VGroup(*[term[i] for term in self.full_sum])
|
|
for i in range(3)
|
|
]
|
|
targets = []
|
|
for part in full_sum_parts:
|
|
part.generate_target()
|
|
targets.append(part.target)
|
|
for R_frac, chi_term, plus in zip(*targets):
|
|
chi_term.scale(0.9)
|
|
chi_term.move_to(R_frac[0], DOWN)
|
|
if R_frac is R_fracs.target[0]:
|
|
new_sum.add(chi_term)
|
|
else:
|
|
new_sum.add(VGroup(chi_term, R_frac[1]))
|
|
new_sum.add(plus)
|
|
new_sum.add(dots)
|
|
new_sum.add(rp)
|
|
new_sum.arrange(RIGHT, buff = SMALL_BUFF)
|
|
new_sum.next_to(self.count_words, RIGHT, SMALL_BUFF)
|
|
R_squared.shift(0.5*SMALL_BUFF*UP)
|
|
R_movers = VGroup()
|
|
for R_frac in R_fracs.target:
|
|
if R_frac is R_fracs.target[0]:
|
|
mover = R_frac
|
|
else:
|
|
mover = R_frac[0]
|
|
Transform(mover, R_squared).update(1)
|
|
R_movers.add(mover)
|
|
|
|
self.play(*it.chain(
|
|
list(map(Write, [lp, rp, dots])),
|
|
list(map(MoveToTarget, full_sum_parts)),
|
|
), run_time = 2)
|
|
self.remove(R_movers)
|
|
self.add(R_squared)
|
|
self.wait()
|
|
self.play(
|
|
MoveToTarget(self.corner_four, run_time = 2),
|
|
FadeOut(self.corner_four.rect)
|
|
)
|
|
self.wait(2)
|
|
self.remove(self.full_sum, self.corner_four)
|
|
self.add(new_sum)
|
|
|
|
self.new_sum = new_sum
|
|
|
|
def show_chi_sum_values(self):
|
|
alt_rhs = TexMobject(
|
|
"\\approx", "4", "R^2",
|
|
"\\left(1 - \\frac{1}{3} + \\frac{1}{5}" + \
|
|
"-\\frac{1}{7} + \\frac{1}{9} - \\frac{1}{11}" + \
|
|
"+ \\cdots \\right)",
|
|
)
|
|
alt_rhs.scale(0.9)
|
|
alt_rhs.next_to(
|
|
self.count_words[-1], DOWN,
|
|
buff = LARGE_BUFF,
|
|
aligned_edge = LEFT
|
|
)
|
|
|
|
self.play(
|
|
*list(map(FadeOut, [
|
|
self.chi_mobs, self.plusses, self.arrows,
|
|
self.radicals, self.row_lines
|
|
])) + [
|
|
FadeOut(self.count_rect),
|
|
Animation(self.new_sum),
|
|
Animation(self.count_words),
|
|
]
|
|
)
|
|
self.play(Write(alt_rhs))
|
|
self.wait(2)
|
|
|
|
self.alt_rhs = alt_rhs
|
|
|
|
def compare_to_pi_R_squared(self):
|
|
approx, pi, R_squared = area_rhs = TexMobject(
|
|
"\\approx", "\\pi", "R^2"
|
|
)
|
|
area_rhs.next_to(self.alt_rhs, RIGHT)
|
|
|
|
brace = Brace(
|
|
VGroup(self.alt_rhs, area_rhs), DOWN
|
|
)
|
|
brace.add(brace.get_text(
|
|
"Arbitrarily good as $R \\to \\infty$"
|
|
))
|
|
|
|
pi_sum = TexMobject(
|
|
"4", self.alt_rhs[-1].get_tex_string(),
|
|
"=", "\\pi"
|
|
)
|
|
pi_sum.scale(0.9)
|
|
pi = pi_sum.get_part_by_tex("pi")
|
|
pi.scale(2, about_point = pi.get_left())
|
|
pi.set_color(YELLOW)
|
|
pi_sum.shift(
|
|
self.alt_rhs[-1].get_bottom(),
|
|
MED_SMALL_BUFF*DOWN,
|
|
-pi_sum[1].get_top()
|
|
)
|
|
|
|
self.play(Write(area_rhs))
|
|
self.wait()
|
|
self.play(FadeIn(brace))
|
|
self.wait(2)
|
|
self.play(FadeOut(brace))
|
|
self.play(*[
|
|
ReplacementTransform(m.copy(), pi_sum_part)
|
|
for pi_sum_part, m in zip(pi_sum, [
|
|
self.alt_rhs.get_part_by_tex("4"),
|
|
self.alt_rhs[-1],
|
|
area_rhs[0],
|
|
area_rhs[1],
|
|
])
|
|
])
|
|
|
|
def celebrate(self):
|
|
creatures = TeacherStudentsScene().get_pi_creatures()
|
|
self.play(FadeIn(creatures))
|
|
self.play(*[
|
|
ApplyMethod(pi.change, "hooray", self.alt_rhs)
|
|
for pi in creatures
|
|
])
|
|
self.wait()
|
|
for i in 0, 2, 3:
|
|
self.play(Blink(creatures[i]))
|
|
self.wait()
|
|
|
|
class IntersectionOfTwoFields(TeacherStudentsScene):
|
|
def construct(self):
|
|
circles = VGroup()
|
|
for vect, color, adj in (LEFT, BLUE, "Algebraic"), (RIGHT, YELLOW, "Analytic"):
|
|
circle = Circle(color = WHITE)
|
|
circle.set_fill(color, opacity = 0.3)
|
|
circle.stretch_to_fit_width(7)
|
|
circle.stretch_to_fit_height(4)
|
|
circle.shift(FRAME_X_RADIUS*vect/3.0 + LEFT)
|
|
title = TextMobject("%s \\\\ number theory"%adj)
|
|
title.scale(0.7)
|
|
title.move_to(circle)
|
|
title.to_edge(UP, buff = SMALL_BUFF)
|
|
circle.next_to(title, DOWN, SMALL_BUFF)
|
|
title.set_color(color)
|
|
circle.title = title
|
|
circles.add(circle)
|
|
new_number_systems = TextMobject(
|
|
"New \\\\ number systems"
|
|
)
|
|
gaussian_integers = TextMobject(
|
|
"e.g. Gaussian \\\\ integers"
|
|
)
|
|
new_number_systems.next_to(circles[0].get_top(), DOWN, MED_SMALL_BUFF)
|
|
new_number_systems.shift(MED_LARGE_BUFF*(DOWN+2*LEFT))
|
|
gaussian_integers.next_to(new_number_systems, DOWN)
|
|
gaussian_integers.set_color(BLUE)
|
|
circles[0].words = VGroup(new_number_systems, gaussian_integers)
|
|
|
|
zeta = TexMobject("\\zeta(s) = \\sum_{n=1}^\\infty \\frac{1}{n^s}")
|
|
L_function = TexMobject(
|
|
"L(s, \\chi) = \\sum_{n=1}^\\infty \\frac{\\chi(n)}{n^s}"
|
|
)
|
|
for mob in zeta, L_function:
|
|
mob.scale(0.8)
|
|
zeta.next_to(circles[1].get_top(), DOWN, MED_LARGE_BUFF)
|
|
zeta.shift(MED_LARGE_BUFF*RIGHT)
|
|
L_function.next_to(zeta, DOWN, MED_LARGE_BUFF)
|
|
L_function.set_color(YELLOW)
|
|
circles[1].words = VGroup(zeta, L_function)
|
|
|
|
mid_words = TextMobject("Where\\\\ we \\\\ were")
|
|
mid_words.scale(0.7)
|
|
mid_words.move_to(circles)
|
|
|
|
for circle in circles:
|
|
self.play(
|
|
Write(circle.title, run_time = 2),
|
|
DrawBorderThenFill(circle, run_time = 2),
|
|
self.teacher.change_mode, "raise_right_hand"
|
|
)
|
|
self.wait()
|
|
for circle in circles:
|
|
for word in circle.words:
|
|
self.play(
|
|
Write(word, run_time = 2),
|
|
self.teacher.change, "speaking",
|
|
*[
|
|
ApplyMethod(pi.change, "pondering")
|
|
for pi in self.get_students()
|
|
]
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Write(mid_words),
|
|
self.teacher.change, "raise_right_hand"
|
|
)
|
|
self.change_student_modes(
|
|
*["thinking"]*3,
|
|
look_at_arg = mid_words
|
|
)
|
|
self.wait(3)
|
|
|
|
class LeibnizPatreonThanks(PatreonThanks):
|
|
CONFIG = {
|
|
"specific_patrons" : [
|
|
"Ali Yahya",
|
|
"Burt Humburg",
|
|
"CrypticSwarm",
|
|
"Erik Sundell",
|
|
"Yana Chernobilsky",
|
|
"Kaustuv DeBiswas",
|
|
"Kathryn Schmiedicke",
|
|
"Karan Bhargava",
|
|
"Ankit Agarwal",
|
|
"Yu Jun",
|
|
"Dave Nicponski",
|
|
"Damion Kistler",
|
|
"Juan Benet",
|
|
"Othman Alikhan",
|
|
"Markus Persson",
|
|
"Yoni Nazarathy",
|
|
"Joseph John Cox",
|
|
"Dan Buchoff",
|
|
"Luc Ritchie",
|
|
"Guido Gambardella",
|
|
"Julian Pulgarin",
|
|
"John Haley",
|
|
"Jeff Linse",
|
|
"Suraj Pratap",
|
|
"Cooper Jones",
|
|
"Ryan Dahl",
|
|
"Ahmad Bamieh",
|
|
"Mark Govea",
|
|
"Robert Teed",
|
|
"Jason Hise",
|
|
"Meshal Alshammari",
|
|
"Bernd Sing",
|
|
"Nils Schneider",
|
|
"James Thornton",
|
|
"Mustafa Mahdi",
|
|
"Mathew Bramson",
|
|
"Jerry Ling",
|
|
"Vecht",
|
|
"Shimin Kuang",
|
|
"Rish Kundalia",
|
|
"Achille Brighton",
|
|
"Ripta Pasay",
|
|
],
|
|
}
|
|
|
|
class Sponsorship(PiCreatureScene):
|
|
def construct(self):
|
|
morty = self.pi_creature
|
|
logo = SVGMobject(
|
|
file_name = "remix_logo",
|
|
)
|
|
logo.set_height(1)
|
|
logo.center()
|
|
logo.set_stroke(width = 0)
|
|
logo.set_fill(BLUE_D, 1)
|
|
VGroup(*logo[6:]).set_color_by_gradient(BLUE_B, BLUE_E)
|
|
logo.next_to(morty.get_corner(UP+LEFT), UP)
|
|
|
|
url = TextMobject("www.remix.com")
|
|
url.to_corner(UP+LEFT)
|
|
rect = ScreenRectangle(height = 5)
|
|
rect.next_to(url, DOWN, aligned_edge = LEFT)
|
|
|
|
self.play(
|
|
morty.change_mode, "raise_right_hand",
|
|
OldLaggedStart(DrawBorderThenFill, logo, run_time = 3)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
ShowCreation(rect),
|
|
logo.scale, 0.8,
|
|
logo.to_corner, UP+RIGHT,
|
|
morty.change, "happy"
|
|
)
|
|
self.wait()
|
|
self.play(Write(url))
|
|
self.wait(3)
|
|
for mode in "confused", "pondering", "happy":
|
|
self.play(morty.change_mode, mode)
|
|
self.wait(3)
|
|
|
|
class Thumbnail(Scene):
|
|
def construct(self):
|
|
randy = Randolph()
|
|
randy.set_height(5)
|
|
body_copy = randy.body.copy()
|
|
body_copy.set_stroke(YELLOW, width = 3)
|
|
body_copy.set_fill(opacity = 0)
|
|
self.add(randy)
|
|
|
|
primes = [
|
|
n for n in range(2, 1000)
|
|
if all(n%k != 0 for k in list(range(2, n)))
|
|
]
|
|
prime_mobs = VGroup()
|
|
x_spacing = 1.7
|
|
y_spacing = 1.5
|
|
n_rows = 10
|
|
n_cols = 8
|
|
for i, prime in enumerate(primes[:n_rows*n_cols]):
|
|
prime_mob = Integer(prime)
|
|
prime_mob.scale(1.5)
|
|
x = i%n_cols
|
|
y = i//n_cols
|
|
prime_mob.shift(x*x_spacing*RIGHT + y*y_spacing*DOWN)
|
|
prime_mobs.add(prime_mob)
|
|
prime_mob.set_color({
|
|
-1 : YELLOW,
|
|
0 : RED,
|
|
1 : BLUE_C,
|
|
}[chi_func(prime)])
|
|
prime_mobs.center().to_edge(UP)
|
|
for i in range(7):
|
|
self.add(SurroundingRectangle(
|
|
VGroup(*prime_mobs[n_cols*i:n_cols*(i+1)]),
|
|
fill_opacity = 0.7,
|
|
fill_color = BLACK,
|
|
stroke_width = 0,
|
|
buff = 0,
|
|
))
|
|
self.add(prime_mobs)
|
|
self.add(body_copy)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|