3

Possible Duplicate:
Combination of List<List<int>>

I have multiple Lists, could be 2 or 3 up to 10 lists, with multiple values in them. Now what I need to do is to get a combination of all of them.

For example, if I have 3 lists with the following values:

  • List 1: 3, 5, 7
  • List 2: 3, 5, 6
  • List 3: 2, 9

I would get these combinations

  • 3,3,2
  • 3,3,9
  • 3,5,2 Etc..

Now the problem is I cannot do this easily because I do not know how many lists I have, therefore determine how many loops I need.

Community
  • 1
  • 1
TD Lemon
  • 385
  • 4
  • 18

5 Answers5

2

You could probably make that a lot easier, but this is what I had in mind just now:

List<List<int>> lists = new List<List<int>>();
lists.Add(new List<int>(new int[] { 3, 5, 7 }));
lists.Add(new List<int>(new int[] { 3, 5, 6 }));
lists.Add(new List<int>(new int[] { 2, 9 }));

int listCount = lists.Count;
List<int> indexes = new List<int>();
for (int i = 0; i < listCount; i++)
    indexes.Add(0);

while (true)
{
    // construct values
    int[] values = new int[listCount];
    for (int i = 0; i < listCount; i++)
        values[i] = lists[i][indexes[i]];

    Console.WriteLine(string.Join(" ", values));

    // increment indexes
    int incrementIndex = listCount - 1;
    while (incrementIndex >= 0 && ++indexes[incrementIndex] >= lists[incrementIndex].Count)
    {
        indexes[incrementIndex] = 0;
        incrementIndex--;
    }

    // break condition
    if (incrementIndex < 0)
        break;
}

If I’m not completely wrong, this should be O(Nm) with m being the number of lists and N the number of permutations (product of the lengths of all m lists).

poke
  • 369,085
  • 72
  • 557
  • 602
1

you could make a List<List<yourValueType> mainlist in which you put all your lists. then with a simple

int numberOfIterations = 1;
foreach(var item in mainlist)
{
    numberOfIterations *= item.Count;
}

this would get the amount of iterations you would have to execute in total.

CuccoChaser
  • 1,059
  • 2
  • 15
  • 27
  • No, for instance, if I have three lists, I would need three overlaped foreach loops – TD Lemon Nov 08 '12 at 10:23
  • Not really, maybe a max of 2 for any amount of number of lists. As the mainlist contains all. Unless ofcourse inside your list with the values are other lists. Then it becomes a problem yes. If that is the case that your lists with values contain lists too, then you should state this in your question to get more accurate answers ;-) – CuccoChaser Nov 08 '12 at 10:26
  • I get now what you mean, I editted my code so it would get the number of iterations you would have to do in total. – CuccoChaser Nov 08 '12 at 10:33
  • 1
    You might want to start with `1`, not `0`. – Rawling Nov 08 '12 at 12:36
  • 1
    oh definitly looked over that small but terrible mistake. tnx – CuccoChaser Nov 08 '12 at 12:40
1

Non-recursive solution, works on any IEnumerables (not just lists) without solidifying them:

public static IEnumerable<IEnumerable<T>> Permutations<T>(
    this IEnumerable<IEnumerable<T>> source)
{
    // Check source non-null, non-empty?

    var enumerables = source.ToArray();
    Stack<IEnumerator<T>> fe = new Stack<IEnumerator<T>>();
    fe.Push(enumerables[0].GetEnumerator());

    while (fe.Count > 0)
    {
        if (fe.Peek().MoveNext())
        {
            if (fe.Count == enumerables.Length)
                yield return new Stack<T>(fe.Select(e => e.Current));
            else
                fe.Push(enumerables[fe.Count].GetEnumerator());
        }
        else
        {
            fe.Pop().Dispose();
        }
    }
}
Rawling
  • 49,248
  • 7
  • 89
  • 127
  • I've been playing around with this a bit to try to make sure it works if `GetEnumerator` doesn't always return the same thing, and in doing so I think I've got this to a point where it's pretty similar to exactly what nested `foreach` loops would really do. – Rawling Nov 08 '12 at 12:32
  • Nice idea i'll +1 you, though you aren't filtering around empty enumerables, hence if you have one it will return false on the movenext get popped, previous one will be moved, then the empty enumerator will be pushed back on and loop.... maybe enumerables = source.Where(e => e.Any()).ToArray(); ? – user1793607 Nov 08 '12 at 12:44
  • @user1793607 If you have an empty enumerable in a stack of `foreach` loops, you also won't get any output, and I wanted to emulate that - you can always filter out any empty enumerables before you call this. – Rawling Nov 08 '12 at 12:48
  • Fair enough. On a micro optimisation note I wonder if return new Stack(fe.Select(e => e.Current)) might be quicker? – user1793607 Nov 08 '12 at 13:15
  • @user1793607 You're right, that'll save buffering them, reversing them and buffering them again. – Rawling Nov 08 '12 at 13:29
  • @Rawling Do you have an example of how to use this code? – Master Yoda May 05 '15 at 14:59
  • @KyleT (1) Add method to a `public static class` (as it's an extension method) (2) call like `new[] { new[] { "Hello", "Goodbye" }, new[] { "World", "Room" } }.Permutations().Select(strings => string.Join(" ", strings))` (3) get result sequence like `"Hello World", "Goodbye World", "Hello Room", "Goodbye Room"` – Rawling May 05 '15 at 15:03
  • @Rawling How could i do this if i need to add the values to properties of a class instead of outputting as strings? – Master Yoda May 06 '15 at 15:06
  • @KyleT I guess you'd do something like `... .Permutations().Select(p => p.ToArray()).Select(a => new MyClass { FirstProp=a[0], SecondProp=a[1], ThirdProp=a[2] })` but they'd all have to be properties of the same type. – Rawling May 06 '15 at 16:31
  • @Rawling Exactly, the problem im facing is that there are multiple different types that will go through the function. – Master Yoda May 06 '15 at 16:36
0

Not very efficient but very easy to understand approach might be to solve this task recursively. Consider a method which computes permutations for N lists. If you have such a method then you can easily compute permutations for N+1 lists by combining all permutation of N lists with every number in the last list. You should also handle corner case which permutations of 0 lists. Then implementation seems to be straightforward:

IEnumerable<IEnumerable<T>> GetAllPermutations<T>(IEnumerable<IEnumerable<T>> inputLists)
{
    if (!inputLists.Any()) return new [] { Enumerable.Empty<T>() };
    else
    {
        foreach (var perm in GetAllPermutations(inputLists.Skip(1)))
            foreach (var x in inputLists.First())
                yield return new[]{x}.Concat(perm);
    }
}
Snowbear
  • 16,924
  • 3
  • 43
  • 67
  • This would be great but can you provide an example of using this solution where you can use multiple types as opposed to just int? – Master Yoda May 15 '15 at 13:16
0

As an alternative, following rawlings general idea the following should work

public static IEnumerable<IEnumerable<T>> Permutations<T> (this IEnumerable<IEnumerable<T>> underlying)
{
    var enumerators = new Queue<IEnumerator<T>>(underlying.Select(u => u.GetEnumerator())
                                                          .Where(enumerator => enumerator.MoveNext());
    Boolean streaming = enumerators.Any();
    if(streaming)
    {
        IEnumerable<T> result;

        IEnumerator<T> finalEnumerator = enumerators.Dequeue();
        Func<Boolean,Boolean> finalAction = b => b ? b : finalEnumerator.MoveNext();

        Func<Boolean,Boolean> interimAction = 
         enumerators.Reverse()
                    .Select(enumerator => new Func<Boolean,Boolean>(b => b ? b : (enumerator.MoveNext() ? true : enumerator.ResetMove())))
                    .Aggregate((f1,f2) => (b => f1(f2(b)));
        enumerators.Enqueue(finalEnumerator);

        Func<Boolean,Boolean> permutationAction = 
                              interimAction == null ? 
                              finalAction :
                              b => finalAction(interimAction(b));

        while(streaming)
        {
              result = new Queue<T>(enumerators.Select(enumerator => enumerator.Current))
              streaming = permutationAction(true);
              yield return result;
        }
}

private static Boolean ResetMove<T>(this IEnumerator<T> underlying)
{
     underlying.Reset();
     underlying.MoveNext();
     return false;
}
user1793607
  • 531
  • 2
  • 6