Total of all numbers from 1 to N will always be zero

You could also use recursion here. Just remember your current integer, your max integer, your current sum and some kind of history of operations (could also be your final sequence). In every level you proceed the path in two dirdctions: adding to your sum and substracting from it.

I did a quick implementation in Python, but it should be easy to transfer this to Java or whatever you are using.

def zero_sum(curr, n, seq, sum):
    if curr == n and sum == 0:
        print(seq)
    elif curr < n:
        zero_sum(curr + 1, n, seq + " - " + str(curr + 1), sum - (curr + 1))
        zero_sum(curr + 1, n, seq + " + " + str(curr + 1), sum + (curr + 1))

zero_sum(1, 7, "1", 1)

Hopefully you get the idea.


The first step is to turn the problem into an entirely regularly formed problem:

 n
 ∑  ±i = -1
i=2

n-2
 ∑  ±(i+2) = -1
i=0

The term 1 at the start has no prefix +/-. And the walking index better runs from 0 when using a Java array.

So one has n-1 coefficients -1 or +1 for the possible values.

A brute force approach would be to start with the highest values, i = n-2.

The upper/lower bounds for j = 0, ..., i would be ± (i + 1) * (2 + i + 2) / 2, so one can cut the evaluation there - when the till then calculated sum can no longer reach -1.

To represent the coefficients, one could make a new int[n - 1] or simply a new BitSet(n-1).

public void solve(int n) {
    int i = n-2;
    int sumDone = 0;
    BigSet negates = new BitSet(n - 1);
    solveRecursively(i, sumDone, negates);
}

private void solveRecursively(int i, int SumDone, BitSet negates) {
    if (i < 0) {
        if (sumDone == -1) {
            System.out.println("Found: " + negates);
        }
        return;
    }
    ...
}

The interesting, actual (home) work I leave to you. (With BitSet better i = n, ... , 2 by -1 seems simpler though.)


The question here is how much efficiency matters. If you're content to do a brute-force approach, a regression method like the one indicated by holidayfun is a fine way to go, though this will become unwieldy as n gets large.

If performance speed matters, it may be worth doing a bit of math first. The easiest and most rewarding check is whether such a sum is even possible: since the sum of the first n natural numbers is n(n+1)/2, and since you want to divide this into two groups (a "positive" group and a "negative" group) of equal size, you must have that n(n+1)/4 is an integer. Therefore if neither n nor n+1 is divisible by four, stop. You cannot find such a sequence that adds to zero.

This and a few other math tricks might speed up your application significantly, if speed is of the essence. For instance, finding one solution will often help you find others, for large n. For instance, if n=11, then {-11, -10, -7, -5} is one solution. But we could swap the -5 for any combination that adds to 5 that isn't in our set. Thus {-11, -10, -7, -3, -2} is also a solution, and similarly for -7, giving {-11, -10, -5, -4, -3} as a solution (we are not allowed to use -1 because the 1 must be positive). We could continue replacing the -10, the -11, and their components similarly to pick up six more solutions.

This is probably how I'd approach this problem. Use a greedy algorithm to find the "largest" solution (the solution using the largest possible numbers), then keep splitting the components of that solution into successively smaller solutions. It is again fundamentally a recursion problem, but one whose running time decreases with the size of the component under consideration and which at each step generates another solution if a "smaller" solution exists. That being said, if you want every solution then you still have to check non-greedy combinations of your split (otherwise you'd miss solutions like {-7, -4, -3} in your n=7 example). If you only wanted a lot of solutions it would definitely be faster; but to get all of them it may be no better than a brute-force approach.

Tags:

Algorithm

Java