Enigmatic Code

Programming Enigma Puzzles

Enigma 1695: Odd one out

From New Scientist #2862, 28th April 2012 [link]

In the following list of five numbers I have consistently replaced digits by letters, with different letters being used for different digits: CAST THE ODD ONE OUT.

All but one of those five numbers is a perfect square. What is the numerical value of the odd one out?

Enigma 1613 is also called “Odd one out”.

[enigma1695]

Advertisements

7 responses to “Enigma 1695: Odd one out

  1. Jim Randell 25 April 2012 at 6:40 pm

    Here’s my first attempt in Python. It’s not very quick though, it takes 4.5s under PyPy. I shall see if I can come up with a more efficient version later.

    from itertools import permutations
    from enigma import concat, printf
    
    # squares
    squares3 = set(pow(n, 2) for n in range(10, 32))
    squares4 = set(pow(n, 2) for n in range(32, 100))
    
    # three digit numbers
    words3 = ('THE', 'ODD', 'ONE', 'OUT')
    
    # what are the letters in them?
    letters3 = list(set().union(*words3))
    
    # and the remaining letters in the 4-digit number
    letters4 = set('CAST').difference(letters3)
    
    # find an assignment that makes at least 3 of the three digit numbers squares
    digits = set('0123456789')
    for p3 in permutations(digits, len(letters3)):
      d = dict(zip(letters3, p3))
      if d['O'] == 0 or d['T'] == 0: continue
    
      numbers = list(int(concat(*(d[l] for l in w))) for w in words3)
      s3 = squares3.intersection(numbers)
      n = len(s3)
      if n < 3: continue
    
      # and assign the remaining letters 
      for p4 in permutations(digits.difference(p3), len(letters4)):
        d.update(zip(letters4, p4))
        if d['C'] == 0: continue
    
        CAST = int(concat(*(d[l] for l in 'CAST')))
        # if one of the 3 digit numbers is not square...
        if n == 3:
          # ... then CAST must be
          if not CAST in squares4: continue
          # find out which of the 3 digit numbers is not square
          ns = set(numbers).difference(squares3).pop()
        else:
          # CAST can't be square
          if CAST in squares4: continue
          ns = CAST
    
        printf("{ns} [CAST={CAST} THE={numbers[0]} ODD={numbers[1]} ONE={numbers[2]} OUT={numbers[3]}]")
    

    Solution: The odd number out is 185.

    The non-square is OUT, with value 185.

    The remaining numbers are: CAST = 3025 (55²), THE = 576 (24²), ODD = 144 (12²), ONE = 196 (14²).

    • Naim Uygun 25 April 2012 at 8:48 pm

      Here is my program:

      from  itertools import permutations
      p=[100,121,144,169,196,225,256,289,324,361,400,441,484,529,576,625,676,729,784,841,900,961,1024,1089,1296,1369,1764,1849,1936,2025,2304,2401,2601,2704,2809,2916,3025,3249,3481,3721,4096,4356,4624,4761,5041,5184,5329,5476,5929,6084,6241,6561,6724,7056,7396,7569,7921,8464,8649,9025,9216,9409,9604,9801]
      say=0
      for w in permutations("1234567890"):
         
          c=int(w[0])
          a=int(w[1])
          s=int(w[2])
          t=int(w[3])
          h=int(w[4])
          e=int(w[5])
          o=int(w[6])
          d=int(w[7])
          n=int(w[8])
          u=int(w[9])
          if c==0 or t==0 or o==0: continue
          cast=1000*c+100*a+10*s+t
          the=100*t+10*h+e
          odd=100*o+10*d+d
          one=100*o+10*n+e
          out=100*o+10*u+t
          
          say=0
          if cast in p: say=say+1
          if the in p : say=say+1
          if odd in p: say=say+1
          if one in p : say=say+1
          if out in p : say=say+1
          if say !=4 : continue
          print(say)
          print(cast,the,odd,one,out)
          input("break")
      
    • Jim Randell 25 April 2012 at 9:53 pm

      OK, here’s a more efficient version. It runs in 246ms.

      from itertools import permutations
      from enigma import concat, printf
      
      # squares
      squares3 = set(str(pow(n, 2)) for n in range(10, 32))
      squares4 = set(str(pow(n, 2)) for n in range(32, 100))
      
      # three digit numbers
      words3 = set(('THE', 'ODD', 'ONE', 'OUT'))
      
      # make a mapping from letters to digits
      # (or None if it is not possible)
      def mapping(letters, digits):
        m = {}
        for l, d in zip(letters, digits):
          if l in m:
            if m[l] != d: return None
          else:
            if d in m.values(): return None
            m[l] = d
        return m
      
      # at least 3 of the 3 digit numbers must be squares
      digits = set('0123456789')
      for w in words3:
        w3 = words3.difference((w,))
        letters = concat(*w3)
        # choose three of the 3 digit squares to go with them
        for s3 in permutations(squares3, 3):
          # and check them for consistency
          m = mapping(letters, concat(*s3))
          if m is None: continue
          # which letter is unassigned?
          l = set(w).difference(m.keys()).pop()
          # assign it
          for d in digits.difference(m.values()):
            m[l] = d
            # and check the corresponding number
            n = concat(*(m[i] for i in w))
            ns = None if n in squares3 else n
            # now assign the remaining letters
            for C, A, S in permutations(digits.difference(m.values()), 3):
              CAST = concat(C, A, S, m['T'])
              # if one of the 3 letter words is not square, then CAST must be
              if ns is not None:
                if not CAST in squares4: continue
              else:
                # CAST can't be square
                if CAST in squares4: continue
                ns = CAST
      
              kw = dict((w, concat(*(m[i] for i in w))) for w in words3)
              printf("{ns} [CAST={CAST} THE={THE} ODD={ODD} ONE={ONE} OUT={OUT}]", **kw)
      
      • Jim Randell 23 March 2017 at 10:08 am

        And here’s a version that uses the SubstitutedExpression() solver from the enigma.py library (which didn’t exist when the puzzle was originally posted).

        It considers the problem as 5 different alphametic puzzles, where each one considers a different candidate word as being the non-square. It runs in 102ms.

        from enigma import SubstitutedExpression, sprintf as f, join
        
        # words
        words = ('CAST', 'THE', 'ODD', 'ONE', 'OUT')
        
        # choose the word that is not a square
        for w in words:
          # create an alphametic puzzle
          exprs = list(f("{z}is_square({x})", z=('not ' if x == w else '')) for x in words)
          p = SubstitutedExpression(exprs) # add [[ d2i={} ]] if leading zeros are allowed
          # and check for solutions
          for s in p.solve():
            # value of the non-square
            v = p.substitute(s, w)
            # and the remaining words
            t = join((f("{x} = {y}", y=p.substitute(s, x)) for x in words if x != w), sep=", ")
            print(f("non-square is {w} = {v} [{t}]"))
        
  2. Brian Gladman 25 April 2012 at 10:04 pm

    And here’s yet one more

    
    from __future__ import print_function
    from itertools import permutations
    
    words = ( 'CAST', 'THE',  'ODD',  'ONE', 'OUT' )
    sq = dict( (x * x, x) for x in range(10, 100) )
    
    # permute the numbers to be used in the last three words
    for p in permutations('0123456789', 6):
      d = dict(zip('DENOTU', p))
      # compute their values for this permutation
      l3 = [int(''.join(d[c] for c in w)) for w in words[2:]]
      # and check that there are two or more squares
      if len([x for x in l3 if x in sq]) > 1:
        # now permute the remaining digit values
        for q in permutations(set('0123456789') - set(p)):
          # and assign to the remaining letters
          d.update(zip('ACHS', q))
          # compile full list of word values 
          l5 = l3 + [int(''.join(d[c] for c in w)) for w in words[:2]]
          # and find all non square values
          l = [x for x in l5 if x not in sq]
          if len(l) == 1:
            print(l[0], sorted(l5))
    
  3. arthurvause 6 May 2012 at 8:45 am

    It is easy to deduce that the non-square has to be one of ODD, ONE or OUT, so I intended the code to test all these cases, but got lucky by finding that OUT is the non-square. The code runs in 35ms

    sq3 = {}
    for s in [str(i*i) for i in range(10,32)] :
        if s[0]<>s[1] and s[0]<>s[2] :
            sq3[s]=set(s)
    
    sq4 = {}
    for s in [str(i*i) for i in range(32,100)] :
        if len(set(s))==4 :
            sq4[s]=set(s)
    
    for THE in sq3:
        if len(sq3[THE])==3:
            for ODD in sq3:
                if len(sq3[ODD])==2 :
                    for ONE in sq3:
                        if len(sq3[ONE])==3 and ONE[0]==ODD[0] and ONE[2]==THE[2] and len(sq3[THE] | sq3[ODD] | sq3[ONE])==6:
                            for CAST in sq4 :
                                if len(sq3[THE] | sq3[ODD] | sq3[ONE] | sq4[CAST])==9 and CAST[3]==THE[0]:
                                    print CAST, THE, ODD, ONE
    
    • arthurvause 6 May 2012 at 9:44 am

      Code tidied up a bit :

      start = time()
      sq3 = {}
      for s in [str(i*i) for i in range(10,32)] :
          if s[0]<>s[1] and s[0]<>s[2] :
              sq3[s]=set(s)
      
      sq4 = {}
      for s in [str(i*i) for i in range(32,100)] :
          if len(set(s))==4 :
              sq4[s]=set(s)
      
      for THE in [i for i in sq3 if len(sq3[i])==3 ]:
        for ODD in [i for i in sq3 if len(sq3[i])==2 ]:
          for ONE in [i for i in sq3 if len(sq3[i])==3 and i[0]==ODD[0] and i[2]==THE[2]]:
            if len(sq3[THE] | sq3[ODD] | sq3[ONE])==6:
              for CAST in [i for i in sq4 if i[3]==THE[0] ] :
                if len(sq3[THE] | sq3[ODD] | sq3[ONE] | sq4[CAST])==9:
                  print CAST, THE, ODD, ONE
      
      

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: