Bash: So generieren Sie eine zufällige Gleitkommazahl mit $ RANDOM

Ist es möglich, mit dem Integer Random echte Zufallszahlen mit einer bestimmten Genauigkeit und in einem bestimmten Bereich zu generieren Generator $ ZUFÄLLIG? Wie können wir beispielsweise eine reelle Zahl mit einer Genauigkeit von 4 zwischen 0 und 1 generieren?

0.1234 0.0309 0.9001 0.0000 1.0000 

Eine einfache Problemumgehung:

printf "%d04.%d04\n" $RANDOM $RANDOM 

Kommentare

  • Bitte geben Sie an, was Sie unter “ reellen Zufallszahlen . Benötigen Sie eine Quelle für Zufallszahlen, die durch Partikelzerfall erzeugt werden, oder sind Sie mit einem Pseudozufallsgenerator zufrieden? Ist Ihre Anwendung dieser Zahlen von kryptografischer oder wissenschaftlicher Bedeutung oder möchten Sie nur etwas, das “ zufällig aussieht „.
  • … oder meinst du eigentlich “ float “ oder “ float Punktnummer?
  • Vielen Dank für Ihren Kommentar. Ich benötige einen Pseudozufallszahlengenerator für Gleitkommazahlen basierend auf $ RANDOM.
  • … für die Implementierung von meta-heuristischen Algorithmen in Bash.

Antwort

awk -v n=10 -v seed="$RANDOM" "BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", rand()) }" 

Dies gibt n Zufallszahlen aus (zehn im Beispiel) im Bereich [0,1] mit vier Dezimalstellen. Es verwendet die Funktion rand() in awk (nicht im Standard awk, sondern wird von den meisten implementiert awk Implementierungen), die einen zufälligen Wert in diesem Bereich zurückgeben. Der Zufallszahlengenerator wird von der Shell „s $RANDOM -Variable gesetzt.

Wenn ein awk -Programm nur BEGIN -Blöcke (und keine anderen Codeblöcke), awk versucht nicht, Eingaben aus dem Standardeingabestream zu lesen.

Auf jedem OpenBSD-System (oder System mit demselben jot -Dienstprogramm , ursprünglich in 4.2BSD), das Folgende generiert 10 Zufallszahlen wie angegeben:

jot -p 4 -r 10 0 1 

Kommentare

  • Wirklich genau genommen, seit der Ausgabe von rand() ist ein Gleitkommawert innerhalb von [0,1), es ist wahrscheinlich nicht ‚ nicht genau gleichmäßig verteilt, wenn es auf vier Dezimalstellen gerundet wird. Es wäre, wenn der Float von unendlicher Genauigkeit wäre, aber es ist nicht ‚ t: Es ist wahrscheinlich, dass ‚ aus zufälligen Bits generiert wird Es gibt also 2 ^ N verschiedene Werte und Sie werden ‚ nicht einheitlich einem Satz von 1000 Werten zugeordnet. Aber solange diese pseudozufälligen Floats genug Bits haben und Sie ‚ nichts wirklich Genaues tun, werden Sie wahrscheinlich ‚ t Beachten Sie.

Antwort

Wie in einer anderen Antwort ausgeführt, gibt es andere Dienstprogramme, die Sie zum Generieren verwenden können zufällige Zahlen. In dieser Antwort beschränke ich meine Ressourcen auf $RANDOM und einige grundlegende arithmetische Funktionen.

Versuchen Sie für Gleitkommazahlen etwas wie

printf "%s\n" $(echo "scale=8; $RANDOM/32768" | bc ) 

Damit erhalten Sie die beste Genauigkeit, da $RANDOM nur Zahlen zwischen 0 und 32767 generiert. (einschließlich 32767!) Aber ich “ Ich habe auch gegen meine Regel bezüglich der Verwendung grundlegender arithmetischer Funktionen verstoßen, indem ich bc aufgerufen habe.

Aber bevor ich fortfahre, möchte ich zwei Punkte genauer betrachten und Bereich für Gleitkommazahlen. Danach werde ich einen Bereich von Ganzzahlen generieren (und wenn Sie Ganzzahlen generieren können, können Sie diese später teilen, um eine Dezimalzahl zu erhalten, wenn Sie die Dienstprogramme verwenden möchten, die Sie dafür bevorzugen.)

Präzision

Nach $RANDOM/32768, seit $RANDOM generiert Werte von 0 bis 32767, das Ergebnis von $RANDOM/32768 sind ebenfalls endlich viele Werte. Mit anderen Worten, es ist immer noch eine diskrete Zufallsvariable (und mit einem Computer werden Sie nie in der Lage sein, sich dieser Tatsache zu entziehen). In diesem Sinne können Sie mit printf ein gewisses Maß an Präzision erreichen.

Wenn Sie eine feinere Abdeckung des Intervall könnten Sie anfangen, in Basis 32768 zu denken. Theoretisch sollte $RANDOM + $RANDOM*32768 Ihnen eine gleichmäßige Verteilung zwischen 0 und 1.073.741.823 geben. Ich bezweifle jedoch, dass die Befehlszeile mit dieser Genauigkeit sehr gut umgehen kann. Einige Punkte in Bezug auf diesen speziellen Fall:

  • Die Summe zweier unabhängiger, gleichmäßig verteilter Zufallsvariablen ist im Allgemeinen nicht einheitlich. In diesem Fall sind sie zumindest theoretisch (siehe dritter Punkt).
  • Denken Sie nicht, dass Sie $RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 ) vereinfachen können.Die beiden Vorkommen von $RANDOM sind wirklich zwei verschiedene Ereignisse.
  • Ich weiß nicht genug darüber, wie $RANDOM ist Wird generiert, um zu wissen, ob ein zweimaliger Aufruf tatsächlich zwei unabhängige zufällige Ereignisse generiert.

Bereich

Betrachten wir nur $RANDOM/32768. Wenn Sie eine Zahl in einem Bereich wünschen, z. B. [a,b), dann

$RANDOM/32768*(b-a) + a 

landen Sie im gewünschten Bereich .

Generierung ganzzahliger Werte

Betrachten Sie zunächst die Generierung von Zufallszahlen zwischen [0,b) wobei b kleiner als 32768 ist. Betrachten Sie das Produkt q*b, wobei q der ganzzahlige Teil von 32768/b ist. Dann können Sie eine Zufallszahl zwischen 0 und 32767 generieren, aber diejenigen wegwerfen, die größer oder gleich q*b sind. Rufen Sie die so generierte Nummer G an. Dann fällt G in den Bereich von 0 bis q*b und seine Verteilung ist gleichmäßig. Wenden Sie nun modulare Arithmetik an, um diesen Wert in den gewünschten Bereich zu bringen:

G % b 

Beachten Sie, dass Sie zufällig eine Zahl wie folgt generieren:

$RANDOM % b 

erstellt keine gleichmäßige Verteilung, es sei denn, b ist zufällig einer der Teiler von 32768.

Schreiben eines Bash-Skripts für dieses

Berechnen q*b wie oben beschrieben klingt wie ein Schmerz. Aber es ist wirklich nicht „t. Sie können es wie folgt erhalten:

q*b = 32768 - ( 32768 % b ) 

In Bash können Sie dies mit

Der folgende Code generiert eine Zufallszahl im Bereich 0..b (ohne b) b=$1

m=$((32768 - $((32768 % $1)) )) a=$RANDOM while (( $a > $m )); do a=$RANDOM done a=$(($a % $1)) printf "$a\n" 

Nachtrag

Technisch gesehen gibt es wenig Grund, mit

m=$((32768 - $((32768 % $1)) )) 

zu arbeiten

a=$RANDOM while (( $a > $1 )); do a=$RANDOM done printf "$a\n" 

Es ist viel mehr Arbeit, aber Computer sind schnell.

Generieren einer Ganzzahl in einem größeren Bereich

Ich werde Sie dies herausfinden lassen. Vorsicht ist geboten, und irgendwann müssen Sie die Speicherbeschränkungen des Computers bei der Verarbeitung von Rechenoperationen berücksichtigen.

Schlussbemerkung

Die akzeptierte Antwort erstellt keine einheitliche Zufallszahl über 0 bis 1.

Versuchen Sie Folgendes, um dies zu sehen.

$ for i in {1..1000}; do echo .$RANDOM; done | awk "{ a += $1 } END { print a }" 

Für eine wirklich gleichmäßige Verteilung über [0,1) sollte ein Durchschnitt nahe .

Wie Sie jedoch sehen können, wenn Sie das obige Snippet ausführen, erhalten Sie stattdessen etwas wie 314.432 oder 322.619. Da es sich um 1000 Zahlen handelt, ist der Durchschnitt davon .322. Der wahre Durchschnitt für diese Folge generierter Zahlen ist .316362

Sie können diesen wahren Durchschnitt mit dem Perl-Skript

Ich füge hier Ganzzahlen hinzu, um Ihnen zu zeigen, wie dieser Ansatz der Verwendung von .$RANDOM nicht das tut, was Sie höchstwahrscheinlich wollen. Mit anderen Worten, denken Sie darüber nach, welche Ganzzahlen generiert werden und welche insgesamt fehlen. Eine große Anzahl wird übersprungen; Einige werden verdoppelt.

Antwort

Auf Systemen, auf denen die printf der Shell die -Format (bash ksh zsh usw.) und ist daher in der Lage, eine interne Basisänderung (hex -> dec) durchzuführen (einheitlich im Bereich

von 0,00003) bis 0.99997):

printf "%.5f\n" "$(printf "0x0.%04xp1" $RANDOM)" 

Sie können sogar mehr Ziffern verwenden, indem Sie mehr Aufrufe an $RANDOM (von 0.000000001 bis 0.999999999)

printf "%.9f\n" "$(printf "0x0.%08xp2" $(( ($RANDOM<<15) + $RANDOM )))" 

Der interne (für die Shell) „$ RANDOM“ -Algorithmus basiert auf einem Schieberegister mit linearer Rückkopplung (LFSR). Diese sind nicht kryptografisch Sichere Pseudozufallszahlengeneratoren (CSPRNGs). Eine bessere Option ist die Verwendung von Bytes vom Gerät /dev/urandom. Dies erfordert den Aufruf eines externen Oktal- oder Hex-Dumps.

$ 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 

Eine sehr einfache (aber ungleichmäßige) Lösung, um einen Float zu erhalten, ist:

printf "0.%04d\n" $RANDOM 

Eine Möglichkeit, es im Bereich [0,1) (ohne 1) einheitlich zu machen:

while a=$RANDOM; ((a>29999)); do :; done; printf "0.%04d\n" "$((a%10000))" 

Antwort

Verwenden Sie $(( ( RANDOM % N ) + MIN ))

Ersetzen Sie N mit MAX-Nummer und MIN mit der Mindestanzahl, die Sie generieren möchten. (N Da MAX exklusiv ist, geben Sie N+1 ein um sowohl MAX als auch MIN zu haben).

Oder Sie können stattdessen $(shuf -i MIN-MAX -n 1) verwenden.

von 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 

Die -n 1 in shuf bedeutet hier nur eine Zufallszahl zu generieren.

Dies generiert Zufallszahlen zwischen 0 ~ 9999 mit führenden Nullen Verwenden von printf (im Ergebnis Nummer 1

ist exklusiv).

printf "0.%04d\n" $(( RANDOM % 1000 )) 0.0215 

Kommentare

  • Dies Außerdem wird im angegebenen Bereich keine echte Zufallszahl erzeugt, außer in dem Fall, in dem N ein Teiler von 32767 ist (die Obergrenze von $ RANDOM).

Antwort

Bei Bash

bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000" 

wobei 1/10000 Ihr Zufall ist Genauigkeit und 4 Ziffern Ihre Ausgabegenauigkeit

Antwort

zsh hat eine rand48() arithmetische Funktion (Wrapper zur erand48() Standardfunktion) in ihrer zsh/mathfunc Modul:

zmodload zsh/mathfunc printf "%.4f\n" $((rand48())) 

Während $RANDOM 15 Bit beträgt, pseudozufällig und reproduzierbar, bash 5.1+ hat eine sicherere 32-Bit-Ganzzahl $SRANDOM, basierend auf wirklich zufälligen Quellen, sofern verfügbar. Es unterstützt keine Gleitkomma-Arithmetik, aber Sie können sie zumindest verwenden, um den Pseudozufallsgenerator von awk zu setzen (der ansonsten standardmäßig das sehr vorhersehbare Ergebnis von time()):

echo "$SRANDOM" | awk " { srand($1) for (i = 0; i < 20; i++) printf "%.4f\n", rand() }" 

(bedenken Sie, dass es immer noch nur 32 Bit Entropie und führt eine deterministische Pseudozufallsgenerierung basierend auf diesem Startwert durch.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.