Pular para o conteúdo

    Capítulo 16, Básico

    Números aleatórios com Generator

    Embaralhar dados, sorteá-los, gerar um conjunto sintético para treinar: tudo isso exige aleatoriedade **controlada**. E o NumPy tem hoje uma forma moderna de fazê-lo, diferente da que muitos tutoriais ainda ensinam.

    Código deste capítulo: básico/cap16_aleatorios.py

    A forma moderna: `default_rng`

    Você cria um gerador com uma semente (seed) e pede números a ele. A mesma semente produz sempre a mesma sequência, e é isso que torna um resultado reproduzível:

    básico/cap16_aleatorios.pylinhas 10 a 16
    import numpy as np
    
    rng = np.random.default_rng(42)
    print(rng.random(3))
    print(rng.integers(0, 10, size=5))
    print(rng.normal(loc=0, scale=1, size=3))
    print(rng.choice([10, 20, 30], size=2, replace=False))
    
    Saída
    [0.77395605 0.43887844 0.85859792]
    [0 6 2 0 5]
    [ 0.1278404  -0.31624259 -0.01680116]
    [30 20]
    
    básico/cap16_aleatorios.pylinhas 18 a 20
    a = np.random.default_rng(7).random(3)
    b = np.random.default_rng(7).random(3)
    print(np.array_equal(a, b))
    
    Saída
    True
    
    MétodoGera
    rng.random(n)Decimais uniformes em [0, 1)
    rng.integers(a, b, size)Inteiros em [a, b)
    rng.normal(media, desvio, size)Distribuição normal (a curva do sino)
    rng.uniform(a, b, size)Decimais uniformes em [a, b)
    rng.choice(opcoes, size, replace, p)Sorteio de valores existentes, com ou sem reposição
    rng.shuffle(a), rng.permutation(a)Embaralhar
    rng.binomial, rng.poisson, rng.exponentialOutras distribuições

    Por que não `np.random.seed`

    A API antiga (np.random.seed(42) seguida de np.random.rand(3)) ainda funciona e aparece em muito material, inclusive em cursos, mas usa um estado global escondido no módulo:

    básico/cap16_aleatorios.pylinhas 25 a 26
    np.random.seed(42)
    print(np.random.rand(3))
    
    Saída
    [0.37454012 0.95071431 0.73199394]
    

    O problema do estado global é que qualquer código, em qualquer lugar, pode consumir números dele e mudar a sua sequência sem você perceber, o que quebra a reprodutibilidade e torna os testes frágeis. Com o Generator, o estado mora em um objeto que você controla. E, por isso, funções que usam aleatoriedade devem receber o gerador como parâmetro:

    básico/cap16_aleatorios.pylinhas 28 a 32
    def sorteio(n, rng):
        return rng.integers(1, 7, size=n)
    
    
    print(sorteio(5, np.random.default_rng(1)).tolist())
    
    Saída
    [3, 4, 5, 6, 1]
    

    Com essa assinatura, o teste da função passa um gerador com semente fixa e verifica um resultado exato, e a produção passa um gerador qualquer.

    A garantia de reprodução

    A mesma semente reproduz os mesmos números na mesma versão do NumPy. Entre versões, a garantia do Generator é mais fraca do que a da API antiga, que o projeto congelou. Para um experimento que precisa ser reproduzido anos depois, registre também a versão da biblioteca.

    Embaralhar: no lugar ou em cópia

    O rng.shuffle embaralha o próprio array e devolve None. Escrever x = rng.shuffle(x) joga fora o array. Para obter um embaralhado e manter o original, use permutation:

    básico/cap16_aleatorios.pylinhas 37 a 43
    vetor = np.arange(1, 6)
    resultado = rng.shuffle(vetor)
    print(resultado, sorted(vetor.tolist()))
    
    original = np.arange(1, 6)
    embaralhado = rng.permutation(original)
    print(original, sorted(embaralhado.tolist()))
    
    Saída
    None [1, 2, 3, 4, 5]
    [1 2 3 4 5] [1, 2, 3, 4, 5]
    

    Gerar um conjunto sintético

    Dados sintéticos com características conhecidas permitem testar uma análise sabendo a resposta. Aqui, mil alturas com média 170 e desvio 10: a média amostral fica perto de 170, mas não exatamente nela, porque é uma amostra:

    básico/cap16_aleatorios.pylinhas 48 a 52
    alturas = rng.normal(170, 10, size=1000)
    print(abs(alturas.mean() - 170) < 1, abs(alturas.std() - 10) < 1)
    
    sorteados = rng.choice(["cara", "coroa"], size=1000, p=[0.9, 0.1])
    print((sorteados == "cara").mean() > 0.8)
    
    Saída
    True True
    True
    

    Exercício 1

    Simular dados

    Escreva simular_dados(n, rng) que jogue um dado de 6 faces n vezes e devolva quantas vezes saiu cada face (um array com 6 contagens), com np.bincount.