# Der Quellcode zum Video 'Der Poissonsche Grenzwertsatz und die Poisson-Approximation' # # wurde entwickelt von # # Oğulcan Aşık # Chavoush Kalhor # Christian Müller # # an der Heinrich-Heine-Universität Düsseldorf. # # Dieses Werk ist lizenziert unter einer Creative Commons Namensnennung-Weitergabe unter gleichen Bedingungen 4.0 International Lizenz. # Um eine Kopie der Lizenz zu erhalten, besuchen Sie https://creativecommons.org/licenses/by-sa/4.0/. # # SPDX-License-Identifier: CC-BY-SA-4.0 from manim import * import math import random random.seed(1312) ORCAblue = "#002b44" ORCAred = "#e61300" ORCAgreen = "#4cb011" ORCAgrey = "#f5f5f5" ORCAwhite = "#ffffff" config.background_color = "#c4d1d7" config.max_files_cached = -1 # video sections and learning outcomes sections = Tex( r"\textsf{\textbf{Der Poissonsche Grenzwertsatz \\ und die Poisson-Approximation}}\\", r"\textsf{\textbf{Kapitel 1: \\ Ein einführendes Beispiel}}\\", r"\textsf{\textbf{Kapitel 2: \\ Stochastische Modellierung}}\\", r"\textsf{\textbf{Kapitel 3: \\ Der Poissonsche Grenzwertsatz}}\\", r"\textsf{\textbf{Kapitel 4: \\ Die Poisson-Approximation}}\\", r"\textsf{\textbf{Zusammenfassung}}", color=ORCAblue, ) outcomes = Tex( r"\textsf{\textbf{In diesem Video}}", r"\begin{itemize} \item \textsf{Motivation und Aussage verstehen} \end{itemize}", r"\begin{itemize} \item \textsf{Anwendungsfälle erkennen} \end{itemize}", r"\begin{itemize} \item \textsf{Rechnungen durchführen} \end{itemize}", color=ORCAblue, ) # animate video sections and learning outcomes class PoissonSection0(Scene): # scene 0: introduction and learning outcomes def construct(self): sections[0].set(font_size=56).move_to(ORIGIN) outcomes[0].to_edge(UL) outcomes[1:4].to_edge(LEFT) logo_license = SVGMobject(file_name="..\Anderes\Logo_CC_BY_SA.svg", width=1.5) self.add(logo_license.to_edge(DR)) self.wait() self.play(Write(sections[0])) self.wait() self.play(FadeOut(sections[0])) self.wait() self.play(FadeIn(outcomes[0])) self.wait() self.play(FadeIn(outcomes[1])) self.wait() self.play(FadeIn(outcomes[2])) self.wait() self.play(FadeIn(outcomes[3])) self.wait() self.play(*[FadeOut(mobject) for mobject in self.mobjects]) class PoissonSection1(Scene): def construct(self): sections[1].move_to(ORIGIN) self.wait() self.play(Write(sections[1])) self.wait() self.play(FadeOut(sections[1])) self.wait() class PoissonSection2(Scene): def construct(self): sections[2].move_to(ORIGIN) self.wait() self.play(Write(sections[2])) self.wait() self.play(FadeOut(sections[2])) self.wait() class PoissonSection3(Scene): def construct(self): sections[3].move_to(ORIGIN) self.wait() self.play(Write(sections[3])) self.wait() self.play(FadeOut(sections[3])) self.wait() class PoissonSection4(Scene): def construct(self): sections[4].move_to(ORIGIN) self.wait() self.play(Write(sections[4])) self.wait() self.play(FadeOut(sections[4])) self.wait() class PoissonSection5(Scene): def construct(self): sections[5].move_to(ORIGIN) self.wait() self.play(Write(sections[5])) self.wait() self.play(FadeOut(sections[5])) self.wait() # begin with regular animations class Poisson1(Scene): # scene 1: number example def construct(self): # store lotto fields tables = [] # store lotto tips numbers = [] # colors for lotto ticket Colors = [ BLUE, TEAL, GREEN, YELLOW, GOLD, LIGHT_BROWN, PURPLE, PINK, LIGHT_PINK, MAROON, RED, ORANGE, ] for i in range(12): # generate empty lotto field tables.append( IntegerTable( np.arange(1, 50).reshape(7, 7), v_buff=0.5, h_buff=0.5, include_outer_lines=True, ).set_color(ORCAblue) ) # generate empty lotto tip numbers.append([]) # show creation of the first three lotto fields if i < 3: self.wait() self.play(FadeIn(tables[i])) self.wait() # select random lotto tip for (m, n) in random.sample( [(m, n) for m in range(1, 8) for n in range(1, 8)], k=6 ): numbers[i].append(7 * (m - 1) + n) # show creation of the first three lotto tips if i < 3: self.play( Animation( tables[i].add_highlighted_cell((m, n), color=Colors[i]), run_time=0.75, ) ) else: tables[i].add_highlighted_cell((m, n), color=Colors[i]) # sort the lotto tip numbers[i].sort() if i < 2: self.wait() # show probability of success if i == 1: probability = MathTex( r"p & =\binom{49}{6}^{-1} \\ & \approx7.15\cdot10^{-8}" ) probability.set_color(ORCAblue).shift(4 * RIGHT) self.play(tables[i].animate.shift(2 * LEFT)) self.wait() self.add(probability) self.wait(5) self.play(FadeOut(tables[i]), FadeOut(probability)) else: self.play(FadeOut(tables[i])) self.wait(0.5) # duplicate third lotto field tableCopy = tables[2].copy() tables[2].save_state() tableCopy.save_state() self.play( tables[2].animate.scale(0.75).move_to(UP + LEFT * 3), tableCopy.animate.scale(0.75).move_to(UP + RIGHT * 3), ) self.wait() # show question question = Tex( r"\textsf{\textbf{Frage:} Mit welcher Wahrscheinlichkeit}\\", r"\textsf{werden genau zweimal die sechs Richtigen getippt?}", ) question.set_color(ORCAblue).to_edge(DOWN) self.play(FadeIn(question)) self.wait() self.play(FadeOut(question)) self.wait() # restore fields then remove the copy self.play(Restore(tables[2]), Restore(tableCopy)) self.remove(tables[2]) self.wait() # generate lotto ticket from lotto fields for t in tables: t.remove(*t.get_vertical_lines()) t.remove(*t.get_horizontal_lines()) t.scale(0.25) ticket = MobjectTable( [tables[0:6], tables[6:12]], v_buff=0.2, h_buff=0.2, include_outer_lines=True, ) ticket.get_vertical_lines().set_color(ORCAblue) ticket.get_horizontal_lines().set_color(ORCAblue) # randomly determine the actual lotto numbers from among the lotto tips j = random.randrange(0, 12) lottery = MathTex(r"\textsf{6 Richtige: }" + str(numbers[j])) lottery.set_color(ORCAblue).to_edge(DOWN) # check which lotto tip is correct checkmarks = VGroup() for k in range(12): if numbers[k] == numbers[j]: checkmarks.add( Circle(color=GREEN, radius=0.25).next_to( ticket.get_cell((1 if k < 6 else 2, k + 1 if k < 6 else k - 5)), UP if k < 6 else DOWN, ) ) else: checkmarks.add( Cross(color=RED, scale_factor=0.25).next_to( ticket.get_cell((1 if k < 6 else 2, k + 1 if k < 6 else k - 5)), UP if k < 6 else DOWN, ) ) # transform lotto field into lotto ticket self.play(ShrinkToCenter(tableCopy)) self.play(GrowFromCenter(ticket)) self.wait() # write actual lotto numbers and checkmarks self.play(Write(lottery), run_time=2) self.wait() self.play(Write(checkmarks), run_time=2) self.wait() class Poisson2(Scene): # scene 2: further examples def construct(self): examples = Tex( r"\textsf{\textbf{Weitere Beispiele}}", r"\begin{itemize} \item \textsf{Fehlerhafte Teile in einem Produktionsprozess} \end{itemize}", r"\begin{itemize} \item \textsf{Verkehrsunfälle innerhalb eines Jahres} \end{itemize}", r"\begin{itemize} \item \textsf{Zerfälle eines radioaktiven Nuklids innerhalb einer Minute} \end{itemize}", color=ORCAblue, ) examples[0].to_edge(UL) examples[1:4].to_edge(LEFT) self.play(FadeIn(examples[0])) self.wait() self.play(FadeIn(examples[1])) self.wait() self.play(FadeIn(examples[2])) self.wait() self.play(FadeIn(examples[3])) self.wait() class Poisson3(Scene): # scene 3: stochastic modelling def construct(self): # model as a single random variable model1A = MathTex(r"X\sim\textsf{Bin}(n,p)") model1B = MathTex(r"n=10^7,\quad p=\binom{49}{6}^{-1}\approx7.15\cdot10^{-8}") model1C = MathTex(r"P(X=2)=\binom{n}{2}p^2(1-p)^{n-2}=\textbf{?}") model1D = MathTex( r"P(X=2)\approx\binom{10^7}{2}\cdot(7.15\cdot10^{-8})^2\cdot(1-7.15\cdot10^{-8})^{10^7-2}=\textbf{?}" ) model1A.set_color(ORCAblue).shift(UP * 2) model1B.set_color(ORCAblue).shift(UP) model1C.set_color(ORCAblue).shift(DOWN) model1D.set_color(ORCAblue).shift(DOWN) # model as part of a sequence of random variables model2A = MathTex(r"X_n\sim\textsf{Bin}(n,p_n)") model2B = MathTex(r"np_n\xrightarrow{n\to\infty}\lambda>0") model2C = MathTex( r"P(X_n=2)=\binom{n}{2}p_n^2(1-p_n)^{n-2}\xrightarrow{n\to\infty}\textbf{?}" ) model2A.set_color(ORCAblue).shift(UP * 2) model2B.set_color(ORCAblue).shift(UP) model2C.set_color(ORCAblue).shift(DOWN) self.wait() self.add(model1A, model1B) self.wait() self.add(model1C) self.wait() model1C.save_state() self.play(Transform(model1C, model1D)) self.wait() self.play(Restore(model1C)) self.wait() self.play(Transform(model1A, model2A)) self.wait() self.play(Transform(model1B, model2B)) self.wait() self.play(ReplacementTransform(model1C, model2C)) self.wait() class Poisson4(Scene): # scene 4: graphic motivation def construct(self): axes = Axes( x_range=[0, 50, 10], y_range=[0, 0.25, 0.05], x_length=10, y_length=6, tips=False, axis_config={"color": ORCAblue}, ).add_coordinates(color=ORCAblue) labels = axes.get_axis_labels(x_label="k", y_label="P(X=k)").set_color(ORCAblue) # value trackers for n and p n = ValueTracker(30) p = ValueTracker(0.75) # legend for binomial distribution binSymbol = Dot(radius=0.05, color=ORCAblue) binText = Tex( r"\textsf{Binomialverteilung} $\textsf{Bin}(n,p)$", color=ORCAblue ) binLegend = VGroup(binSymbol, binText).arrange().to_edge(UR) # legend for parameters npText = MathTex(r"n & = \\", r"p & = \\", r"np & = \\") npText.set_color(ORCAblue).move_to(4.5 * RIGHT + UP) nNumber = ( DecimalNumber(num_decimal_places=0, color=ORCAblue) .next_to(npText[0], RIGHT) .add_updater(lambda m: m.set_value(n.get_value())) ) pNumber = ( DecimalNumber(num_decimal_places=4, color=ORCAblue) .next_to(npText[1], RIGHT) .add_updater(lambda m: m.set_value(p.get_value())) ) npNumber = ( DecimalNumber(num_decimal_places=2, color=ORCAblue) .next_to(npText[2], RIGHT) .add_updater(lambda m: m.set_value(n.get_value() * p.get_value())) ) # draw density of binomial distribution binGraph = VGroup() for k in range(51): binGraph += Dot(radius=0.05, color=ORCAblue).add_updater( lambda m, k=k: m.move_to( axes.c2p( k, math.prod([n.get_value() - x for x in range(k)]) / math.factorial(k) * (p.get_value() ** k) * (1 - p.get_value()) ** (n.get_value() - k) if k <= n.get_value() else 0, ) ) ) # build up scene self.wait() self.play( Create(axes), Create(labels), run_time=2, ) self.wait() # build up graphic self.play( Create(binGraph), Write(binLegend), Write(npText), Write(nNumber), Write(pNumber), Write(npNumber), run_time=2, ) self.wait() # only vary n self.play(n.animate.set_value(50), run_time=5, rate_func=linear) self.wait() # only vary p self.play(p.animate.set_value(0.15), run_time=5, rate_func=linear) self.wait() # vary both n and p with fixed n * p p.add_updater(lambda m: m.set_value(7.5 / n.get_value())) self.play( n.animate.set_value(100), p.animate.set_value(0.075), run_time=10, rate_func=linear, ) self.wait() class Poisson5(Scene): # scene 5: poisson limit theorem (plt) def construct(self): # assumptions of plt assumptions = MathTex( r"\textsf{Sei }(p_n)_{n\in\mathbb{N}}\subseteq(0,1)", r"\textsf{ und }\lambda>0,", r"\textsf{ sodass }np_n\xrightarrow{n\to\infty}\lambda.", ) assumptions.set_color(ORCAblue).to_edge(UL) # name of plt plt_text = Tex(r"\textbf{\textsf{Poissonscher Grenzwertsatz}}") plt_text.set_color(ORCAblue).shift(UP * 2) # statement of plt plt_formula1 = MathTex( r"\binom{n}{k}p_n^k(1-p_n)^{n-k}", r"\xrightarrow{n\to\infty}", r"e^{-\lambda}\frac{\lambda^k}{k!}", r"\quad\forall k\in\mathbb{N}_0", ) plt_formula1.set_color(ORCAblue).shift(UP * 0.5) # box around name and statement plt_box = SurroundingRectangle(VGroup(plt_text, plt_formula1), color=ORCAblue) # explanation of binomial density and poisson density explanation = MathTex( r"\textsf{Bin}(n,p_n)(\{k\}) & = \binom{n}{k}p_n^k(1-p_n)^{n-k}\\", r"\textsf{Poi}(\lambda)(\{k\}) & = e^{-\lambda}\frac{\lambda^k}{k!}", ) explanation.set_color(ORCAblue).to_edge(DOWN) self.wait() self.play(Write(assumptions[0])) self.wait() self.play(Write(assumptions[1])) self.wait() self.play(Write(assumptions[2])) self.wait() self.play(Write(plt_formula1[0])) self.wait() self.play(Write(plt_formula1[1])) self.wait() self.play(Write(plt_formula1[2:])) self.wait() self.play(Write(plt_text)) self.wait() self.play(Create(plt_box)) self.wait() self.play(Indicate(plt_formula1[0], color=None)) self.wait() self.play(FadeIn(explanation[0])) self.wait() self.play(Indicate(plt_formula1[2], color=None)) self.wait() self.play(FadeIn(explanation[1])) self.wait() class Poisson6(Scene): # scene 6: poisson approximation principle def construct(self): # statement of poisson limit theorem textA = Tex(r"\textsf{\textbf{Poissonscher Grenzwertsatz:}}") calculationA = MathTex( r"np_n\to\lambda", r"\:\Rightarrow\:", r"\textsf{Bin}(n,p_n)(\{k\})\to\textsf{Poi}(\lambda)(\{k\})", r"\quad\forall k\in\mathbb{N}_0", ) textA.set_color(ORCAblue).to_edge(LEFT).shift(UP * 2) calculationA.set_color(ORCAblue).shift(UP) # principle of poisson approximation textB = Tex(r"\textsf{\textbf{Poisson-Approximation:}}") calculationB = MathTex( r"\textsf{$n$ groß und $p$ klein}", r"\:\Rightarrow\:", r"\textsf{Bin}(n,p)(\{k\})\approx\textsf{Poi}(np)(\{k\})", r"\quad\forall k\in\mathbb{N}_0", ) textB.set_color(ORCAblue).to_edge(LEFT).shift(DOWN) calculationB.set_color(ORCAblue).shift(DOWN * 2) boxA22 = SurroundingRectangle(calculationA[2], buff=0.1, color=ORCAred) boxA20 = SurroundingRectangle(calculationA[0], buff=0.1, color=ORCAred) boxB20 = SurroundingRectangle(calculationB[0], buff=0.1, color=ORCAred) boxB22 = SurroundingRectangle(calculationB[2], buff=0.1, color=ORCAred) self.wait() self.play(FadeIn(textA), FadeIn(calculationA)) self.wait() self.play(Create(boxA22)) self.wait() self.play(ReplacementTransform(boxA22, boxA20)) self.wait() self.play(Transform(textA.copy(), textB)) self.wait() self.play( Transform(calculationA[0].copy(), calculationB[0]), ReplacementTransform(boxA20, boxB20), ) self.wait() self.play(Transform(calculationA[1].copy(), calculationB[1])) self.wait() self.play( Transform(calculationA[2:].copy(), calculationB[2:]), ReplacementTransform(boxB20, boxB22), ) self.wait() self.play(Uncreate(boxB22)) self.wait() class Poisson7(Scene): # scene 7: poisson approximation example def construct(self): # reminder about parameters reminder = MathTex( r"X\sim\textsf{Bin}(n,p),\quad", r"n=10^7,\quad", r"p=\binom{49}{6}^{-1}", ) reminder.set_color(ORCAblue).to_edge(UP) # exact calculation textC = Tex(r"\textsf{\textbf{exakt:}}") calculationC = MathTex( r"& P(X=2)\\", r"& =\textsf{Bin}(n,p)(\{2\})\\", r"& =\binom{n}{2}p^2(1-p)^{n-2}\\", r"& \approx {{0.12506887}} 63", ) textC.set_color(ORCAblue).shift(UP * 1.5 + LEFT * 5) calculationC.set_color(ORCAblue).shift(DOWN + LEFT * 3) # approximate calculation textD = Tex(r"\textsf{\textbf{approximativ:}}") calculationD = MathTex( r"& P(X=2)\\", r"& \approx\textsf{Poi}(np)(\{2\})\\", r"& =e^{-np}\frac{(np)^2}{2!}\\", r"& \approx {{0.12506887}} 41", ) textD.set_color(ORCAblue).shift(UP * 1.5 + RIGHT * 2) calculationD.set_color(ORCAblue).shift(DOWN + RIGHT * 3) # boxes around matching digits boxC = SurroundingRectangle(calculationC[-2], color=ORCAred) boxD = SurroundingRectangle(calculationD[-2], color=ORCAred) self.wait() self.play(FadeIn(reminder)) self.wait() self.play(FadeIn(textC)) self.wait() self.play(FadeIn(calculationC[0:2])) self.wait() self.play(FadeIn(calculationC[2])) self.wait() self.play(Transform(textC.copy(), textD)) self.wait() self.play(Transform(calculationC[0].copy(), calculationD[0])) self.wait() self.play(Transform(calculationC[1].copy(), calculationD[1])) self.wait() self.play(Transform(calculationC[2].copy(), calculationD[2])) self.wait() self.play(Write(calculationC[3:]), Write(calculationD[3:])) self.wait() self.play(Create(boxC), Create(boxD)) self.wait() self.play(Uncreate(boxC), Uncreate(boxD)) self.wait() class Poisson8(Scene): # scene 8: poisson approximation visualization def construct(self): axes = Axes( x_range=[0, 50, 10], y_range=[0, 0.25, 0.05], x_length=10, y_length=6, tips=False, axis_config={"color": ORCAblue}, ).add_coordinates(color=ORCAblue) labels = axes.get_axis_labels(x_label="k", y_label="P(X=k)").set_color(ORCAblue) # value trackers for n and p n = ValueTracker(30) p = ValueTracker(0.75) # legend for binomial distribution binSymbol = Dot(radius=0.05, color=ORCAblue) binText = Tex( r"\textsf{Binomialverteilung} $\textsf{Bin}(n,p)$", color=ORCAblue ) binLegend = VGroup(binSymbol, binText).arrange().to_edge(UR) # legend for poisson distribution poiSymbol = Triangle(radius=0.05, color=ORCAred, fill_color=ORCAred) poiText = Tex(r"\textsf{Poisson-Verteilung} $\textsf{Poi}(np)$", color=ORCAred) poiLegend = VGroup(poiSymbol, poiText).arrange().to_edge(UR).shift(DOWN) # legend for parameters npText = MathTex(r"n & = \\", r"p & = \\", r"np & = \\") npText.set_color(ORCAblue).shift(RIGHT * 4.5) nNumber = ( DecimalNumber(num_decimal_places=0, color=ORCAblue) .next_to(npText[0], RIGHT) .add_updater(lambda m: m.set_value(n.get_value())) ) pNumber = ( DecimalNumber(num_decimal_places=4, color=ORCAblue) .next_to(npText[1], RIGHT) .add_updater(lambda m: m.set_value(p.get_value())) ) npNumber = ( DecimalNumber(num_decimal_places=2, color=ORCAblue) .next_to(npText[2], RIGHT) .add_updater(lambda m: m.set_value(n.get_value() * p.get_value())) ) # draw density of binomial distribution binGraph = VGroup() for k in range(51): binGraph += Dot(radius=0.05, color=ORCAblue).add_updater( lambda m, k=k: m.move_to( axes.c2p( k, math.prod([n.get_value() - x for x in range(k)]) / math.factorial(k) * (p.get_value() ** k) * (1 - p.get_value()) ** (n.get_value() - k) if k <= n.get_value() else 0, ) ) ) # draw density of poisson distribution poiGraph = VGroup() for k in range(51): poiGraph += Triangle( radius=0.05, color=ORCAred, fill_color=ORCAred ).add_updater( lambda m, k=k: m.move_to( axes.c2p( k, math.exp(-n.get_value() * p.get_value()) * (n.get_value() * p.get_value()) ** k / math.factorial(k), ) ) ) # build up scene self.wait() self.play( Create(axes), Create(labels), run_time=2, ) self.wait() # build up graphic self.play( Create(binGraph), Write(binLegend), Write(npText), Write(nNumber), Write(pNumber), Write(npNumber), run_time=2, ) self.wait() self.play( Create(poiGraph), Write(poiLegend), run_time=2, ) self.wait() # only vary n self.play(n.animate.set_value(50), run_time=7.5, rate_func=linear) self.wait() # only vary p self.play(p.animate.set_value(0.15), run_time=7.5, rate_func=linear) self.wait() # vary both n and p with fixed n * p p.add_updater(lambda m: m.set_value(7.5 / n.get_value())) self.play( n.animate.set_value(200), p.animate.set_value(0.0375), run_time=7.5, rate_func=linear, ) self.wait()