Finding all combinations of duplicates numbers to reach a given sum

I want to finding all combinations in an array to reach a given sum. For example if the array is [1,1,1,2,4,4] and the given target is 5 then should the output be:

Expected Output
1 1 1 2
1 4
1 4

This code is so far i did:

static void sum(int[] arr, int i, int sum, int target, String s) {
    for (int j = i + 1; j < arr.length; j++) {
        if (sum + arr[j] == target) {
            System.out.println(s + " " + String.valueOf(arr[j]));
        } else {
            sum(arr, j, sum + arr[j], target, s + " " + String.valueOf(arr[j]));
        }
    }
}

public static void main(String[] args) {
    int[] numbers = { 1, 1, 1, 2, 4, 4 };
    for (int i = 0; i < numbers.length; i++) {
        sum(numbers, i, numbers[i], 5, String.valueOf(numbers[i]));
    }

}

And the output of this code is :

My Output
1 1 1 2
1 4
1 4
1 4
1 4
1 4
1 4

There is actually a problem when we have duplicates elements , its working for a non duplicates numbers, but not when there is duplicates numbers. I want to know how I can solve that problem so the output seems like the expected one.

One of the basic objects we need to deal with in this problem is a Bag or MultiSet – an unordered set of values that can contain multiple instances of an element. Unfortunately the Java Collections framework doesn’t support this. Rather than mess around trying to get List to work we can write our own very basic version, with just the capabilities we need. (The Guava library has a MultiSet, so you could look to that for production code).

import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeMap;

public class Bag<E> 
{
    private Map<E, Integer> m_values;

    public Bag()
    {
        m_values = new TreeMap<E, Integer>();
    }

    public Bag(Iterable<E> arr)
    {
        this();
        for(E v : arr)
        {
            add(v);
        }
    }

    public Bag(Bag<E> b)
    {
        this();
        for(E v : b.values())
        {
            set(v, b.count(v));
        }
    }

    public void add(E v)
    {
        Integer count = m_values.get(v);
        m_values.put(v, count == null ? 1 : count+1);
    }

    public void add(Bag<E> b)
    {
        for(E v : b.values())
        {
            Integer count = m_values.get(v);
            m_values.put(v, count == null ? b.count(v) : count+b.count(v));
        }
    }

    public void remove(E v)
    {
        Integer count = m_values.get(v);
        if(count == null) throw new NoSuchElementException();
        if(count == 1)
            m_values.remove(v);
        else
            m_values.put(v, count-1);
    }

    public void remove(Bag<E> b)
    {
        for(E v : b.values())
        {
            Integer count = m_values.get(v);    
            Integer bcount = b.count(v);
            if(count == null || count < bcount) throw new NoSuchElementException();
            if(count == bcount)
                m_values.remove(v);
            else
                m_values.put(v, count-bcount);
        }
    }

    public boolean contains(Bag<E> b)
    {
        for(E v : b.values())
        {
            if(count(v) < b.count(v)) return false;
        }
        return true;
    }

    public void set(E v, int count)
    {
        if(count == 0)
            m_values.remove(v);
        else
            m_values.put(v, count);
    }

    public int count(E v)
    {
        Integer count = m_values.get(v);
        return count == null ? 0 : count;
    }

    public Iterable<E> values()
    {
        return m_values.keySet();
    }

    public String toString()
    {
        StringBuilder b = new StringBuilder();
        b.append("[");
        for(E v : values())
        {
            for(int i=0; i<count(v); i++)
            {
                b.append(v + " ");
            }
        }
        b.deleteCharAt(b.length()-1);
        b.append("]");
        return b.toString();
    }
}

The first step in solving the problem is to generate a list of candidate sets that sum to 5. We could do this by generating subsets from the input array, but we have to be careful not to include duplicates. The code for this isn’t too bad, but it’s actually much easier, if a little less efficient, to just generate all possible partitions of the count you’re interested in, in this case 5.

import java.util.ArrayList;
import java.util.List;

public class Partition
{
    public static List<Bag<Integer>> partitions(int n)
    {
        return new Partition(n).partitions;
    }

    private List<Bag<Integer>> partitions;
    private Bag<Integer> current;

    private Partition(int n)
    {
        partitions = new ArrayList<>();
        current = new Bag<Integer>();
        build(n, n);
    }

    private void build(int n, int max)
    {
        if (n == 0)
        {
            partitions.add(0, new Bag<Integer>(current));
        }

        for (int i = Math.min(max, n); i >= 1; i--)
        {
            current.add(i);
            build(n - i, i);
            current.remove(i);
        }
    }

    public static void main(String[] args)
    {
        for (Bag<Integer> b : partitions(5))
        {
            System.out.println(b);
        }
    }
}

Output:

[1 1 1 1 1]
[1 1 1 2]
[1 2 2]
[1 1 3]
[2 3]
[1 4]
[5]

Now we can write a recursive routine to extract maximal sets of these partitions from your input. The only tricky part is to ensure that when we find a set that it isn’t a subset of a solution we’ve already seen, in which case we can ignore it.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Dice
{
    public static List<List<Bag<Integer>>> picks(Integer[] diceArr, int k)
    {
        return new Dice(diceArr, k).output;
    }

    private List<List<Bag<Integer>>> output;
    private List<Bag<Integer>> current; 
    private List<Bag<Integer>> partitions;
    private Bag<Integer> dice;

    private Dice(Integer[] diceArr, int k)
    {
        output = new ArrayList<>();
        current = new ArrayList<>();

        partitions = Partition.partitions(5);
        dice = new Bag<>(Arrays.asList(diceArr));

        build(0);
    }

    private void build(int pos)
    {
        for (int i=pos; i<partitions.size(); i++)
        {
            Bag<Integer> partition = partitions.get(i);

            if(dice.contains(partition))
            {
                dice.remove(partition);
                current.add(partition);
                build(i);
                current.remove(partition);              
                dice.add(partition);
            }           
        }

        // Only add the current list of partitions if we haven't already seen it
        if(!current.isEmpty())
        {
            boolean seen = false;
            for(List<Bag<Integer>> prev : output)
            {
                if(prev.containsAll(current)) 
                {
                    seen = true;
                    break;
                }
            }
            if (!seen) output.add(new ArrayList<>(current));
        }
    }

    public static void main(String[] args)
    {
        int count = 5;
        Integer[] dice = {1, 1, 1, 2, 4, 4};
        List<List<Bag<Integer>>> picks = picks(dice, count);
        for(List<Bag<Integer>> pick : picks)
        {
            System.out.println(pick);
        }
    }
}

Output for {1, 1, 1, 2, 4, 4}:

    [[1 1 1 2]]
    [[1 4], [1 4]]

Output for {1, 1, 1, 2, 3, 4, 4, 4, 5}:

[[1 1 1 2], [5]]
[[1 1 3], [1 4], [5]]
[[2 3], [1 4], [1 4], [1 4], [5]]

You can use a map to save results. If there’s a duplicated result. The map will not save it.