优化距离计算功能

时间:2009-03-05 15:17:07

标签: c# geolocation

在我的代码中,我必须在一对lat / long值之间进行大量的距离计算。

代码如下所示:

double result = Math.Acos(Math.Sin(lat2rad) * Math.Sin(lat1rad) 
+ Math.Cos(lat2rad) * Math.Cos(lat1rad) * Math.Cos(lon2rad - lon1rad));

(lat2rad,例如纬度转换为弧度)。

我已将此功能确定为我的应用程序的性能瓶颈。有没有办法改善这个?

(因为坐标不同,我不能使用查找表)。我还查看了this question,其中建议使用类似网格的查找方案,这可能是一种可能性。

感谢您的时间! ; - )

12 个答案:

答案 0 :(得分:5)

如果您的目标是排名(比较)距离,那么近似值(sincos表格查找)可能会大幅减少您所需的计算量(实施快速拒绝。)

如果近似距离(待排名或比较)之间的差异低于某个阈值,您的目标是仅进行实际的三角计算。

E.g。使用具有1000个样本的查找表(即每sin采样cos2*pi/1000),查找不确定性最多为0.006284。使用uncertainty calculation作为ACos的参数,累积不确定度,也就是阈值不确定度,最多为0.018731。

因此,如果使用Math.Sin(lat2rad) * Math.Sin(lat1rad) + Math.Cos(lat2rad) * Math.Cos(lat1rad) * Math.Cos(lon2rad - lon1rad)sin两个坐标集对(距离)的查找表评估cos,则会产生一定的排名(一个距离看起来比另一个更大)近似),并且差值的模数大于上面的阈值,则近似值是有效的。否则继续进行实际的三角计算。

答案 1 :(得分:4)

CORDIC算法是否适合您(关于速度/准确度)?

答案 2 :(得分:3)

使用来自@Brann的灵感我认为你可以稍微减少一点计算(警告它很长一段时间以来我做了任何这个并且需要进行验证)。某些预先计算的值的查找可能是最快的

你有:

1:ACOS(SIN A SIN B + COS A COS B COS(A-B))

但是2:COS(A-B)= SIN A SIN B + COS A COS B

改写为3:SIN A SIN B = COS(A-B) - COS A COS B

在1中替换SIN A SIN B:

4:ACOS(COS(A-B) - COS A COS B + COS A COS B COS(A-B))

您预先计算X = COS(A-B)和Y = COS A COS B并将值放入4

给予:

ACOS(X - Y + XY)

4次触发计算而不是6次!

答案 3 :(得分:2)

更改存储长/拉的方式:

struct LongLat
{
  float
    long,
    lat,
    x,y,z;
}

创建长/纬度时,还要计算(x,y,z)3D点,该点表示以原点为中心的单位球面上的等效位置。

现在,要确定B点是否比C点更接近A点,请执行以下操作:

// is B nearer to A than C?
bool IsNearer (LongLat A, LongLat B, LongLat C)
{
  return (A.x * B.x + A.y * B.y + A.z * B.z) < (A.x * C.x + A.y * C.y + A.z * C.z);
}

并获得两点之间的距离:

float Distance (LongLat A, LongLat B)
{
  // radius is the size of sphere your mapping long/lats onto
  return radius * acos (A.x * B.x + A.y * B.y + A.z * B.z);
}

您可以删除“半径”字词,有效地规范距离。

答案 4 :(得分:1)

切换到sin / cos / acos的查找表。会更快,有很多c / c ++定点库也包括那些。

以下是Memoization上其他人的代码。如果使用的实际值更加聚集,则可能会有效。

以下是关于Fixed Point的问题。

答案 5 :(得分:1)

什么是瓶颈?是正弦/余弦函数调用还是反正弦调用?

如果您的正弦/余弦调用很慢,您可以使用以下定理来阻止这么多调用:

1 = sin(x)^2 + cos(x)^2
cos(x) = sqrt(1 - sin(x)^2)

但我喜欢映射的想法,所以你不必重新计算你已经计算过的值。虽然地图可以很快变得非常大,但要小心。

答案 6 :(得分:0)

您需要多少精确值?

如果你稍微调整一下你的值,那么你可以存储所有查找的结果并检查它们是否在每次计算之前被使用过?

答案 7 :(得分:0)

好吧,因为lat和long保证在一定范围内,你可以尝试使用某种形式的查找表来进行Math。*方法调用。比如,Dictionary<double,double>

答案 8 :(得分:0)

我认为你可能想重新审视一下你是如何发现这个功能成为瓶颈的。 (IE您是否对应用程序进行了分析?)

对我而言,这个等式似乎非常轻,不应该造成任何麻烦。 当然,我不知道你的申请,你说你做了很多这些计算。

然而,这是需要考虑的事情。

答案 9 :(得分:0)

正如其他人所指出的,你确定这是你的瓶颈吗?

我已经对我正在构建的类似应用程序进行了一些性能测试,其中我称之为使用标准trig返回两点之间距离的简单方法。对它的20,000次调用将它推到了剖析输出的顶部,但是我无法让它更快......这只是剪切#的调用。

在这种情况下,我需要减少对它的#次调用......并不是说这是瓶颈。

答案 10 :(得分:0)

我使用不同的算法来计算2个lati / longi位置之间的距离,它可能比你的轻,因为它只进行1个Cos呼叫和1个Sqrt呼叫。

public static double GetDistanceBetweenTwoPos(double lat1, double long1, double lat2, double long2)
{
  double distance = 0;
  double x = 0;
  double y = 0;

  x = 69.1 * (lat1 - lat2);
  y = 69.1 * (long1 - long2) * System.Math.Cos(lat2 / 57.3);

  //calculation base : Miles
  distance = System.Math.Sqrt(x * x + y * y);

  //Distance calculated in Kilometres
  return distance * 1.609;
}

答案 11 :(得分:0)

某人已经提到过备忘录,这有点类似。如果你将同一点与许多其他点进行比较,那么最好预先计算该等式的部分。

而不是

double result = Math.Acos(Math.Sin(lat2rad) * Math.Sin(lat1rad) 
+ Math.Cos(lat2rad) * Math.Cos(lat1rad) * Math.Cos(lon2rad - lon1rad));

有:

double result = Math.Acos(lat2rad.sin * lat1rad.sin 
+ lat2rad.cos * lat1rad.cos * (lon2rad.cos * lon1rad.cos + lon1rad.sin * lon2rad.sin));

并且我认为这与其他人发布的公式相同,因为当您展开括号时,等式的一部分将消失:)