0

I have a nested loop to find all possible combinations of numbers between 1 and x in groups of 4, where a < b < c < d.

A method is called as each group is discovered to do a simple equivalency test on the sum of those numbers.

The loop does work and produces expected output (1 set of numbers for this particular x), however it takes 12+ seconds to find this answer and another ~5 to test the remaining possibilities, which is definitely bad, considering the x values are < 1000.

I tried having the outer loop iterate a < x - 3 times, the b loop b < x - 2 times, down to d < x times which didn't make a noticeable difference.

What would be a better approach in changing this loop?

for (a = 1; a < x; a++) {
    for (b = a + 1; b < x; b++) {
        for (c = b + 1; c < x; c++) {
            for (d = c + 1; d < x; d++) {
                check(a, b, c, d);
            }
        }
    }
}
clearlight
  • 12,255
  • 11
  • 57
  • 75
  • 2
    How many combinations do you think you are checking? How much time do you think is a reasonable amount of time for each combination check to take? – David Schwartz Jan 09 '17 at 05:07
  • 2
    Are you just trying to calculate the sum of every set of four numbers? I think it might help if you explained a bit more about what you're trying to do here. I would imagine most of the optimization opportunities lie in the `check()` function, which you haven't revealed here. – r3mainer Jan 09 '17 at 05:20
  • The entire loop should run in under a second. As for combinations I have put a print statement in the loop and I believe i'm getting the correct number with any given x. I can add the method code if necessary but it's checking whether a+b+c+d && a * b * c * d == x, and printing those numbers if true. – user6824588 Jan 09 '17 at 05:25
  • You want this to run in under a second? If x is 1000, then the inner loop will iterate (999 choose 4) times, which is over 41 billion unless I'm mistaken. You're going to need a very fast processor to do that. – r3mainer Jan 09 '17 at 05:33
  • If you want your computation to run in less than 1 second then in all likelihood you need an approach altogether different from enumerating and testing every possible combination. – John Bollinger Jan 09 '17 at 05:57
  • If your code is working but you want to improve it, perhaps asking on [codereview.se] is in order. – StoryTeller - Unslander Monica Jan 11 '17 at 11:16
  • what i understood, lets say your number is 'abcd' 4 digit. one case would be a789 where 'a' must be less than 7. in this scenario you have 1789 2789 3789 4789 5789 6789. this is how you want to find combinations ? right ? – Abdullah Raza Jan 11 '17 at 12:56
  • What do `check()`? Because your algorithm can't be optimize. Your algo is O(1) by result you can't be better. You have something like O(n^3 * 2) result. For n = 1000, you have something like `2 000 000 000` result. – Stargateur Jan 11 '17 at 14:29
  • Btw., one could probably *synthesize* 4 multiplicands by doing a prime factorization with 4 or more prime factors and then combining the prime factors to create 4 multiplicands. The problem is that prime factorization is slow, so chances are that your related problem is a computational "hard" one as well. A lot of cryptography depends on that. – Peter - Reinstate Monica Jan 11 '17 at 14:55
  • Please read about backtracking: https://en.wikipedia.org/wiki/Backtracking#Constraint_satisfaction . This is a standard problem for the algorithm. And it's taught in the first year of college (or maybe the second year, can't remember). – Cosmin Jan 11 '17 at 15:24

1 Answers1

2

With such a deep level of nesting, any early exit you can introduce - particularly at the outer loops - could net big gains.

For example, you write that check is testing a + b + c + d == x && a * b * c * d == x - so you can compute the intermediate sum and product, and break when you encounter numbers that would make any selection of later numbers impossible.

An example:

for (a = 1; a < x; a++) {
  for (b = a + 1; b < x; b++) {
    int sumAB = a + b;
    if (sumAB + sumAB > x) break;
    int prodAB = a * b;
    if (prodAB * prodAB > x) break;
    for (c = b + 1; c < x; c++) {
      int sumABC = sumAB + c;
      if (sumABC + c > x) break;
      int prodABC = prodAB * c;
      if (prodABC * c > x) break;
      for (d = c + 1; d < x; d++) {
        int sumABCD = sumABC + d;
        if (sumABCD > x) break;
        if (sumABCD != x) continue;
        int prodABCD = prodABC * d;
        if (prodABCD > x) break;
        if (prodABCD != x) continue;
        printf("%d, %d, %d, %d\n", a, b, c, d);
      }
    }
  }
}

This is just an example - you can constrain all the checks here further (e.g. make the first check be sumAB + sumAB + 3 > x). The point is to focus on early exits.

I added a counter for each loop, counting how many times it was entered, and tried your version and my version, with x = 100. My version has orders of magnitude less loop entries:

No early exits: 99, 4851, 156849, 3764376
With early exits: 99, 4851, 1122, 848

Oak
  • 26,231
  • 8
  • 93
  • 152
  • Actually, 6 or 7 orders of magnitude. – Peter - Reinstate Monica Jan 11 '17 at 14:37
  • 1
    Btw, you should not `continue` but `break` if one of the conditions is met because all subsequent sums and products generated in that branch will be larger than the partial sum or product, so that the branch can be pruned right there. *That* runs a million times faster. The beauty is that the pruning's benefits will grow with x (i.e. when they are needed most) because almost all combinations are too large. – Peter - Reinstate Monica Jan 11 '17 at 14:39
  • @Stargateur do you know for which x it's supposed to show a result? I don't, which indeed makes it difficult to test. The problem is similar to [perfect numbers](https://en.wikipedia.org/wiki/Perfect_number) but not identical, and I couldn't find any reference online of possible answers. – Oak Jan 11 '17 at 14:40
  • @PeterA.Schneider Whoops of course you are right, fixed. Required being a bit more verbose in the innermost loop. – Oak Jan 11 '17 at 14:44
  • You inlined the `check()` :-). – Peter - Reinstate Monica Jan 11 '17 at 14:46
  • @Oak My bad, I didn't understand that the OP say what do `check()` in the comment... The OP should add this in the **question** not in comment... Well never mind. – Stargateur Jan 11 '17 at 14:46