4
#include <iostream>
using namespace std;

int main()
{
    int range = 20;
    int totalCombinations = 0;

    for(int i=1; i<=range-2; i++)
    {
        if(range>i)
        {
            for(int j=1; j<=range-1; j++)
                if(j>i)
                {
                    for(int k=1; k<=range-1; k++)
                        if(k>j)
                        {
                            for(int l=1; l<=range-1; l++)
                                if(l>k)
                                {
                                    for(int m=1; m<=range-1; m++)
                                        if(m>l)
                                        {
                                            for(int f=1; f<=range; f++)
                                                if(f>m)
                                                {
                                                    cout << " " <<i<< " " <<j<< " " <<k<< " " <<l<< " " <<m<< " " <<f;
                                                    cin.get(); //pause
                                                    totalCombinations++;
                                                }
                                        }
                                }
                        }
                }
        }
    }
    cout << "TotalCombinations:" << totalCombinations;
}
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
GENRO
  • 41
  • 1

4 Answers4

4
if(range>i)

Why not just start i at range and avoid the problem? Oh, I had that backwards, but the point stands -- you can easily refactor this to be part of the for condition. No need for an extra conditional.

if(j>i)

Why not just start j at i?

... (Repeat for the other two loops)

That gets rid of half your nesting. As far as the loops themselves go, I would suggest using Extract Method on them.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
3

The first thing you can do is using continue:

for(int i=1; i<=range-2; i++) {
{
    if(range<=i) {
       continue;
    }

    for(int j=1; j<=range-1; j++) {
        if(j<=i) {
           continue;
        }
        //etc for all inner loops
    }
}

This will greatly reduce nesting and IMO improve readability.

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • You can do this. Or you can eliminate the completely redundant IF test (as I spec'd...). +1 because this solves the problem more generally, but I think better solutions exist for this specific case. – Billy ONeal Jun 20 '11 at 08:18
  • At the cost of making the code unreadable and unmaintainable. The first this is what Billy ONeal said: eliminate the unnecessary `if`s. (For what it's worth, the first if will always be false.) – James Kanze Jun 20 '11 at 08:25
  • @James Kanze: I agree that those checks are unnecessary, but the refactoring I suggest can be done formally and it will make it easier to notice that those checks are unnecessary. – sharptooth Jun 20 '11 at 08:40
  • @sharptooth What refactoring. What you're proposing complicates the flow, and makes any analysis as to what is going on much more difficult. – James Kanze Jun 20 '11 at 08:43
  • @James Kanze: That's highly subjective. I personally find code with `continue` much easier to analyze. – sharptooth Jun 20 '11 at 08:46
  • @sharptooth It's not really that subjective. `continue` introduces additional control flows within the loop, which have to be analyzed for things like loop invariants, etc. And I've yet to see any literature explaining how to analyze them; all of the classical literature on program analysis only treats the case where you enter the loop at the top, and leave it at the end. – James Kanze Jun 20 '11 at 09:52
0

The same way you refactor anything. You first have to figure out what the code is doing. In this case, many of the tests are irrelevant, and each of the loops does basically the same thing. You've solved one very specific case (very sloppily) of a more general problem. Working out the general algorithm for the problem will result in a cleaner, simpler solution, and one that is more general. Something like this:

class Combin
{
    int m;
    int n;
    int total;
    std::vector<int> values;

    void calc();
    void dumpVector() const;
public:
    Combin( int m, int n ) : m(m), n(n), total(0) {}
    int operator()() { total = 0; values.clear(); calc(); return total; }
};

void 
Combin::calc()
{
    if ( values.size() == m ) {
        dumpVector();
        ++ total;
    } else {
        values.push_back( values.empty() ? 0 : values.back() + 1 );
        int limit = n - (m - values.size());
        while ( values.back() < limit ) {
            calc();
            ++ values.back();
        }
        values.pop_back();
    }
}

void
Combin::dumpVector() const
{
    for (std::vector<int>::const_iterator iter = values.begin(); iter != values.end(); ++ iter )
        std::cout << ' ' << *iter + 1;
    std::cout << '\n';
}

int main()
{
    Combin c( 6, 20 );
    std::cout << "TotalCombinations:" << c() << std::endl;
    return 0;
}

The only thing really worthy of comment in the above is the calculation of limit in calc, and that's really just an optimization; you could use n and get the same results (but you'd recurse a bit more).

You'll note that in your original versions, the end conditions of the loops are more or less arbitrary: using range systematically would work, or you could work out the formula which I use for limit (which would result in a different end condition for each loop.

Also, my code uses the half open intervals which are ubiquious in C and C++. I think that once you get used to them, you'll find them much easier to reason about.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

My C++ is rusty, so let me give you a C# example. Any number of nested loops can be replaced with just one, as follows:

    public void ManyNestedLoopsTest()
    {
        var limits = new[] {2, 3, 4};
        var permutation = new[] {1, 1, 0};
        const int lastDigit = 2;
        var digitToChange = lastDigit;
        while(digitToChange >= 0)
        {
            if (permutation[digitToChange] < limits[digitToChange])
            {
                permutation[digitToChange]++;
                digitToChange = lastDigit;
                PrintPermutation(permutation);
                continue;
            }
            permutation[digitToChange--] = 1;
        }
    }

    private void PrintPermutation(int[] permutation)
    {
        for(int i=0;i<3;i++)
        {
            Console.Write(permutation[i]);
            Console.Write(" ");
        }
        Console.WriteLine(" ");
    }
Arne Lund
  • 2,366
  • 3
  • 26
  • 39