Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Eindeutige Zufallszahl erzeugen


holgerbremen - Mo 24.10.11 08:38
Titel: Eindeutige Zufallszahl erzeugen
Ich möchte eine 7 stellige Zufallszahl erzeugen. Jede Zahl muss eindeutig sein. Das Problem ist, dass ich mir die bereits erzeugten Zahlen nicht merken möchte. Gibts dafür irgendwelche schlauen Algorithmen?
Es könnte auch notfalls eine alphanumerische "Zahl" erzeugt werden, wenn dies die Sache vereinfacht.


jaenicke - Mo 24.10.11 09:14

Rein logisch:
Wie soll es eine Möglichkeit geben zufällige Zahlen zu erzeugen, wenn diese dennoch einem bestimmten Gesetz folgen sollen? Das ist ein Widerspruch in sich. (Ja, die Algorithmen, die die Zahlen berechnen, sind natürlich nicht echt zufällig, aber zumindest tun sie so.)

Warum möchtest du dir denn die bereits erzeugten Zahlen nicht merken? Hat das einen bestimmten Grund?


Teekeks - Mo 24.10.11 09:30

Nimm doch die immer Aktuelle Unix-Zeit!

Die musst du dir nicht merken und sie ist immer Einmalig (solange man die Uhr nicht zurück stellt).

Wirklich zufällig ist das zwar nicht, ist aber bei deinen Anforderungen auch nicht wirklich Möglich.


Blup - Mo 24.10.11 09:31

GUID, aber nicht bei 7 Stellen.

http://msdn.microsoft.com/de-de/library/bb979128.aspx


jasocul - Mo 24.10.11 09:41

Du willst 7-stellige Zufallszahlen erzeugen, die eindeutg sind.
Das ist Unsinn. Zufallszahlen werden willkürlich erzeugt (in den Grenzen der Algorithmen) und können dadurch auch mehrfach vorkommen. Das dürfte spätestens klar werden, wenn man mal einen Würfel zum Testen verwendet.

Wären deine Zahlen nur numerisch, würdest spätestens mit Ziehung 10.000.000 ein Problem bekommen. Welche Zahl soll denn kommen, wenn es eindeutig sein soll und nur 7 Stellen existieren?

Vielleicht solltest du erklären, was du unter einer Zufallszahl verstehst. Die mathematische Definition passt jedenfalls nicht auf deine Anforderungen.


holgerbremen - Mo 24.10.11 10:32

Ich brauche ca. 2 Mio. Zahlen (streichen wir den Begriff Zufallszahl) aus max. 9.999.999 Möglichkeiten (7 Stellen). Eine Zahl darf nicht wieder gezogen werden. Es müssen bis zu 100 Zahlen pro Minute erzeugt werden.
Ich möchte die Zahlen nicht speichern, da das den Aufwand erhöht und ich somit diverse Fehler abfangen muss. Deswegen wäre die Möglichkeit die Zahlen aus Datum/Uhrzeit zu erzeugen schon mal gut, aber geht das mit 7 Stellen.


Gausi - Mo 24.10.11 10:47

Bei den Werten kannst du es vergessen, mit Zufallszahlen zu arbeiten. Die Wahrscheinlichkeit, dass bei 2 Millionen zufäligen Zahlen aus dem Bereich von 1-10 Millionen eine Zahl doppelt vorkommt, ist sehr hoch.

Also: entweder die Zahlen merken und bei Bedarf neu würfeln, oder ohne Zufall arbeiten.

btw.: Der Aufwand, sich die Zahlen zu merken, dürfte nicht sehr hoch sein. Ich würde da einfach ein Array[1000000 .. 9999999] of Boolean nehmen, das sollte auch gut in den Speicher passen. :D


jasocul - Mo 24.10.11 10:48

Da das Datum in Delphi eigentlich ein Float ist, sollte das kein Problem sein. Vor dem Komma steht die Zahl, die das Datum repräsentiert, nach dem Komma die Zeit in Sekunden. Oder vielleicht sogar Millisekunden. Das weiß ich gerade nicht. Sollte aber auf jeden Fall ausreichend sein.

Wenn es nicht wirklich "zufällig" aussehen muss, kannst du dir auch einfach nur immer die aktuelle Zahl merken und diese einfach hochzählen. Aber ich nehme an, du brauchst die Illusion der Zufälligkeit in deinem Programm.


holgerbremen - Mo 24.10.11 10:58

Zitat:
btw.: Der Aufwand, sich die Zahlen zu merken, dürfte nicht sehr hoch sein. Ich würde da einfach ein Array[1000000 .. 9999999] of Boolean nehmen, das sollte auch gut in den Speicher passen.


Die Zahlen im Speicher merken ist kein Problem. Dazu müßten ich sie auch auf Platte speichern (Wegen Neustart oder Programmabsturz). Was ist, wenn die Datei nicht lesbar ist usw.

Zitat:
Wenn es nicht wirklich "zufällig" aussehen muss, kannst du dir auch einfach nur immer die aktuelle Zahl merken und diese einfach hochzählen. Aber ich nehme an, du brauchst die Illusion der Zufälligkeit in deinem Programm.
Genauso ist es, man soll nur denken, dass die Zahl zufällig ist. Sie muss aber nicht wirklich sein.


Teekeks - Mo 24.10.11 11:07

Wofür brauchst du dass denn?

Vielleicht gibt es eine andere Lösung dafür?
(hachja, die war das mit dem Wechseln des Leuchtobstes?)


Gausi - Mo 24.10.11 11:22

Wenn der User denken soll, dass die Zahl zufällig ist, kommst du mit den Datum/Zeit-Vorschlägen auch nicht weiter. Das ist nicht zufällig.

Du könntest den Wertebereich vergrößern (größere Zahlen erlauben), oder z.B. ein zufälliges Wort mit 7 Zeichen generieren - da hast du dann nicht nur 10 Ziffern, sondern 54 (a..z und A..Z). :nixweiss: Da ist die Kollisionswahrscheinlichkeit schon deutlich geringer.

Ich sehe aber noch nicht dein Problem, was das Speichern der Zahlen angeht:

Speichern der Werte in eine Datei:

Delphi-Quelltext
1:
2:
3:
for i := 1000000 to 9999999 do
   if MyBoolArray[i] then
      MyFileStream.Write(i, SizeOf(Integer));

Auslesen:

Delphi-Quelltext
1:
2:
3:
4:
5:
While MyFileStream.pos < MyFileStream.Size do
begin
  MyFilStream.Read(myNumber, SizeOf(Integer));
  myBoolArray[myNumber] := True;
end;


Hilft natürlich nichts, wenn der User die Datei löscht, die Festplatte zwecks Windows-Neuinstallation formatiert oder ein Meteor auf den Rechner fällt. ;-)


holgerbremen - Mo 24.10.11 12:06

Zitat:
Hilft natürlich nichts, wenn der User die Datei löscht, die Festplatte zwecks Windows-Neuinstallation formatiert oder ein Meteor auf den Rechner fällt.

Und genau von diesem Worst-Case muss ich aber ausgehen (Ausgenommen den Meteor, obwohl hier noch die Größe entscheindent wäre).

Zitat:
Wofür brauchst du dass denn?

Vielleicht gibt es eine andere Lösung dafür?
Für ändere Lösungen wäre ich dankbar. Gebraucht wir es für einen 7-stelligen Gewinncode. Vorgabe ist es, dass der Code 7-stellig numerisch ist und pseudo zufällig ist. Also ein normal-sterblicher auch mehreren Codes keine Reihe erkennen kann. Speichern alter Codes möchte ich aus den oben genannten Gründen ungern.


Gausi - Mo 24.10.11 12:33

Wie willst du denn hinterher die Gültigkeit eines Gewinncodes bestimmen, wenn du die ausgegebenen Codes nicht in irgendweiner Weise speicherst? :gruebel:


jfheins - Mo 24.10.11 12:43

Nimm dir einen PRNG mit einer kurzen periode und wirf einfach alle Zahlen weg, die nicht in dem Bereich liegen.

Guck z.B. bei http://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_usein die Zeile "Microsoft Visual Basic (6 and earlier)[5]"
Das Ding hat eine Periode von 2^24, das passt genau.

Also das teil mit einer bestimmten Zahl initialisieren, und es wird dir immer die gleichen Zahlen ausspucken. Zahlen werden nicht mehrfach gezogen, bevor die Periodenlänge erreicht ist.
Nachteil: Die Zahlen sind nicht perfekt zufällig.


jasocul - Mo 24.10.11 12:52

Oder erzeuge dir erst eine Liste mit eindeutigen Zahlen und mische diese dann.
Ich weiß ja nicht, was alles beim Verteilen der Zahlen passiert, aber du kannst den Zustand ja vielleicht festhalten. Wenn das Programm dann neu gestartet wird, verwirfst du alles, was bis dahin passiert ist.


Teekeks - Mo 24.10.11 13:03

user profile iconGausi hat folgendes geschrieben Zum zitierten Posting springen:
Array[1000000 .. 9999999] of Boolean

Bei einem gewinncode würde doch auch Array[0..9999999of Boolean gehen und die restlichen Stellen mit 0-en auffüllen.


BenBE - Mo 24.10.11 17:56

Fischer-Yates drauf und gut ist. Und dann einfach das Gesamte Array einmal ausgeben. Sind 4x1000000+8+4 Bytes im RAM und nach nem kleinen Delay hast Du bis zum Aufbrauchen des Puffers deine Ruhe.