Como apontado em outra resposta, existem outros utilitários que você pode usar para gerar números aleatórios. Nessa resposta, limito meus recursos a $RANDOM e algumas funções aritméticas básicas.
Para números de ponto flutuante, tente algo como
printf '%s\n' $(echo "scale=8; $RANDOM/32768" | bc )
Isso vai te dar a melhor precisão porque $RANDOM só gera números entre 0 e 32767. (incluindo 32767!) Mas, eu também quebrei minha regra sobre o uso de funções aritméticas básicas invocando bc .
Mas antes de prosseguir, gostaria de analisar duas questões precisão e intervalo para números de ponto flutuante. Depois disso, eu vou olhar para gerar um intervalo de inteiros (e se você pode gerar inteiros, você pode depois dividi-los para obter um decimal, se você quiser usar quaisquer utilitários que você preferir para realizar 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á finitamente muitos valores. Em outras palavras, ainda é uma variável aleatória discreta (e com um computador você nunca conseguirá se afastar desse fato). Com isso em mente, você pode obter algum grau de precisão usando printf .
Se você quer uma cobertura mais fina do intervalo, você poderia começar a pensar na base 32768. Então, teoricamente, $RANDOM + $RANDOM*32768 deve fornecer uma distribuição uniforme entre 0 e 1.073.741.823. Mas tenho dúvidas de que a linha de comando irá lidar com essa precisão muito bem. Alguns pontos relacionados a este caso particular:
- A soma de duas variáveis aleatórias independentes, uniformemente distribuídas, geralmente não uniformes. Neste caso, pelo menos teoricamente falando (ver terceiro ponto), eles são.
- Não pense que você pode simplificar
$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 ) . As duas ocorrências de $RANDOM são realmente dois eventos diferentes.
- Eu não sei o suficiente sobre como
$RANDOM é gerado para saber se chamá-lo duas vezes como isto irá realmente gerar dois eventos aleatórios independentes.
Intervalo
Vamos considerar apenas $RANDOM/32768 . Se você quiser um número em um intervalo, digamos [a,b) , então
$RANDOM/32768*(b-a) + a
vai pousar na faixa desejada.
Geração de valores inteiros
Primeiro, considere gerar números aleatórios entre [0,b) , em que b é menor que 32768 . Considere o produto q*b , em que q é a parte inteira de 32768/b . Então, o que você pode fazer é gerar um número aleatório entre 0 e 32767, mas descartar aqueles que são maiores ou iguais a q*b . Ligue para o número assim gerado G . Então G cairá no intervalo de 0 a q*b e sua distribuição será uniforme. Agora, aplique aritmética modular para obter esse valor reduzido no intervalo desejado:
G % b
Note, gerando aleatoriamente um número da seguinte forma
$RANDOM % b
não criará uma distribuição uniforme, a menos que b simplesmente seja um dos divisores de 32768 .
Escrevendo um script bash para isso
Calcular q*b como descrito acima parece uma dor. Mas realmente não é. Você pode obtê-lo da seguinte maneira:
q*b = 32768 - ( 32768 % b )
No Bash, você pode conseguir isso com
$((32768 - $((32768 % b)) ))
O código a seguir gerará um número aleatório no intervalo 0..b (não incluindo b ). b=$1
m=$((32768 - $((32768 % $1)) ))
a=$RANDOM
while (( $a > $m ));
do
a=$RANDOM
done
a=$(($a % $1))
printf "$a\n"
Adendo
Tecnicamente, há poucos motivos para trabalhar com
m=$((32768 - $((32768 % $1)) ))
O seguinte irá realizar a mesma coisa
a=$RANDOM
while (( $a > $1 ));
do
a=$RANDOM
done
printf "$a\n"
É muito mais trabalho, mas os computadores são rápidos.
Gerando um Integer em um intervalo maior
Eu vou deixar você descobrir isso. Cuidados devem ser tomados, e em algum momento você terá que levar em consideração as limitações de memória do computador no manuseio de operações aritméticas.
Nota final
A resposta aceita não criará um número aleatório uniformemente entre 0 e 1.
Para ver isso,
tente o seguinte
$ for i in {1..1000}; do echo .$RANDOM; done | awk '{ a += $1 } END { print a }'
Para uma distribuição verdadeiramente uniforme sobre [0,1) , você deve ver uma média próxima a 0.500 .
Mas, como você pode ver, executando o snippet acima, você receberá algo como 314.432 ou 322.619 . Como são 1000 números, a média disso é .322 . A média real para essa sequência de números gerados é .316362
Você pode obter essa média real usando o script perl
perl -e '{ $i=0;
$s=0;
while ( $i<=32767 )
{
$j = sprintf "%.5f", ".$i";
$j =~ s/^0\.//;
print "$j\n";
$s += $j;
$i++
};
printf "%.5f\n", $s/32767;
}'
Estou adicionando inteiros aqui para ajudá-lo a ver como essa abordagem de usar .$RANDOM não está fazendo o que você mais deseja. Em outras palavras, pense em quais inteiros estão sendo gerados e quais são perdidos por completo. Um grande número é ignorado; alguns são duplicados.