1

i will take either python of c# solution

i have about 200 numbers:

 19.16 
 98.48 
 20.65 
 122.08 
 26.16 
 125.83 
 473.33 
 125.92 
 3,981.21 
 16.81 
 100.00 
 43.58 
 54.19 
 19.83 
 3,850.97 
 20.83 
 20.83 
 86.81 
 37.71 
 36.33 
 6,619.42 
 264.53 
...
...

i know that in this set of numbers, there is a combination of numbers that will add up to a certain number let's say it is 2341.42

how do i find out which combination of numbers will add up to that?

i am helping someone in accounting track down the correct numbers

Alex Gordon
  • 57,446
  • 287
  • 670
  • 1,062

5 Answers5

4

Here's a recursive function in Python that will find ALL solutions of any size with only two arguments (that you need to specify).

def find_all_sum_subsets(target_sum, numbers, offset=0):
    solutions = []
    for i in xrange(offset, len(numbers)):
        value = numbers[i]
        if target_sum == value:
            solutions.append([value])
        elif target_sum > value:
            sub_solutions = find_all_sum_subsets(target_sum - value, numbers, i + 1)
            for sub_solution in sub_solutions:
                solutions.append(sub_solution + [value])
    return solutions

Here it is working:

>>> find_all_sum_subsets(10, [1,2,3,4,5,6,7,8,9,10,11,12])
[[4, 3, 2, 1], [7, 2, 1], [6, 3, 1], [5, 4, 1], [9, 1], [5, 3, 2], [8, 2], [7, 3], [6, 4], [10]]
>>>
John Machin
  • 81,303
  • 11
  • 141
  • 189
3

You can use backtracking to generate all the possible solutions. This way you can quickly write your solution.

EDIT:

You just implement the algoritm in C#:

public void backtrack (double sum, String solution, ArrayList numbers, int depth, double targetValue, int j)
{
    for (int i = j; i < numbers.Count; i++)
        {
            double potentialSolution = Convert.ToDouble(arrayList[i] + "");
            if (sum + potentialSolution > targetValue)
                continue;
            else if (sum + potentialSolution == targetValue)
            {
                if (depth == 0)
                {
                    solution = potentialSolution + "";
                    /*Store solution*/
                }
                else
                {
                    solution += "," + potentialSolution;
                    /*Store solution*/
                }
            }
            else
            {
                if (depth == 0)
                {
                    solution = potentialSolution + "";
                }
                else
                {
                    solution += "," + potentialSolution;
                }
                backtrack (sum + potentialSolution, solution, numbers, depth + 1, targetValue, i + 1);
            }
        }
}

You will call this function this way:

backtrack (0, "", numbers, 0, 2341.42, 0);

The source code was implemented on the fly to answer your question and was not tested, but esencially you can understand what I mean from this code.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • @lajos ok how do i do this in c# – Alex Gordon Nov 11 '10 at 20:44
  • This is the right answer: the alternatives are essentially brute force. – Steven Sudit Nov 11 '10 at 20:54
  • 3
    You're assuming the list does not contain negative numbers. – Paul Sonier Nov 11 '10 at 21:04
  • Yes, the list in the question contained only positive real numbers, that's why I assumed there are no negative numbers, but good point, I'll upvote your comment. – Lajos Arpad Nov 11 '10 at 21:07
  • To solve the problem with negative numbers, you'll have to generate a Descartes multiplying to generate all possible sums and will filter out all the result sets where the sum doesn't match the desired sum – Lajos Arpad Nov 11 '10 at 21:12
  • @lajosarpad: you could just upvote my answer, which contains a solution which accounts for negative numbers. :-) – Paul Sonier Nov 11 '10 at 21:13
  • By Descartes multiplying I mean we have a set of (X1, X2,..., X200). We can calculate Y1 * X1 + Y2 * X2 + ... + Y200 * X200 where Yi might be 0 or 1 and we have to calculate all the cases, meaning we'll have to calculate 2^200 sums and storing all the "good" ones. – Lajos Arpad Nov 11 '10 at 21:15
  • However, you must make sure you are using everything needed and after a short debug session you'll be able to find any bugs. If by not working you mean it doesn't write anything on the screen or is not storing your variables, note that this: /*Store solution*/ means that you either store your solutions in a file or showing them on the screen. – Lajos Arpad Nov 11 '10 at 21:41
  • Also, a comment like "this is not working" is not too helpful, because I can't determine what's the problem with that. – Lajos Arpad Nov 11 '10 at 21:42
  • "??????????? then test it and edit it" Sorry, I'm only working for people who are paying for my time, and I'm not doing and testing your homework for free. And I'm puzzled by your attitude, you're acting like a boss, which is not acceptible when I'm helping you for free. – Lajos Arpad Nov 11 '10 at 21:48
  • @lajos senor what do u think of McWafflestix's solution? – Alex Gordon Nov 11 '10 at 21:54
  • btw why did u add "" here Convert.ToDouble(numbers[i] + ""); – Alex Gordon Nov 11 '10 at 21:55
  • His solution is good enough, maybe he didn't test it either, it's slower than my solution, to handle negative numbers too and you must handle to return all the potential solutions – Lajos Arpad Nov 11 '10 at 22:00
  • Because the ArrayList contains Objects. numbers[i] + "" converts it to String. Convert.ToDouble() converts the String to a real number (double precision), to have a precise value as a solution. – Lajos Arpad Nov 11 '10 at 22:02
  • 1
    Note that if there are no negative numbers in the input, you can reduce the search space with two quick passes through the input: one to find the smallest number, and one to remove any number that can't be part of the solution because it's larger than the target value minus the smallest number. – Robert Rossney Nov 12 '10 at 01:02
  • That's actually a good idea for optimization, however, for 200 numbers a backtracking without optimization is good enough. However, if we had 200 000 numbers, optimizations would be mandatory. Your idea would be very useful in that case. – Lajos Arpad Nov 12 '10 at 02:04
1

This should be implemented as a recursive algorithm. Basically, for any given number, determine if there is a subset of the remaining numbers for which the sum is your desired value.

Iterate across the list of numbers; for each entry, subtract that from your total, and determine if there is a subset of the remaining list which sums up to the new total. If not, try with your original total and the next number in the list (and a smaller sublist, of course).

As to implementation: You want to define a method which takes a target number, and a list, and which returns a list of numbers which sum up to that target number. That algorithm should iterate through the list; if an element of the list subtracted from the target number is zero, return that element in a list; otherwise, recurse on the method with the remainder of the list, and the new target number. If any recursion returns a non-null result, return that; otherwise, return null.

ArrayList<decimal> FindSumSubset(decimal sum, ArrayList<decimal> list)
{
   for (int i = 0; i < list.Length; i++)
   {
      decimal value = list[i];
      if (sum - value == 0.0m)
      {
          return new ArrayList<decimal>().Add(value);
      }
      else
      {
          var subset = FindSumSubset(sum - value, list.GetRange(i + 1, list.Length -i);
          if (subset != null)
          {
              return subset.Add(value);
          }
      }
   }
   return null;
}

Note, however, that the order of this is pretty ugly, and for significantly larger sets of numbers, this becomes intractable relatively quickly. This should be doable in less than geologic time for 200 decimals, though.

Paul Sonier
  • 38,903
  • 3
  • 77
  • 117
1

Try the following approach if finding a combination of any two (2) numbers:

float targetSum = 3;

float[] numbers = new float[]{1, 2, 3, 4, 5, 6};

Sort(numbers); // Sort numbers in ascending order.

int startIndex = 0;
int endIndex = numbers.Length - 1;

while (startIndex != endIndex)
{
   float firstNumber = numbers[startIndex];
   float secondNumber = numbers[endIndex];

   float sum = firstNumber + secondNumber;

   if (sum == targetSum)
   {
      // Found a combination.
      break;
   }
   else if (sum < targetSum)
   {
      startIndex++;
   }
   else
   {
      endIndex--;
   }
}

Remember that when use floating-point or decimal numbers, rounding could be an issue.

Bernard
  • 7,908
  • 2
  • 36
  • 33
  • @bernardo what if there are 7 numbers that will give me the desired total? – Alex Gordon Nov 11 '10 at 20:47
  • @i am a girl: In this case, the two numbers are given as 'firstNumber' and 'secondNumber'. Again, this only works in the simple case of finding two numbers that add up to the desired sum. – Bernard Nov 11 '10 at 20:48
1

[Begin Edit]:

I misread the original question. I thought that it said that there is some combination of 4 numbers in the list of 200+ numbers that add up to some other number. That is not what was asked, so my answer does not really help much.

[End Edit]

This is pretty clunky, but it should work if all you need is to find the 4 numbers that add up to a certain value (it could find more than 4 tuples):

Just get your 200 numbers into an array (or list or some IEnumerable structure) and then you can use the code that I posted. If you have the numbers on paper, you will have to enter them into the array manually as below. If you have them in softcopy, you can cut and paste them and then add the numbers[x] = xxx code around them. Or, you could cut and paste them into a file and then read the file from disk into an array.

  double [] numbers = new numbers[200];
  numbers[0] = 123;
  numbers[1] = 456; 

  //
  // and so on.
  //

  var n0 = numbers;
  var n1 = numbers.Skip(1);
  var n2 = numbers.Skip(2);
  var n3 = numbers.Skip(3);

  var x = from a in n0
          from b in n1
          from c in n2
          from d in n3
          where a + b + c + d == 2341.42
          select new { a1 = a, b1 = b, c1 = c, d1 = d };

  foreach (var aa in x)
  {
    Console.WriteLine("{0}, {1}, {2}, {3}", aa.a1, aa.b1, aa.c1, aa.d1 );
  }
wageoghe
  • 27,390
  • 13
  • 88
  • 116
  • @wage this is beautiful is numbers an array? can u please show me how i would declare this – Alex Gordon Nov 11 '10 at 20:48
  • 1
    problem with this is you have to know up front how many numbers make up the result. And the time it takes to get a result goes up really fast like this (to the point it won't finish in your lifetime if the set gets bigger) – Doggett Nov 11 '10 at 20:50
  • Yes, but she said that she is trying to help someone in accounting find the set of 4 numbers in a list of about 200 numbers that add up to some value. If that is really the problem that she is trying to solve, there is no need to get much more complicated than this. – wageoghe Nov 11 '10 at 20:54
  • Whoops! I misread, I thought that the original question said there were 4 numbers that add up to the target number. I just went back and see that is not the case. – wageoghe Nov 11 '10 at 20:55
  • I am not really familiar with C# or the syntax you used, but apart from being limited to combinations of 4 this doesn't seem to prevent the same number from being chosen multiple times in the solution? I mean; if the fourth number in the list were `(2341.42 / 4)`, wouldn't it be present in all four lists and lead to the solution where a = b = c = d? – Arkku Nov 11 '10 at 21:14
  • 3
    @i am a girl: It's freakin amazin that you've accepted an answer that the author admits **doesn't answer your question**. – John Machin Nov 12 '10 at 00:33
  • 1
    Yes, I was really surprised by this decision too, because there were several good answer, but with this "send me the code, because I'm the boss" mentality (see one of her comments at my answer) provoked the proper answer from my part and since then I didn't see a logical move from her part. – Lajos Arpad Nov 12 '10 at 04:21
  • So you are saying that backtracking is not working in general? I've written some code on-the fly because you asked me (in a rude way, by the way). The answer initially was the text before EDIT:, as you can also remember. And by saying that my solution (telling you about backtracking and giving an untested example code) is not working means that backtracking is not good enough to solve your problem. – Lajos Arpad Nov 13 '10 at 02:05
  • Stackoverflow is a website to ask and answer questions and this "give me the code" mentality is rude from a person who asks for help. When you started to act like a boss, I told you that I'm helping you and not working for you, but telling me that my answer doesn't work you're making a great mistake, because: An answer is never "working", a source code is working, a machine is working, a person is working, but an answer is not working, it can only be good or bad. My answer is good because it points out that backtracking is the algoritm to solve your problem. – Lajos Arpad Nov 13 '10 at 02:07
  • The other reason "his solution works" is "interesting" is that when you expect a solution you are hiring people to work for you and pay them, on websites of this kind you will get answers most of the time, solutions will be rarer. – Lajos Arpad Nov 13 '10 at 02:11
  • 1
    And "i am a girl": When you typed this "i am helping someone in accounting track down the correct numbers" you seriously believed that programmers will not notice that you're asking them to do YOUR homework and you're not telling the truth? Did you try this in your other 160+ university/highschool homework questions? I'm sorry, I have no more time for you, have nice day. – Lajos Arpad Nov 13 '10 at 02:14