Teaser 2828: Return to Zenda
From The Sunday Times, 4th December 2016 [link] [link]
Each postcode in Ruritania consists of ten digits, the first three showing the area, the next three showing the town, and the final four showing the house. Just twelve houses have “lucky” postcodes that have no repeated digit and which consist of two three-figure squares followed by a four-figure square. Rudolph lives in such a house and he recently met a girl called Flavia. She only told him that hers was the only house in her area with a lucky postcode, and that her area/town/house numbers were all bigger than his. He has found a postcode satisfying these conditions and sent her a Christmas card, but it has been returned as it was not her postcode.
What is Rudolph’s postcode?
[teaser2828]







Jim Randell 9:16 am on 5 October 2021 Permalink |
I think the wording of this puzzle could be a bit clearer. But with a reasonable assumption that leading zeros are not allowed in the squares we get a unique solution.
This Python program runs in 54ms.
Run: [ @replit ]
from enigma import (irange, is_duplicate, cproduct, subsets, filter_unique, unpack, join, printf) # find n-digit squares from a^2 to b^2 with no repeated digits (as # strings) leading zeros are allowed def squares(n, a, b): r = list() for i in irange(a, b): s = str(i * i).zfill(n) if not is_duplicate(s): r.append(s) return r # 3 and 4 digit squares s3s = squares(3, 10, 31) s4s = squares(4, 32, 99) # collect "lucky" (pandigital) postcodes lucky = list((area, town, house) for (area, town, house) in cproduct([s3s, s3s, s4s]) if len(set(area + town + house)) == 10) printf("[{n} lucky postcodes]", n=len(lucky)) # choose the 12 lucky postcodes for postcodes in subsets(lucky, size=12): # flavia's postcode is the only (possible) lucky postcode in the area fs = filter_unique(postcodes, unpack(lambda a, t, h: a)).unique # consider rudolph's postcode for (ra, rt, rh) in postcodes: # find possible postcodes for flavia f = list((a, t, h) for (a, t, h) in fs if a > ra and t > rt and h > rh) # and there must be more than one possibility if len(f) > 1: fmt = lambda s: join(s, sep=":", enc="()") printf("{r} -> {f}",r=fmt((ra, rt, rh)), f=join(map(fmt, f), sep=' '))Solution: Rudolph’s postcode is (324:576:1089).
And there are two possible postcodes for Flavia: (961:784:3025) and (361:784:9025). So Rudolph chose the wrong one.
We can allow the squares to have leading zeros by changing the value of the [[
a]] parameter in the calls to [[squares()]]. But if we do this we find that are many possible solutions.The 12 “lucky” postcodes (area:town:house) are:
Flavia’s postcode is unique by area:
And Rudolph’s postcode must be one of the 12 lucky code above, that has two candidate postcodes for Flavia that are larger in each component. There is only one possibility for Rudolph’s postcode:
Without leading zeros there are exactly 12 lucky postcodes, so we know all possible lucky postcodes are in use. (And this is probably the scenario the setter had in mind).
However, if we allow leading zeros in the squares we can find 34 lucky postcodes, but we know only 12 of them are in use. But how does Flavia know that hers is the only lucky postcode in her area? Is it the only possible lucky postcode in her area? Or just the only one currently in use? In my program I have implemented the latter.
And using this interpretation we find there are many thousands of situations that lead to a solution, with 6 possible values for Rudolph’s postcode. So I think it is probably safe to assume that the setter intended there to be no leading zeros in the squares.
LikeLike
Frits 11:49 am on 9 June 2025 Permalink |
or more efficient
# collect "lucky" (pandigital) postcodes lucky = list() for (area, house) in product(s3s, s4s): if len(ah := set(area + house)) == 7: for town in s3s: if ah.isdisjoint(town): lucky.append((area, town, house))LikeLike
Frits 11:53 am on 5 October 2021 Permalink |
Checking lucky postcodes outside “SubstitutedExpression” would have been easier and faster but this was more challenging.
from enigma import SubstitutedExpression # the alphametic puzzle p = SubstitutedExpression( [# Rudolph "is_square(ABC)", "is_square(DEF)", "is_square(GHIJ)", # does more than one Flavia candidate exist for Rudolph (ABC:DEF:GHIJ) ? # Flavia's postcode (a:b:c) is unique by area "sum(1 for a in range(ABC + 1, 1000) if is_square(a) and \ sum(1 for t in range(100, 1000) if is_square(t) \ if len(set(str(a) + str(t))) == 6 \ for u in range(1000, 10000) if is_square(u) and \ len(set(str(a) + str(t) + str(u))) == 10 \ ) == 1 \ for b in range(DEF + 1, 1000) if is_square(b) \ if len(set(str(a) + str(b))) == 6 \ for c in range(GHIJ + 1, 10000) if is_square(c) and \ len(set(str(a) + str(b) + str(c))) == 10 \ ) > 1", ], answer="(str(ABC) + ':' + str(DEF) + ':' + str(GHIJ))", distinct=("ABCDEFGHIJ"), verbose=0, ) # print answer for (_, ans) in p.solve(): print(f"Rudolph's postcode is {ans}")LikeLike
Frits 1:33 pm on 5 October 2021 Permalink |
Similar.
import sqlite3 sqs = [str(i**2) for i in range(10, 100)] luckies = [] for a in [x for x in sqs if len(x) == 3]: for b in [x for x in sqs if len(x) == 3]: ab = a + b if len(set(ab)) != 6: continue for c in [x for x in sqs if len(x) == 4]: if len(set(ab + c)) != 10: continue luckies.append([a, b, c]) # connect to SQLite database that resides in the memory sqlite_Connection = sqlite3.connect('temp.db') c1 = sqlite3.connect(':memory:') c1.execute("CREATE TABLE postcodes ( \ area INT, \ town INT, \ house INT \ )") # insert entries into table for (x, y, z) in luckies: stmnt = "INSERT INTO postcodes VALUES (" stmnt += x + ", " + y + ", " + z + ")" c1.execute(stmnt) c1.commit() stmnt = "SELECT area, town, house, " stmnt += "(SELECT COUNT(1) FROM postcodes B" stmnt += " WHERE B.AREA > A.AREA and" stmnt += " B.TOWN > A.TOWN and" stmnt += " B.HOUSE > A.HOUSE and" stmnt += " NOT EXISTS (SELECT 1" # Flavia's postcode is unique by area stmnt += " FROM postcodes C" stmnt += " WHERE C.AREA = B.AREA and" stmnt += " (C.TOWN != B.TOWN or" stmnt += " C.HOUSE != B.HOUSE)" stmnt += " )" stmnt += " ) as nFlavia " stmnt += "FROM postcodes A " stmnt += "WHERE nFlavia > 1;" for ans in c1.execute(stmnt): print(f"Rudolph's postcode is {ans[:-1]}") c1.close() if (sqlite_Connection): sqlite_Connection.close()LikeLike
GeoffR 3:41 pm on 5 October 2021 Permalink |
A good variety of solutions for this teaser.
% A Solution in MiniZinc include "globals.mzn"; set of int: sq3 = {x*x | x in 10..31}; set of int: sq4 = {x*x | x in 32..99}; % Rudolph's digits in 3:3:4 digits for area:town:house numbers var 1..9:A; var 0..9:B; var 0..9:C; var 1..9:D; var 0..9:E; var 0..9:F; var 1..9:G; var 0..9:H; var 0..9:I; var 0..9:J; constraint all_different ([A,B,C,D,E,F,G,H,I,J]); % Flavia's digits - 1st set are abc:def:ghij var 1..9:a; var 0..9:b; var 0..9:c; var 1..9:d; var 0..9:e; var 0..9:f; var 1..9:g; var 0..9:h; var 0..9:i; var 0..9:j; constraint all_different([a,b,c,d,e,f,g,h,i,j]); % Flavia's digits - 2nd set are mno:pqr:stuv var 1..9:m; var 0..9:n; var 0..9:o; var 1..9:p; var 0..9:q; var 0..9:r; var 1..9:s; var 0..9:t; var 0..9:u; var 0..9:v; constraint all_different ([m,n,o,p,q,r,s,t,u,v]); % Rudolph's numbers var 100..999: ABC = 100*A + 10*B + C; var 100..999: DEF = 100*D + 10*E + F; var 1000..9999: GHIJ = 1000*G + 100*H + 10*I + J; % Flavia's numbers - 1st set var 100..999: abc = 100*a + 10*b + c; var 100..999: def = 100*d + 10*e + f; var 1000..9999: ghij = 1000*g + 100*h + 10*i + j; % Flavia's numbers - 2nd set var 100..999: mno = 100*m + 10*n + o; var 100..999: pqr = 100*p + 10*q + r; var 1000..9999: stuv = 1000*s + 100*t + 10*u + v; % Square constraints for Rudolph's and Flavia's numbers constraint ABC in sq3 /\ DEF in sq3 /\ GHIJ in sq4; constraint abc in sq3 /\ def in sq3 /\ ghij in sq4; constraint mno in sq3 /\ pqr in sq3 /\ stuv in sq4; % Flavia's area/town/house numbers were all bigger than Rodolph's numbers % We conclude she has two numbers as Rudolph made a mistake on the first. constraint abc > ABC /\ def > DEF /\ ghij > GHIJ; constraint mno > ABC /\ pqr > DEF /\ stuv > GHIJ; % Flavia's two sets of numbers are different areas/ house numbers % ... but make Flavia's town the same in both sets of numbers constraint mno > abc /\ pqr == def /\ ghij > stuv; solve satisfy; output ["Rudolph's numbers : " ++ show([ABC, DEF, GHIJ]) ++ "\n" ++ "Flavia's numbers (1st set): " ++ show([abc, def,ghij]) ++ "\n" ++ "Flavia's numbers (2nd set): " ++ show([mno, pqr, stuv]) ]; % Rudolph's numbers : [324, 576, 1089] % Flavia's numbers (1st set): [361, 784, 9025] % Flavia's numbers (2nd set): [961, 784, 3025] % ---------- % ==========LikeLike