【问题标题】:Get all combinations of List<string> where order doesn't matter and minimum of 2 elements with C#使用 C# 获取 List<string> 的所有组合,其中顺序无关紧要,最少 2 个元素
【发布时间】:2020-11-25 04:39:55
【问题描述】:

我已经查看了有关组合和排列的所有 StackOverflow 帖子,但找不到我正在寻找的确切细致入微的答案,我也无法思考如何正确实现这一点。理想情况下,我正在寻找一种不使用递归的解决方案,这样我就可以避免大列表上的堆栈溢出。如何获得具有以下要求的字符串列表的所有组合?

  • 每个结果列表中至少包含 2 个元素
  • 顺序对于平等并不重要。换句话说,("a", "b") == ("b", "a")

例子:

var input = new List<string> { "a", "b", "c", "d" };

// TODO


// Note: the final order of lists in expectedResult doesn't matter
var expectedResult = new List<List<string>>{
    new List<string> { "a", "b", "c", "d" },
    new List<string> { "a", "b", "c" },
    new List<string> { "a", "b", "d" },
    new List<string> { "a", "c", "d" },
    new List<string> { "b", "c", "d" },
    new List<string> { "a", "b" },
    new List<string> { "a", "c", },
    new List<string> { "a", "d" },
    new List<string> { "b", "c" },
    new List<string> { "b", "d" },
    new List<string> { "c", "d" }
};

Assert.IsTrue(expectedResult.Count == 11);
Assert.IsTrue(expectedResult.All(list => list.Count >= 2));

【问题讨论】:

  • 喜欢循环赛?
  • “顺序对相等无关紧要” - 意思是如果你产生 { "a", "b" } 那么你不想想要产生 { "b", "a" }?
  • @JonathanAlfaro 我不熟悉这个类比......希望上面的断言定义了解决方案。
  • @Enigmativity - 是的,正确
  • @BlueSky - 听起来更像是一个比喻而不是一个类比...... :-)

标签: c# combinations permutation


【解决方案1】:

您可以使用数字掩码相当轻松地做到这一点,以跟踪您访问过的内容和一些位移

给定

public static IEnumerable<T[]> GetCombinations<T>(List<T> source)
{
   for (var i = 0; i < (1 << source.Count); i++)
      yield return source
         .Where((t, j) => (i & (1 << j)) != 0)
         .ToArray();
}

或者如果你有 for 循环强迫症 和类似的扩展方法

public static IEnumerable<T[]> GetCombinations<T>(this List<T> source)
   => Enumerable
      .Range(0, 1 << source.Count)
      .Select(i => source
         .Where((t, j) => (i & (1 << j)) != 0)
         .ToArray());

用法

var input = new List<string> {"a", "b", "c", "d"};

var results = GetCombinations(input)
      .Where(x => x.Length >= 2);

foreach (var items in results)
   Console.WriteLine(string.Join(",",items));

输出

a,b
a,c
b,c
a,b,c
a,d
b,d
a,b,d
c,d
a,c,d
b,c,d
a,b,c,d

加入胡椒盐,按口味分类


前提是,

  1. 对要返回的组合使用位掩码。每个 bit 代表一个truefalse,并与集合中的一个元素相关联。即掩码1100 将意味着返回组合C,D 等。

  2. 使用一个 loop 范围为Math.Pow(2, source.Count)(甚至更好,如Enigmativity 建议的1 &lt;&lt; source.Count)给定集合的最大组合以增加掩码 ..

  3. 然后把它全部放在一个迭代器方法中,一桶笑声......所有的组合都会给出。

唯一的限制是数组的最大大小限制为 max bitsnumeric 类型 你可以 bitshift (在当前实现)即 32/64 个元素,具体取决于 intlong,这将分别产生 2^32 / 2^64 组合。

更新

Enigmativity 的进一步评论(以及我不知道的事情)

您可以使用BigInteger 绕过最大位数限制

BigInteger one = 1; 

for (BigInteger i = 0; i < one << source.Count; i++) 
   yield return source.Where((_, j) => (i & one << j) != 0).ToArray();

【讨论】:

  • 我可以建议避免使用Math.Pow(2, source.Count),因为它使用double 进行计算,而只需使用1 &lt;&lt; source.Count 代替吗?
  • @Enigmativity 伟大的建议,更新和归因
  • 您可以使用BigInteger 绕过最大位数限制 - BigInteger one = 1; for (BigInteger i = 0; i &lt; one &lt;&lt; source.Count; i++) yield return source.Where((_, j) =&gt; (i &amp; one &lt;&lt; j) != 0).ToArray();
  • @Enigmativity 再次感谢。我不是你可以位移一个`BigInteger`。很高兴知道!
  • 是的,我必须自己查一下 - public static BigInteger operator &lt;&lt;(BigInteger value, int shift)
【解决方案2】:

这里有一个解决问题的方法:

static IEnumerable<IEnumerable<string>> GetPermutations(IList<string> input)
{
    List<List<string>> output = new List<List<string>>();

    for (int count = input.Count; count >= 2; count--)
    {
        var indexes = new int[count];

        // Set initial index values to be default (i.e. 0,1,2,3)
        for (int index = 0; index < count; index++)
        {
            indexes[index] = index;
        }

        // Start at the last index (i.e. D) and build output
        for (int arrayIndex = indexes.Length - 1; arrayIndex >= 0; arrayIndex--)
        {
            bool indexCanBeIncremented = true;
            while (indexCanBeIncremented)
            {
                List<string> currentList = new List<string>();

                foreach (var index in indexes)
                {
                    currentList.Add(input[index]);
                }

                if (IsUnique(output, currentList))
                {
                    output.Add(currentList);
                }                       

                if (arrayIndex == indexes.Length - 1 && indexes[arrayIndex] < input.Count - 1 || 
                    arrayIndex < indexes.Length - 1 && indexes[arrayIndex] + 1 != indexes[arrayIndex + 1])
                {
                    indexes[arrayIndex] += 1;
                }
                else
                {
                    indexCanBeIncremented = false;
                }
            }
        }
    }

    return output;
}

public static bool IsUnique(List<List<string>> collection, List<string> input)
{
    foreach (var list in collection)
    {
        if (input.SequenceEqual(list))
        {
            return false;
        }
    }

    return true;
}

此代码基本上跟踪输入的所有索引,并将根据排列构建输出。

输出

a b c d
a b c
a b d
a c d
b c d
a b
a c
a d
b d
c d

【讨论】:

    猜你喜欢
    • 2011-12-11
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多