Distance between two points on the Moon

11

2

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.

mdahmoune

Posted 2018-04-10T13:42:57.673

Reputation: 2 605

7Using that particular formula is an unobservable requirement. Isn't it enough to give the same result as that formula would give? – Adám – 2018-04-10T14:00:58.780

1May we take the input in radians? – Adám – 2018-04-10T14:02:51.657

Must we round, or may we return a result with decimals? – Adám – 2018-04-10T14:04:32.973

@Adám the inputs are in degree to avoid decimal inputs – mdahmoune – 2018-04-10T14:06:23.500

@Adám decimal with any precision or rounded integer – mdahmoune – 2018-04-10T14:09:04.297

@Adám yes of-course, you are allowed to use any formula that gives the same result. – mdahmoune – 2018-04-10T14:11:13.237

1@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. – Adám – 2018-04-10T14:11:37.680

@mdahmoune If any formula is allowed, you should state so. – Adám – 2018-04-10T14:11:54.563

ϕ1 latitude of point 1 in radians: since the only way the angles are used are as arguments to sin, their unit doesn't matter. – Adám – 2018-04-10T14:14:55.473

1@Adám you can use degrees or radian just specify in the answer whether the inputs are in degrees or radian. – mdahmoune – 2018-04-10T14:17:46.690

1I don't think this challenge will prompt varied or interesting answers. When the method of solving it is the same, you get a bunch of cookie-cutter solutions. – mbomb007 – 2018-04-10T14:34:24.977

@mbomb007 Who says the method must be the same... | {@}mdahmoune are you still going to keep that unobservable requirement? What if our language doesn't have sine and only cosine? Can we use cosine and shift the values instead? etc. – user202729 – 2018-04-10T15:05:04.273

1@user202729 it is mentioned in the question that you can use any formula that gives the same result as the haversine formula. – mdahmoune – 2018-04-10T15:24:11.190

5I'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. – caird coinheringaahing – 2018-04-10T15:27:51.483

@cairdcoinheringaahing Of course you can use another formula... – user202729 – 2018-04-10T15:38:32.303

2A shorter formula for most languages would be d = r * acos( sin ϕ1 sin ϕ2 + cos ϕ1 cos ϕ2 cos(λ2 - λ1) ) where r = 1737 – miles – 2018-04-10T17:35:51.123

@miles thanks you can update the question by adding your formula if you want – mdahmoune – 2018-04-10T18:08:44.710

1@miles r * acos(cos(ϕ1 - ϕ2) + cos ϕ1 cos ϕ2 (cos(λ2 - λ1) - 1)) is shorter still in some languages. – Neil – 2018-04-11T11:38:12.257

I agree that Neil's and miles' formulas are mathematically equivalent, but the haversine formula was intended to give better precision in the cases the angles are close to each other. – jxh – 2018-04-11T18:51:52.910

Are we supposed to pretend that the Moon is spherical? It's less oblate than Earth, but still seems wrong to simplify that much... – Toby Speight – 2018-07-11T12:50:33.383

@TobySpeight yes, at least for this challenge – mdahmoune – 2018-07-11T13:37:12.770

Answers

6

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

miles

Posted 2018-04-10T13:42:57.673

Reputation: 15 654

6

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.

Giuseppe

Posted 2018-04-10T13:42:57.673

Reputation: 21 077

Are the e3 and /1000 really necessary? – Jonathan Allan – 2018-04-10T18:45:22.450

1@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 – Giuseppe – 2018-04-10T18:46:34.270

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.

– ceased to turn counterclockwis – 2018-04-10T22:11:02.537

@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. – Giuseppe – 2018-04-10T22:17:05.630

Yes, or using a stable formula like, say, the haversine formula... – ceased to turn counterclockwis – 2018-04-10T22:18:20.633

5

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 π

Adám

Posted 2018-04-10T13:42:57.673

Reputation: 37 779

5

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!

Arnauld

Posted 2018-04-10T13:42:57.673

Reputation: 111 334

2You 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) – l4m2 – 2018-04-10T16:29:51.153

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)) – Kevin Cruijssen – 2018-04-11T07:12:56.127

174 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)) – Kevin Cruijssen – 2018-04-11T08:34:30.417

365 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)) – Olivier Grégoire – 2018-04-11T08:37:33.343

@OlivierGrégoire Very nice. You probably should post it as a new answer. – Arnauld – 2018-04-11T08:50:53.807

5

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.

Olivier Grégoire

Posted 2018-04-10T13:42:57.673

Reputation: 10 647

4

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!

TFeld

Posted 2018-04-10T13:42:57.673

Reputation: 19 246

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 – mdahmoune – 2018-04-10T14:20:28.793

@mdahmoune, Thanks, updated – TFeld – 2018-04-10T14:26:09.183

3

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

Kevin Cruijssen

Posted 2018-04-10T13:42:57.673

Reputation: 67 575

1"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)) – Olivier Grégoire – 2018-04-11T08:04:25.917

"Premature optimization is the root of all evil" I guess you're indeed right.. Thanks! – Kevin Cruijssen – 2018-04-11T08:26:16.647

1I'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)) – Neil – 2018-04-11T08:27:50.267

(That formulation isn't shorter in the original Wolfram Language though.) – Neil – 2018-04-11T08:31:24.220

3

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.

Asone Tuhid

Posted 2018-04-10T13:42:57.673

Reputation: 1 944

Using Neil's formula is 17 bytes shorter: ->a,b,c,d{include Math;1737*acos(cos(a-c)+cos(a)*cos(c)*(cos(d-b)-1))}

– Kevin Cruijssen – 2018-04-12T14:11:28.800

3

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

Asone Tuhid

Posted 2018-04-10T13:42:57.673

Reputation: 1 944

3

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!

Nit

Posted 2018-04-10T13:42:57.673

Reputation: 2 667

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. https://ethproductions.github.io/japt/?v=1.4.5&code=I603&input=

– Oliver – 2018-04-11T16:19:06.153

You can also remove the , after ToMP :-) – Oliver – 2018-04-11T16:30:07.060

@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. – Nit – 2018-04-11T18:04:57.970

1If you do want to go the JavaScript route, keep in mind that you can run everything through shoco. – Oliver – 2018-04-11T19:45:51.323

2

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

Jonathan Allan

Posted 2018-04-10T13:42:57.673

Reputation: 67 804

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×⁽£ġ – miles – 2018-04-10T19:01:45.003

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?! – Jonathan Allan – 2018-04-10T19:04:45.010

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. – miles – 2018-04-10T19:24:24.457

2

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

Luis Mendo

Posted 2018-04-10T13:42:57.673

Reputation: 87 464

1

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!

mbomb007

Posted 2018-04-10T13:42:57.673

Reputation: 21 944

Using Neil's formula is 16 bytes shorter: 1737*acos(cos(a-A)+cos(a)*cos(A)*(cos(O-o)-1))

– Kevin Cruijssen – 2018-04-12T14:09:11.483

1

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!

jxh

Posted 2018-04-10T13:42:57.673

Reputation: 331

I believe you need to add two bytes for the -lm compiler flag. – O.O.Balance – 2018-04-11T12:35:42.490

@O.O.Balance: The presence of the flag is not always required. It depends on how the compiler was installed on the system. – jxh – 2018-04-11T14:37:59.780

Alright. Guess this means I can subtract two bytes on this answer of mine: https://codegolf.stackexchange.com/a/161452/79343 Thanks.

– O.O.Balance – 2018-04-11T15:22:18.613

@O.O.Balance: Upvoted the answer. I also submitted my own solution. – jxh – 2018-04-11T21:31:48.043

Nice. Upvoted yours as well. – O.O.Balance – 2018-04-11T22:23:57.873

1

Python 3, 79 bytes

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

TIO doesn't have geopy.py

RootTwo

Posted 2018-04-10T13:42:57.673

Reputation: 1 749

2@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. – RootTwo – 2018-04-11T02:39:50.177

1

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 π

Adám

Posted 2018-04-10T13:42:57.673

Reputation: 37 779

1

Excel, 53 bytes

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

Using @Neil's formula. Input in Radians.

Wernisch

Posted 2018-04-10T13:42:57.673

Reputation: 2 534

1

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.

Panda0nEarth

Posted 2018-04-10T13:42:57.673

Reputation: 111

1

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!

Luis felipe De jesus Munoz

Posted 2018-04-10T13:42:57.673

Reputation: 9 639

1

SmileBASIC, 60 bytes

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

12Me21

Posted 2018-04-10T13:42:57.673

Reputation: 6 110