Muitos programas precisam de valores (aparentemente) aleatórios para simular eventos do mundo real.
Exemplos comuns e familiares incluem:
('H', 'T').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".
Importante: Este Conceito não aborda números aleatórios criptograficamente seguros, que são um desafio muito mais difícil.
No entanto, bibliotecas bem concebidas como o módulo Random da biblioteca padrão do Julia são rápidas, flexíveis e produzem resultados suficientemente bons para a maioria das aplicações em modelação, simulação e jogos.
O Julia divide a funcionalidade de aleatoriedade em vários locais:
Base, que estão sempre disponíveis.Random.O Random faz parte da biblioteca padrão e provavelmente já vem pré-instalado, mas vais precisar de adicionar using Random no topo do teu programa para trazer o seu conteúdo para o espaço de nomes.
rand()
O que esta função faz depende dos argumentos que lhe dás. Há muitas opções.
Sem argumentos, gera um número de vírgula flutuante entre 0 (inclusive) e 1.
Esta é uma distribuição uniform com todos os valores igualmente prováveis, como discutido na secção Trabalhar com distribuições, abaixo.
Um único argumento inteiro gera um vetor desse comprimento.
julia> rand()
0.10261774967264703
julia> rand(5)
5-element Vector{Float64}:
0.24134501977563894
0.5664193284851202
0.9804412082089355
0.6229551330613335
0.47589221741904664
Para um intervalo diferente, basta deslocar e escalar o resultado de forma adequada.
O exemplo abaixo usa broadcasting para a subtração, abordado no Conceito Operações com Vetores.
O .- simplesmente aplica esta aritmética a cada elemento do vetor.
# numbers between -1.0 and +1.0
julia> (rand(5) .- 0.5) * 2
5-element Vector{Float64}:
-0.5303906759076336
0.9635682226775855
-0.048823697086981754
0.465842804648374
0.9880834344780736
Com um tipo como único argumento, rand usa typemin e typemax como limites.
Provavelmente não é o que queres!
Para inteiros aleatórios, podemos fornecer um intervalo e, opcionalmente, quantos valores gerar.
julia> rand(Int64)
-9159538335234594326 # not very useful
julia> rand(1:10, 5)
5-element Vector{Int64}:
1
1
1
4
7
No exemplo rand(1:10, 5) acima, repara que há valores repetidos (por coincidência), porque cada escolha é independente.
Isto é "amostragem com reposição", discutida em mais detalhe abaixo.
Para valores de vírgula flutuante num intervalo, normalmente vais precisar de especificar um passo. Caso contrário, o passo será 1,0 por omissão, o que raramente é útil.
julia> rand(2.4:0.01:3.2, 4)
4-element Vector{Float64}:
3.19
2.53
3.14
3.13
Em alternativa, fornece um array ou uma tupla, e rand devolve uma entrada aleatória:
julia> rand([4, 9, 16, 25])
16
# coin flip
julia> rand(['H', 'T'])
'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
# mixed types in tuple
julia> rand( (1, 3.2, "name"), 2 )
2-element Vector{Any}:
1
"name"
Imagina que temos um saco com 3 bolas vermelhas e 4 bolas verdes, e retiramos aleatoriamente uma bola do saco. Para obter uma segunda bola, há duas possibilidades:
O cenário 1 é com reposição, o cenário 2 é sem reposição, e dão resultados diferentes.
Para simular amostragem sem reposição em Julia, há algumas opções.
A mais simples (e, no Exercism, a única opção), usa Random.shuffle() para colocar as entradas numa ordem aleatória e depois usa os primeiros n elementos.
Isto é adequado para problemas pequenos, mas pode não escalar bem para coleções grandes: shuffle precisa de gerar o array completo, mesmo que só queiras uma pequena fração dele.
Para fazer amostragem com reposição "em condições", instala o pacote StatsBase.jl.
O pacote fornece a função sample() com uma gama completa de opções.
Podemos razoavelmente esperar que funcionalidades semelhantes sejam adicionadas ao Random numa versão futura, para fazerem parte da biblioteca padrão (os exemplos de código neste documento foram testados com o Julia 1.11).
Até agora, concentrámo-nos em casos em que todos os resultados são igualmente prováveis.
Por exemplo, rand(1:100) tem a mesma probabilidade de dar qualquer inteiro de 1 a 100.
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".
A função rand() descrita acima é usada quando todas as probabilidades são iguais.
Isto chama-se uma distribuição [uniform][uniform-distribution].
Também chamada distribuição "normal" ou curva "em forma de sino", é uma forma muito comum de descrever imprecisão em valores medidos.
Por exemplo, supõe que a fábrica onde trabalhas acabou de comprar 10 000 parafusos que deveriam ser idênticos.
Queres preparar o robô da fábrica para os manusear, por isso pesas uma amostra de 100 e descobres que têm um peso médio (ou mean) de 4,731 g.
É extremamente improvável que isto signifique que todos pesam exatamente 4,731 g.
Talvez descubras que os valores variam entre 4,627 e 4,794 g, mas se agrupam em torno de 4,731 g.
Esta é a Gaussian distribution, para a qual as probabilidades atingem o máximo na média e diminuem simetricamente de ambos os lados (daí "em forma de sino").
Para simular isto em software, precisamos de alguma forma de especificar a largura da curva (normalmente, parafusos caros agrupam-se mais estreitamente em torno da média do que parafusos baratos!).
Por convenção, isto faz-se com o standard deviation: valores pequenos para uma curva estreita e afiada, 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 "o bosão de Higgs foi detetado com uma confiança de 5 sigma", tais comentários dizem respeito ao desvio padrão.
Haverá mais a dizer sobre isto no Conceito Statistics.
randn()
Abreviatura de "random normal", é semelhante à variante de vírgula flutuante de rand(), exceto que os valores são distribuídos como uma gaussiana com média 0 e desvio padrão 1.
Novamente, podes querer escalar a saída em bruto de randn para o desvio padrão e deslocá-la para a média.
O exemplo abaixo converte para média 30 e desvio padrão 5.
julia> raw = randn(5)
5-element Vector{Float64}:
3.0762588867281475
1.5101100620253902
-0.5914858221637778
0.684175554069735
-0.8416433926114673
julia> raw * 5 .+ 30
5-element Vector{Float64}:
45.38129443364074
37.55055031012695
27.04257088918111
33.420877770348675
25.791783036942665
É difícil perceber, olhando para a saída, que a saída em bruto se agrupa mais perto de zero do que no caso de uma distribuição uniforme. Se duvidares, gera 1000 ou mais e traça-os num gráfico para tornar isso mais óbvio.
Random
Este módulo contém o nível seguinte de funcionalidades, omitido do Base para ajudar a minimizar o tamanho da configuração predefinida do Julia.
O Random complementa rand e randn no Base com versões mutáveis, rand! e randn!.
Uma adição útil é randstring, que gera uma string de um determinado comprimento.
Por omissão, usa letras maiúsculas e minúsculas mais os algarismos 0 a 9, mas outras escolhas podem ser especificadas.
julia> using Random
julia> randstring(20)
"BoJnIxrS33pJiWggXZQV"
Além disso, há uma função bitrand para gerar um BitArray aleatório de um comprimento especificado.
julia> bitrand(5)
julia> bitrand(5)
5-element BitVector:
1
1
0
0
1
Para baralhar aleatoriamente entradas num Vector, temos shuffle; também shuffle! para modificar o vetor de entrada no local.
julia> v = ['A', '1', '2', 'J', 'Q', 'K'];
julia> shuffle(v)
6-element Vector{Char}:
'K': ASCII/Unicode U+004B (category Lu: Letter, uppercase)
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)
'2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
'Q': ASCII/Unicode U+0051 (category Lu: Letter, uppercase)
# shuffles are random:
julia> shuffle(v)
6-element Vector{Char}:
'2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
'K': ASCII/Unicode U+004B (category Lu: Letter, uppercase)
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'Q': ASCII/Unicode U+0051 (category Lu: Letter, uppercase)
'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
Por vezes, é útil ter antes os índices baralhados.
Para isso, usa randperm(n), em que n é o comprimento da sequência.
julia> randperm(6)
6-element Vector{Int64}:
6
2
4
1
3
5
Na prática, o exemplo acima dá os mesmos resultados que shuffle(1:6).
Funções relacionadas incluem randsubseq para extrair entradas com probabilidade fixa, e randcycle para permutações cíclicas.
Estas requerem conhecimentos especializados, por isso consulta a documentação se tiveres interesse nelas.
Vários algoritmos de geração de números aleatórios (RNG) estão incorporados no Random como padrão, e qualquer pessoa com competências matemáticas adequadas pode adicionar mais.
Estas coisas estão muito além do âmbito deste documento!
Uma razão mais comum para trabalhar com RNGs é especificar uma seed, que tem o efeito de tornar a sequência de saídas "aleatórias" reproduzível de uma execução para a seguinte.
Essa reprodutibilidade não é apropriada em código de produção, mas pode ajudar em testes e depuração.
Fora do Exercism, há muitos pacotes instaláveis relacionados com aleatoriedade, probabilidade e estatística.
Para mais informações, consulta o Conceito Statistics.
StatsBase.jl
A maioria das funções StatsBase é bastante técnica e não é relevante para este documento.
A exceção é StatsBase.sample, que fornece uma implementação completa de amostragem com ou sem reposição (ver uma secção anterior, acima).
Há também funções para amostragem ponderada (não uniforme).
Distributions.jl
As distribuições uniform e normal (ou gaussiana) foram descritas acima.
O módulo Random também contém randexp para amostrar da Distribuição Exponencial, que está relacionada com a (muito comum) Distribuição de Poisson.
Para uma gama muito mais ampla de opções, existe o pacote Distributions.jl para quem tem formação adequada em estatística.