修剪笛卡尔积的LINQ实现

时间:2011-08-04 20:18:50

标签: linq aggregate combinations cartesian-product multiset

我希望有人能帮助我,至少对我来说,这是一个非常棘手的算法。

问题

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

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

Cartesian产品负责这一点,因此第1阶段已经完成.....现在,这里出现了我无法弄清楚的扭曲 - 至少我无法弄清楚LINQ的做法(我仍然是LINQ菜鸟。

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

<1,2> 1,2,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&lt; - 所有其他四个2和一个3(任何顺序)的结果都被抑制
  • 2,2,2,3,3&lt; - 所有其他三个2和两个3的结果被抑制等等。
  • 2,2,3,3,3
  • 2,3,3,3,3
  • 3,3,3,3,3

对于任何有数学头脑的人 - 我希望你能提供帮助。我实际上已经有了第2部分的工作解决方案,但这是一个完全的黑客攻击并且计算量很大,我正在寻找指导,以便为修剪问题找到更优雅,更有效的LINQ解决方案。

感谢阅读。

PIP

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

更新 - 解决方案

抱歉没有尽早发布此信息......请参阅below

3 个答案:

答案 0 :(得分:3)

您应该实现自己的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),排序会太慢。

答案 1 :(得分:1)

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;
        }
    }

答案 2 :(得分:1)

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

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;
}