【问题标题】:LINQ implementation of Cartesian Product with pruning带有修剪的笛卡尔积的 LINQ 实现
【发布时间】:2011-10-20 08:28:25
【问题描述】:

我希望有人能够帮助我解决至少对我来说相当棘手的算法。

问题

我有一个需要合并的列表 (1 <= size <= 2) 列表(1 <= size <= 5,但在运行时之前大小未知)。这是我正在查看的示例:-

ListOfLists = { {1}, {2,3}, {2,3}, {4}, {2,3} }

所以,我需要做两个阶段:-

(1)。我需要以这样一种方式组合内部列表,即任何组合都只有每个列表中的一个项目,也就是说,这里结果集中的可能组合是:-

  • 1,2,2,4,2
  • 1,2,2,4,3
  • 1,2,3,4,2
  • 1,2,3,4,3
  • 1,3,2,4,2
  • 1,3,2,4,3
  • 1,3,3,4,2
  • 1,3,3,4,3

笛卡尔积会处理这个问题,所以第 1 阶段已经完成......现在,我想不通的转折出现了——至少我想不出 LINQ 的方式来做到这一点(我仍然是 LINQ 菜鸟)。

(2)。我现在需要从这个笛卡尔积中过滤掉任何重复的结果。在这种情况下,重复构成结果集中的任何行,每个不同列表元素的数量与另一行相同,即,

1,2,2,4,3 与 1,3,2,4,2 “相同”

因为第一个列表中的每个不同项目在两个列表中出现的次数相同(1 在每个列表中出现一次,2 在每个列表中出现两次,......

因此,最终结果集应如下所示...

  • 1,2,2,4,2
  • 1,2,2,4,3
  • --
  • 1,2,3,4,3
  • --
  • --
  • --
  • 1,3,3,4,3

另一个例子是最坏的情况(从组合的角度来看),其中 ListOfLists 是 {{2,3}, {2,3}, {2,3}, {2,3}, {2 ,3}},即包含最大大小的内部列表的列表 - 在这种情况下,笛卡尔积结果集中显然会有 32 个结果,但我试图得到的修剪结果集只是: -

  • 2,2,2,2,2
  • 2,2,2,2,3
  • 2,2,2,3,3
  • 2,2,3,3,3
  • 2,3,3,3,3
  • 3,3,3,3,3

致所有有数学头脑的人 - 我希望你能提供帮助。实际上,我已经为第 2 部分找到了一个可行的解决方案,但它完全是 hack 并且计算量很大,我正在寻找指导以找到更优雅、更有效的 LINQ 解决方案来解决修剪问题。

感谢阅读。

目前使用的一些资源(用于获取笛卡尔积)

更新 - 解决方案

很抱歉没有早点发布...见below

【问题讨论】:

    标签: linq aggregate combinations cartesian-product multiset


    【解决方案1】:

    您应该实现自己的IEqualityComparer<IEnumerable<int>>,然后在Distinct() 中使用它。

    IEqualityComparer 中哈希码的选择取决于您的实际数据,但如果您的实际数据与示例中的数据相似,我认为这样的内容就足够了:

    class UnorderedQeuenceComparer : IEqualityComparer<IEnumerable<int>>
    {
        public bool Equals(IEnumerable<int> x, IEnumerable<int> y)
        {
            return x.OrderBy(i => i).SequenceEqual(y.OrderBy(i => i));
        }
    
        public int GetHashCode(IEnumerable<int> obj)
        {
            return obj.Sum(i => i * i);
        }
    }
    

    重要的是GetHashCode()应该是O(N),排序会太慢。

    【讨论】:

    • 什么意思?我的代码没有产生正确的结果?如果您的意思是它不适用于 Equals() 中的 OrderBy()s,那么是的,这就是它们存在的原因。
    • 它会产生正确的结果,没关系。我实际上并没有仔细阅读代码,我只是阅读了您的评论说“排序太慢”,所以我以为您故意将其删除。我的错,这里有点晚了。顺便说一句,我现在正在考虑另一种解决方案,而不是对数组进行排序,但我想这不是 LINQ :)
    • 感谢大家的回复 - 就像软件开发的方式一样,我今天无法重新开始编写自己的代码,因为我一直在修复另一项工作的错误具有更高的业务优先级,因此我将使用您在周末概述的解决方案。
    • @Adrian,有时是“如何在 LINQ 中做 X?”的最佳答案。是“不要使用 LINQ。”。
    【解决方案2】:
    void Main()
    {
        var query =     from a in new int[] { 1 }
                        from b in new int[] { 2, 3 }
                        from c in new int[] { 2, 3 }
                        from d in new int[] { 4 }                   
                        from e in new int[] { 2, 3 }
                        select new int[] { a, b, c, d, e }; 
        query.Distinct(new ArrayComparer());
            //.Dump();
    }
     public class ArrayComparer : IEqualityComparer<int[]>
        {
            public bool Equals(int[] x, int[] y)
            {            
                if (x == null || y == null)
                    return false;
    
                return x.OrderBy(i => i).SequenceEqual<int>(y.OrderBy(i => i));
    
            }
    
            public int GetHashCode(int[] obj)
            {
                if ( obj == null || obj.Length == 0)
                    return 0;
                var hashcode = obj[0];
                for (int i = 1; i < obj.Length; i++)
                {
                    hashcode ^= obj[i];
                }
                return hashcode;
            }
        }
    

    【讨论】:

    • 我只是在挖掘一个类似的相等比较器。它非常相似,但我忍不住指出,实际比较可以通过 Linq 的 SequenceEqual() 而不是 for 循环来完成。
    • 我不认为 XOR 是这里哈希码的好选择。当然,你永远无法避免碰撞,但它们不应该像1,12,2 那样简单。这两个序列不应该有相同的哈希码,因为这样的模式很容易出现在输入中。
    • 感谢大家的回复 - 就像软件开发的方式一样,我今天无法重新开始编写自己的代码,因为我一直在修复另一项工作的错误具有更高的业务优先级,因此我将使用您在周末概述的解决方案,并使用 svick 建议的更强大的哈希码。
    • 已实施、测试、完善!
    • 再次感谢伙计们...只需几分钟就可以将先前解决的笛卡尔积解决方案和这个修剪解决方案放在一起...尤其是在 LinqPad 中进行操作(对于所有正在寻找的外行要了解有关 LINQ 的更多信息,您绝对应该在 linqpad.net 上查看这个很棒的(免费)工具)
    【解决方案3】:

    整个组合多重集,然后修剪结果集以删除重复问题的最终解决方案最终作为静态方法在帮助类中结束。它需要 svick 非常感谢的答案,并将 IEqualityComparer 依赖项注入到我在 Eric Lipperts 的博客 here 找到的现有 CartesianProduct 答案中(我建议阅读他的帖子,因为它解释了他思想中的迭代以及为什么 linq 实现是最好的) .

    static IEnumerable<IEnumerable<T>> CartesianProduct<T>(IEnumerable<IEnumerable<T>> sequences,
                                                           IEqualityComparer<IEnumerable<T>> sequenceComparer)
    {
        IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
        var resultsSet = sequences.Aggregate(emptyProduct, (accumulator, sequence) => from accseq in accumulator
                                                                                      from item in sequence
                                                                                      select accseq.Concat(new[] { item }));
    
        if (sequenceComparer != null)
            return resultsSet.Distinct(sequenceComparer);
        else
            return resultsSet;
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-30
      • 1970-01-01
      • 2021-05-06
      • 1970-01-01
      • 2021-09-05
      • 2013-05-26
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      相关资源
      最近更新 更多