Teaser 3287: Ferry route
From The Sunday Times, 21st September 2025 [link] [link]
A large circular sea, whose diameter is less than 300km, is served by a ferry that makes a clockwise route around half of the sea, serving the ports of Ayton, Beaton, Seaton and Deaton in that order, then back to Ayton. Deaton is diametrically opposite Ayton. Each of the four legs of its route is a straight line, two of them being the same length. The lengths of all of the legs are whole numbers of km, and they all happen to be square numbers.
In increasing order, what are the three different leg lengths?
[teaser3287]
Jim Randell 7:18 am on 21 September 2025 Permalink |
See: Teaser 2549.
If the diameter of the circle is x, and the lengths of the non-diameter legs are a, b, c, then we have:
The following Python program runs in 62ms. (Internal runtime is 643µs).
from enigma import (powers, inf, first, lt, subsets, pi, cb, sumsq, printf) # squares less than 300 sqs = list(first(powers(1, inf, 2), lt(300))) # choose 3 different lengths (x is the diameter) for (a, b, x) in subsets(sqs, size=3): # duplicate one of the short lengths for c in (a, b): # check for a viable quadrilateral if not (x < a + b + c < pi * x): continue if not (cb(x) - sumsq([a, b, c]) * x - 2 * a * b * c == 0): continue # output solution printf("{a}, {b}, {x} [and {c}]")Solution: The leg lengths are 36, 49, 81 km.
The duplicate leg length is 36 km.
The next smallest solution is at 4× these lengths, which brings the diameter to 324 km.
LikeLike
Jim Randell 8:49 am on 22 September 2025 Permalink |
We can suppose that the non-diameter sides are (a, b, a) (i.e. c = a, and it may be the case that a > b), and then the equation can be simplified to:
So by choosing b and x we can then straightforwardly calculate the corresponding value for a, and check it is viable (i.e. a square):
from enigma import (powers, inf, first, lt, subsets, is_square, div, ordered, printf) # squares less than 300 sqs = list(first(powers(1, inf, 2), lt(300))) # choose 2 different lengths (x is the diameter) for (b, x) in subsets(sqs, size=2): # a is the duplicated side a = is_square(div(x * (x - b), 2)) if a is None or a not in sqs: continue # output solution (p, q) = ordered(a, b) printf("{p}, {q}, {x} [and {a}]")LikeLiked by 1 person
Frits 2:29 pm on 22 September 2025 Permalink |
I had come up with similar equation. You can also deduce the parity of “a”.
LikeLike
Ruud 10:35 am on 21 September 2025 Permalink |
import math import itertools for ab, bs, sd, da in itertools.product((i * i for i in range(1, 18)), repeat=4): try: angle_ab = math.asin(ab / da) * 2 angle_bs = math.asin(bs / da) * 2 angle_sd = math.asin(sd / da) * 2 if angle_ab + angle_bs + angle_sd == math.pi: print(sorted({ab, bs, sd, da})) except ValueError: ...LikeLike
Jim Randell 11:38 am on 21 September 2025 Permalink |
@Ruud: How does this code ensure that two of the leg lengths are the same?
And it is probably better to use [[
math.isclose()]] rather than testing floats for equality using==.LikeLike
Ruud 12:55 pm on 21 September 2025 Permalink |
I just checked manually by checking that there are 3 different length. Could have added a simple test, of course.
Yes, math.isclose would have been better, but as the equality check gave already an answer, I thought I could just refrain from isclose.
LikeLike
Ruud 4:14 pm on 21 September 2025 Permalink |
@Jim
I just checked manually that there are exactly three different lengths, but a test would be better.
Yes, math.isclose would have beeen safer.
Here’s my updated version:
import math import itertools for ab, bs, sd, da in itertools.product((i * i for i in range(1, 18)), repeat=4): try: angle_ab = math.asin(ab / da) * 2 angle_bs = math.asin(bs / da) * 2 angle_sd = math.asin(sd / da) * 2 if math.isclose(angle_ab + angle_bs + angle_sd, math.pi) and len(sol := {ab, bs, sd, da}) == 3: print(sorted(sol)) except ValueError: ... peek("done")LikeLike
Alex.T.Sutherland 10:42 am on 28 September 2025 Permalink |
LikeLike