Skip to article frontmatterSkip to article content
Combinatória

Arranjo, Combinação e Permutação

Fundamentação

Código-fonte
main.py
from manim import *
import itertools
import numpy as np

class cena(Scene):
    def construct(self):

        # ABERTURA DO VÍDEO
        inicio = Text("SAEPE Animado")
        fim = inicio.copy()
        triangulo = Triangle(color=BLUE)
        retangulo = Rectangle(width=2.5, height=1.5, color=GREEN)
        pentagono = RegularPolygon(n=5, radius=1, color=ORANGE)
        hexagono = RegularPolygon(n=6, radius=1, color=PURPLE)
        
        self.wait()
        self.play(Write(inicio))
        self.wait()
        self.play(Transform(
            inicio,
            fim.shift(2*UP),
            path_func=utils.paths.straight_path(),
            run_time=1.5,
        ))
        self.wait()

        triangulo.shift(LEFT * 4.5)
        retangulo.shift(LEFT * 1.6)
        pentagono.shift(RIGHT * 1.6)
        hexagono.shift(RIGHT * 4.5)

        self.play(Create(triangulo), Create(retangulo),
                  Create(pentagono), Create(hexagono))
        self.wait(1)

        # AGRUPAMENTO E REDUÇÃO DAS FIGURAS
        grupo1 = VGroup(triangulo, retangulo, pentagono, hexagono)
        self.play(grupo1.animate.scale(0.7))
        self.wait(0.5)

        for _ in range(4):
            self.play(CyclicReplace(*grupo1))
        self.wait(1)

        # Agora adiciona o texto e agrupa tudo
        titulo_gp = Text(
            "Estatística, Probabilidade e Análise Combinatória", font="Fira Sans").scale(0.75)
        titulo_gp.next_to(grupo1, DOWN, buff=0.8)

        self.play(Write(titulo_gp))
        self.wait(1)

        grupo_com_texto = VGroup(grupo1, titulo_gp, inicio)
        self.play(FadeOut(grupo_com_texto))
        t1 = Text("Análise Combinatória", font="Fira Sans").scale(0.5)
        self.play(Write(t1, run_time=2))
        self.wait(0.5)

        linha = Line(
            start=t1.get_bottom() + DOWN * 0.2 + LEFT * t1.width / 2,
            end=t1.get_bottom() + DOWN * 0.2 + RIGHT * t1.width / 2,
            stroke_width=6,
        )
        linha.set_color(RED)

        # Função para atualizar a cor da linha
        def atualizar_linha(obj, dt):
            t = self.time
            nova_cor = interpolate_color(RED, BLUE, (np.sin(t * 2) + 1) / 2)
            obj.set_color(nova_cor)

        linha.add_updater(atualizar_linha)
        self.play(FadeIn(linha))
        self.wait(1)

        # ---------- COMBINAÇÃO ----------
        novo_titulo = Text("Combinação", font="Fira Sans").scale(0.5)
        novo_titulo.move_to(t1.get_center())

        nova_linha = Line(
            start=novo_titulo.get_bottom() + DOWN * 0.2 + LEFT * novo_titulo.width / 2,
            end=novo_titulo.get_bottom() + DOWN * 0.2 + RIGHT * novo_titulo.width / 2,
            stroke_width=6
        ).set_color(linha.get_color())

        self.play(Transform(t1, novo_titulo), Transform(linha, nova_linha))
        self.wait(0.5)

        self.play(FadeOut(t1), FadeOut(linha))
        self.wait(1)

        grupo_titulo = VGroup(novo_titulo, nova_linha)
        self.play(grupo_titulo.animate.scale(0.9).to_corner(UL))
        self.wait(1)

        explicacao_combinacao = Paragraph(
            r"   Em uma combinação, escolhemos elementos de um conjunto, mas a ordem desses elementos não importa.",
            r"Por exemplo, se escolhemos as letras A e B, isso é considerado o mesmo que escolher B e A.",
            r"",
            r"   A fórmula para calcular o número de combinações de n elementos tomados k a k é:",
            alignment="left", line_spacing=0.8, font="CMU Serif"
        ).scale(0.35)

        formula_combinacao = MathTex(r"C(n, k) = \dfrac{n!}{k!(n-k)!}").scale(0.8)

        explicacao_variaveis_comb = Paragraph(
            r"n é o número total de elementos, k é a quantidade escolhida, e ! indica fatorial.",
            alignment="left", line_spacing=0.8, font="CMU Serif"
        ).scale(0.3)

        # Correção: agora usamos novo_titulo como referência
        explicacao_combinacao.next_to(grupo_titulo, DOWN, buff=0.3).align_to(grupo_titulo, LEFT)
        self.play(Write(explicacao_combinacao))
        self.wait(1)

        formula_combinacao.next_to(explicacao_combinacao, DOWN, buff=0.4).align_to(explicacao_combinacao, LEFT)
        self.play(Write(formula_combinacao))
        self.wait(1.2)

        explicacao_variaveis_comb.next_to(formula_combinacao, DOWN, buff=0.3).align_to(formula_combinacao, LEFT)
        self.play(Write(explicacao_variaveis_comb))
        self.wait(1.2)

        self.play(*[FadeOut(mob) for mob in self.mobjects])
        self.wait(0.5)

        # ---------- BOLINHAS ----------
        cores = [RED, GREEN, BLUE, YELLOW, PURPLE]
        nomes = ["A", "B", "C", "D", "E"]
        bolas_base = []

        for cor, nome in zip(cores, nomes):
            bola = Circle(radius=0.12, color=WHITE, fill_color=cor, fill_opacity=1)
            label = Text(nome, font_size=18).next_to(bola, UP, buff=0.05)
            grupo = VGroup(label, bola)
            bolas_base.append(grupo)

        base_group = VGroup(*bolas_base).arrange(RIGHT, buff=0.3).move_to(ORIGIN)
        self.play(FadeIn(base_group))
        self.wait(1)
        self.play(base_group.animate.shift(UP * 2.8))  # ← Ajuste de altura

        # ---------- COMBINAÇÕES ----------
        combinacoes = list(itertools.combinations(bolas_base, 2))
        titulo_comb = Text("Combinações (ordem não importa)", font_size=28).next_to(base_group, DOWN, buff=0.3)
        self.play(Write(titulo_comb))

        combinacao_group = VGroup()
        for i, (a, b) in enumerate(combinacoes):
            copia_a, copia_b = a.copy(), b.copy()
            par = VGroup(copia_a, copia_b).arrange(RIGHT, buff=0.25)

            linha_idx = i // 6
            coluna = i % 6
            par.move_to(LEFT * 4.2 + RIGHT * 1.8 * coluna + DOWN * (1.1 + 0.9 * linha_idx))
            combinacao_group.add(par)

        self.play(LaggedStartMap(FadeIn, combinacao_group, shift=DOWN, lag_ratio=0.03))
        self.wait(2)
        self.play(*[FadeOut(mob) for mob in self.mobjects])
        self.wait(0.5)

        # ---------- ARRANJO ----------
        novo_titulo1 = Text("Arranjo", font="Fira Sans").scale(0.5)
        

        nova_linha1 = Line(
            start=novo_titulo1.get_bottom() + DOWN * 0.2 + LEFT * novo_titulo1.width / 2,
            end=novo_titulo1.get_bottom() + DOWN * 0.2 + RIGHT * novo_titulo1.width / 2,
            stroke_width=6
        ).set_color(linha.get_color())

        self.play(Write(novo_titulo1), Write(nova_linha1))
        self.wait(1)

        grupo_titulo1 = VGroup(novo_titulo1, nova_linha1)
        self.play(grupo_titulo1.animate.scale(0.9).to_corner(UL))
        self.wait(1)

        explicacao_arranjo = Paragraph(
            r"   Os arranjos representam a seleção de elementos de um conjunto em que a ordem dos escolhidos faz diferença.",
            r"Ou seja, escolher A e B não é o mesmo que escolher B e A.",
            r"",
            r"   A fórmula do arranjo simples de n elementos tomados k a k é:",
            alignment="left", line_spacing=0.8, font="CMU Serif"
        ).scale(0.35)

        formula_arranjo = MathTex(r"A(n, k) = \dfrac{n!}{(n - k)!}").scale(0.8)

        explicacao_variaveis_arranjo = Paragraph(
            r"n é o total de elementos, k é o número de posições, e ! representa o fatorial.",
            alignment="left", line_spacing=0.8, font="CMU Serif"
        ).scale(0.3)

        explicacao_arranjo.next_to(grupo_titulo1, DOWN, buff=0.3).align_to(grupo_titulo1, LEFT)
        self.play(Write(explicacao_arranjo))
        self.wait(1)

        formula_arranjo.next_to(explicacao_arranjo, DOWN, buff=0.4).align_to(explicacao_arranjo, LEFT)
        self.play(Write(formula_arranjo))
        self.wait(1.2)

        explicacao_variaveis_arranjo.next_to(formula_arranjo, DOWN, buff=0.3).align_to(formula_arranjo, LEFT)
        self.play(Write(explicacao_variaveis_arranjo))
        self.wait(1.2)

        self.play(*[FadeOut(mob) for mob in self.mobjects])
        self.wait(0.5)

        arranjos = list(itertools.permutations(bolas_base, 2))
        titulo_arr = Text("Arranjos (ordem importa, sem repetição)", font_size=28).to_edge(UP)
        self.play(Write(titulo_arr))

        arranjo_group = VGroup()
        for i, (a, b) in enumerate(arranjos):
            copia_a, copia_b = a.copy(), b.copy()
            par = VGroup(copia_a, copia_b).arrange(RIGHT, buff=0.25)

            linha_idx = i // 8
            coluna = i % 8
            par.move_to(LEFT * 5 + RIGHT * 1.5 * coluna + DOWN * (1.1 + 0.9 * linha_idx))
            arranjo_group.add(par)

        self.play(LaggedStartMap(FadeIn, arranjo_group, shift=DOWN, lag_ratio=0.02))
        self.wait(2)
        self.play(FadeOut(arranjo_group), FadeOut(titulo_arr), FadeOut(base_group))

        # ---------- PERMUTAÇÃO ----------
        novo_titulo2 = Text("Permutação", font="Fira Sans").scale(0.5)
        

        nova_linha2 = Line(
            start=novo_titulo2.get_bottom() + DOWN * 0.2 + LEFT * novo_titulo2.width / 2,
            end=novo_titulo2.get_bottom() + DOWN * 0.2 + RIGHT * novo_titulo2.width / 2,
            stroke_width=6
        ).set_color(linha.get_color())

        self.play(Write(novo_titulo2), Write(nova_linha2))
        self.wait(1)

        grupo_titulo2 = VGroup(novo_titulo2, nova_linha2)
        self.play(grupo_titulo2.animate.scale(0.9).to_corner(UL))
        self.wait(1)

        explicacao_permutacao = Paragraph(
            r"   As permutações são casos especiais de arranjos em que usamos todos os elementos disponíveis.",
            r"A ordem importa, então organizar A, B e C é diferente de organizar C, B e A.",
            r"",
            r"   A fórmula da permutação de n elementos distintos é:",
            alignment="left", line_spacing=0.8, font="CMU Serif"
        ).scale(0.35)

        formula_permutacao = MathTex(r"P(n) = n!").scale(0.8)

        explicacao_variaveis_perm = Paragraph(
            r"n representa o número total de elementos distintos, e ! é o fatorial.",
            alignment="left", line_spacing=0.8, font="CMU Serif"
        ).scale(0.3)

        explicacao_permutacao.next_to(grupo_titulo2, DOWN, buff=0.3).align_to(grupo_titulo2, LEFT)
        self.play(Write(explicacao_permutacao))
        self.wait(1)

        formula_permutacao.next_to(explicacao_permutacao, DOWN, buff=0.4).align_to(explicacao_permutacao, LEFT)
        self.play(Write(formula_permutacao))
        self.wait(1.2)

        explicacao_variaveis_perm.next_to(formula_permutacao, DOWN, buff=0.3).align_to(formula_permutacao, LEFT)
        self.play(Write(explicacao_variaveis_perm))
        self.wait(1.2)

        self.play(*[FadeOut(mob) for mob in self.mobjects])
        self.wait(0.5)

        cores_perm = [RED, GREEN, BLUE]
        nomes_perm = ["A", "B", "C"]
        bolas_perm = []

        for cor, nome in zip(cores_perm, nomes_perm):
            bola = Circle(radius=0.12, color=WHITE, fill_color=cor, fill_opacity=1)
            label = Text(nome, font_size=18).next_to(bola, UP, buff=0.05)
            grupo = VGroup(label, bola)
            bolas_perm.append(grupo)

        titulo_perm = Text("Permutações de 3 bolinhas coloridas (3!)", font_size=30).to_edge(UP)
        self.play(Write(titulo_perm))

        perm_group = VGroup()
        permutacoes = list(itertools.permutations(bolas_perm, 3))

        for i, (a, b, c) in enumerate(permutacoes):
            tripla = VGroup(a.copy(), b.copy(), c.copy()).arrange(RIGHT, buff=0.25)
            linha_idx = i // 3
            coluna = i % 3
            tripla.move_to(LEFT * 2.8 + RIGHT * 2.5 * coluna + DOWN * (1.5 + 1.2 * linha_idx))
            perm_group.add(tripla)

        self.play(LaggedStartMap(FadeIn, perm_group, shift=DOWN, lag_ratio=0.05))
        self.wait(2)
        self.play(FadeOut(perm_group), FadeOut(titulo_perm))

        self.clear()

Exercícios Resolvidos

Código-fonte
exercicio1.py
from manim import *

class cena(Scene):
    def construct(self):

        # Título centralizado
        titulo = Text("Questão Análise Combinatória (SAEPE - 2021)", font="Fira Sans").scale(0.5)
        self.play(Write(titulo))
        self.wait(0.5)

        # Linha exatamente do tamanho do texto
        linha = Line(
            start=titulo.get_bottom() + DOWN * 0.2 + LEFT * titulo.width / 2,
            end=titulo.get_bottom() + DOWN * 0.2 + RIGHT * titulo.width / 2,
            stroke_width=6
        ).set_color(RED)

        # Atualizador de cor animada
        linha.add_updater(lambda m, dt: m.set_color(
            interpolate_color(RED, BLUE, (np.sin(self.time * 2) + 1) / 2)
        ))
        self.play(FadeIn(linha))
        self.wait(0.5)

        # Agrupamento e movimento animado para o canto superior esquerdo
        grupo_titulo = VGroup(titulo, linha)
        self.play(grupo_titulo.animate.scale(0.9).to_corner(UL))
        self.wait(1)

        # Enunciado
        enunciado = Paragraph(
            r"Em uma lanchonete, os sanduíches são montados a partir das escolhas do próprio cliente.",
            r"A lanchonete oferece cinco variedades de pães, quatro tipos de carnes e cinco opções de saladas.",
            r"De quantas maneiras diferentes um cliente pode montar um sanduíche escolhendo",
            r"uma variedade de pão, uma de carne e uma de salada?",
            alignment="left",
            line_spacing=0.8,
            font="CMU Serif"
        ).scale(0.35).next_to(grupo_titulo, DOWN, aligned_edge=LEFT)

        self.play(Write(enunciado, run_time=2))
        self.wait(0.5)

        # Fórmula (Princípio Fundamental da Contagem)
        formula = MathTex(r"n = \text{pães} \times \text{carnes} \times \text{saladas}", font_size=30)
        formula.next_to(enunciado, DOWN, buff=0.8).align_to(enunciado, LEFT)
        self.play(Write(formula))
        self.wait(0.5)

        # Alternativas
        alternativas = VGroup(
            Tex("A) 14", font_size=24),
            Tex("B) 42", font_size=24),
            Tex("C) 100", font_size=24),
            Tex("D) 264", font_size=24),
            Tex("E) 364", font_size=24),
        ).arrange(DOWN, aligned_edge=LEFT, buff=0.3)

        alternativas.next_to(formula, DOWN, buff=0.8).align_to(formula, LEFT)
        self.play(Write(alternativas))
        self.wait(1)

        # Resolução passo a passo
        passo1 = MathTex(r"\text{Pães: } 5", font_size=26)
        passo2 = MathTex(r"\text{Carnes: } 4", font_size=26)
        passo3 = MathTex(r"\text{Saladas: } 5", font_size=26)

        # Fórmula (Princípio Fundamental da Contagem)
        formula = MathTex(r"n = \text{pães} \times \text{carnes} \times \text{saladas}", font_size=30)
        formula.next_to(enunciado, DOWN, buff=0.8).align_to(enunciado, LEFT)
        self.play(Write(formula))
        self.wait(0.5)

        
        passo4 = MathTex(r"n = 5 \cdot 4 \cdot 5", font_size=26)
        passo5 = MathTex(r"n = 100", font_size=28, color=YELLOW)

        passos = VGroup(passo1, passo2, passo3, passo4, passo5).arrange(
            DOWN, aligned_edge=LEFT, buff=0.3
        )
        passos.shift(RIGHT * 2.8 + DOWN * 1)

        for p in passos:
            self.play(Write(p))
            self.wait(0.4)

        # Destacar alternativa correta (letra C)
        alternativa_correta = alternativas[2]
        self.play(Circumscribe(alternativa_correta, color=YELLOW))
        self.play(Indicate(alternativa_correta, color=YELLOW))
        self.play(alternativa_correta.animate.set_color(YELLOW))
        self.wait(2)