From 185097de585bc2b5e31499bd88f46b8158399f9b Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 6 Dec 2017 21:09:27 -0800 Subject: [PATCH] RevisitTwoDCase of putnam.py --- putnam.py | 476 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 437 insertions(+), 39 deletions(-) diff --git a/putnam.py b/putnam.py index 9ead4702..d828b557 100644 --- a/putnam.py +++ b/putnam.py @@ -277,6 +277,7 @@ class HowDoYouStart(TeacherStudentsScene): class TwoDCase(Scene): CONFIG = { + "center" : ORIGIN, "random_seed" : 4, "radius" : 2.5, "center_color" : BLUE, @@ -311,12 +312,13 @@ class TwoDCase(Scene): circle = Circle(radius = self.radius, color = WHITE) center_dot = Dot(color = self.center_color).center() radius = DashedLine(ORIGIN, circle.radius*RIGHT) + VGroup(circle, center_dot, radius).shift(self.center) self.add(center_dot) self.play(ShowCreation(radius)) self.play( ShowCreation(circle), - Rotating(radius, angle = 2*np.pi, about_point = ORIGIN), + Rotating(radius, angle = 2*np.pi, about_point = self.center), rate_func = smooth, run_time = 2, ) @@ -330,27 +332,10 @@ class TwoDCase(Scene): self.set_variables_as_attrs(circle, center_dot) def choose_three_random_points(self): - points = np.array([ - rotate_vector(self.radius*RIGHT, theta) - for theta in 2*np.pi*np.random.random(3) - ]) - for index in 0, 1, 0: - if self.points_contain_center(points): - break - points[index] *= -1 - - point_mobs = self.point_mobs = VGroup(*[ - Dot().move_to(point) for point in points - ]) - point_mobs.highlight(self.point_color) - point_labels = VGroup(*[ - TexMobject("P_%d"%(i+1)) - for i in range(len(point_mobs)) - ]) - point_labels.highlight(point_mobs.get_color()) + point_mobs = self.get_point_mobs() + point_labels = self.get_point_mob_labels() + triangle = self.get_triangle() self.point_labels_update = self.get_labels_update(point_mobs, point_labels) - triangle = self.triangle = RegularPolygon(n = 3) - triangle.set_fill(WHITE, opacity = self.triangle_fill_opacity) self.triangle_update = self.get_triangle_update(point_mobs, triangle) self.update_animations = [ self.triangle_update, @@ -453,6 +438,7 @@ class TwoDCase(Scene): line = Line(LEFT, RIGHT).scale(SMALL_BUFF) line.shift(self.radius*RIGHT) line.rotate(angle + np.pi) + line.shift(self.center) line.set_stroke(arc.get_color()) arc_lines.add(line) @@ -473,14 +459,7 @@ class TwoDCase(Scene): angles = self.get_point_mob_angles() all_arcs = self.all_arcs - lines = VGroup() - for angle in angles[:2]: - line = DashedLine( - self.radius*RIGHT, self.radius*LEFT - ) - line.rotate(angle) - line.highlight(self.point_color) - lines.add(line) + lines = self.get_center_lines() self.add_foreground_mobjects(self.center_dot) for line in lines: @@ -503,8 +482,6 @@ class TwoDCase(Scene): self.change_point_mobs([0, 0, np.pi/2]) self.dither() - self.center_lines = lines - def ask_about_probability_p3_lands_in_this_arc(self): arc = self.arc @@ -567,6 +544,7 @@ class TwoDCase(Scene): Line(DOWN+RIGHT, RIGHT), ) elbow.scale(0.25) + elbow.shift(self.center) ninety_degrees = TexMobject("90^\\circ") ninety_degrees.next_to(elbow, DOWN+RIGHT, buff = 0) proportion = DecimalNumber(0.25) @@ -666,11 +644,56 @@ class TwoDCase(Scene): ##### + def get_point_mobs(self): + points = np.array([ + self.center + rotate_vector(self.radius*RIGHT, theta) + for theta in 2*np.pi*np.random.random(3) + ]) + for index in 0, 1, 0: + if self.points_contain_center(points): + break + points[index] -= self.center + points[index] *= -1 + points[index] += self.center + point_mobs = self.point_mobs = VGroup(*[ + Dot().move_to(point) for point in points + ]) + point_mobs.highlight(self.point_color) + return point_mobs + + def get_point_mob_labels(self): + point_labels = VGroup(*[ + TexMobject("P_%d"%(i+1)) + for i in range(len(self.point_mobs)) + ]) + point_labels.highlight(self.point_mobs.get_color()) + self.point_labels = point_labels + return point_labels + + def get_triangle(self): + triangle = self.triangle = RegularPolygon(n = 3) + triangle.set_fill(WHITE, opacity = self.triangle_fill_opacity) + return triangle + + def get_center_lines(self): + angles = self.get_point_mob_angles() + lines = VGroup() + for angle in angles[:2]: + line = DashedLine( + self.radius*RIGHT, self.radius*LEFT + ) + line.rotate(angle) + line.shift(self.center) + line.highlight(self.point_color) + lines.add(line) + self.center_lines = lines + return lines + def get_labels_update(self, point_mobs, labels): def update_labels(labels): for point_mob, label in zip(point_mobs, labels): label.move_to(point_mob) - vect = point_mob.get_center() + vect = point_mob.get_center() - self.center vect /= np.linalg.norm(vect) label.shift(MED_LARGE_BUFF*vect) return labels @@ -690,10 +713,11 @@ class TwoDCase(Scene): def get_center_lines_update(self, point_mobs, center_lines): def update_lines(center_lines): for point_mob, line in zip(point_mobs, center_lines): - line.rotate( - angle_of_vector(point_mob.get_center()) - \ - line.get_angle() + point = point_mob.get_center() - self.center + line.rotate_in_place( + angle_of_vector(point) - line.get_angle() ) + line.move_to(self.center) return center_lines return UpdateFromFunc(center_lines, update_lines) @@ -716,28 +740,30 @@ class TwoDCase(Scene): radius = self.radius, stroke_width = 5, ) + arc.shift(self.center) all_arcs.add(arc) all_arcs.gradient_highlight(RED, MAROON_B, PINK, BLUE) + self.all_arcs = all_arcs return all_arcs def points_contain_center(self, points): p0, p1, p2 = points v1 = p1 - p0 v2 = p2 - p0 - c = -p0 + c = self.center - p0 M = np.matrix([v1[:2], v2[:2]]).T M_inv = np.linalg.inv(M) coords = np.dot(M_inv, c[:2]) return np.all(coords > 0) and (np.sum(coords.flatten()) <= 1) def get_point_mob_theta_change_anim(self, point_mob, d_theta): - curr_theta = angle_of_vector(point_mob.get_center()) + curr_theta = angle_of_vector(point_mob.get_center() - self.center) d_theta = (d_theta + np.pi)%(2*np.pi) - np.pi new_theta = curr_theta + d_theta def update_point(point_mob, alpha): theta = interpolate(curr_theta, new_theta, alpha) - point_mob.move_to(self.radius*( + point_mob.move_to(self.center + self.radius*( np.cos(theta)*RIGHT + np.sin(theta)*UP )) return point_mob @@ -771,7 +797,7 @@ class TwoDCase(Scene): def get_point_mob_angles(self): point_mobs = self.point_mobs - points = [pm.get_center() for pm in point_mobs] + points = [pm.get_center() - self.center for pm in point_mobs] return np.array(map(angle_of_vector, points)) def have_p3_jump_around_randomly(self, n_jumps, dither_time = 0.75, run_time = 0): @@ -813,6 +839,378 @@ class TryASurfaceIntegral(TeacherStudentsScene): target_mode = "sassy", ) self.dither(2) + +class RevisitTwoDCase(TwoDCase): + CONFIG = { + "random_seed" : 4, + "center" : 3*LEFT + 0.5*DOWN, + "radius" : 2, + "n_random_trials" : 200, + } + def construct(self): + self.setup_circle() + self.show_probability() + self.add_lines_and_comment_on_them() + self.rewrite_random_procedure() + self.four_possibilities_for_coin_flips() + + def setup_circle(self): + point_mobs = self.get_point_mobs() + point_labels = self.get_point_mob_labels() + triangle = self.get_triangle() + circle = Circle(radius = self.radius, color = WHITE) + center_dot = Dot(color = self.center_color) + VGroup(circle, center_dot).shift(self.center) + + self.point_labels_update = self.get_labels_update(point_mobs, point_labels) + self.triangle_update = self.get_triangle_update(point_mobs, triangle) + self.update_animations = [ + self.triangle_update, + self.point_labels_update, + ] + for anim in self.update_animations: + anim.update(1) + + self.add( + center_dot, circle, triangle, + point_mobs, point_labels + ) + self.add_foreground_mobjects(center_dot) + self.set_variables_as_attrs(circle, center_dot) + + def show_probability(self): + title = TexMobject( + "P(\\text{triangle contains the center})", + "=", "1/4" + ) + title.to_edge(UP, buff = MED_SMALL_BUFF) + title.highlight_by_tex("1/4", BLUE) + four = title[-1][-1] + four_circle = Circle(color = YELLOW) + four_circle.replace(four, dim_to_match = 1) + four_circle.scale_in_place(1.2) + + self.n_in = 0 + self.n_out = 0 + frac = TexMobject( + "{0", "\\over", "\\quad 0", "+", "0 \\quad}", "=" + ) + placeholders = frac.get_parts_by_tex("0") + positions = [ORIGIN, RIGHT, LEFT] + frac.next_to(self.circle, RIGHT, 1.5*LARGE_BUFF) + + def place_random_triangles(n, dither_time): + for x in range(n): + self.change_point_mobs_randomly(run_time = 0) + contain_center = self.points_contain_center( + [pm.get_center() for pm in self.point_mobs] + ) + if contain_center: + self.n_in += 1 + else: + self.n_out += 1 + nums = map(Integer, [self.n_in, self.n_in, self.n_out]) + VGroup(*nums[:2]).highlight(self.positive_triangle_color) + VGroup(*nums[2:]).highlight(self.negative_triangle_color) + for num, placeholder, position in zip(nums, placeholders, positions): + num.move_to(placeholder, position) + decimal = DecimalNumber(float(self.n_in)/(self.n_in + self.n_out)) + decimal.next_to(frac, RIGHT, SMALL_BUFF) + + self.add(decimal, *nums) + self.dither(dither_time) + self.remove(decimal, *nums) + return VGroup(decimal, *nums) + + + self.play(Write(title)) + self.add(frac) + self.remove(*placeholders) + place_random_triangles(10, 0.25) + nums = place_random_triangles(self.n_random_trials, 0.05) + self.add(nums) + self.dither() + self.play(*map(FadeOut, [frac, nums, title])) + + def add_lines_and_comment_on_them(self): + center_lines = self.get_center_lines() + center_lines.save_state() + center_line_shadows = center_lines.copy() + center_line_shadows.set_stroke(LIGHT_GREY, 2) + arcs = self.get_all_arcs() + + center_lines.generate_target() + center_lines.target.to_edge(RIGHT, buff = LARGE_BUFF) + rect = SurroundingRectangle(center_lines.target, buff = MED_SMALL_BUFF) + rect.set_stroke(WHITE, 2) + + words1 = TextMobject("Helpful new objects") + words2 = TextMobject("Reframe problem around these") + for words in words1, words2: + words.scale(0.8) + words.next_to(rect, UP) + words.shift_onto_screen() + + self.play(LaggedStart(ShowCreation, center_lines, run_time = 1)) + self.play( + LaggedStart(FadeIn, arcs, run_time = 1), + Animation(self.point_mobs), + ) + self.dither() + self.add(center_line_shadows) + self.play(MoveToTarget(center_lines)) + self.play(ShowCreation(rect), Write(words1)) + self.dither(2) + self.play(ReplacementTransform(words1, words2)) + self.dither(2) + self.play( + center_lines.restore, + center_lines.fade, 1, + *map(FadeOut, [ + rect, words2, center_line_shadows, + self.triangle, arcs, + self.point_mobs, + self.point_labels, + ]) + ) + center_lines.restore() + self.remove(center_lines) + + def rewrite_random_procedure(self): + point_mobs = self.point_mobs + center_lines = self.center_lines + + random_procedure = TextMobject("Random procedure") + underline = Line(LEFT, RIGHT) + underline.stretch_to_fit_width(random_procedure.get_width()) + underline.scale(1.1) + underline.next_to(random_procedure, DOWN) + group = VGroup(random_procedure, underline) + group.to_corner(UP+RIGHT) + + words = VGroup(*map(TextMobject, [ + "Choose 3 random points", + "Choose 2 random lines", + "Flip coin for each line \\\\ to get $P_1$ and $P_2$", + "Choose $P_3$ at random" + ])) + words.scale(0.8) + words.arrange_submobjects(DOWN, buff = MED_LARGE_BUFF) + words.next_to(underline, DOWN) + words[1].highlight(YELLOW) + + point_label_groups = VGroup() + for point_mob, label in zip(self.point_mobs, self.point_labels): + group = VGroup(point_mob, label) + group.save_state() + group.move_to(words[0], LEFT) + group.fade(1) + point_label_groups.add(group) + self.point_label_groups = point_label_groups + + cross = Cross(words[0]) + cross.set_stroke(RED, 6) + + self.center_lines_update = self.get_center_lines_update( + point_mobs, center_lines + ) + self.update_animations.append(self.center_lines_update) + self.update_animations.remove(self.triangle_update) + + #Choose random points + self.play( + Write(random_procedure), + ShowCreation(underline) + ) + self.play(FadeIn(words[0])) + self.play(LaggedStart( + ApplyMethod, point_label_groups, + lambda mob : (mob.restore,), + )) + self.play( + ShowCreation(cross), + point_label_groups.fade, 1, + ) + self.dither() + + #Choose two random lines + self.center_lines_update.update(1) + self.play( + FadeIn(words[1]), + LaggedStart(GrowFromCenter, center_lines) + ) + for x in range(3): + self.change_point_mobs_randomly(run_time = 1) + self.change_point_mobs_to_angles([0.8*np.pi, 1.3*np.pi]) + + #Flip a coin for each line + def flip_point_label_back_and_forth(point_mob, label): + for x in range(6): + point_mob.rotate(np.pi, about_point = self.center) + self.point_labels_update.update(1) + self.dither(0.5) + self.dither(0.5) + + def choose_p1_and_p2(): + for group in point_label_groups[:2]: + group.set_fill(self.point_color, 1) + flip_point_label_back_and_forth(*group) + + choose_p1_and_p2() + self.play(Write(words[2])) + + #Seems convoluted + randy = Randolph().flip() + randy.scale(0.5) + randy.to_edge(DOWN) + randy.shift(2*RIGHT) + + self.play(point_label_groups.fade, 1) + self.change_point_mobs_randomly(run_time = 1) + choose_p1_and_p2() + point_label_groups.fade(1) + self.change_point_mobs_randomly(FadeIn(randy)) + self.play( + PiCreatureSays( + randy, "Seems \\\\ convoluted", + bubble_kwargs = {"height" : 2, "width" : 2}, + target_mode = "confused" + ) + ) + choose_p1_and_p2() + self.play( + FadeOut(randy.bubble), + FadeOut(randy.bubble.content), + randy.change, "pondering", + ) + self.play(Blink(randy)) + self.play(FadeOut(randy)) + + #Choosing the third point + self.change_point_mobs([0, 0, -np.pi/2], run_time = 0) + p3_group = point_label_groups[2] + p3_group.save_state() + p3_group.move_to(words[3], LEFT) + + self.play(Write(words[3], run_time = 1)) + self.play( + p3_group.restore, + p3_group.set_fill, YELLOW, 1 + ) + self.dither() + self.play(Swap(*words[2:4])) + self.dither() + + #Once the continuous randomness is handled + rect = SurroundingRectangle(VGroup(words[1], words[3])) + rect.set_stroke(WHITE, 2) + brace = Brace(words[2], DOWN) + brace_text = brace.get_text("4 equally likely outcomes") + brace_text.scale_in_place(0.8) + + self.play(ShowCreation(rect)) + self.play(GrowFromCenter(brace)) + self.play(Write(brace_text)) + self.dither() + + self.random_procedure_words = words + + def four_possibilities_for_coin_flips(self): + arcs = self.all_arcs + point_mobs = self.point_mobs + arc = arcs[-1] + point_label_groups = self.point_label_groups + arc_update = self.get_arcs_update(arcs) + arc_update.update(1) + self.update_animations.append(arc_update) + + def second_arc_update_func(arcs): + VGroup(*arcs[:-1]).set_stroke(width = 0) + arcs[-1].set_stroke(BLUE, 5) + return arcs + second_arc_update = UpdateFromFunc(arcs, second_arc_update_func) + second_arc_update.update(1) + self.update_animations.append(second_arc_update) + self.update_animations.append(Animation(point_label_groups)) + + def do_the_rounds(): + for index in 0, 1, 0, 1: + point_mob = point_mobs[index] + point_mob.generate_target() + point_mob.target.rotate( + np.pi, about_point = self.center, + ) + self.play( + MoveToTarget(point_mob), + *self.update_animations, + run_time = 0.5 + ) + self.dither() + + do_the_rounds() + self.triangle_update.update(1) + self.remove(arcs) + self.update_animations.remove(arc_update) + self.update_animations.remove(second_arc_update) + self.play(FadeIn(self.triangle)) + self.dither() + self.update_animations.insert(0, self.triangle_update) + do_the_rounds() + self.dither() + self.change_point_mobs_randomly() + for x in range(2): + do_the_rounds() + + + +class ThisIsWhereItGetsGood(TeacherStudentsScene): + def construct(self): + self.teacher_says( + "This is where \\\\ things get good", + target_mode = "hooray" + ) + self.change_student_modes(*["hooray"]*3) + self.dither(2) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +