dynamische Schleife - geht das?



  • Hallo,

    ich habe in meinem Programm aktuell drei Schleifen

    #include <iostream>
    using namespace std;
    
    int main()
    {
    int a, b, c, p;
    for (a=0; a<=100; a+=10){
    for (b=0; b<=100; b+=10){
    for (c=0; c<=100; c+=10){
    p=a+b+c;
    if (p==100){
    // mach was...
    } // Ende if
    } // Ende c
    } // Ende b
    } // Ende a
    return 0;
    }
    

    Wenn ich jetzt noch eine vierte oder fünfte Variable hinzufügen möchte dann muss ich weitere Schleifen manuell einbauen. Ist es möglich sowas dynamisch zu machen? Also am Beginn die Anzahl der Schleifen festlegen und dann arbeitet das Programm genau so viele Schleifen durch.

    /Andi



  • Ja! Es gibt verschiedene Moeglichkeiten, das zu erreichen. Aber es wird sehr selten gebraucht, da es bessere Wege gibt, das spezifische Problem zu loesen.



  • [hier stand Mist :P]
    Willst du eine weitere Variable haben, mit der geloopt werden soll, musst du eine neue Schleife einbauen. Du kannst aber jederzeit schauen, was denn eigentlich genau passiert, wenn du die Schleifen ausschreibst, und entsprechend Code ohne Schleifen oder mit weniger Schleifen erstellen. Aber solange du nicht dein konkretes Problem schilderst, kann man dazu wenig sagen.



  • @knivil
    Und was wären das für Wege?

    @l'abra d'or
    Das verstehe ich jetzt nicht ganz. Wieso ist der Code sinnfrei und was wieso soll ich die Schleifen weglassen?
    Ich möchte am Ende eine Liste mit Werten die in Summe 100 ergeben. Bsp

    A. B. C.
    0 0 100
    0 10 90
    0 20 80
    ...
    0 100 0
    10 0 90
    10 10 80
    ...
    

    Diese Werte benötige ich um damit weiterzurechnen. Das funktioniert auch soweit. Jetzt ist es eben so dass es mal drei und mal mehr Variablen sind. Deswegen die Frage.

    /Andi



  • also eine art bruteforce!?

    kann man mittels rekursion lösen oder mit einem array.
    wenn man nur werte umsortieren möchte, ist evtl. std::next_permutation das, was du gesucht hast...

    bb



  • Beispiel für 4 verschachtelte Schleifen, kann mühelos auf beliebig viele Schachtelungen erweitert werden.

    #include <vector>
    #include <numeric>
    #include <algorithm>
    
    using namespace std;
    
    void mach_wat()
    {
       // Enthält den maximalen Index für jeden Zähler
       vector<unsigned int> MaxIndex( 4,0 );
       MaxIndex[0] = 100;
       MaxIndex[1] = 100;
       MaxIndex[2] = 100;
       MaxIndex[3] = 100;
    
       // Enthält den aktuellen Index für jeden Zähler
       vector<unsigned int> CurrentIndex( 4,0 );
    
       while( CurrentIndex[3] < MaxIndex[3] )
       {
          // Summe aller Indizes bilden
          unsigned int p = accumulate( CurrentIndex.begin(), CurrentIndex.end(), 0 );
    
          // Index erhöhen. Dazu wird der kleinste Index um 1 erhöht und geprüft,
          // ob dabei ein Überlauf auftritt. Wenn ein Überlauf auftritt werden alle
          // vorherigen Indizes auf 0 zurückgesetzt und der nächstgrößere Index um
          // 1 erhöht. Dabei muss wieder geprüft werde, ob bei diesem Index auch 
          // ein Überlauf auftritt.
          unsigned int IncIndex = 0;
          while( IncIndex < 4 )
          {
             if( ++CurrentIndex[IncIndex] == MaxIndex[IncIndex] )
             {
                // Überlauf hat stattgefunden, Stellenindex erhöhen und alle
                // kleineren Indizes auf 0 zurücksetzen
                fill( CurrentIndex.begin(), CurrentIndex.begin() + (++IncIndex), 0 );
             }
             else
             {
                break;
             }
          }
       }
    }
    

    Edit:
    Terminierungsbedingung korrigiert



  • wow, vielen Dank. Ich habe zwischenzeitlich eine Lösung für VBA gefunden, welche in Excel auch funktioniert. Diese wollte ich heute Abend dann mal in c++ umsetzten.

    Wobei die Lösung von DocShoe deutlich kürzer ist! Werde mir beide Varianten zu Hause mal genauer anschauen. Nochmals vielen Dank!

    Option Explicit
    
    Const n = 9             'Anzahl der Elemente (=Summanden)
    Const ziel = 100        'Zielsumme, die du mit n Summanden erreichen willst
    Const intervall = 10     '"Schachtelung" der Summanden
    Const min_Wert = 0      'fragwürdiges Zusatzfeature, entspricht aber deiner urspr. Anforderung:
                            '0=>Lösungen dürfen für Elemente den Wert 0 beinhalten (sollte IMHO 1 sein)
    Const DUP = True
                            'false=nur eindeutige Mengenverteilungen, Position d. Elemente irrelevant
                            '{E1=5, E2=10} == {E1=10, E2=5} => nur ein Eintrag bei (n=2, ziel=15, intervall=5)
    Dim zz, m               'lfd. Zeile für Ergebniseintrag
    Dim x()                 'dyn. Array zum Speichern der einzelnen Lösung
    Dim zeit As Date
    
    Sub main()
        Sheets("Rekursion").Activate
        zeit = Now()
        'Prüfen, ob die gewählten Parameter Lösungen zulassen...
        If ziel Mod intervall <> 0 Then
            MsgBox "Zielsumme nicht durch Intervall teilbar!", , "Abbruch"
            Exit Sub
        End If
        If n * intervall * min_Wert > ziel Then
            MsgBox "Zu viele Elemente oder zu grosses Intervall!", , "Abbruch"
            Exit Sub
        End If
        Cells.Clear                 'Datenbereich leeren
        ReDim x(n - 1)              'Ergebnisarry dimensionieren
        Call neue_Kolonnen(True)    'erste Überschriften eintragen
        Call eb(1, 0)               'Rekursion starten
        MsgBox ("Lösungen insgesamt: " & 65355 * m + zz - 2 & " => " & zz & vbNewLine & Format(Now() - zeit, "HH:MM:SS ms"))
    End Sub
    
    Sub eb(ENr, ZwSumme)
        Dim Wert, bis_Wert
        If ENr = n Then
            x(ENr - 1) = ziel - ZwSumme
            If DUP = False And x(ENr - 1) < x(ENr - 2) Then Exit Sub
            Range(Cells(zz, 1 + n * m + m), Cells(zz, n + n * m + m)) = x
            zz = zz + 1
            If zz = 65357 Then Call neue_Kolonnen(False)
        Else
            bis_Wert = (ziel - ZwSumme) / intervall
    
            For Wert = min_Wert To bis_Wert
                x(ENr - 1) = Wert * intervall
                If ENr > 1 And DUP = False Then
                    If x(ENr - 1) >= x(ENr - 2) Then Call eb(ENr + 1, ZwSumme + Wert * intervall)
                Else
                    Call eb(ENr + 1, ZwSumme + Wert * intervall)
                End If
            Next
        End If
    End Sub
    
    Sub neue_Kolonnen(ErstesMal As Boolean)
        Dim i As Integer
        zz = 2
        If ErstesMal Then
            m = 0
        Else
            m = m + 1
        End If
        For i = 1 To n
            Cells(1, i + m * n + m) = "E" & i
        Next
    End Sub
    


  • sowosamma schrieb:

    int main()
    {
      int a, b, c, p;
      for (a=0; a<=100; a+=10){
        for (b=0; b<=100; b+=10){
          for (c=0; c<=100; c+=10){
            p=a+b+c;
            if (p==100){
              // mach was...
            } // Ende if
          } // Ende c
        } // Ende b
      } // Ende a
      return 0;
    }
    

    Das geht per Rekursion ganz schön. Auch kannst Du Dir viele unnütze Iterationen sparen, indem Du nicht bei jeder Schleife bis 100 gehen musst und die letztee Schleife eliminiert werden kann...

    Skizze:

    void ausgabe(int laenge, const int was[]);
    
    void dings(int laenge, int rest_summe, int speicher[], int position = 0)
    {
      if (position==laenge) {
        if (rest_summe==0) {
          // machwas
          ausgabe(laenge,speicher);
        }
      } else {
        ...
      }
    }
    
    int main()
    {
      const int laenge = 4;
      int s[laenge];
      dings(laenge,100,s);
    }
    

    Mehr verrat ich erstmal nicht. 🙂



  • dank Eurer Hilfe habe ich es glaube ich hinbekommen. Mein Code sieht jetzt so aus:

    #include <iostream>
    
    using namespace std;
    
    int n = 4;
    int ziel = 100;
    int interval = 10;
    int anzahl = 0;
    
    //Funktionen
    void rekursion (int position, int rest_summe, int x[])
    {
    
         int bisWert, wert;
    
         if (n == position)
         {
               x[position - 1] = ziel - rest_summe;
               anzahl++;
               for (int i=0; i<n; i++)
               {
                   cout << x[i] << " "; 
                   }
               cout << endl;
            } // if TRUE
         else
         {
             bisWert = (ziel - rest_summe) / interval;
             for (wert=0; wert <=bisWert; wert++)
             {
                 x[position-1] = wert * interval;
                 rekursion(position+1, rest_summe + wert * interval, x);
                 } // Ende wert
             } // if FALSE
         } // Funktionsende
    
    //Hauptprogramm     
    int main()
    {
        int speicher[n];
        rekursion(1, 0, speicher);
        cout << anzahl << endl;
        system("pause");
    }
    

    Das ist für 2 Wochen c++ Kenntnisse doch nicht schlecht? Vielen Dank nochmal!

    /Andi


Log in to reply