2011-06-20 22 views
7

Pożyczyłem następującą metodę z dowolnego miejsca w Internecie (nie pamiętam gdzie). Ale robi to prosty proces, znajdując odległość między dwoma punktami GPS. Działa dobrze, z tym wyjątkiem, że może być trochę powolny, ponieważ używam go przez miliony punktów. Zastanawiam się, czy ktoś zna podejście, które byłoby obliczeniowo tańsze.Szybszy sposób obliczania odległości geograficznej między dwoma punktami

Dokładność musi być w obszarze ogólnym "poprawne", ale nie musi być w 100% dokładna.

private double distFrom(double lat1, double lng1, double lat2, double lng2) { 
    double earthRadius = 3958.75; 
    double dLat = Math.toRadians(lat2-lat1); 
    double dLng = Math.toRadians(lng2-lng1); 
    double a = Math.sin(dLat/2) * Math.sin(dLat/2) + 
      Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * 
      Math.sin(dLng/2) * Math.sin(dLng/2); 
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    return earthRadius * c; 
    } 
} 

P.s Rzeczywiście znalazłem wiele innych istotnych pytań, ale one tak naprawdę nie koncentrują się na moich obawach dotyczących prędkości.

+0

Do czego używasz odległości? Typowym zastosowaniem tego jest znalezienie wszystkich X w pobliżu Y. Jeśli to dotyczy ciebie, powinieneś rozważyć zastosowanie podejścia ograniczającego, które może ograniczyć twoje obliczenia z milionów do kilkudziesięciu. Na przykład znajdź mi najbliższy sklep spożywczy w promieniu 5 mil. Nie musisz obliczać odległości z mojego domu do sklepu w Kalifornii lub na Alasce. –

Odpowiedz

14

Jeśli nie przeszkadza ignorując lekką spłaszczenia Ziemi (a pisał kod Haversine właśnie to robi tak) rozważyć sprzed konwersji wszystkich swoich kulisty (długość/szerokość geograficzna) koordynuje w 3D jednostek uczestnictwa długość współrzędne kartezjańskie pierwsze, za:

http://en.wikipedia.org/wiki/Spherical_coordinate_system

wówczas kulisty odległość między współrzędnych kartezjańskich p1 i p2 jest prosta:

r * acos(p1 . p2) 

Ponieważ p1 i p2 będzie miał długość jednostkową Zmniejsza cztery mnożenia, dwa dodatki i jednym odwrotnym trygonometrycznych działania na parę.

Należy również zauważyć, że obliczanie produktów z kropek jest idealnym kandydatem do optymalizacji, np. przez GPU, rozszerzenia MMX, bibliotek wektorów itp

Ponadto, jeśli zamiarem jest celu par według odległości, ignorując potencjalnie bardziej odległe pary, można odroczyć drogiego r*acos() część równania sortując listę tylko na wartość iloczynu skalarnego gdyż dla wszystkich poprawnych danych wejściowych (tj zakresie [-1, 1]), jest gwarantowane, że:

acos(x) < acos(y) if x > y 

wtedy po prostu wziąć acos() z wartościami jesteś rzeczywiście zainteresowany

Odp.: po niedokładności związane z używaniem acos(), są one naprawdę istotne tylko w przypadku używania zmiennych o pojedynczej precyzji float. Użycie znacznika cyfr o 16 cyfrach powinno zapewnić odległości z dokładnością do jednego metra lub mniejszą.

+0

piękne rzeczy – Steve

1

To algorytm haversine, zapewni ci przyzwoity poziom dokładności.

Jeśli to naprawdę "miliony" punktów, być może zaimplementuj pamięć podręczną obliczeń, które wykonałeś ... jeśli natkniesz się na parę współrzędnych, które są wystarczająco blisko pary, której odległości " ve już wyliczył, a następnie użyć wartości buforowanej?

Lub spróbuj wykonać buforowanie niektórych pośrednich kroków, np. konwersje stopni do radianów.

+0

Tak, powiedziałbym, że buforowanie może pomóc, teraz jest dość leniwe podejście. Nie sądzę, by zbiór danych był tak duży! – Steve

1

Jeśli poświęcisz dokładność, możesz wprowadzić pewne ulepszenia. O ile pamiętam, sin(x) jest approximately równa x dla małych x. Wygląda również na to, że kilka razy obliczasz te same rzeczy, na przykład: Math.sin(dLat/2) (która może być w przybliżeniu równa dLat/2, jak podano powyżej).

Jednak jeśli wykonujesz miliony takich operacji, to gdzie indziej.

  • Czy Twój algorytm jest optymalny? Może robisz zbyt wiele prostych obliczeń?

  • Jeśli punkty pochodzą z bazy danych, czy można wykonać obliczenia jako procedury przechowywane po stronie serwera bazy danych?

  • Jeśli szukasz najbliższych punktów, możesz je jakoś zindeksować?

  • Czy indeksy geoprzestrzenne mogą Ci pomóc?

+0

Nie używam żadnych form rozszerzeń GIS w DB. Czy to by miało duże znaczenie? – Steve

+0

@steve Może to coś zmienić, ale nie jestem pewien jak duży. Jeśli twoje punkty znajdują się w przestrzennie włączonej bazie danych, możesz pozwolić bazy danych na obliczenie odległości. Ale to trochę poza zakresem twojego pytania (jak to zrobić w java). – steenhulthin

+0

Dodanie db nie jest problemem, o ile mam coś, co mogę wskazać java, do którego dostaję ode mnie numer dystansu, który byłby w porządku :) – Steve

1

Możecie spróbować twierdzenie cosinusów dla trygonometrii sferycznej:

a = sin(lat1) * sin(lat2) 
b = cos(lat1) * cos(lat2) * cos(lon2 - lon1) 
c = arccos(a + b) 
d = R * c 

Ale będzie być niedokładne na krótkich dystansach (i prawdopodobnie po prostu rośnie nieznacznie szybciej).

Istnieje pełna dyskusja here. Jednak formuła haversine jest najbardziej poprawną metodą, więc poza tym, co sugerują inni, może nie być wiele, co możesz zrobić. @ Odpowiedź Alnitaka może działać, ale sferyczne do kartezjańskich konwersji niekoniecznie są szybkie.

+1

to tylko cztery trygondy (lub dwa tryby, 2 muldy , 2 odejmowanie i 2 sqrts), aby przekonwertować z sferycznego na polarny, a punktem mojej odpowiedzi jest to, że musisz to zrobić tylko raz _ punkt, nie dla każdej kombinacji par. – Alnitak

+0

Dzięki! Szczególnie interesują mnie obszary z niewielką odległością, więc być może nie są optymalne. Uruchomę to i zobaczę jak to wygląda! – Steve

+0

Wadą Haversine'a jest to, że obliczenia pośrednie opierają się na różnicach między kątami, więc trzeba je opracować dla każdej pary. Problem opisany w łączu z arccos (użyty tutaj i w mojej odpowiedzi) nie powinien mieć znaczenia, jeśli użyjesz zmiennych "podwójnych" z 16 cyframi precyzji. – Alnitak

Powiązane problemy