Este posibil să se genereze numere aleatoare reale cu o precizie specifică și într-un anumit interval folosind Integer Random Generator $ RANDOM? De exemplu, cum putem genera un număr real cu precizie 4 între 0 și 1?
0.1234 0.0309 0.9001 0.0000 1.0000
O soluție simplă:
printf "%d04.%d04\n" $RANDOM $RANDOM
Comentarii
- Vă rugăm să specificați ce înțelegeți prin ” numere aleatoare reale „. Aveți nevoie de o sursă de numere aleatorii generate de ceva precum degradarea particulelor sau veți fi mulțumit de un generator pseudo-aleator? Aplicația dvs. pentru aceste numere are semnificație criptografică sau științifică sau doriți doar ceva care ” arată aleatoriu „.
- … sau de fapt vrei să spui ” float ” sau ” plutitor numărul punctului?
- Vă mulțumim pentru comentariu. Am nevoie de un generator de numere pseudo-aleatorii pentru numerele cu virgulă mobilă bazate pe $ RANDOM.
- … pentru implementarea algoritmilor meta euristici în bash.
Răspuns
awk -v n=10 -v seed="$RANDOM" "BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }"
Aceasta va genera n
numere aleatorii (zece în exemplu) în intervalul [0,1) cu patru cifre zecimale. Folosește funcția rand()
în awk
(nu în standardul awk
, dar implementat de cele mai comune awk
implementări) care returnează o valoare aleatorie în intervalul respectiv. Generatorul de numere aleatorii este însămânțat de variabila shell „s $RANDOM
.
Când un program awk
are numai BEGIN
blocuri (și niciun alt bloc de cod), awk
nu va încerca să citească intrarea din fluxul său standard de intrare.
Pe orice sistem OpenBSD (sau sistem care are același utilitar jot
, inițial în 4.2BSD), următoarele va genera 10 numere aleatorii, după cum se specifică:
jot -p 4 -r 10 0 1
Comentarii
- Într-adevăr strict, din moment ce rezultatul din
rand()
este un float în cadrul [0,1), probabil că nu este ‘ distribuit exact în mod egal când este rotunjit la patru cifre zecimale. Ar fi, dacă float-ul ar fi de o precizie infinită, dar nu este ‘ t: ‘ este probabil să fie generat din biți aleatori , deci există 2 ^ N valori diferite și ele nu ‘ t mapează uniform la un set de 1000 de valori. Dar atâta timp cât acele flotări pseudo-aleatorii au destui biți și ‘ nu faceți nimic cu adevărat exact, probabil că ați câștigat ‘ t observație.
Răspuns
După cum sa subliniat într-un alt răspuns, există și alte utilități pe care le puteți utiliza pentru a genera numere aleatorii. În acest răspuns, îmi limitez resursele la $RANDOM
și câteva funcții aritmetice de bază.
Pentru numerele cu virgulă mobilă, încercați ceva de genul
printf "%s\n" $(echo "scale=8; $RANDOM/32768" | bc )
Aceasta vă va oferi cea mai bună precizie, deoarece $RANDOM
generează numai numere între 0 și 32767. (inclusiv 32767!) Dar, eu ” De asemenea, mi-am încălcat regula cu privire la utilizarea funcțiilor aritmetice de bază invocând bc
.
Dar înainte de a continua, aș vrea să analizez două aspecte precizie și interval pentru numerele cu virgulă mobilă. După aceea, mă voi uita la generarea unei game de numere întregi (și dacă puteți genera numere întregi, le puteți împărți ulterior pentru a obține o zecimală dacă doriți să folosiți orice utilitare preferați pentru a realiza acest lucru.)
Precizie
Abordarea $RANDOM/32768
, deoarece $RANDOM
generează valori de la 0 la 32767, rezultatul $RANDOM/32768
va fi, de asemenea, finit de multe valori. Cu alte cuvinte, este încă o variabilă discretă aleatorie (și cu un computer nu veți putea niciodată să scăpați de acest fapt). Având în vedere acest lucru, puteți realiza un anumit grad de precizie utilizând printf
.
Dacă doriți o acoperire mai fină a interval, ați putea începe să vă gândiți la baza 32768. Deci, în teorie $RANDOM + $RANDOM*32768
ar trebui să vă ofere o distribuție uniformă între 0 și 1.073.741.823. Dar, mă îndoiesc că linia de comandă va rezolva foarte bine acea precizie. Câteva puncte referitoare la acest caz particular:
- Suma a două variabile aleatoare independente, distribuite uniform, nu în general uniforme. În acest caz, cel puțin teoretic vorbind (a se vedea al treilea punct), acestea sunt.
- Nu credeți că puteți simplifica
$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )
.Cele două apariții ale$RANDOM
sunt într-adevăr două evenimente diferite. - Nu știu suficient despre cât este
$RANDOM
generat pentru a ști dacă apelarea acestuia de două ori așa va genera cu adevărat două evenimente aleatorii independente.
Interval
Să considerăm doar $RANDOM/32768
. Dacă doriți un număr dintr-un interval, spuneți [a,b)
, apoi
$RANDOM/32768*(b-a) + a
vă va ateriza în intervalul dorit .
Generarea valorilor întregi
În primul rând, luați în considerare generarea de numere aleatoare între [0,b)
unde b
este mai mic decât 32768
. Luați în considerare produsul q*b
, unde q
este partea întreagă a 32768/b
. Apoi, ceea ce puteți face este să generați un număr aleatoriu între 0 și 32767, dar să le aruncați pe cele care sunt mai mari sau egale cu q*b
. Apelați numărul astfel generat G
. Apoi G
va intra în intervalul de la 0 la q*b
, iar distribuția acestuia va fi uniformă. Acum, aplicați aritmetica modulară pentru a obține această valoare redusă în intervalul dorit:
G % b
Notă, generând aleatoriu un număr după cum urmează
$RANDOM % b
nu va crea o distribuție uniformă, cu excepția cazului în care b
se întâmplă doar să fie unul dintre divizorii 32768
.
Scrierea unui script bash pentru acest
Calculare q*b
așa cum este descris mai sus sună ca o durere. Dar chiar nu este „t. Puteți obține acest lucru după cum urmează:
q*b = 32768 - ( 32768 % b )
În Bash, puteți obține acest lucru cu
$((32768 - $((32768 % b)) ))
Următorul cod va genera un număr aleatoriu în intervalul 0..b
(nu include b
) . b=$1
m=$((32768 - $((32768 % $1)) )) a=$RANDOM while (( $a > $m )); do a=$RANDOM done a=$(($a % $1)) printf "$a\n"
Addendum
Din punct de vedere tehnic, există puține motive pentru a lucra cu
m=$((32768 - $((32768 % $1)) ))
Următoarele vor realiza același lucru
a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n"
Este „mult mai mult de lucru, dar computerele sunt rapide.
Generarea unui întreg într-un interval mai mare
Vă voi permite să vă dați seama de acest lucru. Trebuie să aveți grijă și, la un moment dat, va trebui să luați în considerare limitările de memorie ale computerului în gestionarea operațiilor aritmetice.
Notă finală
Răspunsul acceptat nu va crea un număr aleatoriu uniform de la 0 la 1.
Pentru a vedea acest lucru, încercați următoarele
$ for i in {1..1000}; do echo .$RANDOM; done | awk "{ a += $1 } END { print a }"
Pentru o distribuție cu adevărat uniformă pe [0,1)
ar trebui să vedeți o medie apropiată de 0.500
.
Dar după cum puteți vedea executând fragmentul de mai sus, veți obține în schimb ceva de genul 314.432
sau 322.619
. Întrucât are 1000 de numere, media acestuia este .322
. Media adevărată pentru această secvență de numere generate este .316362
Puteți obține această medie adevărată folosind scriptul 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; }"
Voi adăuga numere întregi aici pentru a vă ajuta să vedeți cum această abordare a utilizării .$RANDOM
nu face ceea ce doriți cel mai probabil să facă. Cu alte cuvinte, gândiți-vă care numere întregi sunt generate și care dintre ele sunt omise cu totul. Un număr destul de mare este omis; destul de multe sunt dublate.
Răspuns
Pe sistemele în care print-ul shell-ului este capabil să înțeleagă %a
format (bash ksh zsh etc.) și, prin urmare, este capabil să efectueze o schimbare de bază internă (hex -> dec) (uniformă în intervalul [0,1)
de la 0.00003 până la 0.99997):
printf "%.5f\n" "$(printf "0x0.%04xp1" $RANDOM)"
Puteți folosi chiar și mai multe cifre combinând mai multe apeluri către $RANDOM
(de la 0.000000001 la 0.999999999)
printf "%.9f\n" "$(printf "0x0.%08xp2" $(( ($RANDOM<<15) + $RANDOM )))"
Algoritmul intern (către shell) „$ RANDOM” se bazează într-un registru de schimbare a feedbackului liniar (LFSR). Acestea nu sunt criptografice Secure Pseudo Random Number Generators (CSPRNGs). O opțiune mai bună este să utilizați octeți de pe dispozitivul /dev/urandom
. Acest lucru va necesita apelul către o descărcare externă octală sau hexagonală.
$ 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
O soluție foarte simplă (dar neuniformă) pentru a obține un plutitor este:
printf "0.%04d\n" $RANDOM
Un mod de a-l uniformiza în intervalul [0,1)
(fără a include 1):
while a=$RANDOM; ((a>29999)); do :; done; printf "0.%04d\n" "$((a%10000))"
Răspuns
Utilizați $(( ( RANDOM % N ) + MIN ))
Înlocuiți N
cu numărul MAX și MIN cu numărul minim pe care doriți să îl generați. (N
deoarece MAX este exclusiv, puneți N+1
pentru a avea ambele MAX, MIN inclusiv).
Sau puteți utiliza în schimb $(shuf -i MIN-MAX -n 1)
.
din 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
-n 1
în shuf
aici înseamnă că se generează doar un număr aleatoriu.
Acest lucru va genera numere aleatorii între 0 ~ 9999 cu zerouri din partea de sus folosind printf
(în rezultat, numărul 1
este exclusiv).
printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215
Comentarii
- Aceasta de asemenea, nu va produce un număr aleatoriu adevărat în intervalul dat, cu excepția cazului în care N este un divizor al lui 32767 (limita superioară a lui $ RANDOM).
Răspuns
On bash
bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000"
unde 1/10000
este aleatoriu precizie și 4
cifre precizia de ieșire
Răspuns
zsh
are o funcție aritmetică rand48()
(înfășurarea funcției standard erand48()
) în zsh/mathfunc
modul:
zmodload zsh/mathfunc printf "%.4f\n" $((rand48()))
În timp ce $RANDOM
este de 15 biți, pseudo-aleatoriu și reproductibil, bash
5.1+ are un număr întreg de 32 biți mai sigur $SRANDOM
, bazat pe surse cu adevărat aleatorii acolo unde este disponibil. Nu acceptă aritmetica în virgulă mobilă, dar cel puțin o puteți folosi pentru a semăna awk
„generatorul pseudo aleatoriu (care altfel implicit folosește rezultatul foarte previzibil al time()
):
echo "$SRANDOM" | awk " { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }"
(rețineți că este încă doar 32 de biți de entropie și awk
face o generație pseudo-aleatorie deterministă bazată pe acea sămânță)