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)