Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

algorithm

The problem is I have to print all combinations of a sequence of numbers from 1 to N that will always result to zero. It is allowed to insert "+" (for adding) and "-" (for subtracting) between each numbers so that the result will be zero.

//Output
N = 7

1 + 2 - 3 + 4 - 5 - 6 + 7 = 0
1 + 2 - 3 - 4 + 5 + 6 - 7 = 0
1 - 2 + 3 + 4 - 5 + 6 - 7 = 0
1 - 2 - 3 - 4 - 5 + 6 + 7 = 0

So how can I implement this? I am not asking for the actual codes to do this, just a hint and ideas to solve this will do. Thank you..

like image 267
robert Avatar asked Mar 26 '19 09:03

robert


4 Answers

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.

like image 142
holidayfun Avatar answered Oct 21 '22 04:10

holidayfun


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.)

like image 26
Joop Eggen Avatar answered Oct 21 '22 05:10

Joop Eggen


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.

like image 28
Galendo Avatar answered Oct 21 '22 03:10

Galendo


If I were you I would go for a graph implementation, and DFS algorithm. Imagine you have N nodes that are representing your numbers. Each number is connected to another via an "add" edge, or a "subtract" edge. So you have a fully connected graph. You can start from a node and compute all dfs paths that lead to zero.

For more information about DFS algorithm, you can see the wikipage.

Edit: In order to clarify my solution, the graph you will end up having will be a multigraph, which means that it has more than one edge between nodes. DFS in a multigraph is slightly more complicated, but it is not that hard.

like image 33
Farhood ET Avatar answered Oct 21 '22 04:10

Farhood ET