Ich habe mich gefragt , was der beste Weg wäre, zu bekommen gute Zufälligkeit in der Bash, das heißt, was für ein Verfahren , um eine zufällige positive ganze Zahl zu erhalten zwischen wäre MIN
und MAX
so , dass
- Der Bereich kann beliebig groß sein (oder zumindest bis zu 2 32 -1);
- Die Werte sind gleichmäßig verteilt (dh ohne Verzerrung).
- Es ist effizient.
Ein effizienter Weg, Zufälligkeit in Bash zu erhalten, ist die Verwendung der $RANDOM
Variablen. Dies tastet jedoch nur einen Wert zwischen 0 und 2 15 -1 ab, der möglicherweise nicht für alle Zwecke groß genug ist. Leute benutzen normalerweise ein Modulo, um es in den Bereich zu bringen, den sie wollen, zB
MIN=0
MAX=12345
rnd=$(( $RANDOM % ($MAX + 1 - $MIN) + $MIN ))
Dies erzeugt zusätzlich eine Verzerrung, wenn nicht $MAX
zufällig 2 15 -1 = 32767 geteilt wird. Wenn z. B. $MIN
0 und $MAX
9 sind, sind die Werte 0 bis 7 etwas wahrscheinlicher als die Werte 8 und 9, wie dies $RANDOM
niemals bei 32768 oder 32769 der Fall ist. Diese Abweichung wird mit zunehmendem Bereich schlimmer, z. B. wenn $MIN
0 und 9 $MAX
sind 9999, dann werden die Zahlen von 0 bis 2767 haben eine Wahrscheinlichkeit von 4 / 32767 , während die Zahlen 2768 bis 9999 nur eine Wahrscheinlichkeit von haben 3 / 32767 .
Während das obige Verfahren die Bedingung 3 erfüllt, erfüllt es die Bedingungen 1 und 2 nicht.
Die beste Methode, die ich bisher gefunden habe, um die Bedingungen 1 und 2 zu erfüllen, war die /dev/urandom
folgende:
MIN=0
MAX=1234567890
while
rnd=$(cat /dev/urandom | tr -dc 0-9 | fold -w${#MAX} | head -1 | sed 's/^0*//;')
[ -z $rnd ] && rnd=0
(( $rnd < $MIN || $rnd > $MAX ))
do :
done
Sammeln Sie einfach die Zufälligkeit von /dev/urandom
(verwenden Sie diese Option möglicherweise, /dev/random
wenn ein kryptografisch starker Pseudozufallszahlengenerator gewünscht wird, und wenn Sie viel Zeit haben, oder einen Hardware-Zufallszahlengenerator), und löschen Sie jedes Zeichen, das keine Dezimalstelle ist die Ausgabe auf die Länge der $MAX
führenden Nullen und abschneiden. Wenn wir nur zufällig 0'en bekommen dann $rnd
leer ist , so dass in diesem Fall Satz rnd
zu 0
. Überprüfen Sie, ob das Ergebnis außerhalb unseres Bereichs liegt, und wiederholen Sie den Vorgang, wenn dies der Fall ist. Ich habe hier den "Körper" der while-Schleife in die Wache gezwungen, um die Ausführung des Körpers mindestens einmal zu erzwingen, im Geiste, eine do ... while
Schleife zu emulieren , da dies zunächst rnd
undefiniert ist.
Ich glaube, ich habe hier die Bedingungen 1 und 2 erfüllt, aber jetzt habe ich Bedingung 3 durcheinander gebracht. Es ist ein bisschen langsam. Dauert ungefähr eine Sekunde (Zehntelsekunde, wenn ich Glück habe). Tatsächlich ist nicht einmal die Beendigung der Schleife garantiert (obwohl die Wahrscheinlichkeit der Beendigung mit zunehmender Zeit gegen 1 konvergiert).
Gibt es einen effizienten Weg, um unbefangene Zufallszahlen innerhalb eines vorgegebenen und potenziell großen Bereichs in Bash zu erhalten? (Ich werde weiter nachforschen, wenn es die Zeit erlaubt, aber in der Zwischenzeit dachte ich, dass jemand hier eine coole Idee haben könnte!)
Tabelle der Antworten
Die einfachste (und damit tragbare) Idee ist es, einen zufälligen Bitstring gerade lang genug zu generieren. Es gibt verschiedene Möglichkeiten, einen zufälligen Bitstring zu generieren, entweder mit der eingebauten
$RANDOM
Variablen von bash oder mitod
und/dev/urandom
(oder/dev/random
). Wenn die Zufallszahl größer als ist$MAX
, beginnen Sie erneut.Alternativ können externe Tools verwendet werden.
- Die Perl-Lösung
- Pro: ziemlich portabel, einfach, flexibel
- Contra: nicht für sehr große Zahlen über 2 32 -1
- Die Python-Lösung
- Pro: einfach, flexibel, funktioniert auch bei großen Stückzahlen
- Contra: weniger tragbar
- Die zsh-Lösung
- Pro: gut für Leute, die sowieso zsh benutzen
- Contra: wahrscheinlich noch weniger portabel
- Die Perl-Lösung
quelle
rand=$(command)
tun, wenn Siecommand
eine Iteration erhalten, die Ihre Anforderungen erfüllt?dd if=/dev/urandom 2>/dev/null
und weitergeleitet habeod -t d
(umgeht den Umweg über base64), aber es ist mir nicht klar, wie die Konvertierung abläuft und ob sie tatsächlich unvoreingenommen ist. Wenn Sie Ihre Idee zu einem effizienten, funktionierenden Skript erweitern und erklären können, warum es keine Voreingenommenheit gibt, ist dies eine gute Antwort. :)python
oderperl
oder Ihre Lieblingssprache aufrufen , aber dies ist nicht überall verfügbar. Ich würde etwas tragbarer bevorzugen. Nun,awk
die Zufallsfunktion von wäre in Ordnung, denke ich. Aber je tragbarer, desto besser :)perl -e 'print int(rand(2**32-1))');
. Das ist verdammt portabel und wird sehr schnell gehen. Awk wird es nicht schaffen, da die meisten Implementierungen von demselben Startwert ausgehen. So erhalten Sie die gleiche Zufallszahl in nachfolgenden Läufen. Es ändert sich nur innerhalb desselben Laufs.Antworten:
Ich sehe eine andere interessante Methode von hier .
Dies scheint auch eine gute Option zu sein. Es liest 4 Bytes vom Zufallsgerät und formatiert sie als vorzeichenlose Ganzzahl zwischen
0
und2^32-1
.quelle
/dev/urandom
sei denn, Sie wissen, dass Sie brauchen/dev/random
;/dev/random
blockiert unter Linux.od
Befehle anders. Beide geben nur 4-Byte-Ganzzahlen ohne Vorzeichen aus: 1st - from openssl, 2nd - from/dev/random
./dev/urandom
anstatt/dev/random
- Ich sehe keinen Grund, es zu verwenden/dev/random
, und es kann sehr teuer / langsam sein oder andere Teile des Systems verlangsamen. (Fühlen Sie sich frei, bearbeiten Sie sie und erklären Sie, ob sie wirklich benötigt wird.)I
stehtsizeof(int)
vielleicht weniger als4
prinzipiell. Übrigens,od -DAn
scheitert(2**32-1)
aberod -N4 -tu4 -An
funktioniert weiter.Vielen Dank für all Ihre tollen Antworten. Am Ende habe ich die folgende Lösung gefunden, die ich mitteilen möchte.
Bevor ich in näher über das Wie und Warum gehen, hier ist die tl; dr : mein glänzendes neues Skript :-)
Speichern Sie das in
~/bin/rand
und Sie haben bei Ihrer Verfügbarkeit eine süße Zufallsfunktion in der Bash, die eine ganze Zahl in einem beliebigen Bereich abtasten kann. Der Bereich kann negative und positive ganze Zahlen enthalten und bis zu 2 60 -1 lang sein:Alle Ideen der anderen Antwortenden waren großartig. Die Antworten von terdon , JF Sebastian und jimmij verwendeten externe Tools, um die Aufgabe auf einfache und effiziente Weise zu erledigen. Ich bevorzuge jedoch eine echte bash-Lösung für maximale Portabilität und vielleicht ein bisschen, einfach aus Liebe zu bash;)
Ramesh 's und l0b0 ' s Antworten verwendet
/dev/urandom
oder/dev/random
in Kombination mitod
. Das ist gut, aber ihre Ansätze hatten den Nachteil, dass sie nur zufällige ganze Zahlen im Bereich von 0 bis 2 8n -1 für einige n abtasten konnten, da diese Methode Bytes abtastet, dh Bitstrings der Länge 8. Dies sind ziemlich große Sprünge mit zunehmende n.Schließlich beschreibt Falcos Antwort die allgemeine Idee, wie dies für beliebige Bereiche (nicht nur Zweierpotenzen) erfolgen könnte. Im Grunde genommen für einen bestimmten Bereich
{0..max}
, können wir bestimmen , was die nächste Zweierpotenz ist, das heißt, genau wie viele Bits erforderlich sind , zu repräsentierenmax
als Bitstring. Dann können wir nur so viele Bits abtasten und feststellen, ob diese Verzerrung als Ganzzahl größer als istmax
. Wenn ja, wiederholen. Da wir nur so viele Bits abtasten, wie zur Darstellung erforderlich sindmax
, hat jede Iteration eine Wahrscheinlichkeit größer oder gleich 50% des Erfolgs (50% im schlimmsten Fall, 100% im besten Fall). Das ist also sehr effizient.Mein Skript ist im Grunde eine konkrete Implementierung von Falcos Antwort, die in reinem Bash geschrieben und hocheffizient ist, da es die eingebauten bitweisen Operationen von Bash verwendet, um Bitstrings der gewünschten Länge abzutasten. Darüber hinaus wird eine Idee von Eliah Kagan
$RANDOM
gewürdigt , die vorschlägt, die eingebaute Variable zu verwenden, indem Bitfolgen verkettet werden, die aus wiederholten Aufrufen von$RANDOM
. Ich habe tatsächlich sowohl die Verwendungsmöglichkeiten/dev/urandom
als auch implementiert$RANDOM
. Standardmäßig wird das obige Skript verwendet$RANDOM
. (Und ok, wenn/dev/urandom
wir od und tr brauchen , werden diese von POSIX unterstützt.)Wie funktioniert es?
Bevor ich darauf eingehe, zwei Beobachtungen:
Es stellt sich heraus, dass bash keine Ganzzahlen größer als 2 63 -1 verarbeiten kann. Überzeugen Sie sich selbst:
Es scheint, dass Bash intern signierte 64-Bit-Ganzzahlen verwendet, um Ganzzahlen zu speichern. Bei 2 63 "dreht sich alles um" und wir erhalten eine negative ganze Zahl. Wir können also nicht hoffen, mit einer beliebigen Zufallsfunktion einen größeren Bereich als 2 63 -1 zu erhalten. Bash kommt einfach nicht damit klar.
Wann immer wir einen Wert in einem beliebigen Bereich zwischen
min
undmax
mit abtasten möchtenmin != 0
, können wir einfach einen Wert zwischen0
undmax-min
abtasten und dannmin
zum Endergebnis hinzufügen . Dies funktioniert auch , wennmin
und möglicherweise auchmax
sind negativ , aber wir müssen vorsichtig sein , einen Wert zwischen Probe0
und den absoluten Wertmax-min
. Dann können wir uns darauf konzentrieren, wie ein zufälliger Wert zwischen0
und einer beliebigen positiven ganzen Zahl abgetastet wirdmax
. Der Rest ist einfach.Schritt 1: Bestimmen Sie, wie viele Bits benötigt werden, um eine Ganzzahl (den Logarithmus) darzustellen
Für einen bestimmten Wert
max
möchten wir wissen, wie viele Bits erforderlich sind, um ihn als Bitfolge darzustellen. Dies ist so, dass wir später nur so viele Bits wie nötig zufällig abtasten können, was das Skript so effizient macht.Wir werden sehen. Da mit
n
Bits bis zum Wert 2 n -1 dargestellt werden kann, ist die Anzahln
der Bits, die zur Darstellung eines beliebigen Werts erforderlich sind, diex
Obergrenze (log 2 (x + 1)). Wir brauchen also eine Funktion, um die Obergrenze eines Logarithmus zur Basis 2 zu berechnen. Es ist ziemlich selbsterklärend:Wir brauchen die Bedingung,
n>0
damit die Schleife garantiert endet, wenn sie zu groß wird, sich umgibt und negativ wird.Schritt 2: Wählen Sie eine zufällige Bitfolge mit einer Länge aus
n
Die tragbarsten Ideen sind, entweder die eingebaute Variable von bash zu verwenden
/dev/urandom
(oder auch/dev/random
wenn es einen starken Grund gibt)$RANDOM
. Schauen wir uns zuerst an, wie es geht$RANDOM
.Option A: Verwenden von
$RANDOM
Dies basiert auf der Idee von Eliah Kagan. Grundsätzlich
$RANDOM
können wir , da eine 15-Bit-Ganzzahl abgetastet wird,$((RANDOM<<15|RANDOM))
eine 30-Bit-Ganzzahl abtasten. Verschieben Sie also einen ersten Aufruf$RANDOM
um 15 Bit nach links und wenden Sie einen bitweisen oder einen zweiten Aufruf an$RANDOM
, um zwei unabhängig abgetastete Bitstrings effektiv zu verketten (oder zumindest so unabhängig, wie es in bash integriert ist$RANDOM
).Wir können dies wiederholen, um eine 45-Bit- oder 60-Bit-Ganzzahl zu erhalten. Nach dieser Bash kann es nicht mehr verarbeitet werden, aber dies bedeutet, dass wir leicht einen Zufallswert zwischen 0 und 2 60 -1 abtasten können. Um eine n-Bit-Ganzzahl abzutasten, wiederholen wir die Prozedur, bis unsere zufällige Bitfolge, deren Länge in 15-Bit-Schritten zunimmt, eine Länge größer oder gleich n hat. Schließlich schneiden wir die zu großen Bits ab, indem wir sie entsprechend bitweise nach rechts verschieben. Am Ende erhalten wir eine n-Bit-Zufallszahl.
Option B: Verwenden von
/dev/urandom
Alternativ können wir
od
und verwenden/dev/urandom
, um eine n-Bit-Ganzzahl abzutasten.od
Bytes, dh Bitstrings der Länge 8, werden gelesen. Ähnlich wie bei der vorherigen Methode werden nur so viele Bytes abgetastet, dass die äquivalente Anzahl der abgetasteten Bits größer oder gleich n ist, und die zu großen Bits werden abgeschnitten.Die niedrigste Anzahl von Bytes, die benötigt wird, um mindestens n Bits zu erhalten, ist das niedrigste Vielfache von 8, das größer oder gleich n ist, dh Floor ((n + 7) / 8).
Dies funktioniert nur mit 56-Bit-Ganzzahlen. Wenn Sie ein weiteres Byte abtasten, erhalten Sie eine 64-Bit-Ganzzahl, dh einen Wert bis zu 2 64 -1, den die Bash nicht verarbeiten kann.
Zusammenfügen der Teile: Ermitteln Sie zufällige ganze Zahlen in beliebigen Bereichen
Wir können probieren
n
jetzt -Bit bitstrings, aber wir wollen Probe ganze Zahlen in einem Bereich von0
bismax
, gleichmäßig zufällig , womax
willkürlich kann, die nicht unbedingt eine Zweierpotenz. (Wir können Modulo nicht verwenden, da dies zu einer Verzerrung führt.)Der springende Punkt, warum wir uns so sehr bemüht haben, nur so viele Bits abzutasten, wie zur Darstellung des Werts erforderlich sind
max
, ist, dass wir jetzt eine Schleife sicher (und effizient) zum wiederholtenn
Abtasten eines Bitstrings mit -bit verwenden können, bis wir einen niedrigeren Wert abtasten oder gleichmax
. Im ungünstigsten Fall (max
Zweierpotenz) endet jede Iteration mit einer Wahrscheinlichkeit von 50%, und im besten Fall (max
Zweierpotenz minus Eins) endet die erste Iteration mit Sicherheit.Dinge einpacken
Schließlich wollen wir ganze Zahlen zwischen
min
undmax
, womin
undmax
kann beliebig sein, auch negativ abtasten. Wie bereits erwähnt, ist dies jetzt trivial.Lassen Sie uns alles in ein Bash-Skript schreiben. Führe ein paar Argumente aus, die analysiert werden ... Wir wollen zwei Argumente
min
undmax
oder nur ein Argumentmax
, wobei dermin
Standardwert ist0
.... und schließlich, um einen Wert zwischen
min
und gleichmäßig zufällig abzutastenmax
, tasten wir eine zufällige ganze Zahl zwischen0
und dem absoluten Wert vonmax-min
und addierenmin
zum Endergebnis. :-)Davon inspiriert , könnte ich versuchen, diesen PRNG mit Dieharder zu testen und zu vergleichen und meine Erkenntnisse hier einzubringen . :-)
quelle
sizeof(int) == 8
(64bit) aufgrund von--format=u
random.Random
Klasse nutzt 53bit? Generator, um beliebig große Zufallszahlen (mehrere Aufrufe) zurückzugeben,random.SystemRandom
macht dasselbe mitos.urandom()
, das mit implementiert werden kann/dev/urandom
.--format=u8
dann codiere ich die Annahme festsizeof(int)==8
. Auf der anderen Seite gibt es bei Verwendung--format=uL
kein Problem: Ich glaube nicht, dass es eine Plattform gibt, die 64-Bit-Ganzzahlen hat, aber Long-Ints immer noch als etwas Niedrigeres definiert. Grundsätzlich würde ich argumentieren,--format=uL
ermöglicht mehr Flexibilität. Was sind deine Gedanken?long long
dass 64 - Bit sein kann , während int = long = 32bit auf einigen Plattformen. Sie sollten keine 0..2 ** 60-Reichweite beanspruchen, wenn Sie diese nicht auf allen Plattformen garantieren können. Auf der anderen Seite bash selbst nicht diesen Bereich auf solchen Plattformen unterstützen könnte (ich weiß nicht, vielleicht nutzt es maxint_t und dann U8 ist richtig , wenn Sie den festen Bereich behaupten wollen (od
ist nicht die Angabe maxint unterstützen , wenn Ihr Bereich Was auch immer der plattformabhängige Bereich von Bash ist. Wenn der Bash-Bereich von der Größe abhängt, ist uL möglicherweise geeigneter. Möchten Sie den gesamten Bereich, den bash auf allen Betriebssystemen unterstützt, oder einen festen Bereich?Kann es zsh sein?
Sie können auch Samen mit verwenden
rand48(seed)
. Sieheman zshmodules
undman 3 erand48
für detaillierte Beschreibung bei Interesse.quelle
python
ist auf Redhat-basierten Debian-Systemen verfügbar.quelle
Wenn Sie eine Zahl von 0 bis (2 ^ n) -1 wollen, wobei n mod 8 = 0 ist , können Sie einfach n / 8 Bytes abrufen
/dev/random
. Um beispielsweise die dezimale Darstellung eines Zufalls zu erhalten, könnenint
Sie:Wenn Sie nur n Bits verwenden möchten, können Sie zuerst die Obergrenze (n / 8) Bytes verwenden und nach rechts verschieben, um den gewünschten Wert festzulegen. Zum Beispiel, wenn Sie 15 Bits möchten:
Wenn Sie absolut sicher sind, dass Ihnen die Qualität der Zufälligkeit egal ist und Sie eine minimale Laufzeit garantieren möchten, die Sie
/dev/urandom
stattdessen verwenden können/dev/random
. Stellen Sie sicher, dass Sie wissen, was Sie tun, bevor Sie es verwenden/dev/urandom
!quelle
n
zufällige Bytes aus/dev/urandom
und formatieren Sie mitod
. Ähnlich im Geist wie diese Antwort . Beide sind gleich gut :) Obwohl beide den Nachteil haben, einen festen Bereich von 0 bis 2 ^ (n * 8) -1 Bits zu haben, wobei n die Anzahl der Bytes ist. Ich würde eine Methode für einen beliebigen Bereich bevorzugen , bis zu 2 ^ 32-1, aber auch etwas niedriger. Dies schafft die Schwierigkeit der Vorspannung./dev/urandom
statt/dev/random
- Ich sehe keinen Grund für die Verwendung/dev/random
, und es kann sehr teuer / langsam sein oder andere Teile des Systems verlangsamen. (Fühlen Sie sich frei, bearbeiten Sie sie und erklären Sie, ob sie wirklich benötigt wird.)/dev/urandom
Ergebnisse so viel schlechter sind, als/dev/random
dass das Urandom in den meisten Fällen nicht verwendbar ist. Once/dev/urandom
wird initialisiert (beim Systemstart); Die Ergebnisse sind so gut wie/dev/random
für fast alle Anwendungen unter Linux. Auf einigen Systemen sind Zufall und Zufall gleich.--format=u
sollte durch ersetzt werden,--format=u4
weilsizeof(int)
möglicherweise weniger als4
in der Theorie./dev/random
und/dev/urandom
sind nicht zufriedenstellend, und dass „Linux sollte eine sichere RNG hinzufügen , dass Blöcke , bis es ausreichend Saatgut Entropie gesammelt hat und danach verhält sich wieurandom
.“Vorausgesetzt, Sie haben keine Einwände gegen die Verwendung externer Tools, sollte dies Ihre Anforderungen erfüllen:
Es wird die Perl-
rand
Funktion verwendet, die eine Obergrenze als Parameter verwendet. Sie können es nach Belieben einstellen. Wie nahe dies an der wahren Zufälligkeit in der abstrakten mathematischen Definition liegt, würde den Rahmen dieser Website sprengen, sollte aber in Ordnung sein, es sei denn, Sie benötigen es für äußerst sensible Verschlüsselung oder dergleichen. Vielleicht sogar dort, aber ich werde es nicht wagen, eine Meinung zu äußern.quelle
1^32-1
aber Sie müssen es für größere Zahlen optimieren.Sie sollten das nächste (2 ^ X) -1 erhalten, das gleich oder größer als Ihr gewünschtes Maximum ist, und die Anzahl der Bits erhalten. Rufen Sie dann einfach / dev / random mehrmals auf und fügen Sie alle Bits zusammen, bis Sie genug haben, und kürzen Sie alle Bits, die zu viel sind. Wenn die resultierende Zahl größer als Ihre maximale Wiederholung ist. Im schlimmsten Fall haben Sie eine Chance von mehr als 50%, eine Zufallszahl unter Ihr Maximum zu bringen. In diesem schlimmsten Fall nehmen Sie durchschnittlich zwei Anrufe entgegen.
quelle
/dev/urandom
, aber in beiden Antworten ist es immer ein Vielfaches von 8 Bits. Das Abschneiden der Bits, die vor dem Formatieren zu viel für niedrigere Bereiche sind, um sie zu dezimalisieren,od
ist eine gute Idee, um die Effizienz zu verbessern, da die Schleife nur eine erwartete Anzahl von 2 Iterationen aufweist, wie Sie gut erklären. In Kombination mit einer der genannten Antworten ist dies wahrscheinlich der richtige Weg.Ihre Antwort ist interessant, aber ziemlich lang.
Wenn Sie beliebig große Zahlen möchten, können Sie mehrere Zufallszahlen zu einem Helfer zusammenfügen:
Wenn das Problem eine Verzerrung ist, entfernen Sie es einfach.
Zusammenführen dieser Funktionen
quelle