获取List <list <int>&gt;的所有组合(在C#</list <int>中也有部分结果)

时间:2011-04-27 13:55:06

标签: c# algorithm combinations

我需要一种有效的算法来获取整数列表列表的所有可用组合。我也需要部分结果。

一个例子:

{1, 2, 3}
{4, 5}
{6, 7, 8}

我需要得到:

1
2
3
1/4
1/5
2/4
2/5
3/4
3/5
1/4/6
1/4/7
1/4/8
1/5/6
1/5/7
1/5/8
2/4/6
2/4/7
2/4/8
2/5/6
2/5/7
2/5/8
3/4/6
3/4/7
3/4/8
3/5/6
3/5/7
3/5/8

如果可能的话,我以最快的方式需要它。我已经有了算法,但我想知道是否有更好的选择。

谢谢。

编辑: 这是我目前的代码。对不起意大利语评论。

// Istanzia una lista per contenere le liste di valori
List<List<int>> allLists = new List<List<int>>();

... CODE TO FILL THE LISTS ...

// Esegue un ciclo fino al numero di liste recuperate
for (int listIndex = 0; listIndex < allLists.Count; listIndex++)
{
    // Istanzia una lista per contenere le liste di valori fino allo 
    // step corrente
    List<List<int>> stepLists = new List<List<int>>();

    // Esegue un ciclo sulle liste fino allo step corrente
    for (int stepListIndex = 0; stepListIndex <= listIndex; stepListIndex++)
    {
        // Aggiunge la lista a quelle dello step corrente
        stepLists.Add(allLists[stepListIndex]);
    }

    // Esegue il prodotto vettoriale delle liste specificate
    List<List<int>> crossLists = 
        Mathematics.CrossProduct(stepLists, new List<int>());

    // Carica l'elenco delle combinazioni
    CombinationsCollection allCombinations = 
        new CombinationsCollection(Kernel);
    allCombinations.Load();

    // Esegue un ciclo su ciascuna lista recuperata
    foreach (List<int> crossList in crossLists)
    {
    }
}

... OTHER CODE ...

public static List<List<int>> CrossProduct(
    List<List<int>> lists, 
    List<int> root)
{
    // Istanzia delle liste per contenere le combinazioni
    List<List<int>> results = new List<List<int>>();

    // Se ce n'è almeno una
    if (lists.Count > 0)
    {
        // Recupera la prima lista
        List<int> list = (List<int>)lists[0];

        // Se è rimasta solo una lista
        if (lists.Count == 1)
        {
            // Esegue un ciclo su tutti i valori
            foreach (int value in list)
            {
                // Aggiunge un risultato con radice e valore
                results.Add(new List<int>(root) { value });
            }
        }
        else
        {
            // Esegue un ciclo su ogni valore della lista
            foreach (int value in list)
            {
                // Aggiunge ai risultati la prosecuzione del prodotto 
                // vettoriale dalla lista successiva
                results.AddRange(CrossProduct(
                    lists.GetRange(1, lists.Count - 1), 
                    new List<int>(root) { value })
                );
            }
        }
    }

    return results;
}

3 个答案:

答案 0 :(得分:5)

您需要一种方法,可以返回列表中的笛卡尔积部分结果(如标题中所述)。以下是Eric Lippert的CartesianProduct方法扩展的方差 根据您的要求得到部分结果:

public static IEnumerable<IEnumerable<T>> CrossProduct<T>(
     this IEnumerable<IEnumerable<T>> sequences)
{
    IEnumerable<IEnumerable<T>> accumulator = new[] { Enumerable.Empty<T>() };
    var result = new List<IEnumerable<T>>();
    var firstSequence = true;
    foreach (var sequence in sequences)
    {
        var local = new List<IEnumerable<T>>();
        foreach (var accseq in accumulator)
        {
            if (!firstSequence)
                result.Add(accseq);

            foreach (var item in sequence)
                local.Add(accseq.Concat(new[] { item }));
        }
        firstSequence = false;
        accumulator = local;
    }

    return result.Concat(accumulator);
}

对于您提供的输入数据:

var items = new[] { 
    new[] { 1, 2, 3 }, 
    new[] { 4, 5 }, 
    new[] { 7, 8, 9 } 
};
var product = items.CrossProduct();

product变量将包含您想要的结果。

答案 1 :(得分:1)

我相信你要找的是你的序列集的 power set “前缀集”的每个元素的笛卡尔积。让我们分解一下:

首先,您有一组输入序列:

IEnumerable<IEnumerable<int>> inputSet = new[] {new[] {1,2,3}, new[] {4,5}, new[] {6,7,8}};

inputSet的“前缀集”是:

{{},{1,2,3},{{1,2,3},{4,5}},{{1,2,3},{4,5},{6,7 ,8}}}

一组序列的笛卡尔积得到每个序列中1项的所有组合。

我相信您正在寻找的是:(伪代码)

foreach (element in the above prefix set)
{
  Print(cartesian product of the sequences in this element);
}

以下是我用来生成笛卡尔积,电源组等的扩展方法:

public static class CombinatorialExtensionMethods {
    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) 
    { 
        IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() }; 
        return sequences.Aggregate( 
            emptyProduct, 
            (accumulator, sequence) =>  
            from accseq in accumulator  
            from item in sequence  
            select accseq.Concat(new[] {item}));                
    }

    public static IEnumerable<IEnumerable<T>> CartesianPower<T>(this IEnumerable<T> sequence, int power) 
    { 
        var sequences = Enumerable.Repeat<IEnumerable<T>>(sequence,power);
        return sequences.CartesianProduct<T>();
    }

    public static IEnumerable<IEnumerable<T>> Permute<T>(this IEnumerable<T> seq, int k)
    { 
        var sequences = Enumerable.Repeat<IEnumerable<T>>(seq,k);
        IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() }; 
        return sequences.Aggregate( 
            emptyProduct, 
            (accumulator, sequence) =>  
            from accseq in accumulator  
            from item in sequence
            where !accseq.Contains(item)
            select accseq.Concat(new[] {item}));
    }

    public static IEnumerable<IEnumerable<int>> Choose(this IEnumerable<int> seq, int k)
    { 
        var sequences = Enumerable.Repeat<IEnumerable<int>>(seq,k);
        IEnumerable<IEnumerable<int>> emptyProduct = new[] { Enumerable.Empty<int>() }; 
        return sequences.Aggregate( 
            emptyProduct, 
            (accumulator, sequence) =>  
            from accseq in accumulator  
            from item in sequence
            where accseq.All(accitem => accitem.CompareTo(item) < 0)
            select accseq.Concat(new[] {item}));
    }

    public static IEnumerable<IEnumerable<T>> Choose<T>(this IEnumerable<T> seq, int k)
    { 
        IEnumerable<int> idxSequence = Enumerable.Range(0, seq.Count());
        IEnumerable<IEnumerable<int>> idxChoose = idxSequence.Choose(k);
        IEnumerable<IEnumerable<T>> result = Enumerable.Empty<IEnumerable<T>>(); 
        foreach (IEnumerable<int> permutation in idxChoose)
        {
            IEnumerable<T> item = Enumerable.Empty<T>();
            foreach (int index in permutation)
            {
                item = item.Concat(new[] { seq.ElementAt(index) });
            }
            result = result.Concat(new[] { item });
        }
        return result;
    }

    public static IEnumerable<IEnumerable<T>> PowerSet<T>(this IEnumerable<T> seq)
    {
        IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };
        for (int i=1; i<=seq.Count(); i++)
        {
            result = result.Concat(seq.Choose<T>(i));
        }

        return result;
    }
}

使用这些,您的示例的代码将是:

IEnumerable<IEnumerable<int>> sequences = new[] {new[] {1,2,3}, new[] {4,5}, new[] {6,7,8}};
IEnumerable<IEnumerable<IEnumerable<int>>> prefixSet = new[] {new[] { Enumerable.Empty<int>() }};

for (int i=0; i<sequences.Count(); i++)
{
    IEnumerable<IEnumerable<int>> prefixSequence = Enumerable.Empty<IEnumerable<int>>();
    for (int j=0; j<=i; j++)
    {
        prefixSequence = prefixSequence.Concat(new[] { sequences.ElementAt(j) });
    }
    prefixSet = prefixSet.Concat(new[] { prefixSequence });
}

foreach (IEnumerable<IEnumerable<int>> item in prefixSet)
{
    Console.WriteLine(item.CartesianProduct<int>());
}

答案 2 :(得分:0)

这将产生所需的输出。虽然不确定与原始编码相比的性能:

private List<List<int>> test(List<List<int>> lists)
{
    List<List<int>> ret = new List<List<int>>();
    ret.AddRange(from first in lists[0] select new List<int>(new int[] { first }));

    List<List<int>> inner = new List<List<int>>();
    inner.AddRange(from first in lists[0] select new List<int>(new int[] { first }));

    for (int i = 1; i < lists.Count;i++ )
    {
        List<int> l = lists[i];

        var newElements =
          from first in inner
          from second in l
          select new List<int>(first.Concat<int>(new int[] { second }));

        ret.AddRange(newElements);
        inner = newElements.ToList();
    }

    return ret;
}

如果使用

调用
        List<List<int>> rval = test(
            new List<List<int>>(
                new List<int>[] { 
                    new List<int>(new int[] { 1, 2, 3 }),
                    new List<int>(new int[] { 4, 5 }),
                    new List<int>(new int[] { 6, 7, 8 })
            }));

生成的rval包含所需方式的元素。

可能有一些Linq的东西可以重构为更简单或更高效的东西,我对这些东西没有坚实的基础(这就是我在发布它之前测试它的原因; - )