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.
Lanzamiento a cara o cruz de una moneda (aleatoriedad entre dos valores)
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.
¿Qué formas podemos generar una lista?
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