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)