Schleifen in Schleifen



  • Hallo,
    wie kann ich das realisieren? Ich möchte n Schleifen in Schleifen haben:

    // n soll angeben, wie oft die Schleifen verschaltet werden:
    // in diesem Fall wäre n = 3
    for ( blablabla )
       for ( blablabla )
          for ( blablabla )
    

    Ich habe mir gedacht ich mache eine sich selbst aufrufende Funktion (blablabla
    ist immer gleich, aber es muss immer eine andere Zählervariable verwendet werden!).

    Vielen Dank!



  • D schrieb:

    Ich habe mir gedacht ich mache eine sich selbst aufrufende Funktion

    Rekursion wäre auch mein Vorschlag gewesen.



  • Ich finde mit Rekursion lässt es sich am einfachste lösen.

    void foo( int n, ... )
    {
        for( ...... )
        {
            if( n == 0 )
                return;
            else
                foo( n - 1, ... );
        }
    
        return;
    }
    


  • Aber wenn die Schleifenvariable immer gleich heißt kann ich in den Schleifen
    nicht richtig mit ihnen arbeiten, oder???



  • Du mußt dir für jede Ebene eine Zählvariable speichern (am besten in einem Array):

    const int MAXLOOP = 10; // highest number of loops
    int r[MAXLOOP], lower[MAXLOOP], upper[MAXLOOP];
    int maxloop=0;
    
    void action()
    {
      for(int i=0; i<maxloop; i++)
        cout << r[i] << ',';
      cout << endl;
    }
    
    void loopfunc(int n)
    {
      if(n==0) action();
      else
      {
        n--; // use as index
        for(r[n] = lower[n]; r[n] <= upper[n]; r[n]++)
          loopfunc(n);
      }
    }
    
    void do_loop(int n)
    {
      maxloop = n;
      loopfunc(n);
    }
    
    // Aufruf
    //
    // todo: initialize lower[x] and upper[x] for 0 <= x < n
    do_loop(n);// 0 <= n <= MAXLOOP
    

    Jetzt kann man das ganze auch noch objektorientiert verpacken und eine eigene Callback-Funktion (action) angeben und ...



  • Th, ich glaube, er hat nicht so dass Programmierverständnis, deinen Code wird er erst nicht kapieren bzw. zu kompliziert finden.

    Ich hab dir hier was einfaches zusammengeschrieben:
    Wenn du's nicht verstehst, frag nach!

    #include <iostream>
    
    using namespace std;
    
    void function(int n);
    
    int main()
    {
    
    	int n=8; //Anzahl d. Schleifendurchläufe
    	function(n);
    
    	//system("pause");
    	return 0;
    }
    
    void function(int n)
    {
    	if(n>0)
    	{
    		for(int i=0; i<1;i++)
    		{
    		cout << n << " bla\n";
    		}
    		n--;
    		function(n);
    	}
    
    }
    


  • Ich kenne zur Lösung dieses Problems auch einen einfachen Zähler. Denn was man im Endeffekt da benötigt ist nichts anderes als alle Permutationen von n Zahlen bzw Schleifenvariablen.

    class Perm
    {
      int* num;
    
      Perm(int n)
      {
        this->num = new int[n];
      }
    
      int SetNum(int i)
      {
        this->num[i]++;
        return this->num[i];
      }
    
      int SetNext()
      {
        int i = 0;
    
        while (SetNum(i) == 0)
          i++;
      }
    };
    

    In num[] steht dann die gewünschte Permutation im n-Schritt drin.



  • Ja, eine nette Methode, dir den RAM zu zerschießen 😃 Hast du das jetzt schnell hingeschmiert oder ist der Code ausgiebig getestet worden?

    @D: brauchst du im innersten Schleifenrumpf die Werte aller Schleifenvariablen? Wenn ja, mußt du mit einem Array (oder besser mit einem std::vector<>) arbeiten, ansonsten reicht eine lokale Zählvariable in der rekursiven Funktion.



  • Ich hab beizeiten mal ne Klasse Multicounter geschrieben um solche mehrfach geschachtelten Schleifen in einer zu haben. Es gibt zwei Konstruktoren, einen für die Dimension (gleich Anzahl der geschachtelten Schleifen) und einen gemeinsamen Maximalwert, den anderen für einen vector von Maximalwerten für die einzelnen Schleifen.
    Optional kann aus einem von vier Modi gewählt werden:
    LEFTRIGHT lässt den ersten counter bis zu seinem Maximalwert laufen, danach den zweiten usw.
    RIGHTLEFT fängt beim letzten counter an, lässt ihn zum maximalwert laufen, dann den vorletzten usw.
    EQUAL inkrementiert nacheinander den ersten, den zweiten, den dritten... bis zum letzten und fängt dann wieder von vorne an - counter die den Maximalwert erreicht haben, werden übersprungen.
    Auf die einzelnen counter kann über den Indexoperator[] zugegriffen werden, genau wie beim vector.

    template <typename T> class Multicounter {
    public:
      enum Modus {LEFTRIGHT, RIGHTLEFT, EQUAL, EQUAL_RL};
    
      Multicounter(std::size_t dim, T const& max, Modus mod = LEFTRIGHT)
        : counters_(dim, T()), maxcount_(dim, max), modus_(mod),
          //incrementation starts left or right
          nextinc_( ((mod == LEFTRIGHT) || (mod == EQUAL))? 0 : dim-1 ) 
      { if (max == T()) nextinc_ = size(); //maximum = 0, nothing to count}
    
      Multicounter(std::vector<T> const& maxvec, Modus mod = LEFTRIGHT)
        : counters_(maxvec.size(), T()), maxcount_(maxvec), modus_(mod),
          //incrementation starts left or right
          nextinc_( ((mod == LEFTRIGHT) || (mod == EQUAL))? 0 : dim-1 ) 
      { 
        if (counts_[nextinc_] == maxcount_[nextinc_]) {
          if (nextinc_ == 0) setNextInc();
          else setLastInc();
        }
      }
    
      Multicounter& operator++() {
        if (nextinc_ >= size()) return *this;
        ++(counters_[nextinc_]);
        switch(modus_) {
          case EQUAL:
            setNextInc();
            if (nextinc_ == size()) { //start from first index
              nextinc_ = 0;
              if(counters_[nextinc_] >= maxcount[nextinc_]) setNextInc();
            }
            break;
          case EQUAL_RL:
            setLastInc();
            if (nextinc_ == 0) { //start from last index
              nextinc_ = size()-1;
              if(counters_[nextinc_] >= maxcount[nextinc_]) setLastInc();
            break;
          case LEFTRIGHT:
            if (counters_[nextinc_] >= maxcount_[nextinc_]) setNextInc();
            break;
          case RIGHTLEFT:
            if (counters_[nextinc_] >= maxcount_[nextinc_]) setLastInc();
            break;
          default:
            throw std::out_of_range;
        }
        return *this;
      }
    
      const Multicounter operator++(int) {
        Mutlicounter tmp(*this);
        ++(*this);
        return tmp;
      }
    
      const T& operator[](std::size_t index) const {
        return counters_[index];
      }
    
      Mutlicounter max() const {
        Multicounter maxcnt(*this);
        maxcnt.counters_ = maxcount_;
        return maxcnt;
      }
    
      Multicounter size() const {
        return counters_.size()
      }
    
      bool equal(Multicounter const& b) const {
        return (maxcount_ == b.maxcount_ && counters_ == b.counters_);
      }
    
    private;
      void setNextInc() {
        //set nextinc_ to the next index that can be incremented or to size() 
        while (nextinc_ < size()-1 && counters_[++nextinc_] >= maxcount_[nextinc_]);
      }
      void setLastInc() {
        //set nextinc_ to the next SMALLER index that can be incremented or to size() 
        while (nextinc_ > 0 && counters_[--nextinc_] >= maxcount_[nextinc_]);
        if (nextinc_ == 0 && counters_[nextinc_] < maxcount_[nextinc_]) nextinc_ = size();
      }
    
      std::vector<T> counters_;
      vstd::ector<T> maxcount_;
      Modus modus_;
      std::size_t nextinc_;
    
    };
    
    template<typename T>
    bool operator==(Multicounter<T> const& a, Multicounter<T> const& b)
      {return a.equal(b);}
    template <typename T>
    bool operator!=(Multicounter<T> const& a, Multicounter<T> const& b)
      {return !(a == b); }
    

    Beispiel für die Benutzung:

    for(Multicounter<int> icnt(3,16); icnt != icnt.max(); ++icnt) {/*...*/}
    
    //entspricht:
    for (int i = 0; i <= 16; ++i)
      for (int j = 0; i <= 16; ++j)
        for (int k = 0; k <= 16; ++k) {/*...*/}
    //mit icnt[0] == i usw.
    
    std::vector<long int> iv;
    iv.push_back(14);
    iv.push_back(74);
    iv.push_back(34);
    iv.push_back(12);
    iv.push_back(124);
    iv.push_back(30);
    for(Multicounter<long int> jcnt(iv); jcnt != jcnt.max(); ++jcnt) {/*...*/}
    
    //entspricht:
    for (long int i = 0; i <= 14; ++i)
      for (long int j = 0; j <= 74; ++j)
        for (long int k = 0; k <= 34; ++k)
          for (long int l = 0; l <= 12; ++l)
            for (long int m = 0; m <= 124; ++m)
              for (long int n = 0; n <= 30; ++n) {/* ... */}
    
    //für Arrays gibts den iterator-range-konstruktor für vectoren:
    int maxarr[5] = {12, 23, 4, 4, 3};
    for(Multicounter<int> kcnt(std::vector<int>(maxarr, maxarr+5)); kcnt != kcnt.max(); ++kcnt) {/*...*/}
    

    Ich bin ncihtmehr genau sicher wie weit ich das Dingen damals getestet hab, beim drüberschauen sahs jetzt aber ganz in Ordnung aus.



  • Ja, die Klasse sieht super aus (nur getestet ist sie anscheinend nicht, denn es sind einige Syntaxfehler drin).



  • Ich hab jetzt mal die Syntaxfehler verbessert und ein wenig getestet - es hat sich rausgestellt dass ich da leider voelligen Humbug verzapft hab, die Zaehler werden nacheinander hochgezehlt, aber nicht wieder resettet, so dass lange nicht alle moeglichen Kombinationen abgegrast werden - werd mich da spaeter nochmal intensiver mit auseinandersetzen.



  • An CSToll:

    Hey, lass meine Code-Schweinerei in Ruhe. Das wurde alles mit Müh und Not sauber hingeschmiert. 😃

    Aber jetzt mal ernst, ich wollte da einfach nur ein Grundgedanke kurz zeigen. Das Ganze ist natürlich unvollständig und sollte, wenn es gebraucht wird, noch um so einige Dinge (obere und untere Grenzen von den einzelnen Variablen,...) erweitert werden. Aber ich habe die Idee schon mehrmals benutzt, beispielsweise bei der Berechnung des Lösungsraums von Mastermind oder auch bei dem Dameproblem.


Anmelden zum Antworten