Ich möchte eindeutige Zufallszahlen zwischen 0 und 1000 generieren, die sich nie wiederholen (dh 6 wird nicht zweimal angezeigt), aber dazu wird nicht auf eine O (N) -Suche nach vorherigen Werten zurückgegriffen. Ist das möglich?
algorithm
math
random
language-agnostic
Dicroce
quelle
quelle
O(n)
in der Zeit oder im Gedächtnis), sind viele der folgenden Antworten falsch, einschließlich der akzeptierten Antwort.Antworten:
Initialisieren Sie ein Array mit 1001 Ganzzahlen mit den Werten 0-1000 und setzen Sie eine Variable max auf den aktuellen Max-Index des Arrays (beginnend mit 1000). Wählen Sie eine Zufallszahl r zwischen 0 und max, tauschen Sie die Zahl an der Position r mit der Zahl an der Position max aus und geben Sie die Zahl jetzt an der Position max zurück. Dekrementiere max um 1 und fahre fort. Wenn max 0 ist, setzen Sie max wieder auf die Größe des Arrays - 1 und starten Sie erneut, ohne dass das Array neu initialisiert werden muss.
Update: Obwohl ich diese Methode bei der Beantwortung der Frage selbst entwickelt habe, stelle ich nach einigen Recherchen fest, dass es sich um eine modifizierte Version von Fisher-Yates handelt, die als Durstenfeld-Fisher-Yates oder Knuth-Fisher-Yates bekannt ist. Da die Beschreibung möglicherweise etwas schwierig zu befolgen ist, habe ich nachfolgend ein Beispiel angegeben (mit 11 Elementen anstelle von 1001):
Das Array beginnt mit 11 Elementen, die mit Array [n] = n initialisiert wurden. Max beginnt mit 10:
Bei jeder Iteration wird eine Zufallszahl r zwischen 0 und max ausgewählt, Array [r] und Array [max] werden ausgetauscht, das neue Array [max] wird zurückgegeben und max wird dekrementiert:
Nach 11 Iterationen wurden alle Zahlen im Array ausgewählt, max == 0, und die Array-Elemente werden gemischt:
Zu diesem Zeitpunkt kann max auf 10 zurückgesetzt werden und der Vorgang kann fortgesetzt werden.
quelle
N
Iterationen durchführen müssen (11 in diesem Beispiel), um jedes Mal das gewünschte Ergebnis zu erzielenO(n)
? Da SieN
Iterationen durchführen müssen, umN!
Kombinationen aus demselben Anfangszustand zu erhalten, ist Ihre Ausgabe ansonsten nur einer von N Zuständen.Du kannst das:
Dies erfordert also nicht jedes Mal eine Suche nach alten Werten, aber es erfordert immer noch O (N) für das anfängliche Mischen. Aber wie Nils in Kommentaren betonte, wird dies O (1) amortisiert.
quelle
Verwenden Sie ein Schieberegister für die maximale lineare Rückkopplung .
Es ist in ein paar Zeilen C implementierbar und führt zur Laufzeit kaum mehr als ein paar Tests / Verzweigungen, ein wenig Addition und Bitverschiebung durch. Es ist nicht zufällig, aber es täuscht die meisten Menschen.
quelle
Sie können einen linearen Kongruenzgenerator verwenden . Wobei
m
(der Modul) die nächste Primzahl ist, die größer als 1000 ist. Wenn Sie eine Zahl außerhalb des Bereichs erhalten, erhalten Sie einfach die nächste. Die Sequenz wird erst wiederholt, wenn alle Elemente aufgetreten sind und Sie keine Tabelle verwenden müssen. Beachten Sie jedoch die Nachteile dieses Generators (einschließlich mangelnder Zufälligkeit).quelle
k
in der Sequenz weiter als voneinander entfernt sind, niemals zusammen auftreten).Sie können die formaterhaltende Verschlüsselung verwenden , um einen Zähler zu verschlüsseln. Ihr Zähler geht nur von 0 aufwärts und die Verschlüsselung verwendet einen Schlüssel Ihrer Wahl, um ihn in einen scheinbar zufälligen Wert mit dem gewünschten Radix und der gewünschten Breite umzuwandeln. ZB für das Beispiel in dieser Frage: Radix 10, Breite 3.
Blockchiffren haben normalerweise eine feste Blockgröße von zB 64 oder 128 Bit. Mit der formaterhaltenden Verschlüsselung können Sie jedoch eine Standardverschlüsselung wie AES verwenden und mit einem Algorithmus, der immer noch kryptografisch robust ist, eine Verschlüsselung mit kleinerer Breite erstellen, unabhängig von der gewünschten Größe und Breite.
Es wird garantiert nie zu Kollisionen kommen (da kryptografische Algorithmen eine 1: 1-Zuordnung erstellen). Es ist auch reversibel (eine 2-Wege-Zuordnung), sodass Sie die resultierende Zahl nehmen und zu dem Zählerwert zurückkehren können, mit dem Sie begonnen haben.
Diese Technik benötigt keinen Speicher zum Speichern eines gemischten Arrays usw., was auf Systemen mit begrenztem Speicher von Vorteil sein kann.
AES-FFX ist eine vorgeschlagene Standardmethode, um dies zu erreichen. Ich habe mit grundlegendem Python-Code experimentiert, der auf der AES-FFX-Idee basiert, obwohl er nicht vollständig konform ist - siehe Python-Code hier . Es kann beispielsweise einen Zähler mit einer zufällig aussehenden 7-stelligen Dezimalzahl oder einer 16-Bit-Zahl verschlüsseln. Hier ist ein Beispiel für Radix 10, Breite 3 (um eine Zahl zwischen 0 und 999 einschließlich zu geben), wie in der Frage angegeben:
Ändern Sie den Verschlüsselungsschlüssel, um verschiedene sich nicht wiederholende Pseudozufallssequenzen zu erhalten. Jeder Verschlüsselungsschlüssel erzeugt eine andere nicht wiederholte Pseudozufallssequenz.
quelle
k
in der Sequenz mehr als auseinander liegen, niemals zusammen auftreten).k
?1,2,...,N
durch eine Sequenz mit denselben Nummern in einer anderen, aber immer noch konstanten Reihenfolge zu ersetzen . Die Zahlen werden dann nacheinander aus dieser Sequenz gezogen.k
ist die Anzahl der ausgewählten Werte (das OP hat keinen Buchstaben dafür angegeben, daher musste ich einen einführen).Bei niedrigen Zahlen wie 0 ... 1000 ist es einfach, eine Liste mit allen Zahlen zu erstellen und zu mischen. Wenn die Anzahl der zu zeichnenden Zahlen jedoch sehr groß ist, gibt es eine andere elegante Möglichkeit: Sie können eine pseudozufällige Permutation mithilfe eines Schlüssels und einer kryptografischen Hash-Funktion erstellen. Siehe den folgenden C ++ - Beispiel-Pseudocode:
Hier
hash
ist nur eine beliebige Pseudozufallsfunktion, die eine Zeichenfolge einer möglicherweise großen vorzeichenlosen Ganzzahl zuordnet. Die Funktionrandperm
ist eine Permutation aller Zahlen innerhalb von 0 ... pow (2, Bits) -1 unter der Annahme eines festen Schlüssels. Dies folgt aus der Konstruktion, da jeder Schritt, der die Variable ändert,index
reversibel ist. Dies ist inspiriert von einer Feistel-Chiffre .quelle
hash()
, wie im obigen Code verwendet, um eine sichere Pseudozufallsfunktion handelt, wird diese Konstruktion nachweislich (Luby & Rackoff, 1988) eine Pseudozufallspermutation ergeben , die nicht von einer echten Zufallsmischung mit wesentlich weniger Aufwand als einer erschöpfenden unterschieden werden kann Suche im gesamten Schlüsselraum, der in der Schlüssellänge exponentiell ist. Selbst bei Schlüsseln mit angemessener Größe (z. B. 128 Bit) liegt dies über der auf der Erde verfügbaren Gesamtleistung.hash( key + "/" + int2str(temp) )
obige Ad-hoc- Konstruktion durch HMAC zu ersetzen , deren Sicherheit nachweislich auf die der zugrunde liegenden Hash-Komprimierungsfunktion reduziert werden kann. Auch die Verwendung von HMAC könnte dazu führen Es ist weniger wahrscheinlich, dass jemand fälschlicherweise versucht, diese Konstruktion mit einer unsicheren Nicht-Krypto-Hash-Funktion zu verwenden.)Sie können meinen hier beschriebenen Xincrol-Algorithmus verwenden:
http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html
Dies ist eine rein algorithmische Methode zum Generieren von zufälligen, aber eindeutigen Zahlen ohne Arrays, Listen, Permutationen oder hohe CPU-Auslastung.
In der neuesten Version können Sie auch den Zahlenbereich festlegen, z. B. wenn ich eindeutige Zufallszahlen im Bereich von 0-1073741821 möchte.
Ich habe es praktisch benutzt
Es ist offen, kostenlos. Versuche es...
quelle
k
in der Sequenz auseinander liegen, niemals zusammen auftreten).Sie brauchen nicht einmal ein Array, um dieses zu lösen.
Du brauchst eine Bitmaske und einen Zähler.
Initialisieren Sie den Zähler auf Null und erhöhen Sie ihn bei aufeinanderfolgenden Aufrufen. XOR den Zähler mit der Bitmaske (zufällig beim Start ausgewählt oder fest), um eine Pseudozufallszahl zu generieren. Wenn Sie keine Zahlen haben können, die 1000 überschreiten, verwenden Sie keine Bitmaske, die breiter als 9 Bit ist. (Mit anderen Worten, die Bitmaske ist eine Ganzzahl, die nicht über 511 liegt.)
Stellen Sie sicher, dass Sie den Zähler auf Null zurücksetzen, wenn er 1000 überschreitet. Zu diesem Zeitpunkt können Sie - wenn Sie möchten - eine andere zufällige Bitmaske auswählen, um denselben Satz von Zahlen in einer anderen Reihenfolge zu erzeugen.
quelle
Ich denke, dass linearer Kongruenzgenerator die einfachste Lösung wäre.
und es gibt nur 3 Einschränkungen für a , c und m- Werte
PS Die Methode wurde bereits erwähnt, aber der Beitrag hat falsche Annahmen über die konstanten Werte. Die folgenden Konstanten sollten für Ihren Fall gut funktionieren
In Ihrem Fall verwenden Sie können
a = 1002
,c = 757
,m = 1001
quelle
Hier ist ein Code, den ich eingegeben habe und der die Logik der ersten Lösung verwendet. Ich weiß, dass dies "sprachunabhängig" ist, wollte dies aber nur als Beispiel in C # präsentieren, falls jemand nach einer schnellen praktischen Lösung sucht.
quelle
Diese Methodenergebnisse sind geeignet, wenn das Limit hoch ist und Sie nur wenige Zufallszahlen generieren möchten.
Beachten Sie, dass die Zahlen in aufsteigender Reihenfolge generiert werden. Sie können sie jedoch anschließend mischen.
quelle
(top,n)=(100,10)
sind :(0.01047705, 0.01044825, 0.01041225, ..., 0.0088324, 0.008723, 0.00863635)
. Ich habe in Python getestet, daher könnten hier geringfügige Unterschiede in der Mathematik eine Rolle spielen (ich habe sichergestellt, dass alle Operationen zur Berechnungr
Gleitkomma sind).Sie könnten einen guten Pseudozufallszahlengenerator verwenden mit 10 Bits verwenden und 1001 bis 1023 wegwerfen, wobei 0 bis 1000 übrig bleiben.
Von hier bekommen wir das Design für ein 10 Bit PRNG ..
10 Bits, Rückkopplungspolynom x ^ 10 + x ^ 7 + 1 (Periode 1023)
Verwenden Sie einen Galois LFSR, um schnellen Code zu erhalten
quelle
N Nicht wiederholte Zufallszahlen weisen je nach Bedarf eine Komplexität von O (n) auf.
Hinweis: Random sollte statisch sein, wobei die Gewindesicherheit angewendet wird.
quelle
Angenommen, Sie möchten die gemischten Listen immer wieder durchgehen, ohne die
O(n)
Verzögerung jedes Mal zu haben, wenn Sie neu beginnen, um sie erneut zu mischen. In diesem Fall können wir Folgendes tun:Erstellen Sie 2 Listen A und B mit 0 bis 1000 nimmt
2n
Platz ein.Das Mischen der Liste A mit Fisher-Yates braucht
n
Zeit.Wenn Sie eine Zahl zeichnen, mischen Sie in einem Schritt Fisher-Yates auf der anderen Liste.
Wenn sich der Cursor am Listenende befindet, wechseln Sie zur anderen Liste.
Vorverarbeitung
Zeichnen
quelle
[1,3,4,5,2]
wird das gleiche Ergebnis wie schlurfenden produzieren[1,2,3,4,5]
.Die Frage Wie generieren Sie effizient eine Liste von K nicht wiederholenden ganzen Zahlen zwischen 0 und einer Obergrenze N. wird als Duplikat verknüpft - und wenn Sie etwas möchten, das O (1) pro generierter Zufallszahl ist (ohne O (n)) Startkosten)) Es gibt eine einfache Änderung der akzeptierten Antwort.
Erstellen Sie eine leere ungeordnete Karte (eine leere geordnete Karte benötigt O (log k) pro Element) von Ganzzahl zu Ganzzahl - anstatt ein initialisiertes Array zu verwenden. Setzen Sie max auf 1000, wenn dies das Maximum ist.
Der einzige Unterschied zur Verwendung eines initialisierten Arrays besteht darin, dass die Initialisierung von Elementen verschoben / übersprungen wird - es werden jedoch genau dieselben Zahlen aus demselben PRNG generiert.
quelle
Eine andere Möglichkeit:
Sie können ein Array von Flags verwenden. Und nimm den nächsten, wenn er bereits ausgewählt ist.
Aber Vorsicht nach 1000 Aufrufen, die Funktion wird niemals enden, daher müssen Sie eine Schutzmaßnahme treffen.
quelle
Hier ist ein Beispiel für einen COBOL-Code, mit dem Sie herumspielen können.
Ich kann Ihnen die Datei RANDGEN.exe senden, damit Sie damit spielen können, um zu sehen, ob Sie es wollen.
quelle
Die meisten Antworten hier garantieren nicht, dass sie nicht zweimal dieselbe Nummer zurückgeben. Hier ist eine richtige Lösung:
Ich bin nicht sicher, ob die Einschränkung gut spezifiziert ist. Man nimmt an, dass nach 1000 anderen Ausgaben ein Wert wiederholt werden darf, aber dass naiv 0 unmittelbar nach 0 folgen kann, solange beide am Ende und am Anfang von Sätzen von 1000 erscheinen. Umgekehrt ist es möglich, einen Abstand von zu halten 1000 andere Werte zwischen Wiederholungen erzwingen eine Situation, in der sich die Sequenz jedes Mal auf genau dieselbe Weise wiedergibt, da kein anderer Wert außerhalb dieses Grenzwerts aufgetreten ist.
Hier ist eine Methode, die immer mindestens 500 andere Werte garantiert, bevor ein Wert wiederholt werden kann:
quelle
Wenn N größer als 1000 ist und Sie K Zufallsstichproben ziehen müssen, können Sie einen Satz verwenden, der die bisherigen Stichproben enthält. Für jede Ziehung verwenden Sie die Ablehnungsstichprobe , bei der es sich um eine "fast" O (1) -Operation handelt, sodass die Gesamtlaufzeit bei O (N) -Speicherung nahezu O (K) beträgt.
Dieser Algorithmus stößt auf Kollisionen, wenn K "nahe" N ist. Dies bedeutet, dass die Laufzeit viel schlechter als O (K) ist. Eine einfache Lösung besteht darin, die Logik umzukehren, sodass Sie für K> N / 2 alle noch nicht gezogenen Stichproben aufzeichnen. Bei jeder Ziehung wird eine Probe aus dem Ablehnungssatz entfernt.
Das andere offensichtliche Problem bei der Ablehnungsabtastung besteht darin, dass es sich um einen O (N) -Speicher handelt, was eine schlechte Nachricht ist, wenn N in Milliardenhöhe oder mehr liegt. Es gibt jedoch einen Algorithmus, der dieses Problem löst. Dieser Algorithmus wird nach seinem Erfinder Vitters Algorithmus genannt. Der Algorithmus wird beschrieben hier . Der Kern des Vitter-Algorithmus besteht darin, dass Sie nach jeder Ziehung einen zufälligen Sprung mit einer bestimmten Verteilung berechnen, die eine gleichmäßige Abtastung garantiert.
quelle
Fischer Yates
Es ist tatsächlich O (n-1), da Sie nur einen Swap für die letzten beiden benötigen.
Dies ist C #
quelle
Bitte sehen Sie meine Antwort unter https://stackoverflow.com/a/46807110/8794687
Es ist eine der einfachsten Algorithmen , die durchschnittliche Zeit , Komplexität haben O ( s log s ), s die Stichprobengröße bezeichnet. Es gibt dort auch einige Links zu Hash-Tabellen-Algorithmen, deren Komplexität angeblich O ( s ) ist.
quelle
Jemand hat "Zufallszahlen in Excel erstellen" gepostet. Ich benutze dieses Ideal. Erstellen Sie eine Struktur mit 2 Teilen, str.index und str.ran; Erstellen Sie für 10 Zufallszahlen ein Array von 10 Strukturen. Setzen Sie den str.index von 0 auf 9 und str.ran auf eine andere Zufallszahl.
Sortieren Sie das Array nach den Werten in arr [i] .ran. Der str.index ist jetzt in zufälliger Reihenfolge. Unten ist c Code:
quelle