【问题标题】:Combination of List<List<int>>List<List<int>>的组合
【发布时间】:2009-02-13 12:07:45
【问题描述】:

我有一个这种类型的列表 List> 包含这个

List<int> A = new List<int> {1, 2, 3, 4, 5};
List<int> B = new List<int> {0, 1};
List<int> C = new List<int> {6};
List<int> X = new List<int> {....,....};

我想要所有这样的组合

1-0-6
1-1-6
2-0-6
2-1-6
3-0-6

等等。

您认为这可以使用 Linq 解决吗?

【问题讨论】:

  • 这是一个交叉产品,相信 Garry 的回答,它会做到的。
  • 维数是否固定为3?或者(来自 X)这是动态的吗?

标签: c# linq generics list combinations


【解决方案1】:

这和this answer我给另一个问题很相似:

var combinations = from a in A
                   from b in B
                   from c in C
                   orderby a, b, c
                   select new List<int> { a, b, c };

var x = combinations.ToList();

对于可变数量的输入,现在添加了泛型:

var x = AllCombinationsOf(A, B, C);

public static List<List<T>> AllCombinationsOf<T>(params List<T>[] sets)
{
    // need array bounds checking etc for production
    var combinations = new List<List<T>>();

    // prime the data
    foreach (var value in sets[0])
        combinations.Add(new List<T> { value });

    foreach (var set in sets.Skip(1))
        combinations = AddExtraSet(combinations, set);

    return combinations;
}

private static List<List<T>> AddExtraSet<T>
     (List<List<T>> combinations, List<T> set)
{
    var newCombinations = from value in set
                          from combination in combinations
                          select new List<T>(combination) { value };

    return newCombinations.ToList();
}

【讨论】:

  • 我认为这行不通...我相信(来自 X)OP 意味着列表中的项目数(以及维度数)是动态的
  • 嗯,根据我的其他参考答案仍然有可能,您可以只使用一组参数数组并构建它。我会在评论中要求澄清。
  • 我看到你打败了我!
  • 是的,伙计们,项目的数量是动态的!
  • 我明白了 - 你在循环中一次重复跨越 2 个列表 - 很可爱。
【解决方案2】:

如果维数是固定的,这简直就是SelectMany

var qry = from a in A
          from b in B
          from c in C
          select new {A=a,B=b,C=c};

但是,如果维数是由数据控制的,则需要使用递归:

static void Main() {
    List<List<int>> outerList = new List<List<int>>
    {   new List<int>(){1, 2, 3, 4, 5},
        new List<int>(){0, 1},
        new List<int>(){6,3},
        new List<int>(){1,3,5}
    };
    int[] result = new int[outerList.Count];
    Recurse(result, 0, outerList);
}
static void Recurse<TList>(int[] selected, int index,
    IEnumerable<TList> remaining) where TList : IEnumerable<int> {
    IEnumerable<int> nextList = remaining.FirstOrDefault();
    if (nextList == null) {
        StringBuilder sb = new StringBuilder();
        foreach (int i in selected) {
            sb.Append(i).Append(',');
        }
        if (sb.Length > 0) sb.Length--;
        Console.WriteLine(sb);
    } else {
        foreach (int i in nextList) {
            selected[index] = i;
            Recurse(selected, index + 1, remaining.Skip(1));
        }
    }
}

【讨论】:

  • 我以不同的方式管理它,根据您的观点可能更具可读性。你怎么看?
【解决方案3】:

下面使用 .Join 方法生成组合怎么样?

static void Main()
{
    List<List<int>> collectionOfSeries = new List<List<int>>
                                {   new List<int>(){1, 2, 3, 4, 5},
                                    new List<int>(){0, 1},
                                    new List<int>(){6,3},
                                    new List<int>(){1,3,5}
                                };
    int[] result = new int[collectionOfSeries.Count];

    List<List<int>> combinations = GenerateCombinations(collectionOfSeries);

    Display(combinations); 
}

此方法GenerateCombinations(..) 主要工作是生成组合。 此方法是通用的,因此可用于生成任何类型的组合。

private static List<List<T>> GenerateCombinations<T>(
                                List<List<T>> collectionOfSeries)
{
    List<List<T>> generatedCombinations = 
        collectionOfSeries.Take(1)
                          .FirstOrDefault()
                          .Select(i => (new T[]{i}).ToList())                          
                          .ToList();

    foreach (List<T> series in collectionOfSeries.Skip(1))
    {
        generatedCombinations = 
            generatedCombinations
                  .Join(series as List<T>,
                        combination => true,
                        i => true,
                        (combination, i) =>
                            {
                                List<T> nextLevelCombination = 
                                    new List<T>(combination);
                                nextLevelCombination.Add(i);
                                return nextLevelCombination;
                            }).ToList();

    }

    return generatedCombinations;
}

显示助手..

private static void Display<T>(List<List<T>> generatedCombinations)
{
    int index = 0;
    foreach (var generatedCombination in generatedCombinations)
    {
        Console.Write("{0}\t:", ++index);
        foreach (var i in generatedCombination)
        {
            Console.Write("{0,3}", i);
        }
        Console.WriteLine();
    }
    Console.ReadKey();
}

【讨论】:

  • 优秀的解决方案。
【解决方案4】:
//Done in 2 while loops. No recursion required
#include<stdio.h>
#define MAX 100
typedef struct list
{
  int elements[MAX];
}list;
list n[10];
int number,count[10],temp[10];
void print();
int main()
{
  int i,j,mult=1,mult_count;
  printf("Enter the number of lists - ");
  scanf("%d",&number);
  for(i=0;i<number;i++)
  {
    printf("Enter the number of elements - ");
    scanf("%d",&count[i]);
    for(j=0;i<count[i];j++)
    {
      printf("Enter element %d - "j);
      scanf("%d",&n[i].elements[j]);
    }
  }
  for(i=0;i<number;i++)
  temp[i]=0;
  for(i=0;i<number;i++)
  mult*=count[i];
  printf("%d\n",mult);
  mult_count=0;
  while(1)
  {
    print();
    mult_count++;
    if(mult_count==mult)
    break;
    i=0;
    while(1)
    {
      temp[i]++;
      if(temp[i]==count[i])
      {
        temp[i]=0;
        i++;
      }
      else break;
    }
  }
  return 0;
}
void print()
{
  int i;
  for(i=0;i<number;i++)
  {
    printf("%d\n",n[i].elements[temp[i]]);
    printf("\n");
  }
}

【讨论】:

    【解决方案5】:

    只是为了好玩:

    using CSScriptLibrary;
    using System;
    using System.Collections.Generic;
    
    namespace LinqStringTest
    {
        public class Program
        {
            static void Main(string[] args)
            {
    
                var lists = new List<List<int>>() {
                    new List<int> { 0, 1, 2, 3 },
                    new List<int> { 4, 5 },
                    new List<int> { 6, 7 },
                    new List<int> { 10,11,12 },
                };
                var code = GetCode(lists);
                AsmHelper scriptAsm = new AsmHelper(CSScript.LoadCode(code));
    
                var result = (IEnumerable<dynamic>)scriptAsm.Invoke("Script.LinqCombine", lists);
    
                foreach (var item in result)
                {
                    Console.WriteLine(item);
                }
    
                Console.ReadLine();
            }
    
            private static string GetCode(List<List<int>> listsToCombine)
            {
                var froms = "";
                var selects = "";
    
                for (int i = 0; i < listsToCombine.Count; i++)
                {
                    froms += string.Format("from d{0} in lists[{0}]{1}", i, Environment.NewLine);
                    selects += string.Format("D{0} = d{0},", i);
                }
    
                return @"using System;
                  using System.Linq;
                  using System.Collections.Generic;
                  public class Script
                  {
                      public static IEnumerable<dynamic> LinqCombine(List<List<int>> lists)
                      {
                            var x = " + froms + @"
                                    select new { " + selects + @" };
                            return x;
                      }
                  }";
            }
        }
    }
    

    【讨论】:

      【解决方案6】:
          public static List<List<string>> CrossProduct(List<List<string>> s)
          {
              if (!s.Any())
                  return new List<List<string>>();
      
              var c1 = s.First();
              var cRest = s.Skip(1).ToList();
              var sss = from v1 in c1
                        from vRest in CrossProduct(cRest)
                        select (new[] { v1 }.Concat(vRest)).ToList();
              var r = sss.ToList();
              return r;
          }
      

      【讨论】:

        【解决方案7】:

        来自 Abhijeet Nagre 的出色解决方案。在某些系列为空或系列为空时的小改进。

        List<List<T>> generatedCombinations = 
            collectionOfSeries.Where(l => l.Any())
                              .Take(1)
                              .DefaultIfEmpty(new List<T>())
                              .First()
                              .Select(i => (new T[]{i}).ToList())                          
                              .ToList();
        

        【讨论】:

          【解决方案8】:

          从输入列表中生成所有组合

          var combinations = AllCombinationsOf(A, B, C);
          
          public static IEnumerable<List<T>> AllCombinationsOf<T>(params List<T>[] inputs)
          {
              var seed = Enumerable.Repeat(new List<T>(), 1);
              return inputs.Aggregate(seed, CreateCombinations);
          }
          
          private static IEnumerable<List<T>> CreateCombinations<T>(IEnumerable<List<T>> oldCombinations, List<T> newValues)
              =>  from value in newValues
                  from combination in oldCombinations
                  select new List<T>(combination) {value};
          

          this answer 的简化版@Garry Shutler

          Try it online!(测试用例与问题示例一起包含)

          【讨论】:

            【解决方案9】:

            没有 Linq 和递归的解决方案:

            private List<List<T>> GetAllCombinations<T>(List<List<T>> source)
            {
                List<List<T>> result = new List<List<T>>();
            
                foreach (var value in source[0])
                {
                    result.Add(new List<T> { value });
                }
            
                for (int i = 1; i < source.Count; i++)
                {
                    var resultCount = result.Count;
            
                    for (int j = 1; j < source[i].Count; j++)
                    {
                        for (var k = 0; k < resultCount; k++)
                        {
                            result.Add(new List<T>(result[k]));
                        }
                    }
            
                    var t = (result.Count / source[i].Count);
            
                    for (int j = 0; j < source[i].Count; j++)
                    {
                        for (int k = 0; k < t; k++)
                        {
                            result[j * t + k].Add(source[i][j]);
                        }
                    }
                }
            
                return result;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多