15

So here is the problem:

Given input = [100 80 66 25 4 2 1], I need to find the best combination to give me 50.

Looking at this, the best would be 25+25 = 50, so I need 2 elements from the array.

Other combinations include 25+4+4+4+4+4+4+1 and 25+4+4+4+4+4+2+2+1.. etc etc

I need to find all the possibilities which gives me the sum on a value I want.

EDIT: As well as the best possibility (one with least number of terms)

Here is what I have done thus far: First build a new array (simple for loop which cycles through all elements and stores in a new temp array), check for all elements higher than my array (so for input 50, the elements 100,80,66 are higher, so discard them and then my new array is [25 4 2 1]). Then, from this, I need to check combinations.

The first thing I do is a simple if statement checking if any array elements EXACTLY match the number I want. So if I want 50, I check if 50 is in the array, if not, I need to find combinations.

My problem is, I'm not entirely sure how to find every single combination. I have been struggling trying to come up with an algorithm for a while but I always just end up getting stumped.

Any help/tips would be much appreciated.

PS - we can assume the array is always sorted in order from LARGEST to SMALLEST value.

Edward Falk
  • 9,991
  • 11
  • 77
  • 112
user1580457
  • 201
  • 1
  • 4
  • 9
  • Go through each element and think "Am I going to use this element? If so, how many times?" – Dennis Meng Aug 20 '13 at 18:22
  • Hmm ok, but once I eliminate all elements larger, every single element can be used in some combination, which is the issue im facing.. – user1580457 Aug 20 '13 at 18:24
  • Go through in order, and case on how many times you use a given element. Only using it once? That's one case. Using it twice? Another. Don't want to use it at all? Okay, a third case. – Dennis Meng Aug 20 '13 at 18:25
  • 6
    This sounds like it can be mapped directly onto the problem of calculating the coins to return from a cash transaction. one solution is available here: http://stackoverflow.com/questions/4894345/how-do-you-calculate-the-minimum-coin-change-for-transaction – Colin D Aug 20 '13 at 18:25
  • Ok so basically calculate worst case for each element. So for example, 25 can be used 2 times, 1 can be used 50 times, etc.? – user1580457 Aug 20 '13 at 18:25
  • I think you have started correctly by removing those larger than the target. – ThePerson Aug 20 '13 at 18:26
  • @Fernando a dynamic programming solution is probably the way to go. – Colin D Aug 20 '13 at 18:28
  • @Colin D: yep. An Evolutionary algorithm would work either. – Fernando Aug 20 '13 at 18:30
  • @ColinD - that problem is similar, but not the same. Because for that, you have set values everytime for Dimes, Nickels, etc. In this case, the array can vary per case – user1580457 Aug 20 '13 at 18:31
  • @Fernando How can you brute force if you don't know the size of your array (changes every case)? – user1580457 Aug 20 '13 at 18:31
  • What is the requirement? To find the "best" conbination, or all combinations? What defines "best"? The shortest one? – jalynn2 Aug 20 '13 at 18:36
  • 1
    Perhaps this is a [Subset-Sum problem](http://en.wikipedia.org/wiki/Subset_sum_problem) – Rohit Jain Aug 20 '13 at 18:37
  • @jalynn2 the problem is both, but first I want to deal with one step, which is finding all combinations. Then from there I will find the best (and best defines least number of terms used for the combination) – user1580457 Aug 20 '13 at 18:39
  • 1
    @RohitJain Doesn't the subset sum problem not allow repeated values like this problem does? Changing making is a subset of the knapsack family of problems. http://en.wikipedia.org/wiki/Change-making_problem – Colin D Aug 20 '13 at 18:41
  • This really has dynamic programming written all over it – 3xil3 Aug 20 '13 at 18:42
  • You don't have to find all to find the best... finding all is problematic due to memory required since you don't know how big your array is. That's why I asked if you are really required to find all as part of the assignment. – jalynn2 Aug 20 '13 at 18:42
  • @ColinD. Indeed. I should have quoted that. – Rohit Jain Aug 20 '13 at 18:42
  • You need to solve a superset algorithm first. This may help : http://programmers.stackexchange.com/questions/175550/fast-set-indexing-data-structure-for-superset-retrieval http://stackoverflow.com/questions/9353100/quickly-checking-if-set-is-superset-of-stored-sets – blackSmith Aug 20 '13 at 18:44
  • How about, this. Sort the array first. Now only check until the element is less than or equal to 50(in your case). Loop through the first element and subtract it from 50, call it neededSum, while looping through the array keep subtracting the neededSum with the elements, and keep storing the pairs in another array. If neededSum is negative discard the array, if it is zero you got the pairs. If you need the smallest array, you can have a tempArray[] and currentArray[] and depending on the length, you can swap and decide. – JNL Aug 20 '13 at 19:11
  • Will the array ever contain negative numbers? For example: 100 + -50 to give you 50? – Clark Kent Aug 20 '13 at 19:20
  • Google for "knapsack problem". Try to understand the DP solution for filling the knapsack and then apply it to your own problem. – vgru Aug 20 '13 at 21:04
  • Ok guys so it seems like I almost have it working. However, now Im trying to extend it to find the BEST/LEAST combination, so if I have the option of 25+25 or 25+20+5, it should pick 25+25 since it requires the least terms – user1580457 Aug 21 '13 at 02:28
  • 1
    A dynamic programming approach will help, but it will never have good complexity. This is NP-complete-problem. And like all NP-complete problem there is no "fast" solution. See my answer. – le-doude Aug 21 '13 at 14:27
  • 1) If you only want to generate all possibilities, brute force it (brute force literary means to generate all possibilities), see for example my answer how to do that. 2) If you want only the shortest solution, go for Mike Samuel's answer that does a breath-first search. 3) If you need all possibilities _and_ the shortest one, you'll still need to brute force it, see again my answer for an example. 4) If you want a randomized solution, you may try OnoSendai's answer. – Jelle Fresen Aug 22 '13 at 08:42

10 Answers10

5

This is the kind of problem that dynamic programming is meant to solve.

Create an array with with indices, 1 to 50. Set each entry to -1. For each element that is in your input array, set that element in the array to 0. Then, for each integer n = 2 to 50, find all possible ways to sum to n. The number of sums required is the minimum of the two addends plus 1. At the end, get the element at index 50.

Zhe
  • 396
  • 1
  • 13
  • 1
    That's basically it, although OP probably also wants the exact solution (not just the number of steps). – vgru Aug 20 '13 at 21:06
  • Thanks for that comment. You store how to get the current sum in a second array and back track once you are finished iterating. – Zhe Aug 21 '13 at 01:22
  • Is this pseudo-polynominal? What do you mean the numbers of sums required is the minimum of the 2 addends+1? Can you elaborate? Thank you!? – Micromega Aug 21 '13 at 13:44
  • What do you mean with 'for each integer n = 2 to 50, find all possible ways to sum to n'? – Jelle Fresen Aug 21 '13 at 14:26
  • 1
    @Phpdna The actual solution is polynomial (the absolutely correct one), but with the use of dynamic programming you do not have to redo sub paths and access the result for those instantly. So if you call solution(N) it will execute in polynomial time once, and then in O(1) all the other times. But the number of time you will have to call solution(~) within solution(N) is still polynomial. See my answer and think on how you would memorize the result of recursive calls so that you do not redo them in the future. – le-doude Aug 21 '13 at 16:58
  • 1
    @Zhe your solution is suboptimal since you risk calculating unreachable subtotals. Those could be very costly in a more general and bigger case. – le-doude Aug 21 '13 at 17:00
  • @le_douard: I don't understand but here is a duplicate: http://stackoverflow.com/questions/17923931/algorithm-to-find-elements-best-fitting-in-a-particular-amount/17924202?noredirect=1#comment26202256_17924202. There is a dp solution for knapsack. – Micromega Aug 21 '13 at 18:52
  • @Phpdna you are absolutely right to suggest DP. It is just that (from what I understand) your solution will determine all sub solutions for N 1 to 50 ... not all of those are interesting in the general case. For example: If 1 was not available as a step then it would be useless to calculate 49 since there would be no way to go from 49 to 50 (which is not the case here, but I am talking about general problem solution). If we had N = 65025 and A[] = {7500, 5000, 550, 275, 125, 55 , 13}, how would your solution behave? – le-doude Aug 21 '13 at 22:24
  • @le_douard: I don't think the subproblem is difficult. You can also use k. http://stackoverflow.com/questions/16022205/how-do-i-find-the-closest-possible-sum-of-an-arrays-elements-to-a-particular-va/16023064#16023064 – Micromega Aug 21 '13 at 22:38
  • I tried converting your description into code, but I'm having trouble interpreting it. Could you give the code that implements your idea? – Jelle Fresen Aug 22 '13 at 07:44
  • @Phpdna I am not saying anything about a sub-problem and difficulty. I am just saying that your solution works HERE (IN THIS EXACT CASE) because all N less than 50 are obtainable through elements of the input array. But try your solution in the case I mention in my last comment (which is the same but with different target sum and different array of options), and your solution does a lot of useless computations. – le-doude Aug 22 '13 at 12:56
  • @le_douard That is a legitimate point, and I think that is fine if you're only doing something trivial like addition. – Zhe Aug 28 '13 at 18:01
3

Edit: Due to a misinterpretation of the question, I first answered with an efficient way to calculate the number of possibilities (instead of the possibilities themself) to get N using values from a given set. That solution can be found at the bottom of this post as a reference for other people, but first I'll give a proper answer to your questions.


Generate all possibilities, count them and give the shortest one

When generating a solution, you consider each element from the input array and ask yourself "should I use this in my solution or not?". Since we don't know the answer until after the calculation, we'll just have to try out both using it and not using it, as can be seen in the recursion step in the code below.

Now, to avoid duplicates and misses, we need to be a bit careful with the parameters for the recursive call. If we use the current element, we should also allow it to be used in the next step, because the element may be used as many times as possible. Therefore, the first parameter in this recursive call is i. However, if we decide to not use the element, we should not allow it to be used in the next step, because that would be a duplicate of the current step. Therefore, the first parameter in this recursive call is i+1.

I added an optional bound (from "branch and bound") to the algorithm, that will stop expanding the current partial solution if it is known that this solution will never be shorter then the shortest solution found so far.

package otherproblems;

import java.util.Deque;
import java.util.LinkedList;

public class GeneratePossibilities
{
    // Input
    private static int n = 50;
    // If the input array is sorted ascending, the shortest solution is
    // likely to be found somewhere at the end.
    // If the input array is sorted descending, the shortest solution is
    // likely to be found somewhere in the beginning.
    private static int[] input = {100, 80, 66, 25, 4, 2, 1};

    // Shortest possibility
    private static Deque<Integer> shortest;
    // Number of possibilities
    private static int numberOfPossibilities;

    public static void main(String[] args)
    {
        calculate(0, n, new LinkedList<Integer>());
        System.out.println("\nAbove you can see all " + numberOfPossibilities +
            " possible solutions,\nbut this one's the shortest: " + shortest);
    }

    public static void calculate(int i, int left, Deque<Integer> partialSolution)
    {
        // If there's nothing left, we reached our target
        if (left == 0)
        {
            System.out.println(partialSolution);
            if (shortest == null || partialSolution.size() < shortest.size())
                shortest = new LinkedList<Integer>(partialSolution);
            numberOfPossibilities++;
            return;
        }
        // If we overshot our target, by definition we didn't reach it
        // Note that this could also be checked before making the
        // recursive call, but IMHO this gives a cleaner recursion step.
        if (left < 0)
            return;
        // If there are no values remaining, we didn't reach our target
        if (i == input.length)
            return;

        // Uncomment the next two lines if you don't want to keep generating
        // possibilities when you know it can never be a better solution then
        // the one you have now.
//      if (shortest != null && partialSolution.size() >= shortest.size())
//          return;

        // Pick value i. Note that we are allowed to pick it again,
        // so the argument to calculate(...) is i, not i+1.
        partialSolution.addLast(input[i]);
        calculate(i, left-input[i], partialSolution);
        // Don't pick value i. Note that we are not allowed to pick it after
        // all, so the argument to calculate(...) is i+1, not i.
        partialSolution.removeLast();
        calculate(i+1, left, partialSolution);
    }

}

Calculate the number of possibilities efficiently

This is a nice example of dynamic programming. What you need to do is figure out how many possibilities there are to form the number x, using value y as the last addition and using only values smaller than or equal to y. This gives you a recursive formula that you can easily translate to a solution using dynamic programming. I'm not quite sure how to write down the mathematics here, but since you weren't interested in them anyway, here's the code to solve your question :)

import java.util.Arrays;

public class Possibilities
{
    public static void main(String[] args)
    {
        // Input
        int[] input = {100, 80, 66, 25, 4, 2, 1};
        int n = 50;

        // Prepare input
        Arrays.sort(input);

        // Allocate storage space
        long[][] m = new long[n+1][input.length];

        for (int i = 1; i <= n; i++)
            for (int j = 0; j < input.length; j++)
            {
                // input[j] cannot be the last value used to compose i
                if (i < input[j])
                    m[i][j] = 0;
                // If input[j] is the last value used to compose i,
                // it must be the only value used in the composition.
                else if (i == input[j])
                    m[i][j] = 1;
                // If input[j] is the last value used to compose i,
                // we need to know the number of possibilities in which
                // i - input[j] can be composed, which is the sum of all
                // entries in column m[i-input[j]].
                // However, to avoid counting duplicates, we only take
                // combinations that are composed of values equal or smaller
                // to input[j].
                else
                    for (int k = 0; k <= j; k++)
                        m[i][j] += m[i-input[j]][k];
            }

        // Nice output of intermediate values:
        int digits = 3;
        System.out.printf(" %"+digits+"s", "");
        for (int i = 1; i <= n; i++)
            System.out.printf(" %"+digits+"d", i);
        System.out.println();
        for (int j = 0; j < input.length; j++)
        {
            System.out.printf(" %"+digits+"d", input[j]);
            for (int i = 1; i <= n; i++)
                System.out.printf(" %"+digits+"d", m[i][j]);
            System.out.println();
        }

        // Answer:
        long answer = 0;
        for (int i = 0; i < input.length; i++)
            answer += m[n][i];
        System.out.println("\nThe number of possibilities to form "+n+
            " using the numbers "+Arrays.toString(input)+" is "+answer);
    }
}
Jelle Fresen
  • 1,916
  • 1
  • 20
  • 24
1

This is the integer knapsack problem, which is one your most common NP-complete problems out there; if you are into algorithm design/study check those out. To find the best I think you have no choice but to compute them all and keep the smallest one.

For the correct solution there is a recursive algorithm that is pretty simple to put together.

import org.apache.commons.lang.ArrayUtils;
import java.util.*;

public class Stuff {

    private final int target;
    private final int[] steps;


    public Stuff(int N, int[] steps) {
        this.target = N;
        this.steps = Arrays.copyOf(steps, steps.length);
        Arrays.sort(this.steps);
        ArrayUtils.reverse(this.steps);
        this.memoize = new HashMap<Integer, List<Integer>>(N);
    }

    public List<Integer> solve() {
        return solveForN(target);
    }

    private List<Integer> solveForN(int N) {
        if (N == 0) {
            return new ArrayList<Integer>();
        } else if (N > 0) {
            List<Integer> temp, min = null;
            for (int i = 0; i < steps.length; i++) {
                temp = solveForN(N - steps[i]);
                if (temp != null) {
                    temp.add(steps[i]);
                    if (min == null || min.size() > temp.size()) {
                        min = temp;
                    }
                }
            }
            return min;
        } else {
            return null;
        }
    }
}

It is based off the fact that to "get to N" you to have come from N - steps[0], or N - steps1, ...

Thus you start from your target total N and subtract one of the possible steps, and do it again until you are at 0 (return a List to specify that this is a valid path) or below (return null so that you cannot return an invalid path).

The complexity of this correct solution is exponential! Which is REALLY bad! Something like O(k^M) where M is the size of the steps array and k a constant.

To get a solution to this problem in less time than that you will have to use a heuristic (approximation) and you will always have a certain probability to have the wrong answer.

You can make your own implementation faster by memorizing the shortest combination seen so far for all targets (so you do not need to recompute recur(N, _, steps) if you already did). This approach is called Dynamic Programming. I will let you do that on your own (very fun stuff and really not that complicated).

Constraints of this solution : You will only find the solution if you guarantee that the input array (steps) is sorted in descending order and that you go through it in that order.

Here is a link to the general Knapsack problem if you also want to look approximation solutions: http://en.wikipedia.org/wiki/Knapsack_problem

le-doude
  • 3,345
  • 2
  • 25
  • 55
0

You need to solve each sub-problem and store the solution. For example:

1 can only be 1. 2 can be 2 or 1+1. 4 can be 4 or 2+2 or 2+1+1 or 1+1+1+1. So you take each sub-solution and store it, so when you see 25=4+4+4+4+4+4+1, you already know that each 4 can also be represented as one of the 3 combinations.

Then you have to sort the digits and check to avoid duplicate patterns since, for example, (2+2)+(2+2)+(2+2)+(1+1+1+1)+(1+1+1+1)+(1+1+1+1) == (2+1+1)+(2+1+1)+(2+1+1)+(2+1+1)+(2+1+1)+(2+1+1). Six 2's and twelve 1's in both cases.

Does that make sense?

UpAllNight
  • 1,312
  • 3
  • 18
  • 30
  • Yes that kind of makes sense, but what if you have an array that is n elements in length? – user1580457 Aug 20 '13 at 18:35
  • Do you mean the input array? You are calculating the solutions on the fly every time, it shouldn't matter how many elements are in the array. You just have to find all combinations for each amount in the array, using only smaller values in the array to calculate each combination. – UpAllNight Aug 20 '13 at 18:39
  • Hmm ok, I will give this a shot then and see what I can come up with – user1580457 Aug 20 '13 at 18:41
  • As far as I can tell, the solution sort of has nothing to do with the input length, but rather the input values. – Varaquilex Aug 20 '13 at 18:54
  • @Volkanİlbeyli yes, you're right. – user1580457 Aug 20 '13 at 19:22
0

Recursion should be the easiest way to solve this (Assuming you really want to find all the solutions to the problem). The nice thing about this approach is, if you want to just find the shortest solution, you can add a check on the recursion and find just that, saving time and space :)

Assuming an element i of your array is part of the solution, you can solve the subproblem of finding the elements that sums to n-i. If we add an ordering to our solution, for example the numbers in the sum must be from the greater to the smallest, we have a way to find unique solutions.

This is a recursive solution in C#, it should be easy to translate it in java.

    public static void RecursiveSum(int n, int index, List<int> lst, List<int> solution)
    {
        for (int i = index; i < lst.Count; i++)
        {
            if (n == 0)
            {
                Console.WriteLine("");
                foreach (int j in solution)
                {
                    Console.Write(j + " ");
                }
            }
            if (n - lst[i] >= 0)
            {
                List<int> tmp = new List<int>(solution);
                tmp.Add(lst[i]);
                RecursiveSum(n - lst[i], i, lst, tmp);
            }
        }
    }

You call it with

RecursiveSum(N,0,list,new List<int>());

where N is the sum you are looking for, 0 shouldn't be changed, list is your list of allowed numbers, and the last parameter shouldn't be changed either.

Save
  • 11,450
  • 1
  • 18
  • 23
0

What about using the greedy algorithm n times (n is the number of elements in your array), each time popping the largest element off the list. E.g. (in some random pseudo-code language):

array = [70 30 25 4 2 1]
value = 50

sort(array, descending)

solutions = []  // array of arrays

while length of array is non-zero:
    tmpValue = value
    thisSolution = []
    for each i in array:
        while tmpValue >= i:
            tmpValue -= i
            thisSolution.append(i)

    solutions.append(thisSolution)

    array.pop_first() // remove the largest entry from the array

If run with the set [70 30 25 4 2 1] and 50, it should give you a solutions array like this:

[[30 4 4 4 4 4]
 [30 4 4 4 4 4]
 [25 25]
 [4 4 4 4 4 4 4 4 4 4 4 4 2]
 [2 ... ]
 [1 ... ]]

Then simply pick the element from the solutions array with the smallest length.

Update: The comment is correct that this does not generate the correct answer in all cases. The reason is that greedy isn't always right. The following recursive algorithm should always work:

array = [70, 30, 25, 4, 3, 1]

def findSmallest(value, array):
    minSolution = []
    tmpArray = list(array)

    while len(tmpArray):
        elem = tmpArray.pop(0)
        tmpValue = value

        cnt = 0
        while tmpValue >= elem:
            cnt += 1
            tmpValue -= elem

            subSolution = findSmallest(tmpValue, tmpArray)

            if tmpValue == 0 or subSolution:
                if not minSolution or len(subSolution) + cnt < len(minSolution):
                    minSolution = subSolution + [elem] * cnt

    return minSolution

print findSmallest(10, array)
print findSmallest(50, array)
print findSmallest(49, array)
print findSmallest(55, array)

Prints:

[3, 3, 4]
[25, 25]
[3, 4, 4, 4, 4, 30]
[30, 25]

The invariant is that the function returns either the smallest set for the value passed in, or an empty set. It can then be used recursively with all possible values of the previous numbers in the list. Note that this is O(n!) in complexity, so it's going to be slow for large values. Also note that there are numerous optimization potentials here.

JoshG79
  • 1,685
  • 11
  • 14
  • Down voted for being incorrect. A counter example is `array = [4 3 1]` and `value = 10`. Your solution will find `[[4 4 1 1], [3 3 3 1], [1 1 1 1 1 1 1 1 1 1]]`. It will not calculate _all_ solutions, nor will it find the _shortest_ solution (in this case `[4 3 3]`) – Jelle Fresen Aug 21 '13 at 09:53
0

Isn't this just a search problem? If so, just search breadth-first.

abstract class Numbers {
  abstract int total();

  public static Numbers breadthFirst(int[] numbers, int total) {
    List<Numbers> stack = new LinkedList<Numbers>();
    if (total == 0) { return new Empty(); }
    stack.add(new Empty());
    while (!stack.isEmpty()) {
      Numbers nums = stack.remove(0);
      for (int i : numbers) {
        if (i > 0 && total - nums.total() >= i) {
          Numbers more = new SomeNumbers(i, nums);
          if (more.total() == total) { return more; }
          stack.add(more);
        }
      }
    }
    return null;  // No answer.
  }
}

class Empty extends Numbers {
  int total() { return 0; }
  public String toString() { return "empty"; }
}
class SomeNumbers extends Numbers {
  final int total;
  final Numbers prev;
  SomeNumbers(int n, Numbers prev) {
    this.total = n + prev.total();
    this.prev = prev;
  }
  int total() { return total; }
  public String toString() {
    if (prev.getClass() == Empty.class) { return "" + total; }
    return prev + "," + (total - prev.total());
  }

}
Jelle Fresen
  • 1,916
  • 1
  • 20
  • 24
Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
  • While this correctly calculates the solution with the least number of terms (as was eventually requested by the OP), it does not generate all possibilities, and if you would let it continue to run to generate all possibilities it would create duplicates (e.g. [2 1] and [1 2]). – Jelle Fresen Aug 21 '13 at 10:47
  • @JelleFresen, are you sure it doesn't compute all possibilities that don't include non-positive items? Maybe a counterexample would help me understand. – Mike Samuel Aug 21 '13 at 12:40
  • 1
    I meant that it only generates _one_ solution and not _all_. Your code does generate the shortest solution in an efficient way though. On a side note, I suggest next time you make sure your code compiles before posting it, I had to fix numerous syntax errors and other mistakes. – Jelle Fresen Aug 21 '13 at 13:13
  • @JelleFresen, thanks for fixing the bugs. Sorry about that. I wrote one that did, and then got overconfident and refactored it in the wikitext edit window. Now I understand what you meant by "does not generate all possibilities." One could address that by storing the index into numbers on the stack with the `Numbers` instance, and only looping from that index on instead of doing `for (int i : numbers)`. – Mike Samuel Aug 23 '13 at 17:09
0

The problem you pose is interesting but very complex. I'd approach this by using something like OptaPlanner(formerly Drools Planner). It's difficult to describe a full solution to this problem without spending significant time, but with optaplanner you can also get "closest fit" type answers and can have incremental "moves" that would make solving your problem more efficient. Good luck.

Taylor
  • 3,942
  • 2
  • 20
  • 33
0

This is a solution in python: Ideone link

# Start of tsum function
def tsum(currentSum,total,input,record,n):
     if total == N :
        for i in range(0,n):
            if record[i]:
                print input[i]

            i = i+1
            for i in range(i,n):
                if record[i]:
                    print input[i]
            print ""
            return
     i=currentSum
     for i in range(i,n):
         if total+input[i]>sum :
             continue
         if i>0 and input[i]==input[i-1] and not record[i-1] :
             continue
         record[i]=1
         tsum(i+1,total+input[i],input,record,l)
         record[i]=0

# end of function
# Below portion will be main() in Java
record = []
N = 5
input = [3, 2, 2, 1, 1]
temp = list(set(input))
newlist = input
for i in range(0, len(list(set(input)))):
    val = N/temp[i]
    for j in range(0, val-input.count(temp[i])):
        newlist.append(temp[i])

# above logic was to create a newlist/input i.e [3, 2, 2, 1, 1, 1, 1, 1] 
# This new list contains the maximum number of elements <= N 
# for e.g appended three 1's as sum of new three 1's + existing two 1's <= N(5) where as
# did not append another 2 as 2+2+2 > N(5) or 3 as 3+3 > N(5)

l = len(input)

for i in range(0,l):
    record.append(0)
print "all possibilities to get N using values from a given set:"

tsum(0,0,input,record,l)

OUTPUT: for set [3, 2, 2, 1, 1] taking small set and small N for demo purpose. But works well for higher N value as well.

For N = 5

all possibilities to get N using values from a given set: 3 2

3 1 1

2 2 1

2 1 1 1

1 1 1 1 1

For N = 3

all possibilities to get N using values from a given set: 3

2 1

1 1 1

Chinmay Lokesh
  • 671
  • 1
  • 7
  • 9
-1

I made a small program to help with one solution. Personally, I believe the best would be a deterministic mathematical solution, but right now I lack the caffeine to even think on how to implement it. =)

Instead, I went with a SAR approach. Stop and Reverse is a technique used on stock trading (http://daytrading.about.com/od/stou/g/SAR.htm), and is heavily used to calculate optimal curves with a minimal of inference. The Wikipedia entry for parabolical SAR goes like this:

'The Parabolic SAR is calculated almost independently for each trend in the price. When the price is in an uptrend, the SAR emerges below the price and converges upwards towards it. Similarly, on a downtrend, the SAR emerges above the price and converges downwards.'

I adapted it to your problem. I start with a random value from your series. Then the code enters a finite number of iterations.

I pick another random value from the series stack. If the new value plus the stack sum is inferior to the target, then the value is added; if superior, then decreased. I can go on for as much as I want until I satisfy the condition (stack sum = target), or abort if the cycle can't find a valid solution. If successful, I record the stack and the number of iterations. Then I redo everything.

An EXTREMELY crude code follows. Please forgive the hastiness. Oh, and It's in C#. =)

Again, It does not guarantee that you'll obtain the optimal path; it's a brute force approach. It can be refined; detect if there's a perfect match for a target hit, for example.

 public static class SAR
 {
    //I'm considering Optimal as the smallest signature (number of members).
    // Once set, all future signatures must be same or smaller.

    private static Random _seed = new Random();

    private static List<int> _domain = new List<int>() { 100, 80, 66, 24, 4, 2, 1 };

    public static void SetDomain(string domain)
    {
        _domain = domain.Split(',').ToList<string>().ConvertAll<int>(a => Convert.ToInt32(a));
        _domain.Sort();
    }

    public static void FindOptimalSAR(int value)
    {
        // I'll skip some obvious tests. For example:
        //   If there is no odd number in domain, then
        //   it's impossible to find a path to an odd
        //   value.

        //Determining a max path run. If the count goes
        //   over this, it's useless to continue.
        int _maxCycle = 10;

        //Determining a maximum number of runs.
        int _maxRun = 1000000;
        int _run = 0;

        int _domainCount = _domain.Count;

        List<int> _currentOptimalSig = new List<int>();
        List<String> _currentOptimalOps = new List<string>();
        do
        {

            List<int> currSig = new List<int>();
            List<string> currOps = new List<string>();

            int _cycle = 0;
            int _cycleTot = 0;
            bool _OptimalFound = false;

            do
            {
                int _cursor = _seed.Next(_domainCount);

                currSig.Add(_cursor);

                if (_cycleTot < value)
                {
                    currOps.Add("+");
                    _cycleTot += _domain[_cursor];
                }
                else
                {
                    // Your situation doesn't allow for negative
                    // numbers. Otherwise, just enable the two following lines.
                    // currOps.Add("-");
                    // _cycleTot -= _domain[_cursor];
                }

                if (_cycleTot == value)
                {
                    _OptimalFound = true;
                    break;
                }

                _cycle++;
            } while (_cycle < _maxCycle);

            if (_OptimalFound)
            {
                _maxCycle = _cycle;

                _currentOptimalOps = currOps;
                _currentOptimalSig = currSig;

                Console.Write("Optimal found: ");

                for (int i = 0; i < currSig.Count; i++)
                {
                    Console.Write(currOps[i]);
                    Console.Write(_domain[currSig[i]]);
                }

                Console.WriteLine(".");
            }

            _run++;

        } while (_run < _maxRun);
    }
}

And this is the caller:

        String _Domain = "100, 80, 66, 25, 4, 2, 1";

        SAR.SetDomain(_Domain);

        Console.WriteLine("SAR for Domain {" + _Domain + "}");
        do
        {
            Console.Write("Input target value: ");
            int _parm = (Convert.ToInt32(Console.ReadLine()));

            SAR.FindOptimalSAR(_parm);
            Console.WriteLine("Done.");

        } while (true);

This is my result after 100k iterations for a few targets, given a slightly modified series (I switched 25 for 24 for testing purposes):

SAR for Domain {100, 80, 66, 24, 4, 2, 1}
Input target value: 50
Optimal found: +24+24+2.
Done.
Input target value: 29
Optimal found: +4+1+24.
Done.
Input target value: 75
Optimal found: +2+2+1+66+4.
Optimal found: +4+66+4+1.
Done.

Now with your original series:

SAR for Domain {100, 80, 66, 25, 4, 2, 1}
Input target value: 50
Optimal found: +25+25.
Done.
Input target value: 75
Optimal found: +25+25+25.
Done.
Input target value: 512
Optimal found: +80+80+66+100+1+80+25+80.
Optimal found: +66+100+80+100+100+66.
Done.
Input target value: 1024
Optimal found: +100+1+80+80+100+2+100+2+2+2+25+2+100+66+25+66+100+80+25+66.
Optimal found: +4+25+100+80+100+1+80+1+100+4+2+1+100+1+100+100+100+25+100.
Optimal found: +80+80+25+1+100+66+80+80+80+100+25+66+66+4+100+4+1+66.
Optimal found: +1+100+100+100+2+66+25+100+66+100+80+4+100+80+100.
Optimal found: +66+100+100+100+100+100+100+100+66+66+25+1+100.
Optimal found: +100+66+80+66+100+66+80+66+100+100+100+100.
Done.

Cons: It is worth mentioning again: This algorithm does not guarantee that you will find the optimal values. It makes a brute-force approximation.

Pros: Fast. 100k iterations may initially seem a lot, but the algorithm starts ignoring long paths after it detects more and more optimized paths, since it lessens the maximum allowed number of cycles.

OnoSendai
  • 3,960
  • 2
  • 22
  • 46
  • Downvoted for giving an overly complex answer that isn't even guaranteed to give a solution and doesn't give insight to the problem at hand. – Jelle Fresen Aug 21 '13 at 10:53
  • At least you provided a rationale, thanks! But I still think that complexity, as beauty, is on the eye of the beholder; and that this answer demonstrates a brute-force approximation. – OnoSendai Aug 21 '13 at 15:39
  • You're right about the perception of complexity, my apologies for that one. Still I think that, since there exists a perfect answer founded by mathematics, your answer incorrectly educates the OP about the nature of the problem at hand. – Jelle Fresen Aug 22 '13 at 08:00