Er det muligt at generere reelle tilfældige tal med en bestemt præcision og i et specifikt interval ved hjælp af Integer Random Generator $ RANDOM? For eksempel hvordan vi kan generere reelle tal med 4 præcision mellem 0 og 1?
0.1234 0.0309 0.9001 0.0000 1.0000
En simpel løsning:
printf "%d04.%d04\n" $RANDOM $RANDOM
Kommentarer
- Angiv venligst hvad du mener med ” reelle tilfældige tal “. Kræver du en kilde til tilfældige tal genereret af noget som partikelforfald, eller vil du være tilfreds med en pseudo-tilfældig generator? Er din anvendelse af disse antal kryptografisk eller videnskabelig betydning, eller vil du bare have noget, der ” ser tilfældigt ud “.
- … eller mener du faktisk ” float ” eller ” flydende punkt nummer?
- Tak for kommentaren. Jeg har brug for en pseudo tilfældig talgenerator til flydende tal baseret på $ RANDOM.
- … til implementering af meta heuristiske algoritmer i bash.
Svar
awk -v n=10 -v seed="$RANDOM" "BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }"
Dette sender n
tilfældige tal (ti i eksemplet) i området [0,1] med fire decimaler. Det bruger rand()
-funktionen i awk
(ikke i standard awk
men implementeret af de mest almindelige awk
implementeringer), der returnerer en tilfældig værdi i dette interval. Generatoren for tilfældige tal udsås af skallen “s $RANDOM
variabel.
Når et awk
-program kun har BEGIN
blokke (og ingen andre kodeblokke), awk
forsøger ikke at læse input fra sin standardindgangsstrøm.
På ethvert OpenBSD-system (eller system, der har det samme jot
værktøj , oprindeligt i 4.2BSD), følgende genererer 10 tilfældigt tal som angivet:
jot -p 4 -r 10 0 1
Kommentarer
- Virkelig strengt taget, da output af
rand()
er en svømmer inden for [0,1), er den sandsynligvis ikke ‘ t nøjagtigt jævnt fordelt, når den afrundes til fire decimaler. Hvis svømmeren var af uendelig præcision, ville det være, men det er ‘ t: det ‘ sandsynligvis genereres fra tilfældige bits , så der er 2 ^ N forskellige værdier, og de ‘ t kortes ensartet til et sæt med 1000 værdier. Men så længe disse pseudo-tilfældige flyder har nok bits, og du ‘ ikke gør noget rigtig nøjagtigt, vandt du sandsynligvis ‘ t bemærkning.
Svar
Som påpeget i et andet svar, er der andre hjælpeprogrammer, du kan bruge til at generere tilfældige tal. I dette svar begrænser jeg mine ressourcer til $RANDOM
og et par grundlæggende aritmetiske funktioner.
For flydende numre, prøv noget som
printf "%s\n" $(echo "scale=8; $RANDOM/32768" | bc )
Det giver dig den bedste præcision, fordi $RANDOM
kun genererer tal mellem 0 og 32767. (inklusive 32767!) Men jeg ” har også brudt min regel om brug af grundlæggende aritmetiske funktioner ved at påkalde bc
.
Men inden jeg går videre, vil jeg gerne se på to emner præcision og rækkevidde for flydende numre. Derefter vil jeg se på at generere en række heltal (og hvis du kan generere heltal, kan du senere dele dem for at få et decimal, hvis du ønsker at bruge de hjælpeprogrammer, du foretrækker for at opnå det.)
Præcision
Efter fremgangsmåden for $RANDOM/32768
, da $RANDOM
genererer værdier fra 0 til 32767, resultatet af $RANDOM/32768
vil ligeledes være endeligt mange værdier. Med andre ord er det stadig en diskret tilfældig variabel (og med en computer vil du aldrig være i stand til at komme væk fra det faktum). Med det i tankerne kan du opnå en vis grad af præcision ved at bruge printf
.
Hvis du vil have en finere dækning af interval, kan du begynde at tænke i base 32768. Så i teorien skal $RANDOM + $RANDOM*32768
give dig en ensartet fordeling mellem 0 og 1.073.741.823. Men jeg er i tvivl om, at kommandolinjen vil håndtere den præcision meget godt. Et par punkter, der vedrører denne særlige sag:
- Summen af to uafhængige, jævnt fordelte tilfældige variabler i ikke generelt ensartede. I dette tilfælde er de i det mindste teoretisk set (se tredje punkt).
- Tror ikke du kan forenkle
$RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )
.De to forekomster af$RANDOM
er virkelig to forskellige begivenheder. - Jeg ved ikke nok om, hvordan
$RANDOM
er genereret for at vide, om at kalde det to gange som dette virkelig vil generere to uafhængige tilfældige hændelser.
Område
Lad os bare overveje $RANDOM/32768
. Hvis du vil have et tal i et interval, skal du sige [a,b)
, så
$RANDOM/32768*(b-a) + a
lander dig i det ønskede interval .
Generering af heltalværdier
Overvej først at generere tilfældige tal mellem [0,b)
hvor b
er mindre end 32768
. Overvej produktet q*b
, hvor q
er den heltals del af 32768/b
. Så hvad du kan gøre er at generere tilfældigt tal mellem 0 og 32767, men smide dem ud, der er større end eller lig med q*b
. Ring til det således genererede nummer G
. Derefter falder G
i området 0 til q*b
, og fordelingen vil være ensartet. Anvend nu modulær aritmetik for at få denne værdi ned i det ønskede område:
G % b
Bemærk, generer tilfældigt et tal som følger
$RANDOM % b
opretter ikke en ensartet fordeling, medmindre b
tilfældigvis er en af delerne af 32768
.
Skrivning af et bash-script til denne
Beregning q*b
som beskrevet ovenfor lyder som en smerte. Men det er virkelig ikke “t. Du kan få det som følger:
q*b = 32768 - ( 32768 % b )
I Bash kan du få dette med
$((32768 - $((32768 % b)) ))
Følgende kode genererer et tilfældigt tal i området 0..b
(ikke inklusive b
) . b=$1
m=$((32768 - $((32768 % $1)) )) a=$RANDOM while (( $a > $m )); do a=$RANDOM done a=$(($a % $1)) printf "$a\n"
Addendum
Teknisk set er der ringe grund til at arbejde med
m=$((32768 - $((32768 % $1)) ))
Følgende vil opnå det samme
a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n"
Det er meget mere arbejde, men computere er hurtige.
Generering af et heltal i et større område
Jeg lader dig finde ud af dette. Der skal udvises forsigtighed, og på et eller andet tidspunkt skal du tage computerens hukommelsesbegrænsninger i betragtning ved håndtering af aritmetiske operationer.
Afsluttende note
Det accepterede svar opretter ikke et tilfældigt tal ensartet over 0 til 1.
For at se dette, prøv følgende
$ for i in {1..1000}; do echo .$RANDOM; done | awk "{ a += $1 } END { print a }"
For en virkelig ensartet fordeling over [0,1)
skal du se et gennemsnit på tæt på 0.500
.
Men som du kan se ved at køre ovenstående uddrag, får du i stedet noget som 314.432
eller 322.619
. Da det er 1000 tal, er gennemsnittet af dette .322
. Det sande gennemsnit for denne sekvens af genererede tal er .316362
Du kan opnå dette sande gennemsnit ved hjælp af perl-scriptet
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; }"
Jeg tilføjer heltal her for at hjælpe dig med at se, hvordan denne fremgangsmåde ved at bruge .$RANDOM
ikke gør det, du sandsynligvis vil have det til at gøre. Med andre ord, overvej hvilke heltal der genereres, og hvilke der går glip af helt. Et stort antal springes over; en hel del fordobles.
Svar
På systemer, hvor shell “s printf er i stand til at forstå %a
format (bash ksh zsh osv.) og er derfor i stand til at udføre en intern basisændring (hex -> dec) (ensartet i [0,1)
fra 0,00003 til 0.99997):
printf "%.5f\n" "$(printf "0x0.%04xp1" $RANDOM)"
Du kan endda bruge flere cifre ved at kombinere flere opkald til $RANDOM
(fra 0.000000001 til 0.999999999)
printf "%.9f\n" "$(printf "0x0.%08xp2" $(( ($RANDOM<<15) + $RANDOM )))"
Den interne (til shell) “$ RANDOM” -algoritmen er baseret i et lineært feedback-skiftregister (LFSR). Disse er ikke kryptografisk Secure Pseudo Random Number Generators (CSPRNGs). En bedre mulighed er at bruge byte fra /dev/urandom
-enheden. Det kræver opkald til ekstern oktal eller hex-dump.
$ 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
En meget enkel (men ikke-ensartet) løsning til at få en float er:
printf "0.%04d\n" $RANDOM
En måde at gøre det ensartet i området [0,1)
(ekskl. 1):
while a=$RANDOM; ((a>29999)); do :; done; printf "0.%04d\n" "$((a%10000))"
Svar
Brug $(( ( RANDOM % N ) + MIN ))
Udskift N
med MAX-nummer og MIN med minimumantal, du vil generere. (N
da MAX er eksklusiv, skal du sætte N+1
for at have både MAX, MIN inklusive).
Eller du kan bruge $(shuf -i MIN-MAX -n 1)
i stedet.
fra 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
i shuf
betyder her kun at generere et tilfældigt tal.
Dette vil generere tilfældige tal mellem 0 ~ 9999 med førende nuller ved hjælp af printf
(i resultat, nummer 1
er eksklusiv).
printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215
Kommentarer
- Dette producerer heller ikke et ægte tilfældigt tal i det givne interval undtagen i det tilfælde, hvor N er en skillevæg på 32767 (den øvre grænse på $ RANDOM).
Svar
På bash
bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000"
hvor 1/10000
er din tilfældige præcision og 4
cifrer din outputpræcision
Svar
zsh
har en rand48()
aritmetisk funktion (indpakning til erand48()
standardfunktionen) i dens zsh/mathfunc
modul:
zmodload zsh/mathfunc printf "%.4f\n" $((rand48()))
Mens $RANDOM
er 15 bits, pseudo-tilfældig og reproducerbar, bash
5.1+ har et sikrere 32 bit heltal $SRANDOM
, baseret på ægte tilfældige kilder, hvor de er tilgængelige. Det understøtter ikke aritmetik med flydende punkt, men i det mindste kan du bruge det til at så awk
“s pseudo tilfældige generator (som ellers som standard bruger det meget forudsigelige resultat af time()
):
echo "$SRANDOM" | awk " { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }"
(husk, det er stadig kun 32 bit entropi, og awk
gør deterministisk pseudo-tilfældig generation baseret på det frø)