Enigmatic Code

Programming Enigma Puzzles

Enigma 1743: Order, order!

From New Scientist #2911, 6th April 2013 [link]

I have written down a list of four positive whole numbers in increasing order. They are all less than 100, and no two of them have a common factor greater than 1. If I write the numbers in words, then each begins with a different letter of the alphabet and my list is also in alphabetical order. If I tripled the numbers, then the answers would be in reverse alphabetical order when written in words. If I then doubled those answers, the resulting numbers, when written in words, would be in alphabetical order once again.

What is my list of numbers?

Enigma 1388 is also called “Order, order!”

[enigma1743]

Advertisements

3 responses to “Enigma 1743: Order, order!

  1. Jim Randell 3 April 2013 at 6:48 pm

    This Python program runs in 409ms. It uses the number_to_words() routine written for Enigma 1614.

    from enigma import factor
    
    numbers = {
      0: 'zero',
      1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine',
      10: 'ten', 11: 'eleven', 12: 'twelve', 13: 'thirteen', 15: 'fifteen', 18: 'eighteen'
    }
    
    tens = {
      1: 'teen', 2: 'twenty', 3: 'thirty', 4: 'forty', 5: 'fifty', 6: 'sixty', 7: 'seventy', 8: 'eighty', 9: 'ninety'
    }
    
    def number_to_words(n):
      if n in numbers:
        return numbers[n]
      if n < 20:
        return numbers[n % 10] + tens[1]
      if n < 100:
        (d, r) = divmod(n, 10)
        x = tens[d]
        return x + ' ' + numbers[r] if r else x
      if n < 1000:
        (d, r) = divmod(n, 100)
        x = number_to_words(d) + ' hundred'
        return x + ' and ' + number_to_words(r) if r else x
      raise NotImplementedError
    
    # generate a sequence of <s> numbers from <a> to <b>
    # that share no factors, and written as words are in
    # alphabetical order and share no initial letters
    # s - length of sequence
    # a, b - range of numbers (from a up to, but not including b)
    # ft - factors already used
    # wt - last word (if defined)
    def generate(s, a, b, ft, wt):
      if s == 0:
        yield []
      else:
        for n in range(a, b):
          f = set(factor(n))
          if f.intersection(ft): continue
          w = number_to_words(n)
          if wt and not(w[0] > wt[0] and w > wt): continue
          for ns in generate(s - 1, n + 1, b, ft.union(f), w):
            yield [n] + ns
    
    # find a sequence of 4 numbers satisfying the conditions
    for s in generate(4, 1, 100, set(), None):
      # 3x the numbers are in reverse alphabetical order
      s3 = list(number_to_words(3 * x) for x in s)
      if s3 != sorted(s3, reverse=True): continue
      # and 6x the numbers are in normal alphabetical order
      s6 = list(number_to_words(6 * x) for x in s)
      if s6 != sorted(s6): continue
    
      print(s, s3, s6)
    

    Solution: The original list of numbers is 8, 15, 19, 29.

    • Jim Randell 4 April 2013 at 9:51 pm

      I’ve added my numbers_to_words() routine to the enigma.py library (renamed to int2words()), so here’s a fully recursive program that uses that.

      from enigma import factor, int2words
      
      # generate a sequence of <s> numbers from <a> to <b>
      # that share no factors, and written as words are in
      # alphabetical order and share no initial letters
      # s - length of sequence
      # a, b - range of numbers (from a up to, but not including b)
      # ft - factors already used
      # wt, w3t, w6t - last words (if defined) 
      def generate(s, a, b, ft, wt=None, w3t=None, w6t=None):
        if s == 0:
          yield []
        else:
          for n in range(a, b):
            f = set(factor(n))
            if f.intersection(ft): continue
            w = int2words(n)
            if wt and not(w[0] > wt[0] and w > wt): continue
            w3 = int2words(3 * n)
            if w3t and not(w3 < w3t): continue
            w6 = int2words(6 * n)
            if w6t and not(w6 > w6t): continue
            for ns in generate(s - 1, n + 1, b, ft.union(f), w, w3, w6):
              yield [n] + ns
      
      # find a sequence of 4 numbers satisfying the conditions
      for s in generate(4, 1, 100, set()):
        print(s)
      
  2. Brian Gladman 4 April 2013 at 8:44 am

    Here is my version:

    # greatest common divisor
    def gcd(a, b):
      while b > 0:
        a, b = b, a % b
      return a
    
    # convert number to word form for numbers less than one thousand
    l_d1 = ( '', 'one', 'two', 'three', 'four', 'five', 'six',
             'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve' )
    l_d2 = ( 'thir', 'four', 'fif', 'six', 'seven', 'eigh', 'nine' )
    l_d3 = ( 'twen', 'thir', 'for', 'fif', 'six', 'seven', 'eigh', 'nine')
    
    def nbr_to_words(x):
      if x < 13:
        return l_d1[x] if x else 'zero'
      elif x < 20:
        return l_d2[x - 13] + 'teen'
      elif x < 100:
        return l_d3[x // 10 - 2] + 'ty ' + l_d1[x % 10]
      else:
        return (l_d1[x // 100] + ' hundred' + (' and ' +
                  nbr_to_words(x % 100) if x % 100 else ''))
    
    # the numbers are a, b, c and d with a < b < c < d
    for a in range(100):
      # convert multiples of 1, 3 and 6 to words
      a1, a3, a6 = tuple(nbr_to_words(m * a) for m in (1, 3, 6))
    
      for b in range(a + 1, 100):
        # convert multiples of 1, 3 and 6 to words
        b1, b3, b6 = tuple(nbr_to_words(m * b) for m in (1, 3, 6))
    
        # the numbers must be coprime, have different leading letters
        # and have multiples with the specified alphabetical orders
        if (gcd(b, a) == 1 and b1[0] != a1[0]
                and a1 < b1 and  a3 > b3 and a6 < b6):
    
          for c in range(b + 1, 100):
            # convert multiples of 1, 3 and 6 to words
            c1, c3, c6 = tuple(nbr_to_words(m * c) for m in (1, 3, 6))
    
            # the numbers must be coprime, have different leading letters
            # and have multiples with the specified alphabetical orders
            if (gcd(c, a) == gcd(c, b) == 1 and c1[0] not in (a1[0], b1[0])
                    and b1 < c1 and b3 > c3 and b6 < c6):
    
              # the forth number (d)
              for d in range(c + 1, 100):
                # convert multiples of 1, 3 and 6 to words
                d1, d3, d6 = tuple(nbr_to_words(m * d) for m in (1, 3, 6))
    
                # the numbers must be coprime, have different leading letters
                # and have multiples with the specified alphabetical orders
                if (gcd(d, a) == gcd(d, b) == gcd(d, c) == 1
                        and d1[0] not in (a1[0], b1[0], c1[0])
                            and c1 < d1 and c3 > d3 and c6 < d6):
    
                  # output the numbers and the multiples in words
                  print((a, b, c, d))
                  print((a1, b1, c1, d1))
                  print((a3, b3, c3, d3))
                  print((a6, b6, c6, d6))
    

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: