Bash: Cómo generar un número flotante aleatorio usando $ RANDOM

¿Es posible generar números aleatorios reales con una precisión específica y en un rango específico usando el Integer Random? Generador $ RANDOM? Por ejemplo, ¿cómo podemos generar un número real con una precisión de 4 entre 0 y 1?

0.1234 0.0309 0.9001 0.0000 1.0000 

Una solución sencilla:

printf "%d04.%d04\n" $RANDOM $RANDOM 

Comentarios

  • Por favor, especifique lo que quiere decir con » números aleatorios reales «. ¿Necesita una fuente de números aleatorios generados por algo como la desintegración de partículas, o estará satisfecho con un generador pseudoaleatorio? ¿Su aplicación de estos números tiene importancia criptográfica o científica, o simplemente quiere algo que » parezca aleatorio «?
  • … o realmente te refieres a » float » o » flotante número de punto?
  • Gracias por el comentario. Necesito un generador de números pseudoaleatorios para números de punto flotante basado en $ RANDOM.
  • … para implementar algoritmos metaheurísticos en bash.

Respuesta

awk -v n=10 -v seed="$RANDOM" "BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }" 

Esto generará n números aleatorios (diez en el ejemplo) en el rango [0,1) con cuatro dígitos decimales. Utiliza la función rand() en awk (no en la awk estándar, pero implementada por los awk implementaciones) que devuelve un valor aleatorio en ese rango. El generador de números aleatorios es sembrado por la variable shell «s $RANDOM.

Cuando un programa awk solo tiene BEGIN bloques (y ningún otro bloque de código), awk no intentará leer la entrada de su flujo de entrada estándar.

En cualquier sistema OpenBSD (o sistema que tenga la misma jot utilidad , originalmente en 4.2BSD), la siguiente generará 10 números aleatorios como se especifica:

jot -p 4 -r 10 0 1 

Comentarios

  • Realmente hablando estrictamente, ya que la salida de rand() es un flotante dentro de [0,1), probablemente no está ‘ t distribuido exactamente de manera uniforme cuando se redondea a cuatro dígitos decimales. Lo sería, si el flotante fuera de precisión infinita, pero no es ‘ t: es ‘ probable que se genere a partir de bits aleatorios , por lo que hay 2 ^ N valores diferentes, y no se asignan ‘ t uniformemente a un conjunto de 1000 valores. Pero siempre que esos flotadores pseudoaleatorios tengan suficientes bits y usted ‘ no esté haciendo nada realmente exacto, probablemente haya ganado ‘ t aviso.

Respuesta

Como se señaló en otra respuesta, existen otras utilidades que puede utilizar para generar números al azar. En esta respuesta, limito mis recursos a $RANDOM y algunas funciones aritméticas básicas.

Para números de punto flotante, intente algo como

printf "%s\n" $(echo "scale=8; $RANDOM/32768" | bc ) 

Eso le dará la mejor precisión porque $RANDOM solo genera números entre 0 y 32767. (¡incluido 32767!) Pero, yo » También violé mi regla sobre el uso de funciones aritméticas básicas invocando bc.

Pero antes de continuar, me gustaría analizar dos cuestiones: precisión y rango para números de coma flotante. Después de eso, buscaré generar un rango de números enteros (y si puede generar números enteros, luego puede dividirlos para obtener un decimal si lo desea utilizando las utilidades que prefiera para lograrlo).

Precision

Tomando el enfoque de $RANDOM/32768, desde $RANDOM genera valores de 0 a 32767, el resultado de $RANDOM/32768 también será un número finito de valores. En otras palabras, sigue siendo una variable aleatoria discreta (y con una computadora nunca podrá alejarse de ese hecho). Con eso en mente, entonces puede lograr cierto grado de precisión usando printf.

Si desea una cobertura más fina del intervalo, podría comenzar a pensar en la base 32768. Entonces, en teoría, $RANDOM + $RANDOM*32768 debería darle una distribución uniforme entre 0 y 1,073,741,823. Pero, dudo que la línea de comando maneje muy bien esa precisión. Un par de puntos relacionados con este caso particular:

  • La suma de dos variables aleatorias independientes distribuidas uniformemente no es generalmente uniforme. En este caso, al menos teóricamente hablando (ver tercer punto), lo son.
  • No crea que puede simplificar $RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 ).Las dos apariciones de $RANDOM son en realidad dos eventos diferentes.
  • No sé lo suficiente acerca de cómo $RANDOM es generado para saber si llamarlo dos veces de esta manera realmente generará dos eventos aleatorios independientes.

Rango

Consideremos solo $RANDOM/32768. Si desea un número en un rango, diga [a,b), luego

$RANDOM/32768*(b-a) + a 

lo ubicará en el rango deseado .

Generación de valores enteros

Primero, considere generar números aleatorios entre [0,b) donde b es menor que 32768. Considere el producto q*b, donde q es la parte entera de 32768/b. Entonces, lo que puede hacer es generar un número aleatorio entre 0 y 32767, pero descarte aquellos que sean mayores o iguales a q*b. Llame al número así generado G. Entonces G caerá en el rango de 0 a q*b, y su distribución será uniforme. Ahora, aplique aritmética modular para reducir este valor al rango deseado:

G % b 

Nota, generar aleatoriamente un número de la siguiente manera

$RANDOM % b 

no creará una distribución uniforme, a menos que b resulte ser uno de los divisores de 32768.

Escribiendo un script bash para este

Calculando q*b como se describe arriba suena como un dolor. Pero realmente no lo es. Puede obtenerlo de la siguiente manera:

q*b = 32768 - ( 32768 % b ) 

En Bash, puede obtenerlo con

$((32768 - $((32768 % b)) )) 

El siguiente código generará un número aleatorio en el rango 0..b (no incluye b) . b=$1

m=$((32768 - $((32768 % $1)) )) a=$RANDOM while (( $a > $m )); do a=$RANDOM done a=$(($a % $1)) printf "$a\n" 

Anexo

Técnicamente, hay pocas razones para trabajar con

m=$((32768 - $((32768 % $1)) )) 

Lo siguiente logrará lo mismo

a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n" 

Es mucho más trabajo, pero las computadoras son rápidas.

Generando un entero en un rango mayor

Dejaré que lo resuelvas. Se debe tener cuidado y, en algún momento, tendrá que tomar en consideración las limitaciones de memoria de la computadora al manejar operaciones aritméticas.

Nota final

La respuesta aceptada no creará un número aleatorio uniformemente sobre 0 a 1.

Para ver esto, intente lo siguiente

$ for i in {1..1000}; do echo .$RANDOM; done | awk "{ a += $1 } END { print a }" 

Para una distribución verdaderamente uniforme sobre [0,1), debería ver un promedio cercano a 0.500.

Pero como puede ver al ejecutar el fragmento anterior, obtendrá algo como 314.432 o 322.619. Dado que son 1000 números, el promedio de esto es .322. El promedio real para esta secuencia de números generados es .316362

Puede obtener este promedio real usando el script de perl

 perl -e "{ $i=0; $s=0; while ( $i<=32767 ) { $j = sprintf "%.5f", ".$i"; $j =~ s/^0\.//; print "$j\n"; $s += $j; $i++ }; printf "%.5f\n", $s/32767; }" 

Estoy agregando números enteros aquí para ayudarlo a ver cómo este enfoque de usar .$RANDOM no está haciendo lo que probablemente desea que haga. En otras palabras, piense qué enteros se están generando y cuáles se pierden por completo. Se omiten una gran cantidad; algunos se duplican.

Respuesta

En sistemas donde el shell «s printf es capaz de entender el %a (bash ksh zsh, etc.) y por lo tanto es capaz de realizar un cambio de base interno (hex -> dec) (uniforme en [0,1) rango de 0.00003 a 0.99997):

printf "%.5f\n" "$(printf "0x0.%04xp1" $RANDOM)" 

Incluso podría usar más dígitos combinando más llamadas a $RANDOM (de 0.000000001 a 0.999999999)

printf "%.9f\n" "$(printf "0x0.%08xp2" $(( ($RANDOM<<15) + $RANDOM )))" 

El algoritmo interno (al shell) «$ RANDOM» se basa en un registro de desplazamiento de retroalimentación lineal (LFSR). Esos no son criptográficamente Generadores seguros de números pseudoaleatorios (CSPRNG). Una mejor opción es usar bytes del dispositivo /dev/urandom. Eso requerirá la llamada a un volcado octal o hexadecimal externo.

$ printf "%.19f\n" "0x0.$(od -N 8 -An -tx1 /dev/urandom | tr -d " ")" 0.7532810412812978029 $ printf "%.19f\n" "0x0.$(hexdump -n 8 -v -e ""%02x"" /dev/urandom)" 0.9453460825607180595 

Una solución muy simple (pero no uniforme) para obtener un flotador es:

printf "0.%04d\n" $RANDOM 

Una forma de hacerlo uniforme en el rango [0,1) (sin incluir 1):

while a=$RANDOM; ((a>29999)); do :; done; printf "0.%04d\n" "$((a%10000))" 

Respuesta

Use $(( ( RANDOM % N ) + MIN ))

Reemplace N con el número MAX y MIN con el número mínimo que desea generar. (N como MAX es exclusivo, coloque N+1 para tener MAX, MIN inclusive).

O puede usar $(shuf -i MIN-MAX -n 1) en su lugar.

de man shuf :

-i, --input-range=LO-HI treat each number LO through HI as an input line -n, --head-count=COUNT output at most COUNT lines 

El -n 1 en shuf aquí significa generar solo un número aleatorio.

Esto generará números aleatorios entre 0 ~ 9999 con ceros iniciales usando printf (en resultado, el número 1

es exclusivo).

printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215 

Comentarios

  • Este tampoco producirá un número aleatorio verdadero en el rango dado, excepto en el caso de que N sea un divisor de 32767 (el límite superior de $ RANDOM).

Respuesta

En bash

bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000" 

donde 1/10000 es su aleatorio precisión y 4 dígitos de su precisión de salida

Respuesta

zsh tiene una rand48() función aritmética (envoltura de la erand48() función estándar) en su zsh/mathfunc módulo:

zmodload zsh/mathfunc printf "%.4f\n" $((rand48())) 

Mientras que $RANDOM es de 15 bits, pseudoaleatorio y reproducible, bash 5.1+ tiene un número entero de 32 bits más seguro $SRANDOM, basado en fuentes verdaderamente aleatorias cuando estén disponibles. No es compatible con la aritmética de coma flotante, pero al menos puede usarla para generar el generador pseudoaleatorio de awk «(que, de lo contrario, utiliza de forma predeterminada el resultado muy predecible de time()):

echo "$SRANDOM" | awk " { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }" 

(tenga en cuenta que todavía son solo 32 bits de entropía y awk hace una generación pseudoaleatoria determinista basada en esa semilla)

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *