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
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.
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ß
> 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...
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?
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?
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 :)
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.
>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.
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.
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.
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.
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:
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.
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?
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?
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.