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?


2
Consulte estas perguntas e respostas SO: stackoverflow.com/questions/8988824/… , bem como este stackoverflow.com/questions/2556190/… .
Slm


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.


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

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.


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.


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


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).


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


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

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.


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.


$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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.