È possibile generare numeri casuali reali con una precisione specifica e in un intervallo specifico utilizzando Integer Random Generatore $ RANDOM? Ad esempio, come possiamo generare un numero reale con una precisione 4 compresa tra 0 e 1?
0.1234 0.0309 0.9001 0.0000 1.0000
Una semplice soluzione:
printf "%d04.%d04\n" $RANDOM $RANDOM
Commenti
- Specifica cosa intendi per ” numeri casuali reali “. Stai richiedendo una fonte di numeri casuali generati da qualcosa come il decadimento delle particelle o sarai soddisfatto di un generatore pseudo-casuale? La tua applicazione di questi numeri ha un significato crittografico o scientifico o desideri semplicemente qualcosa che ” sembri casuale “.
- … o intendi effettivamente ” float ” o ” floating numero punto?
- Grazie per il commento. Ho bisogno di un generatore di numeri pseudo casuali per numeri in virgola mobile basati su $ RANDOM.
- … per implementare algoritmi meta euristici in bash.
Risposta
awk -v n=10 -v seed="$RANDOM" "BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }"
Questo produrrà n
numeri casuali (dieci nellesempio) nellintervallo [0,1) con quattro cifre decimali. Utilizza la funzione rand()
in awk
(non nello standard awk
ma implementata dai più comuni awk
implementazioni) che restituisce un valore casuale in quellintervallo. Il generatore di numeri casuali è seminato dalla variabile $RANDOM
della shell.
Quando un programma awk
ha solo BEGIN
(e nessun altro blocco di codice), awk
non tenterà di leggere linput dal suo flusso di input standard.
Su qualsiasi sistema OpenBSD (o sistema che ha la stessa jot
utility , originariamente in 4.2BSD), quanto segue genererà 10 numeri casuali come specificato:
jot -p 4 -r 10 0 1
Commenti
- In senso stretto, poiché loutput di
rand()
è un float allinterno di [0,1), probabilmente non è ‘ t distribuito esattamente in modo uniforme quando arrotondato a quattro cifre decimali. Lo sarebbe, se il float fosse di precisione infinita, ma non è ‘ t: è probabile che ‘ sia generato da bit casuali , quindi ci sono 2 ^ N valori diversi, e non ‘ t mappano uniformemente su un insieme di 1000 valori. Ma fino a quando quei float pseudo-casuali hanno abbastanza bit e ‘ non stai facendo nulla di veramente preciso, probabilmente hai vinto ‘ t avviso.
Risposta
Come sottolineato in unaltra risposta, ci sono altre utilità che puoi utilizzare per generare numeri casuali. In questa risposta, limito le mie risorse a $RANDOM
e ad alcune funzioni aritmetiche di base.
Per i numeri in virgola mobile, prova qualcosa come
printf "%s\n" $(echo "scale=8; $RANDOM/32768" | bc )
In questo modo otterrai la migliore precisione perché $RANDOM
genera solo numeri compresi tra 0 e 32767. (incluso 32767!) Ma io ” ho anche infranto la mia regola sulluso delle funzioni aritmetiche di base invocando bc
.
Ma prima di andare avanti, vorrei esaminare due problemi precisione e intervallo per i numeri in virgola mobile. Dopodiché, esaminerò la generazione di un intervallo di numeri interi (e se puoi generare numeri interi, puoi dividerli in seguito per ottenere un decimale se desideri utilizzare le utilità che preferisci per farlo.)
Precisione
Adottando lapproccio di $RANDOM/32768
, da $RANDOM
genera valori da 0 a 32767, il risultato di $RANDOM/32768
sarà similmente un numero finito di valori. In altre parole, è ancora una variabile casuale discreta (e con un computer non sarai mai in grado di sfuggire a questo fatto). Con questo in mente, puoi ottenere un certo grado di precisione utilizzando printf
.
Se desideri una copertura più fine del intervallo, potresti iniziare a pensare in base 32768. Quindi, in teoria $RANDOM + $RANDOM*32768
dovrebbe darti una distribuzione uniforme tra 0 e 1.073.741.823. Ma, dubito che la riga di comando gestirà molto bene questa precisione. Un paio di punti relativi a questo caso particolare:
- La somma di due variabili casuali indipendenti e distribuite uniformemente non è generalmente uniforme. In questo caso, almeno in teoria (vedi terzo punto), lo sono.
- Non pensare di poter semplificare
$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )
.Le due occorrenze di$RANDOM
sono in realtà due eventi diversi. - Non ne so abbastanza di come sia
$RANDOM
generato per sapere se chiamarlo due volte in questo modo genererà veramente due eventi casuali indipendenti.
Intervallo
Consideriamo solo $RANDOM/32768
. Se desideri un numero in un intervallo, dì [a,b)
, quindi
$RANDOM/32768*(b-a) + a
ti porterà nellintervallo desiderato .
Generazione di valori interi
Innanzitutto, considera la possibilità di generare numeri casuali tra [0,b)
dove b
è minore di 32768
. Considera il prodotto q*b
, dove q
è la parte intera di 32768/b
. Quindi quello che puoi fare è generare un numero casuale compreso tra 0 e 32767, ma eliminare quelli che sono maggiori o uguali a q*b
. Chiama il numero così generato G
. Quindi G
ricadrà nellintervallo da 0 a q*b
e la sua distribuzione sarà uniforme. Ora, applica laritmetica modulare per ottenere questo valore ridotto nellintervallo desiderato:
G % b
Nota, generando casualmente un numero come segue
$RANDOM % b
non creerà una distribuzione uniforme, a meno che b
non sia uno dei divisori di 32768
.
Scrittura di uno script bash per questo
Calcolo q*b
come descritto sopra suona come un dolore. Ma in realtà non è “t. Puoi ottenerlo come segue:
q*b = 32768 - ( 32768 % b )
In Bash, puoi ottenerlo con
$((32768 - $((32768 % b)) ))
Il seguente codice genererà un numero casuale nellintervallo 0..b
(non comprensivo di b
) . b=$1
m=$((32768 - $((32768 % $1)) )) a=$RANDOM while (( $a > $m )); do a=$RANDOM done a=$(($a % $1)) printf "$a\n"
Addendum
Tecnicamente, ci sono pochi motivi per lavorare con
m=$((32768 - $((32768 % $1)) ))
Quanto segue porterà a termine la stessa cosa
a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n"
È “molto più faticoso, ma i computer sono veloci.
Generazione di un numero intero in un intervallo più ampio
Ti lascio capire. È necessario prestare attenzione e ad un certo punto dovrai prendere in considerazione i limiti di memoria del computer nella gestione delle operazioni aritmetiche.
Nota finale
La risposta accettata non creerà un numero casuale uniformemente compreso tra 0 e 1.
Per vedere questo, prova quanto segue
$ for i in {1..1000}; do echo .$RANDOM; done | awk "{ a += $1 } END { print a }"
Per una distribuzione veramente uniforme su [0,1)
dovresti vedere una media vicina a 0.500
.
Ma come puoi vedere eseguendo lo snippet sopra, otterrai invece qualcosa come 314.432
o 322.619
. Poiché sono 1000 numeri, la media è .322
. La media reale per questa sequenza di numeri generati è .316362
Puoi ottenere questa media reale utilizzando lo script 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; }"
Sto aggiungendo degli interi qui per aiutarti a vedere come questo approccio di usare .$RANDOM
non sta facendo quello che molto probabilmente vuoi che faccia. In altre parole, pensa a quali numeri interi vengono generati e quali vengono persi del tutto. Un numero abbastanza elevato viene ignorato; alcuni sono raddoppiati.
Risposta
Su sistemi in cui printf di shell è in grado di capire il %a
(bash ksh zsh, ecc.) e quindi è in grado di eseguire un cambio di base interno (hex -> dec) (uniforme in [0,1)
range da 0,00003 a 0.99997):
printf "%.5f\n" "$(printf "0x0.%04xp1" $RANDOM)"
potresti persino utilizzare più cifre combinando più chiamate a $RANDOM
(da 0.000000001 a 0.999999999)
printf "%.9f\n" "$(printf "0x0.%08xp2" $(( ($RANDOM<<15) + $RANDOM )))"
Lalgoritmo “$ RANDOM” interno (alla shell) si basa su un registro a scorrimento a feedback lineare (LFSR). Non sono crittograficamente Generatori di numeri pseudo casuali sicuri (CSPRNG). Unopzione migliore è utilizzare i byte dal dispositivo /dev/urandom
. Ciò richiederà la chiamata a ottale esterno o dump esadecimale.
$ 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 soluzione molto semplice (ma non uniforme) per ottenere un float è:
printf "0.%04d\n" $RANDOM
Un modo per renderlo uniforme nellintervallo [0,1)
(escluso 1):
while a=$RANDOM; ((a>29999)); do :; done; printf "0.%04d\n" "$((a%10000))"
Risposta
Utilizza $(( ( RANDOM % N ) + MIN ))
Sostituisci N
con il numero MAX e MIN con il numero minimo che desideri generare. (N
poiché MAX è esclusivo, metti N+1
per avere entrambi MAX e MIN inclusi).
Oppure puoi utilizzare $(shuf -i MIN-MAX -n 1)
.
da 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
Il -n 1
in shuf
qui significa generare solo un numero casuale.
Questo genererà numeri casuali compresi tra 0 ~ 9999 con zeri iniziali utilizzando printf
(nel risultato, numero 1
è esclusivo).
printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215
Commenti
- Questo inoltre non produrrà un vero numero casuale nellintervallo dato tranne nel caso in cui N è un divisore di 32767 (il limite superiore di $ RANDOM).
Risposta
On bash
bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000"
dove 1/10000
è il tuo casuale precisione e 4
cifre la precisione delloutput
Risposta
zsh
ha una rand48()
funzione aritmetica (wrapper della erand48()
funzione standard) nella sua zsh/mathfunc
modulo:
zmodload zsh/mathfunc printf "%.4f\n" $((rand48()))
Mentre $RANDOM
è di 15 bit, pseudo-casuale e riproducibile, bash
5.1+ ha un numero intero a 32 bit più sicuro $SRANDOM
, basato su fonti veramente casuali, ove disponibili. Non supporta laritmetica in virgola mobile, ma almeno puoi usarlo per seminare lo pseudo generatore di awk
“(che altrimenti per impostazione predefinita usa il risultato molto prevedibile di time()
):
echo "$SRANDOM" | awk " { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }"
(tieni presente che sono ancora solo 32 bit di entropia e awk
genera una generazione pseudo-casuale deterministica basata su quel seme)