Enigmatic Code

Programming Enigma Puzzles

Enigma 1763: Clever spells

From New Scientist #2931, 24th August 2013 [link]

Eve said to me that she had in mind an even three-figure number that was divisible by 3. She also told me that she had spelled out the number in words and that she had counted the number of letters used. Knowing the number of letters would enable me to work out her number, she said.

Oddy said to me that he had in mind an odd three-figure number divisible by 3. He told me that he, too, had written the number in words and that he had counted the number of letters used. He said that knowing the number of letters would again enable me to work out his number.

Then the two of them had a little chat and announced that their numbers had no digit in common.

What were their numbers?

[enigma1763]

Advertisements

2 responses to “Enigma 1763: Clever spells

  1. Jim Randell 21 August 2013 at 6:19 pm

    A straightforward approach in Python (using the int2words() function from the enigma.py library originally written for Enigma 1614) give the answer in a reasonable time. This code runs in 35ms.

    from collections import defaultdict
    from itertools import product
    from enigma import irange, int2words, printf
    
    # accumulate results by parity and letter count
    r = (defaultdict(list), defaultdict(list))
    for n in irange(102, 999, step=3):
      c = sum(1 for x in int2words(n) if x.isalpha())
      r[n % 2][c].append(str(n))
    
    # find unique even and odd numbers
    ns = list(set(v[0] for (k, v) in r[p].items() if len(v) == 1) for p in (0, 1))
    
    # and find (even, odd) pairs with no digit in common
    for (even, odd) in product(*ns):
      if set(even).intersection(odd): continue
      printf("even = {even}, odd = {odd}")
    

    Solution: Eve’s number was 378. Oddy’s number was 201.

  2. Brian Gladman 21 August 2013 at 8:54 pm

    Mine is pretty similar in principle:

    from int_as_words import int_as_words
    from itertools import product
    
    # dictionaries for the even and odd numbers
    even, odd = {}, {}
    # consider numbers divisible by three in 100 .. 1000
    for n in range(102, 1000, 3):
      # convert to words
      length = len(int_as_words(n).replace(' ', ''))
      # if a number of this length has already been seen in the even or
      # odd directory, falsify the entry, otherwise add this number
      if n % 2:
        odd[length] = False if length in odd else n 
      else:
        even[length] = False if length in even else n
    
    # now find all combinations of Eve's and Oddy's numbers
    for eve, odd in product( (nbr for ln, nbr in even.items() if nbr), 
                             (nbr for ln, nbr in odd.items() if nbr) ):
      # and find those that don't share any letters
      if not (set(str(eve)) & set(str(odd))):
        print('Their numbers were {:d} and {:d}.'.format(eve, odd))
    

    but it uses my own ‘number_as_words’ routine:

    # convert number to word form for numbers less than 10 ** 36
    l_lo = ( '', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 
             'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 
             'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 
             'nineteen' )
    l_10 = ( 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 
             'eighty', 'ninety')
    l_hi = ( ' thousand', ' m', ' b', ' tr', ' quadr', ' quint', 
             ' sext', ' sept', ' oct', ' non', ' dec' )
    
    def int_as_words(x, commas=True, l=0):
      if x >= 10 ** 36:
        raise NotImplementedError
      if x < 0:
        return 'minus ' + int_to_words(-x)
      if x < 20:
        return l_lo[x] if x else 'zero'
      elif x < 100:
        return (l_10[x // 10 - 2] + ('', ' ')[x % 10 > 0] + l_lo[x % 10])
      elif x < 1000:
        return (l_lo[x // 100] + ' hundred' + 
                   ('', ' and ' + int_as_words(x % 100, l))[x % 100 > 0])
      else:
        t = int_as_words(x // 1000, commas, l + 1)
        u = ('', l_hi[l] + ('', 'illion')[l > 0])[(x // 1000) % 1000 > 0]
        v = ('', ',')[commas] + (' ', ' and ')[l == 0 and x % 1000 < 100]
        v = ('', v + int_as_words(x % 1000, commas, l))[x % 1000 > 0]
        return t + u + v
    

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: