生成 IEnumerable(Of T) 元素的所有唯一组合

2024-01-21

这个问题实际上与这个帖子 https://stackoverflow.com/questions/1471558/all-possible-combinations-of-elements,只是我正在寻找 VB.NET (.NET 4) 解决方案。我已经花了很长时间试图想出一个通用的解决方案来解决这个“电源组”问题。

Given:

Dim choices As IEnumerable(Of String) = {"Coffee", "Tea", "Milk", "Cookies"}
Dim choiceSets = choices.CombineAll()

我在找choiceSets成为一个IEnumerable(Of IEnumerable(Of T))这样我就可以做类似的事情:

For each choiceSet in choiceSets
    Console.WriteLine(String.Join(", ", choiceSet))
Next

并得到如下结果:

Coffee
Tea
Milk
Cookies
Coffee, Tea
Coffee, Milk
Coffee, Cookies
Tea, Milk
Tea, Cookies
Milk, Cookies
Coffee, Tea, Milk
Coffee, Tea, Cookies
Coffee, Milk, Cookies
Tea, Milk, Cookies
Coffee, Tea, Milk, Cookies

正如你所看到的,这是每一个不重复从源头组合IEnumerable(Of T)(其中可能有 1 到多个项目 - 本示例只有 4),它根据源中项目的顺序进行操作IEnumerable(Of T),并且列表中的每个项目 >= 前一个项目(就内部项目数而言)IEnumerable(Of T).

无论如何,这不是家庭作业;而是家庭作业。虽然感觉确实如此。

编辑:更新了示例,因此结果看起来不像按字母顺序排序,以强调来源IEnumerable(Of T)使用现有的顺序并添加第四个选择以澄清每组内的排序要求。


这是一个纯粹的 Linq 解决方案,灵感来自 Eric Lippert博客文章 http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx关于计算笛卡尔积。我修改了CartesianProduct稍微修改一下方法,使其返回组合:

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<IEnumerable<T>> sequences)
{
    IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
    return sequences.Aggregate(
        emptyProduct,
        (accumulator, sequence) => 
        from accseq in accumulator 
        // Exclude items that were already picked
        from item in sequence.Except(accseq)
        // Enforce ascending order to avoid same sequence in different order
        where !accseq.Any() || Comparer<T>.Default.Compare(item, accseq.Last()) > 0
        select accseq.Concat(new[] {item})).ToArray();
}

基于此扩展方法,您可以产生所需的结果,如下所示:

IEnumerable<string> items = new[] {"Coffee", "Tea", "Milk"};
IEnumerable<IEnumerable<string>> result =
    Enumerable.Range(1, items.Count())
        .Aggregate(
            Enumerable.Empty<IEnumerable<string>>(),
            (acc, i) =>
                acc.Concat(Enumerable.Repeat(items, i).Combinations()));

(它连接 1、2...N 个项目的所有组合)

请注意,这可能不是一个非常有效的解决方案,但我认为这是 Linq 的一个有趣的用法......


编辑:这是一个新版本Combinations保持原始顺序的方法:

public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<IEnumerable<T>> sequences)
{
    var indexedSequences = sequences.Select(seq => seq.Select((item, idx) => new IndexedItem<T>(item, idx)));
    IEnumerable<IEnumerable<IndexedItem<T>>> emptyProduct = new[] { Enumerable.Empty<IndexedItem<T>>() };
    var indexedResult =
        indexedSequences.Aggregate(
            emptyProduct,
            (accumulator, sequence) => 
            from accseq in accumulator 
            // Exclude items that were already picked
            from item in sequence.Except(accseq)
            // Enforce ascending order of indexes to avoid same sequence in different order
            where !accseq.Any() || item.Index > accseq.Last().Index
            select accseq.Concat(new[] {item})).ToArray();
    return indexedResult.Select(seq => seq.Select(i => i.Item));
}

class IndexedItem<T>
{
    public IndexedItem(T item, int index)
    {
        this.Item = item;
        this.Index = index;
    }

    public T Item { get; private set; }
    public int Index { get; set; }
}

可能比以前的版本效率更低,但它完成了工作......

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

生成 IEnumerable(Of T) 元素的所有唯一组合 的相关文章

随机推荐