更高效的Haversine功能

时间:2017-01-12 20:02:45

标签: c# math optimization trigonometry

在我的previous question中,我希望根据功能结果加快列表选择。现在,我的瓶颈就是功能本身。

这是一个基本的Haversine功能,使用以下代码:

private static double Haversine(double lat1, double lat2, double lon1, double lon2)
{            
    const double r = 6371e3; // meters
    var dlat = (lat2 - lat1)/2;
    var dlon = (lon2 - lon1)/2;

    var q = Math.Pow(Math.Sin(dlat), 2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Pow(Math.Sin(dlon), 2);
    var c = 2 * Math.Atan2(Math.Sqrt(q), Math.Sqrt(1 - q));

    var d = r * c;
    return d / 1000;
}

那么......为什么它需要这么快?问题是我称之为很多。想想北方的16,500,000次。

显然,那是很多。在我的用例中,我传递它必须从中获取位置数据的对象,然后将纬度和经度转换为弧度,这会进一步增加时间(仅约15%)。我不知道我能做些什么,但我确实知道通过将它纯粹以弧度(如上所述)传递它需要~4.5秒 - 这超过了我实现中处理时间的75%。为q和c分配值的行似乎占用了最多的时间。

因为它被称为很多,所以我希望能够更快地使它一点。我对多线程解决方案持开放态度(目前我正在开发一个解决方案),但考虑到我之前的问题中的用例(上面链接),实现起来可能有点困难。

2 个答案:

答案 0 :(得分:1)

对我来说这更准确

public static class Haversine {
  public static double calculate(double lat1, double lon1, double lat2, double lon2) {
    var R = 6372.8; // In kilometers
    var dLat = toRadians(lat2 - lat1);
    var dLon = toRadians(lon2 - lon1);
    lat1 = toRadians(lat1);
    lat2 = toRadians(lat2);

    var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Sin(dLon / 2) * Math.Sin(dLon / 2) * Math.Cos(lat1) * Math.Cos(lat2);
    var c = 2 * Math.Asin(Math.Sqrt(a));
    return R * 2 * Math.Asin(Math.Sqrt(a));
  }

  public static double toRadians(double angle) {
    return Math.PI * angle / 180.0;
  }
}

void Main() {
  Console.WriteLine(String.Format("The distance between coordinates {0},{1} and {2},{3} is: {4}", 36.12, -86.67, 33.94, -118.40, Haversine.calculate(36.12, -86.67, 33.94, -118.40)));
}

// Returns: The distance between coordinates 36.12,-86.67 and 33.94,-118.4 is: 2887.25995060711

答案 1 :(得分:0)

这是最优化的,因为我可以得到答案(据我所知,这是最优化的答案可能可能获得而不对公式本身进行一些向导级优化):

private static double Haversine(double lat1, double lat2, double lon1, double lon2)
{
    const double r = 6371; // meters

    var sdlat = Math.Sin((lat2 - lat1) / 2);
    var sdlon = Math.Sin((lon2 - lon1) / 2);
    var q = sdlat * sdlat + Math.Cos(lat1) * Math.Cos(lat2) * sdlon * sdlon;
    var d = 2 * r * Math.Asin(Math.Sqrt(q));

    return d;
}

在我的机器上,这个公式在运行1650万次时几乎完全运行3秒,而上述版本只运行5次。

但是,我认为最大的优化可能是在实际调用此方法的系统中。每500个纬度 - 经度对中有33,000次?这是一个可能 dire 需要优化的系统。对于初学者,您可以先计算对的线性距离平方,然后只计算低于某个阈值的处理对。或者您可以维护一个查找表,以避免多次计算同一对。或者,根据33,000号码的来源,您可以优先排序,这样您就不需要那么多地调用方法