Gere números aleatórios em um intervalo específico

84

Depois de pesquisar um pouco, não consegui encontrar uma maneira simples de usar um comando shell para gerar um número inteiro decimal aleatório incluído em um intervalo específico, que está entre um mínimo e um máximo.

Eu li sobre /dev/random, /dev/urandome $RANDOM, mas nada disso pode fazer o que eu preciso.

Existe outro comando útil ou uma maneira de usar os dados anteriores?

BowPark
fonte

Respostas:

45

No baú da ferramenta POSIX, você pode usar awk:

awk -v min=5 -v max=10 'BEGIN{srand(); print int(min+rand()*(max-min+1))}'

Você não usar isso como uma fonte para gerar senhas ou dados secretos, por exemplo, como acontece com a maioria das awkimplementações, o número pode ser facilmente adivinhado com base no tempo que o comando foi executado.

Com muitas awkimplementações, esse comando executado duas vezes no mesmo segundo geralmente fornece a mesma saída.

Stéphane Chazelas
fonte
11
+1 .. Como usado em um script para atribuir saída a uma variável (sem introduzir um caractere de nova linha e usar zero padding em dois locais):x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}')
Christopher
É baseado no tempo? desde que obtive o mesmo valor em 2 corridas consecutivas ...
Shai Alon
@ShaiAlon, o horário atual é frequentemente usado como o valor inicial padrão para srand (), então geralmente, sim, é baseado no horário. Veja a frase de Stéphane sobre isso em sua resposta. Você pode contornar isso alterando a semente inicial para um número aleatório com awk -v min=5 -v max=10 -v seed="$(od -An -N4 -tu4 /dev/urandom)" 'BEGIN{srand(seed+0); print int(min+rand()*(max-min+1))}'. Você pode usar em $RANDOMvez de, od ... /dev/randommas seu valor inicial está no intervalo relativamente pequeno de 0 a 32767 e, portanto, provavelmente obterá repetições visíveis em sua saída ao longo do tempo.
Ed Morton
141

Você pode tentar shufno GNU coreutils:

shuf -i 1-100 -n 1
cuonglm
fonte
Isso também funciona com o Busybox shuf.
cov
Funciona em raspbian e GitBash também.
NPcomp 22/10
30

mínimo

No BSD e no OSX, você pode usar jot para retornar um único -rnúmero aleatório ( ) do intervalo minpara max, inclusive.

$ min=5
$ max=10
$ jot -r 1 $min $max

Problema de distribuição

Infelizmente, o alcance e a distribuição dos números gerados aleatoriamente são influenciados pelo fato de o jot usar aritmética de ponto flutuante de precisão dupla internamente e printf (3) para o formato de saída, o que causa problemas de arredondamento e truncamento. Portanto, os intervalos mine maxsão gerados com menos frequência, conforme demonstrado:

$ jot -r 100000 5 10 | sort -n | uniq -c
9918  5
20176 6
20006 7
20083 8
19879 9
9938  10

No OS X 10.11 (El Capitan), isso parece ter sido corrigido:

$ jot -r 100000 5 10 | sort -n | uniq -c
16692 5
16550 6
16856 7
16579 8
16714 9
16609 10  

e...

$ jot -r 1000000 1 10 | sort -n | uniq -c
100430 1
99965 2
99982 3
99796 4
100444 5
99853 6
99835 7
100397 8
99588 9
99710 10

Resolvendo o problema de distribuição

Para versões mais antigas do OS X, felizmente existem várias soluções alternativas. Uma é usar a conversão inteira printf (3). A única ressalva é que o intervalo máximo agora se torna max+1. Usando a formatação inteira, obtemos uma distribuição justa em todo o intervalo:

$ jot -w %i -r 100000 5 11 | sort -n | uniq -c
16756 5
16571 6
16744 7
16605 8
16683 9
16641 10

A solução perfeita

Por fim, para obter um lançamento justo dos dados usando a solução alternativa, temos:

$ min=5
$ max_plus1=11  # 10 + 1
$ jot -w %i -r 1 $min $max_plus1

Lição de casa extra

Veja jot (1) para detalhes matemáticos e de formatação e muitos outros exemplos.

Clint Pachl
fonte
15

A $RANDOMvariável normalmente não é uma boa maneira de gerar bons valores aleatórios. A saída da /dev/[u]randomnecessidade também deve ser convertida primeiro.

Uma maneira mais fácil é usar linguagens de nível superior, como por exemplo python:

Para gerar uma variável inteira aleatória entre 5 e 10 (5 <= N <= 10), use

python -c "import random; print random.randint(5,10)"

Não use isso para aplicativos criptográficos.

jofel
fonte
Isso foi útil. Muito obrigado :) Como assinado, muitas vezes funcionou com o Unix-> Solaris 10, os utilitários GNU não são integrados por padrão. Isso funcionou no entanto.
propacience
11

Você pode obter o número aleatório através urandom

head -200 /dev/urandom | cksum

Resultado:

3310670062 52870

Para recuperar a parte do número acima.

head -200 /dev/urandom | cksum | cut -f1 -d " "

Então a saída é

3310670062

zangw
fonte
head -200(ou seu equivalente POSIX head -n 100) retorna as primeiras 200 linhas de /dev/urandom. /dev/urandomnão é um arquivo de texto, esse comando pode retornar de 200 bytes (todos os 0x0a, LF em ASCII) até o infinito (se o byte 0xa nunca for retornado), mas valores entre 45k e 55k são os mais prováveis. cksum retorna um número de 32 bits, por isso é inútil obter mais de 4 bytes /dev/urandom.
Stéphane Chazelas
7

Para gerar uma variável inteira aleatória entre 5 e 10 (incluindo ambos), use

echo $(( RANDOM % (10 - 5 + 1 ) + 5 ))

% funciona como um operador de módulo.

Provavelmente, existem maneiras melhores de converter a variável aleatória $RANDOMem um intervalo específico. Não use isso para aplicativos criptográficos ou nos casos em que você precisa de variáveis ​​aleatórias reais e com igual distribuição (como para simulações).

jofel
fonte
3
Em muitas implementações de shell que possuem $ RANDOM (especialmente as mais antigas, especialmente o bash), isso não é muito aleatório. Na maioria dos shells, o número está entre 0 e 65535, portanto, qualquer intervalo cuja largura não seja uma potência de dois terá discrepância de distribuição de probabilidade. (nesse caso, os números 5 a 8 terão uma probabilidade de 10923/65536, enquanto 9 e 10 terão uma probabilidade de 10922/65536). Quanto maior o alcance, maior a discrepância.
Stéphane Chazelas
Desculpe, isso não é 32767 e 65535; portanto, o cálculo acima está errado (e na verdade é pior).
Stéphane Chazelas
@ StéphaneChazelas eu tinha isso em mente quando escrevi que há melhores maneiras ...
Jofel
5

# echo $(( $RANDOM % 256 )) produzirá um número "aleatório" entre 0-255 nos dialetos sh * modernos.

qrkourier
fonte
Parece semelhante a essa outra resposta de 2 anos atrás.
Jeff Schaller
11
Isso está sujeito ao problema do buraco de pombo (TL; DR: alguns resultados são mais prováveis ​​que outros) se, em vez de 256, você usar um valor que não divide 32768 igualmente.
toon81
4

Talvez o UUID(no Linux) possa ser usado para recuperar o número aleatório

$ cat /proc/sys/kernel/random/uuid
cdd52826-327d-4355-9737-895f58ad11b4

Para obter o número aleatório entre 70e100

POSIXLY_CORRECT=1 awk -F - '{print(("0x"$1) % 30 + 70)}
   ' /proc/sys/kernel/random/uuid
zangw
fonte
3
cat /dev/urandom | tr -dc 'a-fA-F0-9' | fold -w 8 | head -n 1

Isso irá gerar um número hexadecimal de 8 dígitos.

Tom Cooper
fonte
1

Para permanecer totalmente dentro do bash e usar a variável $ RANDOM, mas evite a distribuição desigual:

#!/bin/bash
range=10 
floor=20

if [ $range -gt 32768 ]; then
echo 'range outside of what $RANDOM can provide.' >&2
exit 1 # exit before entering infinite loop
fi 

max_RANDOM=$(( 2**15/$range*$range ))
r=$RANDOM
until [ $r -lt $max_RANDOM ]; do
r=$RANDOM
done
echo $(( r % $range + $floor ))

Este exemplo forneceria números aleatórios de 20 a 29.

Than Angell
fonte
$rdeve ser inicializado para 65535 ou outro valor alto para evitar um [: -lt: unary operator expectederro.
LawrenceC
Corrigida a inicialização $ r antes do teste de loop. Obrigado @LawrenceC!
Que Angell
0

A distribuição é boa:

para ((i = 1; i <= 100000; i ++)) ecoa $ ((% ALEATÓRIO (20 - 10 + 1) + 10)); feito | classificar -n | uniq -c

valor da contagem

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

SteveK
fonte