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

标签 .net vb.net algorithm combinatorics

这个问题实际上和this SO post是一样的,只有我在寻找 VB.NET (.NET 4) 解决方案。我已经花了足够长的时间尝试想出一个通用的解决方案来解决这个“功率组”问题。

给定:

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)

就其值(value)而言,这不是家庭作业;尽管它确实有这种感觉。

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

最佳答案

这是一个纯 Linq 解决方案,灵感来自 Eric Lippert 的 blog post关于计算笛卡尔积。我稍微修改了 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; }
}

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

关于.net - 生成 IEnumerable(Of T) 元素的所有唯一组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5980810/

相关文章:

c# - 在回发之间维护通用列表

c# - VB 2008 程序集中的扩展方法不能在 C# 项目中使用?

确认脚本中的 Javascript IIF

C# .NET 相当于 Java Swing Actions

c# - JWT最快算法

c# - VB.Net 中的赋值表达式

algorithm - 我怎样才能更好地将城市放置在程序生成的地形周围?

algorithm - 有什么算法可以找到双麻烦数吗?

python - 在python函数中修改一个int列表

.net - 如何正确组合多个 bool 后缀表达式?