我有一个2D点类,如下所示:

class Point
{
     public int id_;
     public float x_, y_;

     public Point(int i, float x, float y)
     {
         id_ = i;
         x_ = x;
         y_ = y;
     }

     public float Distance(Point otherPoint)
     {
         return (float)Math.Sqrt(Math.Pow(x_ - otherPoint.x_, 2) + Math.Pow(y_ - otherPoint.y_, 2));
     }
}


在我的主要代码中,我列出了这些要点。给我提出了新的观点。如果要满足最低阈值标准,我想在列表中找到距新点最短的点。

我最初通过将minValue(初始化为1e6)和minID遍历列表以查找最小值来直接编写它。在遍历之外,我检查了该最小值是否小于阈值。那行得通。

但是我想看看是否有更好/更干净的方法来实现它,最终我得到了:

var list = new List<Point>();
list.Add(new Point(0, 10.0f, 1.0f));
list.Add(new Point(1, 1.0f, 0.0f));
list.Add(new Point(2, 0.0f, 0.0f));

var p = new Point(3, 0.6f, 0.0f);
var subList = list.Select((item, index) => new { item, index })
                   .Where(x => (x.item.distance(p) <= 1.0))
                   .Select(x => x.item).ToList();

Point minPoint = subList[Enumerable.Range(0, subList.Count).Aggregate((a, b) => (subList[a].Distance(p) < subList[b].Distance(p) ? a : b))];
Console.WriteLine(minPoint.id_);


有一个更好的方法吗?

最佳答案

我将对问题的两种解决方案有一些想法,这是原始类,去除了不必要的下划线。通常id是唯一的,所以是只读的,我从@CommuSoft的答案中借用了Distance方法,因为他对这种方法是正确的:

class Point
{
    public readonly int id;
    public float x;
    public float y;
    public Point(int id, float x, float y)
    {
        this.id = id;
        this.x = x;
        this.y = y;
    }
    public float Distance(Point p)
    {
        float dx = this.x - p.x;
        float dy = this.y - p.y;
        return (float)Math.Sqrt(dx * dx + dy * dy);
    }
}


共享的部分:

        List<Point> list = new List<Point>();
        list.Add(new Point(0, 10.0f, 1.0f));
        list.Add(new Point(1, 1.0f, 0.0f));
        list.Add(new Point(2, 0.0f, 0.0f));

        Point p = new Point(3, 0.6f, 0.0f);


下一个解决方案IpavluVersionA1在使用内存/分配方面最高效,并且在计算方面也很高效:

        //VersionA1, efficient memory and cpu usage
        Point closest_to_p = null;
        float shortest_d = float.MaxValue;

        //list.ForEach because it is iterating list through for cycle, most effective
        list.ForEach(point =>
        {
            //Distance is computed only ONCE per Point!
            var d = point.Distance(p);
            if (d > 1.0f) return;
            if (closest_to_p == null || shortest_d > d)
            {
                closest_to_p = point;
                shortest_d = d;
            }
        });
        //closest_to_p is cloases point in range with distance 1.0
        //or below or is null, then does not exist


下一个是IpavluVersionA2,最佳性能是:

        //VersionA2, most efficient memory and cpu usage
        Point closest_to_p = null;
        float shortest_d = float.MaxValue;
        int max = list.Count;

        for (int i = 0; i < max; ++i)
        {
            var point = list[i];
            var d = point.Distance(p);
            if (d > 1.0f) continue;
            if (closest_to_p == null || shortest_d > d)
            {
                closest_to_p = point;
                shortest_d = d;
            }
        }
        //closest_to_p is closest point in range with distance 1.0
        //or below or is null, then does not exist


另一个解决方案,使用LINQ方法的IpavluVersionB,必须创建新的结构对象,以保持Point和Distance,但它们很可能是在堆栈上创建的。仅一次计算距离,然后重用价值!

        //versionB
        var null_point =  new KeyValuePair<Point,float>(null, float.PositiveInfinity);
        var rslt_point =
        list
        .Select(xp =>
        {
            var d = xp.Distance(p);
            return d <= 1.0f ? new KeyValuePair<Point, float>(xp, d) : null_point;
        })
        .Aggregate(null_point, (a, b) =>
        {
            if (a.Key == null) return b;
            if (b.Key == null) return a;
            return a.Value > b.Value ? b : a;
        }, x => x.Key);


rslt_point为null或最接近p的点的实例。

基准:


必须以发布模式构建,
必须在Visual Studio外部运行而无需调试,
测试在两种情况下运行了5次,
方案X:3个项目,一千万次,所有方法,时间以毫秒为单位,
方案Y:3百万个项目,1次,所有方法,时间以毫秒为单位,


the code is here

Bechmark结果:


B-列表中的迭代次数,
I-清单中的项目数,
所有数字(以毫秒为单位),
CommuSoft是CommuSoft的解决方案,
Ivan Stoev建议使用匿名类型的解决方案,其行为与使用struct的VersionA2类似,
显然,IpavluVersionA2是最佳性能选择。


B [10000000] I [3]:CommuSoft:3521 IpavluA1:371 IpavluA2:195 IpavluB:1587

B [10000000] I [3]:CommuSoft:3466 IpavluA1:371 IpavluA2:194 IpavluB:1583

B [10000000] I [3]:CommuSoft:3463 IpavluA1:370 IpavluA2:194 IpavluB:1583

B [10000000] I [3]:CommuSoft:3465 IpavluA1:370 IpavluA2:194 IpavluB:1582

B [10000000] I [3]:CommuSoft:3471 IpavluA1:372 IpavluA2:196 IpavluB:1583

B 1 I [3000000]:CommuSoft:919 IpavluA1:21 IpavluA2:17 IpavluB:75

B 1 I [3000000]:CommuSoft:947 IpavluA1:21 IpavluA2:17 IpavluB:75

B 1 I [3000000]:CommuSoft:962 IpavluA1:21 IpavluA2:17 IpavluB:75

B 1 I [3000000]:CommuSoft:969 IpavluA1:21 IpavluA2:17 IpavluB:75

B 1 I [3000000]:CommuSoft:961 IpavluA1:21 IpavluA2:17 IpavluB:75

10-06 12:48