Bash: Jak generovat náhodné floatové číslo pomocí $ RANDOM

Je možné vygenerovat skutečná náhodná čísla se specifickou přesností a v konkrétním rozsahu pomocí Integer Random Generátor $ RANDOM? Například, jak můžeme vygenerovat reálné číslo s přesností 4 mezi 0 a 1?

0.1234 0.0309 0.9001 0.0000 1.0000 

Jednoduché řešení:

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

Komentáře

  • Uveďte, co máte na mysli, “ skutečnými náhodnými čísly „. Požadujete zdroj náhodných čísel generovaných něčím, jako je rozpad částic, nebo budete spokojeni s pseudonáhodným generátorem? Je vaše aplikace těchto čísel kryptografického nebo vědeckého významu, nebo chcete jen něco, co “ vypadá náhodně „.
  • … nebo máte na mysli “ float “ nebo “ plovoucí číslo bodu?
  • Děkujeme za komentář. Potřebuji generátor pseudonáhodných čísel pro čísla s plovoucí desetinnou čárkou na základě $ RANDOM.
  • … pro implementaci meta heuristických algoritmů v bash.

odpověď

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

Tím se vypíše n náhodná čísla (v příkladu deset) v rozsahu [0,1) se čtyřmi desetinnými místy. Využívá funkci rand() v awk (není ve standardu awk, ale implementována většinou awk implementace), která vrací náhodnou hodnotu v tomto rozsahu. Generátor náhodných čísel je nasazen proměnnou shellu „s $RANDOM.

Když má program awk pouze BEGIN bloky (a žádné další bloky kódu), awk se nepokusí číst vstup ze svého standardního vstupního proudu.

Na jakémkoli systému OpenBSD (nebo systému, který má stejný jot nástroj , původně ve verzi 4.2BSD), následující vygeneruje 10 náhodných čísel, jak je uvedeno:

jot -p 4 -r 10 0 1 

Komentáře

  • Opravdu přísně vzato, protože výstup of rand() je float v rámci [0,1), pravděpodobně není ‚ t přesně rovnoměrně rozložen, když je zaokrouhleno na čtyři desetinná místa. Bylo by to, kdyby byl plovák nekonečně přesný, ale není pravděpodobné, že ‚ t: je ‚ generován z náhodných bitů , takže existují 2 ^ N různých hodnot a nemapují ‚ jednotně na sadu 1000 hodnot. Ale pokud mají tyto pseudonáhodné plováky dostatek bitů a vy ‚ neděláte nic opravdu přesného, pravděpodobně jste ‚ t upozornění.

Odpověď

Jak bylo uvedeno v jiné odpovědi, existují další nástroje, které můžete použít ke generování náhodná čísla. V této odpovědi omezím své zdroje na $RANDOM a několik základních aritmetických funkcí.

U čísel s plovoucí desetinnou čárkou zkuste něco jako

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

Tím získáte nejlepší přesnost, protože $RANDOM generuje pouze čísla mezi 0 a 32767. (včetně 32767!) Ale já “ Také jsem porušil své pravidlo o používání základních aritmetických funkcí vyvoláním bc.

Ale než přejdu dál, chtěl bych se podívat na dva problémy přesnost a rozsah pro čísla s plovoucí desetinnou čárkou. Poté se podívám na generování řady celých čísel (a pokud můžete generovat celá čísla, můžete je později rozdělit, abyste získali desetinnou čárku, pokud si přejete použít libovolné nástroje, které chcete dosáhnout.)

Přesnost

Využívá přístup $RANDOM/32768, protože $RANDOM generuje hodnoty od 0 do 32767, výsledkem $RANDOM/32768 bude rovněž konečně mnoho hodnot. Jinými slovy, stále jde o diskrétní náhodnou proměnnou (as počítačem se nikdy nebudete moci od této skutečnosti dostat). S ohledem na to pak můžete dosáhnout určité míry přesnosti pomocí printf.

Pokud chcete jemnější pokrytí interval, můžete začít uvažovat v základně 32768. Teoreticky by vám tedy $RANDOM + $RANDOM*32768 mělo poskytnout jednotné rozdělení mezi 0 a 1 073 741 823. Ale pochybuji, že příkazový řádek zvládne tuto přesnost velmi dobře. Několik bodů týkajících se tohoto konkrétního případu:

  • Součet dvou nezávislých, rovnoměrně rozložených náhodných proměnných, které nejsou obecně jednotné. V tomto případě, alespoň teoreticky (viz třetí bod), jsou.
  • Nemyslíte si, že můžete zjednodušit $RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 ).Dva výskyty $RANDOM jsou opravdu dvě různé události.
  • Nevím dost o tom, jak $RANDOM je generováno, aby se zjistilo, zda toto volání dvakrát vygeneruje skutečně dvě nezávislé náhodné události.

Rozsah

Zvažme pouze $RANDOM/32768. Pokud chcete číslo v rozsahu, řekněme [a,b), pak

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

se dostanete do požadovaného rozsahu .

Generování celočíselných hodnot

Nejprve zvažte generování náhodných čísel mezi [0,b) kde b je méně než 32768. Zvažte produkt q*b, kde q je celočíselná část 32768/b. Pak můžete vygenerovat náhodné číslo mezi 0 a 32767, ale vyhodit ta, která jsou větší nebo rovna q*b. Zavolejte na takto vygenerované číslo G. Potom G bude spadat do rozsahu 0 až q*b a jeho rozdělení bude jednotné. Nyní použijte modulární aritmetiku, aby se tato hodnota snížila do požadovaného rozsahu:

G % b 

Všimněte si, že náhodně vygenerujete číslo následujícím způsobem.

$RANDOM % b 

nevytvoří jednotnou distribuci, pokud b nebude náhodou jedním z dělitelů 32768.

Psaní bash skriptu pro tento

Výpočet q*b jak je popsáno výše, zní jako bolest. Ale opravdu to není „t. Můžete to získat takto:

q*b = 32768 - ( 32768 % b ) 

V Bashi to můžete získat pomocí

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

Následující kód vygeneruje náhodné číslo v rozsahu 0..b (nezahrnuje b) . b=$1

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

Dodatek

Technicky vzato neexistuje malý důvod pro práci s

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

Tímto bude dosaženo stejné věci

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

Je to mnohem více práce, ale počítače jsou rychlé.

Generování celého čísla ve větším rozsahu

Nechám vás to zjistit. Je třeba postupovat opatrně a v určitém okamžiku budete při zpracování aritmetických operací muset vzít v úvahu omezení paměti počítače.

Závěrečná poznámka

Přijatá odpověď nevytvoří náhodné číslo rovnoměrně od 0 do 1.

Chcete-li to zobrazit, zkuste následující

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

Pro skutečně rovnoměrné rozdělení v [0,1) byste měli vidět průměrně blízko 0.500.

Ale jak vidíte spuštěním výše uvedeného fragmentu, místo toho získáte něco jako 314.432 nebo 322.619. Jelikož se jedná o 1000 čísel, je průměrná hodnota .322. Skutečný průměr pro tuto sekvenci generovaných čísel je .316362

Tento skutečný průměr můžete získat pomocí skriptu 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; }" 

Přidávám sem celá čísla, abych vám pomohl zjistit, jak tento přístup používání .$RANDOM nedělá to, co s největší pravděpodobností chcete. Jinými slovy, přemýšlejte o tom, která celá čísla jsou generována a která jsou zcela vynechána. Docela velké množství je přeskočeno; jich je zdvojnásobeno.

Odpověď

V systémech, kde je printf prostředí shell schopen porozumět %a formát (bash ksh zsh atd.), a proto je schopen provádět vnitřní změnu základny (hex -> dec) (jednotná v [0,1) rozsahu od 0,00003 až 0,99997):

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

Můžete dokonce použít více číslic kombinací více volání do $RANDOM (od 0,000000001 do 0,999999999)

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

Interní (do shellu) algoritmus „$ RANDOM“ je založen na posuvném registru s lineární zpětnou vazbou (LFSR). Nejsou kryptograficky Zabezpečené generátory náhodných čísel (CSPRNG). Lepší možností je použít bajty ze zařízení /dev/urandom. To bude vyžadovat volání externího osmičkového nebo hexadecimálního výpisu.

$ 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 

Velmi jednoduché (ale nejednotné) řešení, jak získat float, je:

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

Způsob, jak zajistit jednotnost v rozsahu [0,1) (bez 1):

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

Odpověď

Použijte $(( ( RANDOM % N ) + MIN ))

Nahradit N s MAX číslem a MIN s minimálním počtem, který chcete vygenerovat. (N protože MAX je exkluzivní, vložte N+1 mít MAX, MIN včetně).

Nebo můžete místo toho použít $(shuf -i MIN-MAX -n 1).

z 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 in shuf zde znamená vygenerovat pouze jedno náhodné číslo.

Tím se vygenerují náhodná čísla mezi 0 ~ 9999 s úvodními nulami pomocí printf (ve výsledku číslo 1

je exkluzivní).

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

Komentáře

  • Toto také nevytvoří skutečné náhodné číslo v daném rozsahu, s výjimkou případu, kdy N je dělitelem 32767 (horní hranice $ RANDOM).

Odpověď

Na bash

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

kde 1/10000 je váš náhodný přesnost a 4 číslice vaší přesnosti výstupu

odpověď

zsh má ve své iv id =“ 2002b32e4d rand48() aritmetické funkci (obal do erand48() standardní funkce) „>

modul:

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

Zatímco $RANDOM je 15 bitů, pseudonáhodný a reprodukovatelný, bash 5.1+ má bezpečnější 32bitové celé číslo $SRANDOM založené na skutečně náhodných zdrojích, pokud jsou k dispozici. Nepodporuje aritmetiku s plovoucí desetinnou čárkou, ale alespoň ji můžete použít k nasazení pseudonáhodného generátoru awk (který jinak ve výchozím nastavení používá velmi předvídatelný výsledek time()):

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

(mějte na paměti, že je to stále jen 32 bitů entropie a awk dělá deterministické pseudonáhodné generování založené na tomto semeni)

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *