Bash: Jak wygenerować losową liczbę zmiennoprzecinkową za pomocą $ RANDOM

Czy możliwe jest wygenerowanie rzeczywistych liczb losowych z określoną precyzją i w określonym zakresie przy użyciu funkcji Integer Random Generator $ RANDOM? Na przykład, w jaki sposób możemy wygenerować liczbę rzeczywistą z dokładnością 4 od 0 do 1?

0.1234 0.0309 0.9001 0.0000 1.0000 

Proste obejście:

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

Komentarze

  • Określ, co masz na myśli, używając ” rzeczywistych liczb losowych „. Czy potrzebujesz źródła liczb losowych generowanych przez coś w rodzaju rozpadu cząstek, czy też będziesz zadowolony z generatora pseudolosowego? Czy Twoje podanie tych liczb ma znaczenie kryptograficzne lub naukowe, czy po prostu chcesz czegoś, co ” wygląda losowo „.
  • … czy faktycznie masz na myśli ” float ” czy ” pływające numer punktu?
  • Dziękujemy za komentarz. Potrzebuję generatora liczb pseudolosowych dla liczb zmiennoprzecinkowych opartego na $ RANDOM.
  • … do implementacji algorytmów metahheurystycznych w bash.

Odpowiedź

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

Spowoduje to wyświetlenie n liczb losowych (w przykładzie dziesięć) w zakresie [0,1) z czterema miejscami dziesiętnymi. Używa funkcji rand() w awk (nie w standardowym awk, ale zaimplementowana przez większość awk implementacjach), która zwraca losową wartość z tego zakresu. Generator liczb losowych jest zapełniany przez zmienną powłoki „s $RANDOM.

Gdy program awk ma tylko BEGIN bloki (i żadne inne bloki kodu), awk nie będzie próbowało czytać wejścia ze swojego standardowego strumienia wejściowego.

W dowolnym systemie OpenBSD (lub systemie, który ma to samo narzędzie jot , pierwotnie w 4.2BSD), następujące wygeneruje 10 losowych liczb, jak określono:

jot -p 4 -r 10 0 1 

Komentarze

  • Ściśle mówiąc, wynik z rand() to liczba zmiennoprzecinkowa w granicach [0,1), prawdopodobnie nie jest ' rozłożona dokładnie równo po zaokrągleniu do czterech cyfr dziesiętnych. Byłoby tak, gdyby liczba zmiennoprzecinkowa miała nieskończoną precyzję, ale nie jest ' t: ' prawdopodobnie nie zostanie wygenerowana z losowych bitów więc istnieje 2 ^ N różnych wartości i nie ' nie odwzorowują jednakowo zbioru 1000 wartości. Ale dopóki te pseudolosowe liczby zmiennoprzecinkowe mają wystarczającą liczbę bitów, a Ty ' nie robisz nic dokładnego, prawdopodobnie wygrywasz ' t zauważ.

Odpowiedź

Jak wskazano w innej odpowiedzi, istnieją inne narzędzia, których możesz użyć do wygenerowania losowe liczby. W tej odpowiedzi ograniczam swoje zasoby do $RANDOM i kilku podstawowych funkcji arytmetycznych.

W przypadku liczb zmiennoprzecinkowych spróbuj czegoś takiego jak

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

Zapewni to najwyższą precyzję, ponieważ $RANDOM generuje tylko liczby z zakresu od 0 do 32767. (w tym 32767!) Ale ja ” Złamaliśmy również moją zasadę używania podstawowych funkcji arytmetycznych, wywołując bc.

Ale zanim przejdę dalej, chciałbym przyjrzeć się dwóm problemom precyzja i zakres dla liczb zmiennoprzecinkowych. Następnie przyjrzę się generowaniu zakresu liczb całkowitych (a jeśli możesz wygenerować liczby całkowite, możesz później podzielić je, aby uzyskać ułamek dziesiętny, jeśli chcesz użyć dowolnych narzędzi, aby to osiągnąć).

Precyzja

Przyjmując podejście $RANDOM/32768, ponieważ $RANDOM generuje wartości od 0 do 32767, wynikiem $RANDOM/32768 będzie również nieskończenie wiele wartości. Innymi słowy, nadal jest to dyskretna zmienna losowa (z komputerem nigdy nie będziesz w stanie uciec od tego faktu). Mając to na uwadze, możesz osiągnąć pewien stopień precyzji , używając printf.

Jeśli chcesz dokładniej pokryć interwał, możesz zacząć myśleć w oparciu o bazę 32768. Zatem teoretycznie $RANDOM + $RANDOM*32768 powinno dać ci jednolity rozkład między 0 a 1 073 741 823. Jednak wątpię, czy wiersz poleceń poradzi sobie z taką precyzją bardzo dobrze. Kilka punktów związanych z tym konkretnym przypadkiem:

  • Suma dwóch niezależnych, równomiernie rozłożonych zmiennych losowych nie jest generalnie jednolita. W tym przypadku, przynajmniej teoretycznie (patrz trzeci punkt), tak jest.
  • Nie myśl, że możesz uprościć $RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 ).Dwa wystąpienia $RANDOM to tak naprawdę dwa różne zdarzenia.
  • Nie wiem wystarczająco, jak $RANDOM jest wygenerowane, aby wiedzieć, czy wywołanie go dwukrotnie w ten sposób spowoduje powstanie dwóch niezależnych zdarzeń losowych.

Zakres

Rozważmy tylko $RANDOM/32768. Jeśli chcesz, aby liczba należała do zakresu, powiedz [a,b), a następnie

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

przeniesie Cię do żądanego zakresu .

Generowanie wartości całkowitych

Najpierw rozważ wygenerowanie liczb losowych między [0,b) gdzie b jest mniejsze niż 32768. Rozważ produkt q*b, gdzie q to część całkowita 32768/b. Następnie możesz wygenerować liczby losowe z zakresu od 0 do 32767, ale wyrzucić te, które są większe lub równe q*b. Zadzwoń na wygenerowany w ten sposób numer G. Wtedy G będzie mieścić się w zakresie od 0 do q*b, a jego rozkład będzie jednolity. Teraz zastosuj arytmetykę modularną, aby zmniejszyć tę wartość do żądanego zakresu:

G % b 

Uwaga, losowe generowanie liczby w następujący sposób

$RANDOM % b 

nie utworzy jednolitej dystrybucji, chyba że b jest jednym z dzielników 32768.

Pisanie skryptu bash dla tego

Obliczanie q*b jak opisano powyżej brzmi jak ból. Ale to naprawdę nie jest „t. Możesz to uzyskać w następujący sposób:

q*b = 32768 - ( 32768 % b ) 

W Bash możesz to uzyskać za pomocą

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

Poniższy kod wygeneruje losową liczbę z zakresu 0..b (nie obejmuje b) . b=$1

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

Dodatek

Z technicznego punktu widzenia nie ma powodu, aby pracować z

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

Poniższe czynności pozwolą osiągnąć to samo

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

To dużo więcej pracy, ale komputery są szybkie.

Generowanie liczby całkowitej w większym zakresie

Pozwolę ci to rozgryźć. Należy zachować ostrożność, aw pewnym momencie „będziesz musiał wziąć pod uwagę ograniczenia pamięci komputera podczas wykonywania operacji arytmetycznych.

Uwaga końcowa

Zaakceptowana odpowiedź nie utworzy jednolitej liczby losowej w zakresie od 0 do 1.

Aby to zobaczyć, spróbuj wykonać następujące czynności

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

Aby uzyskać prawdziwie jednorodną dystrybucję w [0,1), średnia powinna być bliska 0.500.

Ale jak widać po uruchomieniu powyższego fragmentu, zamiast tego otrzymasz coś w rodzaju 314.432 lub 322.619. Ponieważ jest to 1000 liczb, średnia z tego wynosi .322. Prawdziwa średnia dla tej sekwencji wygenerowanych liczb to .316362

Prawdziwą średnią można uzyskać za pomocą skryptu Perla

 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; }" 

Dodam tutaj liczby całkowite, aby pomóc Ci zobaczyć, jak to podejście do korzystania z .$RANDOM nie robi tego, co najprawdopodobniej chcesz. Innymi słowy, zastanów się, które liczby całkowite są generowane, a które całkowicie pomijane. Całkiem duża liczba jest pomijana; całkiem sporo jest podwojonych.

Odpowiedź

W systemach, w których printf powłoki jest w stanie zrozumieć %a (bash ksh zsh itp.) i dlatego jest w stanie wykonać wewnętrzną zmianę bazy (hex -> dec) (jednolita w [0,1) w zakresie od 0,00003 do 0.99997):

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

Możesz nawet użyć większej liczby cyfr, łącząc więcej wywołań z $RANDOM (od 0.000000001 do 0.999999999)

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

Wewnętrzny (do powłoki) algorytm „$ RANDOM” oparty jest na rejestrze przesuwnym ze sprzężeniem liniowym (LFSR). Nie są one kryptograficznie Bezpieczne generatory liczb pseudolosowych (CSPRNG). Lepszą opcją jest użycie bajtów z urządzenia /dev/urandom. Będzie to wymagało wywołania zewnętrznego zrzutu ósemkowego lub szesnastkowego.

$ 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 

Bardzo prostym (ale niejednolitym) rozwiązaniem uzyskania wartości zmiennoprzecinkowej jest:

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

Sposób ujednolicenia go w zakresie [0,1) (nie obejmuje 1):

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

Odpowiedź

Użyj $(( ( RANDOM % N ) + MIN ))

Zastąp N z liczbą MAX i MIN z minimalną liczbą, którą chcesz wygenerować. (N ponieważ MAX jest wyłączny, wstaw N+1 aby mieć oba MAX i MIN włącznie).

Lub możesz zamiast tego użyć $(shuf -i MIN-MAX -n 1).

from 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 w shuf tutaj oznacza wygenerowanie tylko jednej losowej liczby.

Spowoduje to wygenerowanie liczb losowych z zakresu 0 ~ 9999 z zerami na początku używając printf (w rezultacie liczba 1

jest wyłączny).

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

Komentarze

  • To również nie zwróci prawdziwej liczby losowej w podanym zakresie, z wyjątkiem przypadku, gdy N jest dzielnikiem 32767 (górna granica $ RANDOM).

Odpowiedź

On bash

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

gdzie 1/10000 to Twój losowy precyzja i 4 cyfry dokładności wyniku

Odpowiedź

zsh ma rand48() funkcję arytmetyczną (opakowanie standardowej funkcji erand48()) w zsh/mathfunc module:

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

Podczas gdy $RANDOM ma 15 bitów, jest pseudolosowe i odtwarzalne, bash 5.1+ ma bezpieczniejszą 32-bitową liczbę całkowitą $SRANDOM, opartą na prawdziwie losowych źródłach, jeśli są dostępne. Nie obsługuje arytmetyki zmiennoprzecinkowej, ale przynajmniej możesz jej użyć do zapoczątkowania pseudolosowego generatora awk (który w innym przypadku domyślnie używa bardzo przewidywalnego wyniku time()):

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

(pamiętaj, że to wciąż tylko 32 bity entropii i awk wykonuje deterministyczne pseudolosowe generowanie na podstawie tego ziarna)

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *