使用LINQ获取不同的数字组合

时间:2014-05-28 16:15:30

标签: c# linq combinations

以下代码根据1,2,3 = 3,2,1 = 2,3,1的逻辑返回所有不同的组合,因此它只返回该组数字的1个实例。

但是,我想更改该逻辑,以便返回所有数字集的所有实例。

我需要对下面的LINQ查询做什么" GetPowerSet"为了实现这一目标?

    public void GetPowersets()
    {
        List<int> ints = new List<int>()
        {
            1,2,2,3,3
        };

        var results = GetPowerSet(ints);


        List<String> combinations = new List<String>();
        foreach (var result in results)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var intValue in result.OrderBy(x => x))
            {
                sb.Append(intValue + ",");
            }
            combinations.Add(sb.ToString());
        }

        string c1 = string.Join("|", combinations.ToArray()).Replace(",|", "|");
        //c1 = "|1|2|1,2|2|1,2|2,2|1,2,2|3|1,3|2,3|1,2,3|2,3|1,2,3|2,2,3|1,2,2,3|3|1,3|2,3|1,2,3|2,3|1,2,3|2,2,3|1,2,2,3|3,3|1,3,3|2,3,3|1,2,3,3|2,3,3|1,2,3,3|2,2,3,3|1,2,2,3,3,"

    }

    public IEnumerable<IEnumerable<int>> GetPowerSet(List<int> list)
    {
        return from m in Enumerable.Range(0, 1 << list.Count)
                     select
                         from i in Enumerable.Range(0, list.Count)
                         where (m & (1 << i)) != 0
                         select list[i];
    }

这是我想要达到的最终结果:(没有重复的组合行:duplicate = 3,2,1和3,2,1是相同的东西。但是1,2,3和3,2, 1不是一回事,两者都应该在最终结果中)

1
2
3
1,2
1,3
2,1
2,3
2,2
3,1
3,2
3,3
1,2,3
1,2,2
1,3,2
1,3,3
2,1,3
2,1,2
2,3,1
2,3,2
2,3,3
2,2,1
2,2,3
3,1,2
3,1,3
3,2,1
3,2,2
3,2,3
3,3,1
3,3,2
1,2,3,2
1,2,3,3
1,2,2,3
1,3,2,2
1,3,2,3
1,3,3,2
2,1,3,2
2,1,3,3
2,1,2,3
2,3,1,2
2,3,1,3
2,3,2,1
2,3,2,3
2,3,3,1
2,3,3,2
2,2,1,3
2,2,3,1
2,2,3,3
3,1,2,2
3,1,2,3
3,1,3,2
3,2,1,2
3,2,1,3
3,2,2,1
3,2,2,3
3,2,3,1
3,2,3,2
3,3,1,2
3,3,2,1
3,3,2,2
1,2,3,2,3
1,2,3,3,2
1,2,2,3,3
1,3,2,2,3
1,3,2,3,2
1,3,3,2,2
2,1,3,2,3
2,1,3,3,2
2,1,2,3,3
2,3,1,2,3
2,3,1,3,2
2,3,2,1,3
2,3,2,3,1
2,3,3,1,2
2,3,3,2,1
2,2,1,3,3
2,2,3,1,3
2,2,3,3,1
3,1,2,2,3
3,1,2,3,2
3,1,3,2,2
3,2,1,2,3
3,2,1,3,2
3,2,2,1,3
3,2,2,3,1
3,2,3,1,2
3,2,3,2,1
3,3,1,2,2
3,3,2,1,2
3,3,2,2,1

&#34; foreach&#34;这样做的方式,往往导致内存异常&#34;一旦数字集太大(我预计LINQ不应该有这个问题)在下面。这是我想要的,返回我想要的结果集。但它很慢并且存在性能问题。我也欢迎提出如何改善它的建议。

public List<List<int>> GetAllCombinationsOfAllSizes(List<int> ints)
{
    List<List<int>> returnResult = new List<List<int>>();

    var distinctInts = ints.Distinct().ToList();
    for (int j = 0; j < distinctInts.Count(); j++)
    {
        var number = distinctInts[j];

        var newList = new List<int>();
        newList.Add(number);
        returnResult.Add(newList);

        var listMinusOneObject = ints.Select(x => x).ToList();
        listMinusOneObject.Remove(listMinusOneObject.Where(x => x == number).First());

        if (listMinusOneObject.Count() > 0)
        {
            _GetAllCombinationsOfAllSizes(listMinusOneObject, newList, ref returnResult);
        }
    }

    return returnResult;
}
public void _GetAllCombinationsOfAllSizes(List<int> ints, List<int> growingList, ref List<List<int>> returnResult)
{
    var distinctInts = ints.Distinct().ToList();
    for (int j = 0; j < distinctInts.Count(); j++)
    {
        var number = distinctInts[j];

        var newList = growingList.ToList();
        newList.Add(number);
        returnResult.Add(newList);

        var listMinusOneObject = ints.Select(x => x).ToList();
        listMinusOneObject.Remove(listMinusOneObject.Where(x => x == number).First());

        if (listMinusOneObject.Count() > 0)
        {
            _GetAllCombinationsOfAllSizes(listMinusOneObject, newList, ref returnResult);
        }
    }

}

我正在寻找的答案是:如何实现我想要的结果集,但是使用LINQ和C#来实现它,其方式比当前的#fore;&#34;更快更有效。我发布的方式?

3 个答案:

答案 0 :(得分:2)

NEW UPDATE (删除旧代码,性能优于OP代码,产生输出)

static IEnumerable<int[]> EnumeratePermutations2(int[] ints)
{
    Dictionary<int, int> intCounts = ints.GroupBy(n => n)
                                         .ToDictionary(g => g.Key, g => g.Count());
    int[] distincts = intCounts.Keys.ToArray();
    foreach (int[] permutation in EnumeratePermutations2(new int[0], intCounts, distincts))
        yield return permutation;
}

static IEnumerable<int[]> EnumeratePermutations2(int[] prefix, Dictionary<int, int> intCounts, int[] distincts)
{
    foreach (int n in distincts)
    {
        int[] newPrefix = new int[prefix.Length + 1];
        Array.Copy(prefix, newPrefix, prefix.Length);
        newPrefix[prefix.Length] = n;
        yield return newPrefix;
        intCounts[n]--;
        int[] newDistincts = intCounts[n] > 0
                             ? distincts
                             : distincts.Where(x => x != n).ToArray();
        foreach (int[] permutation in EnumeratePermutations2(newPrefix, intCounts, newDistincts))
            yield return permutation;
        intCounts[n]++;
    }
}

答案 1 :(得分:0)

基于这个问题。

您的解决方案会返回所有结果集,包括重复项。您的解决方案不排除重复。您的示例输出确实排除了一些重复项,但列出了89组数据。

应该只有64个重复项,因为我理解组合的工作方式是2 ^ List.Count()= 2 ^ 6 = 64种组合

修订答案

我相信这很接近你想要的。我创建了一个简短的解决方案,但我认为它可以重新考虑并加快速度。以下链接有一些我认为应该使用的好的Set Classes:http://www.codeproject.com/Articles/23391/Set-Collections-for-C

我将使用的另一件事是 TPL库,它可以让您加快处理速度。链接:http://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

我的结果集产生了190套。使用以下代码,运行大约需要1.5分钟。

主程序

void Main()
{
    var setList = new List<int>() {1,1,2,3,3,3};
    var setSize = setList.Count();

    var basePowerSet = PowerSet.Generate(setList);
    var results = PowerSet.PS;


    // Results generated in 1 Minute 23 seconds with no errors.

    var sortedSets = new SortedSet<string>();

    foreach( var item in results)
    {
        sortedSets.Add(item.ToString2());
    }

    foreach( var item in sortedSets)
    {
        Console.WriteLine(item);
    }   


}

PowerSet库

public static class PowerSet
{
    // List with no Exact Duplicates but with Permutations
    public static List<List<int>> PS =  new List<List<int>>();


    // This Method Generates the power set with No Exact Duplicates
    // and stores the values into the Property PS.
    public static List<List<int>> Generate(List<int> setList)
    {
        // Generate Base Data to use for final results
        var setSize = setList.Count();
        var basePowerSet = from m in Enumerable.Range(0, 1 << setSize)
                select
                    from i in Enumerable.Range(0, setSize)
                    where (m & (1 << i)) != 0 
                    select setList[i];

        // Temporary Result Set with Duplicates
        var results = new List<List<int>>();

        // Step thru each set and generate list of Permutations for each
        // Power Set generated above.
        foreach( var item in basePowerSet )
        {
            var size = item.Count();
            var positions = from m in Enumerable.Range(0, size)
                select m;

            var lItem = item.ToList();  

            // If the set has 2 or more elements in the set then generate Permutations
            switch(size)
            {
                case 0:
                case 1:
                    break;
                default:

                    // Permutations generated from Linq Extension defined
                    // in Method Permute()
                    var posList = positions.Permute().ToList();

                    // remove first item which is a duplicate.
                    posList.RemoveAt(0);

                    // Generate new Lists based on all possiable
                    // combinations of the data in the set.
                    var x = new List<List<int>>();
                    foreach(var p in posList)
                    {   
                        var y = new List<int>();
                        foreach(var v in p)
                        {
                            //v.Dump("xxxx");
                            y.Add(lItem[v]);
                        }

                        x.Add(y);

                        // Add New Permutation but
                        // Do not add a duplicate set.
                        AddNonDuplicate(x);
                    }                                       
                    break;
            }

            // Add to Temp Results;
            results.Add(item.ToList());

            // Remove Duplicates
            AddNonDuplicate(results);
        }

        return results;
    }


    // Custom Method used to compare values in a set to the
    // Final Result Set named PS.
    public static void AddNonDuplicate(List<List<int>> list )
    {
        //list.Dump();
        if(list.Count() == 0)
            return;

        foreach(var item in list)
        {
            bool found = false;
            var mySize = PS.Count();
            if(mySize <= 0)
                PS.Add(item);
            else
                foreach(var psItem in PS)
                {
                    if( item.ToString2() == psItem.ToString2() )
                        found = true;
                }

            if(!found)
                PS.Add(item);
        }
    }

}

扩展程序库

// My Extension Methods
public static class MyExt
{

    public static IEnumerable<IEnumerable<T>> Permute<T>(this IEnumerable<T> list)
    {
        if (list.Count() == 1)
            return new List<IEnumerable<T>> { list };
        return list
            .Select((a, i1) => 
                        Permute(list.Where((b, i2) => i2 != i1))
                    .Select(b => (new List<T> { a }).Union(b)))
            .SelectMany(c => c);
    }


    public static string ToString2<T>(this List<T> list)
    {
        StringBuilder results = new StringBuilder("{ ");
        var size = list.Count();
        var pos = 1;

        foreach( var i in list )
        {
            results.Append(i.ToString());
            if(pos++!=size)
                results.Append(", ");
        }

        results.Append(" }");               
        return results.ToString().Trim(',');
    }
}

<强>结果

{  }
{ 1 }
{ 1, 1 }
{ 1, 1, 2 }
{ 1, 1, 2, 3 }
{ 1, 1, 2, 3, 3 }
{ 1, 1, 2, 3, 3, 3 }
{ 1, 1, 3 }
{ 1, 1, 3, 2 }
{ 1, 1, 3, 2, 3 }
{ 1, 1, 3, 2, 3, 3 }
{ 1, 1, 3, 3 }
{ 1, 1, 3, 3, 2 }
{ 1, 1, 3, 3, 2, 3 }
{ 1, 1, 3, 3, 3 }
{ 1, 1, 3, 3, 3, 2 }
{ 1, 2 }
{ 1, 2, 1 }
{ 1, 2, 1, 3 }
{ 1, 2, 1, 3, 3 }
{ 1, 2, 1, 3, 3, 3 }
{ 1, 2, 3 }
{ 1, 2, 3, 1 }
{ 1, 2, 3, 1, 3 }
{ 1, 2, 3, 1, 3, 3 }
{ 1, 2, 3, 3 }
{ 1, 2, 3, 3, 1 }
{ 1, 2, 3, 3, 1, 3 }
{ 1, 2, 3, 3, 3 }
{ 1, 2, 3, 3, 3, 1 }
{ 1, 3 }
{ 1, 3, 1 }
{ 1, 3, 1, 2 }
{ 1, 3, 1, 2, 3 }
{ 1, 3, 1, 2, 3, 3 }
{ 1, 3, 1, 3 }
{ 1, 3, 1, 3, 2 }
{ 1, 3, 1, 3, 2, 3 }
{ 1, 3, 1, 3, 3 }
{ 1, 3, 1, 3, 3, 2 }
{ 1, 3, 2 }
{ 1, 3, 2, 1 }
{ 1, 3, 2, 1, 3 }
{ 1, 3, 2, 1, 3, 3 }
{ 1, 3, 2, 3 }
{ 1, 3, 2, 3, 1 }
{ 1, 3, 2, 3, 1, 3 }
{ 1, 3, 2, 3, 3 }
{ 1, 3, 2, 3, 3, 1 }
{ 1, 3, 3 }
{ 1, 3, 3, 1 }
{ 1, 3, 3, 1, 2 }
{ 1, 3, 3, 1, 2, 3 }
{ 1, 3, 3, 1, 3 }
{ 1, 3, 3, 1, 3, 2 }
{ 1, 3, 3, 2 }
{ 1, 3, 3, 2, 1 }
{ 1, 3, 3, 2, 1, 3 }
{ 1, 3, 3, 2, 3 }
{ 1, 3, 3, 2, 3, 1 }
{ 1, 3, 3, 3 }
{ 1, 3, 3, 3, 1 }
{ 1, 3, 3, 3, 1, 2 }
{ 1, 3, 3, 3, 2 }
{ 1, 3, 3, 3, 2, 1 }
{ 2 }
{ 2, 1 }
{ 2, 1, 1 }
{ 2, 1, 1, 3 }
{ 2, 1, 1, 3, 3 }
{ 2, 1, 1, 3, 3, 3 }
{ 2, 1, 3 }
{ 2, 1, 3, 1 }
{ 2, 1, 3, 1, 3 }
{ 2, 1, 3, 1, 3, 3 }
{ 2, 1, 3, 3 }
{ 2, 1, 3, 3, 1 }
{ 2, 1, 3, 3, 1, 3 }
{ 2, 1, 3, 3, 3 }
{ 2, 1, 3, 3, 3, 1 }
{ 2, 3 }
{ 2, 3, 1 }
{ 2, 3, 1, 1 }
{ 2, 3, 1, 1, 3 }
{ 2, 3, 1, 1, 3, 3 }
{ 2, 3, 1, 3 }
{ 2, 3, 1, 3, 1 }
{ 2, 3, 1, 3, 1, 3 }
{ 2, 3, 1, 3, 3 }
{ 2, 3, 1, 3, 3, 1 }
{ 2, 3, 3 }
{ 2, 3, 3, 1 }
{ 2, 3, 3, 1, 1 }
{ 2, 3, 3, 1, 1, 3 }
{ 2, 3, 3, 1, 3 }
{ 2, 3, 3, 1, 3, 1 }
{ 2, 3, 3, 3 }
{ 2, 3, 3, 3, 1 }
{ 2, 3, 3, 3, 1, 1 }
{ 3 }
{ 3, 1 }
{ 3, 1, 1 }
{ 3, 1, 1, 2 }
{ 3, 1, 1, 2, 3 }
{ 3, 1, 1, 2, 3, 3 }
{ 3, 1, 1, 3 }
{ 3, 1, 1, 3, 2 }
{ 3, 1, 1, 3, 2, 3 }
{ 3, 1, 1, 3, 3 }
{ 3, 1, 1, 3, 3, 2 }
{ 3, 1, 2 }
{ 3, 1, 2, 1 }
{ 3, 1, 2, 1, 3 }
{ 3, 1, 2, 1, 3, 3 }
{ 3, 1, 2, 3 }
{ 3, 1, 2, 3, 1 }
{ 3, 1, 2, 3, 1, 3 }
{ 3, 1, 2, 3, 3 }
{ 3, 1, 2, 3, 3, 1 }
{ 3, 1, 3 }
{ 3, 1, 3, 1 }
{ 3, 1, 3, 1, 2 }
{ 3, 1, 3, 1, 2, 3 }
{ 3, 1, 3, 1, 3 }
{ 3, 1, 3, 1, 3, 2 }
{ 3, 1, 3, 2 }
{ 3, 1, 3, 2, 1 }
{ 3, 1, 3, 2, 1, 3 }
{ 3, 1, 3, 2, 3 }
{ 3, 1, 3, 2, 3, 1 }
{ 3, 1, 3, 3 }
{ 3, 1, 3, 3, 1 }
{ 3, 1, 3, 3, 1, 2 }
{ 3, 1, 3, 3, 2 }
{ 3, 1, 3, 3, 2, 1 }
{ 3, 2 }
{ 3, 2, 1 }
{ 3, 2, 1, 1 }
{ 3, 2, 1, 1, 3 }
{ 3, 2, 1, 1, 3, 3 }
{ 3, 2, 1, 3 }
{ 3, 2, 1, 3, 1 }
{ 3, 2, 1, 3, 1, 3 }
{ 3, 2, 1, 3, 3 }
{ 3, 2, 1, 3, 3, 1 }
{ 3, 2, 3 }
{ 3, 2, 3, 1 }
{ 3, 2, 3, 1, 1 }
{ 3, 2, 3, 1, 1, 3 }
{ 3, 2, 3, 1, 3 }
{ 3, 2, 3, 1, 3, 1 }
{ 3, 2, 3, 3 }
{ 3, 2, 3, 3, 1 }
{ 3, 2, 3, 3, 1, 1 }
{ 3, 3 }
{ 3, 3, 1 }
{ 3, 3, 1, 1 }
{ 3, 3, 1, 1, 2 }
{ 3, 3, 1, 1, 2, 3 }
{ 3, 3, 1, 1, 3 }
{ 3, 3, 1, 1, 3, 2 }
{ 3, 3, 1, 2 }
{ 3, 3, 1, 2, 1 }
{ 3, 3, 1, 2, 1, 3 }
{ 3, 3, 1, 2, 3 }
{ 3, 3, 1, 2, 3, 1 }
{ 3, 3, 1, 3 }
{ 3, 3, 1, 3, 1 }
{ 3, 3, 1, 3, 1, 2 }
{ 3, 3, 1, 3, 2 }
{ 3, 3, 1, 3, 2, 1 }
{ 3, 3, 2 }
{ 3, 3, 2, 1 }
{ 3, 3, 2, 1, 1 }
{ 3, 3, 2, 1, 1, 3 }
{ 3, 3, 2, 1, 3 }
{ 3, 3, 2, 1, 3, 1 }
{ 3, 3, 2, 3 }
{ 3, 3, 2, 3, 1 }
{ 3, 3, 2, 3, 1, 1 }
{ 3, 3, 3 }
{ 3, 3, 3, 1 }
{ 3, 3, 3, 1, 1 }
{ 3, 3, 3, 1, 1, 2 }
{ 3, 3, 3, 1, 2 }
{ 3, 3, 3, 1, 2, 1 }
{ 3, 3, 3, 2 }
{ 3, 3, 3, 2, 1 }
{ 3, 3, 3, 2, 1, 1 }

答案 2 :(得分:0)

我没有触及您的GetPowerSet,而是创建了SetComparer来过滤重复次数。

public class SetComparer : IEqualityComparer<IEnumerable<int>>
{
    public bool Equals(IEnumerable<int> x, IEnumerable<int> y)
    {
        return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y));
    }

    public int GetHashCode(IEnumerable<int> set)
    {
        if (set == null) return 0;

        //if you only want one of these 1,2,3 vs 3,2,1
        //plug .OrderBy(x => x) before the Aggregate
        return set.Aggregate(19, (s,i) => s * 31 + i);
    }
}

只需将.Distinct(new SetComparer())链接到结果或GetPowerSet选择语句的末尾。