keine doppelten zahlen bei rand()



  • Hallo Leute und zwar habe ich momentan eine kleine Aufgabe die ich programmieren muss und frage mich ganze zeit wieso meine Do-While-Schleife nicht funktioniert. Wäre nett wenn sich der ein oder Andere den Code-Abschnitt mal anschauen würde und mir einen kleinen Tipp geben kann. Vielen Danke schon mal 🙂

    Mein Problem ist, dass sich bei der rand()-Funktion immer doppelte Zahlen in meinem Array abspeichern. Ich wollte es mit der Fußgesteuerten Schleife verhindern, aber passiert trotzdem weiterhin.

    [code="c"]

    do{
    for (i=0; i<ziehung; i++) {
    Lottozahlen[i] = rand()%Obergrenze+Untergrenze;
    } //Ende der FOR-Schleife für Lottozahlen

    }while (Lottozahlen[i] == Lottozahlen[i+1]);



  • Cobain schrieb:

    Mein Problem ist, dass sich bei der rand()-Funktion immer doppelte Zahlen in meinem Array abspeichern. Ich wollte es mit der Fußgesteuerten Schleife verhindern, aber passiert trotzdem weiterhin.

    Vielleicht ist Dein Algorithmus um Dubletten festzustellen nicht richtig?



  • wie kann man denn sowas im allgemeinen vermeiden im Bezug auf einen Array der Zufallszahlen aufnimmt ?
    Im Netz finde ich nämlich einen solchen Fall nicht erklärt wo Zufallszahlen in einen Array aufgenommen werden.



  • Cobain schrieb:

    wie kann man denn sowas im allgemeinen vermeiden im Bezug auf einen Array der Zufallszahlen aufnimmt ?

    Wie würdest Du es denn machen, wenn Du bewaffnet mit einem Stück Papier, einem Bleistift und einem Würfel eine Reihe von sich nicht wiederholenden gewürfelten Zahlen aufschreiben willst?



  • ??



  • Versuch mal das ganze Verfahren mit einem Bleistift auf einem Blatt Papier nachzustellen.

    Beobachte dich dabei.
    - Wann vergleichst du ob die Zahl schon gezogen wurde? Gleich nach der Ziehung oder wenn alle Zahlen gezogen wurden?

    Damit du deinen Algorithmus verstehst, geh ihn mit dem Debugger durch.
    Oder baue ein paar printf ein, die dir die aktuellen Variablen (i, Lottozahlen[ i] und auch Lottozahlen[i+1]) anzeigen.

    Wie groß ist denn dein Feld ziehung ?



  • der integer Ziehung ist 6 groß



  • So in etwa:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    int diese_zahl_wurde_bereits_gezogen(int, int*);
    
    int main(void)
    {
        int lottozahlen[6];
        int i;
        int zufallszahl;
    
        srand((unsigned)time(NULL));
    
        for (i=0;i<6;i++)
            lottozahlen[i] = 0;
    
        for (i=0;i<6;i++)
        {
            do
            {
                zufallszahl = 1 + (rand() % 49);
            }
            while (diese_zahl_wurde_bereits_gezogen(zufallszahl,lottozahlen));
            lottozahlen[i] = zufallszahl;
        }
    
        for (i=0;i<6;i++)
            printf("%d\n",lottozahlen[i]);
    
        return 0;
    }
    
    int diese_zahl_wurde_bereits_gezogen(int zufallszahl,int lottozahlen[6])
    {
        int i;
    
        for (i=0;i<6;i++)
            if (zufallszahl == lottozahlen[i])
                return 1;
        return 0;
    }
    

    Die Funktion: diese_zahl_wurde_bereits_gezogen überprüft einfach ob im Array die Zufallszahl bereits vorkommt.



  • Und das %Obergrenze+Untergrenze; funktioniert nur bei Untergrenze = 1 richtig.
    Dann kannst du die 1 auch direkt hinschreiben.

    Das 1 + (rand() % 50); von Bitmapper ergibt Zahlen von 1 bis 50 (einschließlich).



  • Habs korrigiert

    1 + (rand() % 49)
    


  • Nur mal so noch nebenbei, die erste For-Schleife ist nicht nötig ^^, wenn du bei der Array definition das erste Elemente mit 0 deklarierst, bekommen sämtliche anderen Elemente des Array ebenfalls 0 😉



  • Ich finde die Variante mit einem shuffle auch schick (wenngleich für 6/49 nicht gerade Laufzeitoptimal):

    initialisiere ein Array mit 1...49
    für alle i Elemente in Array
       tausche Element i mit zufälligem Element in Array
    Nimm die ersten 6 Werte aus Array
    

  • Mod

    Der echte Tim schrieb:

    Ich finde die Variante mit einem shuffle auch schick (wenngleich für 6/49 nicht gerade Laufzeitoptimal):

    initialisiere ein Array mit 1...49
    für alle i Elemente in Array
       tausche Element i mit zufälligem Element in Array
    Nimm die ersten 6 Werte aus Array
    

    Das ist weder effizient (wie du selber feststellst), noch überhaupt korrekt. Dein Shuffle ist biased und produziert gewisse Folgen häufiger als andere. Probier es mal auf Papier mit 3 Elementen aus.

    Ein Fisher-Yates hätte hier folgende Vorteile gegenüber deinem und allen anderen Vorschlägen:
    a) Es ist nicht komplizierter als die anderen Vorschläge, eher im Gegenteil. Im Vergleich zu deinem Vorschlag ist es beispielsweise nur eine minimale Änderung.
    b) Es ist extrem effizient, da man einfach nach den ersten 6 Zahlen aufhören kann
    c) Wegen b ist es auch für andere Probleme gut geeignet. Wie zum Beispiel "ziehe 48 aus 49", bei dem der Algorithmus mit dem Vergleichen einknicken würde. Mein Vorschlag ist vielleicht nicht immer der beste, aber er ist immer ganz ok, da er keine Schwächen hat.

    Also konkret:
    -Fülle Array mit Werten 1-49
    -Ziehe Index 0-48, tausche Array[index] mit Array[0]
    -Ziehe Index 1-48, tausche Array[index] mit Array[1]
    ...
    -Ziehe Index 6-48, tausche Array[index] mit Array[6]
    -Die Werte in Array[0] bis Array[6] sind nun eine zufällige Sequenz von Werten aus dem Array

    PS: Eine andere Schwäche aller Vorschläge hier (inklusive meinem) ist, dass der Zufallsgenerator eventuell einfach nicht gut genug ist. Bei einigen alten Implementierungen (MSVC 6 und älter) ist die Periodenlänge von rand so um die 16 Millionen. Das ist von der gleichen Größenordnung wie die Zahl der Kombinationen von 6 aus 49 (13 Millionen). Das heißt, gewisse Kombinationen zieht man damit auch gerne mal doppelt so oft wie andere, eventuell zieht man manche sogar nie. Man sollte also vielleicht was eigenes implementieren, anstatt zu hoffen, dass rand vernünftig implementiert ist. Die üblichsten Implementierungen haben nämlich auch nur Perioden von 2^31 bis 2^31, was nun auch nicht gerade extrem viel mehr als 13 Millionen ist.



  • SeppJ schrieb:

    Das ist weder effizient (wie du selber feststellst), noch überhaupt korrekt. Dein Shuffle ist biased und produziert gewisse Folgen häufiger als andere. Probier es mal auf Papier mit 3 Elementen aus.

    Dann finde ich das nicht mehr schick sondern sogar ausgesprochen scheisse.



  • SeppJ schrieb:

    Bei einigen alten Implementierungen (MSVC 6 und älter) ist die Periodenlänge von rand so um die 16 Millionen. Das ist von der gleichen Größenordnung wie die Zahl der Kombinationen von 6 aus 49 (13 Millionen). Das heißt, gewisse Kombinationen zieht man damit auch gerne mal doppelt so oft wie andere, eventuell zieht man manche sogar nie. Man sollte also vielleicht was eigenes implementieren, anstatt zu hoffen, dass rand vernünftig implementiert ist. Die üblichsten Implementierungen haben nämlich auch nur Perioden von 2^31 bis 2^31, was nun auch nicht gerade extrem viel mehr als 13 Millionen ist.

    Man müßte schon sauviel Pech haben, daß die Periode des Programms mit der Periode des Zufallszahlengenerators einen großen gemeinsamen Teiler hat.



  • ich vermute momentan, dass das Problem wohl doch woanders liegt, aber den Fehler im Programm finde ich einfach nicht. Wäre cool wenn der ein oder Andere mir da weiterhelfen könnte 😃

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <stdbool.h>
    
        double fak (double n){ //Unterfunktion Fakultät
            if(n == 0){
    		return 1;
            }
                else{
        	return n*fak(n-1);
            }
        }
    
    int main(void) {
    
        int Untergrenze = 1;
        int Obergrenze = 49;
        int ziehung = 6;
    
        int sortierung1;
        int sortierung2;
        int zufallszahl;
        double binom = 1;
    
        int i;
        int k;
        int m = 0;
    
        int eingabe[6];
        int Lottozahlen[6];
        int treffer[6];
    
        srand(time(NULL));
    
        printf("Geben Sie bitte 6 Zahlen von 1 - 49 ein um an der Lotterieziehung teilzunehmen.");
    
        for(i=0; i<ziehung; i++) {
            printf("\n\nZahleneingabe: ");
            scanf("%d",&eingabe[i]);
    
            while (eingabe[i]<1) {
            printf("\n\nIhre Eingabe ist kleiner als zulaessig.");
            printf("\nWiederholen Sie die Zahleneingabe. ");
            printf("\n\nZahleneingabe: ");
            scanf("%d",&eingabe[i]);
            }
            while (eingabe[i]>49) {
            printf("\n\nIhre Eingabe ist groesser als zulaessig.");
            printf("\nWiederholen Sie die Zahleneingabe. ");
            printf("\n\nZahleneingabe: ");
            scanf("%d",&eingabe[i]);
            }
        } //Ende FOR-Schleife für Eingabe
    
        printf("\n\nIhre ausgewaehlten Lottozahlen lauten: \n\n");
        printf("1.Auswahl: %d\n",eingabe[0]);
        printf("2.Auswahl: %d\n",eingabe[1]);
        printf("3.Auswahl: %d\n",eingabe[2]);
        printf("4.Auswahl: %d\n",eingabe[3]);
        printf("5.Auswahl: %d\n",eingabe[4]);
        printf("6.Auswahl: %d\n",eingabe[5]);
    
    while (m<3) {
    
            printf("\n\nDie momentanen Lottozahlen lauten: \n\n");
    
            for (i=0; i<ziehung; i++) {
                Lottozahlen[i] = 0;
            }
    
            for (i=0; i<ziehung; i++) {
    
                zufallszahl = 1 + (rand() % 49);
                Lottozahlen[i] = zufallszahl;
    
                if(i>0) {
    
                        if (Lottozahlen[i-1]==Lottozahlen[i]) {
                                Lottozahlen[i] = Lottozahlen[i] +1;
                        }
                }
    
                 if (Lottozahlen[i-1]=Lottozahlen[i]) {
                                Lottozahlen[i] = Lottozahlen[i] +1;
                }
    
            } // Ende FOR-Schleife für Sortierung der Lottozahlen
    
      }
    
                        for(k=0; k<ziehung; k++) {
                                //printf("I: %d\n",i);
                                for(i=0; i<ziehung; i++) {
                                    //printf("K: %d\n",k);
                                    if(eingabe[k]==Lottozahlen[i]) {
                                        treffer[m]=eingabe[k];
                                        m++;
                                    }
                                }
    
                                if(m<3) {
                                    m=0;
                                }
                        }
    
        		//}
    
                for(i=0; i<ziehung; i++){ //Zufallszahlen ausgeben
                    printf("%d.Lottozahl: %d\n",i+1,Lottozahlen[i]);
                }
    
            if (m>2) {
                printf("\nGlueckwunsch, Sie haben %d Zahlen getroffen.\n",m);
                printf("Die folgenden Zahlen sind richtig: ");
                for(i=0;i<m;i++){ //Ausgabe aller richtig getroffenen Zahlen
                            printf("%d ",treffer[i]);
                }
            }
    
    } //Ende While schleife
    
            binom = fak(49)/(fak(6)*fak(43)); //Binomialkoeffizienten berechnen
            printf("\n\nDie Anzahl der moeglichen Ziehungen: %.0lf\n\n", binom);
    
            return 0;
    
    }
    

  • Mod

    Was ist denn überhaupt das Problem? Du schreibst bloß es "funktioniert nicht". Das ist keine Problembeschreibung anhand derer sich irgendjemand mal eben 200 Zeilen Quelltext ansehen wird.



  • Also es handelt sich um ein Programm für die Lottoziehung. Es werden 6 Zahlen von 1-49 eingegeben. Diese werden dann immer mit der zufälligen Zahlenziehung überprüft bis mindestens drei Zahlen übereinstimmen.

    Es müssen bei jeder zufälligen Zahlenziehung unterschiedliche Zahlen kommen, sprich keine doppelten Zahlen bei der momentanen Ziehung. Bei der nächsten Ziehung kann die ein oder andere Zahl ruhig wieder auftauchen solange sie in dieser ziehung nicht zwei oder drei mal auftaucht.

    In meinem Programm jedoch hab ich das Problem, dass das Programm halt paar Sekunden abläuft und mir dann einfach mal in der letzten Ziehung mindestens 3 gleiche Zahlen in der momentanen Ziehung anzeigt.

    Wenn in der momentanen Ziehung zB. eine 3 vorkommt dann darf sie für die nächsten 5 Zahlen nicht wieder vorkommen, sondern erst bei der nächsten Ziehung-


  • Mod

    Wenn das immer noch das Problem ist, dann lies den Thread noch einmal. Die erste Antwort und alles was danach kam, gelten immer noch:

    Furble Wurble schrieb:

    Vielleicht ist Dein Algorithmus um Dubletten festzustellen nicht richtig?

    Wobei man nun das "vielleicht" in der Antwort streichen kann. Denn das hier ist ganz sicher falsch:

    for (i=0; i<ziehung; i++) {
    
                zufallszahl = 1 + (rand() % 49);
                Lottozahlen[i] = zufallszahl;
    
                if(i>0) {
    
                        if (Lottozahlen[i-1]==Lottozahlen[i]) {
                                Lottozahlen[i] = Lottozahlen[i] +1;
                        }
                }
    
                 if (Lottozahlen[i-1]=Lottozahlen[i]) {
                                Lottozahlen[i] = Lottozahlen[i] +1;
                }
    

    Sowohl die Idee (Du vergleichst nur mit dem Vorgänger) als auch die Umsetzung (Was ist bei der ersten Zahl? Du scheinst diesen Fall bedacht zu haben, aber irgendwie auch wieder nicht) und sogar die grundlegende Syntax ('=' ist kein Vergleich!) sind falsch.

    Es wurden reichlich Anregungen in diesem Thread gegeben, wie du selber eine korrekte Prüfung durchführen kannst oder welche alternativen Verfahren geeignet sein könnten, die ganz ohne Prüfung auskommen.



  • genau diese Stelle war auch der Auslöser weshalb ich den Beitrag verfasst habe. Ich habe es mit Tipps die mir zuvor genannt wurden versucht, aber ohne Erfolg es wird trotzdem weiterhin noch bei der letzten Ziehung drei mal die gleiche Zahl gezogen.


Log in to reply