How to iterate lists with different lengths to find all permutations?

后端 未结 3 1792
长发绾君心
长发绾君心 2020-12-22 07:27

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

相关标签:
3条回答
  • 2020-12-22 07:36

    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
    
    0 讨论(0)
  • 2020-12-22 07:40

    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%
    
    0 讨论(0)
  • 2020-12-22 07:47

    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;
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题