11
\$\begingroup\$

Given latitude/longitude of two points on the Moon (lat1, lon1) and (lat2, lon2), compute the distance between the two points in kilometers, by using any formula that gives the same result as the haversine formula.

Input

  • Four integer values lat1, lon1, lat2, lon2 in degree (angle) or
  • four decimal values ϕ1, λ1, ϕ2, λ2 in radians.

Output

Distance in kilometers between the two points (decimal with any precision or rounded integer).

Haversine formula

d = 2 r \arcsin\left(\sqrt{\sin^2\left(\frac{\phi_2 - \phi_1}{2}\right) + \cos(\phi_1) \cos(\phi_2)\sin^2\left(\frac{\lambda_2 - \lambda_1}{2}\right)}\right)

where

  • r is the radius of the sphere (assume that the Moon's radius is 1737 km),
  • ϕ1 latitude of point 1 in radians
  • ϕ2 latitude of point 2 in radians
  • λ1 longitude of point 1 in radians
  • λ2 longitude of point 2 in radians
  • d is the circular distance between the two points

(source: https://en.wikipedia.org/wiki/Haversine_formula)

Other possible formulas

  • d = r * acos(sin ϕ1 sin ϕ2 + cos ϕ1 cos ϕ2 cos(λ2 - λ1)) @miles' formula.
  • d = r * acos(cos(ϕ1 - ϕ2) + cos ϕ1 cos ϕ2 (cos(λ2 - λ1) - 1)) @Neil's formula.

Example where inputs are degrees and output as rounded integer

42, 9, 50, 2  --> 284
50, 2, 42, 9  --> 284
4, -2, -2, 1  --> 203
77, 8, 77, 8  --> 0
10, 2, 88, 9  --> 2365

Rules

  • The input and output can be given in any convenient format.
  • Specify in the answer whether the inputs are in degrees or radians.
  • No need to handle invalid latitude/longitude values
  • Either a full program or a function are acceptable. If a function, you can return the output rather than printing it.
  • If possible, please include a link to an online testing environment so other people can try out your code!
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.
\$\endgroup\$
  • 7
    \$\begingroup\$ Using that particular formula is an unobservable requirement. Isn't it enough to give the same result as that formula would give? \$\endgroup\$ – Adám Apr 10 '18 at 14:00
  • 1
    \$\begingroup\$ May we take the input in radians? \$\endgroup\$ – Adám Apr 10 '18 at 14:02
  • 1
    \$\begingroup\$ @mdahmoune OK, so you listed in degrees for ease of writing, but may we require input to be in radians? Otherwise this challenge becomes a combo (which is bad) of angle conversion and of the main challenge. \$\endgroup\$ – Adám Apr 10 '18 at 14:11
  • 5
    \$\begingroup\$ I've downvoted this question because it seems more to be 'Who's language can golf this formula the most', which, in my opinion, isn't particularly interesting. \$\endgroup\$ – caird coinheringaahing Apr 10 '18 at 15:27
  • 2
    \$\begingroup\$ A shorter formula for most languages would be d = r * acos( sin ϕ1 sin ϕ2 + cos ϕ1 cos ϕ2 cos(λ2 - λ1) ) where r = 1737 \$\endgroup\$ – miles Apr 10 '18 at 17:35

20 Answers 20

6
\$\begingroup\$

Wolfram Language (Mathematica), 48 bytes

ArcCos[Sin@#*Sin@#3+Cos[#2-#4]Cos@#*Cos@#3]1737&

Try it online!

Uses the formula d = r * acos( sin ϕ1 sin ϕ2 + cos ϕ1 cos ϕ2 cos(λ2 - λ1) ) where r = 1737

\$\endgroup\$
6
\$\begingroup\$

R + geosphere, 54 47 bytes

function(p,q)geosphere::distHaversine(p,q,1737)

Try it online!

Takes input as 2-element vectors of longitude,latitude in degrees. TIO doesn't have the geosphere package but rest assured that it returns identical results to the function below.

Thanks to Jonathan Allan for shaving off 7 bytes.

R, 64 bytes

function(p,l,q,k)1737*acos(sin(p)*sin(q)+cos(p)*cos(q)*cos(k-l))

Try it online!

Takes 4 inputs as in the test cases, but in radians rather than degrees.

\$\endgroup\$
  • \$\begingroup\$ Are the e3 and /1000 really necessary? \$\endgroup\$ – Jonathan Allan Apr 10 '18 at 18:45
  • 1
    \$\begingroup\$ @JonathanAllan no they are not. That's pretty dumb of me, but the default argument for the radius is the earth's in meters so it was logical at the time, lol \$\endgroup\$ – Giuseppe Apr 10 '18 at 18:46
  • \$\begingroup\$ Note that the spherical law of cosines is not numerically stable, in particular for small distances. That's probably ok in Mathematica, but in R and most other languages it's debatable whether the "any formula that gives the same result as the haversine formula" criterion is fulfilled. \$\endgroup\$ – ceased to turn counterclockwis Apr 10 '18 at 22:11
  • \$\begingroup\$ @ceasedtoturncounterclockwis I mostly included it for the sake of having it in base R. I suppose using an arbitrary precision floating point library would mitigate the effect. \$\endgroup\$ – Giuseppe Apr 10 '18 at 22:17
  • \$\begingroup\$ Yes, or using a stable formula like, say, the haversine formula... \$\endgroup\$ – ceased to turn counterclockwis Apr 10 '18 at 22:18
5
\$\begingroup\$

JavaScript (Node.js), 65 bytes

(a,b,c,d,C=Math.cos)=>1737*Math.acos(C(a-c)+C(a)*C(c)*(C(d-b)-1))

Try it online!

Based on Kevin Cruijssen's answer, Miles' and Neil's comments, and upon request of Arnauld.

\$\endgroup\$
5
\$\begingroup\$

JavaScript (ES7), 90 bytes

Note: see @OlivierGrégoire's post for a much shorter solution

A direct port of TFeld's answer. Takes input in radians.

(a,b,c,d,M=Math)=>3474*M.asin((M.sin((c-a)/2)**2+M.cos(c)*M.cos(a)*M.sin((d-b)/2)**2)**.5)

Try it online!

Using the infamous with(), 85 bytes

Thanks to @l4m2 for saving 6 bytes

with(Math)f=(a,b,c,d)=>3474*asin((sin((c-a)/2)**2+cos(c)*cos(a)*sin((d-b)/2)**2)**.5)

Try it online!

\$\endgroup\$
  • 2
    \$\begingroup\$ You can do with(Math)f=(a,b,c,d)=>3474*asin((sin((c-a)/2)**2+cos(c)*cos(a)*sin((d-b)/2)**2)**.5) \$\endgroup\$ – l4m2 Apr 10 '18 at 16:29
  • \$\begingroup\$ 77 bytes using @miles' shorter algorithm: (a,b,c,d,M=Math)=>1737*M.acos(M.sin(a)*M.sin(c)+M.cos(a)*M.cos(c)*M.cos(d-b)) \$\endgroup\$ – Kevin Cruijssen Apr 11 '18 at 7:12
  • 1
    \$\begingroup\$ 74 bytes using @Neil's shorter algorithm: (a,b,c,d,M=Math)=>1737*M.acos(M.cos(a-c)+M.cos(a)*M.cos(c)*(M.cos(d-b)-1)) \$\endgroup\$ – Kevin Cruijssen Apr 11 '18 at 8:34
  • 3
    \$\begingroup\$ 65 bytes optimizing everybody's answer: (a,b,c,d,C=Math.cos)=>1737*Math.acos(C(a-c)+C(a)*C(c)*(C(d-b)-1)) \$\endgroup\$ – Olivier Grégoire Apr 11 '18 at 8:37
  • \$\begingroup\$ @OlivierGrégoire Very nice. You probably should post it as a new answer. \$\endgroup\$ – Arnauld Apr 11 '18 at 8:50
5
\$\begingroup\$

APL (Dyalog Unicode), 40 35 bytesSBCS

Anonymous tacit function. Takes {ϕ₁,λ₁} as left argument and {ϕ₂,λ₂} as right argument.

Uses the formula 2 r √(sin²((ϕ₁-ϕ₂)2) + cos ϕ₁ cos ϕ₂ sin²((λ₁ – λ₂)2))

3474ׯ1○.5*⍨1⊥(×⍨1○2÷⍨-)×1,2×.○∘⊃,¨

Try it online! (the r function converts degrees to radians)


 concatenate corresponding elements; {{ϕ₁ , ϕ₂} , {λ₁ , λ₂}}

 pick the first; {ϕ₁ , ϕ₂}

 then

2×.○ product of their cosines; cos ϕ₁ cos ϕ₂
lit. dot "product" but with trig function selector (2 is cosine) instead of multiplication and times instead of plus

1, prepend 1 to that; {1 , cos ϕ₁ cos ϕ₂}

( multiply that by the result of applying following function to {ϕ₁ , λ₁} and {ϕ₂ , λ₂}:

- their differences; {ϕ₁ - ϕ₂ , λ₁ - λ₂}

2÷⍨ divide that by 2; {(ϕ₁ - ϕ₂)2 , (λ₁ - λ₂)2}

1○ sine of that; {sin((ϕ₁ - ϕ₂)2) , sin((λ₁ - λ₂)2)}

×⍨ square that (lit. self-multiply); {sin²((ϕ₁ - ϕ₂)2) , sin²((λ₁-λ₂)2)}

Now we have {sin²((ϕ₁ - ϕ₂)2) , cos ϕ₁ cos ϕ₂ sin²((λ₁ - λ₂)2)}

1⊥ sum that (lit. evaluate in base-1); sin²((ϕ₁-ϕ₂)2) + cos ϕ₁ cos ϕ₂ sin²((λ₁ - λ₂)2)

.5*⍨ square-root of that (lit. raise that to the power of a half)

¯1○ arcsine of that

3474× multiply that by this


The function to allow input in degrees is:

○÷∘180

÷180 argument divided by 180

 multiply by π

\$\endgroup\$
4
\$\begingroup\$

Python 2, 95 bytes

lambda a,b,c,d:3474*asin((sin((c-a)/2)**2+cos(c)*cos(a)*sin((d-b)/2)**2)**.5)
from math import*

Try it online!

Takes input in radians.


Old version, before i/o was slacked: Takes input as integer degrees, and returns rounded dist

Python 2, 135 bytes

lambda a,b,c,d:int(round(3474*asin((sin((r(c)-r(a))/2)**2+cos(r(c))*cos(r(a))*sin((r(d)-r(b))/2)**2)**.5)))
from math import*
r=radians

Try it online!

\$\endgroup\$
  • \$\begingroup\$ you can drop int and round because decimals are allowed as output, you can also avoid conversion to radians because inputs as radians are also allowed \$\endgroup\$ – mdahmoune Apr 10 '18 at 14:20
  • \$\begingroup\$ @mdahmoune, Thanks, updated \$\endgroup\$ – TFeld Apr 10 '18 at 14:26
3
\$\begingroup\$

Java 8, 113 92 88 82 bytes

(a,b,c,d)->1737*Math.acos(Math.cos(a-c)+Math.cos(a)*Math.cos(c)*(Math.cos(d-b)-1))

Inputs a,b,c,d are ϕ1,λ1,ϕ2,λ2 in radians.

-21 bytes using @miles' shorter formula.
-4 bytes thanks to @OlivierGrégore because I still used {Math m=null;return ...;} with every Math. as m., instead of dropping the return and use Math directly.
-6 bytes using @Neil's shorter formula.

Try it online.

Explanation:

(a,b,c,d)->                  // Method with four double parameters and double return-type
  1737*Math.acos(            //  Return 1737 multiplied with the acos of:
   Math.cos(a-c)             //   the cos of `a` minus `c`,
   +Math.cos(a)*Math.cos(c)  //   plus the cos of `a` multiplied with the cos of `c`
   *(Math.cos(d-b)-1))       //   multiplied with the cos of `d` minus `b` minus 1
\$\endgroup\$
  • 1
    \$\begingroup\$ "Premature optimization is the root of all evil"! 88 bytes: (a,b,c,d)->1737*Math.acos(Math.sin(a)*Math.sin(c)+Math.cos(a)*Math.cos(c)*Math.cos(d-b)) \$\endgroup\$ – Olivier Grégoire Apr 11 '18 at 8:04
  • \$\begingroup\$ "Premature optimization is the root of all evil" I guess you're indeed right.. Thanks! \$\endgroup\$ – Kevin Cruijssen Apr 11 '18 at 8:26
  • 1
    \$\begingroup\$ I've found a shorter formulation: (a,b,c,d)->1737*Math.acos(Math.cos(a-c)+Math.cos(a)*Math.cos(c)*(Math.cos(d-b)-1)) \$\endgroup\$ – Neil Apr 11 '18 at 8:27
  • \$\begingroup\$ (That formulation isn't shorter in the original Wolfram Language though.) \$\endgroup\$ – Neil Apr 11 '18 at 8:31
3
\$\begingroup\$

Japt, 55 50 bytes

MsU *MsW +McU *McW *McX-V
ToMP1/7l¹ñ@McX aUÃv *#­7

Not necessarily quite as precise as the other answers, but boy did I have fun with this one. Allow me to elaborate.
While in most languages, this challenge is quite straightforward, Japt has the unfortunate property that there is no built-in for neither arcsine nor arccosine. Sure, you can embed Javascript in Japt, but that would be what ever the opposite of Feng Shui is.

All we have to do to overcome this minor nuisance is approximate arccosine and we're good to go!

The first part is everything that gets fed into the arccosine.

MsU *MsW +McU *McW *McX-V
MsU                        // Take the sine of the first input and
    *MsW...                // multiply by the cos of the second one etc.

The result is implicitly stored in U to be used later.

Following that, we need to find a good approximation for arccosine. Since I'm lazy and not that good with math, we're obviously just going to brute-force it.

ToMP1/7l¹ñ@McX aUÃv *#­7
T                       // Take 0
 o                      // and create a range from it
  MP                    // to π
    1/7l¹               // with resolution 1/7!.
         ñ@             // Sort this range so that
           McX          // the cosine of a given value
               aU       // is closest to U, e.g. the whole trig lot
                        // we want to take arccosine of.
                 Ã      // When that's done,
                  v     // get the first element
                    *#­7 // and multiply it by 1737, returning implicitly.

We could've used any large number for the generator resolution, manual testing showed 7! is sufficiently large while being reasonably fast.

Takes input as radians, outputs unrounded numbers.

Shaved off five bytes thanks to Oliver.

Try it online!

\$\endgroup\$
  • \$\begingroup\$ You can remove the ( in Mc(X-V. Since the char-code for 1737 isn't ISO-8859-1, it switches to UTF-8, which costs more. You can instead use the char-code for 173+7. ethproductions.github.io/japt/?v=1.4.5&code=I603&input= \$\endgroup\$ – Oliver Apr 11 '18 at 16:19
  • \$\begingroup\$ You can also remove the , after ToMP :-) \$\endgroup\$ – Oliver Apr 11 '18 at 16:30
  • \$\begingroup\$ @Oliver Thanks a lot, the parenthesis were necessary in my original version but became obsolete when golfing it around a bit, I completely missed it though. Also, didn't know about the encoding thing, thanks a lot for that, too. \$\endgroup\$ – Nit Apr 11 '18 at 18:04
  • 1
    \$\begingroup\$ If you do want to go the JavaScript route, keep in mind that you can run everything through shoco. \$\endgroup\$ – Oliver Apr 11 '18 at 19:45
3
\$\begingroup\$

Haskell, 68 66 52 51 bytes

s=cos;(a!b)c d=1737*acos(s(a-c)+s a*s c*(s(d-b)-1))

Try it online!

-1 byte thanks to BMO

\$\endgroup\$
3
\$\begingroup\$

Ruby, 87 70 69 bytes

->a,b,c,d{extend Math;1737*acos(cos(a-c)+cos(a)*cos(c)*(cos(d-b)-1))}

Try it online!

Now using Neil's method, thanks to Kevin Cruijssen.

\$\endgroup\$
2
\$\begingroup\$

Jelly,  23 22  18 bytes

-4 bytes thanks to miles (use of { and } while using their formula.

;I}ÆẠP+ÆSP${ÆA×⁽£ġ

A dyadic function accepting [ϕ1, ϕ2,] on the left and [λ1, λ2] on the right in radians that returns the result (as floating point).

Try it online!


Mine... (also saved a byte here by using a {)

,IÆẠCH;ÆẠ{Ḣ+PƊ½ÆṢ×⁽µṣ

Try it online

\$\endgroup\$
  • \$\begingroup\$ Oh interesting, I refreshed the page again and it showed your edit, just clicking new answer to show the change doesn't update to show your edits. the 18 byte alternative was ;I}ÆẠP+ÆSP${ÆA×⁽£ġ \$\endgroup\$ – miles Apr 10 '18 at 19:01
  • \$\begingroup\$ Never understood how to use { and } they never do what I'd expect. Doesn't that mean I can do the other way in 17?! \$\endgroup\$ – Jonathan Allan Apr 10 '18 at 19:04
  • \$\begingroup\$ Maybe. { and } just create a dyad from a monad. A similar view might be P{ -> ḷP¥. Might be good to add a compose (from J) quick to do something like x (P+$) y -> (P x) + (P y) which can save a byte or two in similar situations. \$\endgroup\$ – miles Apr 10 '18 at 19:24
2
\$\begingroup\$

MATLAB with Mapping Toolbox, 26 bytes

@(x)distance(x{:})*9.65*pi

Anonymous function that takes the four inputs as a cell array, in the same order as described in the challenge.

Note that this gives exact results (assuming that the Moon radius is 1737 km), because 1737/180 equals 9.65.

Example run in Matlab R2017b:

enter image description here

\$\endgroup\$
1
\$\begingroup\$

Python 3, 79 bytes

from geopy import*
lambda a,b:distance.great_circle(a,b,radius=1737).kilometers

TIO doesn't have geopy.py

\$\endgroup\$
  • 2
    \$\begingroup\$ @Nit, my understanding is that it is fair game to use a publicly available library that predates the question. I thinks it's like using MATLAB's mapping tools, or other languages using a math library. \$\endgroup\$ – RootTwo Apr 11 '18 at 2:39
1
\$\begingroup\$

APL (Dyalog Unicode), 29 bytesSBCS

Complete program. Prompts stdin for {ϕ₁,ϕ₂} and then for {λ₁,λ₂}. Prints to stdout.

Uses the formula r acos(sin ϕ₁ sin ϕ₂ + cos(λ₂ – λ₁) cos ϕ₁ cos ϕ₂)

1737ׯ2○+/(2○-/⎕)×@2×/1 2∘.○⎕

Try it online! (the r function converts degrees to radians)


 prompt for {ϕ₁,ϕ₂}

1 2∘.○ Cartesian trig-function application; {{sin ϕ₁,sin ϕ₂} , {cos ϕ₁,cos ϕ₂}}

×/ row-wise products; {sin ϕ₁ sin ϕ₂ , cos ϕ₁ cos ϕ₂}

()×@2 at the second element, multiply the following by that:

 prompt for {λ₁,λ₂}

-/ difference between those; λ₁ – λ₂

2○ cosine of that; cos(λ₁ – λ₂)

Now we have {sin ϕ₁ sin ϕ₂ , cos(λ₁ – λ₂) cos ϕ₁ cos ϕ₂}

+/ sum; sin ϕ₁ sin ϕ₂ + cos(λ₁ – λ₂) cos ϕ₁ cos ϕ₂

¯2○ cosine of that; cos(sin ϕ₁ sin ϕ₂ + cos(λ₁ – λ₂) cos ϕ₁ cos ϕ₂)

1737× multiply r by that; 1737 cos(sin ϕ₁ sin ϕ₂ + cos(λ₁ – λ₂) cos ϕ₁ cos ϕ₂)


The function to allow input in degrees is:

○÷∘180

÷180 argument divided by 180

 multiply by π

\$\endgroup\$
1
\$\begingroup\$

C (gcc), 100 88 65 64 bytes

88 → 65 using @miles' formula
65 → 64 using @Neil's formula

#define d(w,x,y,z)1737*acos(cos(w-y)+cos(w)*cos(y)*(cos(z-x)-1))

Try it online!

\$\endgroup\$
  • \$\begingroup\$ I believe you need to add two bytes for the -lm compiler flag. \$\endgroup\$ – O.O.Balance Apr 11 '18 at 12:35
  • \$\begingroup\$ @O.O.Balance: The presence of the flag is not always required. It depends on how the compiler was installed on the system. \$\endgroup\$ – jxh Apr 11 '18 at 14:37
  • \$\begingroup\$ Alright. Guess this means I can subtract two bytes on this answer of mine: codegolf.stackexchange.com/a/161452/79343 Thanks. \$\endgroup\$ – O.O.Balance Apr 11 '18 at 15:22
  • \$\begingroup\$ @O.O.Balance: Upvoted the answer. I also submitted my own solution. \$\endgroup\$ – jxh Apr 11 '18 at 21:31
  • \$\begingroup\$ Nice. Upvoted yours as well. \$\endgroup\$ – O.O.Balance Apr 11 '18 at 22:23
1
\$\begingroup\$

Excel, 53 bytes

=1737*ACOS(COS(A1-C1)+COS(A1)*COS(C1)*(COS(D1-B1)-1))

Using @Neil's formula. Input in Radians.

\$\endgroup\$
1
\$\begingroup\$

Lobster, 66 bytes

def h(a,c,b,d):1737*radians arccos a.sin*b.sin+a.cos*b.cos*cos d-c

Uses miles's formula, but input is in degrees. This adds extra step of converting to radians before multiplying by radius.

\$\endgroup\$
1
\$\begingroup\$

Python 3, 119 103 bytes

This uses degrees.

from math import*
def f(L):a,o,A,O=map(radians,L);return 1737*acos(cos(a-A)+cos(a)*cos(A)*(cos(O-o)-1))

Try it online!

\$\endgroup\$
1
\$\begingroup\$

PHP, 88 bytes

Port of Oliver answer

function f($a,$b,$c,$d,$e=cos){return 1737*acos($e($a-$c)+$e($a)*$e($c)*($e($d-$b)-1));}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

SmileBASIC, 60 bytes

INPUT X,Y,S,T?1737*ACOS(COS(X-S)+COS(X)*COS(S)*(COS(T-Y)-1))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.