Enigmatic Code

Programming Enigma Puzzles

Enigma 871: Pandigitals

From New Scientist #2026, 20th April 1996 [link]

Uncle Joe had written the digits 0 to 9 on ten cards, some red, the others blue, one digit per card. “Now boys,” he said, “you have to arrange the red cards on the table to form a number, and divide the blue cards into two groups to form two separate numbers. The ‘red’ number must be the product of the ‘blue’ numbers.”

“Like this?” said Tom. “Or this?” said Dick. “Or this?” said Harry.

“All different, and all correct!” replied Uncle Joe.

Only Tom had included a single-digit number in his arrangement. What was Harry’s ‘red’ number?

[enigma871]

7 responses to “Enigma 871: Pandigitals”

1. Jim Randell 18 October 2021 at 9:30 am

We can treat a pandigital multiplication problem as an alphametic expression.

There are only two possibilities:

A × BCDE = FGHIJ
AB × CDE = FGHIJ

The blue cards are AE, and the red cards are FJ.

Tom’s multiplication is of the first type. And Dick and Harry’s are of the second type.

This Python program collects solutions to the two alphametic expressions, and collects together solutions with the same colourings of cards. It then finds sets of cards that can give a type 1 expression and two type 2 expressions. It runs in 83ms.

Run: [ @replit ]

```from enigma import SubstitutedExpression, group, unpack, nsplit, ordered, intersect, product, subsets, sprintf as f, printf

# format a multiplication sum
fmt = lambda x, y, z: f("{x} * {y} = {z}")

# extract the result (z)
getz = unpack(lambda x, y, z: z)

# solve the pandigital expression: x * y = z
def solve(x, y, z):
# make the alphametic expression
p = SubstitutedExpression(fmt(x, y, z), answer=f("({x}, {y}, {z})"), verbose=0)
# group answers by the symbols on the red cards
return group((ans for (_, ans) in p.solve()), by=(lambda t: ordered(*nsplit(getz(t)))))

# there are 2 possibilities
ss1 = solve("A", "BCDE", "FGHIJ")
ss2 = solve("AB", "CDE", "FGHIJ")

# find sets of cards common to both
for k in intersect([ss1.keys(), ss2.keys()]):
# we need 1 solution from the first type, and 2 from the second type
for (T, (D, H)) in product(ss1[k], subsets(ss2[k], size=2, select="P")):
printf("answer = {Hz} [T: {T}; D: {D}; H: {H}]", Hz=getz(H), T=fmt(*T), D=fmt(*D), H=fmt(*H))
```

Solution: Harry’s red number was: 17820.

Tom’s sum is: 3 × 5694 = 17082.

Dick and Harry’s sums are: 36 × 495 = 17820; 45 × 396 = 17820.

But we don’t know which way round. Fortunately the result in each is the same, and this gives the answer.

• Jim Randell 20 October 2021 at 9:44 am

Or, solving the problem entirely using a [[ `SubstitutedExpression` ]] run file (which runs in 81ms):

```#!/usr/bin/env python3 -m enigma -r

SubstitutedExpression

# Tom's sum is: A * BCDE = FGHIJ
"A * BCDE = FGHIJ"

# Dick's is: KL * MNP = some rearrangement of FGHIJ
"set(nsplit(KL * MNP)) == {F, G, H, I, J}"

# Harry's is: QR * STU = some rearrangement of FGHIJ
"set(nsplit(QR * STU)) == {F, G, H, I, J}"

# and Dick's and Harry's sums are different
"(QR, STU) != (KL, MNP)"

--distinct="ABCDEFGHIJ,KLMNPFGHIJ,QRSTUFGHIJ"
```
2. Frits 18 October 2021 at 11:57 am
```
from enigma import SubstitutedExpression
from collections import defaultdict

# only two possibilities:  A × BCDE = FGHIJ   or   AB × CDE = FGHIJ

# the alphametic puzzle
p = SubstitutedExpression(
[ "A * BCDE = FGHIJ",
"sorted(x for x in str(KL * MNO)) == sorted(x for x in str(FGHIJ))",
],
answer="(FGHIJ, A, BCDE), (KL * MNO, KL, MNO)",
distinct=("ABCDEFGHIJ", "KLMNOFGHIJ"),
verbose=0,
)

d = defaultdict(list)
# group answers on first column
for (_, ans) in p.solve():
d[ans[0]].append(ans[1])

for k, vs in d.items():
if len(vs) >= 2:
# as there is nothing to distinguish between Dick and Harry
# they must have the same red number
print(f"Harry's red number was {vs[0][0]}")
```
3. GeoffR 19 October 2021 at 6:19 pm
```% A Solution in MiniZinc
include "globals.mzn";

% Two sums are  A * BCDE == FGHIJ and KL * MNP == QRSTU
var 0..9:A; var 0..9:B; var 0..9:C; var 0..9:D;
var 0..9:E; var 0..9:F; var 0..9:G; var 0..9:H;
var 0..9:I; var 0..9:J;

var 1..9:K; var 0..9:L; var 1..9:M; var 0..9:N;
var 0..9:P; var 1..9:Q; var 0..9:R; var 0..9:S;
var 0..9:T; var 0..9:U;

constraint A > 0 /\ B > 0 /\ C > 0 /\ F > 0;

constraint all_different ([A, B, C, D, E, F, G, H, I, J]);
constraint all_different ([K, L, M, N, P, F, G, H, I, J]);

% 1st sum
var 10..99: AB = 10*A + B;
var 100..999:CDE = 100*C + 10*D + E;
var 1000..9999:BCDE = 1000*B + 100*C + 10*D + E;
var 10000..99999: FGHIJ = 10000*F + 1000*G + 100*H + 10*I + J;

% 2nd sum
var 10..99:KL = 10*K + L;
var 100..999:MNP = 100*M + 10*N + P;
var 10000..99999: QRSTU = 10000*Q + 1000*R + 100*S + 10*T + U;

% Two possible alphametic sums
constraint A * BCDE == FGHIJ /\ KL * MNP == QRSTU;

% Two sums have the same digits in the answers
var set of int:s1 = {F, G, H, I, J};
var set of int:s2 = {Q, R, S, T, U};
constraint s1 == s2;

solve satisfy;

output [show(A) ++ " * " ++ show(BCDE) ++ " = " ++ show(FGHIJ)
++ "\n" ++ show(KL) ++ " * " ++ show(MNP) ++ " = " ++ show(QRSTU) ];

% 3 * 5694 = 17082
% 45 * 396 = 17820
% ----------
% 3 * 5694 = 17082
% 36 * 495 = 17820
% ----------
% ==========
% Tom had a single digit in his sum, so Harry’s ‘red’ number was 17820.

```
4. GeoffR 20 October 2021 at 8:37 am

I tried a two-stage approach for an extra solution to this teaser.

As Tom has a single digit in his solution – for the format A × BCDE = FGHIJ, there must be two solutions for Dick and Harry – for the format AB × CDE = FGHIJ.

The first stage of this solution was to find all the solutions for Harry and Dicks’ equation. There were eight potental solutions, from which the two similar solutions for Harry and Dick are easily identified.

The second stage identified Tom’s numbers to complete the solution for Tom, Dick and Harry.

```from itertools import permutations
from enigma import nsplit

# search for Harry and Dick's numbers
# ... which are of the format AB * CDE = FGHIJ
for p1 in permutations('1234567890', 5):
A, B, C, D, E = p1
if '0' in (A, C): continue
AB, CDE = int(A + B), int(C + D + E)
R1 = AB * CDE
if R1 < 10000:continue
if R1 > 99999:continue
F, G, H, I, J = nsplit(R1)
set1 = {int(A), int(B), int(C), int(D), int(E), F, G, H, I, J}
if len(set1) < 10:continue

# Identify two numbers for Dick and Harry
print(R1, AB, CDE)
# Values of equation (AB * CDE) = AB * CDE
# 16038 27 594
# 17820 36 495 <<< Dick or Harry's numbers
# 15678 39 402
# 17820 45 396 <<< Dick or Harry's numbers
# 19084 52 367
# 16038 54 297
# 58401 63 927
# 26910 78 345

# Harry or Dicks numbers: 17820 = 36 * 495
# or 17820 = 45 * 396

# Find Tom's numbers - format A * BCDE = FGHIJ
for X in range(1, 10):
for Y in range(1000, 9000):
R2 = X * Y
if R2 < 10000:continue
if R2 > 99999:continue
# find digits in Y and R2
a, b, c, d = nsplit(Y)
e, f, g, h, i = nsplit(R2)
set2 = {X, a, b, c, d, e, f, g, h, i}
if len(set2) == 10:
if {e, f, g, h, i} == {1, 7, 8, 2, 0}:
print(f"Tom's Numbers: {X} * {Y} = {R2}")
# Tom's Numbers: 3 * 5694 = 17082

# Harry’s ‘red’ number was 17820

```

This site uses Akismet to reduce spam. Learn how your comment data is processed.