Miércoles, 12 Septiembre 2018 20:31

Generar números aleatoriamente con shuf

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

comprobacionconfichero.png

 

¡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

listaconshuf.png

 

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

listaconshufin.png

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:

 

 

delunoalmil.png

 

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

Publicado en Programación
Miércoles, 12 Septiembre 2018 00:02

Aleatoriedad con shuf

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.

El uso de shuf

shuf se puede usar para ordenar aleatoriamente un fichero de texto:

shuf fichero.txt

shuf.png

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

Publicado en Programación

¡Atención! Este sitio usa cookies y tecnologías similares.

Si no cambia la configuración de su navegador, usted acepta su uso. Saber más

Acepto

Vea nuestra política de cookies y enlaces de interés aquí