0

For example, if A = {10, 20, 30, 40, ..., 200}, then {10, 20, 30, 40, 100} and {90, 110} are two subsets that have the same sum (which is 200).

Since the length of input is only 20? Can't we simply generate all combinations of the numbers , compute their sums store them in a hash map [key = sum, value = count] and in the iterate over the map entries to see if the sum has been seen computed more than once?

user1071840
  • 3,522
  • 9
  • 48
  • 74
  • Since you're looking for two subsets with the same sum - I don't see a way around calculating all the possible subsets [which is `O(2^n)`] and as you suggested - storing them in a HashMap (key = sum and val = subset). – Nir Alfasi Jul 20 '14 at 22:44
  • Is `10+20+40 == 30+40` a valid answer? Must the two subsets be entirely distinct from each other, as opposed to merely not identical subsets? I assume yes, but the question should be edited to clarify. – Aaron McDaid Jul 21 '14 at 09:57
  • @alfasin, if there is exactly one odd number in the set, and all the others are even, then it's clear that the odd number cannot be used in either subset. Therefore it can be removed and totally. This is an example of the kind of logic that can be applied, and means that we don't just have to naively sum every subset. – Aaron McDaid Jul 21 '14 at 10:00
  • @AaronMcDaid "Since the length of input is only 20..." if the input is "just 20" or "much bigger" removing one number is meaningless... – Nir Alfasi Jul 21 '14 at 14:55
  • @alfasin My hope that it is possible to find pairs of disjoint sets of equal sum efficiently enough did not come true. So I retracted my first answer (even though the idea of deriving a range of pairs from one disjoint pair without having to calculate their sums is valid.) – laune Jul 22 '14 at 08:27
  • @walkytalky See previous comment. – laune Jul 22 '14 at 08:28
  • This seems similar to a question I ask a while ago: http://stackoverflow.com/questions/19285438/how-to-efficiently-generate-all-combinations-at-all-depths-whose-sum-is-within Just set the min and max equal to each other – Moop Jul 22 '14 at 16:08

1 Answers1

1

After experimenting a little, I have convinced myself that simply computing all the 2^n sums and storing them is the best approach. However, the efficicieny of implementing a small set is crucial to avoid that the computation of these sums which is of O(2^n*n) takes too long.

I have used the integers 0 to 2^n-1 to represent the set of all subsets (powerset).

public class Sum {
    private Map<Integer,List<Integer>> sum2sets = new HashMap<>();
    private int n;
    private int max;
    private int[] theSet;

    public Sum( int[] theSet ){
        this.theSet = theSet;
        int n = theSet.length;
        max = (1 << n) - 1;
        int maxSum = n*(n+1)/2;
    }

Thus, a sum can be computed with the loop

private int sum( int n ){
    int s = 0;
    for( int i = 0; n > 0; i++ ){
        if( (n & 1) != 0 ) s += theSet[i];
        n = n >> 1;
    }
    return s;
}

The computation of all subsets with equal sums is simple:

public void buildLists(){
    int maxlen = 0;
    int maxsum = 0;
    for( int i = 0; i <= max; i++ ){
       int s = sum( i );
        List<Integer> set = sum2sets.get( s );
        if( set == null ){
        set = new ArrayList<Integer>();
            sum2sets.put( s, set );
        }
        set.add( i );
        int len = sum2sets.size();
        if( len > maxlen ){
            maxsum = s;
            maxlen = len;
        }
    }
    System.out.println( "max. len " + maxlen + " at " + maxsum );
}

The result set of sets with equal sum varies. Successive integers 1, 2,... 20 will produce long lists of sets producing certain sums, e.g. for sum 105 there are 15272 sets.

A selection of: 3, 7, 13, 18, 21, 22, 30, 34, 42, 49, 50, 61, 65, 67, 70, 71, 88, 91, 93, 99 has a maximum number of sets equal to 963 for the sum 994.

Further processing of this map depends on what OP really wants - there have been some questions in the comments.

You can, for instance, find pairs of subsets with the same sum, disjoint or not, but these numbers will be very large.

public String setAsString( int n ){
    StringBuilder sb = new StringBuilder( "[" );
    String del = "";
    for( int i = 0; n > 0; i++ ){
        if( (n & 1) != 0 ){
            sb.append( del ).append( theSet[i] );
            del = ", ";
        }
        n = n >> 1;
    }
    sb.append( "]" );
    return sb.toString();
}

public void dumpAll( int n ){
    for( Map.Entry<Integer,List<Integer>> e: sum2sets.entrySet() ){
        int sum = e.getKey();
        List<Integer> sets = e.getValue();
        if( sets.size() >= 2 ){
            System.out.println( "sum: " + sum );
            for( Integer i: sets ){
                System.out.print( " " + setAsString( i ) );
            }
            System.out.println();
            if( --n == 0 ) break;
        }
    }
}

This is the main method, to run an example.

public static void main( String[] args ){
    int[] nums = new int[]{
         3,   7, 13, 18, 21, 22, 30, 34, 42, 49,
         50, 61, 65, 67, 70, 71, 88, 91, 93, 99 };
    Sum sum = new Sum( nums );
    sum.buildLists();
    sum.dumpAll( 50 );
}

And the glorious output (just a small subset):

max. len 963 at 994
sum: 21
  [3, 18] [21]
sum: 25
  [7, 18] [3, 22]
sum: 28
  [3, 7, 18] [7, 21]
sum: 31
  [13, 18] [3, 7, 21]
sum: 34
  [3, 13, 18] [13, 21] [34]
sum: 37
  [3, 13, 21] [7, 30] [3, 34]
sum: 38
  [7, 13, 18] [3, 13, 22]
sum: 40
  [18, 22] [3, 7, 30]
sum: 41
  [3, 7, 13, 18] [7, 13, 21] [7, 34]
sum: 42
  [3, 18, 21] [7, 13, 22] [42]
sum: 43
  [3, 18, 22] [21, 22] [13, 30]
sum: 44
  [3, 7, 13, 21] [3, 7, 34]
sum: 45
  [3, 7, 13, 22] [3, 42]
sum: 46
  [7, 18, 21] [3, 21, 22] [3, 13, 30]
sum: 47
  [7, 18, 22] [13, 34]
sum: 49
  [3, 7, 18, 21] [7, 42] [49]
sum: 50
  [3, 7, 18, 22] [7, 21, 22] [7, 13, 30] [3, 13, 34] [50]
sum: 51
  [3, 18, 30] [21, 30]
sum: 52
  [13, 18, 21] [22, 30] [18, 34] [3, 7, 42] [3, 49]
sum: 53
  [13, 18, 22] [3, 7, 21, 22] [3, 7, 13, 30] [3, 50]
sum: 54
  [3, 21, 30] [7, 13, 34]
sum: 55
  [3, 13, 18, 21] [7, 18, 30] [3, 22, 30] [3, 18, 34] [21, 34] [13, 42]
sum: 56
  [3, 13, 18, 22] [13, 21, 22] [22, 34] [7, 49]
sum: 57
  [3, 7, 13, 34] [7, 50]
sum: 58
  [3, 7, 18, 30] [7, 21, 30] [3, 21, 34] [3, 13, 42]
sum: 59
  [7, 13, 18, 21] [3, 13, 21, 22] [7, 22, 30] [7, 18, 34] [3, 22, 34] [3, 7, 49]
sum: 60
  [7, 13, 18, 22] [18, 42] [3, 7, 50]
sum: 61
  [18, 21, 22] [13, 18, 30] [3, 7, 21, 30] [61]
sum: 62
  [3, 7, 13, 18, 21] [3, 7, 22, 30] [3, 7, 18, 34] [7, 21, 34] [7, 13, 42] [13, 49]
sum: 63
  [3, 7, 13, 18, 22] [7, 13, 21, 22] [7, 22, 34] [3, 18, 42] [21, 42] [13, 50]
sum: 64
  [3, 18, 21, 22] [3, 13, 18, 30] [13, 21, 30] [30, 34] [22, 42] [3, 61]
sum: 65
  [13, 22, 30] [13, 18, 34] [3, 7, 21, 34] [3, 7, 13, 42] [3, 13, 49] [65]
sum: 66
  [3, 7, 13, 21, 22] [3, 7, 22, 34] [3, 21, 42] [3, 13, 50]
sum: 67
  [3, 13, 21, 30] [3, 30, 34] [7, 18, 42] [3, 22, 42] [18, 49] [67]
sum: 68
  [7, 18, 21, 22] [7, 13, 18, 30] [3, 13, 22, 30] [3, 13, 18, 34] [13, 21, 34] [18, 50] [7, 61] [3, 65]
sum: 69
  [18, 21, 30] [13, 22, 34] [7, 13, 49]
sum: 70
  [18, 22, 30] [3, 7, 18, 42] [7, 21, 42] [3, 18, 49] [21, 49] [7, 13, 50] [3, 67] [70]
sum: 71
  [3, 7, 18, 21, 22] [3, 7, 13, 18, 30] [7, 13, 21, 30] [3, 13, 21, 34] [7, 30, 34] [7, 22, 42] [22, 49] [3, 18, 50] [21, 50] [3, 7, 61] [71]
sum: 72
  [3, 18, 21, 30] [7, 13, 22, 30] [7, 13, 18, 34] [3, 13, 22, 34] [30, 42] [3, 7, 13, 49] [22, 50] [7, 65]
sum: 73
  [3, 18, 22, 30] [21, 22, 30] [18, 21, 34] [13, 18, 42] [3, 7, 21, 42] [3, 21, 49] [3, 7, 13, 50] [3, 70]
sum: 74
  [13, 18, 21, 22] [3, 7, 13, 21, 30] [18, 22, 34] [3, 7, 30, 34] [3, 7, 22, 42] [7, 18, 49] [3, 22, 49] [3, 21, 50] [13, 61] [7, 67] [3, 71]
sum: 75
  [3, 7, 13, 22, 30] [3, 7, 13, 18, 34] [7, 13, 21, 34] [3, 30, 42] [7, 18, 50] [3, 22, 50] [3, 7, 65]
sum: 76
  [7, 18, 21, 30] [3, 21, 22, 30] [3, 18, 21, 34] [7, 13, 22, 34] [3, 13, 18, 42] [13, 21, 42] [34, 42]
sum: 77
  [3, 13, 18, 21, 22] [7, 18, 22, 30] [3, 18, 22, 34] [21, 22, 34] [13, 30, 34] [13, 22, 42] [3, 7, 18, 49] [7, 21, 49] [3, 13, 61] [3, 7, 67] [7, 70]
sum: 78
  [3, 7, 13, 21, 34] [7, 22, 49] [3, 7, 18, 50] [7, 21, 50] [13, 65] [7, 71]
sum: 79
  [3, 7, 18, 21, 30] [3, 7, 13, 22, 34] [3, 13, 21, 42] [7, 30, 42] [3, 34, 42] [30, 49] [7, 22, 50] [18, 61]
sum: 80
  [3, 7, 18, 22, 30] [7, 21, 22, 30] [7, 18, 21, 34] [3, 21, 22, 34] [3, 13, 30, 34] [7, 13, 18, 42] [3, 13, 22, 42] [13, 18, 49] [3, 7, 21, 49] [30, 50] [13, 67] [3, 7, 70]
sum: 81
  [7, 13, 18, 21, 22] [7, 18, 22, 34] [18, 21, 42] [3, 7, 22, 49] [13, 18, 50] [3, 7, 21, 50] [7, 13, 61] [3, 13, 65] [3, 7, 71]
sum: 82
  [13, 18, 21, 30] [18, 30, 34] [18, 22, 42] [3, 7, 30, 42] [3, 30, 49] [3, 7, 22, 50] [3, 18, 61] [21, 61]
sum: 83
  [13, 18, 22, 30] [3, 7, 21, 22, 30] [3, 7, 18, 21, 34] [3, 7, 13, 18, 42] [7, 13, 21, 42] [7, 34, 42] [3, 13, 18, 49] [13, 21, 49] [34, 49] [3, 30, 50] [22, 61] [18, 65] [3, 13, 67] [13, 70]
laune
  • 31,114
  • 3
  • 29
  • 42
  • Not an answer: the question was asked for *all subsets* and this answer deals with pairs only – Nir Alfasi Jul 22 '14 at 14:53
  • 1
    @alfasin Obviously I must have computed all subsets with the same sum to find the number of pairs. But if you look at the title, you'll clearly read "two subsets", which I understood (perhaps incorrectly) as a request for all possible twos. Is this really your reason for downvoting?? – laune Jul 22 '14 at 14:59
  • @alfasin Read my answer: it says "e.g. for sum 105 there are 15272 sets". – laune Jul 22 '14 at 15:00
  • Yes, and the code is also just an example and so is the last two lines, but all these examples deals with subset at the size of 2. – Nir Alfasi Jul 22 '14 at 15:02
  • @alfasin Where are "all these examples dealing with subset at the size of 2"? I have investigated all subsets, for two sets with 20 integers. – laune Jul 22 '14 at 15:11
  • Can you show us how you can use this code to solve an example: how to run your program and what's the output ? – Nir Alfasi Jul 22 '14 at 15:31
  • @alfasin That's not all of the output, but if you promise to check it, I'll post it in full. – laune Jul 22 '14 at 16:01
  • your code doesn't compile due to undefined `setAsString()` - but even when I remove it I don't get the same output as you displayed - what I'm getting is `max. len 963 at 994 sum: 21 9 16 sum: 25 10 33 sum: 28 11 18...` – Nir Alfasi Jul 22 '14 at 16:19
  • @alfasin Added the missing method. The `sum: 21` is OK; the other numbers: you just printed the unconverted int representations of subsets. Looks like you did get the same information, just in different representation. - Did you understand how subsets are represented? – laune Jul 22 '14 at 16:31
  • Now it's working, I didn't have time to look into the logic, changed the -1 to +1. – Nir Alfasi Jul 22 '14 at 17:27
  • @alfasin OK, thanks. - Apart from the trick to represent small sets as int (or long, if necessary) there isn't much in it. That's why I didn't think it necessary to post rather run-of-the-mill code in the first place. – laune Jul 22 '14 at 17:36
  • now lets see you improve it with elimination of subsets like you originally wanted ;) – Nir Alfasi Jul 22 '14 at 17:43
  • @alfasin The generation of all disjoint pairs of subsets in less than O(n^2) where n is the number of subsets is an interesting problem, but I doubt it can be solved satisfactorily, even with a klufhy representation of a small set. - I'm not at all convinced that OP presented the original problem correctly, This isn't really interesting. Google will find a similar but more challenging problem; maybe even more than one. – laune Jul 22 '14 at 17:54
  • Actually it was an interview question I picked from Glassdoor and it just asked for the first pair of subsets. But this is much better than what was asked. – user1071840 Jul 22 '14 at 20:49