Lottozahlen ohne Wiederholung


  • 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.
    


  • Wow, ist das aufwendig im Vergleich zu schon gezeigten Algos, die im Grunde die gleiche Idee umsetzen.



  • @rustyoldguy Das ist überflüssiges Verschieben.



  • @rustyoldguy sagte in Lottozahlen ohne Wiederholung:

    Hab mal ein Verschlüsselungsproggi geschrieben

    Hast du deine Variablennamen verschlüsselt? 😉

    slei, wohin, dmog, sleia, finito, setzeog, genzuza, zuzaar
    


  • Ächz... zu viele gotos. Das geht besser.



  • und die globale variable kann man auch durch eine statische ersetzen.



  • @rustyoldguy sagte in Lottozahlen ohne Wiederholung:

    Warum orientierst du dich nicht am "Original?"

    Weil das gezeigte "Original" ganz fürchterlicher Quellcode ist und mit C++ schonmal gar nichts zu tun hat? Vor 20 Jahren hat man das vielleicht so umgesetzt, aber in der heutigen Zeit ist das die Sorte Quellcode, die man nicht weiter verbreiten sollte.


  • Mod

    @It0101 sagte in Lottozahlen ohne Wiederholung:

    Weil das gezeigte "Original" ganz fürchterlicher Quellcode ist und mit C++ schonmal gar nichts zu tun hat? Vor 20 Jahren hat man das vielleicht so umgesetzt, aber in der heutigen Zeit ist das die Sorte Quellcode, die man nicht weiter verbreiten sollte.

    Vor 20 Jahren war Jahrtausendwende 😮
    Mach 60 Jahre draus, dann bist du ungefähr bei der Zeit wo solcher Code noch halbwegs akzeptabel war. "Go To Statement Considered Harmful" ist von 1968!



  • @SeppJ sagte in Lottozahlen ohne Wiederholung:

    @It0101 sagte in Lottozahlen ohne Wiederholung:

    Weil das gezeigte "Original" ganz fürchterlicher Quellcode ist und mit C++ schonmal gar nichts zu tun hat? Vor 20 Jahren hat man das vielleicht so umgesetzt, aber in der heutigen Zeit ist das die Sorte Quellcode, die man nicht weiter verbreiten sollte.

    Vor 20 Jahren war Jahrtausendwende 😮
    Mach 60 Jahre draus, dann bist du ungefähr bei der Zeit wo solcher Code noch halbwegs akzeptabel war. "Go To Statement Considered Harmful" ist von 1968!

    Ich habe mit C bzw. C++ erst um die Jahrtausendwende angefangen, daher kann ich nicht bewerten was vorher war. Das "GOTO" mies ist, habe ich aber schon im Informatikunterricht anfang der 90er gelernt, weil das auch in Pascal ne blöde Idee war 😉



  • Hab ich ja wirklich 1995 geschrieben.
    Kann man aber sehr sehr sehr leicht umschreiben.
    Der Hacken ist eben, bei nur 6 aus 49 Zahlen kann man fast jedes System nutzen, um das
    ohne Wiederholung hinzukriegen. Wenn man aber per Transposition chiffrieren will und
    Zahlenreihen bis 13072 permutieren will, bleibt dir nur eine Kürzung eines bereits mit Zahlen
    befüllten Arrays übrig bei einer Permutierung einer Zahlenreihe wie die zuletzt genannte,
    kann das schon einige Minuten dauern. Mal abgesehen davon das die Zufallszahlengenerierung
    Systemabhängig ist. Bei Windows-Systemen liegt diese bei 0 bis 32767. Bei Linux-Systemen ist
    die Grenze bei etwa 200 000. Blöderweise kann man dabei keinen "Fortschrittsbalken" programmieren,
    da so lange die Z-Zahlen generiert werden, nichts am Terminal dargestellt werden kann.

    Aber die Sache mit dem Array und dem Kürzen wird zum Beispiel wichtig, wenn du Kartenspiele
    programmierst. Eine Karte die man ausgegeben hat, ist eben nicht mehr im Talon. Wenn du
    zum Beispiel Tarot nimmst, etwa Raider Wait Lenormand oder wie sie alle heißen, wie willst du sicher
    gehen, das eine Karte nicht mehr als einmal ausgegeben wird?

    Angeblich sollen ja auch gewisse amerikanische Geheimdienste die Finger drinn gehabt haben, als man
    festgelegt hat, auf welche Weise eine Zufallszahl generiert wird. Denn ab einer bestimmten Menge wird sich
    eine Wiederholung einstellen. Ist diese Spanne bisa zur Wiederholung zu groß, wetzen sich die Schlapphüte daran mal die Zähne ab.

    Übrigens gab es in Pascal das GOTO nicht. Der Programmrer sollte zu Objektorientierten Denken angeregt werden.
    Das Goto zu umgehen, sollte für jeden ein Kinderspiel sein.

    Ich war blos zu faul, das ganze vorher zu tun. Das überlasse ich den andern. Warum die Lösung gleich auf
    dem Silbertablett präsentieren?



  • @rustyoldguy
    Dein Code ist Schrott, einfach nur Schrott. Auch aus didaktischer Sicht.