In diesem Fall ist der MAX nur 5, sodass ich die Duplikate einzeln überprüfen kann. Wie kann ich dies jedoch einfacher tun? Was ist zum Beispiel, wenn der MAX einen Wert von 20 hat? Vielen Dank.
int MAX = 5;
for (i = 1 , i <= MAX; i++)
{
drawNum[1] = (int)(Math.random()*MAX)+1;
while (drawNum[2] == drawNum[1])
{
drawNum[2] = (int)(Math.random()*MAX)+1;
}
while ((drawNum[3] == drawNum[1]) || (drawNum[3] == drawNum[2]) )
{
drawNum[3] = (int)(Math.random()*MAX)+1;
}
while ((drawNum[4] == drawNum[1]) || (drawNum[4] == drawNum[2]) || (drawNum[4] == drawNum[3]) )
{
drawNum[4] = (int)(Math.random()*MAX)+1;
}
while ((drawNum[5] == drawNum[1]) ||
(drawNum[5] == drawNum[2]) ||
(drawNum[5] == drawNum[3]) ||
(drawNum[5] == drawNum[4]) )
{
drawNum[5] = (int)(Math.random()*MAX)+1;
}
}
Antworten:
Am einfachsten wäre es, eine Liste der möglichen Zahlen (1..20 oder was auch immer) zu erstellen und diese dann zu mischen
Collections.shuffle
. Dann nehmen Sie einfach so viele Elemente, wie Sie möchten. Dies ist großartig, wenn Ihre Reichweite der Anzahl der Elemente entspricht, die Sie am Ende benötigen (z. B. zum Mischen eines Kartenspiels).Das funktioniert nicht so gut, wenn Sie 10 zufällige Elemente im Bereich von 1 bis 10.000 möchten (sagen wir) - Sie würden am Ende unnötig viel Arbeit erledigen. An diesem Punkt ist es wahrscheinlich besser, eine Reihe von Werten, die Sie bisher generiert haben, beizubehalten und Zahlen in einer Schleife zu generieren, bis die nächste nicht mehr vorhanden ist:
Seien Sie jedoch vorsichtig mit der Auswahl des Sets - ich habe es sehr bewusst verwendet,
LinkedHashSet
da es die Einfügereihenfolge beibehält, die uns hier wichtig ist.Eine weitere Möglichkeit besteht darin, immer Fortschritte zu erzielen, indem der Bereich jedes Mal verringert und vorhandene Werte ausgeglichen werden. Angenommen, Sie möchten 3 Werte im Bereich 0..9. Bei der ersten Iteration würden Sie eine beliebige Zahl im Bereich 0..9 generieren - nehmen wir an, Sie generieren eine 4.
Bei der zweiten Iteration würden Sie dann eine Zahl im Bereich 0..8 generieren. Wenn die generierte Zahl kleiner als 4 ist, behalten Sie sie unverändert bei. Andernfalls fügen Sie eine hinzu. Das ergibt einen Ergebnisbereich von 0..9 ohne 4. Angenommen, wir erhalten auf diese Weise 7.
Bei der dritten Iteration würden Sie eine Zahl im Bereich 0..7 generieren. Wenn die generierte Zahl kleiner als 4 ist, behalten Sie sie unverändert bei. Wenn es 4 oder 5 ist, würden Sie eine hinzufügen. Wenn es 6 oder 7 ist, würden Sie zwei hinzufügen. Auf diese Weise beträgt der Ergebnisbereich 0..9 ohne 4 oder 6.
quelle
So würde ich es machen
Wie der geschätzte Herr Skeet betont hat:
Wenn n die Anzahl der zufällig ausgewählten Zahlen ist, die Sie auswählen möchten, und N der gesamte Stichprobenraum der zur Auswahl verfügbaren Zahlen ist:
quelle
quelle
Es gibt eine andere Möglichkeit, mit LFSR "zufällige" geordnete Zahlen zu erstellen. Schauen Sie sich Folgendes an:
http://en.wikipedia.org/wiki/Linear_feedback_shift_register
Mit dieser Technik können Sie die geordnete Zufallszahl nach Index ermitteln und sicherstellen, dass die Werte nicht dupliziert werden.
Dies sind jedoch keine WAHREN Zufallszahlen, da die Zufallsgenerierung deterministisch ist.
Aber je Ihrem Fall können Sie diese Technik verwenden , um die Menge der Verarbeitung auf Erzeugung von Zufallszahlen zu reduzieren , wenn schlurfenden verwenden.
Hier ein LFSR-Algorithmus in Java (ich habe ihn an einen Ort gebracht, an den ich mich nicht erinnere):
quelle
Ein weiterer Ansatz, mit dem Sie angeben können, mit wie vielen Zahlen Sie möchten
size
und mit welchenmin
undmax
Werten die zurückgegebenen ZahlenUm es zu verwenden, werden 7 Zahlen zwischen 0 und 25 zurückgegeben.
quelle
Dies wäre viel einfacher in
java-8
:quelle
Der effizienteste und einfachste Weg, sich nicht wiederholende Zufallszahlen zu erhalten, wird durch diesen Pseudocode erklärt. Es ist nicht erforderlich, verschachtelte Schleifen oder Hash-Lookups zu haben:
Angenommen, die erste durch Iteration erzeugte Zufallszahl 3 beginnt (von 0 bis 19). Dies würde Ergebnisse [0] = Mapping [3] ergeben, dh den Wert 3. Wir würden dann Mapping [3] 19 zuweisen.
In der nächsten Iteration betrug die Zufallszahl 5 (von 0 bis 18). Dies würde Ergebnisse [1] = Mapping [5] ergeben, dh den Wert 5. Wir würden dann Mapping [5] 18 zuweisen.
Angenommen, die nächste Iteration wählt erneut 3 (von 0 bis 17). Ergebnissen [2] würde der Wert von Mapping [3] zugewiesen, aber jetzt ist dieser Wert nicht 3, sondern 19.
Der gleiche Schutz bleibt für alle Nummern bestehen, auch wenn Sie fünfmal hintereinander dieselbe Nummer erhalten haben. Wenn der Zufallszahlengenerator Ihnen beispielsweise fünfmal hintereinander 0 geben würde, wären die Ergebnisse: [0, 19, 18, 17, 16].
Sie würden nie zweimal dieselbe Nummer bekommen.
quelle
Das Generieren aller Indizes einer Sequenz ist im Allgemeinen eine schlechte Idee, da dies viel Zeit in Anspruch nehmen kann, insbesondere wenn das Verhältnis der zu wählenden Zahlen
MAX
gering ist (die Komplexität wird dominiert vonO(MAX)
). Dies wird schlimmer, wenn sich das Verhältnis der zu wählenden Zahlen zuMAX
eins nähert, da dann das Entfernen der gewählten Indizes aus der Folge aller ebenfalls teuer wird (wir nähern unsO(MAX^2/2)
). Bei kleinen Zahlen funktioniert dies jedoch im Allgemeinen gut und ist nicht besonders fehleranfällig.Das Filtern der generierten Indizes mithilfe einer Sammlung ist ebenfalls eine schlechte Idee, da einige Zeit für das Einfügen der Indizes in die Sequenz aufgewendet wird und der Fortschritt nicht garantiert ist, da dieselbe Zufallszahl mehrmals gezeichnet werden kann (aber groß genug
MAX
ist dies unwahrscheinlich ). Dies kann nahezu komplex seinO(k n log^2(n)/2)
, da die Duplikate ignoriert werden und angenommen wird, dass die Sammlung einen Baum für eine effiziente Suche verwendet (jedoch mit erheblichen konstanten Kostenk
für die Zuweisung der Baumknoten und möglicherweise für die Neuverteilung ).Eine andere Möglichkeit besteht darin, die Zufallswerte von Anfang an eindeutig zu generieren, um sicherzustellen, dass Fortschritte erzielt werden. Das heißt, in der ersten Runde wird ein zufälliger Index in
[0, MAX]
generiert:In der zweiten Runde wird nur
[0, MAX - 1]
generiert (da bereits ein Element ausgewählt wurde):Die Werte der Indizes müssen dann angepasst werden: Wenn der zweite Index in die zweite Hälfte der Sequenz fällt (nach dem ersten Index), muss er erhöht werden, um die Lücke zu berücksichtigen. Wir können dies als Schleife implementieren und so eine beliebige Anzahl eindeutiger Elemente auswählen.
Für kurze Sequenzen ist dies ein ziemlich schneller
O(n^2/2)
Algorithmus:Wo
n_select_num
ist Ihr 5 undn_number_num
ist IhrMAX
. Dasn_Rand(x)
gibt zufällige ganze Zahlen in[0, x]
(einschließlich) zurück. Dies kann etwas beschleunigt werden, wenn viele Elemente (z. B. nicht 5, sondern 500) ausgewählt werden, indem die binäre Suche verwendet wird, um die Einfügemarke zu finden. Dazu müssen wir sicherstellen, dass wir die Anforderungen erfüllen.Wir werden eine binäre Suche mit dem Vergleich durchführen,
n + j < rand_num[j]
der der gleiche ist wien < rand_num[j] - j
. Wir müssen zeigen, dass diesrand_num[j] - j
immer noch eine sortierte Sequenz für eine sortierte Sequenz istrand_num[j]
. Dies lässt sich glücklicherweise leicht zeigen, da der niedrigste Abstand zwischen zwei Elementen des Originalsrand_num
eins ist (die generierten Zahlen sind eindeutig, sodass immer ein Unterschied von mindestens 1 besteht). Wenn wir gleichzeitig die Indizesj
von allen Elementen subtrahieren, sind die Indexunterschiederand_num[j]
genau 1. Im "schlimmsten" Fall erhalten wir also eine konstante Sequenz - die jedoch niemals abnimmt. Die binäre Suche kann daher verwendet werden und ergibt einenO(n log(n))
Algorithmus:Und schlussendlich:
Ich habe dies an drei Benchmarks getestet. Zuerst wurden 3 Zahlen aus 7 Elementen ausgewählt und ein Histogramm der ausgewählten Elemente wurde über 10.000 Läufe akkumuliert:
Dies zeigt, dass jedes der 7 Elemente ungefähr gleich oft ausgewählt wurde und keine offensichtliche Verzerrung durch den Algorithmus verursacht wird. Alle Sequenzen wurden auch auf Richtigkeit (Eindeutigkeit des Inhalts) überprüft.
Der zweite Benchmark umfasste die Auswahl von 7 Zahlen aus 5000 Artikeln. Die Zeit mehrerer Versionen des Algorithmus wurde über 10.000.000 Läufe akkumuliert. Die Ergebnisse werden in Kommentaren im Code als bezeichnet
b1
. Die einfache Version des Algorithmus ist etwas schneller.Der dritte Benchmark umfasste die Auswahl von 700 Zahlen aus 5000 Artikeln. Die Zeit mehrerer Versionen des Algorithmus wurde erneut akkumuliert, diesmal über 10.000 Läufe. Die Ergebnisse werden in Kommentaren im Code als bezeichnet
b2
. Die binäre Suchversion des Algorithmus ist jetzt mehr als zweimal schneller als die einfache.Die zweite Methode ist schneller für die Auswahl von mehr als ca. 75 Elementen auf meinem Computer (beachten Sie, dass die Komplexität beider Algorithmen nicht von der Anzahl der Elemente abhängt
MAX
).Es ist erwähnenswert, dass die obigen Algorithmen die Zufallszahlen in aufsteigender Reihenfolge erzeugen. Es wäre jedoch einfach, ein weiteres Array hinzuzufügen, zu dem die Zahlen in der Reihenfolge gespeichert werden, in der sie generiert wurden, und diese stattdessen zurückzugeben (zu vernachlässigbaren zusätzlichen Kosten
O(n)
). Es ist nicht notwendig, die Ausgabe zu mischen: das wäre viel langsamer.Beachten Sie, dass sich die Quellen in C ++ befinden. Ich habe kein Java auf meinem Computer, aber das Konzept sollte klar sein.
EDIT :
Zur Unterhaltung habe ich auch den Ansatz implementiert, der eine Liste mit allen Indizes generiert
0 .. MAX
, sie zufällig auswählt und aus der Liste entfernt, um die Eindeutigkeit zu gewährleisten. Da ich ziemlich hoch gewählt habeMAX
(5000), ist die Leistung katastrophal:Ich habe den Ansatz auch mit einer
set
(einer C ++ - Sammlung) implementiert , die beim Benchmark tatsächlich an zweiter Stelleb2
steht und nur etwa 50% langsamer ist als der Ansatz mit der binären Suche. Dies ist verständlich, daset
ein Binärbaum verwendet wird, bei dem die Einfügekosten der binären Suche ähnlich sind. Der einzige Unterschied besteht in der Möglichkeit, doppelte Elemente zu erhalten, was den Fortschritt verlangsamt.Der vollständige Quellcode ist hier .
quelle
Sie können eine der Klassen verwenden, die die Set-Schnittstelle ( API ) implementieren , und dann jede von Ihnen generierte Zahl mit Set.add () einfügen.
Wenn der Rückgabewert falsch ist, wissen Sie, dass die Nummer bereits zuvor generiert wurde.
quelle
Anstatt dies alles zu tun, erstellen Sie ein
LinkedHashSet
Objekt und Zufallszahlen nachMath.random()
Funktion .... Wenn ein doppelter Eintrag auftritt,LinkedHashSet
fügt das Objekt diese Nummer nicht zu seiner Liste hinzu ... Da in dieser Sammlungsklasse keine doppelten Werte zulässig sind. Am Ende erhalten Sie eine Liste von Zufallszahlen ohne doppelte Werte ....: D.quelle
Ihr Problem scheint sich zu verringern, wenn Sie zufällig k Elemente aus einer Sammlung von n Elementen auswählen. Die Collections.shuffle-Antwort ist somit korrekt, aber wie bereits erwähnt ineffizient: ihr O (n).
Wikipedia: Fisher-Yates-Shuffle hat eine O (k) -Version, wenn das Array bereits vorhanden ist. In Ihrem Fall gibt es kein Array von Elementen, und das Erstellen des Arrays von Elementen kann sehr teuer sein, z. B. wenn max 10000000 statt 20 wäre.
Der Shuffle-Algorithmus umfasst das Initialisieren eines Arrays der Größe n, wobei jedes Element seinem Index entspricht, das Auswählen von k Zufallszahlen für jede Zahl in einem Bereich, wobei das Maximum kleiner als der vorherige Bereich ist, und das Vertauschen von Elementen gegen Ende des Arrays.
Sie können dieselbe Operation in O (k) -Zeit mit einer Hashmap ausführen, obwohl ich zugebe, dass dies eine Art Schmerz ist. Beachten Sie, dass sich dies nur lohnt, wenn k viel kleiner als n ist. (dh k ~ lg (n) oder so), andernfalls sollten Sie den Shuffle direkt verwenden.
Sie verwenden Ihre Hashmap als effiziente Darstellung des Hintergrundarrays im Shuffle-Algorithmus. Elemente des Arrays, die dem Index entsprechen, müssen nicht in der Karte angezeigt werden. Auf diese Weise können Sie ein Array der Größe n in konstanter Zeit darstellen. Es wird keine Zeit für die Initialisierung aufgewendet.
Wählen Sie k Zufallszahlen: Die erste liegt im Bereich von 0 bis n-1, die zweite von 0 bis n-2, die dritte von 0 bis n-3 usw. bis nk.
Behandeln Sie Ihre Zufallszahlen als eine Reihe von Swaps. Der erste zufällige Index wechselt zur endgültigen Position. Der zweite Zufallsindex wechselt zur vorletzten Position. Anstatt jedoch gegen ein Backing-Array zu arbeiten, sollten Sie gegen Ihre Hashmap arbeiten. Ihre Hashmap speichert jedes Element, das nicht in Position ist.
quelle
creating the array of elements could be very expensive
- Warum sollte das Erstellen eines Arrays teurer sein als das Mischen? Ich denke, es gibt absolut keinen Grund für Pessimismus in diesem Punkt :-)Der folgende Code erstellt eine Sequenz-Zufallszahl zwischen [1, m], die zuvor nicht generiert wurde.
quelle
Es gibt einen Algorithmus für den Kartenstapel: Sie erstellen eine geordnete Reihe von Zahlen (der "Kartenstapel") und wählen in jeder Iteration eine Zahl an einer zufälligen Position aus (wobei Sie natürlich die ausgewählte Zahl aus dem "Kartenstapel" entfernen).
quelle
Hier ist eine effiziente Lösung für die schnelle Erstellung eines zufälligen Arrays. Nach der Randomisierung können Sie einfach das
n
-te Elemente
des Arrays auswählen , inkrementierenn
und zurückgebene
. Diese Lösung hat O (1) zum Erhalten einer Zufallszahl und O (n) zum Initialisieren, aber als Kompromiss erfordert sie eine gute Menge an Speicher, wenn n groß genug wird.quelle
Es gibt eine effizientere und weniger umständliche Lösung für Ganzzahlen als eine Collections.shuffle.
Das Problem ist dasselbe wie das sukzessive Auswählen von Elementen nur aus den nicht ausgewählten Elementen in einem Satz und das Ordnen dieser Elemente an einem anderen Ort. Dies ist genau so, als würde man zufällig Karten austeilen oder Gewinnspielkarten aus einem Hut oder einer Tonne ziehen.
Dieser Algorithmus funktioniert zum Laden eines beliebigen Arrays und zum Erreichen einer zufälligen Reihenfolge am Ende des Ladens. Es funktioniert auch zum Hinzufügen zu einer Listensammlung (oder einer anderen indizierten Sammlung) und zum Erreichen einer zufälligen Reihenfolge in der Sammlung am Ende der Hinzufügungen.
Dies kann mit einem einzelnen Array erfolgen, das einmal erstellt wurde, oder mit einer numerisch geordneten Sammlung, z. B. einer Liste. Für ein Array muss die anfängliche Arraygröße genau der Größe entsprechen, die alle beabsichtigten Werte enthält. Wenn Sie nicht wissen, wie viele Werte im Voraus auftreten können, funktioniert auch die Verwendung einer numerisch geordneten Auflistung, z. B. einer ArrayList oder Liste, bei der die Größe nicht unveränderlich ist. Es funktioniert universell für ein Array jeder Größe bis zu Integer.MAX_VALUE, das etwas mehr als 2.000.000.000 beträgt. Listenobjekte haben dieselben Indexgrenzen. Ihr Computer verfügt möglicherweise nicht über genügend Arbeitsspeicher, bevor Sie zu einem Array dieser Größe gelangen. Es kann effizienter sein, ein in die Objekttypen eingegebenes Array zu laden und es nach dem Laden des Arrays in eine Sammlung zu konvertieren. Dies gilt insbesondere dann, wenn die Zielsammlung nicht numerisch indiziert ist.
Dieser Algorithmus erzeugt genau wie geschrieben eine sehr gleichmäßige Verteilung, bei der keine Duplikate vorhanden sind. Ein Aspekt, der SEHR WICHTIG ist, ist, dass das Einfügen des nächsten Elements bis zur aktuellen Größe + 1 möglich sein muss. Für das zweite Element könnte es daher möglich sein, es an Position 0 oder Position 1 zu speichern Für das 20. Element könnte es möglich sein, es an einem beliebigen Ort von 0 bis 19 zu speichern. Es ist genauso gut möglich, dass das erste Element an Ort 0 bleibt, wie es an einem anderen Ort landet. Es ist genauso gut möglich, dass der nächste neue Artikel irgendwohin geht, einschließlich des nächsten neuen Standorts.
Die Zufälligkeit der Sequenz ist so zufällig wie die Zufälligkeit des Zufallszahlengenerators.
Dieser Algorithmus kann auch verwendet werden, um Referenztypen an zufällige Stellen in einem Array zu laden. Da dies mit einem Array funktioniert, kann es auch mit Sammlungen funktionieren. Das bedeutet, dass Sie die Sammlung nicht erstellen und dann mischen oder in beliebiger Reihenfolge der eingefügten Objekte bestellen müssen. Die Sammlung muss nur die Möglichkeit haben, ein Element an einer beliebigen Stelle in der Sammlung einzufügen oder anzuhängen.
quelle
Es hängt wirklich alles davon ab, wofür Sie die zufällige Generierung benötigen, aber hier ist meine Meinung.
Erstellen Sie zunächst eine eigenständige Methode zum Generieren der Zufallszahl. Achten Sie darauf, Grenzwerte zu berücksichtigen.
Als Nächstes möchten Sie eine sehr einfache Entscheidungsstruktur erstellen, die Werte vergleicht. Dies kann auf zwei Arten erfolgen. Wenn Sie nur eine sehr begrenzte Anzahl von Zahlen überprüfen müssen, reicht eine einfache IF-Anweisung aus:
Das Obige vergleicht int1 mit int2 bis int5 und stellt sicher, dass die Zufälle keine Nullen enthalten.
Mit diesen beiden Methoden können wir Folgendes tun:
Gefolgt von:
Wenn Sie eine längere Liste überprüfen müssen, führt eine komplexere Methode zu besseren Ergebnissen sowohl bei der Klarheit des Codes als auch bei der Verarbeitung von Ressourcen.
Hoffe das hilft. Diese Seite hat mir so sehr geholfen, dass ich mich verpflichtet fühlte, zumindest zu versuchen, auch zu helfen.
quelle
Ich habe ein Snippet erstellt, das keine doppelte zufällige Ganzzahl generiert. Der Vorteil dieses Snippets besteht darin, dass Sie ihm die Liste eines Arrays zuweisen und auch das zufällige Element generieren können.
Keine doppelte Zufallsgeneratorklasse
quelle
Am einfachsten ist es, nano DateTime als Langformat zu verwenden. System.nanoTime ();
quelle