Ejemplo n.º 1
0
    def construct(self):
        f0 = TexMobject("F_0 = 0")
        f1 = TexMobject("F_1 = 1")
        fn = TexMobject("F_n = F_{n-1} + F_{n-2}")
        fg = VGroup(f0, f1, fn)
        fg.arrange(RIGHT, buff=LARGE_BUFF).shift(UP)
        self.add(fg)

        fib_small_code = CodeBlock(
            'Java',
            r"""
            public static int fib(int n) {
                if (n == 0) {
                    return 0;
                }
                if (n == 1) {
                    return 1;
                }
                return fib(n - 1) + fib(n - 2);
            }
            """,
        )
        fib_code = CodeBlock(
            'Java',
            r"""
            public static int fib(int n) {
                if (n == 0) {
                    return 0;
                }
                if (n == 1) {
                    return 1;
                }
                int fn1 = fib(n - 1);
                int fn2 = fib(n - 2);
                return fn1 + fn2;
            }
            """,
        )

        fib_small_code.to_edge(RIGHT)
        f0.generate_target()
        f1.generate_target()
        fn.generate_target()
        fsc = fib_small_code.get_code()
        eq_code_buff = LARGE_BUFF * 1.5
        f0.target.next_to(fsc.get_lines((2, 5)), LEFT, buff=eq_code_buff)
        f1.target.next_to(fsc.get_lines((5, 8)), LEFT, buff=eq_code_buff)
        fn.target.next_to(fsc.get_lines(8), LEFT, buff=eq_code_buff)
        self.play(*[MoveToTarget(o) for o in [f0, f1, fn]], )
        self.play(FadeInFrom(fib_small_code, RIGHT), )
        self.wait(duration=3)

        fib_small_code.generate_target()
        fib_small_code.target.move_to(fib_code, aligned_edge=UL)
        self.play(FadeOut(VGroup(f0, f1, fn)), MoveToTarget(fib_small_code))

        adj_desc = TextMobject(
            "Let's make this a little easier to trace through...")
        adj_desc.to_edge(DOWN)
        self.play(FadeIn(adj_desc))
        self.wait()

        fc = fib_code.get_code()
        self.play(
            fsc[-2].next_to,
            fc[-2],
            {
                'direction': ORIGIN,
                'index_of_submobject_to_align': 0,
            },
            fsc[-1].move_to,
            fc[-1],
            {'aligned_edge': LEFT},
        )

        # 012 345 6 78901 2 345
        # int fn1 = fib(n - 1);
        self.play(FadeIn(fc[-4][:7]), )

        # 012345 67890 1 23 4 56789 0 123
        # return fib(n - 1) + fib(n - 2);
        self.play(
            fsc[-2][6:14].move_to,
            fc[-4][7:15],
        )

        # 012345 678 9 0123
        # return fn1 + fn2;
        self.play(
            FadeIn(fc[-4][-1]),
            FadeIn(fc[-2][6:9]),
            fsc[-2][14].move_to,
            fc[-2][9],
        )

        self.play(FadeIn(fc[-3][:7]), )
        self.play(
            fsc[-2][15:23].move_to,
            fc[-3][7:15],
        )
        self.play(
            FadeIn(fc[-3][-1]),
            FadeIn(fc[-2][10:13]),
            fsc[-2][-1].move_to,
            fc[-2][-1],
        )
        self.clear()
        self.add(fib_code, adj_desc)
        self.wait()

        t1 = TextMobject("Okay, let's run it")
        t1.move_to(adj_desc)
        self.play(ReplacementTransform(adj_desc, t1))
        self.wait(duration=2)

        self.play(FadeOut(t1))
Ejemplo n.º 2
0
    def construct(self):
        # Look at power1() and highlight parts of it. Anatomy of a recursive function.
        # Always has two parts: base case and recursive step.
        code_scale = 0.75
        power_code = CodeBlock(
            'Java',
            r"""
            public static int power(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power(x, n - 1);
                return x * t;
            }
            """,
            code_scale=code_scale,
        )
        power_code.to_edge(UP)
        self.add(power_code)
        self.wait()

        title = TextMobject('Anatomy of a Recursive Function').to_edge(UP)
        power_code.generate_target()
        power_code.target.next_to(title, DOWN, buff=MED_LARGE_BUFF)
        self.play(
            FadeInFrom(title, UP),
            MoveToTarget(power_code),
        )
        self.wait()

        pc_groups = [
            VGroup(power_code.get_code().get_lines(p))
            for p in [(1, 2), (2, 5), (5, 7), (7, 8)]
        ]
        pc_t = [g.generate_target() for g in pc_groups]

        # Base Case
        #   - All of these have a base case that ends the recursion and returns.
        #   - Computed directly from the inputs.
        #   - Base cases are often some variant of emptiness. Power1() is a good example, with n == 0.
        #     What if you returned x for n == 1?
        base_shifts = [UP * 6, DOWN, DOWN * 6, DOWN * 6]
        power_code.save_state()
        for t, s in zip(pc_t, base_shifts):
            t.shift(s)

        t1 = TextMobject('Base Case').next_to(title, DOWN, buff=0.75)
        self.play(
            *[MoveToTarget(o) for o in pc_groups],
            FadeIn(t1),
        )
        self.wait()

        points_dwell_time = 2.5
        t2 = TextMobject(
            "- stops the recursion and returns\\\\",
            "- value is computed directly from the inputs, or constant\\\\",
            "- often it's some form of ``emptiness'' or ``singleness''\\\\",
            alignment="").next_to(title, DOWN, buff=LARGE_BUFF * 3.5)
        base_highlights = [
            SurroundingRectangle(pc_groups[1][0][1][0:6]),
            SurroundingRectangle(pc_groups[1][0][1][6:7]),
            SurroundingRectangle(pc_groups[1][0][0][3:7]),
        ]

        for t, h, ph in zip(t2, base_highlights, [None] + base_highlights):
            opt_anim = [FadeOut(ph)] if ph else []
            self.play(
                *opt_anim,
                FadeInFromDown(t),
                FadeIn(h),
            )
            self.wait(duration=points_dwell_time)
        self.play(FadeOut(base_highlights[-1]))
        self.wait()

        self.play(
            FadeOut(t1),
            FadeOut(t2),
            power_code.restore,
        )
        self.wait()

        # Recursive Step
        # The recursive step is some variant of decompose/reduce, call, recombine/compute.
        #   - Recursive case: compute the result with the help of one or more recursive calls to the same function.
        #     In this case, the data is reduced in some way in either size, complexity, or both.
        #   - In this case, reducing n by 1 each time.
        pc_t = [g.generate_target() for g in pc_groups]
        recurse_shifts = [UP * 6, UP * 6, UP * 0.45, DOWN * 6]
        power_code.save_state()
        for t, s in zip(pc_t, recurse_shifts):
            if s is not None:
                t.shift(s)

        t1 = TextMobject('Recursive Step').next_to(title, DOWN, buff=0.75)
        self.play(
            *[MoveToTarget(o) for o in pc_groups],
            FadeIn(t1),
        )
        self.wait()

        t2 = TextMobject(
            "- reduce: make the problem smaller, or break it into parts\\\\",
            "- call: one or more recursive calls\\\\",
            "- recombine: compute the result from the pieces\\\\",
            alignment="").next_to(title, DOWN, buff=LARGE_BUFF * 3.5)

        # 012345678901234567
        # intt=power(x,n-1);
        # returnx*t;
        recurse_highlights = [
            SurroundingRectangle(pc_groups[2][0][0][13:16]),
            SurroundingRectangle(pc_groups[2][0][0][5:17]),
            SurroundingRectangle(pc_groups[2][0][1][6:9]),
        ]

        for t, h, ph in zip(t2, recurse_highlights,
                            [None, *recurse_highlights]):
            opt_anim = [FadeOut(ph)] if ph else []
            self.play(
                *opt_anim,
                FadeInFromDown(t),
                FadeIn(h),
            )
            self.wait(duration=points_dwell_time)
        self.play(FadeOut(recurse_highlights[-1]))
        self.wait()

        self.play(
            FadeOut(t1),
            FadeOut(t2),
            power_code.restore,
        )
        self.wait(0.5)

        power_code.generate_target()
        power_code.target.to_edge(RIGHT)
        bcb = BraceLabel(power_code.target.get_code().get_lines((2, 5)),
                         '\\textit{Base Case}',
                         brace_direction=LEFT,
                         label_constructor=TextMobject,
                         buff=LARGE_BUFF)
        rsb = BraceLabel(power_code.target.get_code().get_lines((5, 7)),
                         '\\textit{Recursive Step}',
                         brace_direction=LEFT,
                         label_constructor=TextMobject,
                         buff=LARGE_BUFF)
        self.play(MoveToTarget(power_code), )
        self.play(
            ShowCreation(bcb),
            ShowCreation(rsb),
        )
        self.wait(0.5)

        t1 = TextMobject('Base Case', ':', ' stops the recursion')
        t2 = TextMobject('Recursive Step', ':', ' reduce - call - recombine')
        t2.next_to(t1.get_part_by_tex(':'),
                   DOWN,
                   submobject_to_align=t2.get_part_by_tex(':'),
                   buff=MED_LARGE_BUFF)
        g = VGroup(t1, t2).shift(DOWN * 1.5)
        self.play(FadeIn(g))
        self.wait(duration=points_dwell_time)

        t3 = TextMobject(
            "In Part 2 we'll look at making this function more efficient!"
        ).scale(0.75)
        t3.to_edge(DOWN).set_color(BLUE)
        self.play(Write(t3))

        # self.play(*[FadeOut(o) for o in self.mobjects])
        self.wait(duration=3)

        end_scale_group = VGroup(*self.mobjects)
        end_fade_group = VGroup(t3, title)
        self.animate_yt_end_screen(end_scale_group,
                                   end_fade_group,
                                   show_elements=False)
Ejemplo n.º 3
0
    def construct(self):
        # - Now, let's change this a bit and break it!
        # - Now modify it (a <= 0) and call it with foo(0).
        # - switch it out for the broken one, and highlight and note the change.
        code_scale = 0.75
        ss_good_code = CodeBlock(
            'Java',
            r"""
            public static double StrangeSqrt(double a) { 
                if (a < 0) { 
                    double b = StrangeSqrt(a * -1);
                    return b;
                } 
                return Math.sqrt(a);
            }
            """,
            code_scale=code_scale,
        )
        ss_good_code.to_edge(UP)
        self.add(ss_good_code)

        t1 = TextMobject(
            "Let's break this code in a very small way and\\\\see what happens..."
        )
        t1.next_to(ss_good_code, DOWN, buff=LARGE_BUFF)
        self.play(FadeIn(t1))
        self.wait()

        gl2 = ss_good_code.get_code().get_lines(2)
        ghr = SurroundingRectangle(gl2[3:6])
        self.play(ShowCreation(ghr))
        self.wait()

        ss_bad_code = CodeBlock(
            'Java',
            r"""
            public static double StrangeSqrt(double a) { 
                if (a <= 0) { 
                    double b = StrangeSqrt(a * -1);
                    return b;
                } 
                return Math.sqrt(a);
            }
            """,
            code_scale=code_scale,
        )
        ss_bad_code.to_edge(UP)
        bl2 = ss_bad_code.get_code().get_lines(2)
        bhr = SurroundingRectangle(bl2[3:7])
        self.play(
            ReplacementTransform(ss_good_code, ss_bad_code),
            ReplacementTransform(ghr, bhr),
        )
        self.wait()

        t2 = TextMobject(
            "This small change will have a big effect on one case: 0\\\\"
            "Let's run it and see.").next_to(ss_bad_code,
                                             DOWN,
                                             buff=LARGE_BUFF)
        self.play(
            FadeOut(t1),
            FadeIn(t2),
        )
        self.wait(duration=3)

        self.play(
            FadeOut(t2),
            FadeOut(bhr),
        )
        self.wait()

        # - Start stepping through this and see the stack start to grow forever.
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                double n = StrangeSqrt(0);
            }
            """,
            line_offset=7,
            code_scale=code_scale - 0.1,
        )
        frame_width = 3.5
        main_frame = StackFrame(main_code,
                                'main()',
                                9, ['n'],
                                width=frame_width)
        main_code.highlight_lines(9)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)
        self.play(
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        ss_frame = StackFrame(ss_bad_code,
                              'StrangeSqrt(0)',
                              1, ['a', 'b'],
                              width=frame_width)
        ss_frame.next_to(main_frame, UP, buff=SMALL_BUFF)

        xi = main_code.pre_call(ss_bad_code, 1)
        self.play(
            *main_code.get_control_transfer_counterclockwise(xi),
            FadeInFrom(ss_frame, UP),
        )
        ss_bad_code.post_control_transfer(xi, self)
        self.wait()

        self.play(ss_bad_code.highlight_lines, 2, ss_frame.set_line, 2,
                  ss_frame.update_slot, 'a', 0)
        self.wait()

        self.play(ss_bad_code.highlight_lines, 3, ss_frame.set_line, 3)
        self.wait()

        def call_ss_again(stack_group, previous_frame, extras=None):
            wait_each_step = len(stack_group) < 3
            runtime = 1.0 if len(stack_group) < 2 else 0.5 if len(
                stack_group) < 6 else 0.20
            extra_anims = []
            if len(stack_group) > 2:
                extra_anims.append(
                    ApplyMethod(
                        stack_group.shift,
                        DOWN * previous_frame.get_height() +
                        DOWN * SMALL_BUFF))
            if extras:
                extra_anims.extend(extras)

            f = StackFrame(ss_bad_code,
                           'StrangeSqrt(0)',
                           1, ['a', 'b'],
                           width=frame_width)
            f.next_to(previous_frame, UP, buff=SMALL_BUFF)
            self.play(*extra_anims,
                      ss_bad_code.highlight_lines,
                      1,
                      f.set_line,
                      1,
                      FadeIn(f),
                      MaintainPositionRelativeTo(f, previous_frame),
                      run_time=runtime)
            if wait_each_step:
                self.wait()

            self.play(ss_bad_code.highlight_lines,
                      2,
                      f.set_line,
                      2,
                      f.update_slot,
                      'a',
                      0,
                      run_time=runtime)
            if wait_each_step:
                self.wait()

            self.play(ss_bad_code.highlight_lines,
                      3,
                      f.set_line,
                      3,
                      run_time=runtime)
            if wait_each_step:
                self.wait()
            stack_group.add(f)
            return f

        sg = VGroup(main_frame, ss_frame)
        curr_ss_frame = ss_frame
        for i in range(3):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame)

        # - When will this program end? Never?
        t1 = TextMobject("Hrm... when is this going to end??").next_to(
            main_code, UP, buff=LARGE_BUFF)
        extra_text = [FadeIn(t1)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        t2 = TextMobject("Never?").next_to(main_code, UP, buff=LARGE_BUFF)
        extra_text = [FadeOut(t1), FadeIn(t2)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        sg.save_state()
        t3 = TextMobject("The stack is getting quite deep!").next_to(
            main_code, UP, buff=LARGE_BUFF)
        self.play(sg.scale, 0.20, {'about_edge': TOP}, FadeOut(t2), FadeIn(t3))
        self.wait(duration=2)
        self.play(sg.restore)

        extra_text = [FadeOut(t3)]
        for i in range(4):
            curr_ss_frame = call_ss_again(sg, curr_ss_frame, extra_text)
            extra_text = None

        # - Well, the stack is finite, so eventually you get a SO.
        #   Use the Java SO exception and show it hit and stop.
        so_frame = StackFrame(ss_bad_code,
                              'StrangeSqrt(0)',
                              1, ['a', 'b'],
                              width=frame_width)
        so_frame.slots().set_color(ORANGE)
        so_frame.header_line().set_color(ORANGE)
        so_frame.background_rect().set_fill(color=[BLACK, RED])
        so_frame.next_to(curr_ss_frame, UP, buff=SMALL_BUFF)
        so_exception = TextMobject(
            '\\texttt{Exception in thread "main"\\\\java.lang.StackOverflowError}',
        ).set_color(YELLOW).scale(0.75).next_to(main_code, UP, buff=LARGE_BUFF)
        self.play(sg.shift,
                  DOWN * so_frame.get_height() + DOWN * SMALL_BUFF,
                  ss_bad_code.get_current_highlight().set_color, ORANGE,
                  FadeIn(so_frame),
                  MaintainPositionRelativeTo(so_frame, curr_ss_frame),
                  Write(so_exception))
        sg.add(so_frame)
        self.wait(duration=2)

        ss_bad_code.generate_target()
        so_exception.generate_target()
        ss_bad_code.target.scale(0.75)
        g = VGroup(so_exception.target,
                   ss_bad_code.target).arrange(RIGHT, buff=LARGE_BUFF)
        g.to_edge(UP)
        sg.generate_target()
        sg.target.scale(0.15).next_to(g, DOWN).to_edge(RIGHT)
        t1 = TextMobject(
            "Space for the call stack is limited,\\\\so we can't make recursive calls forever!"
        )
        self.play(
            *[MoveToTarget(t) for t in [so_exception, ss_bad_code, sg]],
            FadeOutAndShiftDown(main_code),
            FadeInFromDown(t1),
        )
        self.wait(duration=2)

        t2 = TextMobject(
            "This program made {\\raise.17ex\\hbox{$\\scriptstyle\\sim$}}15,000 calls before crashing."
        )
        t4 = TextMobject(
            "\\textit{Java SE 13.0.1 on macOS Catalina 10.15.4}").scale(0.5)
        t2.next_to(t1, DOWN, buff=MED_LARGE_BUFF)
        t4.to_edge(DL)
        self.play(
            FadeIn(t2),
            FadeInFromDown(t4),
        )
        self.wait(duration=4)

        t5 = TextMobject('\\textit{Most common mistake with recursion.}')
        t6 = TextMobject("You'll see this a lot. Everyone does!")
        t6.next_to(t5, DOWN, buff=LARGE_BUFF)
        self.play(
            *[FadeOut(t) for t in [t1, t2, t4]],
            *[FadeIn(t) for t in [t5, t6]],
        )
        self.wait(duration=3)

        # Transition
        t1 = TextMobject(
            "Alright, let's look at a more interesting function...")
        self.play(
            *[FadeOut(o) for o in self.mobjects],
            FadeIn(t1),
        )
        self.wait()

        self.play(FadeOut(t1))
        self.wait()
Ejemplo n.º 4
0
    def construct(self):
        # Even case
        ef0 = TexMobject('x^n=', 'x', '\\times', 'x \\times ... \\times x')
        eb0 = BraceLabel(ef0[1:],
                         '$n$ \\textit{times}',
                         brace_direction=UP,
                         label_constructor=TextMobject)
        ef0g = VGroup(ef0, eb0)

        ef1 = TexMobject('x^n=', 'x \\times ... \\times x', '\\times',
                         'x \\times ... \\times x')
        ef1.next_to(ef0, ORIGIN, index_of_submobject_to_align=0)
        eb1n = BraceLabel(ef1[1:],
                          '$n$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        eb1l = BraceLabel(ef1[1:2],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        eb1r = BraceLabel(ef1[3:4],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        ef2 = TexMobject('x^n=', 'x \\times ... \\times x', '\\times',
                         'x^{\\frac{n}{2}}')
        ef2.next_to(ef0, ORIGIN, index_of_submobject_to_align=0)
        eb2r = BraceLabel(ef2[3:4],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        ef3 = TexMobject('x^n=', 'x^{\\frac{n}{2}}', '\\times',
                         'x^{\\frac{n}{2}}')
        ef3.next_to(ef0, ORIGIN, index_of_submobject_to_align=0)
        eb3r = BraceLabel(ef3[3:4],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        if_even = TextMobject('\\textit{if $n$ is even:}').next_to(ef0, LEFT)
        self.play(ShowCreation(ef0g))
        self.wait()

        self.play(ReplacementTransform(ef0, ef1),
                  ReplacementTransform(eb0, eb1n))
        self.wait()

        self.play(ReplacementTransform(eb1n, eb1l), ShowCreation(eb1r),
                  FadeIn(if_even))
        self.wait(duration=2)

        self.play(ReplacementTransform(ef1, ef2),
                  ReplacementTransform(eb1r, eb2r))
        self.wait()

        self.play(ReplacementTransform(ef2, ef3), FadeOut(eb1l),
                  ReplacementTransform(eb2r, eb3r))
        self.wait()

        even_group = VGroup(if_even, ef3)
        self.play(FadeOutAndShift(eb3r, UL), even_group.to_edge, UL)
        self.wait()

        # Prime1 eq
        p1f = TexMobject('x^n=', 'x', '\\times', 'x^{n-1}')
        p1t = TextMobject('From our first version, we know')
        p1t.next_to(p1f, UP)
        self.play(FadeIn(p1t), ShowCreation(p1f))
        self.wait()

        # Odd case
        ot1 = TextMobject('So if $n$ is odd, then $n-1$ is even... hmm...')
        ot1.next_to(p1f, UP)
        self.play(ReplacementTransform(p1t, ot1))
        self.wait()

        of1 = TexMobject('x^n=', 'x', '\\times',
                         'x^{\\frac{n-1}{2}} \\times x^{\\frac{n-1}{2}}')
        of1.next_to(p1f, ORIGIN, index_of_submobject_to_align=0)

        self.play(ReplacementTransform(VGroup(ef3[1:]).copy(), of1[3]),
                  FadeOut(p1f[3]))
        self.remove(*p1f[:3])
        self.add(of1)
        self.wait()

        if_odd = TextMobject('\\textit{if $n$ is odd:}').next_to(of1[0], LEFT)
        self.play(FadeOut(ot1), FadeIn(if_odd))
        self.wait()

        # Both
        originals = [if_even, ef3, if_odd, of1]
        for o in originals:
            o.generate_target()
        ef3.target.next_to(of1, UP, aligned_edge=LEFT)
        if_even.target.next_to(ef3.target[0], LEFT)
        VGroup(*[o.target for o in originals]).center().to_edge(TOP)

        self.play(*[MoveToTarget(o) for o in originals])
        self.wait()
        eqg = VGroup(*originals)

        # Simplify with int division
        t1 = TextMobject(
            'Fun Fact: if $n$ is odd, and we use \\texttt{int} as the datatype, then'
        ).next_to(eqg, DOWN, buff=LARGE_BUFF)
        div_code = CodeTextString('Java',
                                  'n / 2 == (n - 1) / 2').next_to(t1, DOWN)

        self.play(FadeInFromDown(t1), FadeInFromDown(div_code))
        self.wait()

        of2 = TexMobject('x^n=', 'x', '\\times',
                         'x^{\\frac{n}{2}} \\times x^{\\frac{n}{2}}')
        of2.next_to(of1, ORIGIN, index_of_submobject_to_align=0)

        self.play(ReplacementTransform(div_code, of2[3]), FadeOut(of1[3]),
                  FadeOut(t1))
        self.remove(*of1[:3])
        self.add(of2)
        self.wait()

        originals = [if_even, ef3, if_odd, of2]
        for o in originals:
            o.generate_target()
        for p in [ef3.target[1:], of2.target[3]]:
            p.set_color(ORANGE)
        eqtg = VGroup(*[o.target for o in originals]).center().to_edge(TOP)

        t1 = TextMobject(
            'Using \\texttt{int} for $n$ makes the equations very similar,\\\\'
            'and the coding very simple!')
        t1.next_to(eqtg, DOWN, buff=MED_LARGE_BUFF)
        self.play(*[MoveToTarget(o) for o in originals], FadeIn(t1))
        self.wait()
        eqg = VGroup(*originals)

        # Transform to code
        code_scale = 0.7
        power2_code = CodeBlock(
            'Java',
            r"""
            public static int power2(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power2(x, n / 2);
                if (n % 2 == 0) {
                    return t * t;
                }
                return t * t * x;
            }	
            """,
            code_scale=code_scale,
        )
        power2_code.to_edge(RIGHT)

        ef3.generate_target()
        of2.generate_target()
        ef3.target.next_to(power2_code.get_code().get_lines(7),
                           LEFT,
                           buff=LARGE_BUFF)
        of2.target.next_to(power2_code.get_code().get_lines(9),
                           LEFT,
                           buff=LARGE_BUFF)
        ef3.target.next_to(of2.target,
                           UP,
                           aligned_edge=RIGHT,
                           coor_mask=X_AXIS)

        et = TextMobject('\\textit{even:}')
        ot = TextMobject('\\textit{odd:}')
        ot.next_to(of2.target[0], LEFT)
        et.next_to(ef3.target[0], LEFT)

        self.play(FadeOut(t1))
        self.play(
            FadeOut(if_even),
            FadeOut(if_odd),
            FadeInFrom(power2_code, RIGHT),
            MoveToTarget(ef3),
            MoveToTarget(of2),
            ReplacementTransform(if_even, et),
            ReplacementTransform(if_odd, ot),
        )
        self.wait()

        recursive_call_hr = SurroundingRectangle(
            power2_code.get_code().get_lines(5)[5:-1])
        xn2_hr = SurroundingRectangle(ef3[-1])

        ec_hr = SurroundingRectangle(
            power2_code.get_code().get_lines(7)[-4:-1])
        ef_hr = SurroundingRectangle(ef3[1:])

        oc_hr = SurroundingRectangle(
            power2_code.get_code().get_lines(9)[-6:-1])
        of_hr = SurroundingRectangle(of2[1:])

        for f, c in [(xn2_hr, recursive_call_hr), (ef_hr, ec_hr),
                     (of_hr, oc_hr)]:
            self.play(ShowCreation(f), ShowCreation(c))
            self.wait()
            self.play(Uncreate(f), Uncreate(c))
        self.wait()
Ejemplo n.º 5
0
    def construct(self):
        t1 = TextMobject("Let's go back to our original equation").shift(UP)
        self.play(FadeInFromDown(t1))
        self.wait()

        # Even case
        ef1 = TexMobject('x^n=', 'x \\times ... \\times x', '\\times',
                         'x \\times ... \\times x')
        eb1n = BraceLabel(ef1[1:],
                          '$n$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        eb1l = BraceLabel(ef1[1:2],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        eb1r = BraceLabel(ef1[3:4],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        ef3 = TexMobject('x^n=', 'x^{\\frac{n}{2}}', '\\times', 'x',
                         '^{\\frac{n}{2}}')
        ef3.next_to(ef1, ORIGIN, index_of_submobject_to_align=0)
        eb3l = BraceLabel(ef3[1:2],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        if_even = TextMobject('\\textit{if $n$ is even:}').next_to(
            ef1, LEFT).set_color(YELLOW)
        self.play(ShowCreation(ef1), ShowCreation(eb1n), t1.shift, UP)
        self.wait(duration=2)

        self.play(ReplacementTransform(eb1n, eb1l), ShowCreation(eb1r),
                  FadeIn(if_even), FadeOut(t1))
        self.wait(duration=2)

        self.play(ReplacementTransform(ef1[1], ef3[1]),
                  ReplacementTransform(eb1l, eb3l))
        self.wait()

        tmp_ef3_right = VGroup(ef3[2].copy(), ef3[3].copy(), ef3[4].copy())
        tmp_ef3_right.next_to(ef1[2],
                              ORIGIN,
                              submobject_to_align=tmp_ef3_right[0])
        eb3r = BraceLabel(tmp_ef3_right[1:],
                          '$\\frac{n}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        self.play(ReplacementTransform(ef1[3:], tmp_ef3_right[1:]),
                  ReplacementTransform(eb1r, eb3r))
        self.wait()

        self.play(
            ReplacementTransform(ef1[2], ef3[2]),
            ReplacementTransform(tmp_ef3_right[1:], ef3[3:]),
            FadeOutAndShift(eb3r, LEFT),
            FadeOut(eb3l),
        )
        self.remove(ef1[0])
        self.add(ef3)
        self.wait()

        even_group = VGroup(if_even, ef3)
        self.play(even_group.to_edge, UL)
        self.wait()

        # Odd case
        ot1 = TextMobject(
            "Now, let's figure out what to do if $n$ is odd").shift(
                UP * 2).set_color(BLUE)
        self.play(FadeInFromDown(ot1))

        of1 = TexMobject('x^n=', 'x \\times ... \\times x', '\\times',
                         'x \\times ... \\times x', '\\times x')
        ob1n = BraceLabel(of1[1:],
                          '$n$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        ob1nm1 = BraceLabel(of1[1:-1],
                            '$n-1$ \\textit{times}',
                            brace_direction=UP,
                            label_constructor=TextMobject)
        ob1l = BraceLabel(of1[1:2],
                          '$\\frac{n-1}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)
        ob1r = BraceLabel(of1[3:4],
                          '$\\frac{n-1}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        of3 = TexMobject('x^n=', 'x', '^{\\frac{n-1}{2}}', '\\times', 'x',
                         '^{\\frac{n-1}{2}}', '\\times x')
        of3.next_to(of1, ORIGIN, index_of_submobject_to_align=0)
        ob3l = BraceLabel(of3[1:3],
                          '$\\frac{n-1}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        if_odd = TextMobject('\\textit{if $n$ is odd:}').next_to(
            of1, LEFT).set_color(YELLOW)
        self.play(ShowCreation(of1), ShowCreation(ob1n))
        self.wait(duration=2)

        self.play(ReplacementTransform(ob1n, ob1nm1), FadeIn(if_odd),
                  FadeOut(ot1))
        self.wait(duration=2)

        self.play(ReplacementTransform(ob1nm1, ob1l), ShowCreation(ob1r))
        self.wait(duration=2)

        self.play(ReplacementTransform(of1[1], of3[1:3]),
                  ReplacementTransform(ob1l, ob3l))
        self.wait()

        tmp_of3_right = VGroup(of3[3].copy(), of3[4].copy(), of3[5].copy())
        tmp_of3_right.next_to(of1[2],
                              ORIGIN,
                              submobject_to_align=tmp_of3_right[0])
        ob3r = BraceLabel(tmp_of3_right[1:],
                          '$\\frac{n-1}{2}$ \\textit{times}',
                          brace_direction=UP,
                          label_constructor=TextMobject)

        self.play(ReplacementTransform(of1[3], tmp_of3_right[1:]),
                  ReplacementTransform(ob1r, ob3r))
        self.wait()

        self.play(
            ReplacementTransform(of1[2], of3[3]),
            ReplacementTransform(tmp_of3_right[1:], of3[4:6]),
            ReplacementTransform(of1[4], of3[-1]),
            FadeOutAndShift(ob3r, LEFT),
            FadeOut(ob3l),
        )
        self.remove(of1[0])
        self.add(of3)
        self.wait()

        # Both
        originals = [if_even, ef3, if_odd, of3]
        for o in originals:
            o.generate_target()
        ef3.target.next_to(of3, UP, aligned_edge=LEFT)
        if_even.target.next_to(ef3.target[0], LEFT)
        VGroup(*[o.target for o in originals]).center().to_edge(TOP)

        self.play(*[MoveToTarget(o) for o in originals])
        self.wait()
        eqg = VGroup(*originals)

        # Simplify with int division
        t1 = TextMobject(
            'Fun Fact: if $n$ is odd, and we use \\texttt{int} as the datatype, then',
            tex_to_color_map={
                'int': RED
            }).next_to(eqg, DOWN, buff=LARGE_BUFF)
        div_code = CodeTextString('Java', '(n-1)/2 == n/2').next_to(t1, DOWN)

        self.play(FadeInFromDown(t1), FadeInFromDown(div_code))
        self.wait(duration=3)

        t2 = TextMobject('Integer division \\textit{truncates}: '
                         '\\texttt{5/2 == 2}'
                         ', \\textit{not} '
                         '\\texttt{2.5}')
        t2.next_to(div_code, DOWN, buff=LARGE_BUFF)
        self.play(FadeInFromDown(t2))
        self.wait(duration=3)

        # Indicate movement before each exp transformation from code
        of4 = TexMobject('x^n=', 'x', '^{\\frac{n}{2}}', '\\times', 'x',
                         '^{\\frac{n}{2}}', '\\times x')
        of4.next_to(of3, ORIGIN, index_of_submobject_to_align=0)

        def highlight_and_switch_exp(target_exp, old_exp):
            hrc = SurroundingRectangle(div_code[0][:7])
            hrf = SurroundingRectangle(old_exp)
            self.play(ShowCreation(hrc), ShowCreation(hrf))
            self.wait()
            self.play(Uncreate(hrc), Uncreate(hrf))
            self.play(Indicate(div_code[0][-3:]))
            self.play(
                ReplacementTransform(div_code[0][-3:].copy(), target_exp),
                FadeOut(old_exp))

        highlight_and_switch_exp(of4[2], of3[2])
        tmp_of4_right = VGroup(of4[4].copy(), of4[5].copy())
        tmp_of4_right.next_to(of3[4],
                              ORIGIN,
                              submobject_to_align=tmp_of4_right[0])
        highlight_and_switch_exp(tmp_of4_right[1], of3[5])
        self.wait()

        self.play(
            ReplacementTransform(of3[3:5], of4[3:5]),
            ReplacementTransform(tmp_of4_right[1], of4[5]),
            ReplacementTransform(of3[-1], of4[-1]),
        )
        self.remove(*of3)
        self.add(of4)
        self.wait()

        self.play(*[FadeOut(o) for o in [t1, t2, div_code]])

        originals = [if_even, ef3, if_odd, of4]
        for o in originals:
            o.generate_target()
        for p in [ef3.target[1:], of4.target[1:6]]:
            p.set_color(ORANGE)
        eqtg = VGroup(*[o.target for o in originals]).center().to_edge(TOP)

        t1 = TextMobject(
            'Using \\texttt{int} for $n$ makes the equations very similar,\\\\'
            'and the coding very simple!',
            tex_to_color_map={'int': RED})
        t1.next_to(eqtg, DOWN, buff=MED_LARGE_BUFF)
        self.play(*[MoveToTarget(o) for o in originals], FadeIn(t1))
        self.wait(duration=3)
        eqg = VGroup(*originals)

        # Transform to code
        code_scale = 0.7
        power2_code = CodeBlock(
            'Java',
            r"""
            public static int power2(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power2(x, n / 2);
                if (n % 2 == 0) {
                    return t * t;
                }
                return t * t * x;
            }	
            """,
            code_scale=code_scale,
        )
        power2_code.to_edge(RIGHT)

        ef3.generate_target()
        of4.generate_target()
        ef3.target.next_to(power2_code.get_code().get_lines(7),
                           LEFT,
                           buff=LARGE_BUFF)
        of4.target.next_to(power2_code.get_code().get_lines(9),
                           LEFT,
                           buff=LARGE_BUFF)
        ef3.target.next_to(of4.target,
                           UP,
                           index_of_submobject_to_align=0,
                           coor_mask=X_AXIS)

        et = TextMobject('\\textit{even:}')
        ot = TextMobject('\\textit{odd:}')
        ot.next_to(of4.target[0], LEFT)
        et.next_to(ef3.target[0], LEFT)

        self.play(FadeOut(t1))
        self.play(
            FadeOut(if_even),
            FadeOut(if_odd),
            FadeInFrom(power2_code, RIGHT),
            MoveToTarget(ef3),
            MoveToTarget(of4),
            ReplacementTransform(if_even, et),
            ReplacementTransform(if_odd, ot),
        )
        self.wait()

        highlights = [
            [
                SurroundingRectangle(
                    power2_code.get_code().get_lines(1)[-6:-2]),
                SurroundingRectangle(
                    power2_code.get_code().get_lines(5)[-5:-2]),
                SurroundingRectangle(ef3[-1]),
            ],
            [
                SurroundingRectangle(
                    power2_code.get_code().get_lines(5)[5:-1]),
                SurroundingRectangle(ef3[-2:]),
            ],
            [
                SurroundingRectangle(
                    power2_code.get_code().get_lines(7)[-4:-1]),
                SurroundingRectangle(ef3[1:]),
            ],
            [
                SurroundingRectangle(
                    power2_code.get_code().get_lines(9)[-6:-1]),
                SurroundingRectangle(of4[1:]),
            ],
        ]

        for g in highlights:
            self.play(*[ShowCreation(h) for h in g])
            self.wait(duration=1.5)
            self.play(*[Uncreate(h) for h in g])
        self.wait()

        self.play(*[FadeOut(o) for o in self.mobjects if o != power2_code])
        power2_code.generate_target()
        power2_code.target.center()
        power2_code.target.to_edge(UP)
        self.play(MoveToTarget(power2_code))
Ejemplo n.º 6
0
    def construct(self):
        code_scale = 0.7
        power2_code = CodeBlock(
            'Java',
            r"""
            public static int power2(int x, int n) {
                if (n == 0) {
                    return 1;
                }
                int t = power2(x, n / 2);
                if (n % 2 == 0) {
                    return t * t;
                }
                return t * t * x;
            }	
            """,
            code_scale=code_scale,
        )
        power2_code.to_edge(UP)
        self.add(power2_code)

        t1 = TextMobject(
            "This version should call itself fewer than $n$ times")
        t1.next_to(power2_code, DOWN, buff=LARGE_BUFF)
        self.play(FadeInFromDown(t1))
        self.wait(duration=2.5)

        t2 = TextMobject(
            'How many times do you think \\texttt{power2(2,30)} will call itself?',
            tex_to_color_map={'30': YELLOW})
        t2.next_to(t1, ORIGIN)
        self.play(FadeOutAndShift(t1, UP), FadeInFromDown(t2))
        self.wait(duration=2)

        t3 = TextMobject("We know it'll be less than 30!  Maybe 15?")
        t3.next_to(t2, DOWN, buff=MED_LARGE_BUFF)
        self.play(FadeInFromDown(t3))
        self.wait(duration=2)

        t4 = TextMobject("How about a lot less?  Let's see...")
        t4.next_to(t3, ORIGIN)
        self.play(ReplacementTransform(t3, t4))
        self.wait(duration=2)

        self.play(*[FadeOut(o) for o in self.mobjects if o != power2_code])

        # Start stepping through this and see it go.
        main_code = CodeBlock(
            'Java',
            r"""
            public static void main(String[] args) {
                int y = power2(2, 30);
            }
            """,
            line_offset=10,
            code_scale=code_scale - 0.1,
        )
        frame_width = 3.5
        main_frame = StackFrame(main_code,
                                'main()',
                                12, ['y'],
                                width=frame_width,
                                slot_char_width=8)
        main_code.highlight_lines(12)
        VGroup(main_code, main_frame).arrange(RIGHT,
                                              buff=LARGE_BUFF).to_edge(DOWN)
        self.play(
            FadeInFromDown(main_frame),
            FadeInFromDown(main_code),
        )
        self.wait()

        def call_power2(x, n, call_stack, cc_num):
            new_cc_num = TextMobject(str(len(call_stack) -
                                         1)).move_to(cc_num).set_color(YELLOW)
            update_cc_anims = [ReplacementTransform(cc_num, new_cc_num)]

            call_ret_delay = 0.5
            stack_frame = StackFrame(power2_code,
                                     'power(%d, %d)' % (x, n),
                                     1, ['x', 'n', 't'],
                                     width=frame_width)
            call_stack.animate_call(stack_frame,
                                    self,
                                    extra_anims=update_cc_anims)

            self.play(
                *stack_frame.get_update_line_anims(2),
                stack_frame.update_slot,
                'x',
                x,
                stack_frame.update_slot,
                'n',
                n,
            )

            if n == 0:
                self.play(*stack_frame.get_update_line_anims(3))
                self.wait(duration=call_ret_delay)
                call_stack.animate_return(self)
                return 1
            else:
                self.play(*stack_frame.get_update_line_anims(5))
                self.wait(duration=call_ret_delay)

                t = call_power2(x, n // 2, call_stack, new_cc_num)
                self.play(
                    *stack_frame.get_update_line_anims(6),
                    stack_frame.update_slot,
                    't',
                    t,
                )

                if n % 2 == 0:
                    self.play(*stack_frame.get_update_line_anims(7))
                    self.wait(duration=call_ret_delay)
                    call_stack.animate_return(self)
                    return t * t

                self.play(*stack_frame.get_update_line_anims(9))
                self.wait(duration=call_ret_delay)
                call_stack.animate_return(self)
                return t * t * x

        cc_label = TextMobject('Recursive Calls').scale(0.75)
        cc_label.to_edge(UL).shift(DOWN)
        call_counter = TextMobject('0').set_color(YELLOW).next_to(
            cc_label, DOWN)
        self.play(FadeIn(call_counter), FadeIn(cc_label))

        result = call_power2(2, 30, CallStack(main_frame), call_counter)
        self.play(
            *main_frame.get_update_line_anims(13),
            main_frame.update_slot,
            'y',
            result,
        )
        self.wait()

        t1 = TextMobject(
            'We got our answer in just 5 recursive calls!').set_color(YELLOW)
        t1.next_to(power2_code, DOWN, buff=MED_LARGE_BUFF)
        self.play(ShowCreation(t1))
        self.wait(duration=2)

        t1.generate_target()
        t1.target.center().to_edge(TOP)
        t2 = TextMobject('But why?').next_to(t1.target, DOWN, buff=LARGE_BUFF)
        self.play(
            MoveToTarget(t1),
            FadeInFromDown(t2),
            *[FadeOut(o) for o in self.mobjects if o != t1],
        )