使用LINQ生成数字序列

时间:2010-04-29 12:04:40

标签: c# linq

我尝试编写一个LINQ语句,它返回所有可能的数字组合(我需要这个测试,我受到了这个article of Eric Lippert的启发)。我调用的方法原型如下:

IEnumerable<Collection<int>> AllSequences( int start, int end, int size );

规则是:

  • 所有返回的集合的长度为size
  • 集合中的数字值必须增加
  • 应使用startend之间的每个数字

所以调用AllSequences( 1, 5, 3 )应该会产生10个集合,每个集合大小为3:

1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4 
2 3 5
2 4 5 
3 4 5

现在,不知怎的,我真的很想看到纯LINQ 解决方案。我可以自己编写一个非LINQ解决方案,所以请不要在没有LINQ的情况下投入解决方案 到目前为止,我的尝试已经结束,我必须使用我的方法的递归调用的结果加入一个数字 - 例如:

return from i in Enumerable.Range( start, end - size + 1 )
       select BuildCollection(i, AllSequences( i, end, size -1));

但我无法管理它在LINQ基础上实现BuildCollection() - 甚至跳过此方法调用。你能帮帮我吗?

3 个答案:

答案 0 :(得分:50)

 Enumerable.Range(1, 12)
           .Select(x => (x - 1) + 1);

答案 1 :(得分:34)

想想我已经得到了它。

IEnumerable<List<int>> AllSequences(int start, int end, int size)
{
    if (size == 0)
        return Enumerable.Repeat<List<int>>(new List<int>(), 1);

    return from i in Enumerable.Range(start, end - size - start + 2)
           from seq in AllSequences(i + 1, end, size - 1)
           select new List<int>{i}.Concat(seq).ToList();
}

答案 2 :(得分:13)

我想,像下面这样的事情应该可以胜任。

public static IEnumerable<IEnumerable<int>> AllSequences(int start, int end,
    int size)
{
    return size <= 0 ? new[] { new int[0] } :
           from i in Enumerable.Range(start, end - size - start + 2)
           from seq in AllSequences(i + 1, end, size - 1)
           select Enumerable.Concat(new[] { i }, seq);
}

解决方案的关键是compound from clause,这对于处理嵌套的可枚举非常方便。

请注意,我已将方法签名稍微更改为IEnumerable<IEnumerable<int>>,因为使用(纯)LINQ时更方便。但是,如果您愿意,可以随时将其轻松转换为IEnumerable<ICollection<int>>

如果代码需要任何解释,请告诉我,但我希望LINQ语法能够让它变得相当清楚。

编辑1:修正了错误并提高了简洁性。

编辑2: 因为我很无聊而且没有更好的事情要做(不,不是真的),我想我会编写一个扩展方法来计算给定元素列表的组合,并使用AllSequences方法。 / p>

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IList<T> source,
    int num)
{
    return AllSequences(0, source.Count - 1, num).Select(
        seq => seq.Select(i => source[i]));
}

也许不是最有效的计算组合方式,但肯定是非常紧凑的代码!