É possível gerar números aleatórios reais com uma precisão específica e em um intervalo específico usando o Integer Random Generator $RANDOM? Por exemplo, como podemos gerar um número real com precisão 4 entre 0 e 1?
0.1234
0.0309
0.9001
0.0000
1.0000
Uma solução simples:
printf "%d04.%d04\n" $RANDOM $RANDOM
Isso produzirá
n
números aleatórios (dez no exemplo) no intervalo [0,1) com quatro dígitos decimais. Ele usa arand()
função inawk
(não no padrão,awk
mas implementada pelas implementações mais comunsawk
) que retorna um valor aleatório nesse intervalo. O gerador de números aleatórios é propagado pela$RANDOM
variável do shell.Quando um
awk
programa possui apenasBEGIN
blocos (e nenhum outro bloco de código),awk
não tentará ler a entrada de seu fluxo de entrada padrão.Em qualquer sistema OpenBSD (ou sistema que tenha o mesmo
jot
utilitário , originalmente no 4.2BSD), o seguinte irá gerar 10 números aleatórios conforme especificado:Conforme apontado em outra resposta, existem outros utilitários que você pode usar para gerar números aleatórios. Nesta resposta, limito meus recursos a
$RANDOM
, e algumas funções aritméticas básicas.Para números de ponto flutuante, tente algo como
Isso lhe dará a melhor precisão porque
$RANDOM
gera apenas números entre 0 e 32767. (incluindo 32767!) Mas também quebrei minha regra sobre o uso de funções aritméticas básicas invocandobc
.Mas antes de prosseguir, gostaria de examinar dois problemas de precisão e intervalo para números de ponto flutuante. Depois disso, analisarei a geração de um intervalo de números inteiros (e se você puder gerar números inteiros, poderá dividi-los posteriormente para obter um decimal, se desejar, usando quaisquer utilitários de sua preferência para fazer isso).
Precisão
Tomando a abordagem de
$RANDOM/32768
, uma vez que$RANDOM
gera valores de 0 a 32767, o resultado de$RANDOM/32768
também será um número finito de valores. Em outras palavras, ainda é uma variável aleatória discreta (e com um computador você nunca conseguirá escapar desse fato). Com isso em mente, você pode obter algum grau de precisão usandoprintf
.Se você deseja uma cobertura mais fina do intervalo, pode começar a pensar na base 32768. Assim, em teoria,
$RANDOM + $RANDOM*32768
deve fornecer uma distribuição uniforme entre 0 e 1.073.741.823. Mas, duvido que a linha de comando lide com essa precisão muito bem. Alguns pontos relacionados a este caso específico:$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )
. As duas ocorrências de$RANDOM
são realmente dois eventos diferentes.$RANDOM
é gerado para saber se chamá-lo duas vezes assim realmente gerará dois eventos aleatórios independentes.Variar
Vamos considerar apenas
$RANDOM/32768
. Se você quiser um número em um intervalo, digamos[a,b)
, entãovai pousar você no intervalo desejado.
Geração de valores inteiros
Primeiro, considere gerar números aleatórios entre
[0,b)
ondeb
é menor que32768
. Considere o produtoq*b
, ondeq
é a parte inteira de32768/b
. Então o que você pode fazer é gerar um número aleatório entre 0 e 32767, mas descartar aqueles que forem maiores ou iguais aq*b
. Ligue para o número assim geradoG
. EntãoG
cairá no intervalo de 0 aq*b
, e sua distribuição será uniforme. Agora, aplique a aritmética modular para obter esse valor reduzido para o intervalo desejado:Observe, gerando aleatoriamente um número da seguinte maneira
não criará uma distribuição uniforme, a menos que seja
b
apenas um dos divisores de32768
.Escrevendo um script bash para isso
Calcular
q*b
como descrito acima soa como uma dor. Mas realmente não é. Você pode obtê-lo da seguinte maneira:No Bash, você pode obter isso com
O código a seguir gerará um número aleatório no intervalo
0..b
(não incluindob
).b=$1
Termo aditivo
Tecnicamente, há poucos motivos para trabalhar com
O seguinte realizará a mesma coisa
Dá muito mais trabalho, mas os computadores são rápidos.
Gerando um inteiro em um intervalo maior
Vou deixar você descobrir isso. É preciso ter cuidado e, em algum momento, você terá que levar em consideração as limitações de memória do computador ao lidar com operações aritméticas.
Nota final
A resposta aceita não criará um número aleatório uniformemente de 0 a 1.
Para ver isso, tente o seguinte
Para uma distribuição verdadeiramente uniforme,
[0,1)
você deve ver uma média próxima a0.500
.Mas, como você pode ver executando o snippet acima, você obterá algo como
314.432
ou322.619
. Como são 1000 números, a média disso é.322
. A verdadeira média para esta sequência de números gerados é.316362
Você pode obter essa média real usando o script perl
Estou adicionando números inteiros aqui para ajudá-lo a ver como essa abordagem de uso
.$RANDOM
não está fazendo o que você provavelmente deseja. Em outras palavras, pense em quais números inteiros estão sendo gerados e quais estão totalmente perdidos. Um grande número é ignorado; alguns são duplicados.Em sistemas onde o printf do shell é capaz de entender o
%a
formato (bash ksh zsh , etc.[0,1)
)Você pode até usar mais dígitos combinando mais chamadas para
$RANDOM
(de 0,000000001 a 0,999999999)O algoritmo "$RANDOM" interno (para o shell) é baseado em um registrador de deslocamento de realimentação linear (LFSR). Esses não são Geradores de Números Pseudo Aleatórios Criptograficamente Seguros (CSPRNGs). Uma opção melhor é usar bytes do
/dev/urandom
dispositivo. Isso exigirá a chamada para dump externo octal ou hexadecimal.Uma solução muito simples (mas não uniforme) para obter um float é:
Uma maneira de torná-lo uniforme no intervalo
[0,1)
(não incluindo 1):Usar
$(( ( RANDOM % N ) + MIN ))
Substitua
N
pelo número MAX e MIN pelo número mínimo que você deseja gerar. (N
como MAX é exclusivo, coloqueN+1
MAX, MIN inclusive).Ou você pode usar
$(shuf -i MIN-MAX -n 1)
em vez disso.de
man shuf
:O
-n 1
aquishuf
significa gerar apenas um número aleatório.Isso irá gerar números aleatórios entre 0~9999 com zeros à esquerda usando
printf
(no resultado, o número 1 é exclusivo).Na festa
onde
1/10000
está sua precisão aleatória e4
dígitos sua precisão de saídazsh
possui umarand48()
função aritmética (envoltório para aerand48()
função padrão) em seuzsh/mathfunc
módulo:Enquanto
$RANDOM
é de 15 bits, pseudo-aleatório e reproduzível,bash
5.1+ tem um número inteiro de 32 bits mais seguro$SRANDOM
, baseado em fontes verdadeiramente aleatórias quando disponíveis. Ele não suporta aritmética de ponto flutuante, mas pelo menos você pode usá-lo para semearawk
o gerador pseudo-aleatório (que, por padrão, usa o resultado muito previsível detime()
):(lembre-se de que ainda são apenas 32 bits de entropia e
awk
gera geração pseudo-aleatória determinística com base nessa semente)