Bash: Hogyan lehet véletlenszerű úszószámot előállítani a $ RANDOM használatával

Lehetséges-e valós véletlen számok generálása meghatározott pontossággal és meghatározott tartományban az Integer Random segítségével Generátor $ RANDOM? Például hogyan állíthatunk elő valós számot 4 pontossággal 0 és 1 között?

0.1234 0.0309 0.9001 0.0000 1.0000 

Egyszerű megoldás:

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

Megjegyzések

  • Kérjük, adja meg, mit jelent ” valós véletlen számok alatt “. Szüksége van egy véletlenszerű számok forrására, amelyet valamilyen részecskék bomlása generál, vagy örülni fog egy ál-véletlen generátornak? Alkalmazza ezeket a kriptográfiai vagy tudományos jelentőségű számokat, vagy csak valami olyasmit szeretne, amely ” véletlenszerűen néz ki “.
  • … vagy valójában ” lebegő ” vagy ” lebegőre gondolsz pont száma?
  • Köszönöm a megjegyzést. Szükségem van egy álvéletlenszám-generátorra a $ RANDOM alapján lebegőpontos számokhoz.
  • … meta-heurisztikus algoritmusok bash-ban történő megvalósításához.

Válasz

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

Ez n véletlenszerű számokat fog kiadni (a példában tíz) a [0,1) tartományban, négy tizedesjegygel. A rand() függvényt használja a awk fájlban (a awk szabványban nem, de a leggyakoribb awk implementációk), amely véletlenszerű értéket ad vissza ebben a tartományban. A véletlenszám-generátort az “s $RANDOM változó váltja ki.

Amikor egy awk program csak BEGIN blokkok (és nincsenek más kódblokkok), awk nem próbálja meg beolvasni a bemenetet a szokásos bemeneti folyamából.

Bármely OpenBSD rendszeren (vagy olyan rendszeren, amelynek ugyanaz az jot segédprogramja , eredetileg a 4.2BSD-ben található) a következő 10 véletlenszerű számot generál a megadott módon:

jot -p 4 -r 10 0 1 

Megjegyzések

  • Ténylegesen szigorúan véve, mivel a kimenet a rand() egy lebegő a [0,1) -en belül, valószínűleg nem egyenletes eloszlású, ha négy tizedesjegyre kerekítjük. Az lenne, ha az úszó végtelen pontosságú lenne, de nem ‘ t: valószínűleg ‘ véletlen bitekből áll elő , tehát 2 ^ N különböző érték van, és nem ‘ t térképeznek fel egységesen egy 1000 értékű halmazra. De mindaddig, amíg ezek az ál-véletlenszerű úszók elegendő bitet tartalmaznak, és te ‘ nem csinálsz semmi igazán pontos dolgot, valószínűleg nyertél ‘ t értesítést.

Válasz

Amint egy másik válasz rámutatott, vannak más segédprogramok is, amelyekkel létrehozhat véletlenszerű számok. Ebben a válaszban az erőforrásaimat $RANDOM -re és néhány alapvető számtani függvényre korlátozom.

Lebegőpontos számokhoz próbálkozzon hasonlóval:

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

Ezzel a legjobb pontosságot érheti el, mert a $RANDOM csak 0 és 32767 közötti számokat generál (beleértve a 32767-et is!) De én ” Megszegtem az alapvető számtani függvények használatára vonatkozó szabályomat is a bc meghívásával.

De mielőtt továbblépnék, két kérdést szeretnék megvizsgálni pontosság és tartomány a lebegőpontos számokhoz. Ezt követően megvizsgálom az egész számok generálását (és ha egész számokat generálhat, később később fel is oszthatja őket, hogy tizedesjegyet kapjon, ha bármilyen segédprogramot szeretne használni, amelyhez ezt szeretné elérni.)

Pontosság

A (z) $RANDOM/32768 megközelítést alkalmazva, mivel $RANDOM 0 és 32767 közötti értékeket generál, az $RANDOM/32768 eredménye is végesen sok érték lesz. Más szavakkal, ez még mindig egy diszkrét véletlen változó (és egy számítógéppel soha nem lesz képes elmenni ettől a ténytől). Ezt szem előtt tartva bizonyos fokú pontosságot érhet el a printf használatával.

Ha finomabb képet szeretne a intervallumban kezdhetett el gondolkodni a 32768-as alapon. Tehát elméletileg a $RANDOM + $RANDOM*32768 -nek egyenletes eloszlást kell adnia 0 és 1 073 741 823 között. De kétlem, hogy a parancssor nagyon jól fogja kezelni ezt a pontosságot. Néhány pont ehhez a konkrét esethez kapcsolódik:

  • Két független, egyenletesen elosztott, általában nem egységes véletlen változó összege. Ebben az esetben, legalábbis elméletileg (lásd a harmadik pontot).
  • Ne gondolja, hogy leegyszerűsítheti a $RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 ).A $RANDOM két előfordulása valóban két különböző esemény.
  • Nem tudom eléggé a $RANDOM generálva, hogy tudjuk, vajon kétszer így hívva valóban két független véletlenszerű esemény generálható-e.

Tartomány

Tekintsük a következőt: $RANDOM/32768. Ha számot szeretne egy tartományba, mondja ki: [a,b), akkor

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

a kívánt tartományba kerül .

Egész értékek előállítása

Először fontolja meg véletlenszerű számok generálását a [0,b) ahol b kisebb, mint 32768. Tekintsük a (z) q*b szorzatot, ahol q a 32768/b egész része. Ezután annyit tehet, hogy véletlen számot generál 0 és 32767 között, de kidobja azokat, amelyek nagyobbak vagy egyenlőek q*b. Hívja az így generált számot: G. Ekkor a G 0 és q*b tartományba esik, és eloszlása egyenletes lesz. Most alkalmazzon moduláris számtant, hogy ez az érték a kívánt tartományba kerüljön:

G % b 

Megjegyzés: véletlenszerűen állítson elő egy számot az alábbiak szerint:

$RANDOM % b 

nem hoz létre egységes eloszlást, hacsak b csak véletlenül a 32768.

bash szkript írása ehhez a

Számítás q*b a fent leírtak szerint fájdalomnak tűnik. De valójában nem “t”. A következőképpen szerezheti be:

q*b = 32768 - ( 32768 % b ) 

A Bash-ban ezt a

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

A következő kód véletlenszerű számot generál a 0..b tartományba (nem tartalmazza a b) . b=$1

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

Kiegészítés

Technikailag kevés oka van arra, hogy működjenek együtt

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

A következők ugyanezt fogják megvalósítani

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

Ez sokkal több munkát végez, de a számítógépek gyorsak.

Egész szám generálása nagyobb tartományban

Ezt “kitalálom”. Óvatosan kell eljárni, és valamikor figyelembe kell vennie a számítógép memória korlátait az aritmetikai műveletek kezelésekor.

Utolsó megjegyzés

Az elfogadott válasz nem hoz létre véletlenszerű számot 0 és 1 között.

Ennek megtekintéséhez próbálja ki a következőket p>

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

A (z) [0,1) felett valóban egyenletes eloszlás érdekében átlagosan .

De amint a fenti kódrészlet futtatásával láthatja, ehelyett valami 314.432 vagy 322.619. Mivel ez 1000 számot jelent, ennek átlaga .322. A generált számok ezen sorozatának valós átlaga .316362

Ezt a valódi átlagot a perl szkript használatával nyerheti meg.

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

Egész számokat adok ide, hogy jobban lássam, hogy a .$RANDOM használatának ez a megközelítése nem azt teszi, amit a legvalószínűbb módon szeretne. Más szavakkal, gondolkodjon el azon, hogy mely egész számok keletkeznek, és melyek maradnak ki teljesen. Elég nagy számot hagynak ki; jó néhány megduplázódik.

Válasz

Olyan rendszereken, ahol a shell printf képes megérteni a %a formátum (bash ksh zsh stb.), és ezért képes egy belső alapváltozást végrehajtani (hex -> dec) (egyenletes [0,1) tartományban 0,00003 0,99997-ig):

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

Akár több számjegyet is használhatna, ha több hívást egyesítene $RANDOM -re (0,000000001-től 0.999999999)

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

A ($ shell) “$ RANDOM” belső algoritmus egy lineáris-visszacsatolásos regiszteren (LFSR) alapszik. Ezek nem kriptográfiai szempontból Biztonságos pszeudo véletlenszám-generátorok (CSPRNG). Jobb megoldás, ha bájtokat használ a /dev/urandom eszközből. Ehhez külső oktális vagy hexadecimális hívásra lesz szükség.

$ 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 

Nagyon egyszerű (de nem egységes) megoldás az úszó megszerzéséhez:

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

Annak érdekében, hogy egységessé váljon a [0,1) tartományban (az 1-et nem tartalmazza):

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

Válasz

Használja a $(( ( RANDOM % N ) + MIN ))

Cserélje a N MAX számmal és MIN a létrehozni kívánt minimális számmal. (N mivel a MAX kizárólagos, tegye a következőt: N+1 hogy mindkettő MAX, MIN legyen).

Vagy használhatja helyette a $(shuf -i MIN-MAX -n 1) szót.

a 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 

A -n 1 itt: shuf itt azt jelenti, hogy csak egy véletlen számot generál.

Ez véletlen számokat generál 0 ~ 9999 között vezető nullákkal. a printf használatával (ennek eredményeként 1

kizárólagos).

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

Megjegyzések

  • Ez szintén nem fog valódi véletlen számot előállítani az adott tartományban, kivéve abban az esetben, ha N osztója 32767 (a $ RANDOM felső határa).

Válasz

On bash

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

ahol 1/10000 a véletlenszerű pontosság és 4 számolja a kimenet pontosságát

Válasz

rand48() számtani függvénye van (a erand48() standard függvény burkolója) a zsh/mathfunc modul:

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

Míg $RANDOM 15 bites, ál-véletlenszerű és reprodukálható, A bash 5.1+ biztonságosabb 32 bites egész számmal rendelkezik $SRANDOM, valóban véletlenszerű források alapján, ahol elérhetőek. Nem támogatja a lebegőpontos aritmetikát, de legalább használhatja awk “ál véletlen generátor (amely egyébként alapértelmezés szerint a time()):

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

(ne feledje, hogy még mindig csak 32 bit entrópia, és awk determinisztikus ál-véletlen generálást hajt végre az adott mag alapján)

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük