mikrocontroller.net

Forum: PC-Programmierung zufallszahl mit rand() erzeugen


Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Emperor_L0ser (Gast)
Datum:

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

Autor: CHRiS (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
geht das?
for(i = 1; i <= 1; i++)

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: interessent (Gast)
Datum:

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

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);
  }
}

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?
for(i = 1; i <= 1; i++)

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

Autor: flo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mhh mal wieder zu langsam ... kommt davon wenn man zwischendurch alles 
andere macht.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@rolf magnus
war ein tippfehler habe srand genommen...

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: znil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
okey danke....das war der fehler...

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
int zufallszahl() {
  return rand() % 256;
}

int main(void) {
  ...
  srand ((unsigned) time(NULL));
  ...

  x = zufallszahl();
}

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
************

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

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

Nicht eher das größte Vielfache?

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Robért Wieden (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kann bitte mal jemand den kompletten Quell code hier rein 
stellen...danke

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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:
int myRand()
{
  int x;

  while( (x = rand()) >= RAND_MAX - (RAND_MAX % UpperBound) )
    ; 
  return x % UpperBound;
}

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
#define UPPPER_BOUND   10

int myRand( int UpperBound )
{
  int x;

  while( (x = rand()) >= RAND_MAX - (RAND_MAX % UPPPER_BOUND) )
    ; 
  return x % UPPPER_BOUND;
}

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.