Generar números aleatorios en un rango específico.


84

Después de buscar un poco en Google, no pude encontrar una manera simple de usar un comando de shell para generar un número entero decimal aleatorio incluido en un rango específico, que está entre un mínimo y un máximo.

Leí sobre /dev/random, /dev/urandomy $RANDOM, pero ninguno de estos puede hacer lo que necesito.

¿Hay otro comando útil o una forma de usar los datos anteriores?


2
Consulte estas preguntas y respuestas sobre SO: stackoverflow.com/questions/8988824/… , así como este stackoverflow.com/questions/2556190/… .
slm


Respuestas:


46

En el cofre de herramientas POSIX, puede usar awk:

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

No , no usar eso como una fuente para generar contraseñas o datos secretos, por ejemplo, como ocurre con la mayoría de awklas implementaciones, el número fácilmente se puede adivinar en base al tiempo que comando se ha ejecutado.

Con muchas awkimplementaciones, ese comando ejecutado dos veces en el mismo segundo generalmente le dará la misma salida.


1
+1 .. Como se usa en un script para asignar la salida a una variable (sin introducir un carácter de nueva línea y usar un relleno de cero para dos lugares):x=$(awk -v min=$min_var -v max=$max_var 'BEGIN{srand(); printf("%.2d", int(min+rand()*(max-min+1)))}')
Christopher

¿Se basa en el tiempo? desde que obtuve el mismo valor en 2 carreras consecutivas ...
Shai Alon

@ShaiAlon, el tiempo actual a menudo se usa como el valor inicial predeterminado para srand (), por lo que, por lo general, sí, se basa en el tiempo, vea la oración de Stéphane sobre eso en su respuesta. Puede evitar eso cambiando la semilla inicial a un número aleatorio con 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))}'. Podría usar en $RANDOMlugar de, od ... /dev/randompero su valor inicial está en el rango relativamente pequeño de 0 a 32767, por lo que probablemente obtendrá repeticiones notables en su salida con el tiempo.
Ed Morton

142

Puedes probar shufdesde GNU coreutils:

shuf -i 1-100 -n 1

Esto también funciona con Busybox shuf.
cov

Funciona en raspbian y GitBash también.
nPcomp

30

jota

En BSD y OSX puede usar jot para devolver un solo número aleatorio ( -r) desde el intervalo minhasta max, inclusive.

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

Problema de distribución

Desafortunadamente, el rango y la distribución de los números generados aleatoriamente están influenciados por el hecho de que jot usa aritmética de coma flotante de doble precisión internamente y printf (3) para el formato de salida, lo que causa problemas de redondeo y truncamiento. Por lo tanto, los intervalos miny maxse generan con menos frecuencia como se demuestra:

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

En OS X 10.11 (El Capitan) esto parece haberse solucionado:

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

y...

$ 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

Resolviendo el problema de distribución

Para versiones anteriores de OS X, afortunadamente hay varias soluciones. Una es usar la conversión entera printf (3). La única advertencia es que el intervalo máximo ahora se convierte max+1. Al utilizar el formato de enteros, obtenemos una distribución justa en todo el intervalo:

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

La solución perfecta

Finalmente, para obtener un lanzamiento justo de los dados usando la solución, tenemos:

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

Tarea extra

Ver jot (1) para los detalles de formato y matemática sangrientos y muchos más ejemplos.


15

La $RANDOMvariable normalmente no es una buena forma de generar buenos valores aleatorios. La salida de /dev/[u]randomnecesidad también debe convertirse primero.

Una forma más fácil es usar lenguajes de nivel superior, como por ejemplo python:

Para generar una variable entera aleatoria entre 5 y 10 (5 <= N <= 10), use

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

No use esto para aplicaciones criptográficas.


Esto fue útil. Muchas gracias :) Como se ha firmado, a menudo ha funcionado con Unix-> Solaris 10, las utilidades GNU no están integradas por defecto. Esto funcionó sin embargo.
propatience

11

Puede obtener el número aleatorio a través de urandom

head -200 /dev/urandom | cksum

Salida:

3310670062 52870

Para recuperar la parte del número anterior.

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

Entonces la salida es

3310670062


head -200(o su equivalente POSIX head -n 100) devuelve las primeras 200 líneas de /dev/urandom. /dev/urandomno es un archivo de texto, ese comando podría regresar de 200 bytes (todos los 0x0a, LF en ASCII) al infinito (si el byte 0xa nunca se devuelve) pero los valores entre 45k y 55k son los más probables. cksum devuelve un número de 32 bits, por lo que no tiene sentido obtener más de 4 bytes /dev/urandom.
Stéphane Chazelas

7

Para generar una variable entera aleatoria entre 5 y 10 (incluidos ambos), use

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

% funciona como operador de módulo.

Probablemente hay mejores formas de convertir la variable aleatoria $RANDOMen un rango específico. No utilice esto para aplicaciones criptográficas o en casos en que necesite variables aleatorias reales, distribuidas equitativamente (como para simulaciones).


3
En muchas implementaciones de shell que tienen $ RANDOM (especialmente las más antiguas, especialmente bash), eso no es muy aleatorio. En la mayoría de los shells, el número está entre 0 y 65535, por lo que cualquier rango cuyo ancho no sea una potencia de dos tendrá una discrepancia de distribución de probabilidad. (en este caso, los números del 5 al 8 tendrán una probabilidad de 10923/65536, mientras que el 9 y el 10 tendrán una probabilidad de 10922/65536). Cuanto más amplio es el rango, mayor es la discrepancia.
Stéphane Chazelas

Lo sentimos, eso es 32767 no 65535, por lo que el cálculo anterior es incorrecto (y en realidad es peor).
Stéphane Chazelas

@ StéphaneChazelas Tenía esto en mente cuando escribí que hay mejores maneras ...
jofel


4

Quizás el UUID(en Linux) se pueda usar para recuperar el número aleatorio

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

Para obtener el número aleatorio entre 70y100

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

Esto generará un número hexadecimal de 8 dígitos.


1

Para permanecer completamente dentro de bash y usar la variable $ RANDOM pero evitar la distribución 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 ejemplo proporcionaría números aleatorios del 20 al 29.


$rdebe inicializarse a 65535 u otro valor alto para evitar un [: -lt: unary operator expectederror.
LawrenceC

Se corrigió la inicialización de $ r antes de la prueba de bucle. Gracias @LawrenceC!
Que Angell

0

La distribución es buena:

for ((i = 1; i <= 100000; i ++)) do echo $ ((RANDOM% (20 - 10 + 1) + 10)); hecho | ordenar -n | uniq -c

valor de conteo

9183 10

9109 11

8915 12

9037 13

9100 14

9138 15

9125 16

9261 17

9088 18

8996 19

9048 20

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.