Pascalsches Dreieck im Programm ausgeben


  • Mod

    Du kannst den Kladderadatsch mit der Zeichenbreite auch weglassen:

    #include <stdio.h>
    #include <stdlib.h>
    
    unsigned long long fak(unsigned n)
    {
      unsigned long long result = 1;
      unsigned i = 1;
      for (; i <=n; ++i)
        result *= i;
      return result;
    }
    
    unsigned binomial(unsigned n, unsigned k)
    {
      return fak(n)/(fak(k)*fak(n-k));
    }
    
    int main ()
    {
      unsigned max_n;
      puts("Maximales n (0-20)? ");
      if (scanf("%ud", &max_n) == 1)
        {
          if (max_n > 20)  // Zahlen werden zu groß
            exit(1);
    
          const int zeichenbreite = 7; // Maximale Breite einer Zahl bei n=20
    
          unsigned n, k;
          for(n = 0; n <= max_n; ++n)
            {
              // Whitespace vorne
              int num_whitespace = ((max_n)/2. - n/2.)*zeichenbreite;
              int i;
              for(i = 0; i < num_whitespace; ++i)
                putchar(' ');
    
              // Eigentliche Werte
              for(k = 0; k <= n; ++k)
                {
                  printf("%7u ", binomial(n,k));
                }
              putchar('\n');
            }
        }
    
      return 0;
    }
    

    Dann sieht es bei 4 eben nicht mehr so:

    1 
       1 1 
      1 2 1 
     1 3 3 1 
    1 4 6 4 1
    

    sondern so aus:

    1 
                    1       1 
                 1       2       1 
             1       3       3       1 
          1       4       6       4       1
    

    Nun verständlicher?

    Im Programm oben waren übrigens noch ein paar Schönheitsfehler drin, an den Grenzen hätte es >= statt >= heißen müssen:

    if (max_n > 20)  // Zahlen werden zu groß
            exit(1);
          else if (max_n == 20)
            zeichenbreite = 7;
          else if (max_n >= 16)
            zeichenbreite = 6;
          else if (max_n >= 13)
            zeichenbreite = 5;
          else if (max_n >= 9)
            zeichenbreite = 4;
          else if (max_n >= 5)
            zeichenbreite = 3;
          else
            zeichenbreite = 2;
    


  • Hmmm mit überlegen verstehe ich einiges vielleicht doch. ZUm Beispiel kannte ich auch result = i noch nicht bin jetzt aber drauf gekommen, das das result= resulti heißen muss.

    Und Zeichenbreite ich glaube ich die Anzahl der Spalten, die freigehalten werdens ollen für die Zahlen im DReieck, da die Zahlen größer werden, je größer max_n ist, muss die Zeichenbreite größer werden damit es noch ein Dreieck bleibt...oder?
    Ich verstehe dann aber diese Zeile überhaupt nicht

    char formatstring[6];
    sprintf(formatstring, "%%%uu ", zeichenbreite - 1);

    Ich glaube jetzt verstehe ich fast alles bis auf diese 2 Zeilen....und die Zeile wo im printf befehl nochmal formatstring vorne steht, aber das müsste auch an disen zwei zeilen liegen, was heißt denn hier formatstring[6]
    wäre echt seeeeehr nett wenn du mir diese zwei zeilen noch erklären könntest.
    Und nochmal tauuuusend DaNK!



  • scanf() gibt die Anzahl der gelesenen Werte zurück.
    Man kann mit scanf auch mehrere verschiedene Daten einlesen.

    Nachzulesen z.B hier: http://www.cplusplus.com/reference/clibrary/cstdio/scanf/
    Die Seite kannst du dir gleich merken, da dort auch die anderen Funktionen der C-Standardlibrary erklärt werden.
    ⚠ Das ist eigentlich für C++, die Besonderheiten bei C werden aber erklärt.



  • Ach du hattest in zwischen schon geantwortet, 🙂 DANKESEHR!!!


  • Mod

    xx_20 schrieb:

    . Und dass du oben scanf gleich mit in der if-Bedingung hast, kann ich auch nicht verstehen, sowas sehe ich auch das erste mal und ich habe versucht das so auseinanderzunehmen:
    scanf("%ud", &max_n);
    if(max_n == 1)

    Das hast du auch völlig falsch interpretiert. Guck dir scanf an:
    http://www.cplusplus.com/reference/clibrary/cstdio/scanf/

    Return Value
    On success, the function returns the number of items successfully read. This count can match the expected number of readings or fewer, even zero, if a matching failure happens.
    In the case of an input failure before any data could be successfully read, EOF is returned.

    Es wird also geprüft, ob der eingegebene Wert überhaupt eine Zahl war. Nix mit dem Wert von max_n. Warum sollte ich den Wert von max_n überhaupt auf Gleichheit mit 1 prüfen?

    ich komme irgendwie überhaupt nicht klar mit diesem Programm, verstehe zum Beispiel auch überhaupt nicht was die Zahlen für die "Zeichenbreite" sein sollen.

    Bei größeren max_n werden die Zahlen unten im Dreieck immer länger. Damit das Dreieck noch Dreieckig bleibt, muss man entsprechend mehr Füllzeichen einfügen. Die Grenzen 5,9,13,16,19 habe ich durch ausprobieren bestimmt. Vergleiche die Ausgaben meiner vereinfachten Version und der Version mit der variablen Zeichenbreite. Sieht doch viel besser aus mit variabler Zeichenbreite.

    Und das Schlimme ist, das ich mit diesem "Zwischenergebnis" bei meinem Tutor war, bevor ich es hier reingepostet habe, und er mir gesagt hat, dass die Schleifen so richtig sind, und ich mir da als BEdingung nur noch was anderes überlegen muss, damit ich das Dreieck hinbekomme. Und für den Fall, dass ich das richtig hinbekomme hatte ich schon den Inhalt des Dreieck "programmiert", wo er auch meinte es sei richtig und wenn ich diese BEdingung richtig bestimme wäre mein Programm fertig 😞

    Nun, kommt erst einmal drauf an, ob dein Tutor überhaupt Ahnung hat. Wie genau hast du deine Schleifen denn gemacht? Es sollte auf jeden Fall der "Trick" auftauchen, k von 0 bis n laufen zu lassen. Sonst hast du keine Chance, auf das Dreieck zu kommen. Oder du bekommst ein Dreieck dieser Form:

    1
    1 1
    1 2 1
    1 3 3 1
    1 4 6 4 1
    

    Das geht auch mit vereinfachter Bedingung:

    #include <stdio.h>
    #include <stdlib.h>
    
    unsigned long long fak(unsigned n)
    {
      unsigned long long result = 1;
      unsigned i = 1;
      for (; i <=n; ++i)
        result *= i;
      return result;
    }
    
    unsigned binomial(unsigned n, unsigned k)
    {
      if (k>n) return 0;   // ACHTUNG: Hier auch geändert!
      return fak(n)/(fak(k)*fak(n-k));
    }
    
    int main ()
    {
      unsigned max_n;
      puts("Maximales n (0-20)? ");
      if (scanf("%ud", &max_n) == 1)
        {
          if (max_n > 20)  // Zahlen werden zu groß
            exit(1);
    
          unsigned n, k;
          for(n = 0; n <= max_n; ++n)
            {
              for(k = 0; k <= max_n; ++k)  // Dann muss man hier nicht denken
                {
                  unsigned wert = binomial(n,k);
                  if (wert > 0) printf("%7u ", wert);
                }
              putchar('\n');
            }
        }
    
      return 0;
    }
    

    Wenn es ein schönes Dreieck werden soll, dann findest du die Erklärung in meinem Beitrag ganz oben und die konkrete Umsetzung als Code dieser Bedingungen hast du auch gezeigt bekommen.



  • sprintf(formatstring, "%%%uu ", zeichenbreite - 1);
    

    Da steht erstmal %% . Das schreibt ein % in den Formatstring.
    Das %u danach schreibt den entsprechenden Paramter als vorzeichenlose Dezimalzahl in den Formatstring. Ist zeichenbreit z.B. 4 dann wird in formatstring eine 3 geschrieben.
    Das u und das Leerzeichen werden übernommen.
    Also steht in Formatstring ein "%3u ".
    Damit wird dann das printf "gefüttert".

    Für solche Fälle gibt es aber den * im Formatstring.

    printf("%*u ", zeichenbreite - 1, binomial(n,k));
    

  • Mod

    xx_20 schrieb:

    Ich verstehe dann aber diese Zeile überhaupt nicht

    char formatstring[6];
    sprintf(formatstring, "%%%uu ", zeichenbreite - 1);

    Ich glaube jetzt verstehe ich fast alles bis auf diese 2 Zeilen....und die Zeile wo im printf befehl nochmal formatstring vorne steht,

    Dann schauen wir nochmal in das vereinfachte Beispiel:

    printf("%7u ", binomial(n,k));
    

    Da gibt die 7 (wie du in einer printf-Referenz nachlesen kannst) die Zahl der Zeichen an, auf die die Ausgabe aufgefüllt werden soll. Das mache ich, wie du richtig erkannt hast, weil die Zahlen unten breiter werden, die Ausgabe der Zahlen oben aber genau so breit sein muss wie unten, damit das Dreieck schön wird.

    So, nun ist diese breite aber eine Variable. ich will also bei max_n = 20 da
    "%7u " stehen haben und bei max_n=4 will ich "%2u " als Formatstring. Ich kann aber natürlich nicht

    printf("%"zeichenbreite"u ", binomial(n,k));
    

    schreiben. Das ist Unsinn, so funktioniert das nicht. Also muss ich mir einen Formatstring basteln. Der Formatstring von printf muss nicht etwas sein, dass man beim Programmieren fest einbaut, das kann irgendeine Zeichenkette (d.h. in C ein Array von chars) sein, zum Beispiel mein char formatstring[6]. Den muss ich nun noch mit dem passenden Inhalt füllen. sprintf ist wie printf, aber anstatt auf die Standardausgabe schreibt es in eine Zeichenkette, hier den Formatstring. Ansonsten ist alles genau gleich wie bei printf. Nun will ich also die oben genannten Zeichenketten in den Formatstring schreiben. Doch Ohweh! Wie gibt man mit printf ein Prozentzeichen aus? Wieder in die Referenz geguckt: Ach, mit %%. Somit können wir den Befehl auseinandernehmen:

    sprintf(formatstring, "%%%uu ", zeichenbreite - 1);
    

    Da steht:
    Schreibe an die Stelle von formatstring folgedes: Ein Prozenzeichen, dann eine vorzeichenlose Zahl, dann ein Leerzeichen. Und dann noch die Angabe, dass die vorzeichenlose Zahl den Wert zeichenbreite-1 haben soll. Und somit hat formatstring hinterher den Inhalt den ich möchte und ich kann dies als Formatstring für das printf benutzen.


  • Mod

    DirkB schrieb:

    Für solche Fälle gibt es aber den * im Formatstring.

    printf("%*u ", zeichenbreite - 1, binomial(n,k));
    

    :p Ich sollte mir meinen Rat zu Herzen nehmen und selber öfters in die Referenz gucken. Das ist so natürlich viel einfacher. Hatte mich schon gewundert, dass dies so kompliziert sein sollte in C.



  • Im K&R gabs das auch noch nicht 😃

    @xx_20
    K&R bezeichnet das Ur-C, das in dem Buch "The C Programming Language" (deutsch: Programmieren in C) von Kernighan und Ritchie beschrieben wird.


  • Mod

    DirkB schrieb:

    @xx_20
    K&R bezeichnet das Ur-C, das in dem Buch "The C Programming Language" (deutsch: Programmieren in C) von Kernighan und Ritchie beschrieben wird.

    Übrigens das Lehrbuch für C überhaupt. Zumindest in der englischen Originalausgabe die ich kenne, die deutsche Übersetzung ist angeblich nicht so gelungen. Aber zum Programmieren muss man sowieso Englisch fließend lesen können. solltest du dir vielleicht mal besorgen, wenn du Hilfe brauchst.



  • Ihr könnt euch nicht vorstellen, wie glücklich ihr mich gemacht hab. Ich danke euch seeeehrvom HErzen!!!!!!!!!!!!!!
    Und jetzt verstehe ich sogar alles in dem Programm! 🕶

    👍


Anmelden zum Antworten