Forum: PC-Programmierung Zufallszahlen mit rand() und gleichmäßig verteilt


von SlideR (Gast)


Lesenswert?

Hallo zusammen,


ich suche eine Funktion, die mir eine Zufallszahl (unsigned char) 
generiert und dabei die gleichmäßige Verteilung gewährleistet wird. Es 
sollen dabei die Grenzen vorgegeben werden können. (z.B. 0 bis 3 und 0 
bis 255).

Ich habe nun schon über mehrere Möglichkeiten gelesen. Nach Aussage 
sollte die nachfolgende Funktion die gleichmäßige Verteilung 
gewährleisten:

int rand_stat( int a, int e)
{
    double r = e - a + 1;
    return a + (int)(r * rand()/(RAND_MAX+1.0));
}

Kann die Funktion dafür eingesetzt werden?

Warum muss r als double deklariert werden? Kann ich diese auch für 
Zahlen im Format unsigned char umschreiben?


Könnte mir jemand die Funktion in ein paar Sätze erklären?

Ich danke Euch im Voraus.


Schönen Abend noch

von Karl H. (kbuchegg)


Lesenswert?

SlideR schrieb:

> Kann die Funktion dafür eingesetzt werden?

Nein.
Da hilft auch der double nichts.

Da kommt das sog. Schubladen Argument ins Spiel:
Es ist unmöglich n Paar Socken in m Schubladen zu legen, so dass in 
allen Schubladen gleich viele Socken liegen, solange m nicht ein 
ganzzahliges Vielfaches von n ist.

D.h. Wenn du 5 Schubladen hast, kannst du nur 5, 10, 15, 20, .... Paar 
Socken da drinnen gleichmässig verteilen. Bei allen anderen Zahlen wird 
es IMMER (egal was du machst) so sein, dass in einigen Schubladen mehr 
und in anderen Schubladen weniger Socken enthalten sind.

Die Umrechnung auf double bringt dabei genau gar nichts. Denn auch dann 
ist es ja so, dass sich die Anzahl der Zahlen die aus aus
   r * rand()/(RAND_MAX+1.0)
entstehen können, nicht erhöht.
Ob ich jetzt die Zahlen 0, 1, 2, 3, 4, 5 habe, oder 0.0, 0.1, 0.2, 0.3, 
0.4, 0.5 oder 0.00, 0.05, 0.10, 0.15, 0.20, 0.25 ändert nichts daran, 
dass ich nur 5 verschiedene Zahlen erzeugt habe. Sie haben einen anderen 
Wertebereich, ja. Aber es sind deswegen (von der Anzahl her) nicht mehr 
oder weniger.

Die einzige Chance, wenn man genau sein will, ist es, einige der Paar 
Socken nicht einzusortieren. Sind 5 Schubladen verfügbar und muss ich 
30001 paar Socken einsortieren, dann werden in 4 Schubladen 6000 Paar 
Socken liegen und in einer 6001. -> keine Gleichverteilung. Welche diese 
Lade mit dem 1 Paar mehr ist, kann ich durch eine Umnummerierung der 
Socken steuern, aber ich kann das grundsätzliche Problem der fehlenden 
Gleichverteilung damit nicht beheben.
Nur dadurch, dass ich 1 Paar zur Seite legen und aus den 30001 genau 
30000 Paar mache (einem Vielfachen von 5), gelingt es in allen Laden 
gleich viele Socken zu haben.

> Könnte mir jemand die Funktion in ein paar Sätze erklären?

Die angedachte Funktion kannst du dir selbst ganz leicht erklären.
Denk dir ein paar Zahlen zwischen 0 und 10 aus. zb

  5   3   8   7   2

Wie kannst du jetzt aus diesen Zahlen die dem Prinzip nach gleiche 
Zahlenreihe zwischen 40 und 50 erzeugen?
Ganz einfach: Indem du zu den Zahlen, die du dir ausgedacht hast einfach 
40 dazuzählst.

  45 43 48 47 42

Wie kannst du mit deinen ursprünglichen Zahlen von 1 bis 10 eine dem 
Prinzip nach gleiche Zahlenreihe von 40 bis 60 erzeugen?
Ganez einfach: Nimm alle Zahlen mal 2 (warum gerade 2?), dann hast du 
Zahlen von 0 bis 20.

  10  6  16  14  4

Und wenn du dann noch 40 dazuzählst, dann hast du Zahlen im Bereich 40 
bis 60.

  50  46  56  54  44

Und jetzt siehst du mal in deiner Funktion nach, was du da wieder 
findest.

von SlideR (Gast)


Lesenswert?

Vielen Dank für die super Erklärung, echt genial!

Bringt die Funktion denn einen Unterschied zum einfachen Modulo 
Operator? Beispiel rand()%3 ?

Gibt es eine andere Möglichkeit Zahlen zu generieren, ohne dass bei 
einem kleinen Zahlenbereich z.B. 0 bis 2 die 0 "viel" öfter vorkommt als 
die 1 und 2?


Gruß

von Robert L. (lrlr)


Lesenswert?

> die 0 "viel" öfter vorkommt als
>die 1 und 2?

wie kommst du auf die idee, dass das passiert ??

Karl Heinz Buchegger ist da nur etwas zu "kleinlich"

es geht ja schließlich um Zufallszahlen, da "müssen" in manchen 
Schubladen mal mehr mal weniger Socken sein...

von SlideR (Gast)


Lesenswert?

Wenn ich zum Bsp. rand() % 6 habe generiert er mir zahlen zwischen 0 und 
1, aber die 0 und 1 kommt öfter vor:


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


Nun suche ich nach einer Methode mit der ich dieses Problem umgehe. 
Gerade bei kleinen Wertebereichen wirkt sich dieses negativ auf die 
gleichmäßige Verteilung aus.

Was kann ich dagegen machen?

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

SlideR schrieb:
> Was kann ich dagegen machen?

Ein System wählen bei dem RAND_MAX nicht nur 7 ist...?
Lies doch einfach mal was geschrieben wurde, wie willst du bei deinem 
Beispiel den die übrigen zwei Werte "gleichmäßiger" verteilen?

von SlideR (Gast)


Lesenswert?

Ich stehe gerade aufm Schlauch, sorry. Aus diesem Grund bitte ich um 
Hilfe!

Wenn ich das nun richtig verstanden habe, kann ich die funktion:


int rand_stat( int a, int e)
{
    double r = e - a + 1;
    return a + (int)(r * rand()/(RAND_MAX+1.0));
}

verwenden und wie kann ich nun RAND_MAX beeinflussen?


Sorry, aber ich komme gerade nicht mehr hinterher :)

von Karl H. (kbuchegg)


Lesenswert?

Was sie dir sagen wollen

> Ein System wählen bei dem RAND_MAX nicht nur 7 ist...?
> Karl Heinz Buchegger ist da nur etwas zu "kleinlich"

Klär doch erst mal ab, ob das für deine konkrete Aufgabenstellung ein 
Problem ist. Dein konkretes rand hat ein RAND_MAX von 32767 oder höher. 
Ein damit naiv implementierter Würfel

    rand() % 6

hat die Verteilung

0: 5462
1: 5461
2: 5461
3: 5461
4: 5461
5: 5461

d.h. die 0 kommt ein kleines bischen zu häufig vor. Allerdings nicht 
sehr viel. Für eine Spielbank wäre das inakzeptabel. Da gehts aber auch 
um sehr viel Geld. Für ein privates Mensch-ärgere-dich-nicht spielt die 
kleine Abweichung allerdings praktisch gesehen keine Rolle.

Wenn dir das nicht reicht, dann musst du eben 'überzählige Socken 
verwerfen'.
Du rechnest dir aus, welches die größte Zahl ist, die rand() noch 
bringen darf und alles was größer ist, wird verworfen

maxFaktor = RAND_MAX / 6;    // 6 wegen dem Würfel
maxRand   = maxFaktor * 6;

....


   do {
     x = rand();
   } while( x > maxRand );

   return x % 6;




Die Sache mit den Socken hab ich deswegen aufs Tapet gebracht, weil man 
immer wieder Web-Seiten sieht, die eine derartige Rumrechnerei 
veranstalten, mit dem Hinweis, das dadurch die Gleichverteilung wieder 
hergestellt werden solle. Jetzt weißt du, das das Humbug ist und du 
kennst auch das Argument, mit dem du diesem Humbug entgegen treten 
kannst. Es gibt keine Möglichkeit, durch eine simple Umrechnerei eine 
exakte Gleichverteilung herzustellen, wenn der Generator ganze Zahlen 
liefert und der Wertebereich des Generators kein Vielfaches des 
Wertebereichs der Zielverteilung ist. Ob das ein Problem ist, muss jeder 
für sich und anhand seiner Aufgabenstellung entscheiden.
Denn was ja auch nicht vergessen werden darf: Bei rand() haben wir es 
mit Pseudozufallszahlen zu tun. D.h. alleine schon deshalb wird auch der 
nie (bei großen Anzahlen von generierten Zahlen) eine exakte 
Gleichverteilung liefern.

von sepp (Gast)


Lesenswert?

>ich suche eine Funktion, die mir eine Zufallszahl (unsigned char)
>generiert und dabei die gleichmäßige Verteilung gewährleistet wird.

Muss es rand() sein? Ansonten kannst du dir die Boost oder C++11 Random 
Library man anschauen.

von R. M. (rmax)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Denn was ja auch nicht vergessen werden darf: Bei rand() haben wir es
> mit Pseudozufallszahlen zu tun. D.h. alleine schon deshalb wird auch der
> nie (bei großen Anzahlen von generierten Zahlen) eine exakte
> Gleichverteilung liefern.

Ich würde sagen, umgekehrt wird ein Schuh draus: Ein guter, 
gleichverteilter Pseudozufallszahlengenerator sollte spätestens wenn er 
einmal komplett durchlaufen wurde, auch wirklich exakt gleiche 
Häufigkeiten für die Zahlen geliefert haben. Bei echten Zufallszahlen 
ist es Teil des Zufalls, daß die Häufigkeiten praktisch nie exakt 
übereinstimmen. Dafür sind es aber jedes Mal andere Zahlen, deren 
Häufigkeit etwas nach oben oder unten abweicht.

von SlideR (Gast)


Lesenswert?

Vielen Dank für Eure Antworten.


Karl Heinz Buchegger, danke nun habe ich es auch verstanden. :)


Nein, es muss nicht unbedingt die rand()-Funktion sein.

von Klaus W. (mfgkw)


Lesenswert?

Karl Heinz Buchegger schrieb:
> d.h. die 0 kommt ein kleines bischen zu häufig vor. Allerdings nicht
> sehr viel.

Ja, in aller Regel wird man mit modulo gut hinkommen.
Wenn man ganz kleinlich ist, kann man ja den von rand() gelieferten Wert 
erst ansehen. Ist er größer oder gleich als der größte glatt teilbare 
Werte unterhalb RAND_MAX (also bei 32767 für RAND_MAX und Wunschwerten 
0...6 wäre das bei >=32767), verwirft man den Wert einfach und holt sich 
mit rand() den nächsten, bis man einen hat der kleienr ist. Nimmt man 
den modulo 7 (in diesem Fall), hat man wirklich eine Gleichverteilung.

von Rafael (Gast)


Lesenswert?

Ich sitze vor dem selben Problem wie der Autor "SlideR".

Ich möchte einen Zufallsgenerator programmieren, dieser soll die untere 
Grenze und die obere Grenze übergeben bekommen und mir die Zufallszahl 
als Rückgabewert liefern.

Bisher habe ich folgendes erreicht:
1
unsigned char rand_stati(const unsigned char lower_range, const unsigned char upper_range)
2
{
3
4
  unsigned char range,help,help1;
5
  
6
  range  = upper_range-lower_range;
7
  help = RAND_MAX - (RAND_MAX % range);
8
  
9
  do
10
  {
11
    help1 = rand();
12
  }while (help1 < help);
13
  
14
  return (help1 % range) + lower_range;
15
}

zusätzlich generiere ich mir vor jedem Programmaufruf einen random 
Startwert mit srand(). srand() bekommt eine integer Wert übergeben.


Kann das alles so funktionieren? Ich bin mit einem "Programmierkünsten" 
leider am Ende angekommen und bitte Euch um Hilfe.

von Karl H. (kbuchegg)


Lesenswert?

Rafael schrieb:

Nachdenken.

>   do
>   {
>     help1 = rand();
>   }while (help1 < help);

wieso "kleiner"?

> zusätzlich generiere ich mir vor jedem Programmaufruf einen random
> Startwert mit srand(). srand() bekommt eine integer Wert übergeben.

Ich hoffe du meinst damit:
Du rufst srand() EIN EINZIGES MAL beim Programmstart auf.
srand() vor jedem Aufruf von rand() aufzurufen ist absolut 
kontraproduktiv und definitiv nicht das was du machen willst. Damit 
rand() eine gleichverteilte Zufallsfolge generieren kann ist es 
unabdingbar, dass es in Ruhe arbeiten kann, OHNE dass du dauernd mit 
srand() dazwischenfunkst.

> Kann das alles so funktionieren? Ich bin mit einem "Programmierkünsten"
> leider am Ende angekommen und bitte Euch um Hilfe.

Geh deinen Programmtext einfach mit ein paar Zahlen durch und simuliere 
(also: du spielst Computer) dein Programm. Passiert das, was du 
möchtest?

von Rafael (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Nachdenken.
>
>>   do
>>   {
>>     help1 = rand();
>>   }while (help1 < help);
>
> wieso "kleiner"?

Ich werde das Programm nochmal durchgehen, vielleicht ist mir da doch 
ein Fehler unterlaufen. Danke für den Tipp.

Karl Heinz Buchegger schrieb:
> Du rufst srand() EIN EINZIGES MAL beim Programmstart auf.
> srand() vor jedem Aufruf von rand() aufzurufen ist absolut
> kontraproduktiv und definitiv nicht das was du machen willst. Damit
> rand() eine gleichverteilte Zufallsfolge generieren kann ist es
> unabdingbar, dass es in Ruhe arbeiten kann, OHNE dass du dauernd mit
> srand() dazwischenfunkst.


Ich rufe srand() vor jedem Programmdurchlauf auf, jedoch übergebe ich 
srand() einen über ein Timer generierten Wert, so habe ich ca. 65 000 
unterschiedliche Startwerte. Reicht das so etwa nicht? Sollte ich 
srand() wirklich nur einmal im Programm aufrufen?

von Klaus W. (mfgkw)


Lesenswert?

warum rufst du es überhaupt auf?

von Vlad T. (vlad_tepesch)


Lesenswert?

Rafael schrieb:
> Sollte ich
> srand() wirklich nur einmal im Programm aufrufen?

ja!

Am besten direkt am Anfang in der Initialisierung.
am besten ein einmaliges

srand(time(0));

wenn du während der Entwicklung reproduzierbarkeit wünschst (was 
meistens eine gute Idee ist) kannst du auch eine fixe kosntante angeben, 
da bekommst du immer die selbe "Zufalls"zahlen-Folge.

von Klaus W. (mfgkw)


Lesenswert?

Wobei man dann auch gar kein srand() braucht ...

Daher die Frage, ob es hier überhaupt Sinn macht.

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.