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")):
        # answer = H.z
        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"
      --answer="QR * STU"
      
  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. Pingback: New Scientist Enigma 871 – Pandigitals | PuzzlingInPython

  4. 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.
    
    
    
    
  5. 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
    
    
    

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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

%d bloggers like this: