Benötige Hilfe! Zufallszahlen eingrenzen (Lotto)
-
Müsste nicht das hier auch gehen (Nur auf die schnelle hingeklatscht):
bool weiter = false; int summe; int i = 0; // Durchgänge while(!weiter && i < 6) { // Berechnungen rein usw. if(summe >= 142 && summe <= 156) { weiter = true; } }
-
schrieb:Müsste nicht das hier auch gehen (Nur auf die schnelle hingeklatscht):
Ja, aber wieso sollte man so etwas tun?

-
schrieb:Müsste nicht das hier auch gehen (Nur auf die schnelle hingeklatscht):
bool weiter = false; int summe; int i = 0; // Durchgänge while(!weiter && i < 6) { // Berechnungen rein usw. if(summe >= 142 && summe <= 156) { weiter = true; } }.........!!
-
keyzer777 schrieb:
Da ich leider schon länger nicht mehr mit C++ gearbeitet hat komme ich zur Zeit da nicht weiter und hoffe das mir hier vielleicht jemand etwas unter die Arme greifen kann.
Hallo keyzer,
unabhängig davon, ob Du mit C++ oder einer anderen Sprache arbeitest, sollte man sich vorher überlegen, wie man sein Problem in kleine handliche Teile zerlegen kann. In Deinem Fall gibt es eine Menge verschiedener Möglichkeiten - eine naheliegende, die hier auch schon angewendet wurde, ist:
1.) 6 verschiedene Zahlen aus 1 bis 49 bestimmen
2.) prüfen, ob diese die gewünschten Eigenschaften haben
3.) wenn nein, dann bei 1.) weitermachen
4.) Zahlen ausgebenAllein für den Teil 1.) ist hier im Forum schon einiges gesagt worden, suche einfach mal nach dem Stichwort 'Lotto'. Um 6 paarweise verschieden(!) Zahlen aus 1 bis 49 zu bestimmen gibt es im wesentlichen zwei Möglichkeiten:
1a) bestimme eine Zahl zwischen 1 bis 49
1b) falls sie noch nicht im Ergebnis vorkommt, füge sie hinzu
1c) falls noch keine 6 Zahlen beisammen sind, mache bei 1a) weiteroder
1a) generiere eine Menge von Zahlen von 1 bis 49
1b) mache 6 mal:
1c) wähle zufällig eine Zahl aus der Menge aus und lösche sie dort und füge sie dem Ergebnis hinzuDie erste Variante ist vielleicht einfacher zu codieren, aber hat u.a. den Nachteil, dass sie länger dauert, wenn eine Zahl ausgewählt wird, aber nur noch wenige zur Auswahl stehen. Bei 6 aus 49 ist das noch kein Problem, aber da Du weitere Bedingungen eingeführt hast, die die Auswahl irgendwann einschränken, würde ich hier die zweite Variante wählen.
Dazu baue ich zunächst eine Klasse, die den Pott von 49 Kugeln (jede Kugel mit einer Zahl drauf) modelliert. Dort kann man zufällig Zahlen entnehmen.
#include <cstdlib> // rand, srand #include <iostream> #include <ctime> // time() #include <vector> class _49 { public: _49() : m_kugeln() { // hier wird 'm_kugeln' mit den Werten von 1 bis 49 gefüllt for( int kugel = 1; kugel <= 49; ++kugel ) m_kugeln.push_back( kugel ); } // -- liefert zufällig eine der noch enthaltenen Kugeln und entfernt sie aus der Menge int zieheEineKugel() { init(); using namespace std; const vector< int >::iterator zufaelligerIndex = m_kugeln.begin() + int( (rand() / (RAND_MAX+1.0)) * m_kugeln.size() ); const int gezogeneKugeln = *zufaelligerIndex; m_kugeln.erase( zufaelligerIndex ); return gezogeneKugeln; } private: static void init() { if( !m_initialized ) { using namespace std; srand( static_cast< unsigned int >( time(0) ) ); m_initialized = true; } } static bool m_initialized; std::vector< int > m_kugeln; }; bool _49::m_initialized = false; int main() { using namespace std; _49 kugeln; for( int i = 0; i < 6; ++i ) cout << kugeln.zieheEineKugel() << endl; return 0; }im main() ist ein einfaches Beispiel, wie diese Klasse zu benutzen ist. Also einfach ein Objekt mit diesem Typ anlegen - hier 'kugeln' und dann immer eine ziehen. Es ist garantiert, dass Du nie zwei Kugeln mit dem gleichem Wert ziehst.
Jetzt gehe ich einen kleinen Schritt weiter und betrachte die Ziehung der 6 Kugeln als Einheit. Ziel ist es, eine Ziehung vom einem Algorithmus generieren zu lassen
#include <algorithm> // copy #include <cstdlib> // rand, srand #include <ctime> // time() #include <iostream> #include <iterator> // ostream_iterator #include <set> #include <vector> class _49 ... // wie oben // -- liefert 6 'Kugeln' aus 1 bis 49 std::set< int > ziehung_6_aus_49() { _49 kugeln; std::set< int > ergebnis; for( int i=0; i<6; ++i ) ergebnis.insert( kugeln.zieheEineKugel() ); return ergebnis; } int main() { using namespace std; set< int > gezogenenKugeln = ziehung_6_aus_49(); // -- Ergebnis der Ziehung ausgeben copy( gezogenenKugeln.begin(), gezogenenKugeln.end(), ostream_iterator< int >( cout, " " ) ); cout << endl; return 0; }damit wäre der oben erwähnte Teil 1 erledigt; und Teil 4 bei der Gelegenheit auch - sonst sieht man ja nicht, ob Teil 1 erfolgreich war

Jetzt zu "2.) prüfen, ob diese die gewünschten Eigenschaften haben".
Hier kann man massiv Algorithmen aus dem C++-Standard einsetzen. Zusammen mit dem Funktoren aus <functional>. Ich unterstelle mal, dass Du damit nicht vertraut bist, aber ich versuch' es trotzdem mal - wir sind ja hier schließlich im C++-Forum und das ist alles Standard!#include <algorithm> // copy #include <cassert> #include <cmath> // abs #include <cstdlib> // rand, srand #include <ctime> // time() #include <functional> // bind*, less, usw. #include <iostream> #include <iterator> // ostream_iterator #include <numeric> // accumulate #include <set> #include <vector> class _49 ... // wie oben // -- liefert 6 'Kugeln' aus 1 bis 49 std::set< int > ziehung_6_aus_49() .. // wie oben struct istPaerchen : std::binary_function< int, int, bool > { // liefert true, falls a und b ein Pärchen sind result_type operator()( first_argument_type a, first_argument_type b ) const { return std::abs( a - b ) == 1; } }; // -- liefert 'true', falls die Menge der gezogenen Kugeln im Intervall [first,last) // die Anforderungen erfüllt template< typename T > bool passt( T first, T last ) { using namespace std; assert( distance( first, last ) == 6 ); const int summe = accumulate( first, last, 0 ); return summe >= 142 && summe <= 156 // 142 <= summe <= 256 && count_if( first, last, bind2nd( less< int >(), 25 ) ) == 2 // 2 mal x < 25 && count_if( first, last, bind2nd( greater< int >(), 25 ) ) == 4 // 4 mal x > 25 && count_if( first, last, bind2nd( modulus< int >(), 2 ) ) == 4 // 4 mal x ist ungerade (=> 2 mal gerade) && adjacent_find( first, last, istPaerchen() ) != last // mindestens ein Pärchen existiert ; } int main() { using namespace std; set< int > gezogenenKugeln = ziehung_6_aus_49(); // -- Ergebnis der Ziehung ausgeben copy( gezogenenKugeln.begin(), gezogenenKugeln.end(), ostream_iterator< int >( cout, " " ) ); cout << endl; cout << "Die Ziehung erfuellt " << (passt( gezogenenKugeln.begin(), gezogenenKugeln.end() )? "" : "NICHT") << " die Anforderungen" << endl; return 0; }Neu ist der Algorithmus 'passt()', der aus einer Menge von (6) 'Kugeln' bestimmt, ob die von Dir gesetzten Anforderungen erfüllt sind.
Wenn Du dieses Programm jetzt ein paar Mal ausführst, so wirst Du merken, dass Deine Anforderungen praktisch nie erfüllt sind. Nach meiner Abschätzung wirst Du das Programm im Mittel 280 Mal aufrufen, bevor es einmal meldet 'Anforderungen erfüllt'. Das geht natürlich einfacher in einer Schleife, und damit sind wir bei
"3.) wenn nein, dann bei 1.) weitermachen" .. und dazu muss man nur noch das main() erweitern:
int main() { using namespace std; set< int > gezogenenKugeln; do { gezogenenKugeln = ziehung_6_aus_49(); } while( !passt( gezogenenKugeln.begin(), gezogenenKugeln.end() ) ); // wiederholen, solange es nicht passt // -- Ergebnis der Ziehung ausgeben copy( gezogenenKugeln.begin(), gezogenenKugeln.end(), ostream_iterator< int >( cout, " " ) ); cout << endl; return 0; }.. das soll für's erste reichen.
Gruß
Werner
-
int( (rand() / (RAND_MAX+1.0)) * m_kugeln.size() );Den Trick werde ich nie verstehen. Jetzt sind nicht mehr die ersten 36 Zahlen um 0.1497% häufiger, sondern die Häufigeren Zahlen verteilen sich jetzt gleichmäßig über das Intervall.
Klingt für mich nach Mystik wiesrand(time(0)*time(0));Mein Tipp: Entweder nicht machen oder richtig machen.
-
Hallo keyzer,
Du wirst vielleicht einwenden, dass mein Vorschlag viel zu kompliziert ist, und nicht mehr macht, als der von matze-o - mal abgesehen davon, dass er die 25 vergessen hat. OK - ich meine komplizierter ist er gar nicht, sondern nur ungewohnt bzw befremdlich für jemanden der das so nicht kennt.
Ein Vorteil liegt darin, dass man den Vorgang der Ziehung jetzt ändern kann, ohne sich mit dem, Rest herumschlagen zu müssen. Ich hatten im ersten Beitrag schon erwähnt, dass es mehr als eine Möglichkeit gibt, zu den von Dir gewünschten Lottozahlen zu kommen. Ein großer Nachteil der bisher vorgestellten Lösungsmöglichkeit ist der Aufwand so viele Lottoziehungen machen zu müssen, bis das gewünschte dabei ist. Die zweite Möglichkeit, die ich hier vorstellen will, soll diesen Nachteil beheben.
1.) generiere den 'Pott' mit 49 Kugeln (class _49)
2.) mache folgendes 6 mal
3.) prüfe ob noch Kugeln vorhanden sind, falls nein beginne von vorn (-> 1.)
4.) ziehe zufällig eine Kugel und füge sie dem Ergebnis hinzu
5.) beseitige alle Kugeln aus dem Pott, die zusammen mit den bereits gezogenen Kugeln nicht mehr in Frage kommen
5.a) -- Ende der Schleife 2.)
6.) Ergebnis ausgebenDas Problem dabei ist, dass das gar nicht so einfach ist, zu bestimmen, welche Kugeln noch in Frage kommen und welche nicht. Deshalb wähle ich einen gemischten Ansatz, wo am Ende immer noch geprüft wird, ob die Anforderungen passen.
Zunächst muss aber _49 noch etwas erweitert werden, Kugeln sollen entfernt werden können und eine Abfrage, ob keine Kugeln mehr da sind, fehlt noch
class _49 { public: _49(); // wie oben int zieheEineKugel(); // wie oben // -- entfernt alle Kugeln mit der Eigenschaft 'pred( kugel ) == true' template< typename F > void remove_if( F pred ) { m_kugeln.erase( std::remove_if( m_kugeln.begin(), m_kugeln.end(), pred ), m_kugeln.end() ); } bool empty() const { return m_kugeln.empty(); } private: static void init(); // wie oben static bool m_initialized; std::vector< int > m_kugeln; };Um die oben beschriebene Vorgehensweise zu erreichen, erweitere ich den Algorithmus der Ziehung
template< typename T > struct KeinPaerchenMoeglich_type : std::unary_function< int, bool > { KeinPaerchenMoeglich_type( T first, T last ) : m_first( first ), m_last( last ) {} result_type operator()( argument_type a ) const { return std::find_if( m_first, m_last, std::bind2nd( istPaerchen(), a ) ) == m_last; } private: T m_first, m_last; }; template< typename T > KeinPaerchenMoeglich_type< T > keinPaerchenMoeglich( T first, T last ) { return KeinPaerchenMoeglich_type< T >( first, last ); } std::set< int > ziehung_6_aus_49_b() { using namespace std; std::set< int > ergebnis; do { ergebnis.clear(); // wg. Wiederholungen _49 kugeln; bool geradeZahlenEntfernt = false; bool ungeradeZahlenEntfernt = false; bool kleiner25Entfernt = false; bool groesser25Entfernt = false; int cntGerade = 0; int cntKleiner25 = 0; kugeln.remove_if( bind2nd( equal_to< int >(), 25 ) ); // Kugel 25 darf nicht drin vorkommen for( int anz = 1; ; ++anz ) { if( kugeln.empty() ) break; const int kugel = kugeln.zieheEineKugel(); ergebnis.insert( kugel ); if( anz == 6 ) break; // <== Schleifenende fertig if( kugel < 25 ) ++cntKleiner25; if( kugel % 2 == 0 ) ++cntGerade; if( anz < 2 ) continue; if( !kleiner25Entfernt && cntKleiner25 == 2 ) { kugeln.remove_if( bind2nd( less< int >(), 25 ) ); kleiner25Entfernt = true; } if( !geradeZahlenEntfernt && cntGerade == 2 ) { kugeln.remove_if( not1( bind2nd( modulus< int >(), 2 ) ) ); geradeZahlenEntfernt = true; } if( anz < 4 ) continue; if( !groesser25Entfernt && (anz - cntKleiner25) == 4 ) { kugeln.remove_if( bind2nd( greater< int >(), 25 ) ); groesser25Entfernt = true; } if( !ungeradeZahlenEntfernt && (anz - cntGerade) == 4 ) { kugeln.remove_if( bind2nd( modulus< int >(), 2 ) ); ungeradeZahlenEntfernt = true; } if( anz == 5 && adjacent_find( ergebnis.begin(), ergebnis.end(), istPaerchen() ) == ergebnis.end() ) { kugeln.remove_if( keinPaerchenMoeglich( ergebnis.begin(), ergebnis.end() ) ); } } } while( ergebnis.size() < 6 ); return ergebnis; }der Rest bleibt gleich; lediglich im main muss statt 'ziehung_6_aus_49()' 'ziehung_6_aus_49_b()' aufgerufen werden. Den Prüfer 'passt' könnte man jetzt noch soweit vereinfachen, nur die Summe zu prüfen, der Rest der Anforderungen ist von 'ziehung_6_aus_49_b()' garantiert.
Gruß
Werner
-
volkard schrieb:
int( (rand() / (RAND_MAX+1.0)) * m_kugeln.size() );Den Trick werde ich nie verstehen.
das stand mal im Magazin in Marcus' Artikel "Zufälle gibt's". Inzwischen hat er den Artikel aber überarbeitet.
volkard schrieb:
Jetzt sind nicht mehr die ersten 36 Zahlen um 0.1497% häufiger, sondern die Häufigeren Zahlen verteilen sich jetzt gleichmäßig über das Intervall.
Klingt für mich nach Mystik wiesrand(time(0)*time(0));Mein Tipp: Entweder nicht machen oder richtig machen.
Und mein Tipp - nicht nur lästern was falsch ist, sondern auch zeigen, wie es richtig geht.
Gruß
Werner
-
Werner Salomon schrieb:
volkard schrieb:
Jetzt sind nicht mehr die ersten 36 Zahlen um 0.1497% häufiger, sondern die Häufigeren Zahlen verteilen sich jetzt gleichmäßig über das Intervall.
Klingt für mich nach Mystik wiesrand(time(0)*time(0));Mein Tipp: Entweder nicht machen oder richtig machen.
Und mein Tipp - nicht nur lästern was falsch ist, sondern auch zeigen, wie es richtig geht.
Nagut. Besser nicht machen, in beiden Fällen. Und überhaupt ganz oft.
Das Bißchen Schieflage stört nicht. Falls doch, nehme man erstmal einen Zufallszahlengenerator, der 32-bittige Zahlen liefert. Dann sind die häufigeren nur noch um 0,00000114% häufiger. Falls das noch stört, die kleine Anti-Biasing-Schleife rein, siehe Marc++us.Und zum Quadrieren des seeds: Ich denke, es ist offensichtlich, daß das Quadrieren den Zufallsraum beschneidet. Nicht offensichtlich ist nur, wie stark die Beschneidung ist, sagen wir mal >80%.
-
Außerdem habe ich Angst vor Objektorientierung, vor bind, ein wenig vor Templates und vor allem vor Funktionen mit 59 Zeilen (, die die schöne Zufälligkeit nebenbei wieder zerkloppen), aber dafür mag ich funktionale Zerlegung und tue gerne schreiben wie ein N00b.
#include <iostream> #include <cstdlib> #include <ctime> using namespace std; bool istSchonDrin(int ziehung[6],int anzahlSchonDrin,int suchzahl){ for(int i=0;i!=anzahlSchonDrin;++i) if(ziehung[i]==suchzahl) return true; return false; } int findeNeueZahl(int ziehung[6],int anzahlSchonDrin){ int neueZahl; do neueZahl=rand()%49+1; while(istSchonDrin(ziehung,anzahlSchonDrin,neueZahl)); return neueZahl; } void erzeugeZiehung(int ziehung[6]){ for(int i=0;i!=6;++i) ziehung[i]=findeNeueZahl(ziehung,i); } void printZiehung(int ziehung[6]){ for(int i=0;i!=6;++i) cout<<ziehung[i]<<' '; cout<<'\n'; } int berechneSumme(int ziehung[6]){ int summe=0; for(int i=0;i!=6;++i) summe+=ziehung[i]; return summe; } int zaehleZahlenUnter25(int ziehung[6]){ int anzahl=0; for(int i=0;i!=6;++i) if(ziehung[i]<25) ++anzahl; return anzahl; } int zaehleZahlenUeber25(int ziehung[6]){ int anzahl=0; for(int i=0;i!=6;++i) if(ziehung[i]>25) ++anzahl; return anzahl; } int zaehleGeradeZahlen(int ziehung[6]){ int anzahl=0; for(int i=0;i!=6;++i) if(ziehung[i]%2==0) ++anzahl; return anzahl; } int zaehleUngeradeZahlen(int ziehung[6]){ int anzahl=0; for(int i=0;i!=6;++i) if(ziehung[i]%2==1) ++anzahl; return anzahl; } bool hatZahlenpaeaechen(int ziehung[6]){ for(int i=0;i!=6;++i){ if(istSchonDrin(ziehung,6,ziehung[i]-1)) return true; if(istSchonDrin(ziehung,6,ziehung[i]+1)) return true; } return false; } int main(){ srand(time(0)); int ziehung[6]; for(;;){ erzeugeZiehung(ziehung); int summe=berechneSumme(ziehung); if(summe<142 || summe>156) continue; if(zaehleZahlenUnter25(ziehung)!=2) continue; if(zaehleZahlenUeber25(ziehung)!=4) continue; if(zaehleGeradeZahlen(ziehung)!=2) continue; if(zaehleUngeradeZahlen(ziehung)!=4) continue; if(!hatZahlenpaeaechen(ziehung)) continue; printZiehung(ziehung); } return 0; }
-
Pöse Purschen dürfen das srand aus der main rausmachen und im global scope
bool _initRandom=(srand(time(0)),1);schreiben. Aber das ist nicht im Sinne des happy debugging. Denn auf einmal macht das jeder und falls man mal reproduzierbare Fehler braucht, kann man in der main nicht mehr zurückschalten.
-
Ah, ich weiß, was
srand(time(0)*time(0));bringen soll! Nix mit "Das macht man halt so".
Wenn man das nicht hat und das Programm sekündlich startet, steigt bei vielen Compilern zwischen den Programmläufen die erste erzeugte Zufallszahl jeweils um 1. Das wollte er so wegmachen.Besser, mit einer großen ungeraden Zahl plutimizieren. Mal einen normalen LCG angenommen. Ungerade Zahlen sind automatisch teilerfremd zur Zweierpotenz und haben ein Inverses -> kein Bit geht verloren.
srand(time(0)*67234863);Dumm nur, wenn es jetzt jemand alle 659479247 Sekunden startet (ja, recht unwahrscheinlich).
Aber cool ist es erst, einfach ein rand() hinterherzuwerfen, die zweite Zahl ist dann schon so durcheinander, wie es dem rand() halt seine Art ist.
srand(time(0));rand();
-
hallo werner salomon,
vielen dank das du dir da so viele mühe gemacht hast aber ganz ehrlich, wie du schon gesagt hast, das is ne nummer zu hoch für mich. da hab ich nicht die zeit für mich mit den ganzen befehlen zu beschäftigen die mir fremd sind.
ich war auf der suche nach einer lösung mir den gewöhnlichen schleifen, if, vor, do-while und was es da noch gibt.hallo volkard
ehrlich ich weiß nicht genau was der befehl
srand(time(NULL) * time(NULL));
hab ich halt bei jemandem abgeschrieben der was ähnliches versucht hat.
bewirkt, aber es funktioniert bei mir. ich bekomme immer 6 unterschiedliche zahlen von 1-49nochmal zu meiner do-while-schleife
ich wollte das das programm nur fortfährt wenn es in blabla eine zahl größer 142 und kleiner 156 errechnet hat.
mit:do
{
blablabla
}
while ( summe >= 142 && summe <= 156);funt das ja nicht.
hab dann mal rumprobiert und mit:do
{
do
{
blabla
}
while (summe >= 157);
}
while (summe <= 141);macht er das was ich will...
-
Also, die erste do ... while Schleife ist ansich schon korrekt.
Ich verstehe Dich nicht ganz. Die Leute hier haben Dir wirklich gute Tipps gegeben und Du schlägst alles in den Wind und bist nicht zufrieden. Ich weiß nicht warum Du das Projekt machst, aber wenn Du das gescheit machen willst, dann solltest Du vielleicht mal auf die hören und versuchen zu verstehen.Deine Variante der do ... while Schleife kann zu total falschen Verhalten führen. Zum Beispiel wenn die innere Schleife eine Zahl >= 157 bekommt, dann läuft sie durch, obwohl Du willst das die Schleife nur läuft wenn die Zahl <= 156 und >= 142 ist. Das geht nicht gut...