需要递归地生成文件数组的每个唯一组合

Posted

技术标签:

【中文标题】需要递归地生成文件数组的每个唯一组合【英文标题】:Need to generate every unique combination of an array of files recursively 【发布时间】:2011-04-19 00:25:39 【问题描述】:

我研究并发现 很多 类似的请求,但没有什么是我需要的。

这是我的问题。我正在使用 C#,并且我有一个 FileInfo[] 数组,其中包含未知数量的元素。

FileInfo[] files = new FileInfo[]

    new FileInfo(@"C:\a.jpg"),
    new FileInfo(@"C:\b.jpg"),
    new FileInfo(@"C:\c.jpg"),
    new FileInfo(@"C:\d.jpg"),
    new FileInfo(@"C:\e.jpg"),
    new FileInfo(@"C:\f.jpg"),
    new FileInfo(@"C:\g.jpg"),
    new FileInfo(@"C:\h.jpg"),
    new FileInfo(@"C:\i.jpg"),
; // Using 9 elements for this example

并且我需要生成这些文件的每个可能的重新排序组合的列表,而不是重复文件。

所以,我的一些结果会是这样的(示例不是代码格式):

a, b, c, d, e, f, g, h, i
a, b, c, d, e, f, g, i, h // i & h switched
a, b, c, d, e, f, h, g, i // last 3 elements switched

a, a, b, b, c, c, d, d, e // THIS IS NOT ACCEPTED, because elements are duplicated

以此类推,直到我想出所有可能的组合

所以结果总数应该是数组中元素数的阶乘。在这个例子中,有 9 个元素,所以应该有 9*8*7*6*5*4*3*2*1=362,880 种可能的组合。

我已经搞砸了这几天了,我就是想不通。任何帮助表示赞赏,尤其是代码示例!

谢谢!

【问题讨论】:

您想生成一个列表还是测试一个元素是否在该列表中? 您能否澄清一下,您为什么需要它?说真的,可能有比生成 350000 个 FileInfo 数组更好的解决方案。 @gaearon 呵呵呵呵,一定很真实 我确信有更好的方法。这只是我的游戏计划。在我提交了当前的帮助请求后,我决定提交大图。可以在这里找到:***.com/questions/3825387/… 【参考方案1】:

使用 Linq 轻松实现:

IEnumerable<FileInfo[]> permutations =
    from a in files
    from b in files.Except(new[]  a )
    from c in files.Except(new[]  a, b )
    from d in files.Except(new[]  a, b, c )
    from e in files.Except(new[]  a, b, c, d )
    from f in files.Except(new[]  a, b, c, d, e )
    from g in files.Except(new[]  a, b, c, d, e, f )
    from h in files.Except(new[]  a, b, c, d, e, f, g )
    from i in files.Except(new[]  a, b, c, d, e, f, g, h )
    select new[]  a, b, c, d, e, f, g, h, i ;

编辑:

这是一个通用解决方案,适用于任意数量的项目:

static class ExtensionMethods

    public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<T> source, int count)
    
        IEnumerable<IEnumerable<T>> result = new[]  Enumerable.Empty<T>() ; 
        for (int i = 0; i < count; i++)
        
            result =  
                from seq in result 
                from item in source.Except(seq)
                select seq.Concat(new[]  item ); 
         
        return result;
    

如下使用:

IEnumerable<IEnumerable<FileInfo>> permutations = files.GetPermutations(9);

(这个解决方案的灵感来自Eric Lippert's article about cartesian products。)


编辑 2:

这是一个使用Aggregate的变体:

static class ExtensionMethods

    public static IEnumerable<IEnumerable<T>> GetPermutations2<T>(this IEnumerable<T> source, int count)
    
        IEnumerable<IEnumerable<T>> seed = new[]  Enumerable.Empty<T>() ; 
        return Enumerable.Repeat(source, count)
            .Aggregate(
                seed,
                (accumulator, sequence) =>
                    from acc in accumulator
                    from item in sequence.Except(acc)
                    select acc.Concat(new[]  item ));
    

【讨论】:

但是如果项目数不知道怎么办? 是的,我知道...我正在努力,但可能会有点困难;) 哇!感谢您的快速回复! Linq 看起来很有前途,我需要阅读它。但与此同时,由于我不太了解 Linq 的语法,我不确定您提供的是有效代码还是伪代码,并且它在我的系统上出错了。你能提供一个可行的例子吗?谢谢! @Jason,请参阅通用解决方案的更新答案。你遇到了什么错误 ?您需要使用 .NET 3.5 或更高版本,否则将无法正常工作 @Jason,这不是伪代码,我在 LINQPad 中运行它,它工作正常。它返回预期的排列数,第一个似乎是正确的(我当然没有检查所有 362880 排列...)【参考方案2】:

有多种算法可用于执行此操作。下面的页面列出了 3 个不同的:

Counting And Listing All Permutations

【讨论】:

【参考方案3】:

你真的想要集合的所有排列。

http://www.codeproject.com/KB/recipes/Combinatorics.aspx http://blogs.msdn.com/b/updownroundnround/archive/2009/12/14/generating-all-permutations-c-enumerator-implementation.aspx

编辑:这是您正在谈论的内容的一个示例:http://www.codeproject.com/KB/recipes/Premutations.aspx

【讨论】:

以上是关于需要递归地生成文件数组的每个唯一组合的主要内容,如果未能解决你的问题,请参考以下文章

sh 计算目录中的文件数(递归)

Linux最大打开文件数

S3 中每个目录的最大文件数

Linux 下应用程序最大打开文件数的理解和修改

Linux 下应用程序最大打开文件数的理解和修改

如何计算每个目录中的文件数?