Is het mogelijk om echte willekeurige getallen te genereren met een specifieke precisie en in een specifiek bereik met behulp van de Integer Random Generator $ RANDOM? Hoe kunnen we bijvoorbeeld een reëel getal genereren met een precisie van 4 tussen 0 en 1?
0.1234 0.0309 0.9001 0.0000 1.0000
Een eenvoudige oplossing:
printf "%d04.%d04\n" $RANDOM $RANDOM
Reacties
- Geef aan wat je bedoelt met ” echte willekeurige getallen “. Heb je een bron van willekeurige getallen nodig die wordt gegenereerd door zoiets als deeltjesverval, of ben je blij met een pseudo-willekeurige generator? Is uw toepassing van deze cijfers van cryptografische of wetenschappelijke betekenis, of wilt u gewoon iets dat ” er willekeurig uitziet “.
- … of bedoel je eigenlijk ” float ” of ” zwevend puntnummer?
- Bedankt voor je reactie. Ik heb een generator voor pseudo-willekeurige getallen nodig voor getallen met drijvende komma gebaseerd op $ RANDOM.
- … voor het implementeren van meta-heuristische algoritmen in bash.
Antwoord
awk -v n=10 -v seed="$RANDOM" "BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }"
Dit levert n
willekeurige getallen op (tien in het voorbeeld) in het bereik [0,1) met vier decimalen. Het gebruikt de rand()
-functie in awk
(niet in standaard awk
maar geïmplementeerd door de meest voorkomende awk
implementaties) die een willekeurige waarde in dat bereik retourneert. De generator voor willekeurige getallen wordt gezaaid door de shell “s $RANDOM
variabele.
Wanneer een awk
programma slechts BEGIN
blokken (en geen andere codeblokken), awk
zal niet proberen om invoer uit zijn standaard invoerstroom te lezen.
Op elk OpenBSD-systeem (of systeem dat hetzelfde jot
hulpprogramma heeft, oorspronkelijk in 4.2BSD), de volgende genereert 10 willekeurige getallen zoals gespecificeerd:
jot -p 4 -r 10 0 1
Reacties
- Echt strikt genomen, aangezien de output van
rand()
is een float binnen [0,1), het is waarschijnlijk niet ‘ t precies gelijk verdeeld wanneer afgerond op vier decimalen. Het zou zijn, als de float van oneindige precisie zou zijn, maar het is niet ‘ t: het ‘ wordt waarschijnlijk gegenereerd uit willekeurige bits , dus er zijn 2 ^ N verschillende waarden, en ze ‘ kunnen niet uniform worden toegewezen aan een set van 1000 waarden. Maar zolang die pseudo-willekeurige drijvers genoeg bits hebben, en je ‘ niets echt exact doet, heb je waarschijnlijk ‘ t gewonnen opmerking.
Antwoord
Zoals aangegeven in een ander antwoord, zijn er andere hulpprogrammas die u kunt gebruiken om willekeurige nummers. In dit antwoord beperk ik mijn bronnen tot $RANDOM
, en een paar elementaire rekenkundige functies.
Probeer voor getallen met drijvende komma zoiets als
printf "%s\n" $(echo "scale=8; $RANDOM/32768" | bc )
Dat levert de beste precisie op omdat $RANDOM
alleen getallen tussen 0 en 32767 genereert. (inclusief 32767!) Maar ik ” heb ook mijn regel overtreden over het gebruik van elementaire rekenkundige functies door bc
aan te roepen.
Maar voordat ik verder ga, zou ik “twee kwesties willen bekijken precisie en bereik voor getallen met drijvende komma. Daarna zal ik kijken naar het genereren van een reeks gehele getallen (en als je gehele getallen kunt genereren, kun je ze later delen om een decimaal te krijgen als je wilt dat je de hulpprogrammas gebruikt die je wilt om dat te bereiken.)
Precisie
Met de benadering van $RANDOM/32768
, sinds $RANDOM
genereert waarden van 0 tot 32767, het resultaat van $RANDOM/32768
zal eveneens eindig veel waarden zijn. Met andere woorden, het is nog steeds een discrete willekeurige variabele (en met een computer kom je daar nooit bij vandaan). Met dat in gedachten kun je een zekere mate van precisie bereiken door printf
te gebruiken.
Als je een fijnere dekking van de interval, zou je kunnen denken in basis 32768. In theorie zou $RANDOM + $RANDOM*32768
je dus een uniforme verdeling tussen 0 en 1.073.741.823 moeten geven. Maar ik betwijfel of de opdrachtregel die precisie heel goed zal verwerken. Een paar punten met betrekking tot dit specifieke geval:
- De som van twee onafhankelijke, uniform verdeelde willekeurige variabelen is over het algemeen niet uniform. In dit geval, tenminste theoretisch gesproken (zie derde punt), zijn ze dat.
- Denk niet dat je
$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )
kunt vereenvoudigen.De twee exemplaren van$RANDOM
zijn eigenlijk twee verschillende gebeurtenissen. - Ik weet niet genoeg over hoe
$RANDOM
is gegenereerd om te weten of het twee keer op deze manier aanroepen echt twee onafhankelijke willekeurige gebeurtenissen zal genereren.
Bereik
Laten we eens kijken naar $RANDOM/32768
. Als je een getal in een bereik wilt, zeg dan [a,b)
, en
$RANDOM/32768*(b-a) + a
brengt je in het gewenste bereik .
Genereren van gehele getallen
Overweeg eerst om willekeurige getallen te genereren tussen [0,b)
waarbij b
kleiner is dan 32768
. Beschouw het product q*b
, waarbij q
het gehele deel is van 32768/b
. Wat je dan kunt doen is een willekeurig getal tussen 0 en 32767 genereren, maar die groter dan of gelijk aan q*b
weggooien. Bel het aldus gegenereerde nummer G
. Dan valt G
in het bereik van 0 tot q*b
, en de distributie zal uniform zijn. Pas nu modulaire rekenkunde toe om deze waarde terug te brengen tot het gewenste bereik:
G % b
Let op, genereer willekeurig een getal als volgt
$RANDOM % b
zal geen uniforme distributie creëren, tenzij b
toevallig een van de delers is van 32768
.
Een bash-script schrijven voor dit
Berekenen q*b
zoals hierboven beschreven klinkt als lastig. Maar het is echt niet “t. Je kunt het als volgt krijgen:
q*b = 32768 - ( 32768 % b )
In Bash kun je dit krijgen met
$((32768 - $((32768 % b)) ))
De volgende code genereert een willekeurig getal in het bereik 0..b
(niet inclusief b
) . b=$1
m=$((32768 - $((32768 % $1)) )) a=$RANDOM while (( $a > $m )); do a=$RANDOM done a=$(($a % $1)) printf "$a\n"
Addendum
Technisch gezien is er” weinig reden om mee te werken
m=$((32768 - $((32768 % $1)) ))
Het volgende zal hetzelfde bereiken
a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n"
Het “is veel meer werk, maar computers zijn snel.
Een geheel getal genereren in een groter bereik
Ik zal je dit laten uitzoeken. Voorzichtigheid is geboden, en op een gegeven moment moet je rekening houden met de geheugenbeperkingen van de computer bij het verwerken van rekenkundige bewerkingen.
Laatste opmerking
Het geaccepteerde antwoord zal geen gelijkmatig willekeurig getal tussen 0 en 1 creëren.
Probeer het volgende om dit te zien
$ for i in {1..1000}; do echo .$RANDOM; done | awk "{ a += $1 } END { print a }"
Voor een echt uniforme verdeling over [0,1)
zou je een gemiddelde moeten zien van bijna 0.500
.
Maar zoals je kunt zien door het bovenstaande fragment uit te voeren, krijg je in plaats daarvan zoiets als 314.432
of 322.619
. Aangezien het 1000 getallen zijn, is het gemiddelde hiervan .322
. Het echte gemiddelde voor deze reeks gegenereerde getallen is .316362
Je kunt dit echte gemiddelde verkrijgen met het perl-script
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; }"
Ik voeg hier gehele getallen toe om u te helpen zien hoe deze benadering van het gebruik van .$RANDOM
niet doet wat u waarschijnlijk wilt dat het doet. Met andere woorden, bedenk welke gehele getallen worden gegenereerd en welke helemaal worden gemist. Een behoorlijk groot aantal wordt overgeslagen; er zijn er nogal wat verdubbeld.
Antwoord
Op systemen waar shell “s printf de %a
formaat (bash ksh zsh, etc.) en is daarom in staat om een interne basiswijziging uit te voeren (hex -> dec) (uniform in [0,1)
bereik van 0,00003 tot 0.99997):
printf "%.5f\n" "$(printf "0x0.%04xp1" $RANDOM)"
Je zou zelfs meer cijfers kunnen gebruiken door meer oproepen naar $RANDOM
(van 0.000000001 tot 0.999999999)
printf "%.9f\n" "$(printf "0x0.%08xp2" $(( ($RANDOM<<15) + $RANDOM )))"
Het interne (naar de shell) “$ RANDOM” -algoritme is gebaseerd op een lineair feedback-schuifregister (LFSR). Die zijn niet cryptografisch Beveiligde Pseudo Random Number Generators (CSPRNGs). Een betere optie is om bytes van het /dev/urandom
-apparaat te gebruiken. Daarvoor is een aanroep naar een externe octale of hex-dump vereist.
$ 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
Een zeer eenvoudige (maar niet-uniforme) oplossing om een float te krijgen is:
printf "0.%04d\n" $RANDOM
Een manier om het uniform te maken in het bereik [0,1)
(exclusief 1):
while a=$RANDOM; ((a>29999)); do :; done; printf "0.%04d\n" "$((a%10000))"
Antwoord
Gebruik $(( ( RANDOM % N ) + MIN ))
Vervang N
met MAX nummer en MIN met minimum aantal dat u wilt genereren. (N
aangezien MAX exclusief is, plaats N+1
om zowel MAX als MIN te hebben).
Of je kunt in plaats daarvan $(shuf -i MIN-MAX -n 1)
gebruiken.
van 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
De -n 1
in shuf
betekent hier slechts één willekeurig getal genereren.
Dit genereert willekeurige getallen tussen 0 ~ 9999 met voorloopnullen met printf
(in resultaat, nummer 1
is exclusief).
printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215
Reacties
- Dit zal ook geen echt willekeurig getal produceren in het opgegeven bereik, behalve in het geval dat N een deler is van 32767 (de bovengrens van $ RANDOM).
Antwoord
On bash
bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000"
waarbij 1/10000
jouw willekeurige precisie en 4
cijfers uw uitvoerprecisie
Antwoord
zsh
heeft een rand48()
rekenkundige functie (wrapper naar de erand48()
standaardfunctie) in zijn zsh/mathfunc
module:
zmodload zsh/mathfunc printf "%.4f\n" $((rand48()))
Terwijl $RANDOM
15 bits is, pseudo-willekeurig en reproduceerbaar, bash
5.1+ heeft een veiliger 32-bits geheel getal $SRANDOM
, gebaseerd op echt willekeurige bronnen, indien beschikbaar. Het ondersteunt geen drijvende-kommaberekeningen, maar je kunt het in ieder geval gebruiken om de pseudo-willekeurige generator van awk
“te zaaien (die anders standaard het zeer voorspelbare resultaat van time()
):
echo "$SRANDOM" | awk " { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }"
(houd er rekening mee dat het nog steeds maar 32 bits entropie is, en awk
doet deterministische pseudo-willekeurige generatie op basis van dat zaad)