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

.net 生成IEnumerable(of T)元素的所有唯一组合,.net,vb.net,algorithm,combinatorics,.net,Vb.net,Algorithm,Combinatorics,这个问题实际上与相同,只是我在寻找一个VB.NET(.NET 4)解决方案。我已经旋转了足够长的时间,试图想出一个通用的解决方案来解决这个“电源设置”问题 鉴于: Dim choices As IEnumerable(Of String) = {"Coffee", "Tea", "Milk", "Cookies"} Dim choiceSets = choices.CombineAll() 我正在寻找choiceSets成为IEnumerable(Of IEnumerable(Of T)),这

这个问题实际上与相同,只是我在寻找一个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)
中的项数计算的前一项

无论如何,这不是家庭作业;虽然感觉确实很像


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

一个简单的递归解决方案(大量列表创建开销):

静态列表GetChoiceSet(IEnumerable选项)
{
if(choices==null | |!choices.Any())
返回null;
其他的
{
var first=选择。取(1);
var inner=GetChoiceSets(choices.Skip(1));
if(内部==null)
返回新列表{first,新列表{};
其他的
返回inner.Select(lst=>first.Union(lst)).Union(inner.ToList();
}
}
使用链接SO算法的稍微不那么简单的迭代解决方案:

    static List<List<string>> GetChoiceSets2(List<string> choices)
    {
        int capacity = (int)Math.Pow(2, choices.Count());
        int bit = 1;
        List<List<string>> choiceSets = new List<List<string>>(capacity);
        for (int i = 0; i < capacity; i++)
            choiceSets.Add(new List<String>());

        for (int i = 0; i < choices.Count(); i++)
        {
            for (int n = 0; n < capacity; n++)
            {
                if ((n & bit) == bit)
                    choiceSets[n].Add(choices[i]);
            }
            bit *= 2;
        }

        return choiceSets;
    }
静态列表GetChoiceSets2(列表选项)
{
int capacity=(int)Math.Pow(2,choices.Count());
int位=1;
列表选项集=新列表(容量);
对于(int i=0;i

这些可能都可以改进,但取决于使用的集合的大小,其中一个应该足够有效。

我没有在VB.NET中编程,这只是键入的。所以可能会有严重的错误。但这种方法应该奏效

static List<List<string>> GetChoiceSets(List<string> choices)
{
    int capacity = (int) Math.Pow(2, choices.Count()) - 1;
    int bit = 1;
    List<List<string>> choiceSets = new List<List<string>>(capacity);
    for (int i = 0; i < capacity; i++)
        choiceSets.Add(new List<String>());

    n = 0;
    for (int size = 1; size <= choices.Count(); size++)
    {
        List<int> indexes = new List<int>(size);
        for (int i = 0; i < size; i++)
            indexes.Add(i);

        // We break out after exhausting all sets of this size.
        for (;;) {
            // Insert solution.
            for (int i = 0; i < size; i++)
                choiceSets[n].Add(choices[indexes[i]]);
            n++;

            // Figure out the first place we can advance indexes.
            int j = 1;
            for (; j <= size; j++) {
                if (indexes[size - j] < choices.Count() - j) {
                    break;
                }
            }
            threshold = choices.Count() - j
            // Did we finish?
            if (threshold < 0)
                break;

            // We will increment the index at threshold, and make following ones
            // increment from there.
            indexes[threshold]++;
            for (int i = 1; i + threshold < choices.Count(); i++)
                indexes[threshold + i] = indexes[threshold] + i;
        }
    }

    return choiceSets;
}
静态列表GetChoiceSets(列表选项)
{
int capacity=(int)Math.Pow(2,choices.Count())-1;
int位=1;
列表选项集=新列表(容量);
对于(int i=0;i对于(int size=1;size这里有一个纯Linq解决方案,灵感来自Eric Lippert关于计算笛卡尔积的文章。我稍微修改了
CartesianProduct
方法,使其返回组合:

公共静态IEnumerable组合(此IEnumerable序列)
{
IEnumerable emptyProduct=new[]{Enumerable.Empty()};
返回序列.聚合(
空产品,
(累加器,顺序)=>
来自蓄能器中的accseq
//排除已拾取的项目
按顺序从项目开始。除(accseq)
//强制执行升序以避免不同顺序中的相同顺序
其中!accseq.Any()| | Comparer.Default.Compare(item,accseq.Last())>0
选择accseq.Concat(new[]{item})).ToArray();
}
基于此扩展方法,您可以生成如下所示的所需结果:

IEnumerable items=new[]{“咖啡”、“茶”、“牛奶”};
可数结果=
Enumerable.Range(1,items.Count())
.合计(
Enumerable.Empty(),
(acc,i)=>
acc.Concat(可枚举.重复(项,i).组合());
(它连接1、2…N项的所有组合)

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


编辑:这里有一个新版本的
Combinations
方法,它保持了原始顺序:

公共静态IEnumerable组合(此IEnumerable序列)
{
var indexedSequences=sequences.Select(seq=>seq.Select((item,idx)=>newindexeditem(item,idx));
IEnumerable emptyProduct=new[]{Enumerable.Empty()};
var指数结果=
索引序列。聚合(
空产品,
(累加器,顺序)=>
来自蓄能器中的accseq
//排除已拾取的项目
按顺序从项目开始。除(accseq)
//强制执行索引的升序,以避免在不同的顺序中出现相同的序列
其中!accseq.Any()| | item.Index>accseq.Last().Index
选择accseq.Concat(new[]{item})).ToArray();
返回indexedResult.Select(seq=>seq.Select(i=>i.Item));
}
类索引
{
公共索引项(T项,整数索引)
{
此项=项;
这个。索引=索引;
}
公共T项{get;私有集;}
公共int索引{get;set;}
}
可能比以前的版本效率更低,但它完成了任务…

IEnumerable seed=new[]{Enumerable.Empty()};
IEnumerable<IEnumerable<string>> seed = new[] { Enumerable.Empty<string>() };

choices.Aggregate(
    seed,
    (acc, item) =>
        acc.SelectMany(a => new[] { a, a.Concat(new[] {item}) }))
选择。合计( 种子, (附件,项目)=> acc.SelectMany(a=>new[]{a,a.Concat(new[]{item}}))

choices.Aggregate(
种子,
(附件,项目)=>
来自acc的一名员工
从新[]{Enumerable.Empty(),新[]{item}中的c
选择a.Concat(c))

如果对其他人有用,我已将发布到VB.NET的原始C#扩展名Thomas Levesque转换为:

    <System.Runtime.CompilerServices.Extension()> _
    Public Function Combinations(Of T)(ByVal sequences As IEnumerable(Of IEnumerable(Of T))) As IEnumerable(Of IEnumerable(Of T))
        Dim seed As IEnumerable(Of IEnumerable(Of T)) = {  Enumerable.Empty(Of T) }
        Dim r = sequences.Aggregate(seed, 
             Function(ByVal accumulator, ByVal sequence) _
               From accseq In accumulator    _
               From item In sequence.Except(accseq) _
               Where (Not accseq.Any()) OrElse Comparer(Of T).Default.Compare(item, accseq.Last()) > 0  _
               Select accseq.Concat(  {item}  ) ).ToArray()
        Return r
    End Function
我找到了另一个(在那里查找C#代码)

公共函数GetPowerSet(O
choices.Aggregate(
    seed,
    (acc, item) =>
        from a in acc
        from c in new[] { Enumerable.Empty<string>(), new[] { item } }
        select a.Concat(c))
    <System.Runtime.CompilerServices.Extension()> _
    Public Function Combinations(Of T)(ByVal sequences As IEnumerable(Of IEnumerable(Of T))) As IEnumerable(Of IEnumerable(Of T))
        Dim seed As IEnumerable(Of IEnumerable(Of T)) = {  Enumerable.Empty(Of T) }
        Dim r = sequences.Aggregate(seed, 
             Function(ByVal accumulator, ByVal sequence) _
               From accseq In accumulator    _
               From item In sequence.Except(accseq) _
               Where (Not accseq.Any()) OrElse Comparer(Of T).Default.Compare(item, accseq.Last()) > 0  _
               Select accseq.Concat(  {item}  ) ).ToArray()
        Return r
    End Function
    Dim allRolesArray  = {1,4,5,2,0}
    Dim comboCountValues = Enumerable.Range(0, allRolesArray.Count()+1)
    Dim allRoleCombos = comboCountValues.Aggregate(
        Enumerable.Empty(Of IEnumerable(Of Integer))(),
        Function (acc, i) acc.Concat(Enumerable.Repeat(allRolesArray, i).Combinations() ) ).ToList
    Public Function GetPowerSet(Of T)(items As IEnumerable(Of T)) As IEnumerable(Of IEnumerable(Of T))

         Dim result = From m In Enumerable.Range(0, 1 << items.Count)
                 Select
                    From i In Enumerable.Range(0, items.Count)
                    Where (m And (1 << i)) <> 0
                        Select items(i)
         Return result

End Function