Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate Combinations of generic list

I need to create a list from another list that contains every combination possible. In researching possible solutions I have found many interesting approaches but all seem to generate results based on the count of records provided. I need the combinations to increase to a max threshold.

i.e. consider the following array

1,2,3,4,5

I need the results to look similar to (threshold is 3 in this example)

1
1,2
1,2,3
1,2,4
1,2,5
1,3,4
2,3,5... etc

In actuality, the data will be IEnumerable. I used a simple int[] to illustrate the desired results.

like image 529
jason Avatar asked Mar 22 '23 04:03

jason


2 Answers

My solution uses a simple recursive algorithm to create the combinations:

  • When we walk through the sequence we can immediately return a sequence that only holds the current value. I have written a simple extension method to create an IEnumerable for a single item.

  • Next we recursively generate all combinations for the remaining elements with the threshold reduced by 1 and combine each of them with the current value.

I assume that elements should not be repeated (i.e. { 1, 1 } or { 1, 2, 1 } are not allowed). If you want to allow repeated elements you can remove the remaining variable and replace it with values in the recursive call to GetCombinations.

Please note the use of the yield keyword. This means that the code uses deferred execution. There is no need to store any intermediate results before you actually enumerate the result.

public static IEnumerable<IEnumerable<T>> GetCombinations<T>(IEnumerable<T> values, int threshold)
{
    var remaining = values;

    foreach (T value in values)
    {
        yield return value.Yield();

        if (threshold < 2)
        {
            continue;
        }

        remaining = remaining.Skip(1);

        foreach (var combination in GetCombinations(remaining, threshold - 1))
        {
            yield return value.Yield().Concat(combination);
        }
    }
}

public static IEnumerable<T> Yield<T>(this T item)
{
    yield return item;
}

For the integer array { 1, 2, 3, 4, 5 } the output is:

1
1, 2
1, 2, 3
1, 2, 4
1, 2, 5
1, 3
1, 3, 4
1, 3, 5
1, 4
1, 4, 5
1, 5
2
2, 3
2, 3, 4
2, 3, 5
2, 4
2, 4, 5
2, 5
3
3, 4
3, 4, 5
3, 5
4
4, 5
5
like image 194
pescolino Avatar answered Apr 06 '23 00:04

pescolino


Assuming you already have a solution to find the combinations for a particular count (which you've said you have), let's say of the signature:

public static IEnumerable<IEnumerable<T>> Combinations<T>(
    IList<T> source, int count)

Then you can easily get the combinations for all counts by calling it N times:

public static IEnumerable<IEnumerable<T>> Combinations<T>(IList<T> source)
{
    return Enumerable.Range(0, source.Count)
            .SelectMany(i => Combinations(source, i));
}
like image 35
Servy Avatar answered Apr 05 '23 23:04

Servy