Find elements in a list that, together add up to a target number

Suppose I have a list:

List<int> _arr = new List<int> {1, 3, 4};

And a target of 4

I want to return {1, 3} as 1 + 3 = 4 and {4} as 4 = 4 using linq from the given list.

How do I do that?

Upvotes: 2

Views: 3858

Answers (4)

catpol
catpol

Reputation: 68

If you are after a really good and fast solution to this problem you are not alone.

This is a well know SUBSET SUM problem. I suggest you could look it up at Wikipedia here:

http://en.wikipedia.org/wiki/Subset_sum_problem

Of course you could iterate through all possible subsets looking for your sum but be aware that there is (2 ^ CountOfListElements) possible combinations. If the list is always small in size you will have no problems coding it. Even exponential time solution will work for small sets.

Upvotes: 1

Nolonar
Nolonar

Reputation: 6122

I think the best solution to this problem, is Dynamic Programming.

Create a 2-Dimensional Array, with dimensions [a + 1, _arr.Length]:

  0 1 2 3 4
1
3
4

Then, for each column in that 2D array, fill in each cell, such that the sum of all cells of a column equals to the index of the column:

  0 1 2 3 4
1 0 1 x 0 1
3 0 0 x 1 1
4 0 0 x 0 0

// Alternative solution
  0 1 2 3 4
1 0 1 x 0 0
3 0 0 x 1 0
4 0 0 x 0 1

Here, x means, there is no solution. Also, column 4 has 2 solutions, so you need to account for that, maybe with a List<int>[a]?

As to how to fill in those cells exactly:
Column 0 will hold all zeroes, because it's a solution that can be achieved without a sum.
For Column i, you want to do i - n (where n is the current number from _arr)

  0 1
1 0 1   // 1 - 1 == 0, so you put 1 in here
3 0 0
4 0 0

  0 1 2
1 0 1 x // 2 - 1 == 1, 1 already has a solution, but you already used the number necessary to solve 1
3 0 0 x
4 0 0 x

If you had more than one 1s

  0 1 2
1 0 1 1 //             1 - 1 == 0
1 0 0 1 // 2 - 1 == 1, 1 already has a solution
3 0 0 0
4 0 0 0

// Alternative solution
  0 1 2
1 0 0 1 // 2 - 1 == 1, 1 already has a solution
1 0 1 1 //             1 - 1 == 0
3 0 0 0
4 0 0 0

If you need more information on dynamic programming: Knapsack Problem Dynamic Programming Algorithm

Upvotes: 2

George Duckett
George Duckett

Reputation: 32428

It's easy once we have a method to get all subsets of an enumerator/list
(found here: Answer: Most Elegant Way to Get All Subsets of an Array in C#)

using System;
using System.Collections.Generic;
using System.Linq;

public static class Program
{
    static void Main(string[] args)
    {
        var test = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        var target = 6;

        var matches = from subset in test.SubSetsOf()
                      where subset.Sum() == target
                      select subset;

        Console.WriteLine("Numbers: {0}", test.Select(i => i.ToString()).Aggregate((a, n) => a + ", " + n));
        Console.WriteLine("Target: {0}", target);

        foreach (var match in matches)
        {
            Console.WriteLine(match.Select(m => m.ToString()).Aggregate((a, n) => a + " + " + n) + " = " + target.ToString());
        }

        Console.ReadKey();
    }

    public static IEnumerable<IEnumerable<T>> SubSetsOf<T>(this IEnumerable<T> source)
    {
        // Deal with the case of an empty source (simply return an enumerable containing a single, empty enumerable)
        if (!source.Any())
            return Enumerable.Repeat(Enumerable.Empty<T>(), 1);

        // Grab the first element off of the list
        var element = source.Take(1);

        // Recurse, to get all subsets of the source, ignoring the first item
        var haveNots = SubSetsOf(source.Skip(1));

        // Get all those subsets and add the element we removed to them
        var haves = haveNots.Select(set => element.Concat(set));

        // Finally combine the subsets that didn't include the first item, with those that did.
        return haves.Concat(haveNots);
    }
}

Output:

Numbers: 1, 2, 3, 4, 5, 6, 7, 8, 9
Target: 6
1 + 2 + 3 = 6
1 + 5 = 6
2 + 4 = 6
6 = 6

Upvotes: 12

MBender
MBender

Reputation: 5650

I don't think you can do that using a simple LINQ query. In fact, I'm having a hard time coming up with a simple solution for this using full-blown T-SQL!

The problem is you're trying to return not single items, but various collections of items based on possible permutations.

On that note, I do hope someone comes up with such a LINQ query, cos something tells me it would will be awesome. ;)

Upvotes: 1

Related Questions