Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 4:35 pm on 21 January 2022 Permalink | Reply
    Tags:   

    Teaser 3096: That strikes a chord 

    From The Sunday Times, 23rd January 2022 [link] [link]

    Skaredahora used a scale with seven notes, labelled J to P, for an idiosyncratic composition. It had a sequence of chords, each comprising exactly three different notes (the order being immaterial). The sequence started and ended with JNP. Each change from one chord to the next involved two notes increasing by one step in the repeating scale, and the other decreasing by two steps (e.g. JNP changing to KLJ).

    Every chord (eventually) reachable from JNP was used at least once; every allowable change from one of these chords to another was used exactly once. The number of chord changes between the first pair of JNPs was more than that between the last such pair, but, given that, was the smallest it could be.

    With notes in alphabetical order, what was the seventh chord in the sequence?

    [teaser3096]

     
    • Jim Randell's avatar

      Jim Randell 7:20 pm on 21 January 2022 Permalink | Reply

      I am assuming notes that are an octave apart are considered identical. So going up three notes from M to P gives the same note as going down 4 notes from M to P. Also that chords are identical if they contain the same three notes, and a chord change requires at least one of the notes to change.

      This Python program constructs the graph of transitions between chords, and then finds paths from JNP back to JNP that traverse all the edges (Eulerian circuits). We then apply the remaining conditions, and find the 7th element of the paths.

      It runs in 51ms.

      Run: [ @replit ]

      from enigma import (mod, all_different, ordered, tuples, Accumulator, join, printf)
      
      # there are 7 notes in the scale
      fn = mod(7)
      
      # format a sequence, 0..6 represent J..P
      fmt = lambda s: join("JKLMNOP"[i] for i in s)
      fmts = lambda ss: join((fmt(s) for s in ss), sep=" ", enc="()")
      
      # start node = JNP
      start = (0, 4, 6)
      assert fmt(start) == 'JNP'
      
      # generate changes for chord <s>
      def changes(s):
        (x1, y1, z1) = (fn(n + 1) for n in s)
        (x2, y2, z2) = (fn(n - 2) for n in s)
        for t in ((x2, y1, z1), (x1, y2, z1), (x1, y1, z2)):
          t = ordered(*t)
          if t != s and all_different(*t):
            yield t
      
      # generate possible transitions, starting with <start>
      adj = dict()
      trans = set()
      ns = {start}
      while ns:
        ns_ = set()
        for s in ns:
          ts = set(changes(s))
          adj[s] = ts
          trans.update((s, t) for t in ts)
          ns_.update(ts)
        ns = ns_.difference(adj.keys())
      
      # find sequences that use every transition in <ts>, starting from <ss>
      def seqs(ts, ss):
        # are we done?
        if not ts:
          yield ss
        else:
          # make a transition
          s = ss[-1]
          for x in adj[s]:
            t = (s, x)
            if t in ts:
              yield from seqs(ts.difference([t]), ss + [x])
            
      # find viable sequences
      def generate(trans, start):
        for ss in seqs(trans, [start]):
          # sequence must be a circuit
          if ss[0] != ss[-1]: continue
          # find the indices of start
          js = list(j for (j, x) in enumerate(ss) if x == start)
          # and distances between them
          ds = list(j - i for (i, j) in tuples(js, 2))
          # first distance is greater than last
          if not (ds[0] > ds[-1]): continue
          # return (distances, sequence)
          yield (ds, ss)
      
      # find the smallest first distance
      r = Accumulator(fn=min, collect=1)
      for (ds, ss) in generate(trans, start):
        r.accumulate_data(ds[0], ss)
      
      # output solutions
      for ss in r.data:
        printf("{x} {ss}", x=fmt(ss[6]), ss=fmts(ss))
      

      Solution: The seventh chord in the sequence is: KNO.

      The complete sequence is:

      1. JNP
      2. JKL
      3. KMP
      4. JKL
      5. LMO
      6. JNP
      7. KNO
      8. LMO
      9. KMP
      10. JNP

      [Follow the [@replit] link, click on “Show Files”, and select “Teaser 3096.mp3” to hear the chords played with JP transposed to notes AG in a single octave].

      There are 4 chords between the first two JNPs, and 3 chords between the last two JNPs.

      And this is the only progression of the form (JNP [4] JNP [3] JNP).

      There are 4 other viable progressions of the form (JNP [5] JNP [2] JNP), but we are only interested in progressions with the shortest possible distance between the first two occurrences of JNP.

      Like

  • Unknown's avatar

    Jim Randell 9:46 am on 20 January 2022 Permalink | Reply
    Tags:   

    Teaser 2878: Magic slides 

    From The Sunday Times, 19th November 2017 [link] [link]

    I have a toy consisting of eight tiles that can move by sliding them around a three-by-three frame:

    At any stage a tile adjacent to the space can slide into that space. I gave the toy to my grandson in the state illustrated and after some moves he presented it back to me with the space once again in the bottom right-hand corner but with the “2” (amongst others) not in its original position. Furthermore, his arrangement had some “magical” properties: each row, each column, and the diagonal from bottom left to top right all added up to the same total.

    What was his arrangement?

    [teaser2878]

     
    • Jim Randell's avatar

      Jim Randell 9:47 am on 20 January 2022 Permalink | Reply

      We’ve looked at sliding puzzles before. See: Enigma 1444, Enigma 106, Enigma 1210, Enigma 1160, Enigma 588.

      In order for a position to be reachable the number of “inversions” (pairs of tiles that are out of order) must be even.

      This Python program runs in 111ms.

      Run: [ @replit ]

      from enigma import (subsets, irange, all_same, printf)
      
      # fill out the grid
      for (A, B, C, D, E, F, G, H) in subsets(irange(1, 8), size=len, select="P"):
        # B cannot be 2
        if B == 2: continue
        # check the magic lines
        if not all_same(
          # rows
          A + B + C, D + E + F, G + H,
          # columns
          A + D + G, B + E + H, C + F,
          # diagonal
          C + E + G,
        ): continue
      
        # the number of inversions must be even
        n = sum(i > j for (i, j) in subsets((A, B, C, D, E, F, G, H), size=2))
        if n % 2 != 0: continue
      
        # output solution
        printf("{A} {B} {C} / {D} {E} {F} / {G} {H}")
      

      Solution: The arrangement was:

      And we can use the sliding puzzle program I wrote for Enigma 1444 [link] to verify that there is a sequence of moves that produces the required arrangement:

      % python sliding-puzzle.py 3 3 2 3 7 6 1 5 4 8
      [SOLVED] Moves: 44, Elapsed Time: 0m08s
      

      Like

  • Unknown's avatar

    Jim Randell 9:43 am on 18 January 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 901: Otherwise encaged 

    From The Sunday Times, 12th November 1978 [link]

    Our local pet shop caters especially for people who wish to keep mice in pairs. The shop sells three designs of half-cages and each customer buys two half-cages and joins them together to make a cage for two mice.

    Each of the three designs of half-cages, the Astoria, the Dorchester and the Hilton, is based on the plan above and has 3 walls ga, ab, bj together with walls at some of cd, de, ef, gh, hi, ij, dh and ei.

    Each customer buys two half-cages of different designs and joins them together such that point g of each coincides with point j of the other. A mouse is then placed in area abfc of each and allowed to run freely except where it is prevented from going by the walls. There may be some parts of the overall cage which cannot be reached by either mouse.

    The half-cages have been designed so that in no case can two mice reach each other and such that the following situation occurs: when an Astoria is joined to a Dorchester, the mouse from the Astoria has a larger area to move in than the other mouse; when a Dorchester is joined to a Hilton, the mouse from the Dorchester has a larger area; and when a Hilton is joined to an Astoria, the mouse from the Hilton has a larger area.

    When I was last in the shop I noticed that the Astoria was the only cage with a wall at dh, and also that was the only cage with a wall at ef.

    Draw a plan of the Dorchester and of the Hilton.

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    All puzzles from the The Sunday Times Book of Brain-Teasers: Book 1 (1980) book are now available on the site. I will shortly move on to puzzles from the The Sunday Times Book of Brain-Teasers: Book 2 (1981).

    [teaser901]

     
    • Jim Randell's avatar

      Jim Randell 9:44 am on 18 January 2022 Permalink | Reply

      I used the [[ grid_adjacency() ]] function from the enigma.py library to provide the transitions between the areas of a cage, and then removed those that are impeded by the inclusion of relevant walls.

      The following Python program runs in 179ms.

      Run: [ @replit ]

      from enigma import (subsets, grid_adjacency, cproduct, join, printf)
      
      # walls and the passage they impede
      walls = dict(
        cd=(0, 3), de=(1, 4), ef=(2, 5), dh=(3, 4),
        ei=(4, 5), gh=(3, 6), hi=(4, 7), ij=(5, 8),
      )
      
      # walls that exist in A, and only in A
      A_walls = {'ef', 'dh'}
      
      # walls that may exist in any of A, D, H
      ADH_walls = set(walls.keys()).difference(A_walls)
      
      # possible walls for A (must include 'ef', 'dh')
      As = list(A_walls.union(s) for s in subsets(ADH_walls))
      
      # possible walls for DH (must not include 'ef', 'dh')
      DHs = list(subsets(ADH_walls, fn=set))
      
      # adjacency matrix with no walls
      adj = grid_adjacency(3, 4)
      
      # construct a cage from two half-cages
      def construct(X, Y):
        # copy the default matrix
        m = list(set(x) for x in adj)
        # remove any adjacencies from the top walls
        for k in X:
          (x, y) = walls[k]
          m[x].discard(y)
          m[y].discard(x)
        # remove any adjacencies from the bottom walls
        for k in Y:
          (x, y) = (11 - j for j in walls[k])
          m[x].discard(y)
          m[y].discard(x)
        # return the new adjacency matrix
        return m
      
      # find the area of the maze reachable from ns
      def area(m, ns):
        xs = set()
        while ns:
          n = ns.pop()
          xs.add(n)
          ns.update(m[n].difference(xs))
        return xs
      
      # check an X+Y construction
      def check(X, Y):
        m = construct(X, Y)
        # area reachable from X should be greater than from Y
        return len(area(m, {0})) > len(area(m, {11}))
      
      # choose a set of walls for A and D
      for (A, D) in cproduct([As, DHs]):
        # check an A+D construction
        if not check(A, D): continue
      
        # choose a (dfferent) set of walls for H
        for H in DHs:
          if H == D: continue
          # check a D+H and H+A constructions
          if not (check(D, H) and check(H, A)): continue
      
          # output solution
          fmt = lambda s: join(sorted(s), sep=" ", enc="()")
          printf("D={D} H={H}; A={A}", D=fmt(D), H=fmt(H), A=fmt(A))
      

      Solution: The plans of the Dorchester and Hilton are shown below:

      The Astoria can be either of the following 2 diagrams:

      (The “walled-orf” area in the second is always inaccessible when A is combined with D or H, so the inclusion of the ij wall doesn’t make any difference).

      When combined they produce the following cages:

      Like

  • Unknown's avatar

    Jim Randell 9:33 am on 16 January 2022 Permalink | Reply
    Tags:   

    A Brain Teaser: Trial and Error 

    From The Sunday Times, 13th April 1952 [link]

    A bank cashier has a pile of 364 similar coins of which one Is known to be of abnormal weight, the remainder being all of equal weight. For testing, he has only a pair of scales, in which he can balance one pile of coins against another.

    What is the smallest number of such “trials” in which he can be sure of finding the odd coin, and what is the greatest number of coins among which he can likewise be sure of finding a single coin of odd weight in nine “trials”?

    This one of the occasional Holiday Brain Teasers published in The Sunday Times prior to the start of numbered Teasers in 1961. A prize of 5 guineas was offered.

    [teaser-1952-04-13] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 9:38 am on 16 January 2022 Permalink | Reply

      We have tackled similar problems before, see: Enigma 1589, Teaser 2500.

      For Enigma 1589 I wrote a constructive solution that searches for a minimal decision tree.

      For Teaser 2500 I gave a program that will construct minimal decision trees for a set of related problems.

      For a “type 1” problem (where we need to find the counterfeit coin, and determine if it is heaver or lighter), with n weighings we can deal with a set of (3^n − 3) / 2 coins.

      However, each decision tree for the “type 1” problem has a leaf that is impossible to reach if we are guaranteed that there is exactly one counterfeit coin. But if there are no counterfeit coins then every weighing balances and we reach the impossible leaf.

      So, for this type of puzzle (where we need to determine which coin is counterfeit, but not if it is heavier or lighter), we can put one of the coins aside and then proceed with the weighings for a “type 1” puzzle, if we reach the impossible leaf we know that it must be the unweighed coin that is counterfeit, and we don’t need to determine whether it is lighter or heavier, so we can stop. This means we can manage 1 extra coin compared to the “type 1” puzzle.

      So in n weighings we can deal with a set of (3^n − 1) / 2 coins. (The same as a “type 2” problem, where we have to determine the counterfeit coin, and whether it is lighter or heavier, but we have a reference coin available).

      Hence:

      With 6 weighings we can deal with a set of (3^6 − 1) / 2 = 364 coins.

      And with 9 weighings we can determine the counterfeit coin from (3^9 − 1) / 2 = 9841 coins.

      Solution: 364 coins require 6 weighings. With 9 weighings we can deal with up to 9841 coins.


      Note that the formula applies to n ≥ 2 weighings.

      A set consisting of 1 coin requires 0 weighings (as the set must contain a counterfeit coin), and with a set consisting of 2 coins it is not possible to determine which coin is counterfeit (unless we have a reference coin, or are told that the counterfeit is light (or heavy)).

      With 4 coins (1, 2, 3, 4) we can weigh 1 against 2. If they balance (and so are both good), we weigh one of these good coins against 3. If the second weighing balances, the counterfeit is 4. If it doesn’t balance, it is 3. If the initial weighing does not balance, then the counterfeit coin is 1 or 2, and 3 and 4 are good. So weigh 1 against 3. If they balance, the counterfeit is 2. If not the counterfeit is 1. So we can solve 4 coins with 2 weighings.


      The published solution (27th April 1952) is as follows: [link]

      Readers were posed the problem before a cashier having a pile of coins of which he knows one to be abnormal in weight, and a pair of scales but no other Instrument. They were asked (a) the least number of trial weighings he must make to be sure of identifying the abnormal coin in a pile of 364, and (b) the largest number of coins among which he may be sure of Identifying the abnormal one In nine “trials.” This was a highly successful brain-teaser in respect of the number of clever brains which it evidently teased. There were nearly 1,500 entries, but barely one in ten of them gave both answers correctly. Answers to (a) ranged from 6 to 16, and to (b) from 22 to 19,683.

      On the other hand, correct entrants had little success in expressing the method of trial succinctly. The clue is to start by weighing 121 against 121. If they balance, the remaining 122 include the abnormal coin; if not, the 122 are known to be all “good” coins. Combinations from the good and doubtful groups then reduce the scale of alternative third trials to 27 or 40, fourth to 9 or 13, fifth to 3 or 4, and sixth to one coin in each pan.

      The correct answers are: (a) 6, (b) 9,841. The latter follows the formula (3^n − 1) / 2 where n = the maximum number of trials; many entrants offered 3^n (giving the answer 19,683), which is demonstrably incorrect, e.g., for n=2.

      Like

  • Unknown's avatar

    Jim Randell 4:05 pm on 14 January 2022 Permalink | Reply
    Tags:   

    Teaser 3095: Diddums! 

    From The Sunday Times, 16th January 2022 [link] [link]

    “Diddums” was Didi’s dada’s name for numbers where the number of positive DIvisors (including 1 and the number), the number of Digits, and the Digit sUM are equal.

    Didi enjoyed mental arithmetic, doing it speedily and accurately. For numbers of digits from 1 to 9 she listed in ascending order all possible numbers whose digit sum equalled the number of digits. Working through her list in order, she quickly found the first two diddums (1 and 11) and the next three diddums. After many further calculations, Didi confirmed that the sixth diddum (which is even) was listed.

    “Now I’m bored”, she moaned.

    “Diddums!”, said Didi’s dada.

    What is the sixth diddum’s digit sum?

    [teaser3095]

     
    • Jim Randell's avatar

      Jim Randell 4:49 pm on 14 January 2022 Permalink | Reply

      This Python program generates the numbers in order.

      To find the first 6 numbers takes 74ms. (Internal runtime is 13ms).

      Run: [ @replit ]

      from enigma import (tau, irange, inf, arg, printf)
      
      # generate numbers with a digit sum of t and k digits
      def generate(t, k, n=0):
        if k == 1:
          if t < 10:
            yield 10 * n + t
        else:
          for d in irange((0 if n else 1), min(9, t)):
            yield from generate(t - d, k - 1, 10 * n + d)
      
      # generate "diddums" numbers in order
      def solve():
        for k in irange(1, inf):
          for n in generate(k, k):
            if tau(n) == k:
              yield (k, n)
      
      N = arg(6, 0, int)
      
      # find the first N numbers
      for (i, (k, n)) in enumerate(solve(), start=1):
        printf("[{i}] {k} -> {n}")
        if i == N: break
      

      Solution: The sixth number on the list has a digit sum of 8.

      The list of numbers looks like this:

      1 → 1
      2 → 11
      4 → 1003, 1111, 2101
      8 → 10000034, …, 70000001 (630 numbers)
      10 → 1000003024, …, 5100000112 (70 numbers)
      12 → 100000001028, …, 900000101001 (6320 numbers)
      14 → 10000000260032, …, 70001000100032 (2149 numbers)
      16 → 1000000000000186, …

      For the larger numbers it is more efficient to use Pollard’s Rho method [[ prime_factor_rho() ]] to find prime factors, which we can do by calling [[ tau() ]] like this:

      tau(n, prime_factor_rho)
      

      Like

    • GeoffR's avatar

      GeoffR 8:41 pm on 14 January 2022 Permalink | Reply

      A brute force solution ran in about 20 sec. After the first sixth number, there were many similar solutions to the sixth number.

      from enigma import nsplit, tau, divisors
      
      cnt = 0  #counter for number of diddum numbers.
      
      for k in range(1, 100000000):
        n = nsplit(k) #split number into digits
        sn = sum(n)   #find sum of digits
        nd = len(n)   #find number of digits
        # check sum of digits = number of digits
        if sn == nd:
          tk = tau(k)
          # check sum of digits = no. of divisors
          if sn == tk:
            print(f"{cnt+1}: num={k}, no. divisors={tau(k)}")
            print(f"Divisors are {divisors(k)}")
            print()
            cnt += 1
            # stop after the 6th number
            if cnt == 6:
              break
      
      
      

      Like

    • NigelR's avatar

      NigelR 9:55 am on 15 January 2022 Permalink | Reply

      I used a very similar approach to GeoffR (a sledgehammer once again!) but without the enigma functions. I also added a fudge to exploit the ‘(which is even)’ advice for the 6th number by using an increment of 2 once the 5th number had been found. This reduced the run time by a couple of days.

      import math
      #function to return list of digits in n
      def digs(n):    
          cont = []
          while n:
              n,a = divmod(n,10)
              cont.append(a)
          return cont        
      #function to return list of divisors of n
      def divisg(n):
          divs = [1]
          for i in range(2,int(math.sqrt(n))+1):
              if n%i == 0:
                  divs.extend([i,int(n/i)])
          divs.extend([n])
          return list(set(divs))
      #main
      i = 0
      inc = 1 
      count = 0
      while count < 6:
          i += inc
          num = digs(i)      
          if sum(num) != len(num): continue
          div = list(divisg(i))
          if len(div) != len(num):continue
          print(i,num,div)
          count += 1
          if count==5:
              if i%2 != 0: i+=1
              inc=2
      

      Like

    • Hugh+Casement's avatar

      Hugh+Casement 8:52 am on 16 January 2022 Permalink | Reply

      That was a giant sledgehammer, Nigel! There are faster ways. For example, the square of a prime has three integer divisors: none fits here. The cube of a prime has four divisors: again none found. The product of two different primes also has four divisors: that provides the next three (mentioned in the puzzle). The product of one prime and the square of another has six divisors; one of those primes obviously has to be 2 to give an even product.

      I’ll spare you my program in Basic, but after rejecting a few numbers of factors & digits it quickly found the sixth diddums and a further five.

      Like

    • GeoffR's avatar

      GeoffR 3:42 pm on 23 January 2022 Permalink | Reply

      This programme considers ‘Diddums’ numbers for separate three, four, five, six, seven and eight digit groups. This method produced the six-number solution with a much faster run-time than my previous posting. It ran in 28 msec.

      
      from enigma import Primes, tau, divisors, nsplit
      
      from enigma import Timer
      timer = Timer('ST3095', timer='E')
      timer.start()
      
      # primes for 4-digit 'Diddums' numbers
      primes = list(Primes(200))
      
      # Check 3,4,5,6,7,8 digit 'Diddums' numbers
      DD_nums = []
      
      DD_nums.append(1) # two given 'Diddums' numbers
      DD_nums.append(11)
      
      # 3-digit 'Diddums' numbers
      D3 = [x * x for x in range(10, 20) if x * x in range(100, 301)]
      for n in D3:
        if tau(n) == 3 and sum(nsplit(n)) == 3:
          DD_nums.append(n)
      
      # 4-digit 'Diddums' numbers - use a  and b as primes,
      # where four divisors are 1, a, b, a * b
      for a in primes:
        for b in primes:
          if a == b:
            continue
          if a * b in range(1000, 4001):
            if a * b in DD_nums:
              continue
            if tau(a * b) == 4 and sum(nsplit(a * b)) == 4:
              DD_nums.append(a * b)
      
      # 5-digit 'Diddums' numbers for powers p ^ 4,
      # five divisors are 1, p, p^2, p^3, p^4
      for p in (11, 13, 17):
        num = p ** 4
        if num in range(10000, 50001):
          if tau(num) == 5 and sum(nsplit(num)) == 5:
            DD_nums.append(num)
      
      # 6-digit 'Diddums' numbers 
      # product of 2 primes in a^2 * b format gives six divisors
      for a in primes:
        for b in primes:
          if a == b:
            continue
          if a * a * b in range(100000, 600001):
            if a * a * b in DD_nums:
              continue
            if tau(a * a * b) == 6 and sum(nsplit((a * a * b))) == 6:
              DD_nums.append(a * a * b)
      
      # 7-digit 'Diddums' numbers for powers p^6,
      # seven divisors are 1, p, p^2, p^3, p^4, p^5, p^6
      for p in (11, 13):   
        num = p ** 6
        if num in range(1000000, 7000001):
          if tau(num) == 7 and sum(nsplit(num)) == 7:
            DD_nums.append(num)
      
      # check first 10,000 8-digit  numbers (initially) for
      # potential 8-digit numbers summing to eight
      D8 = []
      
      for n in range(10000000, 10010000):
        ns = nsplit(n)
        if sum(ns) == 8:
          D8.append(n)
      
      for n in D8:
        if tau(n) == 8:
          DD_nums.append(n)
          break
      
      print('Diddums Numbers are ', sorted(DD_nums))
      timer.stop()      
      timer.report()
      
      # Numbers are  [1, 11, 1003, 1111, 2101, 10000034]
      # [ST3095] total time: 0.0276404s (27.64ms)
      
      

      Like

    • Frits's avatar

      Frits 8:37 am on 29 June 2023 Permalink | Reply

      Only fast for this puzzle (for general purposes too slow).
      Using tau(n, prime_factor_rho) wasn’t necessary.

         
      from enigma import tau
      from itertools import compress
      
      sumd = lambda n: sum(int(x) for x in str(n))
      
      def primesbelow(n):
        # rwh_primes1v2(n):
        """ Returns a list of primes < n for n > 2 """
        sieve = bytearray([True]) * (n // 2 + 1)
        for i in range(1, int(n ** 0.5) // 2 + 1):
          if sieve[i]:
            sieve[2 * i * (i + 1)::2 * i + 1] = \
            bytearray((n // 2 - 2 * i * (i + 1)) // (2 * i + 1) + 1)
        return [2, *compress(range(3, n, 2), sieve[1:])]
      
      # return next valid diddum
      def next_diddum(m, n, maxd):
        while m <= maxd:  
          m += 9
          if sumd(m) == n and tau(m) == n:
            return m
        return maxd + 1   # too high
      
      ds = [1, 11]   # two given diddum numbers
      
      # if n = 3, 6 or 9 then a diddum can't be of the form p^(n-1) as it must be 
      # 3 must be a divisor (3^2 = 9, 3^5 = 243 and 3^8 = 6561)
      
      # if n = 6 then a diddum can't be of the form p1 * p2**2 as p1 and p2 must 
      # be chosen from 2 and 3 
      
      for n in [4, 5, 7, 8, 9]:
        maxd = n * 10**(n - 1)              # maximum diddum number
        if n in {5, 7}:                     # only option p^(n-1) for prime numbers
          maxp = int(maxd**(1 / (n - 1)))   # maximum prime number
          # test prime power p^(n-1), divisors are 1, p, p^2, ..., p^(n-2), p^(n-1)
          P = primesbelow(maxp + 1)
          # prime number 3 may not be used if sum digits is not divisible by 3
          P = [p for p in P if not n % 3 or p != 3]   
          
          ds.extend(pwr for p in [x for x in P if 10 < x <= maxp]
                    if sumd(pwr := p**(n - 1)) == n)
        else:
          found5 = (len(ds) == 5)
         
          # initial valid diddum (start just too low)
          m = next_diddum(10**(n - 1) + n - 10, n, maxd)
          while m <= maxd:
            ds.append(m)
            if found5: 
              print("sixth diddum's digit sum:", m)
              exit()
              
            m = next_diddum(m, n, maxd)
      

      Like

  • Unknown's avatar

    Jim Randell 9:17 am on 13 January 2022 Permalink | Reply
    Tags:   

    Teaser 2850: On course 

    From The Sunday Times, 7th May 2017 [link] [link]

    Hackers-on-Sea golf course is designed by the great Jack Arnold. It is a par-seventy course and its eighteen holes are all par three, four or five. The numbers of all the par-five holes are primes, and the numbers of the par-four holes are odd.

    Recently I only had time to play half the course and ideally my nine consecutive holes should be par thirty-five. However, there is no such collection of holes.

    What are the numbers of the par-four holes?

    [teaser2850]

     
    • Jim Randell's avatar

      Jim Randell 9:17 am on 13 January 2022 Permalink | Reply

      If n3, n4, n5 are the numbers of par 3, 4, 5 holes respectively we have:

      n3 + n4 + n5 = 18
      3 × n3 + 4 × n4 + 5 × n5 = 70

      We can treat the problem as a “money changing” puzzle, i.e. we want to make an amount of 70 using 18 coins of denominations 3, 4, 5.

      The following Python program uses the [[ express() ]] function from the enigma.py library. It runs in 50ms.

      Run: [ @replit ]

      from enigma import (express, primes, irange, subsets, tuples, printf)
      
      # primes up to 18
      primes = set(primes.between(1, 18))
      
      # odd numbers up to 18
      odds = set(irange(1, 18, step=2))
      
      for (n3, n4, n5) in express(70, [3, 4, 5], min_q=1):
        if not (n3 + n4 + n5 == 18): continue
      
        # choose par 5 holes (prime)
        for p5s in subsets(primes, size=n5):
      
          # choose par 4 holes (odd)
          for p4s in subsets(odds.difference(p5s), size=n4):
      
            # the rest are par 3
            par = [3] * 18
            for k in p5s: par[k - 1] = 5
            for k in p4s: par[k - 1] = 4
      
            # no consecutive sequence of 9 holes sums to 35
            if any(sum(t) == 35 for t in tuples(par, 9)): continue
      
            printf("p4s={p4s} p5s={p5s}; {par}")
      

      Solution: The par 4 holes are: 1, 9.

      The par 5 holes are: 2, 3, 5, 7, 11, 13, 17.


      We can use a more sophisticated “money changing” algorithm by using:

      from enigma import Denominations
      ...
      
      for (n3, n4, n5) in Denominations(3, 4, 5).express(70, min_q=1):
        ...
      

      Like

  • Unknown's avatar

    Jim Randell 9:45 am on 11 January 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 903: Ding-dong 

    From The Sunday Times, 26th November 1978 [link]

    Today a great celebration will take place on Bells Island, for it is the Feast of Coincidus. On this island there is monastery and a nunnery. At regular intervals (a whole number of minutes) the monastery bell dongs once. The nunnery bell rings at regular intervals too (different from the intervals of the monastery’s bell, but also a whole number of minutes). So the island also regularly reverberates with a ding from the nunnery’s bell. The Feast of Coincidus takes place whenever the monastery’s dong and the nunnery’s ding occur at the same moment, and that is exactly what is due to happen at noon today.

    Between consecutive Feasts the dongs from the monastery and the dings from the nunnery occur alternately and, although the two noises only coincide on Feast days, they do occur a minute apart at some other times.

    When the bells coincided last time (at noon, a prime number of days ago) this whole island indulged in its usual orgy of eating and drinking.

    How many days ago was that?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    After this puzzle was published The Sunday Times was hit by industrial action, and the next issue was not published until 18th November 1979.

    [teaser903]

     
    • Jim Randell's avatar

      Jim Randell 9:47 am on 11 January 2022 Permalink | Reply

      If the shorter interval is i minutes, then, working forwards from the previous feast (= 0) the bell rings at (minutes):

      i, 2i, 3i, 4i, … ni

      And if the shorter interval is (i + x) minutes, then that bell rings at:

      (i + x), 2(i + x), 3(i + x), … (n − 1)(i + x)

      And the last 2 times coincide:

      ni = (n − 1)(i + x)
      i = (n − 1)x
      x = i / (n − 1)

      And there are times where the bells ring only 1 minute apart. As the bells are drifting apart at the beginning and together at the end, and their interval is a whole number of minutes, they must ring 1 minute apart immediately after and immediately before they coincide. i.e. x = 1 and n = i + 1

      So, the number of intervals for the shorter bell is 1 more than than the length of the interval in minutes.

      And in prime p days there are 1440p minutes, so we have:

      1440p = ni = (i + 1)i
      p = i(i + 1) / 1440

      So we need to find two consecutive integers, whose product is the product of a prime and 1440.

      Run: [ @replit ]

      from enigma import irange, inf, div, is_prime, printf
      
      for i in irange(1, inf):
        p = div(i * (i + 1), 1440)
        if p is not None and is_prime(p):
          printf("i={i} p={p}")
          break
      

      Solution: The last feast was 1439 days ago.

      So, almost 4 years ago.

      We have: i = p = 1439.

      Like

    • Hugh+Casement's avatar

      Hugh+Casement 1:15 pm on 11 January 2022 Permalink | Reply

      The periods are 24 hours for one and 23 hours 59 minutes for the other.

      Like

  • Unknown's avatar

    Jim Randell 4:40 pm on 7 January 2022 Permalink | Reply
    Tags:   

    Teaser 3094: There’s always next year 

    From The Sunday Times, 9th January 2022 [link] [link]

    George and Martha annually examine around 500 candidates (give or take 5%). It is board policy to pass about a third of the entrants but the precise recent percentage pass rates were as above.

    The number of entrants in each year up to 2021 was different as was the number of successful candidates. George told Martha of the number of entries for 2022 (different again) and Martha calculated that, if X were to be once again a whole number (also different again but within the above range), the total number of successful candidates over the five-year period would be a perfect square.

    How many entries are there for 2022 and how many successful candidates did Martha calculate for 2022?

    [teaser3094]

     
    • Jim Randell's avatar

      Jim Randell 5:08 pm on 7 January 2022 Permalink | Reply

      I considered total candidates for each year in the range [475, 525], and by allowing the (integer) percentages to be in the range [30, 36] I was able to find a unique solution.

      This Python program runs in 53ms.

      Run: [ @replit ]

      from enigma import (
        divc, divf, div, irange, cproduct, unzip,
        seq_all_different, is_square, printf
      )
      
      # given pass rates (percentages)
      rates = [30, 32, 35, 36]
      
      # find viable "<n> of <t>" pairs for percentages 30 - 36
      pairs = dict()
      for x in irange(30, 36):
        pairs[x] = list()
        for n in irange(divc(475 * x, 100), divf(525 * x, 100)):
          t = div(100 * n, x)
          if t is not None:
            pairs[x].append((n, t))
      
      # consider possible (n, t) pairs for the given years
      for nts in cproduct(pairs[x] for x in rates):
        (ns, ts) = unzip(nts)
        if not (seq_all_different(ns) and seq_all_different(ts)): continue
        nsum = sum(ns)
      
        # consider possible (n, t) pairs for 2022
        for (k, vs) in pairs.items():
          if k in rates: continue
          for (n, t) in vs:
            if n in ns or t in ts: continue
            # is the total number of n's a square?
            if is_square(n + nsum):
              printf("k={k}% -> {n} of {t} [ns={ns} ts={ts}]")
      

      Solution: There were 500 entries for 2022. Martha’s calculation was for 165 successful candidates.

      Like

    • GeoffR's avatar

      GeoffR 8:29 pm on 7 January 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      predicate is_sq(var int: y) =
      exists(z in 1..ceil(sqrt(int2float(ub(y))))) (z*z = y );
      
      % Total entrants for each year & given pass rates
      var 475..525:Y2018;  %Pass % = 30
      var 475..525:Y2019;  %Pass % = 32
      var 475..525:Y2020;  %Pass % = 35
      var 475..525:Y2021;  %Pass % = 36
      
      var 475..525:Y2022;  
      var 30..36:P5;  % Per Cent pass rate for Y2022
      
      % total entrants, successful candidates, pass rates are all different
      constraint all_different( [Y2018, Y2019, Y2020, Y2021, Y2022]);
      constraint all_different ([SC2018, SC2019, SC2020, SC2021, SC2022]);
      constraint all_different([30, 32, 35, 36, P5]);
      
      % Total passes for each year
      % LB = 0.3 * 475 = 142, UB = 0.36 * 525 = 189
      var 142..189:SC2018; var 142..189:SC2019; var 142..189:SC2020;
      var 142..189:SC2021; var 142..189:SC2022;
      
      % calculate successful candidates for each year
      constraint 100 * SC2018 == 30 * Y2018;
      constraint 100 * SC2019 == 32 * Y2019;
      constraint 100 * SC2020 == 35 * Y2020;
      constraint 100 * SC2021 == 36 * Y2021;
      constraint 100 * SC2022 == P5 * Y2022;
      
      % total successful candidates for years (2018 - 2022) is a square
      constraint is_sq(SC2018 + SC2019 + SC2020 + SC2021 + SC2022);
      
      solve satisfy;
      
      output [ "Successful candidates for 2022 = " ++ show(SC2022) ++ "\n"
      ++ "Total entrants for 2022 = " ++ show(Y2022) 
      ++ "\n"  ++ "2022 Pass % = " ++ show(P5)];
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:09 am on 6 January 2022 Permalink | Reply
    Tags: by: David Preston   

    Brain-Teaser 896: Handshaking numbers 

    From The Sunday Times, 8th October 1978 [link]

    My wife and I attended a formal dinner at which there were just eight other people, namely the four couples Mr and Mrs Ailsa, Mr and Mrs Blackler, Mr and Mrs Caroline and Mr and Mrs Duncan. Introductions were made, and a certain number of handshakes took place (but, of course, no one shook hands with him/herself, nor with his/her spouse, nor more than once with anyone else). At the end of the evening I asked each other person how many hands they had shaken, and I was surprised to find that all the answers given were different. Also, the total of the number of handshakes made by the other four men was the same as the total of handshakes made by all five women.

    The only woman I shook hands with was my old friend Mrs Ailsa. We had a long chat and then I took her to one side and, being a jealous fellow, I asked her whose hands my wife had shaken. She was unable to tell me, but luckily I was able to work it out later from the above information.

    Whose hands did my wife shake? How many hands did Mrs Ailsa shake?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser896]

     
    • Jim Randell's avatar

      Jim Randell 9:11 am on 6 January 2022 Permalink | Reply

      This one is quite fun to solve manually.

      The largest possible number of handshaking partners is 8 (if you shake hands with everyone except yourself and your spouse).

      So the setter must have got the responses 0 – 8, when he asked the other 9 people their numbers of handshakes.

      Programatically I used a MiniZinc model to construct the handshaking matrix, and then formatted the output using my minizinc.py wrapper.

      The following Python 3 program runs in 207ms.

      from enigma import join, printf
      from minizinc import MiniZinc
      
      # we label the people in pars (1, 2) (3, 4) ... (9, 10)
      people = ["Mr S", "Mrs S", "Mr A", "Mrs A", "Mr B", "Mrs B", "Mr C", "Mrs C", "Mr D", "Mrs D"]
      # map people to integers, and integers to people
      p2i = dict((x, i) for (i, x) in enumerate(people, start=1))
      i2p = dict((v, k) for (k, v) in p2i.items())
      
      ps2is = lambda ps: join((p2i[p] for p in ps), sep=", ", enc="{}")
      MrS = p2i["Mr S"]
      MrABCD = ps2is("Mr " + x for x in "ABCD")
      MrsSABCD = ps2is("Mrs " + x for x in "SABCD")
      
      # construct the MiniZinc model
      model = f"""
      include "globals.mzn";
      
      set of int: People = 1..10;
      
      % boolean handshake matrix
      array [People, People] of var 0..1: x;
      
      % handshakes are reciprocal
      constraint forall (i, j in People) (x[i, j] = x[j, i]);
      
      % no-one shakes hands with themselves
      constraint forall (i in People) (x[i, i] = 0);
      
      % no-one shakes hands with their spouse
      constraint forall (i in People where i mod 2 = 1) (x[i, i + 1] = 0);
      constraint forall (i in People where i mod 2 = 0) (x[i, i - 1] = 0);
      
      % total number of handshakes
      array [People] of var 0..8: t;
      constraint forall (i in People) (t[i] = sum (j in People) (x[i, j]));
      
      % apart from Mr S, everyone had a different total
      constraint all_different([t[i] | i in People where i != {MrS}]);
      
      % total for Mr A, B, C, D = total for Mrs S, A, B, C, D
      constraint sum ([t[i] | i in {MrABCD}]) = sum ([t[i] | i in {MrsSABCD}]);
      
      % Mr S shook hands with Mrs A, but no other wives
      constraint x[{MrS}, {p2i["Mrs A"]}] = 1;
      constraint x[{MrS}, {p2i["Mrs B"]}] = 0;
      constraint x[{MrS}, {p2i["Mrs C"]}] = 0;
      constraint x[{MrS}, {p2i["Mrs D"]}] = 0;
      
      % eliminate duplicates by placing an order on Mr B, C, D
      constraint increasing([t[{p2i["Mr B"]}], t[{p2i["Mr C"]}], t[{p2i["Mr D"]}]]);
      
      solve satisfy;
      """
      
      # solve the model
      for s in MiniZinc(model).solve():
        # output the handshakes
        x = s['x']
        for k in sorted(x.keys()):
          v = list(k_ for k_ in x[k].keys() if x[k][k_] == 1)
          printf("{k} [{n}] = ({v})", k=i2p[k], v=join((i2p[i] for i in v), sep=", "), n=len(v))
        printf()
      

      Solution: The setters wife (Mrs S) shook hands with: Mrs A, Mr B, Mr C, Mr D; Mrs A shook hands with 8 others.


      Manually:

      We have “Mr S” and the other 8 who participated in 0 – 8 handshakes, one value each.

      “0” can’t shake hands with anybody, and “8” can’t shake hands with “0”, so must shake hands with everyone else (and so must be married to “0”).

      Filling in the handshakes we know:

      Mr S: 8, …
      0: none (complete, married to 8)
      1: 8 (complete)
      2: 8, …
      3: 8, …
      4: 8, …
      5: 8, …
      6: 8, …
      7: 8, …
      8: Mr S, 1, 2, 3, 4, 5, 6, 7 (complete, married to 0)

      Now “7”, can’t shake hands with “0”, “1” or “8”, so must shake hands with everyone else (and must be married to “1”). And this completes “7” and “2”. Similarly we complete “6” and “3” (and “6” must be married to “2”), then “5” and “4” (“5” is married to “3”):

      Mr S: 5, 6, 7, 8
      0: none (complete, married to 8)
      1: 8 (complete, married to 7)
      2: 7, 8 (complete, married to 6)
      3: 6, 7, 8 (complete, married to 5)
      4: 5, 6, 7, 8 (complete)
      5: Mr S, 4, 6, 7, 8 (complete, married to 3)
      6: Mr S, 3, 4, 5, 7, 8 (complete, married to 2)
      7: Mr S, 2, 3, 4, 5, 6, 8 (complete, married to 1)
      8: Mr S, 1, 2, 3, 4, 5, 6, 7 (complete, married to 0)

      So “0” – “8” are complete, and hence so is “Mr S”, and he must be married to “4” (“Mrs S”).

      And Mr S shook hands with just one woman (Mrs A), so three of 5, 6, 7, 8 must be men (and the other one is Mrs A).

      We know the sum of the handshakes of the 5 wives must sum to half of sum(0, 1, 2, …, 8) = 36, i.e. 18. And Mrs S is “4”.

      So we need the remaining 4 wives (one each from (0, 8) (1, 7) (2, 6) (3, 5)) to sum to 14.

      The only possibilities are 0 + 7 + 2 + 5 = 14, and 8 + 1 + 2 + 3 = 14.

      The first is ruled out as 5 and 7 can’t both be wives (Mr S only shook hands with one woman).

      So the wives are 1, 2, 3, 8 (and 4). And the corresponding husbands are 7, 6, 5, 0 (and Mr S).

      Hence: “8” = “Mrs A”; “0” = “Mr A”.

      And Mr B, C, D not distinguishable in the puzzle so we can order them by the number of handshakes (the other arrangements will give further solutions), so we can now complete the matrix:

      (4) Mr S: Mr B, Mr C, Mr D, Mrs A
      0 = Mr A: none
      1 = Mrs D: Mrs A
      2 = Mrs C: Mr D, Mrs A
      3 = Mrs B: Mr C, Mr D, Mrs A
      4 = Mrs S: Mr B, Mr C, Mr D, Mrs A
      5 = Mr B: Mr S, Mrs S, Mr C, Mr D, Mrs A
      6 = Mr C: Mr S, Mrs B, Mrs S, Mr B, Mr D, Mrs A
      7 = Mr D: Mr S, Mrs C, Mrs B, Mrs S, Mr B, Mr C, Mrs A
      8= Mrs A: Mr S, Mrs D, Mrs C, Mrs B, Mrs S, Mr B, Mr C, Mr D

      Like

    • NigelR's avatar

      NigelR 7:13 pm on 12 January 2022 Permalink | Reply

      I found it very much quicker to solve this puzzle manually than unentangle the code but the program below makes no assumptions about the underlying logic other than the sum of digits 0-8 being 36.
      I am thinking of using the nickname Thor since a big hammer seems to be my weapon of choice!!

      
      from itertools import combinations as comb,permutations as perm
      m=['am','bm','cm','dm'] #list of males not including setter
      f=['af','bf','cf','df','ef'] #list of females
      pairs=[['am','af'],['bm','bf'],['cm','cf'],['dm','df'],['em','ef']] #pairs of spouses
      
      #find combinations of digits where m and f both sum to 18
      def gen():
          for x in comb([0,1,2,3,4,5,6,7,8],4):
              if sum(x)!=18:continue
              y = [y for y in range(0,9) if y not in x]    
              for h in perm(x):
                  for w in perm(y):
                      yield  h+w
      #check allocations of shakes match 0-8 and setter shakes hands with Mrs A: :
      def test1():
          for i in mat:
              if len(mat[i])!=i:return False
          if 'em' not in mat[alloc['af']]: return False   
      #...and Mrs A is only female to shake setter's hand
          count=0
          for i in f:
              if 'em' in mat[alloc[i]]:count+=1
          if count >1:return False
          return True
      
      for seq in gen():    
          mat= {i:[] for i in range(0,9)}  # clear mat entries from previous iteration
          alloc= {i:j for (i,j) in zip(m+f,seq)} # set alloc to access attendees with current shake numbers
          point= {j:i for [i,j] in alloc.items()} #pointer - transpose of alloc to cross reference shake numbers and attendees
          
          for i in range(0,9):
             for j in range(i+1,9):
                  mat[j].append(point[8-i]) #populate mat  
      
          for i in mat:
              pair=[j for j in pairs if point[i] in j]
              mat[i] = [y for y in mat[i] if y not in [item for items in pair for item in items]]
              if len(mat[i])<i:mat[i].append('em')
          if not test1():continue
      #mat now contains valid set of handshakes for each attendee           
          for x in mat:
              print(point[x], 'shakes hand of',mat[x])
          break
      

      Like

  • Unknown's avatar

    Jim Randell 2:05 pm on 4 January 2022 Permalink | Reply
    Tags:   

    Teaser 2875: Easy as ABC 

    From The Sunday Times, 29th October 2017 [link] [link]

    I have ten cards and on each is one of the letters A, B, C, E, L, N, T, V, W and Y. On the back of each card is a different digit.

    The digits on T, E, N add to 10.

    The digits on T, W, E, L, V, E add to 12.

    The digits on T, W, E, N, T, Y add to 20.

    The digits on A, B, C add to …

    If I told you that last total, then you should be able to answer the following question:

    What are the digits on T, E and N respectively?

    [teaser2875]

     
    • Jim Randell's avatar

      Jim Randell 2:06 pm on 4 January 2022 Permalink | Reply

      Presumably we are to find the unique answer to the final question.

      The values of A, B, C are independent of the rest of the puzzle, and we are only interested in their sum, so we can place an ordering on them (and the remaining permutations would provide further solutions).

      I used the [[ SubstitutedExpression() ]] solver from the enigma.py library to find possible values of A + B + C and (T, E, N), and the used the [[ filter_unique() ]] function to find sums that give a unique value for (T, E, N).

      The following Python program runs in 60ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, filter_unique, uniq, unpack, printf)
      
      # find solutions to the given sums
      p = SubstitutedExpression(
        [
          "sum([T, E, N]) = 10",
          "sum([T, W, E, L, V, E]) = 12",
          "sum([T, W, E, N, T, Y]) = 20",
          "A < B < C", # to remove duplicate solutions
        ],
        answer="(A + B + C, (T, E, N))",
        verbose=0,
      )
      
      # find solutions where A + B + C uniquely identifies (T, E, N)
      ss = filter_unique(
        uniq(ans for (d, ans) in p.solve()),
        unpack(lambda ABC, TEN: ABC),
        unpack(lambda ABC, TEN: TEN),
      ).unique
      
      # output solutions
      for (ABC, TEN) in ss:
        printf("A + B + C = {ABC} -> (T, E, N) = {TEN}") 
      

      Solution: T = 3; E = 2; N = 5.

      The complete set of values:

      {A, B, C} = {7, 8, 9}
      {L, V} = {0, 4}
      E = 2
      N = 5
      T = 3
      W = 1
      Y = 6

      Like

    • GeoffR's avatar

      GeoffR 6:38 pm on 4 January 2022 Permalink | Reply

      The only way I could find a unique solution for (T,E,N) in MiniZinc was to maximise the sum of (A+B+C). Other potential values of (T,E,N) were found, but they were not unique.

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 0..9:A; var 0..9:B; var 0..9:C; var 0..9:E;
      var 0..9:L; var 0..9:N; var 0..9:T; var 0..9:V;
      var 0..9:W; var 0..9:Y;
      
      constraint all_different([A, B, C, E, L, N, T, V, W, Y]);
      constraint T > 0;
      
      constraint T + E + N == 10;
      constraint T + W + E + L + V + E == 12;
      constraint T + W + E + N + T + Y == 20;
      
      solve maximize(A + B + C);
      
      output["[A + B + C] = " ++ show(A + B + C) ++ 
      ", [T,E,N] = " ++ show([T,E,N]) ];
      
      % [A + B + C] = 17, [T,E,N] = [1, 0, 9]
      % ----------
      % [A + B + C] = 18, [T,E,N] = [2, 0, 8]
      % ----------
      % [A + B + C] = 20, [T,E,N] = [2, 0, 8]
      % ----------
      % [A + B + C] = 22, [T,E,N] = [2, 0, 8]
      % ----------
      % [A + B + C] = 23, [T,E,N] = [1, 2, 7]
      % ----------
      % [A + B + C] = 24, [T,E,N] = [3, 2, 5] <<< Solution with unique values of  A, B and C.
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 11:37 am on 2 January 2022 Permalink | Reply
    Tags:   

    An American Brain Teaser 

    From The Sunday Times, 5th June 1949 [link]

    The conundrum below below was said to have been posed by an American professor, with unflattering results, to an academic congress.

    A man calling on a friend in an American town saw a number of children playing in the garden. Without counting them he said to his host: “Surely they are not all yours?”. The other man replied: “No, there are four families, the largest being my own, the next largest my brother’s, the third largest my younger sister’s, and the smallest my elder sister’s. It is a pity that there are too few to make up a couple of base-ball teams”. (There are nine a side at base-ball).

    Then he added: “Oddly enough, the numbers of the four families of children, multiplied together, make the street number of this house”. The visitor, who knew the street number, thought for a moment and said: “Has the smallest family one or two children?”. His host having given the answer, he then stated with certainty how many there were in each family.

    How many were there?

    This one of the occasional Holiday Brain Teasers published in The Sunday Times prior to the start of numbered Teasers in 1961 that I have found. A prize of 10 guineas was offered.

    [teaser-1949-06-05] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 11:38 am on 2 January 2022 Permalink | Reply

      This Python program runs in 51ms.

      Run: [ @replit ]

      from enigma import (irange, decompose, group, multiply, unpack, printf)
      
      # generate possible arrangements
      def generate():
        # consider the total number of children (< 18)
        for t in irange(10, 17):
          # break the total down into 4 families
          for ns in decompose(t, 4):
            yield ns
      
      # group the arrangements by product
      g = group(generate(), by=multiply)
      
      # consider products, and arrangements that make that product
      for (k, vs) in g.items():
        # there must be more than 1 arrangement
        if not (len(vs) > 1): continue
        # now group the arrangements by the smallest value ...
        g1 = group(vs, unpack(lambda a, b, c, d: a))
        # ... which must be 1 or 2
        if not (set(g1.keys()) == {1, 2}): continue
        # and find unique groups
        for (a, ns) in g1.items():
          if len(ns) == 1:
            printf("kids = {ns[0]}; k={k} vs={vs}")
      

      Solution: The numbers of children in the families were: 2, 3, 4, 5.


      The house number is 120, so the possible numbers of children are:

      (1, 3, 5, 8)
      (1, 4, 5, 6)
      (2, 3, 4, 5)

      The response for the number of children in the smallest family must be “2” (as “1” would not allow the numbers to be deduced), and this leads to a single arrangement.

      Like

    • GeoffR's avatar

      GeoffR 4:39 pm on 2 January 2022 Permalink | Reply

      % A Solution in MiniZinc
      % Look for three solutions - ref Jim's posting
      include "globals.mzn";
      
      int: total == 18; % max sum of ages < total
      var 24..360: num; % max UB = 3*4*5*6
      
      % Three solutions for 4 children
      var 1..10:a; var 1..10:b; var 1..10:c; var 1..10:d; 
      var 1..10:e; var 1..10:f; var 1..10:g; var 1..10:h; 
      var 1..10:i; var 1..10:j; var 1..10:k; var 1..10:m; 
      
      constraint all_different([a, b, c, d]);
      constraint all_different([e, f, g, h]);
      constraint all_different([i, j, k, m]);
      
      % max total ages for four children
      constraint a + b + c + d < total /\ e + f + g + h < total
       /\ i + j + k + m < total;
       
      % the smallest family has one or two children
      constraint a == 1 \/ a == 2;
      constraint e == 1 \/ e == 2;
      constraint i == 1 \/ i == 2;
      constraint a != e /\ a != i;
       
      % make (a,b,c,d) the smallest family - one or two children
      constraint (a + b + c + d) < (e + f + g + h) 
      /\ (e + f + g + h) < (i + j + k + m);
      
      % the street number of the house
      constraint num == a * b * c * d /\ num == e * f * g * h 
      /\ num == i * j * k * m;
      
      % put children's ages in order
      constraint increasing([a, b, c, d]) /\  increasing([e, f, g, h])
      /\ increasing ([i, j, k, m]);
      
      solve satisfy;
      
      output["House number = " ++ show(num) ++ "\n"
      ++ " Family children(1) = " ++ show([a, b, c, d]) ++ "\n"
      ++ " Family children(2) = " ++ show([e, f, g, h]) ++ "\n"
      ++ " Family children(3) = " ++ show([i, j, k, m]) ++ "\n" ];
      
      % House number = 120
      %  Family children(1) = [2, 3, 4, 5]  <<< Solution
      %  Family children(2) = [1, 4, 5, 6]
      %  Family children(3) = [1, 3, 5, 8]
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 3:56 pm on 30 December 2021 Permalink | Reply
    Tags:   

    Teaser 3093: Shout snap! 

    From The Sunday Times, 2nd January 2022 [link] [link]

    My grandson and I play a simple card game. We have a set of fifteen cards on each of which is one of the words ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, SHOUT and SNAP. We shuffle the cards and then display them one at a time. Whenever two consecutive cards have a letter of the alphabet occurring on both we race to shout “Snap!”. In a recent game there were no snaps. I counted the numbers of cards between the “picture-cards” (J, Q, K) and there was the same number between the first and second picture-cards occurring as between the second and third. Also, the odd-numbered cards (3, 5, 7, 9) appeared in increasing order during the game.

    In order, what were the first six cards?

    [teaser3093]

     
    • Jim Randell's avatar

      Jim Randell 4:48 pm on 30 December 2021 Permalink | Reply

      The following Python 3 program runs in 478ms.

      Run: [ @replit ]

      from enigma import subsets, intersect, join, printf
      
      # the cards
      cards = "ACE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE TEN JACK QUEEN KING SHOUT SNAP".split()
      
      # which cards snap?
      snap = set()
      for (x, y) in subsets(cards, size=2):
        if intersect([x, y]):
          snap.update([(x, y), (y, x)])
      
      # generate sequences with no snaps
      def generate(cards, xs=[], pics=[], subseq=None):
        # are we done?
        if not cards:
          yield xs
        else:
          # choose the next card
          for (i, x) in enumerate(cards):
            if xs and (x, xs[-1]) in snap: continue
      
            # check subsequence cards appear in order
            ss = subseq
            if x in subseq:
              if x != subseq[0]: continue
              ss = ss[1:]
      
            # check picture cards appear equally distanced
            ps = pics
            if x in {'JACK', 'QUEEN', 'KING'}:
              ps = ps + [len(xs)]
              if len(ps) == 3 and ps[2] - ps[1] != ps[1] - ps[0]: continue
      
            # solve for the remaining cards
            yield from generate(cards[:i] + cards[i + 1:], xs + [x], ps, ss)
      
      # find solutions
      for ss in generate(cards, subseq=['THREE', 'FIVE', 'SEVEN', 'NINE']):  
        printf("{ss}", ss=join(ss, sep=" "))
      

      Solution: The first six cards were: EIGHT, SNAP, THREE, KING, ACE, SHOUT.

      And the full sequence is:

      EIGHT, SNAP, THREE, KING, ACE, SHOUT, FIVE, TWO, QUEEN, SIX, TEN, FOUR, SEVEN, JACK, NINE

      There are 4 cards between KING and QUEEN, and 4 cards between QUEEN and JACK.

      Like

      • NigelR's avatar

        NigelR 3:50 pm on 31 December 2021 Permalink | Reply

        A typical brute force approach from me. It is neither pretty nor quick, but it gets the job done (eventually!). I could probably have mined a bitcoin using less processing effort.

        
        # STBT 3093
        
        from itertools import permutations as perm
        
        cards=['ACE','TWO','THREE','FOUR','FIVE','SIX', 'SEVEN', 'EIGHT', 'NINE', 'TEN', 'JACK', 'QUEEN', 'KING','SHOUT','SNAP']
        oddnums=['THREE','FIVE','SEVEN','NINE']
        odds=[]
        evens=[]
        
        def oddtest(self): #test self for numbers 3-9 in correct sequence
            ind = [self.index(z) for z in oddnums]
            for z in range(0,3):
                if ind[z]>ind[z+1]:return False
            return True 
        
        def pictest(): #test picture cards for consistent spacing
            j,q,k=seq.index('JACK'),seq.index('QUEEN'),seq.index('KING')
            if q<j and q<k:return False #Q must be between J & K for equal spacing since q is an even and other two are odds
            if q>j and q>k:return False
            if abs(q-j)!=abs(k-q): return False  #check for equal spacing
            return True 
        
        def seqtest(): #test seq for no shared letters in adjacent cards
            for i in range(0,14):
                for z in seq[i]:
                    if z in seq[i+1]:return False
            return True
        
        seq=cards
        evens=[x for x in cards if 'E' in x] # there are 8 cards with letter E, so can only be placed on even slots to avoid adjacency
        odds=[x for x in cards if x not in evens]
        
        for x in list(perm(evens)):
            if not oddtest(x): continue
            for y in list(perm(odds)):
                for i in range(0,8): seq[i*2]=x[i]
                for i in range(1,8): seq[(2*i)-1]=y[i-1]
                if not pictest():continue
                if not seqtest():continue
                print ('The first six cards drawn in order were:',seq[0:6])
        
        

        Like

        • Jim Randell's avatar

          Jim Randell 10:54 am on 2 January 2022 Permalink | Reply

          My first program was quite slow too. I generated all sequences without a “snap” and then applied the remaining conditions.

          To improve the speed I moved the conditions inside the [[ generate() ]] function, and that got the code down to running in less than a second.

          Your observation that there are 8 cards with an E, and 7 cards without (so they must be interleaved, starting with an E), speeds up my code to 287ms.

          We replace line 20 in my program with:

                if xs:
                  # can't snap with the previous card
                  if (x, xs[-1]) in snap: continue
                else:
                  # first card must have an E
                  if not ('E' in x): continue
          

          Like

          • Brian Gladman's avatar

            Brian Gladman 4:37 pm on 4 January 2022 Permalink | Reply

            @Jim I went down exactly the same path and arrived at a close to identical solution to your own.

            I missed the significance of cards with ‘E’s until it was pointed out here and this provides a major improvement in performance.

            If I add this to your version after line 18:

                pos_even = not (len(xs) & 1)
            

            and this after line 20:

                  if pos_even and 'E' not in x: continue
            

            the run time for me (using profile )drops by a factor of over 20.

            Like

          • Jim Randell's avatar

            Jim Randell 9:51 pm on 4 January 2022 Permalink | Reply

            Using the observation that the “with E” and “without E” sets of cards must be interleaved allows us to go back to the simpler approach where we generate (interleaved) sets of cards, and then check them for the remaining conditions, and still have the program run in a short time.

            from enigma import (cproduct, intersect, filter2, join, printf)
            
            # the cards
            cards = "ACE TWO THREE FOUR FIVE SIX SEVEN EIGHT NINE TEN JACK QUEEN KING SHOUT SNAP".split()
            
            # partition the cards into 2 sets
            (cs1, cs2) = filter2((lambda x: 'E' in x), cards)
            assert len(cs1) == len(cs2) + 1
            
            # which cards snap?
            snap = set()
            for (x, y) in cproduct([cs1, cs2]):
              if intersect([x, y]):
                snap.update([(x, y), (y, x)])
            
            # generate sequences with no snaps, interleaving cs1 and cs2
            def generate(cs1, cs2, xs=[]):
              # are we done?
              if not cs1:
                yield xs
              else:
                # choose the next card
                for (i, x) in enumerate(cs1):
                  if xs and (x, xs[-1]) in snap: continue
                  # solve for remaining cards
                  yield from generate(cs2, cs1[:i] + cs1[i + 1:], xs + [x])
            
            # find solutions
            for ss in generate(cs1, cs2):
              pos = lambda x: ss.index(x)
              # check THREE, FIVE, SEVEN, NINE appear in order
              if not (pos('THREE') < pos('FIVE') < pos('SEVEN') < pos('NINE')): continue
              # check JACK, QUEEN, KING are evenly spaced
              (i, j, k) = sorted(pos(x) for x in ['JACK', 'QUEEN', 'KING'])
              if not (j - i == k - j): continue
              # output solution
              printf("{ss}", ss=join(ss, sep=" "))
            

            Like

  • Unknown's avatar

    Jim Randell 10:23 am on 30 December 2021 Permalink | Reply
    Tags:   

    Teaser 2855: Fab four 

    From The Sunday Times, 11th June 2017 [link] [link]

    Here are four numbers, where each is the same fixed whole-number multiple of the previous one. However, in some places I have consistently replaced digits by letters — and in the other places the digits have been replaced by [asterisks]. In this way the numbers have become the fabulous four:

    JOHN
    P*UL
    R***O
    ****GE

    What number is my GROUP?

    [teaser2855]

     
    • Jim Randell's avatar

      Jim Randell 10:24 am on 30 December 2021 Permalink | Reply

      I used the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      I added lower case letters (which don’t have to be distinct from each other) to fill in the gaps in the words.

      The following run file executes in 83ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --distinct="EGHJLNOPRU"
      --reorder=0
      
      "{JOHN} * {z} = {PaUL}"
      "{PaUL} * {z} = {RingO}"
      "{RingO} * {z} = {jeorGE}"
      
      --answer="{GROUP}"
      

      Solution: GROUP = 57209.

      The expressions are:

      1238 × 8 = 9904
      9904 × 8 = 79232
      79232 × 8 = 633856

      alphametically:

      JOHN × N = PPUL
      PPUL × N = RPOHO
      RPOHO × N = EHHNGE

      Like

  • Unknown's avatar

    Jim Randell 10:03 am on 28 December 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 894: Prime consideration 

    From The Sunday Times, 24th September 1978 [link]

    At our local the other night I made the acquaintance of a chap called Michael and his wife Noelle. We talked a good bit about our respective families, our activities and our hobbies. When I happened to mention my interest in mathematical puzzles to Michael he said that he knew one at first hand which might give me some amusement. He proceeded to tell me that although he himself, his parents (who were born in different years), Noelle and their children (featuring no twins, triplets, etc.) had all been born in the nineteen-hundreds and none of them in a leap year, the numerical relationship between their years of birth was nevertheless in two respects a bit unusual.

    “In the first place”, he said, “just one of us has our year of birth precisely equal to the mean of all the others’ years of birth. But more remarkably the difference between my father’s year of birth and that of any one of the rest of us is a prime, and the same is true of that of my mother. And she, like Noelle here, had no child after she had passed her twenties”.

    And then with a grin he concluded, “So now you’ll know about the whole family and even, if you want, be able to figure out just how old Noelle is without having to be rude and ask her!”

    In which year was Noelle born? And how many children does she have?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser894]

     
    • Jim Randell's avatar

      Jim Randell 10:03 am on 28 December 2021 Permalink | Reply

      There are no multiple births for Noelle, but she could manage to have 2 children in a calendar year (by having one early in the year, and one late in the year).

      Also, if she was born at the end of the year (as is suggested by her name), it is possible to squeeze a child (or two) in during the calendar year containing her 30th birthday.

      In order to get a unique solution, we need to assume Michael and Noelle have more than 1 child (justified by his use of “children”).

      This Python program runs in 52ms.

      Run: [ @replit ]

      from enigma import (defaultdict, irange, Primes, subsets, div, printf)
      
      # check for leap years
      is_leap = lambda y: y % 4 == 0 and (y % 100 != 0 or y % 400 == 0)
      
      # primes less than 100
      primes = Primes(100)
      
      # find possible 19xx years
      years = list(y for y in irange(1900, 1978) if not is_leap(y))
      
      # look for viable mother -> children maps
      kids = defaultdict(list)
      for (m, c) in subsets(years, size=2):
        k = c - m
        if 16 < k < 31:
          kids[m].append(c)
      
      # choose a year for M's mother
      for (MM, Ms) in kids.items():
        # all other years must have a prime difference to MM
        ys1 = set(y for y in years if abs(y - MM) in primes)
      
        # find a viable birth year for M
        for M in Ms:
          if not (M in ys1): continue
      
          # and for his father
          for MF in ys1:
            k = M - MF
            if not (k > 15 and k in primes): continue
            # all other years must also have a prime difference to MF
            ys2 = set(y for y in ys1 if abs(y - MF) in primes).difference([M, MF])
      
            # now find a year for N
            for N in ys2:
              ks = sorted(ys2.intersection(kids.get(N, [])))
              if not ks: continue
      
              # no multiple births, so (0, 1, 2) children in each year
              for ns in subsets((0, 1, 2), size=len(ks), select="M"):
                k = sum(ns)
                if k < 2: continue
                # ages
                ages = [MF, MM, M, N]
                for (n, v) in zip(ns, ks):
                  if n: ages.extend([v] * n)
                t = sum(ages)
                # exactly one of the birth years is the mean year
                m = div(t, len(ages))
                if m is None or ages.count(m) != 1: continue
      
                # output solution
                printf("MF={MF} MM={MM} -> M={M}; N={N} -> {k} kids; {ages} -> {m}")
      

      Solution: Noelle was born in 1943. She has 4 children.

      Michael’s father was born in 1900 (which was not a leap year), and his mother in 1902.

      Michael was born in 1931 (prime differences of 31 and 29 to his father and mother), and his mother was 28 or 29 when he was born.

      Noelle was born in 1943 (prime differences of 43 and 41 to Michael’s father and mother), towards the end of the year.

      Her four children were born in 1961 (one towards the beginning of the year, when Noelle was 17, and one towards the end of the year, when she was 17 or 18), and in 1973 (one towards the beginning of the year, when Noelle was 29, and one towards the end of the year, when she was still 29).

      Like

  • Unknown's avatar

    Jim Randell 9:14 am on 26 December 2021 Permalink | Reply
    Tags:   

    A Brain-Teaser: [Square convoy] 

    From The Sunday Times, 2nd January 1949 [link]

    Four ships in convoy are at the corners of a square with sides one mile long. Ship B is due north of A; C is due east of B, and D is due south of C. The four ships are each sailing north at 10 miles per hour. A motor-boat, which travels at 26 miles per hour, starts at A, delivers a message to B, then to C, then to D, and finally returns to A. The distances between the ships are traversed by the shortest possible routes, and no allowance need be made for time taken in turning.

    How far does the convoy advance whilst the motor-boat is going round?

    This is earliest of the occasional Holiday Brain Teasers published in The Sunday Times prior to the start of numbered Teasers in 1961 that I have found. Prizes of 10 guineas and 5 guineas were awarded.

    This puzzle was originally published with no title.

    [teaser-1949-01-02] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 9:16 am on 26 December 2021 Permalink | Reply

      (1) A to B:

      The position of the motorboat is given by: 26t

      And the position of B is given by: (1 + 10t)

      These coincide when:

      26t = 1 + 10t
      16t = 1
      t = 1/16

      (2) B to C:

      The motorboat travels along the hypotenuse of a right-angled triangle, a distance: 26t.

      And C travels along another side, a distance: 10t.

      The remaining side being of length 1.

      (26t)² = (10t)² + 1²
      676t² = 100t² + 1
      t² = 1/576
      t = 1/24

      (3) C to D:

      The motorboat’s position is: −26t.

      D’s position is: −1 + 10t.

      −26t = −1 + 10t
      t = 1/36

      (4) D to A:

      A mirror image of B to C.

      So the total time taken is:

      T = 1/16 + 1/24 + 1/36 + 1/24
      T = 25/144

      And the distance travelled by each boat in the fleet:

      D = 10T
      D = 125/72
      D = 1.736 miles
      D = 110000 inches = 1 mile, 1295 yards, 20 inches

      Solution: The distance travelled by the fleet was 1 mile, 1295 yards, 20 inches.

      Like

  • Unknown's avatar

    Jim Randell 5:53 pm on 23 December 2021 Permalink | Reply
    Tags: ,   

    Teaser 3092: A Christmas Carol 

    From The Sunday Times, 26th December 2021 [link] [link]

    A Christmas Carol was published on 19/12/1843, when Bob Cratchit was in his forties, almost seven years after Jacob Marley’s death on Christmas Eve. On Marley’s last Christmas Day, a working day for them all as always, Scrooge said to Cratchit and Marley: “We three will work the same dates next year as this year, except that I’ll cover Cratchit’s birthday so he can have the day off, since Marley never works on his”. On Boxing Day, Scrooge decided to allocate extra work dates for next year to anyone whose number of work dates was going to be below the average of the three of them, bringing them up to exactly that average. Daily, up to and including New Year’s Eve, Scrooge repeated this levelling-up to the new average, never needing fractions of a day.

    What was Bob Cratchit’s full date of birth?

    In the book The Sunday Times Teaser Book 2 (2023), the “levelling-up” process is described as follows:

    … Scrooge decided to allocate extra work dates for the next year to the one whose number of work dates was going to be below the average of the three of them, bringing him up to exactly that average. Daily, up to and including New Year’s Eve, Scrooge repeated this levelling-up for that person to the new average, never needing fractions of a day.

    Which is intended to indicate that only one of the three is “levelled-up” each time the process is applied.

    [teaser3092]

     
    • Jim Randell's avatar

      Jim Randell 5:55 pm on 23 December 2021 Permalink | Reply

      I’m not sure I understand how this is meant to work as a puzzle.

      If Cratchit is in his forties on 1843-12-19, then he was born sometime between 1794 and 1803.

      And, if Marley died on 1836-12-24, his last Christmas Day would be 1835-12-25, so Scrooge was planning the dates for 1836.

      All we know for sure (for the initial schedule) is that they all work every Christmas Day, Marley never works on his birthday, and Scrooge will work on Cratchit’s birthday in 1836 but Cratchit won’t.

      So the number of possible workdays initially scheduled for 1836 is: Scrooge 2-366, Cratchit 1-365, Marley 1-365.

      I found 3429 initial assignments of numbers of work days in 1836 that would allow Scrooge to perform his procedure 6 times.

      For example if the initial number of allocated work days is (99, 268, 308) we have:

      26th: (99, 268, 308), mean = 225 → (225, 268, 308)
      27th: (225, 268, 308), mean = 267 → (267, 268, 308)
      28th: (267, 268, 308), mean = 281 → (281, 281, 308)
      29th: (281, 281, 308), mean = 290 → (290, 290, 308)
      30th: (290, 290, 308), mean = 296 → (296, 296, 308)
      31st: (296, 296, 308), mean = 300 → (300, 300, 308)

      But there doesn’t seem to be any specified conditions to allow us to narrow down these numbers to a set that would allow us to determine a specific date for Cratchit’s birthday.

      However, there is a particular date that suggests itself as the only one of the candidate birthdates that has a certain property, so maybe that is the required answer. And it is the same unique date that we get if we make certain assumptions about the behaviour of Scrooge.

      Solution: [See below]

      Like

      • Jim Randell's avatar

        Jim Randell 10:22 am on 1 January 2022 Permalink | Reply

        The published puzzle text is flawed.

        Apparently the setter intended that only one of the three would be “levelled up” each time the process is applied.

        This leads to a single levelling up process:

        26th: (1, 365, 366), mean = 244 → (244, 365, 366)
        27th: (244, 365, 366), mean = 325 → (325, 365, 366)
        28th: (325, 365, 366), mean = 352 → (352, 365, 366)
        29th: (352, 365, 366), mean = 361 → (361, 365, 366)
        30th: (361, 365, 366), mean = 364 → (364, 365, 366)
        31st: (364, 365, 366), mean = 365 → (365, 365, 366)

        So, before the “levelling up” process started the work days allocated were 1, 365, 366.

        Scrooge must be 366, as he is working an extra day compared to 1835, and there were 365 days in 1835. The extra day worked is the leap day in 1836, and this must also be Cratchit’s birthday.

        Cratchit must be 365, as Marley did not work on his birthday in 1835.

        Hence, Marley must be the 1. He only worked Christmas day.

        So the intended answer was:

        Solution: Bob Cratchit was born on 29th February 1796.

        And this was the answer I suspected, as it was the only date with a certain property in the required range (i.e. the only 29th February), without worrying about the “levelling up” stuff at all.

        Like

  • Unknown's avatar

    Jim Randell 7:30 am on 23 December 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 891: Ups and downs 

    From The Sunday Times, 3rd September 1978 [link]

    An electrician living in a block of flats has played a joke on the tenants by rewiring the lift. The buttons numbered 0 to 9 in the lift should correspond to the ground floor, first floor, etc., but he has rewired them so that although (for his own convenience) the buttons for the ground floor and his own floor work correctly, no other button takes you to its correct floor. Indeed when you get in the lift on the ground floor and go up, three of the buttons take you twice as high as they should, and two buttons take you only half as high as they should.

    The milkman is unaware of the rewiring and so early yesterday morning, rather bleary-eyed, he followed his usual ritual which consists of taking nine pints of milk into the lift, pushing button 9, leaving one pint of milk when the lift stops, pushing button 8, leaving one pint of milk when the lift stops, and so on in decreasing order until, having pushed button and having left his last pint, he usually returns to his van.

    However, yesterday when he tried to follow this procedure all seemed to go well until, having pushed button 1 , when the lift stopped he found a pint of milk already there. So he took the remaining pint back to his van, with the result that just one of the tenants (who lived on one of the floors below the electrician’s) did not get the pint of milk she’d expected.

    The surprising thing was that during the milkman’s ups and downs yesterday he at no time travelled right past the floor which he thought at that time he was heading towards.

    List the floors which received milk, in the order in which the milkman visited them.

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser891]

     
    • Jim Randell's avatar

      Jim Randell 7:32 am on 23 December 2021 Permalink | Reply

      The electrician makes a journey by selecting buttons 9, 8, 7, …, 3, 2, 1, and in the process visits one of the floors twice and one of the floors no times. And the floor that is not visited is a lower number than the electrician’s floor (which is the only button that gives the correct floor).

      I assumed there are exactly 3 buttons that go to floor exactly twice the number on the button, and exactly 2 that go to a floor exactly half the number on the button.

      This Python 3 program chooses the buttons that go to floors twice and half their labelled values, and then fills out the rest of the mapping as it goes along. It runs in 53ms.

      Run: [ @replit ]

      from enigma import (irange, update, product, subsets, diff, singleton, trim, map2str, printf)
      
      # consider a journey made with buttons <bs>, visiting floors <fs> using map <m> (button -> floor)
      def journey(bs, fs, m):
        # are we done?
        if not bs:
          yield (fs, m)
        else:
          (b, f_) = (bs[0], fs[-1])
          # is the button assigned?
          f = m.get(b, None)
          if f is None:
            (ss, upd) = (irange(0, 9), 1)
          else:
            (ss, upd) = ([f], 0)
          # choose a target floor
          for f in ss:
            # if unassigned: cannot be the right floor, or twice or half
            if upd and (f == b or f == 2 * b or b == 2 * f): continue
            # cannot be a previously visited floor, except for the final floor
            if (f in fs) != (len(bs) == 1): continue
            # journey cannot pass the correct floor
            if not (f_ < b < f or f_ > b > f):
              m_ = (update(m, [(b, f)]) if upd else m)
              yield from journey(bs[1:], fs + [f], m_)
      
      # exactly 3 buttons go to twice the number, exactly 2 buttons go to half the number
      for (ds, hs) in product(subsets([1, 2, 3, 4], size=3), subsets([2, 4, 6, 8], size=2)):
        # remaining floors
        rs = diff(irange(1, 9), ds + hs)
        if len(rs) != 4: continue
      
        # choose the floor for the electrician
        for e in rs:
          if e == 1: continue
      
          # initial map
          m0 = { 0: 0, e: e }
          m0.update((k, 2 * k) for k in ds) # doubles
          m0.update((k, k // 2) for k in hs) # halfs
      
          # consider possible journeys for the milkman
          for (fs, m) in journey([9, 8, 7, 6, 5, 4, 3, 2, 1], [0], m0):
      
            # the unvisited floor is lower than e
            u = singleton(x for x in irange(1, 9) if x not in fs)
            if u is None or not (u < e): continue
      
            # output solution
            printf("{fs} {m}", fs=trim(fs, head=1, tail=1), m=map2str(m, arr='->'))
      

      Solution: The milkman visited floors: 7, 4, 2, 3, 5, 8, 6, 9.

      He then returns to floor 2, and finds he has already visited.

      The rewired map of button → floor is:

      0 → 0
      1 → 2 (twice)
      2 → 9
      3 → 6 (twice)
      4 → 8 (twice)
      5 → 5 (electrician’s floor)
      6 → 3 (half)
      7 → 2
      8 → 4 (half)
      9 → 7

      Like

  • Unknown's avatar

    Jim Randell 9:59 am on 21 December 2021 Permalink | Reply
    Tags:   

    Teaser 2849: Swift tailor 

    From The Sunday Times, 30th April 2017 [link] [link]

    When measuring gentlemen’s chest sizes in inches, the tailors five foot long tape overlapped so that the set of numbers 1 to 9 was aligned with a consecutive set of higher numbers.

    Taking each pair of these nine aligned lower and higher numbers as a fraction the tailor saw that just two of the nine “fractions” were in their simplest form and did not cancel down (i.e. the pair of numbers had no common factor greater than one).

    All of this was also true when he measured the smaller waist size.

    What (in inches) were the gentleman’s chest and waist sizes?

    [teaser2849]

     
    • Jim Randell's avatar

      Jim Randell 10:00 am on 21 December 2021 Permalink | Reply

      Assuming the tape measure is graduated 1″ … 60″.

      The smallest possible measurement is: 1/10 … 9/18 corresponding to a measurement of 9″ (although that is ludicrously small). And the largest is: 1/52 … 9/60 (corresponding to 51″).

      This Python program runs in 46ms.

      Run: [ @replit ]

      from enigma import (irange, is_coprime, printf)
      
      # consider possible measurements
      for m in irange(9, 51):
        # count how many pairs are co-prime
        pairs = ((i, m + i) for i in irange(1, 9))
        ps = list(p for p in pairs if is_coprime(*p))
        # we are looking for measurements with 2 co-prime pairs
        if len(ps) == 2:
          printf("m={m} {ps}")
      

      Solution: The measurements are: chest = 42″; waist = 30″.

      When the measurement is 30″ we get the following lowest term fractions: 1/31, 7/37.

      When the measurement is 42″ we get the following lowest term fractions: 1/43, 5/47.

      Like

    • Hugh+Casement's avatar

      Hugh+Casement 1:12 pm on 21 December 2021 Permalink | Reply

      That works out at 76 and 107 cm: a fine figure of a man!
      Somehow I find 84 and 90 cm more realistic.

      Like

  • Unknown's avatar

    Jim Randell 10:13 am on 19 December 2021 Permalink | Reply
    Tags: by: Mr. A. C. Jolliffe   

    A Brain Teaser: Rose beds 

    From The Sunday Times, 25th December 1955 [link]

    In my garden I had, until recently, two square rose beds of different sizes. At my wife’s suggestion I modified the layout, making two square plots of equal size but leaving the area devoted to roses unchanged.

    My two next-door neighbours, as it happened, were also the possessors of square rose beds — each had three, one of a certain size with two equal beds of another size, the dimensions being different for the two neighbours. They, admiring the change in my garden, decided to follow suit. Each now has three square beds of equal size, but each has kept the same area as before devoted to roses. We have discovered that all our eight rose beds are now the same size, also that the side of every rose bed was and is an integral number of feet.

    Given that the final beds are of the smallest possible size fulfilling the conditions, how large were our original beds?

    This is one of the occasional Holiday Brain Teasers published in The Sunday Times prior to the start of numbered Teasers in 1961. Three 1 guinea book tokens were offered for the first 3 correct solutions.

    [teaser-1955-12-25] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 10:13 am on 19 December 2021 Permalink | Reply

      I’m assuming “leaving the area devoted to roses unchanged”, means the total area of the plots after the change is the same as it was before.

      So equating the setters before and after areas we get:

      a² + b² = 2x²

      And using the same value of x for the neighbours we have:

      a² + 2b² = 3x²

      This program finds the smallest possible shared value of x, and the corresponding (a, b) values. It runs in 49ms.

      Run: [ @replit ]

      from enigma import (irange, inf, sq, is_square, printf)
      
      # find solutions for a^2 + k.b^2 = n
      def solve(k, n):
        for b in irange(1, inf):
          a2 = n - k * sq(b)
          if not (a2 > 0): break
          a = is_square(a2)
          if a is None: continue
          yield (a, b)
      
      # consider the final size
      for x in irange(1, inf):
      
        # find: a^2 + b^2 = 2x^2
        ss1 = list((a, b) for (a, b) in solve(1, 2 * sq(x)) if a < b)
        if not ss1: continue
      
        # find: a^2 + 2b^2 = 3x^2
        ss2 = list((a, b) for (a, b) in solve(2, 3 * sq(x)) if a != b)
        if not (len(ss2) > 1): continue
      
        # output solution
        printf("x={x}; ss1={ss1} ss2={ss2}")
        break
      

      Solution: The original beds were: setter = 7×7 + 23×23; neighbours = 25×25 + 2(11×11), 23×23 + 2(13×13).

      The size of the final beds is: 17×17 (the setter has 2, and each of the neighbours has 3).

      Like

  • Unknown's avatar

    Jim Randell 4:34 pm on 17 December 2021 Permalink | Reply
    Tags:   

    Teaser 3091: Birthday money 

    From The Sunday Times, 19th December 2021 [link] [link]

    My two daughters were born on the same day but seven years apart. Every birthday, with only one year’s exception, I have given them both five pounds for each year of their age. They are now grown-up, but I have continued to do this on their birthdays, except for the one year when I couldn’t afford to give either of them anything. Averaged out over all of their birthdays, including the one for which they received nothing, my elder daughter has now received 21 per cent more per birthday than her younger sister.

    How much in total have I given to my daughters as birthday presents?

    [teaser3091]

     
    • Jim Randell's avatar

      Jim Randell 5:08 pm on 17 December 2021 Permalink | Reply

      This Python program finds the solution constructively. It runs in 52ms.

      Run: [ @replit ]

      from enigma import (Rational, irange, unzip, printf)
      
      Q = Rational()
      
      # look for solutions
      def solve():
        # initial ages
        ages = [7, 0]
      
        # collect gifts (newest first)
        gifts = list(list(5 * x for x in irange(age, 1, step=-1)) for age in ages)
        totals = list(sum(x) for x in gifts)
      
        while True:
          # add in data for the next year
          for (i, _) in enumerate(ages):
            ages[i] += 1
            v = 5 * ages[i]
            gifts[i].insert(0, v)
            totals[i] += v
      
          # consider missing years
          for ms in unzip(gifts):
            # calculate averages without missing year
            avgs = list(Q(t - m, a) for (t, m, a) in zip(totals, ms, ages))
      
            # look for avgs[0] = 121% of avgs[1]
            if 100 * avgs[0] == 121 * avgs[1]:
              yield (ages, totals, ms)
      
      # find the first solution
      for (ages, totals, ms) in solve():
        t = sum(totals) - sum(ms)
        printf("total = {t}; ages = {ages}; missing={ms}")
        break
      

      Solution: The total of the gifts is £ 6660.

      The daughters are currently aged 40 and 33, and the missing amounts are £ 140 and £ 105, when the daughters were 28 and 21.

      In total the elder daughter has received £ 3960 (an average of £ 99 per year), and the younger has received £ 2700 (an average of £ 81 + 9/11 per year).

      And we have:

      121% of (81 + 9/11)
      = (121/100) × (900/11)
      = 99


      Analytically we see that the £ 5 multiplier cancels out and we are looking for values of:

      (T(n + 7) − (k + 7)) / (n + 7) = (121/100) (T(n) − k) / n

      where: n is the age of the younger daughter, and k ∈ [1, n] is her age in the missing year.

      This simplifies to:

      k = (3n² − 76n − 479)n / (6n + 242)

      And we can then just look for the first n that gives a viable value for k:

      from enigma import (irange, inf, div, T, printf)
      
      # consider age of youngest daughter
      for n in irange(1, inf):
        # calculate missing year
        k = div(((3 * n - 76) * n - 479) * n, 6 * n + 242)
        if not (k is None or k < 0 or k > n):
          # output solution
          t1 = T(n) - k
          t2 = T(n + 7) - (k + 7)
          t = 5 * (t1 + t2)
          printf("[n={n} k={k}] total = {t}; ages = ({n+7}, {n})")
          break
      

      Further analysis shows that in order for k to be positive we require: n > (38 + √2881) / 3 ≈ 30.56.

      And for k ≤ n we require: n ≤ 103/3 ≈ 34.33.

      So we only need to check k ∈ [31, 34], which we can do manually.

      n = 31 ⇒ k = 3.48…
      n = 32 ⇒ k = 11.87…
      n = 33 ⇒ k = 21
      n = 34 ⇒ k = 30.87…

      Like

    • GeoffR's avatar

      GeoffR 9:12 am on 18 December 2021 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Assume max daughters ages are 63 and 70 years and father is in his nineties.
      % Max UB amount - 1st daughter = 63 * 64 div 2 * 5 = 10080, say 10100
      % Max UB amount - 2nd daughter = 70 * 71 div 2 * 5 = 12425, say 12500
      
      var 1..63:D1; % max 1st daughter's age
      var 1..70:D2; % max 2nd daughter's age
      var 1..63: MA; % missed age year
      
      % Total gift amounts for two daughters
      var 5..10100: D1_tot;
      var 5..12500: D2_tot;
      var 10..22600: Total;
      
      % Elder daughter is 7 years older than her sister
      constraint D2 == D1 + 7;
      
      % Total gift amounts, allowing for the missing age year
      constraint D1_tot == 5 * D1 * (D1 + 1) div 2 - 5 * MA;
      constraint D2_tot == 5 * D2 * (D2 + 1) div 2 - 5 * (MA + 7);
      constraint Total == D1_tot + D2_tot;
      
      % Elder daughter received 21 per cent more per birthday than her sister
      % (D2_tot/D2) / (D1_tot/D1) = 121/100
      constraint 100 * D2_tot * D1 == 121 * D1_tot * D2;
      
      solve satisfy;
      
      output ["Total (£) = " ++ show(Total) ++ "\n" ++ "Daughter's ages are " ++ 
      show(D1) ++ " and " ++ show(D2) ++ " years"]; 
      
      
      
      

      Like

    • NigelR's avatar

      NigelR 3:23 pm on 19 December 2021 Permalink | Reply

      apologies Jim – I haven’t worked out how to post code properly. Is there a guide somewhere, please?

      acum,bcum=140,0  #140 is accumulated gifts for daughter 1 for birthdays 1-7
      for y in range(8,90): #start from 1st birthday for daughter 2
          acum+=5*y
          bcum+=5*(y-7)
          for i in range (8,y):  #iterate over previous years to remove 1 year at a time
              if abs(((acum-5*i)/y)-1.21*((bcum-(5*(i-7)))/(y-7)))<0.0001:
                  print("total gifted is:",(acum-5*i)+(bcum-5*(i-7)))
      

      Like

      • Jim Randell's avatar

        Jim Randell 3:44 pm on 19 December 2021 Permalink | Reply

        Hi,

        You can include code like this:

        [code language="python"]
        your code here
        [/code]
        

        For longer programs (80 lines for more) you can also include the collapse="true" parameter, to hide it until clicked on.

        Be careful though,it is not possible to edit a comment in WordPress once it is posted.

        Like

    • Tony Brooke-Taylor's avatar

      Tony Brooke-Taylor 7:31 am on 20 December 2021 Permalink | Reply

      With a bit of rearrangement of a formula for the ratio of averages you can pretty much solve this manually. Once you have the limits you can almost intuit the correct value for the younger daughter’s age but the program below loops until it finds a solution.

      # Find real roots of a quadratic equation with conventional coefficients a, b, c
      def quad_roots(a, b, c):
          r = (b ** 2 - 4 * a * c)
          if r >= 0:
              low = (-b - r**(1/2))/2/a
              up = (-b + r**(1/2))/2/a
              return low, up
      
      
      # Define missing_year as the age of the younger daughter when the gift was missed
      # Make this the subject of the equation formed from taking the ratio of averages given
      
      # The lower bound for the younger age can be inferred from the condition that missing_year exceeds 0
      low_lim = max([int(r) for r in quad_roots(3, -76, -479) if r > 0]) + 1
      # The upper bound for the younger age can be inferred from the condition that it exceeds missing_year
      up_lim = max([int(r) for r in quad_roots(3, -(76 + 6), -(479 + 242)) if r > 0]) + 1
      
      # Find the value for the age now of the younger daughter, such that missing_year is an integer
      for young_age, missing_year in ((n, (3 * n ** 2 - 76 * n - 479) / (6 * n + 242) * n) for n in range(low_lim, up_lim)):
          if missing_year % 1 == 0:
              total = 0
              for d in [0, 7]:
                  total += ((young_age + d) * ((young_age + d)+1)/2 - (missing_year + d))
              print("Total given to both daughters is", total * 5)
              break
      
      

      Like

c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel
Design a site like this with WordPress.com
Get started