Ist eine Möglichkeit.
Aber sehen wir uns daoch mal deine Variante an (die ist nämlich vom
prinzipiellen Gedankengang gar nicht so schlecht, auch wenn du das nicht
weißt) und wo das Problem dort liegt.
1
voidZufallsZahlAuswahlAktivierung(void)
2
{
3
// lokale Variablen
4
intAnzahlKegel;
5
6
AnzahlKegel=rand();
7
while(AnzahlKegel<=10)
8
{
9
AnzahlKegel=rand();
10
}
11
}
rand liefert eine Zahl zwischen 0 und RAND_MAX. D.h. AnzahlKegel kriegt
mal irgendeine Zahl.
Und dann gehts in die while Schleife. Die wird solange ausgeführt,
solange AnzahlKegel kleiner oder gleich 10 ist. Sagen wir mal rand()
hätte 6 geliefert. Dann gehts in die while Schleife hinein und es wird
eine neue Zufallszahl generiert. Die sei 8. Das Kriterium für die
Schleife ist immer noch erfüllt (8 ist kleiner oder gleich 10) und die
while Schleife dreht die nächste Runde -> neue Zufallszahl bestimmen.
rand() liefert 48. Und jetzt ist die Bedingung für eine erneute
Wiederholung der Schleife nicht mehr gegeben und die Funtion endet.
Das ist also genau das umgekehrte dessen was du willst. Die Bedingung
für die Schleifenwiederholung ist ganz einfach verkehrt herum.
-> while( AnzahlKegel > 10 )
Aber es geht noch weiter. Was passiert den mit der so ermittelten
Zufallszahl. Da AnzahlKegel eine lokale Variable dieser Funktion ist:
nichts.
Du gibst diese Anzahl nicht als Returnwert der Funktion zurück, noch
machst du sonst etwas damit.
1
intZufallsZahlAuswahlAktivierung(void)
2
{
3
intZahl;
4
5
Zahl=rand();
6
while(Zahl>10)
7
{
8
Zahl=rand();
9
}
10
11
returnZahl;
12
}
Jetzt kannst du beim Aufruf die so ermittelte Zahl aus der Funktion
empfangen und damit weiterarbeiten:
Beachte: Die Variable AnzahlKegel in deiner Funktion hat nichts mit der
gleichnamigen Variable in main() zu tun. Das sind 2 verschiedene
Variablen. Wenn du die eine veränderst, veränderst du nicht automatisch
die andere.
ziemlich lange dauern, weil 10 recht klein ist und MAX_INT sehr groß
sein kann...
Durchschnittlich kommen da bei einem 16-Bit-uC also 3000 Fehlversuche
auf einen Treffer.
> ziemlich lange dauern, weil 10 recht klein ist und MAX_INT sehr groß> sein kann...
Ah, ja.
Dazu wollt ich noch was schreiben, ehe ich gemerkt habe, dass seine
Funktion keinen Returnwert hat.
Was ist das Problem?
Das Problem ist in der Mathematik als das "Schubladenproblem" bekannt.
Es ist unmöglich m Gegenstände in n Schubladen zu legen, sodass in allen
Schubladen gleich viele Gegenstände liegen, wenn m kein ganzzahliges
Vielfaches von n ist.
Oder etwas weniger mathematisch angehaucht gesagt: Ich kann machen was
ich will, wenn ich 8 Paar Socken in 5 Schubladen gleichmässig verteilen
soll, werde ich Schiffbruch erleiden. Egal was ich tue. 8 ist kein
Vielfaches von 5. Erst mit 10 Paar Socken geht das wieder und dann sind
in jeder Schublade 2 Paar Socken.
In der Umkehrung heist das aber auch, wenn ich 32767 verschiedene
mögliche Zufallszahlen habe und ich möchte daraus Zufallszahlen von 0
bis 10 (also 11 Stück) generieren, dann kann ich tun was ich will, ich
werde es nie schaffen, dass alle Zahlen gleich häufig auftreten. 11 ist
nun mal kein ganzzahliger Teiler von 32767
Mal mit etwas kleineren Zahlen veranschaulicht: sei die größte von
rand() generierbare Zufallszahl gleich 5 und ich möchte Zahlen von 0 bis
3 haben, dann wird durch eine Modulo-Division folgende Abbildung erzielt
0 0 (weil 0 % 4 gleich 0)
1 1 (weil 1 % 4 gleich 1)
2 2
3 3
4 0 (weil 4 % 4 gleich 0)
5 1
generiert also rand() alle Zahlen von 0 bis 6 gleichmässig, dann
entstehen durch die Modulo Division weniger 3-en und 2-en als 0-en und
1-en. Und zwar ganz schön heftig.
Seien von 100 Zufallszahlen 20 Stück 0, 20 Stück 1, 20 Stück 2, etc
(also perfekt gleichverteilt), so münden diese 100 Stück Zufallszahlen
darin, dass daraus 40 Stück 0-en, 40 Stück 1-en, 20 Stück 2-en und 20
Stück 3-en entstehen. Von einer Gleichverteilung kann also keine Rede
mehr sein: 0 und 1 sind doppelt so häufig wie 2 und 3
Klar bei einem RAND_MAX von 32767 ist dieser Effekt nicht mehr sehr
ausgeprägt, weil der prozentuale Fehler kleiner wird, wenn RAND_MAX sehr
groß ist im Vergleich zur größten gewünschten Zufallszahl. Aber er ist
vorhanden. Ob man das in einem Bastlerprojekt gewillt ist zu tolerieren,
muss man im Einzelfall entscheiden.
Aber was ist ein Ausweg aus dem Dilemma?
Zurück zu den Socken: Da es keine Möglichkeit gibt, bleibt nur eines.
Einige Socken werden verworfen. Wenn 17 Socken auf 5 Laden aufzuteilen
sind, werden 2 Socken zur Seite gelegt und die restlichen 15 Socken auf
die 5 Laden gleichmässig verteilt. Eine andere Möglichkeit gibt es
nicht. Und dieses 'gibt es nicht' ist unabhängig davon, was ich mit den
von rand() generierten Zahlen anstelle. Manchmal sieht man im Web die
Empfehlung sich doch zunächst mal aus den rand()-Werten eine double Wert
im Bereich 0 bis 1 zu machen, etc. Aber diese Leute unterschätzen die
Mathematik. Wenn Mathe einmal sagt: Es ist unmöglich, dann ist es das
auch wirklich!
Warum gerade 2? Nun weil 15 die nächst kleinere Zahl an Socken ist, die
ein Vielfaches von 5 Laden darstellt.
Das kann man auch auf Zufallszahlen übertragen.
Man bestimmt sich zunächst die Grenze, bis zu der man gewillt ist,
Zahlen die rand() liefert zu akzeptieren. Mathematisch gesprochen:
Gesucht ist wieder mal das größte Vielfache von in diesem Fall 11,
welches gerade noch kleiner als RAND_MAX ist.
Alle Zahlen aus rand() die größer als diese Zahl ist, werden verworfen
und nicht zur Generierung der eigentlich gewünschten Zufallszahl
herangezogen. Mit dem Rest der Zahlen kann man dann die Umrechnung
machen.
@Karl heinz:
Naja, aber was wäre wenn man den kleinsten gemeinsammen Teiler benutzen
würde.
Also unser Rand() kann die Entscheidung auf binärer Ebene zwischen 0 und
1 doch ziemlich gleichverteilt treffen, richtig ?
Nun bauen wir uns ein Array[] mit 11 Elementen und schreiben dahinein
die Zahlen 1 bis 11.
Wenn wir eine Zahl erzeugen wollen gehen wir so vor:
1.) gehe das Array[] mehrmals von Unten nach Oben durch
2.) in jedem Schritt erzeuge mit Rand() eine Zahl im Set {0,1}
3.) ist es die 1 dann vertausche die beiden Zahlen am aktuellen Index
und (Index +1) % Length(Array[])
Nachdem man das mehrmals gemacht hat liefere die Zahl am Array Index 0
als Ergebnis zurück. So müsste man den Fehler auf das Minimalste
reduzieren können auf 1/Periode des RNGs wenn die Periode ungerade ist
und auf 0 wenn die Periode gerade ist.
Gruß Hagen
Danke euch allen für eure Mühe ;)
Nur hab ich jez noch ein Problem...
Wenn ich diese Idee benutze:(und alles andere so lase wie es anscheinden
sein muss :D)
int myRand()
{
int x;
while( (x = rand()) >= RAND_MAX - (RAND_MAX % UpperBound) )
;
return x % UpperBound;
}
Dann steht beim 'Compiler'-ähnlichem Programm --> undefined reference to
rand
Und das bei jeder Zeile, auf der dieses rand verwendet wird.
Auch wenn ich die andere Idee nehme :
int ZufallsZahlAuswahlAktivierung(void)
{
int Zahl;
Zahl = rand();
while ( Zahl > 10 )
{
Zahl = rand();
}
return Zahl;
}
dann noch
AnzahlUmgeworfeneKegel = ZufallsZahlAuswahlAktivierung();
einfüge.
Kommt genau die gleiche Meldung. Könnte das sein, dass das Programm
diesen Befehl gar nicht kennt, obwohl ich die richtige Library eingefügt
habe? Oder liegt das an was anderem?
Noch zur letzten Idee, die würde sicherlich auch Funktionieren (wenn
nicht das gleiche Auftrtitt wie oben...) nur leider bin ich nicht gerade
ne 'Heldin' was Arrays angeht... aber Danke trotzdem ;)
Und nochmal ein grosses Dankeschön an alle die sich die Mühe gemacht
haben und mir evt. auch beim nächsten helfen können :) :P
Mfg Rahel und schon bald ein gutes Wochenende :)
Rahel Mischler wrote:
> Dann steht beim 'Compiler'-ähnlichem Programm --> undefined reference to> rand> Und das bei jeder Zeile, auf der dieses rand verwendet wird.
'undefined reference' bedeutet immer, dass der Compiler auf ein Wort
(Funktionsname oder Variablenname) gestossen ist, das er nicht kennt. In
der Sprache C gibt es nur relativ wenige Schlüsselwörter, die der
Compiler von Haus aus kennt. int, for, if, while etc. gehören dazu.
Alles andere wird durch vordefinierte Funktionen abgedeckt, wie zb
sin(), cos(), sqrt() oder eben auch rand().
Um eine Funktion verwenden zu können, muss der Compiler vor der
Verwendung wissen, dass es diese Funktion überhaupt gibt. Dazu muss es
eine Funktionsdeklaration geben. Der Compiler möchte nämlich ganz gerne
überprüfen, ob es eine Funktion gibt und wenn ja, welche Datentypen die
übergebenen Argumente haben müssen bzw. was der Returnwert dieser
Funktion für einen Datentyp hat. Und da er die Funktionen von Haus aus
nicht kennt, muss man das vor der ersten Verwendung klarstellen.
Für die vorgefertigten, mitgelieferten Funktionen befinden sich diese
Funktionsdeklarationen allesamt in sog. Header Files, die mit #include
üblicherweise am Anfang deines Programmtextes eingebunden werden. Um
eine bestimmte Funktion zu verwenden, muss man also wissen in welchem
Header File (es gibt davon mehrere, nach Themenkreisen geordnet) sich
die Deklaration der Funktion befindet. Diese Information wiederrum
bekommt man aus seinem C-Lehrbuch, bzw. in dem man das Hilfesystem
seines C-Systems nach der Funktion befragt, dort steht das normalerweise
auch immer mit dabei.
In deinem Fall ist es so, dass die entsprechende Funktionsdeklaration in
stdlib.h enthalten ist (stdlib : STanDardLIBrary)
Also am Anfang einfach
#include <stdlib.h>
und dann sollte rand() bekannt sein.
Und demnächst an in die nächste Buchhandlung, und ein Standard-Werk über
C kaufen. Du wirst es brauchen.
Hagen Re wrote:
> Also unser Rand() kann die Entscheidung auf binärer Ebene zwischen 0 und> 1 doch ziemlich gleichverteilt treffen, richtig ?> Nun bauen wir uns ein Array[] mit 11 Elementen und schreiben dahinein> die Zahlen 1 bis 11.> Wenn wir eine Zahl erzeugen wollen gehen wir so vor:> 1.) gehe das Array[] mehrmals von Unten nach Oben durch> 2.) in jedem Schritt erzeuge mit Rand() eine Zahl im Set {0,1}> 3.) ist es die 1 dann vertausche die beiden Zahlen am aktuellen Index> und (Index +1) % Length(Array[])>> Nachdem man das mehrmals gemacht hat liefere die Zahl am Array Index 0> als Ergebnis zurück. So müsste man den Fehler auf das Minimalste> reduzieren können auf 1/Periode des RNGs wenn die Periode ungerade ist> und auf 0 wenn die Periode gerade ist.
Ich denke, das könnte funktionieren.
Allerdings bin ich sehr vorsichtig geworden, was Gleichverteilungen
angeht. Ich hab eines gelernt: Es ist unheimlich einfach, unbemerkt eine
Gleichverteilung zu zerstören.
Da so ein Verfahren aber verwendet wird um eine zufällige Permutation
von n Zahlen (n war in dem Beispiel 11) zu erzeugen, denke ich, es ist
ok. Müsste man mal ausprobieren und ein paar Zufallszahlentests drüber
laufen lassen.
Und ehe jemand nachfrägt: Nein, mann muss wirklich bei jedem Aufruf eine
neue Permutation erzeugen. Es würde nicht reichen, einmal eine
Permutation für jeweils 11 Aufrufe zu erzeugen. Hat man aus dieser
Permutation 10 Werte abgeholt, kann jeder mit einem IQ größer als 15 die
als nächstes zu liefernde Zahl, die 11.te, vorhersagen und das wär dann
nicht mehr zufällig)
Danke für die Idee mit dem Buch, aber hab schon eins ;) und das ich das
brauchen werde is auch klar, bin ja erst im zweiten Lehrjahr...
Das mit der Library is mir klar, aber was komisch is, dass diese Meldung
kommt, obwohl ich diese Bibliothek eingebunden habe.
Alls ich angefangen habe, dieses Programm umzusetzten, musste ich erst
einmal schauen wo das welche Befehle/Anwendungen drin stehen, und da
fand ich die Bibliotheken, welche auch oben 'eingebunden' wurde.
Kann das sein, das dieser Befehl einfach nicht erkannt wird? (Obwohl die
restlichen der gleichen Bibliothek erkannt werden...)
Hab leider auch kein Buch zum Board selber, da ichs in der Berufschule
kaufen musste... und ne brauchbare hilfe is im Programm selber auch
nicht aufzufinden.
Zunächst mal:
Du 'bindest' keine Bibliothek ein. Ein Header File ist ein Textfile wie
jedes andere auch. Das kann man im Editor aufmachen und reinschauen, was
da drinnen steht (wenn man die Datei erst mal gefunden hat)
Ist bei dir in der stdlib.h eine Funktion rand() angegeben?
Nach einem Kaffee ist mir auch der Gedanke durch den Kopf geschossen,
dass 'undefined reference' eigentlich eher eine Linker-Fehlermeldung
ist. Der Compiler würde da eher 'undeclared identifier' oder sowas in
der Richtung von sich geben.
Das heist: Dir fehlt beim Linken tatsächlich eine Bibliothek. Das wäre
allerdings in der Tat ungewöhnlich, denn die C-Standard Funktionen sind
allesamt in der C-Runtime Library gesammelt, und diese wird automatisch
immer mit eingelinkt.
Wie war denn das bei deinem ursprünglichen Program?
Dort hast du ja auch rand() benutzt. Hat das geklappt?
Ja, die Funktion ist in der Header-Datei vorhanden.
Und nei, das hat schon dort nicht geklappt, und dachte mir es könnte an
was anderem liegen, darum hab ich überhaupt erst mit dem Eintrag
angefange.
Hättest du mir sonst ne Idee, wie ich so nen 'Zufallsgenrator' machen
kann ohne das rand(). Ausser du wüsstes wie man diesen Fehler beheben
kann? ;)
Mfg
Rahel Mischler wrote:
> Ja, die Funktion ist in der Header-Datei vorhanden.
Hehe, nein. Die Funktion ist in der Header-Datei nicht vorhanden. Nur
deren Prototyp. Sourcecode findest du da nämlich nicht.
> Hättest du mir sonst ne Idee, wie ich so nen 'Zufallsgenrator' machen> kann ohne das rand(). Ausser du wüsstes wie man diesen Fehler beheben> kann? ;)
Schau mal hier:
Beitrag "Re: Zufallsgenerator - ungleiche Startwerte erzeugen"