É possível gerar números aleatórios reais com uma precisão específica e em um intervalo específico usando o Aleatório Inteiro Gerador $ 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
Comentários
- Especifique o que você quer dizer com ” números aleatórios reais “. Você está exigindo uma fonte de números aleatórios gerados por algo como o decaimento de partículas ou ficará feliz com um gerador pseudo-aleatório? A sua aplicação desses números tem significado criptográfico ou científico ou você apenas quer algo que ” pareça aleatório “.
- … ou você realmente quer dizer ” float ” ou ” flutuante número do ponto?
- Obrigado por comentar. Preciso de um gerador de números pseudoaleatórios para números de ponto flutuante com base em $ RANDOM.
- … para implementar algoritmos meta heurísticos em bash.
Resposta
awk -v n=10 -v seed="$RANDOM" "BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }"
Isso produzirá n
números aleatórios (dez no exemplo) no intervalo [0,1) com quatro dígitos decimais. Ele usa a função rand()
em awk
(não no padrão awk
, mas implementado pelos mais comuns awk
implementações) que retorna um valor aleatório nesse intervalo. O gerador de números aleatórios é propagado pela variável shell “s $RANDOM
.
Quando um programa awk
tem apenas BEGIN
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 em 4.2BSD), o seguinte irá gerar 10 números aleatórios conforme especificado:
jot -p 4 -r 10 0 1
Comentários
- Estritamente falando, desde a saída de
rand()
é um float dentro de [0,1), provavelmente não é ‘ t exatamente distribuído uniformemente quando arredondado para quatro dígitos decimais. Seria, se o flutuante tivesse precisão infinita, mas não é ‘ t: é ‘ provável que seja gerado a partir de bits aleatórios , portanto, há 2 ^ N valores diferentes e eles não ‘ t mapeiam uniformemente para um conjunto de 1000 valores. Mas, desde que essas flutuações pseudoaleatórias tenham bits suficientes e você ‘ não estiver fazendo nada realmente exato, você provavelmente ganhou ‘ t aviso.
Resposta
Como apontado em outra resposta, existem outros utilitários que você pode usar para gerar Números aleatórios. Nesta resposta, eu limitei 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 lhe dará a melhor precisão porque $RANDOM
só gera números entre 0 e 32.767. (incluindo 32.767!) 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 examinar duas questões precisão e intervalo para números de ponto flutuante. Depois disso, examinarei a geração de um intervalo de inteiros (e se você pode gerar inteiros, poderá dividi-los posteriormente para obter um decimal se desejar usar os utilitários de sua preferência para fazer isso.)
Precisão
Tomando a abordagem de $RANDOM/32768
, desde $RANDOM
gera valores de 0 a 32767, o resultado de $RANDOM/32768
da mesma forma terá muitos valores finitos. Em outras palavras, ainda é uma variável aleatória discreta (e com um computador você nunca conseguirá fugir desse fato). Com isso em mente, você pode obter algum grau de precisão usando printf
.
Se você quiser uma cobertura mais fina do intervalo, você poderia começar a pensar na base 32768. Portanto, em teoria $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 lidará com essa precisão muito bem. Alguns pontos relacionados a este caso particular:
- A soma de duas variáveis aleatórias independentes e uniformemente distribuídas não é geralmente uniforme. Neste caso, pelo menos teoricamente falando (veja o 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. - Não sei o suficiente sobre como
$RANDOM
é gerado para saber se chamá-lo duas vezes desta forma realmente gerará dois eventos aleatórios independentes.
Intervalo
Vamos considerar apenas $RANDOM/32768
. Se você quiser um número em um intervalo, diga [a,b)
, então
$RANDOM/32768*(b-a) + a
o levará ao intervalo desejado .
Geração de valores inteiros
Primeiro, considere gerar números aleatórios entre [0,b)
onde b
é menor que 32768
. Considere o produto q*b
, onde q
é a parte inteira de 32768/b
. Então o que você pode fazer é gerar um número aleatório entre 0 e 32.767, mas descartar aqueles que são maiores ou iguais a q*b
. Ligue para o número gerado assim 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
Observe, gerando aleatoriamente um número como segue
$RANDOM % b
não criará uma distribuição uniforme, a menos que b
seja apenas um dos divisores de 32768
.
Escrevendo um script bash para este
Calculando q*b
conforme descrito acima, parece uma dor de cabeça. Mas realmente não é. Você pode obtê-lo da seguinte maneira:
q*b = 32768 - ( 32768 % b )
No Bash, você pode obter isso com
$((32768 - $((32768 % b)) ))
O código a seguir irá gerar um número aleatório no intervalo 0..b
(não inclui 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 resultará na mesma coisa
a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n"
Dá muito mais trabalho, mas os computadores são rápidos.
Gerando um número 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 acima de 0 a 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 em [0,1)
, você deve ver uma média próxima a 0.500
.
Mas como você pode ver ao executar o snippet acima, você obterá algo como 314.432
ou 322.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 verdadeira 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ê provavelmente deseja. Em outras palavras, pense em quais números inteiros estão sendo gerados e quais foram perdidos. Um grande número é ignorado; alguns são duplicados.
Resposta
Em sistemas em que shell “s printf é capaz de entender o (bash ksh zsh, etc.) e, portanto, é capaz de realizar uma mudança de base interna (hex -> dec) (uniforme no intervalo [0,1)
de 0,00003 para 0,99997):
printf "%.5f\n" "$(printf "0x0.%04xp1" $RANDOM)"
Você pode até usar mais dígitos combinando mais chamadas para $RANDOM
(de 0,000000001 a 0,999999999)
printf "%.9f\n" "$(printf "0x0.%08xp2" $(( ($RANDOM<<15) + $RANDOM )))"
O algoritmo interno (para o shell) “$ RANDOM” é baseado em um registro de deslocamento de feedback linear (LFSR). Eles não são criptograficamente Geradores de números pseudo-aleatórios seguros (CSPRNGs). Uma opção melhor é usar bytes do dispositivo /dev/urandom
. Isso exigirá a chamada para dump octal ou hexadecimal externo.
$ printf "%.19f\n" "0x0.$(od -N 8 -An -tx1 /dev/urandom | tr -d " ")" 0.7532810412812978029 $ printf "%.19f\n" "0x0.$(hexdump -n 8 -v -e ""%02x"" /dev/urandom)" 0.9453460825607180595
Uma solução muito simples (mas não uniforme) para obter um float é:
printf "0.%04d\n" $RANDOM
Uma maneira de torná-lo uniforme no intervalo [0,1)
(não incluindo 1):
while a=$RANDOM; ((a>29999)); do :; done; printf "0.%04d\n" "$((a%10000))"
Resposta
Use $(( ( RANDOM % N ) + MIN ))
Substitua N
com o número MAX e MIN com o número mínimo que você deseja gerar. (N
como MAX é exclusivo, coloque N+1
para ter MAX, MIN inclusive).
Ou você pode usar $(shuf -i MIN-MAX -n 1)
em vez disso.
de man shuf
:
-i, --input-range=LO-HI treat each number LO through HI as an input line -n, --head-count=COUNT output at most COUNT lines
O -n 1
em shuf
aqui 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, número 1
é exclusivo).
printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215
Comentários
- Este também não produzirá um número aleatório verdadeiro no intervalo dado, exceto no caso em que N é um divisor de 32767 (o limite superior de $ RANDOM).
Resposta
No bash
bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000"
onde 1/10000
é o seu aleatório precisão e 4
dígitos sua precisão de saída
Resposta
zsh
tem uma rand48()
função aritmética (wrapper para a erand48()
função padrão) em sua :
zmodload zsh/mathfunc printf "%.4f\n" $((rand48()))
Enquanto $RANDOM
tem 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 propagar o awk
“gerador pseudoaleatório (que de outra forma, por padrão, usa o resultado muito previsível de time()
):
echo "$SRANDOM" | awk " { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }"
(tenha em mente que ainda é apenas 32 bits de entropia e awk
faz geração pseudo-aleatória determinística com base nessa semente)