为 linq groupby 编写自定义比较器

2024-01-08

同样,这个示例是我的实际问题的一个非常简化的版本,涉及 linq 分组的自定义比较器。我做错了什么?

下面的代码产生下面的结果 (1.2, 0), (4.1, 0), (4.1, 0), (1.1, 0),

然而我期待以下结果,因为 1.1 和 1.2 之间的距离

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<Point> points = new List<Point> { 
            new Point(1.1, 0.0)
            , new Point(4.1, 0.0) 
            , new Point(1.2, 0.0)
            , new Point(4.1, 0.0)
        };

        foreach (var group in points.GroupBy(p => p, new PointComparer()))
        {
            foreach (var num in group)
                Console.Write(num.ToString() + ", ");

            Console.WriteLine();
        }

        Console.ReadLine();
    }
}

class PointComparer : IEqualityComparer<Point>
{
    public bool Equals(Point a, Point b)
    {
        return Math.Abs(a.X - b.X) < 1.0;
    }

    public int GetHashCode(Point point)
    {
        return point.X.GetHashCode()
            ^ point.Y.GetHashCode();
    }
}

class Point
{
    public double X;
    public double Y;

    public Point(double p1, double p2)
    {
        X = p1;
        Y = p2;
    }

    public override string ToString()
    {
        return "(" + X + ", " + Y + ")";
    }
}

使用相等比较器的分组算法(我认为所有 LINQ 方法)总是首先比较哈希码,然后只执行Equals如果两个哈希码相等。您可以看到,如果在相等比较器中添加跟踪语句:

class PointComparer : IEqualityComparer<Point>
{
    public bool Equals(Point a, Point b)
    {
        Console.WriteLine("Equals: point {0} - point {1}", a, b);
        return Math.Abs(a.X - b.X) < 1.0;
    }

    public int GetHashCode(Point point)
    {
        Console.WriteLine("HashCode: {0}", point);
        return point.X.GetHashCode()
            ^ point.Y.GetHashCode();
    }
}

结果是:

HashCode: (1.1, 0)
HashCode: (4.1, 0)
HashCode: (1.2, 0)
HashCode: (4.1, 0)
Equals: point (4.1, 0) - point (4.1, 0)
(1.1, 0), 
(4.1, 0), (4.1, 0), 
(1.2, 0), 

仅针对哈希码相等的两个点Equals被处决。

现在你可以通过总是返回来欺骗比较0作为哈希码。如果这样做,输出将是:

HashCode: (1.1, 0)
HashCode: (4.1, 0)
Equals: point (1.1, 0) - point (4.1, 0)
HashCode: (1.2, 0)
Equals: point (4.1, 0) - point (1.2, 0)
Equals: point (1.1, 0) - point (1.2, 0)
HashCode: (4.1, 0)
Equals: point (4.1, 0) - point (4.1, 0)
(1.1, 0), (1.2, 0), 
(4.1, 0), (4.1, 0), 

现在对于每一对Equals被执行了,你就得到了你的分组。

But...

什么是“平等”?如果再加一点(2.1, 0.0),您想要一组中的哪些点?使用符号对于模糊等式,我们有 -

1.1 ≈ 1.2
1.2 ≈ 2.1

but

1.1 !≈ 2.1

这意味着1.1 and 2.1永远不会在一个群体中(他们的Equals永远不会过去)这取决于点的顺序无论1.1 or 2.1被分组为1.2.

所以你现在正处于滑坡状态。按邻近度对点进行聚类绝非易事。你正在进入的领域聚类分析 https://en.wikipedia.org/wiki/Cluster_analysis.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为 linq groupby 编写自定义比较器 的相关文章

随机推荐