我有一个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