Suppose I have a list:
List _arr = new List {1, 3, 4};
And a target of 4
I want to return
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.
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 1
s
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
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. ;)
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