¿Cómo generar un número aleatorio en Bash?


236

¿Cómo generar un número aleatorio dentro de un rango en Bash?


21
¿Qué tan aleatorio tiene que ser?
bdonlan

Respuestas:


283

Uso $RANDOM. A menudo es útil en combinación con aritmética de shell simple. Por ejemplo, para generar un número aleatorio entre 1 y 10 (inclusive):

$ echo $((1 + RANDOM % 10))
3

El generador real está en variables.c, la función brand(). Las versiones anteriores eran un generador lineal simple. La versión 4.0 de bashutiliza un generador con una cita de un artículo de 1985, lo que presumiblemente significa que es una fuente decente de números pseudoaleatorios. No lo usaría para una simulación (y ciertamente no para criptografía), pero probablemente sea adecuado para tareas básicas de secuencias de comandos.

Si está haciendo algo que requiere números aleatorios serios que puede usar /dev/randomo /dev/urandomsi están disponibles:

$ dd if=/dev/urandom count=4 bs=1 | od -t d

21
Ten cuidado aquí. Si bien esto está bien en caso de apuro, hacer aritmética en números aleatorios puede afectar dramáticamente la aleatoriedad de su resultado. en el caso de $RANDOM % 108 y 9 son mensurables (aunque marginalmente) menos probables que 0-7, incluso si $RANDOMes una fuente sólida de datos aleatorios.
dimo414

3
@ dimo414 Tengo curiosidad por "marginalmente", ¿tiene una fuente donde pueda obtener más información al respecto?
PascalVKooten

58
Al modular su entrada aleatoria, está "haciendo palomas " los resultados. Dado que $RANDOMel rango son 0-32767los números 0, se 7asignan a 3277diferentes entradas posibles, pero 8y 9solo se pueden producir de 3276diferentes maneras (porque 32768y 32769no son posibles). Este es un problema menor para los hacks rápidos, pero significa que el resultado no es uniforme al azar. Las bibliotecas aleatorias, como las de Java Random, ofrecen funciones para devolver adecuadamente un número aleatorio uniforme en el rango dado, en lugar de simplemente modificar un número no divisible.
dimo414

1
@JFSebastian muy cierto: el problema con el módulo es que puede romper la uniformidad de cualquier RNG, no solo los PRNG malos, sino que gracias por llamarlo.
dimo414

14
Solo por contexto, el encasillamiento básico para% 10 significa que 8 y 9 tienen aproximadamente 0.03% menos probabilidades de ocurrir que 0–7. Si su script de shell requiere números aleatorios uniformes más precisos que eso, entonces, por supuesto, use un mecanismo más complejo y adecuado.
Nelson

71

Por favor vea $RANDOM:

$RANDOM es una función Bash interna (no una constante) que devuelve un entero pseudoaleatorio en el rango de 0 a 32767. No debe usarse para generar una clave de cifrado.


1
¿ 32767Tiene algún significado especial?
Jin Kwon

14
@JinKwon 32767es 2^16 / 2 - 1cuál es el límite superior para un entero de 16 bits con signo.
Jeffrey Martinez

@ JinKwon, ¿podría aclarar por qué no dice que es así 2^15 - 1? Es equivalente, así que tengo curiosidad si hay algún contexto que me falta.
Brett Holman el

11
@BrettHolman Creo que estaba tratando de señalar la parte "firmada" del entero de 16 bits firmado. 2 ^ 16 valores, divididos por la mitad para entradas positivas y negativas.
cody

Esta respuesta no responde la pregunta.
mocoso

48

También puede usar shuf (disponible en coreutils).

shuf -i 1-100000 -n 1

¿Cómo pasas en vars como final de rango? Tengo esto:shuf -i 1-10 -n 1: syntax error in expression (error token is "1-10 -n 1")
dat tutbrus

1
Agregue un en $varlugar del final del rango, como este:var=100 && shuf -i 1-${var} -n 1
knipwim

2
Prefiero esta opción, ya que es fácil generar N números aleatorios con -n. Por ejemplo, generar 5 números aleatorios entre 1 y 100 :shuf -i 1-100 -n 5
aerijman

Hasta donde yo entiendo, los números no son aleatorios. Si lo especifica shuf -i 1-10 -n 10, obtendrá todos los números del 1 al 10 exactamente uno. Si especificas -n 15, obtendrás solo esos 10 números exactamente una vez. Eso es realmente solo barajar, no generar números aleatorios.
Radlan

Para obtener números aleatorios con reemplazo: -r
Geoffrey Anderson

36

Prueba esto desde tu shell:

$ od -A n -t d -N 1 /dev/urandom

Aquí, -t despecifica que el formato de salida debe ser con signo decimal; -N 1dice que lea un byte de /dev/urandom.


2
La pregunta pide números en un rango.
JSycamore

99
puedes eliminar espacios:od -A n -t d -N 1 /dev/urandom |tr -d ' '
Robert

23

también puedes obtener un número aleatorio de awk

awk 'BEGIN {
   # seed
   srand()
   for (i=1;i<=1000;i++){
     print int(1 + rand() * 100)
   }
}'

1
+1 ya sabes, al principio pensé por qué querrías hacerlo así, pero en realidad me gusta bastante.
zelanix

1
Gracias por dar una solución que incluye la siembra. ¡No pude encontrarlo en ningún lado!
so.very

2
+1 para la siembra. Vale la pena mencionar que srand()la semilla es el tiempo de CPU actual. Si necesita especificar una semilla específica, para que RNG pueda duplicarse, use srand(x)where xis the seed. Además, citado del manual de funciones numéricas de GNU awk, "diferentes implementaciones de awk usan diferentes generadores de números aleatorios internamente". El resultado es que si está interesado en generar una distribución estadística, debe esperar ligeras variaciones de un tiempo de ejecución a otro en una plataforma diferente (todas en ejecución awko gawk).
Cbhihe

18

Hay $ RANDOM. No sé exactamente cómo funciona. Pero funciona. Para las pruebas, puede hacer:

echo $RANDOM

16

Me gusta este truco:

echo ${RANDOM:0:1} # random number between 1 and 9
echo ${RANDOM:0:2} # random number between 1 and 99

...


77
$ RANDOM está en el rango de 0 - 32767. Terminará con más números que comienzan con 1, 2 o 3, que tendrá 4-9. Si está de acuerdo con una distribución desequilibrada, esto funcionará bien.
jbo5112

2
@ jbo5112 tiene toda la razón, ¿qué pasa con mostrar el último dígito? echo $ {RANDOM: 0-1} para un dígito, $ {RANDOM: 0-2} para dos dígitos ...?
fraff

44
Si utiliza los últimos dígitos, será bastante bueno, pero incluirá 0 y 00. En un solo dígito, 0-7 ocurrirá 0.03% más a menudo que 8-9. En 2 dígitos, 0-67 ocurrirá 0.3% más a menudo que 68-99. Si necesita una distribución aleatoria de números tan buena, espero que no esté usando bash. Con el original: ${RANDOM:0:1}tiene una probabilidad del 67.8% de darle un 1 o un 2, ${RANDOM:0:2}solo tiene una probabilidad del 0.03% de darle un número de un solo dígito (debe ser 1%), y ambos tienen una probabilidad del 0.003% de darle un 0 Todavía hay casos de uso en los que esto está bien (por ejemplo, entrada no coherente).
jbo5112

12

Número aleatorio entre 0 y 9 inclusive.

echo $((RANDOM%10))

2
Lo malo, no leí la página del manual correctamente. $RANDOMsolo va de 0 a 32767. Debería haber dicho "Número aleatorio principalmente entre 1 y 3, con algunos extremos";)
David Newcomb

1
¿Qué? Todavía estará entre 0 y 9, aunque 8 y 9 tendrán una probabilidad ligeramente menor de ocurrir que 0 a 7, como se menciona en otra respuesta.
kini

6

Si está utilizando un sistema Linux, puede obtener un número aleatorio de / dev / random o / dev / urandom. Tenga cuidado con / dev / random se bloqueará si no hay suficientes números aleatorios disponibles. Si necesita velocidad sobre aleatoriedad, use / dev / urandom.

Estos "archivos" se llenarán con números aleatorios generados por el sistema operativo. Depende de la implementación de / dev / random en su sistema si obtiene números verdaderos o pseudoaleatorios. Se generan números aleatorios verdaderos con la ayuda del ruido acumulado de los controladores de dispositivos como el mouse, el disco duro y la red.

Puede obtener números aleatorios del archivo con dd


5

Tomé algunas de estas ideas e hice una función que debería funcionar rápidamente si se requieren muchos números aleatorios.

llamar odes costoso si necesita muchos números aleatorios. En cambio, lo llamo una vez y almaceno 1024 números aleatorios de / dev / urandom. Cuando randse llama, se devuelve el último número aleatorio y se escala. Luego se elimina del caché. Cuando el caché está vacío, se leen otros 1024 números aleatorios.

Ejemplo:

rand 10; echo $RET

Devuelve un número aleatorio en RET entre 0 y 9 inclusive.

declare -ia RANDCACHE
declare -i RET RAWRAND=$(( (1<<32)-1 ))

function rand(){  # pick a random number from 0 to N-1. Max N is 2^32
  local -i N=$1
  [[ ${#RANDCACHE[*]} -eq 0 ]] && { RANDCACHE=( $(od -An -tu4 -N1024 /dev/urandom) ); }  # refill cache
  RET=$(( (RANDCACHE[-1]*N+1)/RAWRAND ))  # pull last random number and scale
  unset RANDCACHE[${#RANDCACHE[*]}-1]     # pop read random number
};

# test by generating a lot of random numbers, then effectively place them in bins and count how many are in each bin.

declare -i c; declare -ia BIN

for (( c=0; c<100000; c++ )); do
  rand 10
  BIN[RET]+=1  # add to bin to check distribution
done

for (( c=0; c<10; c++ )); do
  printf "%d %d\n" $c ${BIN[c]} 
done

ACTUALIZACIÓN: Eso no funciona tan bien para todos los N. También desperdicia bits aleatorios si se usa con pequeños N. Observando que (en este caso) un número aleatorio de 32 bits tiene suficiente entropía para 9 números aleatorios entre 0 y 9 (10 * 9 = 1,000,000,000 <= 2 * 32) podemos extraer múltiples números aleatorios de cada 32 valor fuente aleatorio.

#!/bin/bash

declare -ia RCACHE

declare -i RET             # return value
declare -i ENT=2           # keep track of unused entropy as 2^(entropy)
declare -i RND=RANDOM%ENT  # a store for unused entropy - start with 1 bit

declare -i BYTES=4         # size of unsigned random bytes returned by od
declare -i BITS=8*BYTES    # size of random data returned by od in bits
declare -i CACHE=16        # number of random numbers to cache
declare -i MAX=2**BITS     # quantum of entropy per cached random number
declare -i c

function rand(){  # pick a random number from 0 to 2^BITS-1
  [[ ${#RCACHE[*]} -eq 0 ]] && { RCACHE=( $(od -An -tu$BYTES -N$CACHE /dev/urandom) ); }  # refill cache - could use /dev/random if CACHE is small
  RET=${RCACHE[-1]}              # pull last random number and scale
  unset RCACHE[${#RCACHE[*]}-1]  # pop read random number
};

function randBetween(){
  local -i N=$1
  [[ ENT -lt N ]] && {  # not enough entropy to supply ln(N)/ln(2) bits
    rand; RND=RET       # get more random bits
    ENT=MAX             # reset entropy
  }
  RET=RND%N  # random number to return
  RND=RND/N  # remaining randomness
  ENT=ENT/N  # remaining entropy
};

declare -ia BIN

for (( c=0; c<100000; c++ )); do
  randBetween 10
  BIN[RET]+=1
done

for c in ${BIN[*]}; do
  echo $c
done

Intenté esto: tomó 10 segundos de 100% de CPU y luego imprimí 10 números que no parecían aleatorios en absoluto.
Carlo Wood

Ahora recuerdo. Este código genera 100,000 números aleatorios. Pone a cada uno en un 'contenedor' para ver qué tan aleatorio es. Hay 10 contenedores. Estos números deben ser similares si cada número aleatorio entre 0 y 9 es igualmente probable. Si desea imprimir cada número, repita $ RET después de randEntre 10.
philcolbourn

od -An -tu4 -N40 /dev/urandomgenerará 10 enteros aleatorios de 32 bits sin signo separados con espacios en blanco. puede almacenarlo en una matriz y usarlo después. Su código parece ser una exageración.
Ali

@ Ali, OP no especificó que querían 32 bits ni ningún otro número aleatorio de tamaño. Yo y algunos otros interpretamos esta pregunta como un número aleatorio dentro de un rango. Mi función rand logra este objetivo y también reduce la pérdida de entropía que, si se agota, hace que los programas se bloqueen. od on / dev / urandom devuelve solo números aleatorios de 2 ^ N bits y OP necesitaría almacenar múltiples valores en una matriz, extraerlos secuencialmente de esta matriz y reponer esta matriz. ¿Quizás pueda codificar esto como respuesta y manejar otros rangos de números aleatorios?
philcolbourn

@philcolbourn, tienes razón acerca de que el OP no especifica qué tipo de número aleatorio quiere y me llamó la atención. Pero solo preguntó: "¿Cómo generar un número aleatorio en bash?". Mi punto es que solo pidió un número aleatorio. Aunque esta crítica se aplica a mi comentario anterior (que genera 10 números aleatorios) también.
Ali

5

Lectura de archivos especiales de caracteres / dev / random o / dev / urandom es el camino a seguir.

Estos dispositivos devuelven números verdaderamente aleatorios cuando se leen y están diseñados para ayudar al software de aplicación a elegir claves seguras para el cifrado. Tales números aleatorios se extraen de un grupo de entropía que es contribuido por varios eventos aleatorios. {LDD3, Jonathan Corbet, Alessandro Rubini y Greg Kroah-Hartman]

Estos dos archivos son interfaz para aleatorización del núcleo, en particular

void get_random_bytes_arch(void* buf, int nbytes)

que extrae bytes realmente aleatorios del hardware si dicha función es implementada por hardware (por lo general, es), o extrae del grupo de entropía (compuesto de temporizaciones entre eventos como interrupciones del mouse y del teclado y otras interrupciones que están registradas con SA_SAMPLE_RANDOM).

dd if=/dev/urandom count=4 bs=1 | od -t d

Esto funciona, pero escribe salidas innecesarias desde ddstdout. El siguiente comando da solo el número entero que necesito. Incluso puedo obtener el número especificado de bits aleatorios que necesito mediante el ajuste de la máscara de bits dada a la expansión aritmética:

me@mymachine:~/$ x=$(head -c 1 /dev/urandom > tmp && hexdump 
                         -d tmp | head -n 1 | cut -c13-15) && echo $(( 10#$x & 127 ))

3

Qué pasa:

perl -e 'print int rand 10, "\n"; '

1
Para obtener un número aleatorio criptográficamente seguro, debe leer / dev / urandom o usar las bibliotecas Crypt :: Random.
kh

3

Tal vez llego un poco tarde, pero ¿qué pasa con el uso jotpara generar un número aleatorio dentro de un rango en Bash?

jot -r -p 3 1 0 1

Esto genera un número aleatorio ( -r) con 3 decimales de precisión ( -p). En este caso particular, obtendrá un número entre 0 y 1 ( 1 0 1). También puede imprimir datos secuenciales. La fuente del número aleatorio, según el manual, es:

Los números aleatorios se obtienen a través de arc4random (3) cuando no se especifica ninguna semilla, y a través de random (3) cuando se da una semilla.


1
Debe instalarse: sudo apt install athena-jot
xerostomus

3

Basado en las excelentes respuestas de @Nelson, @Barun y @Robert, aquí hay un script Bash que genera números aleatorios.

  • Puede generar cuántos dígitos desea.
  • cada dígito se genera por separado, lo /dev/urandomcual es mucho mejor que el incorporado de Bash$RANDOM
#!/usr/bin/env bash

digits=10

rand=$(od -A n -t d -N 2 /dev/urandom |tr -d ' ')
num=$((rand % 10))
while [ ${#num} -lt $digits ]; do
  rand=$(od -A n -t d -N 1 /dev/urandom |tr -d ' ')
  num="${num}$((rand % 10))"
done
echo $num

Exactamente lo que buscaba.
Prometeo

2

Genere un número aleatorio en el rango de 0 a n (entero de 16 bits con signo). Conjunto de resultados en $ RAND variable. Por ejemplo:

#!/bin/bash

random()
{
    local range=${1:-1}

    RAND=`od -t uI -N 4 /dev/urandom | awk '{print $2}'`
    let "RAND=$RAND%($range+1)"
}

n=10
while [ $(( n -=1 )) -ge "0" ]; do
    random 500
    echo "$RAND"
done

1

Ramificación aleatoria de un programa o sí / no; 1/0; salida verdadero / falso:

if [ $RANDOM -gt 16383  ]; then              # 16383 = 32767/2 
    echo var=true/1/yes/go_hither
else 
    echo var=false/0/no/go_thither
fi

de si eres perezoso para recordar 16383:

if (( RANDOM % 2 )); then 
    echo "yes"
else 
    echo "no"
fi
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.