• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

Java: mehrere Zufallszahlen fast immer gleich?

spunti

Hacker
Hallo,

ich brauche in Java mehrere Zufallszahlen hintereinander aber die sind nahezu immer gleich.
Da ich keine Initialisierung von Random() angebe, sollte der eigentlich die Systemzeit benutzen, aber das kann doch nicht sein, daß dann immer dasselbe rauskommt oder nimmt der nicht die Mikrosekundenzeit, sondern die Minuten der Systemzeit? Soll ich da echt per Hand die Mikrosekunden der Systemzeit zum Initialisieren nehmen - das wäre ja ein Witz?

Z.B. will ich so mehrere Zufallszahlen zwischen 0 und 5:

Random random = new Random();
int randomNumber = nextInt(nextInt(5));

Da kommen aber fast immer dieselben raus, wenn ich das mehrmals aufrufe. Und wenn ich nur ein random-Objekt erzeuge und die zweite Zeile mehrmals ausführe, ändert das auch nichts.

spunti
 
Das könnte daran liegen dass der Umfang nur 5 Zahlen ist, probiers mal mit meherern Zahlen, normalerweise müsste das mit der Systemzeit funktionieren.
 

TeXpert

Guru
ich denke auch dass das Hauptproblem die 5 Zahlen sind, ansonsten sind die nämlich schön verteilt.

schauen wir doch mal und zählen die Verteilung:

Code:
import java.util.Random;

class r {
  public static void main(String args[]){
     int collect[] = new int[5];

     java.util.Random rand = new Random();

     for (int i = 0; i < 10000; i++)
	collect[rand.nextInt(5)]++;

     for (int i=0;i<5;i++)
         System.out.println(i + ": " + collect[i]);
  }
}
ergibt:
Code:
p$ java r
0: 1926
1: 1977
2: 2045
3: 1963
4: 2089
sieht doch fast gleichverteilt aus :)
 
OP
S

spunti

Hacker
Hm,
also ich benötige zwingend eine ganze Zahl zwischen 0 und 5. Das ist nicht verhandelbar:)


@texpert
In deinem Beispiel hast du aber auch den engen Bereich von 0-5 gewählt. Nur die Anzahl der Zufallszahlerzeugungen hast du erhöht. Vielleicht kann ich mit diesem Vorschlag etwas hinfrickeln, allerdings wäre so ein Workaround "böse". Damit will ich sagen, daß man sowas nicht macht, scheint mir einfach keine saubere Lösung für das Problem.

Ich könnte auch den Bereich von 0-50 erweitern und dann wieder künstlich per Hand verkleinern, wäre aber genauso "böse". Stellt euch mal vor, jeder der hintereinander ein paar halbwegs ausgewogene Zufallszahlen zwischen 0 und 5 benötigt, muß solche Sachen anstellen. Das geht doch nicht.

PS:
Texperts Vorschlag muß nicht stimmen. Z.B. könnte das Proggie erst mal das Feld 0 2000 mal hochzählen (weil jedes mal dieselbe Zufallszahl 0 erzeugt wurde) und dann erst das nächste Feld (weil dann die für die Initialisierung benötigte Systemzeit vielleicht irgendwo umspringt - an der ganze Sekundenstelle z.B. oder noch schlimmer).

spunti
 

TeXpert

Guru
spunti schrieb:
@texpert
In deinem Beispiel hast du aber auch den engen Bereich von 0-5 gewählt. Nur die Anzahl der Zufallszahlerzeugungen hast du erhöht. Vielleicht kann ich mit diesem Vorschlag etwas hinfrickeln, allerdings wäre so ein Workaround "böse". Damit will ich sagen, daß man sowas nicht macht, scheint mir einfach keine saubere Lösung für das Problem.

richtig, aber um eine statistische Aussage treffen zu können, ob die Pseudozufallszahlen (es handelt sich schließlich immer um Pseudo-Randomness) gleichverteilt sind (zumindest annhähern) kannst Du nicht nur mit 3 oder 4 Stichproben arbeiten. Wenn das Verfahren korrekt ist, dann sollte die Gleichverteilung ein Fixpunkt der Reihenentwicklung werden.

Wenn Du nur ein paar Zahlen brauchst, kannst Du davon ausgehen, dass das dann auch funktioniert. Zusätzlich, Nehmen wir 5 Zahlen aus 0-4. Wenn Du Dir eine in Deinen Augen 'optimale' Zufallsverteilung vorstellst dann ist die unter allen möglichen permutationen um "unwahrscheinlichsten", insbesondere sind Häufungen von einzelnen Zahlen sehr wahrscheinlich. Nimm z.B. eine Münze und 3 Würfe:
Code:
K K K
K K Z
K Z K
K Z Z
Z K K
Z K Z 
Z Z K
Z Z Z
dann gibts nur 2 Alternierende Folgen, d.h. die "menschlisch-optimal-erkannte" Zufälligkeit ist relativ unwahrscheinlich ;)
 
A

Anonymous

Gast
wenn du meinst die Zahlen seinen nicht zufällig genug, dann versuch doch mal zB nach der Formel "x = random(1000) % 6" zu arbeiten, das sollte eine brauchbare zufällige Verteilung zwischen 0 und 5 geben.

robi
 

sparrow

Member
Hallo,
folgendes funktioniert sehr gut zur Erzeugung von Zufallszahlen:

Code:
Random r = new Random();
int num = 1 + Math.abs(r.nextInt()) % 49;
num ist dann eine Zufallszahl >=1 <=49.

Der Code stammt aus dem http://www.javabuch.de

Noch ein Tipp:
Manchmal hilft es
Random r = new Random();
vor dem erneuten ermitteln einer Zufallszahl neu zu instanzieren.
 

TeXpert

Guru
mal was ganz anderes: Ich würde dem Java-random-Algo vertrauen, wenn ein Bug drin wäre wäre der mit sehr hoher WKeit schon gefunden worden und der Algo basiert
Java Doku schrieb:
This is a linear congruential pseudorandom number generator, as defined by D. H. Lehmer and described by Donald E. Knuth in The Art of Computer Programming, Volume 2: Seminumerical Algorithms, section 3.2.1.
und den Algos von DEK vertraue ich mittlerweile blind ;)
 

Mumie

Hacker
Hallo,
ich habe hier einen Würfel! Allerdings in C.

====================================
/* Wuerfel.c */

#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
#include <time.h>

int i, Augenzahl; /* i wird nur als Zählvariable in der Schleife gebraucht */
double rechteck01;
int Zufall;


int main(void)
{
srand ((unsigned) time (NULL)); /* Initialisierung des Zufalls*/

for (i = 1; i <=10; i++)
{
Zufall = rand();
rechteck01 = (double) Zufall / (RAND_MAX); /* so das 0 <= rechteck01 < 1 */
rechteck01 = rechteck01 * 6; /* Jetzt ist 0 <= rechteck01 < 6 */
Augenzahl = floor(rechteck01); /* Augenzahl = {0,1,2,3,4,5} */
Augenzahl = Augenzahl +1; /* Augenzahl = {1,2,3,4,5,6} */
printf("\nAugenzahl = %d ", Augenzahl);
}

return 0;
}
====================================

Der muß mit der folgenden Zeile kompiliert werden:
gcc -Wall Wuerfel.c -lm -o Wuerfel

Das -lm ist wichtig!

Ich hoffe, der würfelt niemals die 7.

Falls die Zufallszahlen von 0 bis 5 gehen sollen, dann muß man
die Augenzahl eben wiede um 1 verringern.

Viele Grüsse
Mumie
 

TeXpert

Guru
Mumie schrieb:
Hallo,
ich habe hier einen Würfel! Allerdings in C.

Sorry, aber: Und? Was sagt das bitte über die Qualtiät des Pseudorandom-Algos von Java aus? Spunti machts ja richtig, er hat sich nur über die Verteilung gewundert (wobei die i.O. ist ;))...
 

Mumie

Hacker
Ja, natürlich. Der Algorithmus ist bestimmt gut. Aber warum gibt Spunti keine Initialisierung von Random an? Bei meinen ersten Versuchen den Würfel zu programmieren habe ich gemerkt, das dann immer die gleiche Reihenfolge von Zufallszahlen kommt.

Ich selbst bin gerade beim 8. Tag von diesem C-Kurs. Es geht um Zeiger und um Adressen. Diese Adressen sind bei jedem Programmstart neu. Irgendwie "zufällig". Vielleicht kann man auch diese Adressen als Initialisierung verwenden? - Nur so eine Idee von mir. Oder kann man die Adressen selber verwenden? Es handelt sich um Zahlen von 0 bis 9. Man könnte die einzelnen Ziffern auslesen. Alle die nicht passen kann man verwerfen. Und übrig bleibt eine zufällige Zahlenfolge.

Ich habe übrigens eine neue Version von meinem Würfel. Ich habe den Tip von robi verwendet:
=====================================

/* Würfel2 */

#include <stdio.h>
#include <stdlib.h>
#include <tgmath.h>
#include <time.h>



int i, Augenzahl; /* i wird nur als Zählvariable in der Schleife gebraucht */


int main(void)
{
srand ((unsigned) time (NULL)); /* Initialisierung des Zufalls*/

for (i = 1; i <=10; i++)
{
Augenzahl = (rand() % 6); /* Augenzahl = {0,1,2,3,4,5} */
Augenzahl = Augenzahl +1; /* Augenzahl = {1,2,3,4,5,6} */
printf("\nAugenzahl = %d ", Augenzahl);
}

return 0;
}
======================================

Das ist viel kürzer. Da bin ich auch ganz stolz drauf.

Viele Grüsse
Mumie
 

TeXpert

Guru
Mumie schrieb:
Ja, natürlich. Der Algorithmus ist bestimmt gut. Aber warum gibt Spunti keine Initialisierung von Random an? Bei meinen ersten Versuchen den Würfel zu programmieren habe ich gemerkt, das dann immer die gleiche Reihenfolge von Zufallszahlen kommt.

Dir ist klar, dass Du Dich gerade etwas blamierst?
JAVA Doku schrieb:
public Random()

Creates a new random number generator. Its seed is initialized to a value based on the current time:

public Random() { this(System.currentTimeMillis()); }

Two Random objects created within the same millisecond will have the same sequence of random numbers.
er legt ein neues Objekt an und das reicht.

Ich selbst bin gerade beim 8. Tag von diesem C-Kurs. Es geht um Zeiger und um Adressen. Diese Adressen sind bei jedem Programmstart neu. Irgendwie "zufällig". Vielleicht kann man auch diese Adressen als Initialisierung verwenden?
nein, sie sind nur scheinbar zufällig, denn es ist trivial ein Konstruk zu produzieren, in dem sie nicht mehr zufällig sind.
Code:
#include <iostream>

class foo {
  public:
   foo() : a(4), b(6) {};
  private:
   int a;
   int b;
};

int main() {

    foo *fooptr;

    for (int i=0; i<25; i++){
        fooptr = new foo();
        std::cout << fooptr << std::endl;
        delete fooptr;
    }
}
(c++ mit g++ -o foo foo.cpp kompileren)

Alle die nicht passen kann man verwerfen. Und übrig bleibt eine zufällige Zahlenfolge.
zufällige Folgen sind im rechner nicht möglcih, und über pseudozufallszahlen sind schon viele Artikel geschrieben worden, ich empfehle wirklich ienen Blick in die Bücher von DEK.

Ich habe übrigens eine neue Version von meinem Würfel. Ich habe den Tip von robi verwendet:
=====================================

Augenzahl = (rand() % 6); /* Augenzahl = {0,1,2,3,4,5} */
Augenzahl = Augenzahl +1; /* Augenzahl = {1,2,3,4,5,6} */

nimm doch bitte mal den Code-Block...

dann
Code:
Zahl = (rand() % 6) +1;
ist noch kürzer ;)[/code]
 
OP
S

spunti

Hacker
An sich sehe ich das wie Texpert, wenn es eine bessere Möglichkeit für Zufallszahlen gäbe, hätte man das auch so implementiert.
Gut, der nimmt zum Initialisieren in Java also tatsächlich Milisekunden. Und ich werd es erst mal so lassen, denn es wird bald einen größeren Bereich geben aus dem Zufallszahlen ermittelt werden.

Auf jeden Fall sollte ich nicht immer ein neues random-Objekt erzeugen (ob tatsächlich ein neues erzeugt oder initialisiert wird, weiß ich gar nicht), sondern nur die Generierung mit nextInt wiederholen. Das sieht optisch schon mal etwas besser aus bilde ich mir ein:)


@sparrow
Steht in dem Javabuch auch, warum das Beispiel da so angegeben wurde? (Also ob er die andere Art, ganze Zufallszahlen in einem Bereich zu erzeugen, nur nicht kennt oder da auch irgendwelche Schwächen ausgemacht hat? Ich hab hier Auflage 3 des Buches, da ist das Beispiel noch nicht drin.)
 

Mumie

Hacker
Hallo TeXpert,
ich habe foo kompiliert. Wenn es läuft, kommt immer das gleiche Ergebnis:
0x804a008
0x804a008
0x804a008
und so weiter. Und das bei jedem Neustart. War das so geplant?

Ich habe noch eine Idee. Eine ganze Reihe von Dingen auf dem Computer werden ja vom Anwender "zufällig" verursacht. So auch der Wert, den eine bestimmte Adresse hat. Man könnte vielleicht eine "zufällige" Adresse nehmen. Und den Wert auslesen.
Ich finde das Thema Zufallszahlen sehr interessant. Ich werde trotzdem im Moment keine weiteren Bücher lesen. Auch nicht von DEK. Zuerst kommt der C-Kurs drann.

Viele Grüsse
Mumie
 

TeXpert

Guru
Mumie schrieb:
Hallo TeXpert,
ich habe foo kompiliert. Wenn es läuft, kommt immer das gleiche Ergebnis:
0x804a008
0x804a008
0x804a008
und so weiter. Und das bei jedem Neustart. War das so geplant?

genau :) und wenn Du jetzt noch überlegst was ich Dir damit sagen will ... (hint: nein, Zeiger sind nicht als Zufallsgenerator geeignet...)

Du siehst, die Speicheradresse liegt immer auf der gleichen Stelle. Jetzt nehmen wir ein kleines Speicherleck in Kauf:
Code:
#include <iostream>

class foo {
  public:
   foo() : a(4), b(6) {};
  private:
   int a;
   int b;
};

int main() {

    foo *fooptr;
    foo *fooptr2;
    for (int i=0; i<25; i++){
        fooptr = new foo();
        std::cout << fooptr << std::endl;
        if (0==(i %2))
            delete fooptr;

    }
}
und löschen nur jeden 2. Zeiger Du siehst, dass die neuen Speicheradressen _sehr_ vorhersagbar sind.


Ich habe noch eine Idee. Eine ganze Reihe von Dingen auf dem Computer werden ja vom Anwender "zufällig" verursacht. So auch der Wert, den eine bestimmte Adresse hat. Man könnte vielleicht eine "zufällige" Adresse nehmen.
die Adressen werden fortlaufen vergeben... und wo nimmst Du die zufällige Adresse her?

Die einzige Möglichkeit ist den Menschen hinzuzunehmen, also z.B. einen ton über ein Mikro aufnehmen, oder wie es bei manchen Keygeneratoren gemacht wird, zufälliges Tippen (evtl. den Rythmus)... oder wilde Mausbewegungen...

das können aber allesnur Parameter für die Kongruenzfunktion sein, mit der dann der Generator arbeitet. Ad definitionem kann ein Computer nur Pseudozufallszahlen generieren, d.h. bei gleichen Parametern und gleichem Seed kommt die gleiche Folge raus (das ist sinnvoll beim Debuggen...)


Ich werde trotzdem im Moment keine weiteren Bücher lesen. Auch nicht von DEK. Zuerst kommt der C-Kurs drann.
:) sorry ich bin bei sowas erher der anderen Meinung, eine Programmier-Sprache lernt sich relativ schnell das größere Problem sind die zugrundeliegenden Algorithmen, wenn Du hier mit Pseudocode "programmieren" kannst ist der Transfer relativ einfach.
 

Snubnose

Hacker
spunti, was meist du eigentlich mit 'nahezu immer gleich' ?
Mach mal ein Beispiel...

und poste am besten gleich mal deinen Code zu deinem Proggi
 
OP
S

spunti

Hacker
Es geht jetzt und lag da ran:

"Auf jeden Fall sollte man NICHT immer ein neues random-Objekt erzeugen pro Zufallszahl."

(daneben hatte ich mich nat. auch noch selbst etwas ausgetrickst, so daß die letzte zahl seltener gefunden wurde)

spunti
 

TeXpert

Guru
spunti schrieb:
Es geht jetzt und lag da ran:

"Auf jeden Fall sollte man NICHT immer ein neues random-Objekt erzeugen pro Zufallszahl."

das ist doch klar ;) überleg mal wie ein Pseudorandomgenerator funktioniert:

:arrow: Init mit einem Wert X0
:arrow: Berechne Xn+1 durch Abbildungsvorschrift aus Xn

Wenn sich der Seed dann kaum Unterscheidet ist die erste Zufallszahl auch recht ähnlich
 

Braslett

Newbie
Man kann auch zwischen den 'nextInt(5)' zufällig lang rechnen mit z.B. 'nextInt(1000)' aus einem zweiten Random.

Braslett
 
Oben