Percursos
/
Python
Python
/
Programa
/
Aleatoriedade
Al

Aleatoriedade em Python

{one: "1 exercício", many: "%{count} exercícios", other: "%{count} exercícios"}

Sobre Aleatoriedade

Muitos programas precisam de valores aleatórios (aparentemente) para simular acontecimentos do mundo real.

Exemplos comuns e familiares incluem:

  • Lançamento de uma moeda ao ar: um valor aleatório de ('Heads', 'Tails').
  • Lançamento de um dado: um inteiro aleatório de 1 a 6.
  • Baralhar um baralho de cartas: uma ordenação aleatória de uma lista de cartas.

Gerar valores verdadeiramente aleatórios com um computador é um desafio técnico surpreendentemente difícil, por isso podes ver estes resultados referidos como "pseudoaleatórios".

Na prática, uma biblioteca bem concebida como o módulo random da biblioteca padrão do Python é rápida, flexível e dá resultados suficientemente bons para a maioria das aplicações em modelação, simulação e jogos.

O resto desta página vai listar algumas das funções mais comuns de random. Encorajamos-te a explorar a documentação completa de random, pois há muitas mais opções do que as que abordamos aqui.

Caution

O módulo random NÃO deve ser usado para aplicações de segurança ou criptografia.

Em vez disso, o Python disponibiliza o módulo secrets. Este é especialmente otimizado para segurança criptográfica. Alguns dos problemas anteriores e das razões para a criação do módulo secrets podem ser encontrados em PEP 506.

Importar

Antes de poderes utilizar as ferramentas do módulo random, tens primeiro de o importar:

>>> import random

# Choose random integer from a range
>>> random.randrange(1000)
360

>>> random.randrange(-1, 500)
228

>>> random.randrange(-10, 11, 2)
-8

# Choose random integer between two values (inclusive)
>>> random.randint(5, 25)
22

Para evitares escrever o nome do módulo, podes importar funções específicas pelo nome:

>>> from random import choice, choices

# Using choice() to pick Heads or Tails 10 times
>>> tosses = []
... for side in range(10):
...    tosses.append(choice(['H', 'T']))    

>>> print(tosses)
['H', 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'H']


# Using choices() to pick Heads or Tails 8 times
>>> picks = []
... picks.extend(choices(['H', 'T'], k=8))
... print(picks)
['T', 'H', 'H', 'T', 'H', 'H', 'T', 'T']

Criar inteiros aleatórios

A função randrange() tem três formas, para selecionar um valor aleatório de range(start, stop, step):

  1. randrange(stop) dá um inteiro n tal que 0 <= n < stop
  2. randrange(start, stop) dá um inteiro n tal que start <= n < stop
  3. randrange(start, stop, step) dá um inteiro n tal que start <= n < stop e n está na sequência start, start + step, start + 2*step...

No caso comum em que step == 1, a função randint(a, b) pode ser mais conveniente e legível. Os resultados possíveis de randint() incluem o limite superior, por isso randint(a, b) é o mesmo que usar randrange(a, b+1):

>>> import random

# Select one number at random from the range 0, 499
>>> random.randrange(500)
219

# Select 10 numbers at random between 0 and 9 two steps apart.
>>> numbers = []
... for integer in range(10):
...    numbers.append(random.randrange(0, 10, 2))

>>> print(numbers)
[2, 8, 4, 0, 4, 2, 6, 6, 8, 8]

# roll a die
>>> random.randint(1, 6)  
4

Trabalhar com sequências

As funções nesta secção partem do princípio de que estás a começar a partir de uma sequência ou de outro contentor.

Normalmente será uma list ou, com algumas limitações, um tuple ou um set (um tuple é imutável e um set não tem ordem).

choice() e choices()

A função choice() devolve uma entrada escolhida aleatoriamente de uma determinada sequência. No caso mais simples, pode ser o lançamento de uma moeda ao ar:

# This will pick one of the two values in the list at random 5 separate times 
>>> [random.choice(['H', 'T']) for _ in range(5)]
['T', 'H', 'H', 'T', 'H']

Podemos fazer essencialmente o mesmo com a função choices(), fornecendo um argumento nomeado com o comprimento da lista:

>>> random.choices(['H', 'T'], k=5)
['T', 'H', 'T', 'H', 'H']

Nos exemplos acima, assumimos uma moeda equilibrada, com igual probabilidade de sair cara ou coroa, mas também é possível especificar pesos. Por exemplo, se um saco contiver 10 bolas vermelhas e 15 bolas verdes, e quisermos tirar uma ao acaso:

>>> random.choices(['red', 'green'], [10, 15])
['red']

sample()

O exemplo de choices() acima pressupõe aquilo a que os estatísticos chamam "amostragem com reposição". Cada escolha não tem efeito na probabilidade das escolhas futuras, e a distribuição das escolhas potenciais mantém-se igual de escolha para escolha.

No exemplo das bolas vermelhas e verdes: após cada escolha, devolvemos a bola ao saco e agitamos bem antes da escolha seguinte. Isto contrasta com uma situação em que tiramos uma bola vermelha e ela permanece fora. Não devolver a bola significa que há agora menos bolas vermelhas no saco, e a próxima escolha tem agora uma probabilidade menor de ser vermelha.

Para simular esta "amostragem sem reposição", o módulo random disponibiliza a função sample(). A sintaxe de sample() é semelhante à de choices(), exceto que acrescenta um parâmetro nomeado counts:

>>> random.sample(['red', 'green'], counts=[10, 15], k=10)
['green', 'green', 'green', 'green', 'green', 'red', 'red', 'red', 'red', 'green']

As amostras são devolvidas pela ordem em que foram escolhidas.

shuffle()

Tanto choices() como sample() devolvem novas listas quando k > 1. Em contrapartida, shuffle() baralha a ordem de uma lista no local, e a ordem original perde-se:

>>> my_list = [1, 2, 3, 4, 5]
>>> random.shuffle(my_list)
>>> my_list
[4, 1, 5, 2, 3]

Trabalhar com distribuições

Até agora, concentrámo-nos em casos em que todos os resultados são igualmente prováveis. Por exemplo, random.randrange(100) tem igual probabilidade de dar qualquer inteiro de 0 a 99.

Muitas situações do mundo real são muito menos simples do que isto. Como resultado, os estatísticos criaram uma grande variedade de distributions para descrever matematicamente resultados do "mundo real".

Distribuições uniformes

Para inteiros, randrange() e randint() são usados quando todas as probabilidades são iguais. Isto chama-se uma distribuição uniform.

Existem equivalentes em vírgula flutuante para randrange() e randint().

random() dá um valor float x tal que 0.0 <= x < 1.0.

uniform(a, b) dá x tal que a <= x <= b.

>>> [round(random.random(), 3) for _ in range(5)]
[0.876, 0.084, 0.483, 0.22, 0.863]

>>> [round(random.uniform(2, 5), 3) for _ in range(5)]
[2.798, 2.539, 3.779, 3.363, 4.33]

Distribuição gaussiana

Também chamada distribuição "normal" ou curva "em forma de sino", esta é uma forma muito comum de descrever a imprecisão em valores medidos.

Por exemplo, supõe que a fábrica onde trabalhas acabou de comprar 10 000 parafusos que deviam ser idênticos. Queres configurar o robô da fábrica para lidar com eles, por isso pesas uma amostra de 100 e concluis que têm um peso médio (ou mean) de 4,731 g. É extremamente improvável que isto signifique que pesam todos exatamente 4,731 g. Talvez concluas que os valores variam entre 4,627 e 4,794 g, mas se agrupam em torno de 4,731 g.

Esta é a Gaussian distribution, na qual as probabilidades atingem o máximo na média e diminuem simetricamente para ambos os lados (daí "em forma de sino"). Para simular isto em software, precisamos de alguma forma de especificar a largura da curva (normalmente, os parafusos caros agrupam-se mais estreitamente em torno da média do que os baratos! ).

Por convenção, isto faz-se com o standard deviation: valores pequenos para uma curva acentuada e estreita, valores grandes para uma curva baixa e larga. Os matemáticos adoram letras gregas, por isso usamos μ ('mu') para representar a média e σ ('sigma') para representar o desvio padrão. Assim, se leres que "95% dos valores estão dentro de 2σ de μ" ou que "o bosão de Higgs foi detetado com uma confiança de 5 sigma", comentários como estes dizem respeito ao desvio padrão.

>>> mu = 4.731
>>> sigma = 0.316
>>> [round(random.gauss(mu, sigma), 3) for _ in range(5)]
[4.72, 4.957, 4.64, 4.556, 4.968]
Editar via GitHub A ligação abre numa nova janela ou separador