Lottozahlen ohne Wiederholung



  • Hallo,

    typischer Algorithmus für Lottozahlen ziehen ist:

    
    Array a mit Zahlen 1 bis 49
    for i=0 bis 5
    {
      x = rand(49-i)
      result[i] = a[x];
      swap(a[x], a[49-i]); // gezogene Zahl aus dem rand-Bereich raus nehmen
    }
    
    


  • Leider komme ich immer noch nicht weiter... eure Denkanstöße haben mich schon ein kleines Stückchen weitergebracht, aber letztendlich bräuchte ich noch Hilfe.
    Die Erstellung einer Funktion gelingt mir nicht.



  • Kürzer wirst du es nicht hinkriegen.

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    enum {MYSIZE=6,MYASIZE=49};
    
    int enthaelt(const int *a,int z)
    {
    	int i;
    	for(i=0;i<MYSIZE;++i)
    	if(a[i]==z) return 1;
    	return 0;
    }
    
    int main()
    {
        int i, x, array[MYSIZE]={0};
    
        srand(time(0));              //Zufallszahlengenerator
    
        for(i=0; i<MYSIZE; i++)          // Schleife zur Bestimmung der 6 Felder
        {
        	while( enthaelt(array,x=rand()%MYASIZE+1) );
        	printf(" %d ",array[i]=x);
        }
    
        return 0;
    }
    

    https://ideone.com/PUziXy



  • Vielen Dank! Das ist genau so, wie ich es haben soll.
    Könntest du einmal kurz erklären was enum bedeutet?


  • Mod

    @Luckyingmar sagte in Lottozahlen ohne Wiederholung:

    Vielen Dank! Das ist genau so, wie ich es haben soll.

    Mag zwar sein, dass dein Lehrer das so akzeptiert, aber wenn du wirklich etwas lernen möchtest, dann arbeite lieber den Vorschlag von Jockelx durch. Warum hat Jockelx das wohl vorgeschlagen? Wo liegen die Vorteile? Wieso ist bei 6 aus 49 die andere Lösung von Wutz auch ganz gut?



  • Ich habe versucht, den Vorschlag von Wutz und Jockelx durchzuarbeiten. Allerdings bin ich nicht weit gekommen. Ich konnte mit den Denkanstößen nicht viel anfangen, ich bin auch noch ziemlich unerfahren in der Thematik.
    Ich versuche die Lösung von Wutz an meine Schreibweise anzupassen und diese zu verstehen!



  • Ich möchte nochmal SeppJs Frage zum Vergleich der beiden Lösungen erweitern, vielleicht hilft dir das auch weiter bei der Beantwortung:

    Was passiert, wenn ich statt 6 aus 49 ein neues Spiel entwerfe mit

    • 6 aus 49000
    • 48900 aus 49000


  • also ich würde es so machen:

    #define NUMNUMBERS 6
    #define NUMBERMIN 1
    #define NUMBERMAX 49
    
    int main()
    {
         //Deklarationen
         unsigned int numbers[NUMBNUMBERS];
         int i, j;
         //Schleife zur Bestimmung der Zufallszahlen
         for(i = 0; i < NUMNUMBERS; i++)
         {
              numbers[i] = NUMBERMIN + (rand() % (NUMBERMAX - NUMBERMIN + 1)); //Zufallszahl in [NUMBERMIN, NUMBERMAX] bestimmen
    
              for(j = 0; j < i; j++) //Schleife zum Abgleich aller bisherigen Zahlen mit der neuen
              {
                   if(numbers[j] == numbers[i]) //Bisherige Zahl ist vorhanden
                   {
                        break
                   }
               }
    
              if(numbers[i] == numbers[j])
              {
                        i--; //continue bewirkt Sprung ans Schleifenende, d.h. i wird inkrementiert. Um wieder an die gleiche Stelle im Array zu schreiben, wird i daher hier dekrementiert.
                        continue;
              }
         }
    }
    

  • Mod

    @Wade1234 : Das ist ja das gleiche wie von Wutz, aber in schlechter lesbar. Da muss man ja erst einmal gründlich nachdenken, was Zeile 25 bezwecken soll und ob das überhaupt legal ist. Der lange Kommentar, der zur Erklärung von vier Zeichen nötig ist, sollte ein Alarmzeichen sein.

    Du hast dich übrigens auch prompt gleich mehrmals verschrieben, es funktioniert so nicht. Und damit sind nicht nur Syntaxfehler gemeint. Selbst nach deren Korrektur ist dein Programm eine Endlosschleife.

    Als Vorbild daher denkbar schlecht geeignet, aber @Luckyingmar kann sich ja mal daran versuchen, die Fehler zu finden, verstehen, und zu beheben. Daher sage ich auch absichtlich nicht, wo die Fehler liegen. Die meisten sollten für den geübten Leser offensichtlich sein (und falls nicht-offensichtliche Fehler drin sind, kenne ich sie schließlich selber nicht 😃 )



  • @SeppJ ich habs nochmal korrigiert und dieses mal auch getestet.

    #include <stdlib.h>
    
    #define NUMNUMBERS 6
    #define NUMBERMIN 1
    #define NUMBERMAX 49
    
    int main()
    {
         unsigned int numbers[NUMNUMBERS];
         int i, j;
    
         for(i = 0; i < NUMNUMBERS; i++)
         {
              numbers[i] = NUMBERMIN + rand() % (NUMBERMAX - NUMBERMIN + 1);
    
              for(j = 0; j < i; j++)
              {
                   if(numbers[j] == numbers[i])
                   {
                        i--;
                        continue;;
                   }
               }
         }
    
         return 0;
    }
    

    ich glaube, dass ich es vorher auch so (mit den fehlern) geschrieben hatte, aber irgendwie hatte ich dann plötzlich einen einfall oder so. das von wutz halte ich eigentlich für sehr unsauber, also eine zuweisung in printf geht z.b. gar nicht.


  • Mod

    So ist das auch schon wesentlich verständlicher. Aber die Zeilen 20 und 21 finde ich immer noch fies. Die logische Anforderung "ziehe so lange eine neue Zahl, bis diese nicht in der Liste der schon gezogenen Zahlen ist" lässt sich nahezu 1:1 in richtig feines C hin und zurück übersetzen. Deine Lösung hingegen ergibt sich nicht als direkte Übersetzung der logischen Beschreibung, und die Rückübersetzung ist erst recht verwirrend.



  • @Wade1234 Gibt es eigentlich einen Grund, warum du auf Macros statt Konstanten gegangen bist?


  • Mod

    @wob sagte in Lottozahlen ohne Wiederholung:

    @Wade1234 Gibt es eigentlich einen Grund, warum du auf Macros statt Konstanten gegangen bist?

    C89 Kompatibilität? Ginge natürlich auch mit enums, siehe Wutz, aber da die Werte keine Beziehung zueinander haben, ist es ziemlich wurscht. Aber wie in C++ mit const ginge es erst ab C99.



  • @Jockelx sagte in Lottozahlen ohne Wiederholung:

    Hallo,

    typischer Algorithmus für Lottozahlen ziehen ist:

    
    Array a mit Zahlen 1 bis 49
    for i=0 bis 5
    {
      x = rand(49-i)
      result[i] = a[x];
      swap(a[x], a[49-i]); // gezogene Zahl aus dem rand-Bereich raus nehmen
    }
    
    

    Noch besser ist übrigens einfach nur gezogenen Zahlen ans Ende (oder auch Anfang) des Arrays a zu swappen und das result Array komplett wegzulassen.



  • @Wade1234 Warum ein continue?
    Beim ersten Match ist die Zahl doppelt und es muss neu gezogen werden.
    Wenn mehr als eine Zahl doppelt ist, funktioniert der Algorithmus nicht.



  • @SeppJ also ich habe es jetzt so gemacht:

    #include <stdlib.h>
    #include <time.h>
    #include <stdio.h>
    
    #define NUMNUMBERS 6
    #define NUMBERMIN 1
    #define NUMBERMAX 49
    
    int main()
    {
         unsigned int numbers[NUMNUMBERS];
         unsigned int tempnumber;
         int i, j;
    
         srand(time(0));
    
         for(i = 0; i < NUMNUMBERS; i++)
         {
              for(;;)
              {
                   tempnumber = NUMBERMIN + rand() % (NUMBERMAX - NUMBERMIN + 1);
    
                   for(j = 0; j < i; j++)
                   {
                        if(tempnumber == numbers[j])
                        {
                             break;
                        }
                   }
    
                   if(j == i)
                   {
                        numbers[i] = tempnumber;
                        printf("%u\n", numbers[i]);
    
                        break;
                   }
              }
         }
    
         printf("\n");
    
         return 0;
    }
    

    @wob ehrlich gesagt habe ich das irgendwann einfach so gelernt. enums verwendet man / ich bei fortlaufenden konstanten, weil da unkompliziert weitere konstanten eingefügt werden können.

    @DirkB ja den gedanken, dass nur die innere schleife erneut durchlaufen wird, hatte ich auch und deshalb habe ich da oben auch dieses komische programm zusammengefrickelt.



  • @TGGC sagte in Lottozahlen ohne Wiederholung:

    Noch besser ist übrigens einfach nur gezogenen Zahlen ans Ende (oder auch Anfang) des Arrays a zu swappen und das result Array komplett wegzulassen.

    Oh ja, das stimmt allerdings.


  • Mod

    Endlosschleifen mit if ... break als Kontrollelement. Benutzung von Zählvariablen nach Abschluss einer Schleife. Findest du das wirklich gut? Vielleicht solltest du noch einmal Abstand nehmen und nicht versuchen, deinen jetzigen Ansatz zu reparieren, sondern in Ruhe einen schönen Ansatz zu finden.

    Dein Programm besteht derzeit aus einer Reihe von Fehlerkorrekturalgorithmen für den jeweils davor liegenden Algorithmus. Zerlege das Problem in kleinere Unterprobleme, und finde einfache, korrekte Lösungen für diese Probleme. Dann setz diese Lösungen zusammen. Wutzs Programm hat nicht deshalb 10 Zeilen, weil er Zuweisungen in Funktionsaufrufen macht (ok, ein bisschen trägt das schon bei, aber nicht alles), sondern weil er effiziente Teillösungen effizient zusammen gebaut hat.



  • @Jockelx sagte in Lottozahlen ohne Wiederholung:

    Hallo,

    typischer Algorithmus für Lottozahlen ziehen ist:

    
    Array a mit Zahlen 1 bis 49
    for i=0 bis 5
    {
      x = rand(49-i)
      result[i] = a[x];
      swap(a[x], a[49-i]); // gezogene Zahl aus dem rand-Bereich raus nehmen
    }
    
    

    Ist auch genau meine Vorgehensweise, wenn ich irgendeine zufällige Reihenfolge von sich nicht wiederholenden Zahlen brauche, egal ob man aus einem Set von Zahlen alle braucht, oder nur ein paar ( 6 aus 49 ). Verschachtelte Schleifen sind überhaupt nicht notwendig.
    Das schöne an dem Algorithmus ist auch, dass allein das Array bestimmt, welche Zahlen verwendet werden. Die müssen auch gar nicht fortlaufend sein. Deswegen ist der Algorithmus auch so vielseitig.



  • Warum orientierst du dich nicht am "Original?"

    Du hast 49 Kugeln von 1 bis 49. Also ein Array von 0 bis 48.
    Dieses Array initialisierst du mit Zahlen von 1 bis 49.

    Dann generierst du eine Zufallszahl von 0 bis 48.
    Du streichst diese Zahl aus dem Array, in dem du das diese Zahl daraus löscht.
    Stell dir das so vor, wie wenn du aus einem Stapel Bücher eins raus nimmst.
    Beispiel:
    generierte Zufallszahl: 45
    Array vor dem Streichen: ...........42,43,44,45,46,47,48,49
    Nach dem Streichen...................42,43,44,46,47,48,49

    Da jetzt aber di Anzahl deiner Kugeln(Elemente) jetzt 48 sind.
    generierst du jetzt eine Z-Zahl von 0 bis 47
    dann wieder kürzen, dann ne Z-Zahl von 0 bis 46
    Bis du deine Zahlen hast.

    Das ganze machst du also 6 mal, mit Zusatzzahl 7 mal.
    In einem zweitzen Array legtst du dann deine generierten Zahlen ab.
    Beispiel:

    /***-----
    | getrand.c
    |
    | Demonstrates how to generate a random| number between 1 and a given value.
    |
    | NOTE: In real use, you would not want| to call srand() before every call to
    | rand(), because if you call rand()| quickly in succession, it will keep
    | returning the same results. Instead,| move the call to srand to the beginning
    | of your program and only call it once. It works in this program, but only
    | because getrand() is called a single| time.
    -----***/
    
    #include  <stdlib.h>
    #include  <stdio.h>
    #include  <time.h>
    
    int initrandom = 0;
    //generiert Zufallszahlen von 1 bis 32767;
    int getrand (int lowerlimit, int toprange)
    {
    // bis 32767
     //srand (time (NULL));
     if (initrandom == 0)
      {
      srand (time (NULL));
      initrandom = 1;
      }
     return (rand () % toprange) + lowerlimit;
    }
    
    void zeigezahlen(int zuzaar[], int zuzalimit)
    {
    int zeize = 0, slei;
    printf("\nZufallszahlen von 1 bis %d:\n", zuzalimit);
    
     for (slei = 0; slei < zuzalimit; slei++)
      {
      printf ("Nr.%d = %d  ", slei, zuzaar[slei]);
      zeize++;
      if (zeize >= 6) { printf("\n"); zeize = 0; }
      }
    }
    
    void initarreale(int obergrenze, int (*zuzaar)[], int (*schluessel)[], int (*wuerfel)[])
    {
    int slei;
    if (obergrenze > 89) obergrenze = 89;
    
     for (slei = 0; slei < 99; slei++)
       (*wuerfel)[slei] = 0;
    
     for (slei = 0; slei < obergrenze; slei++)
      {
       (*zuzaar)[slei] = 0;
       (*wuerfel)[slei] = slei;
       (*schluessel)[slei] = 0;
      }
    
    }
    
    void kuerzearreal(int zuza, int og, int wuerfel[], int (*schluessel)[], int stelle)
    {
    int slei;
    
    (*schluessel)[stelle] = wuerfel[zuza];
    for (slei = zuza; slei < og; slei++)
     {
        wuerfel[slei] = wuerfel [slei + 1];
     }
    printf("\nog: %d zuza: %d  stelle:%d   nach Kuerzung:   ", og, zuza, stelle);
    for (slei = 0; slei < (og - 1); slei++)
     printf(" -%d- ", wuerfel[slei]);
    }
    
    int main (void)
    {
     int untergrenze, toprange = 49, slei, wohin, dmog, sleia;
     int zuzaar[100], schluessel[100], wuerfel[100];
    
    untergrenze = 0;
    
    hauptmenue:
    printf("\nZufallszahlengenerator");
    printf("\nEnde...0   Obergrenze eingeben...1   Zahlen generieren...2");
    
    wouhi: printf("\nIhre Wahl: ");
        scanf("%d", &wohin);
        if (wohin == 0 ) goto finito;
        if (wohin == 1 ) goto setzeog;
        if (wohin == 2 ) goto genzuza;
      goto wouhi;
    
    setzeog: printf("\nObergrenze: (max 90)");
             scanf("%d", &toprange);
      goto hauptmenue;
    
    genzuza: if (toprange > 89) toprange = 89;
        initarreale(toprange, &zuzaar, &schluessel, &wuerfel);
    /*
         for (slei = 0; slei < 99; slei++)
          {
           zuzaar[slei] = slei;
          // wuerfel[slei] = schluessel[slei] = 0;
          }
    */
          zeigezahlen(zuzaar, toprange);
          zeigezahlen(schluessel, toprange);
          zeigezahlen(wuerfel, (toprange + 3));
          dmog = toprange;
          sleia = 0;
    
          for (slei = 0; slei < toprange; slei++)
           {
            zuzaar[slei] = getrand(untergrenze, dmog);
            kuerzearreal(zuzaar[slei], toprange, wuerfel, &schluessel, sleia);
            sleia++;
            dmog--;
            if (dmog == 0 ) goto fertig;
           }
    fertig:
    /*Generierte Zufallszahlen zeigen */
     zeigezahlen(zuzaar, toprange);
     zeigezahlen(schluessel, toprange);
    
     goto hauptmenue;
    
     finito: printf("\nProgrammende");
     return 0;
    }
    
    Hab mal ein Verschlüsselungsproggi geschrieben, das die Bit-Folgen von Dateien quasi durcheinander wirbelt.
    Da wird zuerst ein Array von 0 bis 131071 erstellt(=16384Bit) und mit 0 bis 131071 initialisiert. Jede Zahl darf nur einmal vorkommen. Diese Zahlen werden dann permutiert. Rein theoretisch gibt es dann 131071! (Faktorielle) Möglichkeiten, wie der Schlüssel dann aussieht.
    
    Genauer genommen ist das wegen dem mehrfachen Vorkommen einzelner Elemente innerhalb einer Klasse lauf Euler
     Verschlüsselungstiefe = { (16384+16384)! /(16384! *16384!)}
    Aber nur bei Gleichverteilung der gesetzten zu den ungesetzten Bits.
    Kannst ja mal nach Talarius googlen. 
    Deshalb habe ich diese Vorgehensweise entwickeln müssen, da die Zeit beim erstellen der Schlüsseldatei exponentiell
    ansteigt. Aber das würde jetzt zu weit gehen.