Onko mahdollista tuottaa todellisia satunnaislukuja tietyllä tarkkuudella ja tietyllä alueella Integer Random -toiminnolla Generaattori $ RANDOM? Esimerkiksi kuinka voimme tuottaa reaaliluvun 4 tarkkuudella välillä 0 ja 1?
0.1234 0.0309 0.9001 0.0000 1.0000
Yksinkertainen kiertotapa:
printf "%d04.%d04\n" $RANDOM $RANDOM
Kommentit
- Määritä mitä tarkoitat ” todellisilla satunnaisluvuilla ”. Tarvitsetko lähdettä satunnaisluvuista, jotka on generoitu esimerkiksi hiukkasten hajoamisen kautta, vai oletko tyytyväinen näennäissatunnaisgeneraattoriin? Onko sovelluksesi näihin salaus- tai tieteellisesti merkittäviin lukuihin vai haluatko vain jotain, joka ” näyttää satunnaiselta ”.
- … vai tarkoitatko todella ” kellua ” vai ” kelluvaa pisteen numero?
- Kiitos kommentista. Tarvitsen näennäissatunnaisgeneraattorin $ RANDOM -perusteisiin liukulukuihin.
- … meta-heurististen algoritmien toteuttamiseen bashissa.
Vastaus
awk -v n=10 -v seed="$RANDOM" "BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }"
Tämä tuottaa n
satunnaislukuja (esimerkissä kymmenen) alueella [0,1] neljän desimaalin tarkkuudella. Se käyttää rand()
-funktiota kohdassa awk
(ei vakiona awk
, mutta yleisin awk
toteutukset), joka palauttaa satunnaisarvon kyseisellä alueella. Satunnaislukugeneraattorin kylvää kuori ”s $RANDOM
-muuttuja.
Kun awk
-ohjelmassa on vain BEGIN
-lohkot (eikä muita koodilohkoja), awk
ei yritä lukea tuloa vakiotulovirrastaan.
Missä tahansa OpenBSD-järjestelmässä (tai järjestelmässä, jossa on sama jot
-apuohjelma , alun perin 4.2BSD: ssä), toimi seuraavasti luo 10 satunnaislukua määritetyllä tavalla:
jot -p 4 -r 10 0 1
kommentit
- todella tiukasti ottaen, koska tulos
rand()
on kelluva [0,1): ssä, se ei todennäköisesti ole ’ t jakautunut tasaisesti tasaisesti pyöristettynä neljään desimaaliin. Se olisi, jos uimuri olisi äärettömän tarkasti, mutta se ei ’ t: se ’ luodaan todennäköisesti satunnaisista biteistä , joten on 2 ^ N erilaista arvoa, ja he eivät ’ t kartoita tasaisesti 1000 arvon joukkoa. Mutta niin kauan kuin näillä näennäissatunnaisilla kellukkeilla on tarpeeksi bittejä, etkä ’ tee mitään oikeaa, olet todennäköisesti voittanut ’ t Huomaa.
Vastaa
Kuten toisessa vastauksessa todettiin, on olemassa muita apuohjelmia, joiden avulla voit luoda satunnaisluvut. Tässä vastauksessa rajoitan resurssini $RANDOM
: iin ja muutamaan aritmeettiseen perustoimintoon.
Kokeile liukulukujen numeroita kuten
printf "%s\n" $(echo "scale=8; $RANDOM/32768" | bc )
Se antaa sinulle parhaan tarkkuuden, koska $RANDOM
tuottaa vain numeroita välillä 0 ja 32767. (mukaan lukien 32767!) Mutta minä ” Olemme rikkoneet myös sääntöni aritmeettisten perusfunktioiden käytöstä kutsumalla bc
.
Mutta ennen kuin jatkan, haluaisin tarkastella kahta asiaa tarkkuus ja alue liukulukuluvuille. Sen jälkeen tarkastelen kokonaislukualueiden luomista (ja jos pystyt tuottamaan kokonaislukuja, voit jakaa ne myöhemmin desimaalin saamiseksi, jos haluat käyttää mitä tahansa apuohjelmia, jotka haluat saavuttaa.)
Tarkkuus
Lähestymistapa $RANDOM/32768
, koska $RANDOM
tuottaa arvot 0: sta 32767: een, $RANDOM/32768
-tuloksesta tulee myös lopullisesti monia arvoja. Toisin sanoen, se on edelleen erillinen satunnaismuuttuja (ja tietokoneen kanssa et voi koskaan päästä eroon tästä tosiasiasta). Tässä mielessä voit saavuttaa jonkin verran tarkkuutta käyttämällä printf
.
Jos haluat hienomman peitteen välein, voit alkaa ajatella tukiasemassa 32768. Joten teoriassa $RANDOM + $RANDOM*32768
pitäisi antaa sinulle tasainen jakauma välillä 0-1 073 741 823. Mutta epäilen komentorivin käsittelevän tätä tarkkuutta erittäin hyvin. Pari asiaan liittyvää asiaa:
- Kahden itsenäisen, tasaisesti jakautuneen satunnaismuuttujan summa, joka ei yleensä ole yhtenäinen. Tässä tapauksessa ainakin teoriassa (katso kolmas kohta) ne ovat.
- Älä usko, että voit yksinkertaistaa
$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )
.Kaksi$RANDOM
-tapahtumaa ovat todellakin kaksi erilaista tapahtumaa. - En tiedä tarpeeksi siitä, miten
$RANDOM
on luotu tietämään, synnyttääkö se kahdesti tällä tavoin todella kaksi riippumatonta satunnaistapahtumaa.
Alue
Tarkastellaan vain $RANDOM/32768
. Jos haluat numeron alueelle, sano [a,b)
, niin
$RANDOM/32768*(b-a) + a
laskeutuu halutulle alueelle .
Kokonaislukuarvojen generointi
Harkitse ensin satunnaislukujen luomista [0,b)
jossa b
on pienempi kuin 32768
. Tarkastellaan tuotetta q*b
, jossa q
on kohteen 32768/b
kokonaisluku. Sitten mitä voit tehdä, on luoda satunnaisluku välillä 0 ja 32767, mutta heittää ne, jotka ovat suurempia tai yhtä suuria kuin q*b
. Soita tällä tavalla luotuun numeroon G
. Sitten G
putoaa alueelle 0 – q*b
, ja sen jakauma on tasainen. Käytä nyt modulaarista aritmeettia saadaksesi tämän arvon alitetuksi halutulle alueelle:
G % b
Huomaa, generoi luku satunnaisesti seuraavasti:
$RANDOM % b
ei luo yhtenäistä jakelua, ellei b
ole vain yksi 32768
.
bash-komentosarjan kirjoittaminen tälle
Laskeminen q*b
edellä kuvatulla tavalla kuulostaa tuskalta. Mutta se ei todellakaan ole ”t”. Voit saada sen seuraavasti:
q*b = 32768 - ( 32768 % b )
Bashissa voit saada tämän
$((32768 - $((32768 % b)) ))
Seuraava koodi luo satunnaisluvun alueelle 0..b
(ei sisällä b
) . b=$1
m=$((32768 - $((32768 % $1)) )) a=$RANDOM while (( $a > $m )); do a=$RANDOM done a=$(($a % $1)) printf "$a\n"
Lisäys
Teknisesti ei ole juurikaan syytä työskennellä
m=$((32768 - $((32768 % $1)) ))
Seuraavilla saavutetaan sama asia
a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n"
Se ”on paljon enemmän työtä, mutta tietokoneet ovat nopeita.
Luomalla kokonaisluku suuremmalle alueelle
Annan tämän selvittää. On oltava varovainen, ja jossain vaiheessa sinun on otettava huomioon tietokoneen muistirajoitukset käsitellessäsi aritmeettisia operaatioita.
Viimeinen huomautus
Hyväksytty vastaus ei luo satunnaislukua tasaisesti välillä 0-1.
Jos haluat nähdä tämän, kokeile seuraavaa
$ for i in {1..1000}; do echo .$RANDOM; done | awk "{ a += $1 } END { print a }"
Todella tasaisen jakauman jakautumiselle [0,1)
sinun pitäisi nähdä keskiarvo lähellä 0.500
.
Mutta kuten yllä olevasta koodinpätkästä näet, saat sen sijaan jotain 314.432
tai 322.619
. Koska se on 1000 numeroa, tämän keskiarvo on .322
. Tämän generoidun numerosarjan todellinen keskiarvo on .316362
Voit saada tämän todellisen keskiarvon perl-komentosarjalla
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; }"
Lisään tähän kokonaislukuja auttamaan sinua ymmärtämään, miten tämä .$RANDOM
-toiminnon käyttötapa ei tee sitä, mitä todennäköisesti haluat sen tekevän. Toisin sanoen, mieti mitä kokonaislukuja luodaan ja mitkä jätetään kokonaan väliin. Melko suuri määrä ohitetaan; melko monta on kaksinkertaistettu.
Vastaa
Järjestelmissä, joissa shellin printf pystyy ymmärtämään %a
-muodossa (bash ksh zsh jne.) ja pystyy siksi suorittamaan sisäisen pohjanmuutoksen (hex -> dec) (yhtenäinen [0,1)
-alueella 0.00003 – 0,99997):
printf "%.5f\n" "$(printf "0x0.%04xp1" $RANDOM)"
Voit jopa käyttää useampia numeroita yhdistämällä useampia puheluja $RANDOM
-numeroon (0.000000001 – 0.999999999)
printf "%.9f\n" "$(printf "0x0.%08xp2" $(( ($RANDOM<<15) + $RANDOM )))"
Sisäinen (kuoren) ”$ RANDOM” -algoritmi perustuu lineaarisen palautteen siirtorekisteriin (LFSR). Niitä ei ole salattu Suojatut pseudo-satunnaislukugeneraattorit (CSPRNG). Parempi vaihtoehto on käyttää tavuja /dev/urandom
-laitteesta. Se vaatii kutsun ulkoiseen oktaali- tai heksadumpiin.
$ 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
Hyvin yksinkertainen (mutta epätasainen) ratkaisu kellukkeen saamiseksi on:
printf "0.%04d\n" $RANDOM
Tapa tehdä siitä tasainen alue [0,1)
(ilman 1):
while a=$RANDOM; ((a>29999)); do :; done; printf "0.%04d\n" "$((a%10000))"
vastaus
Käytä $(( ( RANDOM % N ) + MIN ))
Korvaa N
MAX-numerolla ja MIN-arvolla ja vähimmäismäärällä, jonka haluat luoda. (N
koska MAX on yksinomainen, laita N+1
jos sinulla on molemmat MAX, MIN mukaan lukien).
Tai voit käyttää sen sijaan $(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
shuf
tarkoittaa tässä vain yhden satunnaisluvun luomista.
Tämä tuottaa satunnaislukuja välillä 0 ~ 9999 johtavien nollien kanssa käyttämällä printf
(tuloksena luku 1
on yksinomainen).
printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215
Kommentit
- Tämä ei myöskään tuota todellista satunnaislukua annetulla alueella paitsi siinä tapauksessa, että N on jakaja 32767 ($ RANDOM: n yläraja).
Vastaa
On bash
bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000"
missä 1/10000
on satunnaisesi tarkkuus ja 4
laskee tulosteen tarkkuuden
vastaus
zsh
on rand48()
aritmeettinen toiminto (vakiotoiminnon erand48()
kääre) zsh/mathfunc
moduuli:
zmodload zsh/mathfunc printf "%.4f\n" $((rand48()))
Vaikka $RANDOM
on 15 bittiä, näennäissatunnainen ja toistettava, bash
5.1+: lla on turvallisempi 32-bittinen kokonaisluku $SRANDOM
, joka perustuu todella satunnaisiin lähteisiin, jos sellaisia on käytettävissä. Se ei tue liukulukuaritmeettista, mutta ainakin voit käyttää sitä awk
”pseudo-satunnaisgeneraattorin (joka muuten oletusarvoisesti käyttää hyvin ennustettavissa olevaa tulosta time()
):
echo "$SRANDOM" | awk " { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }"
(pidä mielessä, että siinä on edelleen vain 32 bittiä entropiaa ja awk
tekee deterministisen näennäissatunnaisen sukupolven kyseisen siemenen perusteella)