Enigmatic Code

Programming Enigma Puzzles

Enigma 1106: Not a square unused

From New Scientist #2262, 28th October 2000

Harry, Tom and I each found a set consisting of a 4-digit perfect square, a 3-digit perfect square and a 2-digit perfect square that between them used nine different digits; but none of us could add a 1-digit square with the unused digit because 9, 4, 1 and 0 all appeared in each of our three sets. No two of us found exactly the same set; none of the squares in my set appeared in either Harry’s set or Tom’s set. There is one further set that none of us found whose unused digit is again not itself a perfect square.

List in ascending order the three squares in this set that none of us found.



2 responses to “Enigma 1106: Not a square unused

  1. Jim Randell 17 July 2017 at 8:23 am

    This Python program uses the SubstitutedExpression() solver from the enigma.py library to solve the embedded problem and find possible sets that satisfy the conditions Tom, Dick and Harry are looking for, and then examines which arrangement of possible solutions satisfies the remaining conditions. It runs in 86ms.

    from itertools import combinations
    from enigma import irange, is_duplicate, SubstitutedExpression, diff, printf
    # 2, 3, 4 digit squares without duplicated digits
    squares = list(s for s in (i * i for i in irange(4, 99)) if not is_duplicate(s))
    # find 10 digit solutions to the problem
    p = SubstitutedExpression(
      [ "A not in (0, 1, 4, 9)", "BC in squares", "DEF in squares", "GHIJ in squares" ],
      env={ 'squares': squares }, # incorporate the list of squares into the environment
      answer="(BC, DEF, GHIJ)", # answer (the three squares chosen)
      template="[unused = A, squares = (BC, DEF, GHIJ)]", # solution template
      verbose=4, # output the solutions
      solution="", # but don't output the substitution map
    # record the potential answer sets
    ss = list(ans for (s, ans) in p.solve())
    # check answers have no squares in common
    def check(s, t):
      return all(a != b for (a, b) in zip(s, t))
    # choose a set for me
    for D in ss:
      # choose sets for Harry and Tom
      for (H, T) in combinations(diff(ss, [D]), 2):
        # none of the squares in my set appear in H or T
        if not(check(D, H) and check(D, T)): continue
        # and find unused sets
        for U in diff(ss, [D, T, H]):
          printf("U={U} [D={D} H/T={H}/{T}]")

    Solution: The three squares in the set none of them found were: 36, 841, 9025.

    There are only 4 possible sets of squares:

    [1] (16, 784, 9025) with 3 left over;
    [2] (36, 841, 9025) with 7 left over;
    [3] (36, 289, 5041) with 7 left over;
    [4] (36, 729, 5041) with 8 left over.

    Dick found set [1]. Tom and Harry found sets [3] and [4]. Leaving [2] as the set none of them found.

  2. Brian Gladman 17 July 2017 at 5:21 pm
    from collections import defaultdict
    # map the number of digits (2, 3 or 4) to a dictionary of
    # squares with this number of different digits (the keys)
    # and then to the set of digits in the square (as values)
    sqrs = defaultdict(dict)
    for n in range(4, 100):
      sq = n * n
      ss = str(sq)
      dgts = set(ss)
      n_dgts = len(dgts)
      if n_dgts == len(ss):
        sqrs[n_dgts][sq] = dgts
    # create sets of triples of 2, 3 and 4 digit squares with nine
    # different digits amoung them and the tenth digit not square
    tr_set = set()
    # two digit squares and their digit sets
    for sq2, st2 in sqrs[2].items():
      # three digit squares and their digit sets
      for sq3, st3 in sqrs[3].items():
        # their digits must be different
        if not (st2 & st3):
          s23 = st2 | st3
          # four digit squares and their digit sets
          for sq4, st4 in sqrs[4].items():
            # all nine digits must be different
            if not s23 & st4:
              # all digits that are square must be present
              if set('0149') < s23 | st4:
                tr_set.add(frozenset((sq2, sq3, sq4)))
    # we are told that there are four such triples
    assert len(tr_set) == 4
    # pick a triple for me
    for my_tr in tr_set:
      # those triples remaining
      rest = tr_set.difference([my_tr])
      # look for two triples that don't share any values with mine
      th_trp = {s for s in rest if not (s & my_tr)}
      if len(th_trp) == 2:
        f = lambda x: tuple(sorted(x))
        tt, ht = (f(x) for x in th_trp)
        # find the fourth triple
        ex, = rest.difference(th_trp)
        print(f'Mine: {f(my_tr)}, [Tom, Harry]: [{tt}, {ht}], Other: {f(ex)}')

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: