Enigmatic Code

Programming Enigma Puzzles

Enigma 1: Serious ages

From New Scientist #1143, 22nd February 1979 [link]

“If they hadn’t invented the Closed University,” said Radiotimes of Ilea, “I shouldn’t have learned about the serious function. Every birthday now I work out how serious my new ages is. For instance, I am 27 today, and I calculate the seriousness of 27 as 4; or, as we say at the CU, S(27) = 4. That is because, as you see from the blackboard, there are 4 ways in which you can compose an arithmetical progression of one or more positive integers, in which each term is 1 greater than the previous one, with the sum of the terms totalling 27”.

“I see,” I said.

“Just to be sure you do see,” said Radiotimes, “tell me:

(a) if I live to be 100, what is/are the most serious birthday(s) I shall have had?

(b) How much over 100 should I have to live to reach a more serious birthday than that?”

This is the first Enigma puzzle ever published in New Scientist!

A £5 prize was offered for the solution (about 14.3 times the 35p cover price of the magazine).

An incomplete solution was published in New Scientist #1144, but that was followed up with a clarification in New Scientist #1145.

[enigma1]

Advertisements

6 responses to “Enigma 1: Serious ages

  1. Jim Randell 1 December 2011 at 4:29 pm

    This Python program finds the solution in 30ms:

    from itertools import count
    
    def S(n):
      s = 1
      for i in count(2):
        t = (i * (i - 1)) // 2
        if t > n:
          break
        (d, r) = divmod(n - t, i)
        if d > 0 and r == 0:
          s += 1
      return s
    
    # (a) if I live to be 100, what are the most serious birthdays I shall have had?
    (mn, ms) = ([], 0)
    for n in range(1, 101):
      s = S(n)
      if s > ms:
        (mn, ms) = ([n], s)
      elif s == ms:
        mn.append(n)
    print("max(S(n)) @ n={n}, S(n)={s}".format(n=mn, s=ms))
    
    # (b) How much over 100 should I have to live to reach a more serious birthday than that?
    for n in count(101):
      s = S(n)
      if s > ms:
        print("S({n}) = {s}".format(n=n, s=s))
        break
    

    Solution: (a) The most serious birthdays are: 45, 63, 75, 90 and 99. All have seriousness of 6. (b) Birthday 105 exceeds these with seriousness 8.

  2. Hugh Casement 29 June 2015 at 8:15 am

    I think I’m right in saying that a number has seriousness 1 if and only if it’s a power of 2 (including 2^0 = 1).
    The smallest numbers with these seriousnesses appear to be:
    S(1) = 1, S(3) = 2, S(9) = 3, S(15) = 4, S(81) = 5, S(45) = 6, S(729) = 7, S(105) = 8, S(225) = 9, S(405) = 10, S(315) = 12, S(3645) = 14, S(2025) = 15, S(945) = 16.
    For S = 11 or 13 one would have to go a lot higher.
    Can’t see much of a pattern!

    • Jim Randell 3 October 2016 at 8:35 am

      The sequence appears in OEIS as A001227 [ https://oeis.org/A001227 ], where it is characterised as “a(n) = the number of odd divisors of n”.

      So we could shorten the computation of S(n) to:

      def S(n):
        return sum(1 for d in divisors(n) if d % 2 == 1)
      

      So we can find lowest values of n for S(n) by multiplying together odd numbers:

      n = 1, S(n) = 1
      n = 3, S(3) = 2
      n = 3^2 = 9, S(9) = 3
      n = 3 × 5 = 15, S(15) = 4
      n = 3^4 = 81, S(81) = 5
      n = 3^2 × 5 = 45, S(45) = 6
      n = 3^6 = 729, S(729) = 7
      n = 3 × 5 × 7 = 105, S(105) = 8
      n = 3^2 × 5^2 = 225, S(225) = 9
      n = 3^4 × 5 = 405, S(405) = 10
      n = 3^10 = 59049, S(59049) = 11
      n = 3^2 × 5 × 7 = 315, S(315) = 12
      n = 3^12 = 531441, S(531441) = 13
      n = 3^6 × 5 = 3645, S(3645) = 14
      n = 3^4 × 5^2 = 2025, S(2025) = 15
      n= 3^5 × 5 × 7 = 945, S(945) = 16
      n = 3^16 = 43046721, S(43046721) = 17
      n = 3^2 × 5^2 × 7 = 1575, S(1575) = 18
      n = 3^18 = 387420489, S(387420489) = 19
      n = 3^4 × 5 × 7 = 2835, S(2835) = 20

      OEIS sequence A038547 [ https://oeis.org/A038547 ] lists the smallest number with exactly n odd divisors. For prime p, n = 3^(p − 1) gives S(n) = p.

  3. geoffrounce 1 October 2016 at 1:31 pm

    To mark the occasion of publishing 1000 Enigmas with Enigma 364, I thought I would try the first Enigma, published in 1979 .Keep up the good work , Jim

    Here is my programme for Enigma 1

    
    from collections import defaultdict
    d = defaultdict(list)   # for part(a) numbers 1-100
    d2  = defaultdict(list) # for part(b) numbers 100-120
    
    # to check seriousness of ages for ages between 1 and 100 and 100 and 120
    def chk_index(index):
        ser_age = 0
        for cnt in range(1, index+1):
            subtot = cnt
            c = 0
            next = 0
            while subtot <= index + 1:
                c += 1
                next = cnt + c
                subtot = subtot + next
                if subtot == index:
                    ser_age += 1
                if subtot > index:
                    continue
            if cnt == index:   
                ser_age += 1   
        return index, ser_age
    
    # Part (a)
    # check seriousness of ages for ages between 1 and 100
    for n in range(1,101):
        i,sa = chk_index(n)
        d[sa] += [i]
    
    # print dictionary d
    print('Part (a) - serious numbers in range 1 - 100')
    print('-------------------------------------------')
    print('Seriousness [ Ages ] No of Values')
    
    for k,v in sorted(d.items()):
        print(k,v, len(v))
        print()
        
    print('Max seriousness of ages 1 - 100 =', max(d))
    print()
    
    
    # PART (b)
    # check seriousness of ages for ages between 100 and 120
    for n in range(100,121):
        i,sa = chk_index(n)
        d2[sa] += [i]
    
    # print dictionary d2
    print('Part (b) - next highest serious number > 6')
    print('------------------------------------------')
    
    print('Seriousness [ Ages ] No of Values')
    
    for k,v in sorted(d2.items()):
        print(k,v, len(v))
        print()
    
    print('Max seriousness - numbers 101 to 120 =', max(d2))
    
    

    The output of this programme confirms the previously results and shows the ‘seriousness of all ages between 1 and 100:

    Part (a) – serious numbers in range 1 – 100
    ——————————————-
    Seriousness [ Ages ] No of Values
    1 [1, 2, 4, 8, 16, 32, 64] 7

    2 [3, 5, 6, 7, 10, 11, 12, 13, 14, 17, 19, 20, 22, 23, 24, 26, 28, 29, 31, 34, 37, 38, 40, 41, 43, 44, 46, 47, 48, 52, 53, 56, 58, 59, 61, 62, 67, 68, 71, 73, 74, 76, 79, 80, 82, 83, 86, 88, 89, 92, 94, 96, 97] 53

    3 [9, 18, 25, 36, 49, 50, 72, 98, 100] 9

    4 [15, 21, 27, 30, 33, 35, 39, 42, 51, 54, 55, 57, 60, 65, 66, 69, 70, 77, 78, 84, 85, 87, 91, 93, 95] 25

    5 [81] 1

    6 [45, 63, 75, 90, 99] 5

    Max seriousness of ages 1 – 100 = 6 ( for 45, 63, 75, 90, 99 )

    Part (b) – next highest serious number > 6
    ———————————————–
    Seriousness [ Ages ] No of Values
    2 [101, 103, 104, 106, 107, 109, 112, 113, 116, 118] 10

    3 [100] 1

    4 [102, 108, 110, 111, 114, 115, 119, 120] 8

    6 [117] 1

    8 [105] 1

    Max seriousness – numbers 101 to 120 = 8 (for 105)

  4. geoffrounce 3 October 2016 at 3:04 pm

    I amended my programme to find all the components of a serious number

    
    # Generic solver for Enigma 1 - "Serious Ages'
    # Finds the 'seriousness' of any number entered
    
    def chk_index(index):
        print('Components of serious age :',index)
        print('---------------------------------')
        ser_age = 0
        
        for cnt in range(1, index+1):
            subtot = cnt
            c = 0; next = 0
            
            while subtot <= index + 1:
                c += 1
                next = cnt + c
                subtot = subtot + next
    
                if subtot == index:
                    ser_age += 1
                    a = 1 - 2*cnt
                    b = ((2*cnt - 1)**2 + 8*index)**0.5
                    
                    # find no. of terms in addition
                    nterms = int(1/2*(1- 2*cnt - ((2*(cnt-1)**2 + 8*index)**0.5)))
                    if b < a: nterms = int(1/2*(a-b))
                    if b > a: nterms = int(1/2*(a+b))
                    
                    fs = 's ={0:3d}, 1st term ={1:6d}, no terms ={2:4d}'
                    print(fs.format(ser_age, cnt, nterms))
                    show_nums(cnt, nterms)
                    
                if subtot > index:
                    continue
        
            if cnt == index:   # age on its own is a serious age component
                ser_age += 1
                
        return ser_age, index
    
    def show_nums(cnt, nterms):
        # show components of this number
        print('Terms:')
        for n in range(1,nterms+1):
            nterm = cnt + n - 1
            print(nterm, end = ' ')
            
        # show total    
        lterm = cnt + nterms - 1
        sumnt = (cnt + lterm) * n/2
        sumnt = int(sumnt)
        print('\nSum: ',sumnt, )
        print()
        
    
    # REPEATED USER INPUT
    # repeated input of numbers for serious age components
    
    while True:
        print()
        print('Enter 0 to finish...')
    
        num = int(input("Enter a number: "))
        print()
        print(chk_index(num))
        print('--------------')
        if num == 0:       
            break           
    
    '''
    Sample usage
    ------------
    
    Enter 0 to finish...
    Enter a number: 105  -   Part (b) of question
    
    Components of serious age : 105
    ---------------------------------
    s =  1, 1st term =     1, no terms =  14
    Terms:
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 
    Sum:  105
    
    s =  2, 1st term =     6, no terms =  10
    Terms:
    6 7 8 9 10 11 12 13 14 15 
    Sum:  105
    
    s =  3, 1st term =    12, no terms =   7
    Terms:
    12 13 14 15 16 17 18 
    Sum:  105
    
    s =  4, 1st term =    15, no terms =   6
    Terms:
    15 16 17 18 19 20 
    Sum:  105
    
    s =  5, 1st term =    19, no terms =   5
    Terms:
    19 20 21 22 23 
    Sum:  105
    
    s =  6, 1st term =    34, no terms =   3
    Terms:
    34 35 36 
    Sum:  105
    
    s =  7, 1st term =    52, no terms =   2
    Terms:
    52 53 
    Sum:  105
    
    (8, 105)
    
    '''
    
    
  5. geoffrounce 3 October 2016 at 3:30 pm

    Hugh commented on powers of 2 having a seriousness of 1.

    As powers of 2 can be expressed as 1 * 2 ^ n, they will only have 1 odd number as a divisor, so would have a seriousness of 1, according to Jim’s formula.

    I then started looking into numbers with repeated digits with some interesting results.

    The programme below looks at the numbers of odd and even divisors of the following numbers with repeated digits :(111111,222222,333333,444444,555555,666666,777777,888888,999999)

    The first part of the programme confirms these are all serious numbers, both in terms of Jims’ function and my solver.

    The second part of the programme then finds all the odd and even divisors of these numbers, the odd divisors being the ‘serious’ numbers

    Where there are total odd or even divisors > 0, these divisors are all multiples of 16 – I guess there must be a reason

    
    from enigma import divisors
    
    # Jim's function 
    def S(n):
      return sum(1 for d in divisors(n) if d % 2 == 1)
    
    for nn in (111111,222222,333333,444444,555555,666666,777777,888888,999999):
        print( (nn, S(nn)), end = ' ')
    
    # Output
    # (111111, 32) (222222, 32) (333333, 48) (444444, 32) (555555, 64)
    # (666666, 48) (777777, 48) (888888, 32) (999999, 64)
    
    print(); print()
    for n in (111111,222222,333333,444444,555555,666666,777777,888888,999999):
        l1 = [ d for d in divisors(n) if d % 2 == 1]  # odd divisors
        len1 = len(l1)
        
        l2 = [ d for d in divisors(n) if d % 2 == 0]  # even divisors
        len2 = len(l2)
        
        print(n, len1, len2)
    
    # Output
    #Number  No of odd   No. of even Seriouness
    #        divisors    divisors
    #111111     32           0          32
    #222222     32           32         32
    #333333     48           0          48
    #444444     32           64         32
    #555555     64           0          64
    #666666     48           48         48 
    #777777     48           0          48
    #888888     32           96         32
    #999999     64           0          64
    
    
    

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: