Ya hemos visto que, dado que %$valor lo que hace es calcular el resto de la división entre la cifra que precede a % (dividendo) y la cifra que sucede a % (divisor), $RANDOM%limitesuperior no vale para generar números aleatorios de forma fideligna (a no ser que seamos trileros, pero en este caso no buscamos una verdadera aleatoriedad).
También hemos visto que shuf sí que sirve para crear órdenes aleatorios. Y ahora vamos a ver cómo usar shuf para generar números aleatorios.
Veamos cómo usar shuf en el primer ejemplo que usé en contra de $RANDOM: una aleatoriedad dos valores. Pero antes de ello, vuelvo a remarcar que shuf no genera nada, sino que hace un orden aleatorio de una lista. Así que primero hay que generar una lista y luego ordenar aleatoriamente con shuf.
Veamos varias formas de hacerlo:
En un fichero auxiliar:
#/bin/bash
let comienzo=0
let final=1
for i in $(seq $comienzo $final)
do
echo $i >> numeros.txt
done
Y ahora podemos ejecutarlo y luego ordenar con shuf. Veamos a ver si la aleatoriedad es tal:
En este pantallazo vemos que de 12 tiradas, han salido siete veces 0 y cinco veces 1. Está dentro de la normalidad. Pero vamos a ver qué ocurre si tiramos mil veces la moneda:
#/bin/bash
let ceros=0
let unos=0
for i in $(seq 1 1000)
do
let moneda=$(shuf -n1 numeros.txt)
if [ $moneda -eq 0 ]
then
let ceros++
else
let unos++
fi
done
echo "El número de ceros que ha salido es: "$ceros
echo "El número de unos que ha salido es: "$unos
Y lo comprobamos (vuelvo a poner pantallazo para que se vea que no hay trampa ni cartón):
¡Incluso me ha llegado a dar un empate de 500 a 500!
Visto que shuf es más válido en este caso, vamos a ver cómo generar una lista aleatoria con shuf. Compruebo en el mismo script para evitar hacer un artículo excesivamente largo:
#/bin/bash
let ceros=0
let unos=0
for i in $(seq 1 1000)
do
numeros=$(shuf -i 0-1)
if [[ ${numeros:0:1} == "0" ]]
then
let ceros++
else
let unos++
fi
done
echo "El número de ceros que ha salido es: "$ceros
echo "El número de unos que ha salido es: "$unos
Funciona... y de nuevo hemos tenido un empate a 500 en una tirada.
Pero ahora te puedes preguntar, por qué si existe el parámetro -i en shuf para generar una lista con orden aleatorio y el parámetro -n para extraer un número determinado de elementos, ¿por qué no lo usas en la misma línea?
No te falta razón para pensarlo, pero quería mostrar distintas formas de lanzar la moneda. Y hacer un artículo evolutivo que acabase con la mejor opción:
#/bin/bash
let ceros=0
let unos=0
for i in $(seq 1 1000)
do
numeros=$(shuf -i 0-1 -n 1)
if [[ $numeros == "0" ]]
then
let ceros++
else
let unos++
fi
done
echo "El número de ceros que ha salido es: "$ceros
echo "El número de unos que ha salido es: "$unos
Y vuelve a funcionar... incluso me ha dado otro empate a 500.
¿Y si en lugar de tirar a cara o cruz hacemos un rango mayor, por ejemplo de 0 a 1000?
Simplemente le cambiamos el rango en -i. Y si además, para comprobar si funciona, vamos a aumentar el número de tiradas.
#/bin/bash
let ceros=0
let quinientos=0
let miles=0
for i in $(seq 1 10000)
do
numeros=$(shuf -i 0-1000 -n 1)
if [[ $numeros == "0" ]]
then
let ceros++
elif [[ $numeros == "500" ]]
then
let quinientos++
elif [[ $numeros == "1000" ]]
then
let miles++
fi
done
echo "El número de ceros que ha salido es: "$ceros
echo "El número de quinientos que ha salido es: "$quinientos
echo "El número de miles que ha salido es: "$miles
Y al probarlo, da unos resultados tales como:
Que son unos resultados que son totalmente coherentes dentro de lo que es la aleatoriedad por lo que podemos decir que la mejor forma para generar un número aleatorio en Bash es
shuf -i $limiteinferior-$limitesuperior -n 1
En tres artículos he estado argumentando en contra del uso de $RANDOM para generar números aleatorios:
El mito de $((RANDOM%$limitesuperior))
Más sobre la "aleatoriedad" de $RANDOM
Dados cargados en $RANDOM
Pero es poco honesto criticar algo sin aportar una solución, así que aquí voy a explicar el uso de shuf y en otro artículo explicaré varias formas de usar shuf para generar números aleatorios.
shuf se puede usar para ordenar aleatoriamente un fichero de texto:
shuf fichero.txt
Como vemos al usar cat, las palabras de fichero.txt tenían un orden que, al usar shuf, han cambiado de orden.
También shuf permite seleccionar X elementos de una lista. Ya lo hemos usado otras veces, como al elegir aleatoriamente una tipografía o un color con ImageMagick. Pero veamos con el ejemplo anterior:
shuf -n2 fichero
Con el parámetro -n$numerodelineas muestra el número de líneas incado como valor a -n.
Otra forma de usar shuf es recibiendo una lista desde la salida de otro fichero.
Veamos un ejemplo creando una serie de ficheros en un directorio y luego seleccionando uno al azar recogiendo con shuf la salida de ls.
Otro parámetro muy interesante es -o para especificar un fichero donde guardar la salida de shuf. Evolucionemos la instrucción anterior:
ls | shuf -o ordenaleatorio.txt