Teaser 3129: Bounce count
From The Sunday Times, 11th September 2022 [link] [link]
At the local arcade, Claire and David played an air hockey game, using a square table with small pockets at each corner, on which a very small puck can travel 1m left-right and 1m up-down between the perimeter walls. Projecting the puck from a corner, players earn a token for each bounce off a wall, until the puck drops into a pocket.
In their game, one puck travelled 1m further overall than its left-right distance (and the other, travelled 2m further). Claire’s three-digit number of tokens was a cube, larger than David’s number which was triangular (1 + 2 + 3 + …). Picking up an extra token, they found they could split their collection into two piles, one consisting of a cube number of tokens and the other a square.
How many tokens did they end up with?
I’ve modified the wording slightly to remove a typo and improve clarity.
[teaser3129]
Jim Randell 5:23 pm on 9 September 2022 Permalink |
With this kind of puzzle it is easier to reflect the table, rather than reflecting the puck. (Assuming the puck bounces in a well behaved fashion). See: Teaser 1917.
If a play has x bounces off the left/right sides, and y bounces of the top/bottom sides, then in order for the play to be viable, (x + 1) and (y + 1) must be coprime, and:
It seems we need to find scores C (a cube) and D (a triangular number) such that (C + D + 1) can be expressed as the sum of a cube and a square.
This Python program runs in 71ms. (Internal run time is 14.6ms).
Run: [ @replit ]
from enigma import ( coprime_pairs, is_square, sq, is_cube, is_triangular, cproduct, powers, inf, printf ) # generate (x, y, z) values, where z is d - x def generate(): # consider coprime pairs for (a, b) in coprime_pairs(200): d = is_square(sq(a) + sq(b)) if d is None: continue for (x, y) in [(a, b), (b, a)]: z = d - x if z in {1, 2}: yield (x - 1, y - 1, z) # find possible values for C and D (Cs, Ds) = (list(), list()) for (x, y, z) in generate(): t = x + y if 99 < t < 1000 and is_cube(t): Cs.append((x, y, z, t)) if t < 1000 and is_triangular(t): Ds.append((x, y, z, t)) # can total t be decomposed into a square and a cube? def is_sq_cb(t): for c in powers(1, inf, 3): s = t - c if s < 1: return False if is_square(s): return True # choose values for C and D for ((Cx, Cy, Cz, Ct), (Dx, Dy, Dz, Dt)) in cproduct([Cs, Ds]): T = Ct + Dt + 1 # Cz/Dz are different; Ct > Dt; T is a square + a cube if Cz != Dz and Dt < Ct and is_sq_cb(T): # output solution printf("T={T} [C=(x={Cx} y={Cy} z={Cz} t={Ct}); D=(x={Dx} y={Dy} z={Dz} t={Dt})]")Solution: They ended up with a total of 171 tokens.
C won 125 (= 5^3) tokens on her go. The puck made 111 left/right bounces and 14 up/down bounces. The left/right distance travelled was 112m and the total distance travelled was 113m. So C’s overall distance was 1m more than the total left/right distance.
D won 45 (= tri(9)) tokens on his go. The puck made 34 left/right bounces and 11 up/down bounces. The left/right distance travelled was 35m and the total distance travelled was 37m. So D’s overall distance was 2m more than the total left/right distance.
Combining their tokens with 1 extra token give 125 + 45 + 1 = 171 tokens in total.
And 171 = 144 + 27 = 12^2 + 3^3.
LikeLike
Jim Randell 9:33 pm on 9 September 2022 Permalink |
Faster (and a bit shorter) to use [[
pythagorean_triples()]] to generate the (x, y, z) values. And we don’t need to check the coprime condition if we just look at primitive pythagorean triples.This Python program runs in 54ms. (Internal run time is 579µs).
Run: [ @replit ]
from enigma import ( pythagorean_triples, is_square, is_cube, is_triangular, cproduct, powers, inf, ifirst, lt, printf ) # generate (x, y, z) values, where z is d - x def generate(): for (a, b, c) in pythagorean_triples(1001, primitive=1): z = c - b if z in {1, 2}: yield (b - 1, a - 1, z) # find possible values for C and D (Cs, Ds) = (list(), list()) for (x, y, z) in generate(): t = x + y if 99 < t < 1000 and is_cube(t): Cs.append((x, y, z, t)) if t < 1000 and is_triangular(t): Ds.append((x, y, z, t)) # can total t be decomposed into a square and a cube? def is_sq_cb(t): return any(is_square(t - c) for c in ifirst(powers(1, inf, 3), count=lt(t))) # choose values for C and D for ((Cx, Cy, Cz, Ct), (Dx, Dy, Dz, Dt)) in cproduct([Cs, Ds]): T = Ct + Dt + 1 # Cz/Dz are different; Ct > Dt; T is a square + a cube if Cz != Dz and Dt < Ct and is_sq_cb(T): # output solution printf("T={T} [C=(x={Cx} y={Cy} z={Cz} t={Ct}); D=(x={Dx} y={Dy} z={Dz} t={Dt})]")LikeLike
Frits 9:48 am on 10 September 2022 Permalink |
@Jim, how do you guarantee one puck travelled 1m and the other 2m?
LikeLike
Jim Randell 9:50 am on 10 September 2022 Permalink |
@Frits: line 10, and line 30.
LikeLike
Frits 9:54 am on 10 September 2022 Permalink |
Can’t both C and D not have a difference of 1 m? or both 2m?
LikeLike
Jim Randell 9:55 am on 10 September 2022 Permalink |
No. Line 30 ensures the z values are different.
LikeLike
Frits 10:00 am on 10 September 2022 Permalink |
@you are right, I only saw line 10. I may have overlooked it as I use z as the hypotenuse.
LikeLike
Frits 8:31 pm on 9 September 2022 Permalink |
from itertools import product from enigma import is_square, is_cube, is_triangular # can number <n> be decomposed into a square and a cube? def check(n): for i in range(int(n**(1/3)) + 1): if is_square(n - i**3): return True return False g1 = [] g2 = [] # travelled distance is hypothenuse z # for one person one side is z - 1, for the other person it is z - 2 for z in range(1, 1003): # store sides of a right triangle (x, y, z) if x + y - 2 < 1000 # z**2 - (z - 1)**2 = 2z - 1 if (rt := is_square(2 * z - 1)) and (z - 1 + rt - 2) < 1000: g1.append((z, z - 1, rt)) # z**2 - (z - 2)**2 = 4(z - 1) if (rt := is_square(4 * (z - 1))) and (z - 2 + rt - 2) < 1000: g2.append((z, z - 2, rt)) # check if number of earned tokens x + y - 2 is cube or triangular g1 = [(x + y - 2, (c, t), (z, x, y)) for z, x, y in g1 if ((c := is_cube(x + y - 2)) and x + y - 2 > 99) or (t := is_triangular(x + y - 2))] g2 = [(x + y - 2, (c, t), (z, x, y)) for z, x, y in g2 if ((c := is_cube(x + y - 2)) and x + y - 2 > 99) or (t := is_triangular(x + y - 2))] # check all combinations for p1, p2 in product(g1, g2): # there should be at least one cube and one triangular number if any(p1[1][i] == p2[1][i] == None for i in range(2)): continue # cube should be higher than triangular number if p1[0] == p2[0]: continue if p1[0] > p2[0] and p1[1][0] == None: continue if p2[0] > p1[0] and p2[1][0] == None: continue # can we arrange all their tokens plus one into a cube and a square? if check(p1[0] + p2[0] + 1): print("answer:", p1[0], "and", p2[0])LikeLike
Frits 11:31 am on 10 September 2022 Permalink |
Easier to read.
from enigma import is_square, is_cube, is_triangular, ceil # can number <n> be decomposed into a square and a cube? def check(n): for i in range(1, ceil(n**(1/3))): if is_square(n - i**3): return True return False # travelled distance is hypothenuse z of a right triangle # for one person one side is z - 1, for the other person it is z - 2 cubes, tris = [], [] for z in range(1, 1003): # 1m or 2m further for f in range(1, 3): # calculate other side (besides z and z - f) other = is_square(2 * f * z - f**2) # z**2 - (z - f)**2 if not other: continue # for a right triangle (x, y, z) we earn x + y - 2 tokens tkns = z - f + other - 2 if is_cube(tkns) and tkns > 99: cubes.append((tkns, f, (z - f, other))) if is_triangular(tkns): tris.append((tkns, f, (z - f, other))) # check all combinations for c in cubes: for t in tris: # cube larger than triangular and using both 1m further and 2m further if t[0] >= c[0] or t[1] == c[1]: continue # can we arrange all their tokens plus one into a cube and a square? if check(c[0] + t[0] + 1): print("answer:", c[0], "and", t[0])LikeLike
Jim Randell 3:38 pm on 10 September 2022 Permalink |
@Frits: Yes, I think that is much clearer.
Although I don’t see a check to ensure that the left/right and up/down distances are coprime. This is necessary for a valid path. If this is not the case the puck will hit a pocket before it reaches the end of the path.
LikeLike
Frits 9:14 pm on 10 September 2022 Permalink |
You are right, I had to verify the coprime thing with the following graph.
If both distances share a factor then there is at least one point on the hypothenuse (besides the end points) where both x and y are whole numbers (and thus the puck drops prematurely in a pocket).
| f(x) = b - (x * b) / a b-| suppose b and a share factor k, k > 1 |\ so a = k * a2, b = k * b2 | \ | \ f(x) = b - (x * b2 / k) / (a2 / k) | \ if x = a2 then f(x) = b - b2 (integer) | \ +--------- aLikeLike
Jim Randell 10:46 pm on 10 September 2022 Permalink |
See also: Enigma 1308, Enigma 1110.
LikeLike