C#List <string>的所有唯一组合

时间:2018-10-17 21:08:02

标签: c# combinations permutation

这个问题已经问了很多遍了,但是我见过的每个SO帖子都希望值的长度是特定的,而我只是想知道每个唯一的组合,而不论长度如何。

下面的代码仅提供了一个列表,其中恰好有3个组合条目(它们不是唯一的)。

List<string> list = new List<string> { "003_PS", "003_DH", "003_HEAT" };
var perms = list.GetPermutations();

public static class Extensions 
{
    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            var itemAsEnumerable = Enumerable.Repeat(item, 1);
            var subSet = items.Except(itemAsEnumerable);
            if (!subSet.Any())
            {
                yield return itemAsEnumerable;
            }
            else
            {
                foreach (var sub in items.Except(itemAsEnumerable).GetPermutations())
                {
                    yield return itemAsEnumerable.Union(sub);
                }
            }
        }
    }
}

/*
  OUTPUT:
      003_PS,   003_DH,   003_HEAT
      003_PS,   003_HEAT, 003_DH
      003_DH,   003_PS,   003_HEAT
      003_DH,   003_HEAT, 003_PS
      003_HEAT, 003_PS,   003_DH
      003_HEAT, 003_DH,   003_PS
*/

我要的是这个东西

/*
OUTPUT:
    003_PS, 003_DH, 003_HEAT
    003_PS, 003_DH
    003_PS, 003_HEAT
    003_PS

    003_DH, 003_HEAT
    003_DH

    003_HEAT
 */

大小不限于3个项目,每个条目都是唯一的。

此功能需要更改什么?我愿意使用LINQ解决方案

感谢您的帮助。谢谢!

编辑:以上列表输出不准确

**编辑#2:   这是我要寻找的Javascript版本。但是我不知道什么是C#等效语法:

function getUniqueList(arr){
    if (arr.length === 1) return [arr];
    else {
        subarr = getUniqueList(arr.slice(1));
        return subarr.concat(subarr.map(e => e.concat(arr[0])), [[arr[0]]]);
    }
}

1 个答案:

答案 0 :(得分:5)

好,您有n个项目,并且想要这些项目的2 n 个组合。

仅供参考,在设备上进行操作时称为“电源设备”;由于序列可以包含重复项,而集合不能包含重复项,因此您想要的不是精确幂集,但是它足够接近。如果您的序列包含重复项,那么我呈现的代码在某种意义上是错误的,因为我们会说{10, 10}的结果将是{}, {10}, {10}, {10, 10},但是如果您不喜欢这样做,那么请删除在开始之前重复操作,然后您将在计算功率集。

计算所有组合的顺序非常简单。我们的理由如下:

  • 如果项为零,则只有一个组合:项为零的组合。
  • 如果有n> 0个项目,则组合是所有带有 的组合是第一个元素,而所有组合是没有的第一个元素。

让我们写代码:

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

static class Extensions
{
    public static IEnumerable<T> Prepend<T>(
        this IEnumerable<T> items,
        T first)
    {
        yield return first;
        foreach(T item in items)
            yield return item;
    }
    public static IEnumerable<IEnumerable<T>> Combinations<T>(
        this IEnumerable<T> items)
    {
        if (!items.Any())
            yield return items;
        else
        {
          var head = items.First();
          var tail = items.Skip(1);
          foreach(var sequence in tail.Combinations()) 
          {
            yield return sequence; // Without first
            yield return sequence.Prepend(head);
          }
       }
    }
}

public class Program
{
    public static void Main()
    {
        var items = new [] { 10, 20, 30 };
        foreach(var sequence in items.Combinations())
            Console.WriteLine($"({string.Join(",", sequence)})");
    }
}

然后我们得到输出

()
(10)
(20)
(10,20)
(30)
(10,30)
(20,30)
(10,20,30)

请注意,如果原始序列很长,此代码效率不高

练习1:您明白为什么吗?

练习2:对于长序列,您能否在时间和空间上使此代码高效? (提示:如果您可以使headtailprepend操作的内存效率更高,那将有很长的路要走。)

练习3:您在JavaScript中使用了相同的算法;可以在JS算法的各个部分和C#版本之间进行类比吗?

当然,我们假设原始序列中的项目数很短。如果是一百,您将要等待很长的时间来枚举所有数万亿个组合。

如果要获得具有特定数量元素的所有组合 ,请参阅我有关该算法的系列文章:https://ericlippert.com/2014/10/13/producing-combinations-part-one/