来自给定集合的所有可能组合,但不重复集合的内部元素

时间:2013-12-14 18:00:45

标签: c# linq collections combinatorics

我有一些固定的整数集,每个集合中的distict值增加,例如:

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

是否有任何算法或更好的C#代码,从给定集合生成所有可能的组合,但不重复其内部整数,即

[{1, 2, 4}, {3, 6}] < all integers are unique

[{1, 2, 4}, {3, 5, 7}] < all integers are unique

[{1, 3}, {2, 4}, {5, 6}] <– all integers are unique.

等等。

====================================

这可能是输入数据的组织:

        var set1 = new HashSet<int>() { 1, 2, 4 };
        var set2 = new HashSet<int>() { 1, 3 };
        var set3 = new HashSet<int>() { 2, 4 };
        var set4 = new HashSet<int>() { 2, 7 };
        var set5 = new HashSet<int>() { 3, 6 };
        var set6 = new HashSet<int>() { 5, 6 };
        var set7 = new HashSet<int>() { 3, 5, 7 };

        var inputList = new List<HashSet<int>>();
        inputList.Add(set1);
        inputList.Add(set2);
        inputList.Add(set3);
        inputList.Add(set4);
        inputList.Add(set5);
        inputList.Add(set6);
        inputList.Add(set7);

我需要从inputList获取所有可能列表(即组合)的列表(或集合),每个内部列表(组合)中都有唯一的整数。


这个问题被标记为重复,并且这个问题在这里已经有了答案: “生成所有可能的组合(8个答案)”。但是,在我看来,它本质上是一个不同的问题:

  • 输入数据不是两个长度相同的数组,而是每个集合中具有不同元素数量的集合列表;

  • 相同的元素可能存在于不同的集合中。

1 个答案:

答案 0 :(得分:0)

我个人认为这是一个两部分问题:

编辑/更正

因此,深入研究问题的目的是找到SETS的组合而不是这些组中的个别数字(正如我上面最初指出的那样)。考虑到这一点,我会做以下事情:

  • 创建一个方形的二维布尔数组。每个布尔值表示该索引处的集合是否与另一个索引处的集合冲突。首先填写此内容。
  • 使用标准组合词(与上述相同),但结合对布尔列表的检查以丢弃无效结果。

我相信这应该有效。我(可能)稍后在这里创建一个例子!

守则

重要提示:这在任何意义上都不是很好或优化,但它确实有效,它确实说明了解决这个问题的方法。一个简单的优化是将布尔数组传递到生成组合的递归步骤,并在生成冲突时立即停止递归。这将节省大量的时间和计算能力。也就是说,我会将其作为练习留给读者。

只需将以下内容粘贴到新的控制台应用程序中即可。祝你好运!

    static void Main(string[] args)
    {
        // create/read sets here
        var integers = new List<int[]>(){new []{1,2,4}, new []{1,3}, new []{2, 4}, new []{2, 7}, new []{3, 6}, new []{5, 6}, new []{2, 5, 7}};

        // allocate/populate booleans - loop may be able to be refined
        var conflicts = new bool[integers.Count, integers.Count];
        for (var idx = 0; idx < integers.Count; idx++)
            for (var cmpIdx = 0; cmpIdx < integers.Count; cmpIdx++)
                conflicts[idx, cmpIdx] = integers[idx].Any(x => integers[cmpIdx].Contains(x));

        // find combinations (index combinations)
        var combinations = GetCombinations(integers.Count);

        // remove invalid entries
        for (var idx = 0; idx < combinations.Count; idx++)
            if (HasConflict(combinations[idx], conflicts))
                combinations.RemoveAt(idx--);

        // print out the final result
        foreach (var combination in combinations) PrintCombination(combination, integers);

        // pause
        Console.ReadKey();
    }


    // get all combinatins
    static List<Combination> GetCombinations(int TotalLists)
    {
        var result = new List<Combination>();

        for (var combinationCount = 1; combinationCount <= TotalLists; combinationCount++)
            result.AddRange(GetCombinations(TotalLists, combinationCount));

        return result;
    }

    static List<Combination> GetCombinations(int TotalLists, int combinationCount)
    {
        return GetCombinations(TotalLists, combinationCount, 0, new List<int>());
    }

    // recursive combinatorics - loads of alternatives including linq cartesian coordinates
    static List<Combination> GetCombinations(int TotalLists, int combinationCount, int minimumStart, List<int> currentList)
    {
        // stops endless recursion - forms final result
        var result = new List<Combination>();

        if (combinationCount <= 0)
        {
            if ((currentList ?? new List<int>()).Count > 0)
            {
                result.Add(new Combination() { sets = currentList });
                return result;
            }

            return null;
        }

        for (var idx = minimumStart; idx <= TotalLists - combinationCount; idx++)
        {
            var nextList = new List<int>();
            nextList.AddRange(currentList);
            nextList.Add(idx);

            var combinations = GetCombinations(TotalLists, combinationCount - 1, idx + 1, nextList);
            if (combinations != null) result.AddRange(combinations);
        }

        return result;
    }

    // print the combination
    static void PrintCombination(Combination value, List<int[]> sets)
    {
        StringBuilder serializedSets = new StringBuilder();

        foreach (var idx in value.sets)
        {
            if (serializedSets.Length > 0) serializedSets.Append(", ");
            else serializedSets.Append("{");

            serializedSets.Append("{");
            for (var setIdx = 0; setIdx < sets[idx].Length; setIdx++)
            {
                if (setIdx > 0) serializedSets.Append(", ");
                serializedSets.Append(sets[idx][setIdx].ToString());
            }
            serializedSets.Append("}");
        }

        serializedSets.Append("}");
        Console.WriteLine(serializedSets.ToString());
    }

    static bool HasConflict(Combination value, bool[,] conflicts)
    {
        for (var idx = 0; idx < value.sets.Count; idx++)
            for (var cmpIdx = idx + 1; cmpIdx < value.sets.Count; cmpIdx++)
                if (conflicts[value.sets[idx], value.sets[cmpIdx]]) return true;

        return false;
    }

    // internal class to manage combinations
    class Combination { public List<int> sets; }