1

This one should not be too hard but my mind seems to be having a stack overflow (huehue). I have a series of Lists and I want to find all permutations they can be ordered in. All of the lists have different lengths.

For example:

List 1: 1

List 2: 1, 2

All permutations would be:

1, 1

1, 2

In my case I don't switch the numbers around. (For example 2, 1) What is the easiest way to write this?

Emric Månsson
  • 495
  • 6
  • 15
  • 1
    Possible duplicate of [Listing all permutations of a string/integer](http://stackoverflow.com/questions/756055/listing-all-permutations-of-a-string-integer) –  Jul 31 '16 at 07:54
  • What do you mean by a series of lists? You example only shows two. Do you have only two or do you have more? Are you after the cartesian product of all the lists? – Enigmativity Jul 31 '16 at 07:59
  • Do you mean permutations? Or maybe you really mean combinations (aka cartesian product)? Combinations of two lists ABC and 12 would be A1, B1, C1, A2, B2, C2 – Matthew Watson Jul 31 '16 at 08:12
  • That's right I mean combinations with several lists. Imagine a bike lock. Sorry for being unclear. – Emric Månsson Jul 31 '16 at 08:32
  • @EmricMånsson - Sorry, but that makes it even less clear. How do you get combinations with several lists? – Enigmativity Jul 31 '16 at 08:47
  • @Enigmativity Like you illustrated with list ABC and 12. But with more lists basically. Example: AB, 12 and $%. A1$, A1%, A2$, A2% and so on. – Emric Månsson Jul 31 '16 at 08:56
  • [See this article by Eric Lippert](https://blogs.msdn.microsoft.com/ericlippert/2010/06/28/computing-a-cartesian-product-with-linq/) – Matthew Watson Jul 31 '16 at 10:18

4 Answers4

1

I can't say if the following is the easiest way, but IMO it's the most efficient way. It's basically a generalized version of the my answer to the Looking at each combination in jagged array:

public static class Algorithms
{
    public static IEnumerable<T[]> GenerateCombinations<T>(this IReadOnlyList<IReadOnlyList<T>> input)
    {
        var result = new T[input.Count];
        var indices = new int[input.Count];
        for (int pos = 0, index = 0; ;)
        {
            for (; pos < result.Length; pos++, index = 0)
            {
                indices[pos] = index;
                result[pos] = input[pos][index];
            }
            yield return result;
            do
            {
                if (pos == 0) yield break;
                index = indices[--pos] + 1;
            }
            while (index >= input[pos].Count);
        }
    }
}

You can see the explanation in the linked answer (shortly it's emulating nested loops). Also since for performace reasons it yields the internal buffer w/o cloning it, you need to clone it if you want store the result for later processing.

Sample usage:

var list1 = new List<int> { 1 };
var list2 = new List<int> { 1, 2 };
var lists = new[] { list1, list2 };

// Non caching usage
foreach (var combination in lists.GenerateCombinations())
{
    // do something with the combination
}

// Caching usage
var combinations = lists.GenerateCombinations().Select(c => c.ToList()).ToList();

UPDATE: The GenerateCombinations is a standard C# iterator method, and the implementation basically emulates N nested loops (where N is the input.Count) like this (in pseudo code):

for (int i0 = 0; i0 < input[0].Count; i0++)
for (int i1 = 0; i1 < input[1].Count; i1++)
for (int i2 = 0; i2 < input[2].Count; i2++)
...
for (int iN-1 = 0; iN-1 < input[N-1].Count; iN-1++)
yield { input[0][i0], input[1][i1], input[2][i2], ..., input[N-1][iN-1] }

or showing it differently:

for (indices[0] = 0; indices[0] < input[0].Count; indices[0]++)
{
    result[0] = input[0][indices[0]];
    for (indices[1] = 0; indices[1] < input[1].Count; indices[1]++)
    {
        result[1] = input[1][indices[1]];
        // ...
        for (indices[N-1] = 0; indices[N-1] < input[N-1].Count; indices[N-1]++)
        {
            result[N-1] = input[N-1][indices[N-1]];
            yield return result;
        }
    }
}
Community
  • 1
  • 1
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Thanks for the answer! This seems like a very valid solution. However I'm kinda new to IEnumerables and optional values in a for loop. I will come back in a couple of hours when I understand the code and upvote your answer. :D – Emric Månsson Jul 31 '16 at 10:14
  • You are welcome. I wasn't sure if you need a solution or understanding how to implement it, so I assumed the former, otherwise probably would concentrated more on the explanation. – Ivan Stoev Jul 31 '16 at 10:38
  • Ok so I still don't fully understand the code (also read the answer from the other post). ._. If you feel like it I would very much appreciate an explanation of what is happening. I've read a bit about IEnumerables but still can't wrap my head around it. – Emric Månsson Jul 31 '16 at 16:55
1

I made the following IEnumerable<IEnumerable<TValue>> class to solve this problem which allows use of generic IEnumerable's and whose enumerator returns all permutations of the values, one from each inner list. It can be conventiently used directly in a foreach loop.

It's a variant of Michael Liu's answer to IEnumerable and Recursion using yield return

I've modified it to return lists with the permutations instead of the single values.

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

namespace Permutation
{
    public class ListOfListsPermuter<TValue> : IEnumerable<IEnumerable<TValue>>
    {
        private int count;
        private IEnumerable<TValue>[] listOfLists;

        public ListOfListsPermuter(IEnumerable<IEnumerable<TValue>> listOfLists_)
        {
            if (object.ReferenceEquals(listOfLists_, null))
            {
                throw new ArgumentNullException(nameof(listOfLists_));
            }
            listOfLists =listOfLists_.ToArray();
            count = listOfLists.Count();
            for (int i = 0; i < count; i++)
            {
                if (object.ReferenceEquals(listOfLists[i], null))
                {
                    throw new NullReferenceException(string.Format("{0}[{1}] is null.", nameof(listOfLists_), i));
                }
            }
        }

        // A variant of Michael Liu's answer in StackOverflow
        // https://stackoverflow.com/questions/2055927/ienumerable-and-recursion-using-yield-return
        public IEnumerator<IEnumerable<TValue>> GetEnumerator()
        {
            TValue[] currentList = new TValue[count];
            int level = 0; 
            var enumerators = new Stack<IEnumerator<TValue>>();
            IEnumerator<TValue> enumerator = listOfLists[level].GetEnumerator();
            try
            {
                while (true)
                {
                    if (enumerator.MoveNext())
                    {
                        currentList[level] = enumerator.Current;
                        level++;
                        if (level >= count)
                        {
                            level--;
                            yield return currentList;
                        }
                        else
                        {
                            enumerators.Push(enumerator);
                            enumerator = listOfLists[level].GetEnumerator();
                        }
                    }
                    else
                    {
                        if (level == 0)
                        {
                            yield break;
                        }
                        else
                        {
                            enumerator.Dispose();
                            enumerator = enumerators.Pop();
                            level--;
                        }
                    }
                }
            }
            finally
            {
                // Clean up in case of an exception.
                enumerator?.Dispose();
                while (enumerators.Count > 0) 
                {
                    enumerator = enumerators.Pop();
                    enumerator.Dispose();
                }
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

You can use it directly in a foreach like this:

        public static void Main(string[] args)
        {
            var listOfLists = new List<List<string>>()
            {
                { new List<string>() { "A", "B" } },
                { new List<string>() { "C", "D" } }
            };
            var permuter = new ListOfListsPermuter<string>(listOfLists);
            foreach (IEnumerable<string> item in permuter)
            {
                Console.WriteLine("{ \"" + string.Join("\", \"", item) + "\" }");
            }
        }

The output:

{ "A", "C" }
{ "A", "D" }
{ "B", "C" }
{ "B", "D" }
Christopher Hamkins
  • 1,442
  • 9
  • 18
0

Nested loops:

List<int> listA = (whatever), listB = (whatever);
var answers = new List<Tuple<int,int>>;
for(int a in listA)
    for(int b in listB)
        answers.add(Tuple.create(a,b));
// do whatever with answers
solarshado
  • 567
  • 5
  • 18
  • That is nearly right. However in my case I can end up with 40 lists with different lengths. I read the tuple documentation and you can make 8 sized tuples. Most probably I could combine a list easily from the a and b ints but I don't know how I would make ~40 nested for loops? Is there a recursive solution? – Emric Månsson Jul 31 '16 at 09:13
0

Try this:

Func<IEnumerable<string>, IEnumerable<string>> combine = null;
combine = xs =>
    xs.Skip(1).Any()
    ? xs.First().SelectMany(x => combine(xs.Skip(1)), (x, y) => String.Format("{0}{1}", x, y))
    : xs.First().Select(x => x.ToString());

var strings = new [] { "AB", "12", "$%" };

foreach (var x in combine(strings))
{
    Console.WriteLine(x);
}

That gives me:

A1$
A1%
A2$
A2%
B1$
B1%
B2$
B2%
Enigmativity
  • 113,464
  • 11
  • 89
  • 172