Teaser 3084: Face value
From The Sunday Times, 31st October 2021 [link] [link]
Plato: I have written a different whole number (chosen from 1 to 9 inclusive) on each of the faces of one of my regular solids and have labelled each vertex with the product of the numbers on its adjacent faces. If I tell you the sum of those eight vertex labels, you can’t deduce my numbers, but if I rearrange the numbers on the faces and tell you the new sum, then you can deduce the numbers.
Eudoxus: Tell me the new sum then.
Plato: No, but I’ll tell you it’s a 10th power.
Eudoxus: Aha! I know your numbers now.
Plato: Yes, that’s right. But if I now told you the original sum, you couldn’t work out which numbers were originally opposite each other.
What was the original sum?
[teaser3084]
Jim Randell 5:01 pm on 29 October 2021 Permalink |
The cube is the only Platonic Solid [@wikipedia] with 8 vertices.
This Python program runs in 51ms.
Run: [ @replit ]
from enigma import (irange, inf, subsets, partitions, group, unpack, peek, powers, printf) # generate face pairs and vertex sum for a set of numbers def arrange(ns): for fs in partitions(ns, 2, distinct=1): ((U, D), (L, R), (F, B)) = fs # calculate the values on the vertices vs = ( U * L * F, U * R * F, U * L * B, U * R * B, D * L * F, D * R * F, D * L * B, D * R * B, ) yield (ns, fs, sum(vs)) # generate all possible face pairs and vertex sums def generate(): for ns in subsets(irange(1, 9), size=6): yield from arrange(ns) # map vertex sum to sets of numbers d = group(generate(), by=unpack(lambda ns, fs, t: t), f=unpack(lambda ns, fs, t: ns), fn=set) # look for vertex sums that are 10th powers m = max(d.keys()) for k in powers(1, inf, 10): if k > m: break vs = d.get(k, None) if vs is None: continue printf("{k} -> {vs}") assert len(vs) == 1 # map vertex sums (for this set of numbers) to face pairs r = group(arrange(peek(vs)), by=unpack(lambda ns, fs, t: t), f=unpack(lambda ns, fs, t: fs)) # look for sums with multiple face pairs (and multiple number sets) for (t, fs) in r.items(): if not (len(fs) > 1 and len(d[t]) > 1): continue printf(" {t} -> {fs}")Solution: Plato’s original labelling gave a vertex sum of 1188.
There are many ways to achieve this, so we couldn’t tell which set of numbers he used:
But he then rearranged his numbers to give a vertex sum of 1024, which can only be done in one way:
So we know he used the numbers (2, 3, 5, 6, 7, 9).
He then tells us that even though we know the numbers, we still can’t tell his original arrangement.
This is because there are two ways to achieve 1188 using this set of numbers:
And this is the only set of numbers for 1188 that has more than one arrangement.
Manually, noting:
gives us a quick way to calculate the vertex sum. It is the same as the product of the three sums of opposite face pairs.
And this lets us simplify the
arrange()function above:from enigma import multiply def arrange(ns): for fs in partitions(ns, 2, distinct=1): yield (ns, fs, multiply(x + y for (x, y) in fs))And we see that the only possible value for the 10th power is 2^10 = 1024.
So the values on opposite faces must sum to powers of 2:
The only arrangement that gives 1024 is:
We can then look for 2 different arrangements of these numbers into pairs that give the same vertex sum. And 1188 is the only possibility.
LikeLike
Frits 11:54 am on 30 October 2021 Permalink |
@Jim, len(vs) is likely to be 1 but I am not sure if it can be used for checking if the numbers can be deduced.
LikeLike
Jim Randell 12:03 pm on 30 October 2021 Permalink |
Yes, we can check the values in
vsall use the same numbers:And then
nsis just this single value.I’ll update my code.
(In fact, I’ve rewritten it to be a bit faster and more compact).
LikeLike
Frits 11:19 am on 30 October 2021 Permalink |
from enigma import SubstitutedExpression, tri ndigits = 9 nfaces = 6 npairs = nfaces // 2 # highest possible sum h = ((tri(ndigits) - tri(ndigits - nfaces)) / npairs) ** npairs powers = [i for i in range(2, int(h ** 0.1) + 1)] # based on the entries in <powers> we can easily deduct the digits used # in the new sum (in this case the highest pair sum must be a 4th power) # the alphametic puzzle p = SubstitutedExpression( [ # find 2 different arrangements with same sum "A < C < E", # order the pairs "A < B and C < D and E < F", # order within the pairs "G < I < K", # order the pairs "G < H and I < J and K < L", # order within the pairs # different arrangements "(A, B, C, D, E, F) < (G, H, I, J, K, L)", # same sum "(A + B) * (C + D) * (E + F) == (G + H) * (I + J) * (K + L)", ], answer="((A, B), (C, D), (E, F)), ((G, H), (I, J), (K, L)), \ (A + B) * (C + D) * (E + F)", # from manual analysis digits=(2, 3, 5, 6, 7, 9), distinct=("ABCDEF","GHIJKL"), verbose=0, ) # print answers for (_, ans) in p.solve(): print(f"the original sum was {ans[2]}")LikeLike
Jim Randell 1:11 pm on 30 October 2021 Permalink |
Or you could do:
from enigma import (partitions, multiply, filter_unique, printf) # possible face pairs pairs = partitions((2, 3, 5, 6, 7, 9), 2, distinct=1) # calculate vertex sum fn = lambda fs: multiply(x + y for (x, y) in fs) # find vertex sums with multiple face pairs for fs in filter_unique(pairs, fn).non_unique: printf("{t} -> {fs}", t=fn(fs))LikeLike
GeoffR 9:31 am on 5 November 2021 Permalink |
from enigma import all_different from itertools import combinations, permutations from collections import defaultdict D3084 = defaultdict(list) # If the cube sides are (L1, L2, R1, R2, U, D), the sum # of eight vertices = (L1 + L2) * (R1 + R2) * (U + D) # Since 2 ^ 10 = 1024 and 3 ^ 10 = 59049 and the maximum # vertices sum for face digits (1..9) = 2805 (17*15*11) # ... the 10th power must be 2 ^ 10 = 1024 # find cube face values for 10th power = 1024 for p1 in permutations(range(1, 10), 6): L1, L2, R1, R2, U, D = p1 if (L1 + L2) * (R1 + R2) * (U + D) == 1024: set_faces = set((L1, L2, R1, R2, U, D)) face_pairs = list(combinations(set_faces, 2)) # map six cube face values to sum of eight vertices for A, B, C in combinations(face_pairs, 3): if all_different(A[0], A[1], B[0], B[1], C[0], C[1]): # digits must be same as 10th power of 1024 if {A[0], A[1], B[0], B[1], C[0], C[1]} == set_faces: VSum = (A[0] + A[1]) * (B[0] + B[1]) * (C[0] + C[1]) D3084[VSum].append(((A[0], A[1]), (B[0], B[1]), (C[0], C[1]))) for k, v in D3084.items(): # original number must have non-unique face values if len(v) > 1: print(f"The original number was {k}") print(f"Face Values are {v}")LikeLike
GeoffR 9:53 am on 8 November 2021 Permalink |
This programme ran in 300ms with the Chuffed Solver, but took much longer with the Geocode Solver. I had a constraint to limit the set length of 15 values to 14 (i.e. 1 No. duplicated), but it proved not necessary in practice, and increased the run time substantially to 9.5s. This constraint is commented out in the programme.
The tenth power of two is shown in the output after the code, as is the original sum of 1188 (i.e. the answer), for two instances.
% A Solution in MiniZinc include "globals.mzn"; % min vertex sum = (1+2) * (3+4) * (5+6) = 231 % max vertex sum = (9+8) * (7+6) * (5+4) = 1989 % ..as 2^10 = 1024 and 3^10 = 59049, therefore 1024 is the only % possible 10th power within min and max sum limits % the cube has six faces with different digits 1..9 var 1..9: a; var 1..9: b; var 1..9: c; var 1..9: d; var 1..9: e; var 1..9: f; constraint all_different([a, b, c, d, e, f]); % N1..N15 are all the cube possible vertex totals var 231..1989: N1; var 231..1989:N2; var 231..1989:N3; var 231..1989: N4; var 231..1989:N5; var 231..1989:N6; var 231..1989: N7; var 231..1989:N8; var 231..1989:N9; var 231..1989: N10; var 231..1989:N11; var 231..1989:N12; var 231..1989: N13; var 231..1989:N14; var 231..1989:N15; % the fifteen possible vertex totals, % from six of the possible nine digits 1..9 constraint N1 == (a+b) * (c+d) * (e+f); constraint N2 == (a+b) * (c+e) * (d+f); constraint N3 == (a+b) * (c+f) * (d+e); constraint N4 == (a+c) * (b+d) * (e+f); constraint N5 == (a+c) * (b+e) * (d+f); constraint N6 == (a+c) * (b+f) * (d+e); constraint N7 == (a+d) * (b+c) * (e+f); constraint N8 == (a+d) * (b+e) * (c+f); constraint N9 == (a+d) * (b+f) * (c+e); constraint N10 == (a+e) * (b+c) * (d+f); constraint N11 == (a+e) * (b+d) * (c+f); constraint N12 == (a+e) * (b+f) * (c+d); constraint N13 == (a+f) * (b+c) * (d+e); constraint N14 == (a+f) * (b+d) * (c+e); constraint N15 == (a+f) * (b+e) * (c+d); % One of the vertex totals is a 10th power i.e.2 ^ 10 = 1024 % ...this constraint fixes the six digits for the solution constraint sum([ N1 == 1024, N2 == 1024, N3 == 1024, N4 == 1024, N5 == 1024, N6 == 1024, N7 == 1024, N8 == 1024, N9 == 1024, N10 == 1024, N11 == 1024, N12 == 1024, N13 == 1024, N14 == 1024, N15 == 1024 ]) == 1; % constraint card ({N1, N2, N3, N4, N5, N6, N7, N8, % N9, N10, N11, N12, N13, N14, N15}) == 14; solve satisfy; output["N1 = " ++ show(N1) ++ ", sides: " ++ show([a,b]) ++ ", " ++ show([c,d]) ++ ", " ++ show([e,f]) ++ "\nN2 = " ++ show(N2) ++ ", sides: " ++ show([a,b]) ++ ", " ++ show([c,e]) ++ ", " ++ show([d,f]) ++ "\nN3 = " ++ show(N3) ++ ", sides: " ++ show([a,b]) ++ ", " ++ show([c,f]) ++ ", " ++ show([d,e]) ++ "\nN4 = " ++ show(N4) ++ ", sides: " ++ show([a,c]) ++ ", " ++ show([b,d]) ++ ", " ++ show([e,f]) ++ "\nN5 = " ++ show(N5) ++ ", sides: " ++ show([a,c]) ++ ", " ++ show([b,e]) ++ ", " ++ show([d,f]) ++ "\nN6 = " ++ show(N6) ++ ", sides: " ++ show([a,c]) ++ ", " ++ show([b,f]) ++ ", " ++ show([d,e]) ++ "\nN7 = " ++ show(N7) ++ ", sides: " ++ show([a,d]) ++ ", " ++ show([b,c]) ++ ", " ++ show([e,f]) ++ "\nN8 = " ++ show(N8) ++ ", sides: " ++ show([a,d]) ++ ", " ++ show([b,e]) ++ ", " ++ show([c,f]) ++ "\nN9 = " ++ show(N9) ++ ", sides: " ++ show([a,d]) ++ ", " ++ show([b,f]) ++ ", " ++ show([c,e]) ++ "\nN10 = " ++ show(N10) ++ ", sides: " ++ show([a,e]) ++ ", " ++ show([b,c]) ++ ", " ++ show([d,f]) ++ "\nN11 = " ++ show(N11) ++ ", sides: " ++ show([a,e]) ++ ", " ++ show([b,d]) ++ ", " ++ show([c,f]) ++ "\nN12 = " ++ show(N12) ++ ", sides: " ++ show([a,e]) ++ ", " ++ show([b,f]) ++ ", " ++ show([c,d]) ++ "\nN13 = " ++ show(N13) ++ ", sides: " ++ show([a,f]) ++ ", " ++ show([b,c]) ++ ", " ++ show([d,e]) ++ "\nN14 = " ++ show(N14) ++ ", sides: " ++ show([a,f]) ++ ", " ++ show([b,d]) ++ ", " ++ show([c,e]) ++ "\nN15 = " ++ show(N15) ++ ", sides: " ++ show([a,f]) ++ ", " ++ show([b,e]) ++ ", " ++ show([c,d]) ]; % 15 Possible vertex totals for 6 sides of a cube % N1 = 1210, sides: [3, 7], [9, 2], [5, 6] % N2 = 1120, sides: [3, 7], [9, 5], [2, 6] % N3 = 1050, sides: [3, 7], [9, 6], [2, 5] % N4 = 1188, sides: [3, 9], [7, 2], [5, 6] <<< original sum = 1188 (answer) % N5 = 1152, sides: [3, 9], [7, 5], [2, 6] % N6 = 1092, sides: [3, 9], [7, 6], [2, 5] % N7 = 880, sides: [3, 2], [7, 9], [5, 6] % N8 = 900, sides: [3, 2], [7, 5], [9, 6] % N9 = 910, sides: [3, 2], [7, 6], [9, 5] % N10 = 1024, sides: [3, 5], [7, 9], [2, 6] <<< 10th power = 1024 (2^10) % N11 = 1080, sides: [3, 5], [7, 2], [9, 6] % N12 = 1144, sides: [3, 5], [7, 6], [9, 2] % N13 = 1008, sides: [3, 6], [7, 9], [2, 5] % N14 = 1134, sides: [3, 6], [7, 2], [9, 5] % N15 = 1188, sides: [3, 6], [7, 5], [9, 2] <<< original sum = 1188 (answer) % ----------LikeLike
Frits 12:47 pm on 9 November 2021 Permalink |
The all_different clause has been replaced by a < b < c < d < e < f.
Also the card statement has been changed to less equal to 14.
Now the Geocode solver takes less than half a second (printing all solutions) .
Chuffed still takes 11 seconds.
% A Solution in MiniZinc include "globals.mzn"; % min vertex sum = (1+2) * (3+4) * (5+6) = 231 % max vertex sum = (9+8) * (7+6) * (5+4) = 1989 % ..as 2^10 = 1024 and 3^10 = 59049, therefore 1024 is the only % possible 10th power within min and max sum limits % the cube has six faces with different digits 1..9 var 1..4: a; var 2..5: b; var 3..6: c; var 4..7: d; var 5..8: e; var 6..9: f; constraint a < b /\ b < c /\ c < d /\ d < e /\ e < f; % N1..N15 are all the cube possible vertex totals var 231..1989: N1; var 231..1989:N2; var 231..1989:N3; var 231..1989: N4; var 231..1989:N5; var 231..1989:N6; var 231..1989: N7; var 231..1989:N8; var 231..1989:N9; var 231..1989: N10; var 231..1989:N11; var 231..1989:N12; var 231..1989: N13; var 231..1989:N14; var 231..1989:N15; % the fifteen possible vertex totals, % from six of the possible nine digits 1..9 constraint N1 == (a+b) * (c+d) * (e+f); constraint N2 == (a+b) * (c+e) * (d+f); constraint N3 == (a+b) * (c+f) * (d+e); constraint N4 == (a+c) * (b+d) * (e+f); constraint N5 == (a+c) * (b+e) * (d+f); constraint N6 == (a+c) * (b+f) * (d+e); constraint N7 == (a+d) * (b+c) * (e+f); constraint N8 == (a+d) * (b+e) * (c+f); constraint N9 == (a+d) * (b+f) * (c+e); constraint N10 == (a+e) * (b+c) * (d+f); constraint N11 == (a+e) * (b+d) * (c+f); constraint N12 == (a+e) * (b+f) * (c+d); constraint N13 == (a+f) * (b+c) * (d+e); constraint N14 == (a+f) * (b+d) * (c+e); constraint N15 == (a+f) * (b+e) * (c+d); % One of the vertex totals is a 10th power i.e.2 ^ 10 = 1024 % ...this constraint fixes the six digits for the solution constraint sum([ N1 == 1024, N2 == 1024, N3 == 1024, N4 == 1024, N5 == 1024, N6 == 1024, N7 == 1024, N8 == 1024, N9 == 1024, N10 == 1024, N11 == 1024, N12 == 1024, N13 == 1024, N14 == 1024, N15 == 1024 ]) == 1; constraint card ({N1, N2, N3, N4, N5, N6, N7, N8, N9, N10, N11, N12, N13, N14, N15}) <= 14; solve satisfy; output["N1 = " ++ show(N1) ++ ", sides: " ++ show([a,b]) ++ ", " ++ show([c,d]) ++ ", " ++ show([e,f]) ++ "\nN2 = " ++ show(N2) ++ ", sides: " ++ show([a,b]) ++ ", " ++ show([c,e]) ++ ", " ++ show([d,f]) ++ "\nN3 = " ++ show(N3) ++ ", sides: " ++ show([a,b]) ++ ", " ++ show([c,f]) ++ ", " ++ show([d,e]) ++ "\nN4 = " ++ show(N4) ++ ", sides: " ++ show([a,c]) ++ ", " ++ show([b,d]) ++ ", " ++ show([e,f]) ++ "\nN5 = " ++ show(N5) ++ ", sides: " ++ show([a,c]) ++ ", " ++ show([b,e]) ++ ", " ++ show([d,f]) ++ "\nN6 = " ++ show(N6) ++ ", sides: " ++ show([a,c]) ++ ", " ++ show([b,f]) ++ ", " ++ show([d,e]) ++ "\nN7 = " ++ show(N7) ++ ", sides: " ++ show([a,d]) ++ ", " ++ show([b,c]) ++ ", " ++ show([e,f]) ++ "\nN8 = " ++ show(N8) ++ ", sides: " ++ show([a,d]) ++ ", " ++ show([b,e]) ++ ", " ++ show([c,f]) ++ "\nN9 = " ++ show(N9) ++ ", sides: " ++ show([a,d]) ++ ", " ++ show([b,f]) ++ ", " ++ show([c,e]) ++ "\nN10 = " ++ show(N10) ++ ", sides: " ++ show([a,e]) ++ ", " ++ show([b,c]) ++ ", " ++ show([d,f]) ++ "\nN11 = " ++ show(N11) ++ ", sides: " ++ show([a,e]) ++ ", " ++ show([b,d]) ++ ", " ++ show([c,f]) ++ "\nN12 = " ++ show(N12) ++ ", sides: " ++ show([a,e]) ++ ", " ++ show([b,f]) ++ ", " ++ show([c,d]) ++ "\nN13 = " ++ show(N13) ++ ", sides: " ++ show([a,f]) ++ ", " ++ show([b,c]) ++ ", " ++ show([d,e]) ++ "\nN14 = " ++ show(N14) ++ ", sides: " ++ show([a,f]) ++ ", " ++ show([b,d]) ++ ", " ++ show([c,e]) ++ "\nN15 = " ++ show(N15) ++ ", sides: " ++ show([a,f]) ++ ", " ++ show([b,e]) ++ ", " ++ show([c,d]) ];LikeLike
Frits 12:50 pm on 9 November 2021 Permalink |
@Jim/GeoffR,
Please let me know how you post minizinc programs.
LikeLike
Jim Randell 2:08 pm on 9 November 2021 Permalink |
@Frits: Details on using
[code] ... [/code]tags are here [link].For languages that don’t have a supported syntax highlighter you can use [[
language="text"]].LikeLike