生成排列的LINQ代码的说明

时间:2012-10-04 17:36:03

标签: c# linq algorithm

有人可以向我解释这段代码如何生成排列吗?

我看到它正在使用递归列表构建,但我不能完全了解精确的机制。

public static IEnumerable<IEnumerable<T>> Permute<T>(this IEnumerable<T> list)
{
    if (list.Count() == 1)
        return new List<IEnumerable<T>> { list };
    return list
        .Select((a, i1) => 
                    Permute(list.Where((b, i2) => i2 != i1))
                   .Select(b => (new List<T> { a }).Union(b)))
        .SelectMany(c => c);
}

1 个答案:

答案 0 :(得分:6)

首先,我要指出这依赖于Union的排序,这是未指定的,只有在原始集合中的值是唯一的情况下才有效。将Union更改为Concat可以解决此问题。

我怀疑它的价格也非常昂贵。总之...

你可以通过归纳来理解它。它绝对适用于基于if的单个元素集合。那么,让我们从那里开始。

假设我们假设它适用于这样的大小n的集合:

{ e1, ... en }

现在让我们看看它是否适用于大小为n + 1的集合。该调用将绕过“if”,并进入递归返回部分。所以我们离开了:

list.Select((a, i1) => 
                Permute(list.Where((b, i2) => i2 != i1))
               .Select(b => (new List<T> { a }).Union(b)))
    .SelectMany(c => c);

SelectMany部分只是展开了一系列收藏 - 这很简单。所以让我们关注Select电话。它在说......

  • 对于原始集合中的每个元素(a),我们知道它具有索引i1 ...
    • 制定清单除了那个元素(那是list.Where(...)部分)
    • 对于那个较小的列表的每个排列(即Permute调用)......
    • ...生成一个新的列表,其中包含元素a,后跟“当前排列”(即new List(...).Union(b)部分。)

因此,如果我们的集合是{x,y,z},我们会得到:

  • { y, z }
  • 为前缀的x的每个集合
  • { x, z }
  • 为前缀的y的每个集合
  • { x, y }
  • 为前缀的z的每个集合

这几乎定义了“每个排列”......所以它适用于n + 1

鉴于基本情况和归纳步骤,它适用于任何大小或等于1的集合。