Bash: Kuinka luoda satunnainen float-luku käyttämällä $ RANDOM

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)

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *