Forum: PC-Programmierung zufallszahl mit rand() erzeugen


von znil (Gast)


Lesenswert?

hallo leute,
wie erzeuge ich eine zufallszahl mit random, die sich beim aufruf der
random funktion auch ändert? folgender quellcode liefert immer die
gleiche zahl:
void zufallszahl ()
{
int i;
rand ((unsigned) time(NULL));
  for(i = 1; i <= 1; i++)
  {
    zahl = 0 + ( rand() % 255);
    printf("erzeugte Zufallszahl %i \n\r" , zahl);
    return (zahl);
  }
}
ich benötige bei jedem aufruf von zufallszahl() allerdings eine andere
zahl zwischen 0 und 255.kann mir jemand weiterhelfen?!danke schonmal

von Emperor_L0ser (Gast)


Lesenswert?

moin,
ich bin mir nicht mehr ganz sicher, aber müsste beim initialisieren 
nicht
srand() verwendet werden?

von CHRiS (Gast)


Lesenswert?

geht das?
1
for(i = 1; i <= 1; i++)

von znil (Gast)


Lesenswert?

Danke erstmal für die schnellen Antworten

@Emperor_L0ser (Gast)
ja hast recht, hab ich bei mir im Quellcode auch gemacht nur irgendwie 
beim hierher kopieren gings verloren...;)

@CHRiS (Gast)
ich will halt nur einen Wert und keine Zahlenfolge....muss eigentlich 
von 1 bis 2 heißen. Das war allerdings nicht der Fehler.

Das Problem ist, dass bei jedem Aufruf der Funktion der gleiche 
zufallswert erzeugt wird und ich benötige unterschiedliche Werte...weiß 
nicht wo der fehler liegen soll.
Hat noch jemand ne idee?

von interessent (Gast)


Lesenswert?

Hast du srand() in der Funktion aufgerufen? Dann wäre klar, dass da 
immer das selbe rauskommt.

von znil (Gast)


Lesenswert?

habe es so implementiert


void zufallszahl ()
{
int i;
srand ((unsigned) time(NULL));
  for(i = 1; i <= 2; i++)
  {
    zahl = 0 + ( rand() % 255);
    printf("erzeugte Zufallszahl %i \n\r" , zahl);
    return (zahl);
  }
}

von Rolf Magnus (Gast)


Lesenswert?

> rand ((unsigned) time(NULL));

Eigentlich sollte das einen Fehler vom Compiler bringen, es sei denn, du 
hast den zur Funktion gehörenden Header nicht eingebunden. Dann sollte 
aber wenigstens eine Warnung kommen. Du meintest vermutlich srand.

geht das?
1
for(i = 1; i <= 1; i++)

Gehen tut das schon. Die Schleife wird halt genau einmal ausgeführt.

von flo (Gast)


Lesenswert?

mhh mal wieder zu langsam ... kommt davon wenn man zwischendurch alles 
andere macht.

von znil (Gast)


Lesenswert?

@rolf magnus
war ein tippfehler habe srand genommen...

von Rolf Magnus (Gast)


Lesenswert?

srand() soll natürlich nur einmal ausgeführt werden und nicht bei jedem 
Aufruf der Funktion erneut. Damit gibst du den Startwert vor, mit dem 
der Generator initialisiert wird. Wenn du vor jedem Aufruf den Startwert 
neu setzt, klappt das natürlich nur mäßig gut.

>Das Problem ist, dass bei jedem Aufruf der Funktion der gleiche
> zufallswert erzeugt wird und ich benötige unterschiedliche Werte

Vermutlich nicht bei jedem Aufruf, sondern immer genau eine Sekunde lang 
bzw bis time() einen neuen Wert liefert.

von znil (Gast)


Lesenswert?

okey danke....das war der fehler...

von yalu (Gast)


Lesenswert?

Um Zahlen von 0 bis 255 (0 und 255 eingeschlossen, was wahrscheinlich
deine Absicht ist) zu erzeugen, musst du %256, nicht %255 rechnen.

Der Typ von zufallszahl() sollte int sein.

> for(i = 1; i <= 2; i++)
>   ...
>   return (zahl);
>   ...

Warum zwei Schleifendurchläufe, wenn aus dem ersten sowieso per return
herausgesprungen wird? Wieso lässt du die Schleife nicht einfach ganz
weg?

Vereinfachte Variante:
1
int zufallszahl() {
2
  return rand() % 256;
3
}
4
5
int main(void) {
6
  ...
7
  srand ((unsigned) time(NULL));
8
  ...
9
10
  x = zufallszahl();
11
}

von Rolf Magnus (Gast)


Lesenswert?

> Um Zahlen von 0 bis 255 (0 und 255 eingeschlossen, was wahrscheinlich
> deine Absicht ist) zu erzeugen, musst du %256, nicht %255 rechnen.

Meistens wird eher noch empfohlen, % gar nicht zu verwenden, sondern den 
Wert passend runterzudividieren bzw -shiften, da manche 
Zufallszahlengeneratoren bei den unteren Bits nicht so gut mit der 
Zufallsverteilung sind wie bei den oberen bits. In der Manpage zu rand 
unter Linux heißt es:

************
The versions of rand() and srand() in the Linux C Library use the same
random number generator as random() and srandom(), so the lower-order 
bits should be as random as the higher-order bits.  However, on older
rand() implementations, and on current implementations on different 
systems, the lower-order bits are much less random than the higher-order 
bits.
************

von Karl H. (kbuchegg)


Lesenswert?

Es gibt noch ein 2-tes Problem mit der Modulo Version:
Die Normalverteilung von rand() kann empfindlich gestört werden.

Beispiel:
Angenommen der Zufallszahlengenerator liefert Zahlen im Bereich
0 bis 7. Um in den Bereich 0 bis 5 zu kommen wird gemacht:

     rand() % 6

Um zu sehen, was hier passiert untersuchen wir mal, was bei jeder
einzelnen rand() Zahl durch den % 6 entsteht

  Zahl von rand()       % 6
        0                0
        1                1
        2                2
        3                3
        4                4
        5                5
        6                0
        7                1

Wenn also alle Zahlen die rand() liefert tatsächlich gleich
wahrscheinlich sind, dann macht die % 6 Rechnung daraus eine
Verteilung in der 0 und 1 doppelt so häufig auftreten wie 2,
3, 4, 5. Sowas wird jeden Kasinobetreiber in den Ruin treiben :-)

In C gibt es eine Konstante, aus der man die größte Zahl ablesen
kann, die von rand() geliefert werden kann: RAND_MAX
Nur dann wenn bei der Berechnung % n das n auch RAND_MAX teilt,
bleibt die Normalverteilung erhalten. In allen anderen Fällen
wird man sich mit dieser Methode einen Fehler in der Verteilung
einhandeln.

von yalu (Gast)


Lesenswert?

> Die Normalverteilung von rand() kann empfindlich gestört werden.

Das ist schon richtig, besonders auf 8- und 16-Bit-Systemen, wo der
Wertebereich der rand-Funktion nur bis 2**15-1=32767 reicht (bspw. auf
dem AVR). Du meinst aber sicher die Gleichverteilung, nicht die
Normalverteilung ;-).

> Nur dann wenn bei der Berechnung % n das n auch RAND_MAX teilt,

Nicht ganz korrekt: Das Gleichverteilung bleibt dann erhalten, wenn n
RAND_MAX+1 teilt. Die Zufallszahlen liegen im Bereich 0 bis RAND_MAX,
0 und RAND_MAX eingeschlossen, das sind also RAND_MAX+1
unterschiedliche Werte.

Um Zufallszahlen im Bereich 0 bis 255 zu generieren (was der OP
vermutlich beabsichtigt) ist also
1
rand()%256
korrekt, wenn

- RAND_MAX=256*n-1 und

- die rand-Funktion das von Rolf Magnus beschriebene Problem nicht
  aufweist.

Diese Bedingungen sind zumindest für die glibc und die avr-libc erfüllt.


Andere Frage in diesem Zusammenhang:

Kennt jemand eigentlich eine Methode mit nach oben abschätzbarer
Laufzeit, gleichverteilte Zufallszahlen in "krummen" Intervallen (also
bspw. 0 bis 5) unter Zuhilfenahme der rand-Funktion zu generieren?

Es soll also keine grundlegend neue rand-Funktion entwickelt werden.
Mehrfachaufrufe von rand() sind aber erlaubt, wenn die Anzahl der
erforderlichen Aufrufe eine obere Grenze hat.

von Karl H. (kbuchegg)


Lesenswert?

yalu wrote:
> Kennt jemand eigentlich eine Methode mit nach oben abschätzbarer
> Laufzeit, gleichverteilte Zufallszahlen in "krummen" Intervallen (also
> bspw. 0 bis 5) unter Zuhilfenahme der rand-Funktion zu generieren?
>
> Es soll also keine grundlegend neue rand-Funktion entwickelt werden.
> Mehrfachaufrufe von rand() sind aber erlaubt, wenn die Anzahl der
> erforderlichen Aufrufe eine obere Grenze hat.

Die Methode die ich aus comp.lang.c++ kenne, funktioniert
so:

Du bestimmst dir das kleinste Vielfache deines n, welches
gerade noch unter MAX_RAND liegt. Dann lässt du mit rand()
eine Zufallszahl generieren und prüfst, ob sie über dieser
Grenze liegt. Wenn ja -> rand() erneut aufrufen und prüfen.
Wenn nein, kannst du ganz normal mit dem % weitermachen wie
gehabt.

Im Grunde verwirfst du einfach alle Zufallszahlen über einer
Grenze, wobei die Grenze so gewählt wurde, dass sie ein Vielfaches
deines interessierenden Bereiches ist.


(Irgendwie bin ich heute nicht in Form. Obige Beschreibung liest
sich grauenhaft. Ich hoffe man kann die Idee dahinter rauslesen)

von yalu (Gast)


Lesenswert?

> (Irgendwie bin ich heute nicht in Form. Obige Beschreibung liest
> sich grauenhaft. Ich hoffe man kann die Idee dahinter rauslesen)

Nee, kein Problem, ich habe das sofort verstanden.

So ähnlich mache ich das normalerweise auch, funktioniert ja auch
bestens. Der Schönheitsfehler liegt nur darin, dass (wenn auch mit
geringer Wahrscheinlichkeit) die rand-Funktion u.U. sehr oft
aufgerufen werden muss, bis man einen Wert erhält, der unterhalb der
Grenze liegt.

Deswegen habe ich oben die Forderung nach abschätzbarer Laufzeit bzw.
nach einer oberen Grenze der Anzahl der rand-Aufrufe gegeben. Nur dann
ist die Methode bspw. in Anwendungen mit harten Echtzeitanforderungen
einsetzbar.

von Rolf Magnus (Gast)


Lesenswert?

> Du bestimmst dir das kleinste Vielfache deines n, welches
> gerade noch unter MAX_RAND liegt.

Nicht eher das größte Vielfache?

von yalu (Gast)


Lesenswert?

Das erscheint irgendwie sinnvoller.

Aber du hast doch gelesen, dass Karl heinz heute nicht so in Form ist.
Und der Leser soll ja auch noch etwas zum mitdenken haben ;-)

von Robért Wieden (Gast)


Lesenswert?

kann bitte mal jemand den kompletten Quell code hier rein 
stellen...danke

von Karl H. (kbuchegg)


Lesenswert?

kompletten Quellcode wofür?

Wenn du einen Generator für saubere Gleichverteilung in
jedem beliebigen Bereich (natürlich kleiner RAND_MAX + 1)
meinst, dann zb so:
1
int myRand()
2
{
3
  int x;
4
5
  while( (x = rand()) >= RAND_MAX - (RAND_MAX % UpperBound) )
6
    ; 
7
  return x % UpperBound;
8
}

Wie und warum das funktioniert, sollte aus den Beschreibungen
von weiter oben eigentlich klar sein.

Wenn deine obere Grenze konstant ist, dann wäre allerdings
diese Version besser, da dann der Compiler einen Teil
der Berechnungen zur Compilezeit erledigen kann
1
#define UPPPER_BOUND   10
2
3
int myRand( int UpperBound )
4
{
5
  int x;
6
7
  while( (x = rand()) >= RAND_MAX - (RAND_MAX % UPPPER_BOUND) )
8
    ; 
9
  return x % UPPPER_BOUND;
10
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.