Updates from December, 2022 Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 11:05 am on 18 December 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 409: House number 

    From The Sunday Times, 9th March 1969 [link]

    I live in a long street in which the houses are all on one side numbered consecutively.

    If I add my house number to the sum of a certain number of consecutive house numbers on my immediate left, the answer is equal to the sum of the same number of consecutive house numbers on my immediate right.

    Moreover, if I add the square of my house number to the sum of the squares of a different number of consecutive house numbers on my immediate left, the result is equal to the sum of the squares of the same number of consecutive house numbers on my immediate right.

    I do not live at number 12, and perhaps I should add that my house number is less than 14,000.

    What is it?

    This puzzle is included in the book Sunday Times Brain Teasers (1974).

    [teaser409]

     
    • Jim Randell's avatar

      Jim Randell 11:13 am on 18 December 2022 Permalink | Reply

      Here is a constructive solution.

      For each possible house number value n, it look for left/right sequences that give equal sums (the left sequence includes n and the right sequence does not).

      It runs in 223ms.

      Run: [ @replit ]

      from enigma import (irange, identity, sq, printf)
      
      # find the size of left/right sequences with equal sums
      # return the size of the right sequence (left is 1 more)
      def check(n, fn=identity):
        (sl, sr, a, b) = (fn(n), 0, n, n)
        while sl > sr and a > 1:
          a -= 1
          b += 1
          sl += fn(a)
          sr += fn(b)
        if sl == sr: return (b - n)
      
      # consider values for n
      for n in irange(1, 13999):
        # check for sum
        k1 = check(n)
        if not k1: continue
        # check for sum of squares
        k2 = check(n, sq)
        if not k2: continue
        # output solution
        printf("n={n}: k1={k1} k2={k2}")
      

      Solution: The house number is: 420.

      And we have:

      sum([400 .. 420]) = sum([421 .. 440]) = 8610
      sumsq([406 .. 420]) = sumsq([421 .. 434]) = 2558815


      With some analysis we can have a faster program.

      If we consider the setter at house n, and the k consecutive houses immediately before and after him we have:

      [(n − k) + (n − k + 1) + … + (n − 1) + n] = [(n + 1) + (n + 2) + … + (n + k)]

      There are (k + 1) terms of the left and k terms on the right, so matching them up pairwise, except the n term on the left, we get:

      n = [(n + 1) − (n − k)] + [(n + 2) − (n − k + 1)] + … + [(n + k) − (n − 1)]
      n = (k + 1) + (k + 1) + … + (k + 1)
      n = k(k + 1)

      So the house number n is the product of 2 consecutive integers.

      We have a similar situation with the squares of the numbers:

      [(n − m)² + (n − k + 1)² + … + (n − 1)² + n] = [(n + 1)² + (n + 2)² + … + (n + m)²]

      Which we can pair up like this:

      n² = [(n + 1)² − (n − 1)²] + [(n + 2)² − (n − 2)²] + … + [(n + m)² − (n − m)²]
      n² = (4n) + (8n) + … + (4mn)
      n² = 4n(1 + 2 + … + m)
      n = 2m(m + 1)

      So the house number n is also twice the product of 2 consecutive integers.

      The following Python program finds the required answer using this analysis, and also verifies that the sums of the sequences are equal.

      It runs in 59ms. (Internal runtime is 317µs).

      Run: [ @replit ]

      from enigma import (irange, inf, sumsq, printf)
      
      # verify left/right sequences
      def verify(ls, rs, fn):
        # construct the sequences
        (ls, rs) = (list(ls), list(rs))
        # and their sums
        (sl, sr) = (fn(ls), fn(rs))
        # output results
        printf("-> [{l0}..{l1}] = {sl}; [{r0}..{r1}] = {sr}", l0=ls[0], l1=ls[-1], r0=rs[0], r1=rs[-1])
        if sl != sr: printf("=> FAIL")
      
      # collect values: k -> k * (k + 1)
      d = dict()
      for k in irange(1, inf):
        n = k * (k + 1)
        if not (n < 14000): break
        # have we seen half this value?
        m = d.get(n // 2)
        if m is not None:
          printf("n={n}: k={k} m={m}")
          # output solution
          verify(irange(n - k, n), irange(n + 1, n + k), sum)
          verify(irange(n - m, n), irange(n + 1, n + m), sumsq)
      
        # record this value
        d[n] = k
      

      And we find the same two candidate solutions:

      12 = 3×4 = 2×(2×3)
      420 = 20×21 = 2×(14×15)

      The next two candidates would be:

      14280 = 119×120 = 2×(84×85)
      485112 = 696×697 = 2×(492×493)

      Which is OEIS A098602 [ @oeis.org ].

      And can be generated using the following recurrence relation:

      >>> fn = unpack(lambda a, b, c: 35 * (c - b) + a)
      >>> first(fib(0, 12, 420, fn=fn), 8)
      [0, 12, 420, 14280, 485112, 16479540, 559819260, 19017375312]
      

      Like

      • John Crabtree's avatar

        John Crabtree 3:26 am on 21 December 2022 Permalink | Reply

        k and m are related by a negative form of the Pell equation, ie
        (2k+1)² – 2(2m+1)² = -1
        with values of (2k+1, 2m+1) = (1, 1), (7, 5), (41, 29), (239, 169), (1393, 985) etc

        Like

      • Jim Randell's avatar

        Jim Randell 3:57 pm on 1 October 2025 Permalink | Reply

        The house number n is the product of 2 consecutive integers, and also twice the product of a different 2 consecutive integers:

        n = x(x + 1)
        n = 2y(y + 1)

        x(x + 1) = 2y(y + 1)

        setting: X = 2x + 1, Y = 2y + 1, we get:

        (X − 1)(X + 1) = 2(Y − 1)(Y + 1)
        X² − 1 = 2.Y² − 2
        X² − 2.Y² = −1

        (Which is the Pell equation noted by John Crabtree).

        We can then use the [[ diop_quad() ]] solver from the pells.py library to solve this:

        from enigma import (div, printf)
        import pells
        
        # solve X^2 - 2.Y^2 = -1
        for (X, Y) in pells.diop_quad(1, -2, -1):
          # X = 2x + 1, Y = 2y + 1
          (x, y) = (div(X - 1, 2), div(Y - 1, 2))
          if x is None or y is None: continue
          # calculate the house number (= x(x + 1) = 2y(y + 1))
          n = x * (x + 1)
          if n < 1 or n == 12: continue
          if not (n < 14000): break
          # output solution
          printf("n={n} [X={X} Y={Y}; x={x} y={y}]")
        

        Solutions to the Pell equation are generated by the following recurrence relation:

        (X, Y) = (1, 1)
        (X’, Y’) = (3X + 4Y, 2X + 3Y)

        Like

  • Unknown's avatar

    Jim Randell 4:26 pm on 16 December 2022 Permalink | Reply
    Tags:   

    Teaser 3143: Pipe fittings 

    From The Sunday Times, 18th December 2022 [link] [link]

    A plumber had three thin metal pipes with square, rectangular and elliptical cross-sections. In order to fit them into his van, he slid the rectangular pipe inside the elliptical pipe and the elliptical pipe inside the square pipe, before placing the pipe assembly in the van. There are four points where the pipes all touch, as shown in the diagram. The maximum and minimum widths of the elliptical and rectangular pipes and the diagonal width of the square pipe were all even numbers of mm less than 1000, of which one was a perfect square.

    What were the five widths (in increasing order)?

    [teaser3143]

     
    • Jim Randell's avatar

      Jim Randell 5:20 pm on 16 December 2022 Permalink | Reply

      If we consider the diagram to be centred on the origin (0, 0), and the point where all four figures meet in the (+, +) quadrant is (x, y), then the rectangle has dimensions (2x, 2y) and the diagonal of the square is 2z where z = x + y.

      And if the ellipse has semi-major axis a and semi-minor axis b, then the equation of the tangent at the point (p, q) is:

      x(p/a²) + y(q/b²) = 1

      and this is the same as the line that defines the side of the square tube:

      x + y = z

      Hence:

      a² = x.z
      b² = y.z

      Assuming exactly one of the required widths is a perfect square gives a unique answer to the puzzle.

      This Python program runs in 134ms. (Internal runtime is 76ms).

      Run: [ @replit ]

      from enigma import (irange, decompose, is_square, icount_exactly as icount, printf)
      
      # choose a value for z
      for z in irange(5, 499):
        # consider values for x and y
        for (y, x) in decompose(z, 2, increasing=1, sep=1, min_v=1):
          # calculate values for a and b
          a = is_square(x * z)
          b = is_square(y * z)
          if a and b:
            # calculate the widths (in increasing order)
            ws = sorted(2 * n for n in (a, b, x, y, z))
            # check exactly one of the widths is a perfect square
            if icount(ws, is_square, 1):
              # output solution
              printf("ws={ws} [z={z} x={x} y={y} a={a} b={b}]")
      

      Solution: The widths are (in mm): 180, 300, 320, 400, 500.


      Swapping [[ icount_exactly() ]] for [[ icount_at_least() ]] reveals 4 further solutions, so it is necessary to assume exactly one of the widths is a perfect square.

      Solutions with at least one perfect square are:

      (36, 60, 64, 80, 100) [3]
      (144, 240, 256, 320, 400) [3]
      (180, 300, 320, 400, 500) [1]
      (100, 260, 576, 624, 676) [3]
      (324, 540, 576, 720, 900) [3]

      Like

      • Jim Randell's avatar

        Jim Randell 6:09 pm on 16 December 2022 Permalink | Reply

        Or we can use the [[ pythagorean_triples() ]] function from the enigma.py library to get a more efficient program.

        This Python program runs in 58ms. (Internal runtime is 777µs).

        Run: [ @replit ]

        from enigma import (pythagorean_triples, div, sq, is_square, icount_exactly as icount, printf)
        
        # generate values for a, b, z, st: a^2 + b^2 = z^2
        for (b, a, z) in pythagorean_triples(499):
          # calculate x and y
          (x, y) = (div(sq(n), z) for n in (a, b))
          if x is None or y is None: continue
          # calculate the widths (in increasing order)
          ws = sorted(2 * n for n in (a, b, x, y, z))
          # check exactly one of the widths is a perfect square
          if icount(ws, is_square, 1):
            # output solution
            printf("ws={ws} [z={z} a={a} b={b} x={x} y={y}]")
        

        Like

    • GeoffR's avatar

      GeoffR 4:51 pm on 17 December 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % x, y = rectangle dimensions and a,b ellipse semi major/minor axes
      var 4..498:x; var 4..498:y;
      var 4..498:a; var 4..498:b;
      var 4..498:z;
      
      constraint a > b /\ z = x + y;
      
      % Using Jim's derivation and named variables
      constraint a * a == x * z /\ b * b == y * z;
      
      predicate is_sq(var int: m) =
      exists(n in 1..ceil(sqrt(int2float(ub(m))))) (n*n = m );
      
      constraint sum([is_sq(2*a), is_sq(2*b), is_sq(2*x),
       is_sq(2*y), is_sq(2*z)]) == 1;
      
      solve satisfy;
      
      output ["Five widths [2a, 2b, 2x, 2y, 2z ] = " ++ 
      show([2*a, 2*b, 2*x, 2*y, 2*z])];
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:56 am on 15 December 2022 Permalink | Reply
    Tags: ,   

    Teaser 2686: Good innings 

    From The Sunday Times, 16th March 2014 [link] [link]

    Tomorrow is St Patrick’s Day, so Pat will go on his usual tour of inns. Last year each inn had the same admission charge (made up of a two-figure number of pounds plus a two-figure number of pence surcharge). This four-figure total of pence happened to be the same as the four-figure number of pence Pat started the evening with, except that the order of the digits was reversed. In the first inn he spent one pound less than a third of what he had left after paying admission. In the second inn he also spent one pound less than a third of what he came in with after paying admission. That left him just enough to get into the third inn.

    How much was that?

    [teaser2686]

     
    • Jim Randell's avatar

      Jim Randell 9:57 am on 15 December 2022 Permalink | Reply

      The entry fee is a 4-digit number of pence, and Pat’s initial funds are the reverse of this number (also a 4-digit number).

      As Pat pays 3 entry fees, each one cannot be more than £33.33, so we don’t need to check entry fees more than that.

      Run: [ @replit ]

      from enigma import (irange, nreverse, as_int, ediv, catch, printf)
      
      # check for viable spending pattern
      def check(entry):
        # starting amount is the reverse of the entry fee
        funds = start = nreverse(entry)
        # first pub
        funds = as_int(funds - entry, "+")
        spend1 = as_int(ediv(funds, 3) - 100, "+")
        funds = as_int(funds - spend1, "+")
        # second pub
        funds = as_int(funds - entry, "+")
        spend2 = as_int(ediv(funds, 3) - 100, "+")
        funds = as_int(funds - spend2, "+")
        # third pub
        funds = as_int(funds - entry, "0")
        # output solution
        printf("entry = {entry}; start = {start}; spend1 = {spend1}; spend2 = {spend2}")
      
      # consider 4-digit entry fees
      for entry in irange(1000, 3333):
        catch(check, entry)
      

      Solution: The entry fee was: £14.56.

      So Pat’s finances go:

      start = £65.41
      pub 1 entry = £14.56 → £50.85
      pub 1 spend = £15.95 → £34.90
      pub 2 entry = £14.56 → £20.34
      pub 2 spend = £5.78 → £14.56
      pub 3 entry = £14.56 → £0.00

      Like

  • Unknown's avatar

    Jim Randell 1:03 pm on 13 December 2022 Permalink | Reply
    Tags:   

    Teaser 2001: Unfamiliar territory 

    From The Sunday Times, 21st January 2001 [link]

    I was forced to make an emergency landing somewhere in Latin America. I got out of the plane, crossed a terrain interrupted by a higgledy-piggledy coating of identical bright marble cubes and found myself in a town. I learnt that the citizens had 10 different characters for digits. I then spotted what I took to be the plinth for a statue; it consisted of six layers. I learnt that each layer was made up of one solid square of cubes like those I had seen earlier. Beside this edifice was what I took to be an explanatory notice; included in it was the following, which I was told was a list of the number of cubes in each successive layer:

    ICNGTC
    EEAGTR
    RTGSEG
    IIIGNAE
    ERGINNO
    RAIGGSS

    My informant told me I should be able to work out the value of each character.

    “Thank you for that”, I said, “but can you tell me the name of your country?”

    “Why yes”, he replied, “you are now in 86997 321542387

    Where did I make my forced landing?

    Teaser 2001 was originally published in the year 2001. It is the only Teaser puzzle to have this property.

    [teaser2001]

     
    • Jim Randell's avatar

      Jim Randell 1:04 pm on 13 December 2022 Permalink | Reply

      We can use the [[ SubstitutedExpression() ]] solver from the enigma.py library to find the assignments of letters to digits, and then use the [[ translate() ]] function to decode the required answer.

      This Python program runs in 68ms. (Internal runtime is 17.9ms).

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, sprintf as f, translate, printf)
      
      # the following are encoded square numbers
      sqs = "ICNGTC EEAGTR RTGSEG IIIGNAE ERGINNO RAIGGSS".split()
      
      # construct the alphametic puzzle
      p = SubstitutedExpression(list(f("is_square_p({x})") for x in sqs))
      
      # solve it
      for s in p.solve(verbose=0):
        # map digits (as strings) to letters
        r = dict((str(v), k) for (k, v) in s.items())
        # decode the phrase
        t = translate("86997 321542387", r)
        # output the solution
        printf("answer = {t}")
      

      Solution: The plane landed in: TERRA INCOGNITA.

      And the squares are:

      ICNGTC = 312481 = 559²
      EEAGTR = 667489 = 817²
      RTGSEG = 984064 = 992²
      IIIGNAE = 3334276 = 1826²
      ERGINNO = 6943225 = 2635²
      RAIGGSS = 9734400 = 3120²

      The answer published with Teaser 2003 says: “TERRA INCOGNITO (sic)”, although clearly each word of the answer must end with the same letter.

      Like

    • GeoffR's avatar

      GeoffR 5:33 pm on 13 December 2022 Permalink | Reply

      
      % A Solution in MiniZinc 
      include "globals.mzn";
      
      var 1..9:I; var 0..9:C; var 0..9:N; var 0..9:G; var 0..9:T; 
      var 1..9:E; var 0..9:A; var 1..9:R; var 0..9:S; var 0..9:O; 
      
      constraint all_different ([I, C, N, G, T, E, A, R, S, O]);
      
      predicate is_sq(var int: y) =
      exists(z in 1..ceil(sqrt(int2float(ub(y))))) (z*z = y );
      
      function var int: num6 (var int: u, var int: v, var int: w, var int: x, 
      var int: y, var int: z) = 100000*u + 10000*v + 1000*w + 100*x + 10*y + z;
      
      function var int: num7 (var int: t,var int: u, var int: v, var int: w, var int: x, 
      var int: y, var int: z) = 1000000*t + 100000*u + 10000*v + 1000*w + 100*x + 10*y + z;
      
      % Three 6-digit squares
      var 100000..999999:ICNGTC = num6(I, C, N, G, T, C);
      var 100000..999999:EEAGTR = num6(E, E, A, G, T, R);
      var 100000..999999:RTGSEG = num6(R, T, G, S, E, G);
      constraint sum([is_sq(ICNGTC), is_sq(EEAGTR), is_sq(RTGSEG)]) == 3;
      
      % Three 7-digit squares
      var 1000000..9999999:IIIGNAE = num7(I, I, I, G, N, A ,E);
      var 1000000..9999999:ERGINNO = num7(E, R, G, I, N, N, O);
      var 1000000..9999999:RAIGGSS = num7(R, A, I, G, G, S, S);
      constraint sum([is_sq(IIIGNAE), is_sq(ERGINNO), is_sq(RAIGGSS)]) == 3;
      
      solve satisfy;
      
      output[ "[I,C,N,G,T,E,A,R,S,O] = " ++ show([I,C,N,G,T,E,A,R,S,O]) ];
      
      % [I, C, N, G, T, E, A, R, S, O] = 
      % [3, 1, 2, 4, 8, 6, 7, 9, 0, 5]
      % ----------
      % ==========
      % Landing Location is [8,6,9,9,7  3,2,1,5,4,2,3,8,7]
      %      -  which means [T E R R A  I N C O G N I T A]
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 12:29 pm on 14 December 2022 Permalink | Reply

      A two-stage solution in Python. It finds the digits / letters representation first. It then forms a dictionary to translate the landing code, using the ‘translate’ function from the enigma.py library.

      
      from itertools import permutations
      from enigma import is_square, translate
      
      for p1 in permutations('1234567890', 5):
          I, C, N, G, T = p1
          if I == '0':continue
      
          # Three 6-digit squares
          ICNGTC = int(I + C + N + G + T + C)
          if not is_square(ICNGTC): continue
          q1 = set('1234567890').difference([I, C, N, G, T])
          for p2 in permutations(q1):
              E, A, R, S, O = p2
              if '0' in (E, R): continue
              EEAGTR = int(E + E + A + G + T + R)
              if not is_square(EEAGTR):continue
              RTGSEG = int(R + T + G + S + E + G)
              if not is_square(RTGSEG):continue
      
              # Three 7-digit squares
              IIIGNAE = int(I + I + I + G + N + A + E)
              if not is_square(IIIGNAE):continue
              ERGINNO = int(E + R + G + I + N + N + O)
              if not is_square(ERGINNO):continue
              RAIGGSS = int(R + A + I + G + G + S + S)
              if not is_square(RAIGGSS):continue
              
              # output letters/digits - for a dictionary
              print(f"'I'={I}, 'C'={C}, 'N'={N}, 'G'={G}, 'T'={T}")
              print(f"'E'={E}, 'A'={A}, 'R'={R}, 'S'={S}, 'O'={O}")
              # 'I'=3, 'C'=1, 'N'=2, 'G'=4, 'T'=8
              # 'E'=6, 'A'=7, 'R'=9, 'S'=0, 'O'=5
      
      # 2nd stage - use output to form a dictionary - digits to letters
      DL = { '1': 'C', '2': 'N', '3': 'I', '4': 'G', '5': 'O', \
             '6': 'E', '7': 'A', '8': 'T', '9': 'R', '0': 'S'}
      
      # Translate landing code
      t = translate("86997 321542387", DL)
      print(f"Answer = {t}")
      # Answer = TERRA INCOGNITA
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:35 am on 11 December 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 257: Brothers and sisters 

    From The Sunday Times, 3rd April 1966 [link]

    The boys and girls of a primary school have recently done a survey among themselves. They find that half the children who have brothers are girls who have no sisters, and that half the children who have sisters are girls who have no brothers. Boys who have no sisters are equal in number to boys who have sisters but not brothers. In all, there are fourteen more children with sisters than there are children with brothers.

    How many of the boys have neither brothers nor sisters?

    This puzzle is included in the book Sunday Times Brain Teasers (1974).

    [teaser257]

     
    • Jim Randell's avatar

      Jim Randell 9:35 am on 11 December 2022 Permalink | Reply

      Each child fits into one of the following 8 categories (boy/girl; brother?; sister?):

      a = boy; no brothers; no sisters
      b = boy; brother; no sisters
      c = boy; brother; sister
      d = boy; no brother; sister
      e = girl; no brothers; no sisters
      f = girl; brother; no sisters
      g = girl; brother; sister
      h = girl; no brother; sister

      We are told:

      [1] “half the children who have brothers are girls who have no sisters”
      (b + c + f + g) / 2 = f
      ⇒ b + c + f + g = 2f
      ⇒ b + c + g = f

      [2] “half the children who have sisters are girls who have no brothers”
      (c + d + g + h) / 2 = h
      ⇒ c + d + g + h = 2h
      ⇒ c + d + g = h

      [3] “boys who have no sisters are equal in number to boys who have sisters but not brothers”
      a + b = d
      ⇒ a = d − b

      [4] “there are fourteen more children with sisters than there are children with brothers”
      (c + d + g + h) = 14 + (b + c + f + g)

      And we want to find the value of a:

      Substituting for (c + d + g + h) from [2] and (b + c + f + g) from [1] into [4]:
      (c + d + g + h) = 14 + (b + c + f + g)
      2h = 14 + 2f
      ⇒ h − f = 7

      [2] − [1]:
      (c + d + g) − (b + c + g) = h − f
      ⇒ d − b = h − f

      Hence:
      a = d − b = h − f = 7

      Solution: 7 of the boys have neither brothers or sisters.

      Like

    • GeoffR's avatar

      GeoffR 1:07 pm on 12 December 2022 Permalink | Reply

      I used Jim’s initial equations to explore possible values of variables (a..h)
      Variable ‘e’ is not used in any of the equations, so it can take any value (0..10)

      It looks as though only the following values are fixed:

      a = boy; no brothers; no sisters (a = 7)
      d = boy; no brother; sister (d = 7)

      Multiple outputs showed that all values, apart from a and d, are not constrained in value by the equations. Value h can increase above 7 by varying other values.

      One example below shows that the four equations are still satisfied for this example:

      [a, b, c, d, e, f, g, h] = [7, 0, 0, 7, 4, 2, 2, 9]

      Equation 1: 0 + 0 + 2 + 2 = 2 * 2
      Equation 2: 0 + 7 + 2 + 9 = 2 * 9
      Equation 3: 7 + 0 = 7
      Equation 4: (0 + 7 + 2 + 9) = 14 + (0 + 0 + 2 + 2)

      
      % A Solution in MiniZinc 
      include "globals.mzn";
      
      % boy/girl categories, as above
      var 0..10:a; var 0..10:b; var 0..10:c; var 0..10:d;
      var 0..10:e; var 0..10:f; var 0..10:g; var 0..10:h;
      
      % [1] “half the children who have brothers are girls who have no sisters”
      constraint b + c + f + g == 2 * f;
      
      % [2] “half the children who have sisters are girls who have no brothers”
      constraint c + d + g + h == 2 * h;
      
      % [3] “boys who have no sisters are equal in number to boys who have sisters but not brothers”
      constraint a + b == d;
      
      % [4] “there are fourteen more children with sisters than there are children with brothers”
      constraint (c + d + g + h) = 14 + (b + c + f + g);
      
      solve satisfy;
      
      output ["[a, b, c, d, e, f, g, h] = " ++ show([a, b, c, d, e, f, g, h]) ];
      
      % [a, b, c, d, e, f, g, h] = 
      % [7, 0, 0, 7, 0, 0, 0, 7]
      % ----------
      % Many other solutions are possible.
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 10:20 am on 13 December 2022 Permalink | Reply

        @Geoff:

        As you say e can be any value.

        And we can also choose any value for b and c, and any value for f that not less than (b + c).

        We then have:

        a = 7
        d = b + 7
        g = f − (b + c)
        h = f + 7

        So: a = 7; d ≥ 7; h ≥ 7.

        Like

  • Unknown's avatar

    Jim Randell 4:17 pm on 9 December 2022 Permalink | Reply
    Tags:   

    Teaser 3142: Mrs Hyde’s garden design tips 

    From The Sunday Times, 11th December 2022 [link] [link]

    I’m planting four differently-coloured plots in a line. Mrs Hyde’s design principles classify eight colours into five categories. Adjacent plots mustn’t have the same category, or even certain pairs of categories.

    “Every scheme you’ve suggested has at least one adjacent pair of categories that breaks my rules”, said Mrs Hyde. “White, Green, Yellow, White has Honest and Social adjacent; Blue, Orange, Pink, Violet has Ardent and Social adjacent; Green, Violet, Blue, Orange has Ardent and Honest adjacent; Violet, White, Orange, Red has Droll and Honest adjacent; and White, Violet, Green, Blue has Jolly and Social adjacent. However, if you change one colour in one of your suggestions then it will work”.

    What are the four colours in order in the changed suggestion that would be allowable?

    [teaser3142]

     
    • Jim Randell's avatar

      Jim Randell 4:55 pm on 9 December 2022 Permalink | Reply

      Here is my first attempt. It is not particularly quick, but I’ll have another look at this puzzle later.

      The program maps the different colours to the categories and checks that each of the given arrangements provides (at least) the corresponding objection. One we have a viable map we change one of the colours in one of the given arrangements and look for new arrangements that do not give rise to any of the objections raised previously.

      This Python program runs in 866ms.

      Run: [ @replit ]

      from enigma import (subsets, tuples, update, wrap, uniq, map2str, printf)
      
      # colours and categories
      cols = "BGOPRVWY"
      cats = "ADHJS"
      
      # invalid arrangements and objections
      inv = {
        'WGYW': 'HS',
        'BOPV': 'AS',
        'GVBO': 'AH',
        'VWOR': 'DH',
        'WVGB': 'JS',
      }
      
      # banned adjacencies
      banned = set(frozenset([x]) for x in cats)
      banned.update(map(frozenset, inv.values()))
      
      # check each of the arrangements provides the required objection
      def check(m):
        # check each of the arrangements
        for (k, v) in inv.items():
          adj = set(v)
          if not any({m[x], m[y]} == adj for (x, y) in tuples(k, 2)): return False
        # looks OK
        return True
      
      @wrap(uniq)
      # change one of the arrangements
      def change(ks):
        for k in ks:
          for (i, x) in enumerate(k):
            # make a changed arrangement
            for y in cols:
              if y != x:
                yield update(k, [(i, y)])
      
      # map colours to categories
      for ss in subsets(cats, size=len(cols), select="M"):
        m = dict(zip(cols, ss))
        # each category must be represented
        if not set(m.values()).issuperset(cats): continue
        # check the given arrangements are objectionable
        if not check(m): continue
      
        # find a changed arrangement that is not objectionable
        for s in change(inv.keys()):
          # look for banned adjacencies in the new arrangement
          if not any(frozenset([m[x], m[y]]) in banned for (x, y) in tuples(s, 2)):
            # output solution
            printf("{s} [{m}]", m=map2str(m, sep=" ", enc=""))
      

      Solution: The allowable arrangement is: White, Red, Green, Blue.

      The colour assignments are:

      Blue → Ardent
      Green → Jolly
      Orange → Honest
      Pink → Ardent
      Red → Droll
      Violet → Social
      White → Social
      Yellow → Honest

      And so the given arrangements (and their objections) are:

      White (S); Green (J); Yellow (H); White (S) → S and J adjacent; H and S adjacent
      Blue (A); Orange (H); Pink (A); Violet (S) → A and H adjacent (twice); A and S adjacent
      Green (J); Violet (S); Blue (A); Orange (H) → A and S adjacent; A and H adjacent
      Violet (S); White (S); Orange (H); Red (D) → S and S adjacent; S and H adjacent; D and H adjacent
      White (S); Violet (S); Green (J); Blue (A) → S and S adjacent; J and S adjacent;

      However, swapping Violet for Red in the final arrangement gives:

      White (S); Red (D); Green (J); Blue (A)

      Which does not give any of the objections we have discovered so far.

      Like

      • Jim Randell's avatar

        Jim Randell 6:25 pm on 9 December 2022 Permalink | Reply

        Here is a much faster version of my program (and not much longer). It uses the [[ SubstitutedExpression() ]] solver from the enigma.py library to assign the colours to the categories, such that the objections for each arrangement applies.

        It runs in 57ms. (Internal runtime is 5.6ms).

        Run: [ @replit ]

        from enigma import (SubstitutedExpression, wrap, uniq, update, tuples, map2str, printf)
        
        # colours and categories
        cols = "BGOPRVWY"
        cats = "ADHJS"
        
        # invalid arrangements and objections
        inv = {
          'WGYW': 'HS',
          'BOPV': 'AS',
          'GVBO': 'AH',
          'VWOR': 'DH',
          'WVGB': 'JS',
        }
        
        # map categories to digits 1-5
        d = dict(A=1, D=2, H=3, J=4, S=5)
        
        # construct an alphametic puzzle to assign categories to the colours
        p = SubstitutedExpression(
          [
            # WGYW has H and S adjacent
            "{3, 5} in [{W, G}, {G, Y}, {Y, W}]",
            # BOPV has A and S adjacent
            "{1, 5} in [{B, O}, {O, P}, {P, V}]",
            # GVBO has A and H adjacent
            "{1, 3} in [{G, V}, {V, B}, {B, O}]",
            # VWOR has D and H adjacent
            "{2, 3} in [{V, W}, {W, O}, {O, R}]",
            # WVGB has J and S adjacent
            "{4, 5} in [{W, V}, {V, G}, {G, B}]",
          ],
          digits=(1, 2, 3, 4, 5),
          distinct=(),
        )
        
        # set of banned adjacencies
        banned = set(frozenset([d[x]]) for x in cats)
        banned.update(frozenset([d[x], d[y]]) for (x, y) in inv.values())
        
        @wrap(uniq)
        # change one of the arrangements
        def change(ks):
          for k in ks:
            for (i, x) in enumerate(k):
              # make a changed arrangement
              for y in cols:
                if y != x:
                  yield update(k, [(i, y)])
        
        # solve the alphametic puzzle to map colours to categories
        for m in p.solve(verbose=0):
          # find a changed arrangement that is not objectionable
          for s in change(inv.keys()):
            # look for banned adjacencies in the new arrangement
            if not any(frozenset([m[x], m[y]]) in banned for (x, y) in tuples(s, 2)):
              # output solution
              printf("{s} [{m}]", m=map2str(m, sep=" ", enc=""))
        

        Like

  • Unknown's avatar

    Jim Randell 9:19 am on 8 December 2022 Permalink | Reply
    Tags: ,   

    Teaser 2694: Impaired 

    From The Sunday Times, 11th May 2014 [link] [link]

    Pat was given a rod of length 40 centimetres and he cut it into six pieces, each piece being a whole number of centimetres long, and with the longest piece twice as long as one of the other pieces. Then he took two of the pieces and wrote down their total length. He did this for every possible set of two pieces and he never got the same total more than once. However, when he started again and repeated the process but with sets of three pieces, one of the totals was repeated.

    What (in increasing order) were the lengths of his pieces?

    [teaser2694]

     
    • Jim Randell's avatar

      Jim Randell 9:20 am on 8 December 2022 Permalink | Reply

      Each of the pieces must be of a different length. Otherwise there will be duplication amongst the 2-subsets.

      This Python program runs in 52ms. (Internal runtime is 533µs).

      Run: [ @replit ]

      from enigma import (
        irange, inf, decompose, subsets, seq_all_different as all_different, printf
      )
      
      # totals of k-subsets of ps
      subs = lambda ps, k: (sum(ss) for ss in subsets(ps, size=k))
      
      # there are pieces of length x and 2x
      for x in irange(1, inf):
        # remaining length (must allow 4 more pieces)
        r = 40 - 3 * x
        if r < 10: break
        # cut the remaining length into 4 pieces
        for ps in decompose(r, 4, increasing=1, sep=1, min_v=1, max_v=2 * x - 1):
          ps += (x, 2 * x)
      
          # all 2-subsets have different totals
          if not all_different(subs(ps, 2)): continue
      
          # but there are exactly 2 3-subsets that have the same total
          ss = list(subs(ps, 3))
          if len(set(ss)) + 1 != len(ss): continue
      
          # output solution
          printf("ps = {ps}", ps=sorted(ps))
      

      Solution: The lengths of the pieces are (cm): 1, 4, 5, 7, 9, 14.

      This gives two 3-subsets that give a total of 20:

      1 + 5 + 14 = 20
      4 + 7 + 9 = 20

      The set (1, 3, 4, 5, 9, 18) gives no repeated totals for 2-subsets or 3-subsets.

      Like

    • GeoffR's avatar

      GeoffR 8:01 pm on 8 December 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % six lengths of rod - assume max single length = 30cm.
      var 1..30:A; var 1..30:B; var 1..30:C; 
      var 1..30:D; var 1..30:E; var 1..30:F;
      
      constraint all_different([A, B, C, D, E, F]);
      
      % put the rods in increasing size order
      constraint increasing([A, B, C, D, E, F]);
      
      % initial rod is 40 cm long
      constraint A + B + C + D + E + F == 40;
      
      % the longest rod is twice as long as one of the other rods
      constraint F == 2*E \/ F == 2*D \/ F == 2*C \/ F == 2*B \/ F == 2*A;   
      
      % sub-totals for 2 rods (15 no.)-  UB = 30 + 29 = 59
      var 3..59: t1; var 3..59: t2; var 3..59: t3; var 3..59: t4; var 3..59: t5;
      var 3..59: t6; var 3..59: t7; var 3..59: t8; var 3..59: t9; var 3..59: t10;
      var 3..59: t11; var 3..59: t12; var 3..59: t13; var 3..59: t14; var 3..59: t15;
      
      % All two rod sums
      constraint t1 = A + B /\ t2 = A + C /\ t3 = A + D /\ t4 = A + E
      /\ t5 = A + F /\ t6 = B + C /\ t7 = B + D /\ t8 = B + E 
      /\ t9 = B + F /\ t10 = C + D /\ t11 = C + E /\ t12 = C + F
      /\ t13 = D + E /\ t14 = D + F /\ t15 = E + F;
      
      % All 2 rod sums are different
      constraint all_different([t1, t2, t3, t4, t5, t6, t7, t8, t9, t10,
      t11, t12, t13, t14, t15]);
      
      % sub-totals for 3 rods (20 no.)-  UB = 30+29+28 = 87
      var 6..87:T1; var 6..87:T2; var 6..87:T3; var 6..87:T4; 
      var 6..87:T5; var 6..87:T6; var 6..87:T7; var 6..87:T8;
      var 6..87:T9; var 6..87:T10; var 6..87:T11; var 6..87:T12;
      var 6..87:T13; var 6..87:T14; var 6..87:T15; var 6..87:T16; 
      var 6..87:T17; var 6..87:T18; var 6..87:T19; var 6..87:T20;
      
      % All 3-digit sums - one of sums is duplicated - see output
      constraint T1 = A+B+C /\ T2 = A+B+D /\ T3 = A+B+E /\ T4 = A+B+F
      /\ T5 = A+C+D /\  T6 = A+C+E /\ T7 = A+C+F /\ T8 = A+D+E
      /\ T9 = A+D+F /\ T10 = A+E+F /\ T11 = B+C+D /\ T12 = B+C+E
      /\ T13 = B+C+F /\ T14 = B+D+E /\ T15 = B+D+F /\ T16 = B+E+F
      /\ T17 = C+D+E /\ T18 = C+D+F /\ T19 = C+E+F /\ T20 = D+E+F;
      
      % array of 3-digit sums
      array[1..20] of var 6..87: y; 
      constraint y = [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,
      T11, T12, T13, T14, T15, T16 ,T17, T18, T19, T20];
      
      % array of 2-digit sums
      array[1..15] of var 3..59: x; 
      constraint x = [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
      t12, t13, t14, t15];
      
      solve satisfy;
      
      output[" Six Rod Pieces are [A,B,C,D,E,F] = " ++ show([A,B,C,D,E,F]) 
      ++ " \n 2-rod length sums: " ++ show(x)
      ++ " \n 3-rod length sums: " ++ show(y)];
      
      % Six Rod Pieces are [A,B,C,D,E,F] = [1, 4, 5, 7, 9, 14]  << ANSWER
      %  2-rod length sums: [5, 6, 8, 10, 15, 9, 11, 13, 18, 12, 14, 19, 16, 21, 23] 
      %  3-rod length sums: [10, 12, 14, 19, 13, 15, 20, 17, 22, 24, 16,
      %  18, 23, 20, 25, 27, 21, 26, 28, 30]
      % ----------
      %  - INVALID, No duplicated 3 rod sums
      %  Six Rod Pieces are [A,B,C,D,E,F] = [1, 3, 4, 5, 9, 18] 
      %  2-rod length sums: [4, 5, 6, 10, 19, 7, 8, 12, 21, 9, 13, 22, 14, 23, 27] 
      %  3-rod length sums: [8, 9, 13, 22, 10, 14, 23, 15, 24, 28, 12, 16,
      %  25, 17, 26, 30, 18, 27, 31, 32]
      % ----------
      % ==========
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:42 am on 6 December 2022 Permalink | Reply
    Tags:   

    Teaser 2690: Leaving present 

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

    When maths teacher Adam Up reached the age of 65, he asked his colleagues for some spring bulbs as a leaving present. So they gave him some packs of bulbs with, appropriately enough, each pack containing 65 bulbs.

    Adam planted all the bulbs in clumps of different sizes, the number of bulbs in each clump being a prime number. Furthermore, these prime numbers overall used each of the ten digits exactly once. Had he been given any fewer packs this would have been impossible.

    How many bulbs were there in the smallest and largest clumps?

    [teaser2690]

     
    • Jim Randell's avatar

      Jim Randell 10:42 am on 6 December 2022 Permalink | Reply

      I implemented a multiset exact cover algorithm [[ mcover() ]] when I revisited Enigma 1712. And the same function can be used to solve this puzzle.

      As we are looking for the smallest number of packs we can start by considering primes up to 65, to see if the problem can be solved using a single pack, and if not we can then add primes between 66 and 130 into the mix, and so on until we find a number of packs that provides solutions.

      The following Python program runs in 56ms. (Internal run time is 3.7ms).

      Run: [ @replit ]

      from enigma import (irange, primes, inf, nsplit, multiset, mcover, printf)
      
      # incremental multiple
      N = 65
      
      # target digit contents (each digit once)
      tgt = multiset.from_seq(irange(0, 9), count=1)
      
      # start with 0 multiples
      k = 0
      m = dict()  # map primes to digit content
      for p in primes.irange(2, inf):
        # have we finished a chunk?
        k_ = p // N
        if k_ > k:
          k = k_
          kN = k * N
          # look for covers using this collection of primes
          n = 0
          for ss in mcover(m, tgt, (lambda ss: sum(ss) > kN)):
            # check for required sum
            if sum(ss) == kN:
              # output solution
              printf("{ss} -> {k} * {N}", ss=sorted(ss))
              n += 1
          # are we done?
          if n > 0:
            printf("[{n} solutions]")
            break
      
        # add primes that are subsets of the target into the list
        ds = multiset.from_seq(nsplit(p))
        if ds.issubset(tgt):
          m[p] = ds
      

      Solution: The smallest clump had 5 bulbs. The largest clump had 401 bulbs.

      There are 2 ways to achieve the solution with 9 packs of 65 bulbs (= 585 bulbs in total):

      5 + 29 + 67 + 83 + 401 = 585
      5 + 23 + 67 + 89 + 401 = 585

      Like

    • GeoffR's avatar

      GeoffR 7:02 pm on 6 December 2022 Permalink | Reply

      The only way I could find a solution in MiniZinc was to assume the ten digit pattern of the primes i.e. 1,2,2,2,3. There has to be at least one 3-digit prime with zero as the middle digit.

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Using a 1,2,2,2,3 digit prime pattern
      
      var 1..9:A; var 1..9:B; var 1..9:C; var 1..9:D;
      var 1..9:E; var 1..9:F; var 1..9:G; var 1..9:H;
      var 0..9:I; var 1..9:J;
      
      var 10..99:BC = 10*B + C;
      var 10..99:DE = 10*D + E;
      var 10..99:FG = 10*F + G;
      var 100..999:HIJ = 100*H + 10*I + J;
      
      predicate is_prime(var int: x) = 
      x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x)))))
      ((i < x) -> (x mod i > 0));
      
      constraint sum([is_prime(A), is_prime(BC), is_prime(DE), 
      is_prime(FG), is_prime(HIJ)]) == 5;
      
      constraint card( {A,B,C,D,E,F,G,H,I,J}) == 10;
      % There are 65 bulbs in each pack
      constraint (A + BC + DE + FG + HIJ) mod 65 == 0;
      
      constraint increasing([A,BC,DE,FG,HIJ]);
      
      solve satisfy;
      
      output ["Smallest clump = " ++ show(A) ++ 
      " , Largest clump = " ++ show(HIJ) ++
      "\n" ++ "[A,BC,DE,FG,HIJ] = " ++ show([A,BC,DE,FG,HIJ] )];
      
      % Smallest clump = 5 , Largest clump = 401
      % [A,BC,DE,FG,HIJ] = [5, 29, 67, 83, 401]
      % ----------
      % Smallest clump = 5 , Largest clump = 401
      % [A,BC,DE,FG,HIJ] = [5, 23, 67, 89, 401]
      % ----------
      % ==========
      
      
      

      Out of interest I then adapted the program to check a different prime digit pattern, which was 2,2,3,3 for the ten digits. All the answers used 18 packs of 65 bulbs, exactly double the original solution using 9 packs of 65 bulbs = 585

      % [AB,CD,EFG,HIJ] for different 10-digits pattern
      % [AB,CD,EFG,HIJ] = [43, 61, 257, 809]
      % [AB,CD,EFG,HIJ] = [53, 89, 421, 607]
      % [AB,CD,EFG,HIJ] = [59, 83, 421, 607]
      % [AB,CD,EFG,HIJ] = [53, 67, 241, 809]
      % [AB,CD,EFG,HIJ] = [43, 67, 251, 809]
      % [AB,CD,EFG,HIJ] = [29, 83, 457, 601]
      % [AB,CD,EFG,HIJ] = [23, 89, 457, 601]
      % [AB,CD,EFG,HIJ] = [29, 53, 487, 601]
      % [AB,CD,EFG,HIJ] = [23, 59, 487, 601]
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:51 am on 4 December 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 252: Blocks 

    From The Sunday Times, 27th February 1966 [link]

    “See these eleven blocks?”, says a so-called friend. “Four of them of 8 inch thickness, two of them of 4 inch thickness, three of 3 inch and two of 1 inch thickness”.

    “Pile them in a column 51 inches high with a 3 inch block at the bottom so that, remaining in position, individual blocks or combinations of adjacent blocks can be used to measure every thickness in exact inches from 1 inch to 48 inches”.

    In what order do they stand?

    This puzzle is included in the book Sunday Times Brain Teasers (1974).

    There are now 790 Teaser puzzles available on the site. And this accounts for 25% of all the Teaser puzzles published (to date).

    [teaser252]

     
    • Jim Randell's avatar

      Jim Randell 8:54 am on 4 December 2022 Permalink | Reply

      (See also: Teaser 119, Teaser 560).

      This Python program performs an exhaustive search of all possible stacks of blocks.

      It runs in 268ms.

      Run: [ @replit ]

      from enigma import (multiset, subsets, csum, irange, printf)
      
      # the blocks
      blocks = multiset.from_dict({8: 4, 4: 2, 3: 3, 1: 2})
      assert blocks.sum() == 51
      
      # extract a 3 block
      blocks.remove(3)
      
      # and consider possible arrangements of the remaining blocks
      for bs in subsets(blocks, size=len, select="mP", fn=list):
        bs.insert(0, 3)
        # find what thicknesses can be measured
        ts = set(b - a for (a, b) in subsets(csum(bs, empty=1), size=2))
        # can we measure all values from 1 to 48
        if all(x in ts for x in irange(1, 48)):
          printf("{bs}")
      

      Solution: The pile of blocks (bottom to top) is one of the following two sequences:

      (3, 4, 3, 8, 8, 8, 8, 4, 1, 1, 3)
      (3, 1, 1, 4, 8, 8, 8, 8, 3, 4, 3)

      One being the reverse of the other.

      Like

  • Unknown's avatar

    Jim Randell 4:35 pm on 2 December 2022 Permalink | Reply
    Tags:   

    Teaser 3141: Multiple extensions 

    From The Sunday Times, 4th December 2022 [link] [link]

    All the phone extensions at Mick’s work place are four-digit numbers with no zeros or twos, and no digit appears more than once in a number. He can enter all the extension numbers on the keypad by starting at key 2 (but not pressing it) then moving in any direction, including diagonally, to an adjacent key and pressing it; then similarly moving to and pressing adjacent keys until the number is entered. A couple of examples of his extension numbers are 3685 and 5148.

    He phoned two different extension numbers this morning and later realised that one was an exact multiple of the other.

    What was the larger extension number he dialled?

    [teaser3141]

     
    • Jim Randell's avatar

      Jim Randell 4:54 pm on 2 December 2022 Permalink | Reply

      The following Python program runs in 58ms. (Internal run time is 6.2ms).

      Run: [ @replit ]

      from enigma import (nconcat, div, ordered, printf)
      
      # adjacency matrix
      adj = {
        1: [2, 4, 5],
        2: [1, 3, 4, 5, 6],
        3: [2, 5, 6],
        4: [1, 2, 5, 7, 8],
        5: [1, 2, 3, 4, 6, 7, 8, 9],
        6: [2, 3, 5, 8, 9],
        7: [4, 5, 8],
        8: [4, 5, 6, 7, 9],
        9: [5, 6, 8],
      }
      
      # generate k-length numbers with no repeated digits
      def solve(k, ds):
        if k == 0:
          yield ds
        else:
          for d in adj[ds[-1]]:
            if d not in ds:
              yield from solve(k - 1, ds + [d])
      
      # start at 2, and dial 4 more digits
      ns = list()
      for ds in solve(4, [2]):
        n = nconcat(ds[1:])
        # look for previous numbers that pair with this one
        for m in ns:
          (x, y) = ordered(m, n)
          k = div(y, x)
          if k:
            printf("{y} = {k} * {x}")
        # add in the new number
        ns.append(n)
      

      (Or a more efficient (but longer) variation on this code [@replit]).

      Solution: The larger number was: 4758.

      And the smaller number was: 1586.

      4758 = 3 × 1586

      Like

  • Unknown's avatar

    Jim Randell 8:58 am on 1 December 2022 Permalink | Reply
    Tags:   

    Teaser 2689: The right choice 

    From The Sunday Times, 6th April 2014 [link] [link]

    I am organising a tombola for the fete. From a large sheet of card (identical on both sides) I have cut out a lot of triangles of equal area. All of their angles are whole numbers of degrees and no angle exceeds ninety degrees. I have included all possible triangles with those properties and no two of them are identical. At the tombola entrants will pick a triangle at random and they will win if their triangle has a right-angle. The chances of winning turn out to be one in a certain whole number.

    What is that whole number?

    [teaser2689]

     
    • Jim Randell's avatar

      Jim Randell 8:59 am on 1 December 2022 Permalink | Reply

      Once we have chosen the three angles of the triangle (each an integer between 1° and 90°, that together sum 180°), we can form a triangle with the required area by scaling it appropriately.

      This Python program counts the number of possible triangles and the number of them that are right angled.

      It runs in 53ms. (Internal runtime is 1.0ms).

      Run: [ @replit ]

      from enigma import (decompose, fraction, printf)
      
      # generate possible triangles
      triangles = lambda: decompose(180, 3, increasing=1, sep=0, min_v=1, max_v=90)
      
      # count:
      # t = total number of triangles
      # r = number with a right angle
      r = t = 0
      for (a, b, c) in triangles():
        t += 1
        if c == 90: r += 1
      
      # output solution
      (p, q) = fraction(r, t)
      printf("P(win) = {r}/{t} = {p}/{q}")
      

      Solution: The chance of winning is 1/16.

      There are 720 triangles, and 45 of them are right angled.

      I think it would be annoying to choose a triangle with an 89° angle.

      Like

  • Unknown's avatar

    Jim Randell 8:28 am on 29 November 2022 Permalink | Reply
    Tags:   

    Teaser 2685: Not the Gold Cup 

    From The Sunday Times, 9th March 2014 [link] [link]

    Five horses took part in a race, but they all failed to finish, one falling at each of the first five fences. Dave (riding Egg Nog) lasted longer than Bill whose horse fell at the second fence; Big Gun fell at the third, and the jockey wearing mauve lasted the longest. Long Gone lasted longer than the horse ridden by the jockey in yellow, Chris’s horse fell one fence later than the horse ridden by the jockey in green, but Fred and his friend (the jockey in blue) did not fall at adjacent fences. Nig Nag was ridden by Wally and Dragon’s jockey wore red.

    Who was the jockey in yellow, and which horse did he ride?

    [teaser2685]

     
    • Jim Randell's avatar

      Jim Randell 8:28 am on 29 November 2022 Permalink | Reply

      I used the [[ SubstitutedExpression ]] solver from the enigma.py library to assign the fence values (1-5) to the 3 groups of jockeys, horses and colours.

      The following run file executes in 66ms. (Internal runtime of the generated program is 94µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      # assign fence numbers (1-5) to the following groups:
      #
      #  jockeys = A (Wally), B (Bob), C (Chris), D (Dave), E (Fred)
      #  horses  = F (Egg Nog), G (Big Gun), H (Long Gone), I (Nig Nag), J (Dragon)
      #  colours = K (mauve), L (yellow), M (green), N (blue), P (red)
      
      SubstitutedExpression
      
      --base="6"
      --digits="1-5"
      --distinct="ABCDE,FGHIJ,KLMNP"
      --template="(A B C D E) (F G H I J) (K L M N P)"
      --solution=""
      
      # "Dave (D) riding Egg Nog (F) lasted longer than Bill (B) who fell at the 2nd fence"
      "D = F"
      "D > B"
      "2 = B"
      
      # "Big Gun (G) fell at the 3rd"
      "3 = G"
      
      # "mauve (K) lasted the longest"
      "5 = K"
      
      # "Long Gone (H) lasted longer than yellow (L)
      "H > L"
      
      # "Chris's (C's) horse fell one fence later than the horse ridden by green (M)"
      "M + 1 = C"
      
      # "Fred (E) and his friend blue (N) did not fall at adjacent fences"
      "abs(E - N) > 1"
      
      # "Nig Nag (I) was ridden by Wally (A)"
      "I = A"
      
      # "Dragon's (J's) jockey wore red"
      "J = P"
      

      We can wrap this in Python code to format the solution in a more friendly form:

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, group, printf)
      
      # load the run file
      p = SubstitutedExpression.from_file("teaser2685.run")
      
      # map symbols to jockey, horse, colour names
      name = dict(
        A="Wally", B="Bob", C="Chris", D="Dave", E="Fred",
        F="Egg Nog", G="Big Gun", H="Long Gone", I="Nig Nag", J="Dragon",
        K="mauve", L="yellow", M="green", N="blue", P="red",
      )
      
      # solve the puzzle
      for s in p.solve(verbose=0):
        # group symbols by value
        d = group(s.keys(), by=s.get)
        # output solution for each fence
        for k in sorted(d.keys()):
          # extract jockey, horse, colour
          (j, h, c) = (name[s] for s in sorted(d[k]))
          printf("{k}: {j} ({c}) on {h}")
        printf()
      

      Solution: Fred was in yellow, riding Big Gun.

      The full solution is:

      1st fence: Wally (blue) on Nig Nag
      2nd fence: Bob (red) on Dragon
      3rd fence: Fred (yellow) on Big Gun
      4th fence: Dave (green) on Egg Nog
      5th fence: Chris (mauve) on Long Gone

      Like

  • Unknown's avatar

    Jim Randell 9:47 am on 27 November 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 222: British triangles 

    From The Sunday Times, 25th July 1965 [link]

    British Triangles, naturally, stand on horizontal bases with their points upwards. All their sides, never more than a  sensible 60 inches, and their heights measure an exact number of inches. No B.T. is isosceles or right angled.

    You can often put more than one B.T. on a common base. On a base of 28 inches 8 B.T.s are erected.

    What are their heights?

    This puzzle is included in the book Sunday Times Brain Teasers (1974).

    [teaser222]

     
    • Jim Randell's avatar

      Jim Randell 9:47 am on 27 November 2022 Permalink | Reply

      If the 3 sides of a triangle are a, b, c, then the area A is given by Heron’s formula:

      s = (a + b + c) / 2
      A = √(s(s − a)(s − b)(s − c))

      And if the height (from side a) is h, then we also have:

      A = ha / 2

      Hence the height h can be determined from the sides a, b, c:

      h = 2A / a
      h = (2/a)√(s(s − a)(s − b)(s − c))

      This Python program considers possible values for the 2nd and 3rd sides of the triangle, and then looks for values where the height is an integer.

      It runs in 53ms. (Internal runtime is 1.8ms).

      Run: [ @replit ]

      from enigma import (irange, div, is_square, subsets, sq, printf)
      
      # check for a viable triangle
      def is_triangle(a, b, c):
        # check triangle inequalities
        if not (a + b > c and a + c > b and b + c > a): return
        # no triangle is isosceles
        if len({a, b, c}) < 3: return
        # no triangle is right angled
        sqs = (sq(a), sq(b), sq(c))
        if sum(sqs) == 2 * max(sqs): return
        # looks OK
        return (a, b, c)
      
      # calculate integer height from side a
      def height(a, b, c):
        p = a + b + c
        x = div(p * (p - 2 * a) * (p - 2 * b) * (p - 2 * c), 4)
        if not x: return
        x = is_square(x)
        if not x: return
        h = div(x, a)
        return h
      
      # base
      a = 28
      # consider possible integer length for the remaining sides, b, c
      for (b, c) in subsets(irange(1, 60), size=2, select="R"):
        if not is_triangle(a, b, c): continue
        # calculate the height of the triangle
        h = height(a, b, c)
        if not h: continue
        # output viable triangles
        printf("a={a} b={b} c={c} -> h={h}")
      

      Solution: The heights of the triangles are 9″ (2 triangles), 15″ (4 triangles), 24″ (2 triangles).

      The four triangles found by the program are shown below. And they can be mirrored to produce the remaining four triangles.

      Like

    • Hugh Casement's avatar

      Hugh Casement 7:41 am on 28 November 2022 Permalink | Reply

      Alternatively we can think of it as two right-angled triangles joined together.
      If their bases are d and e, then d² = b² – h² and e² = c² – h².
      d + e, or |d – e| in the case of the obtuse-angled triangles, must equal a = 28.
      b must not equal c, for then the overall triangle would be isosceles
      (that is the case when h = 48, b = c = 50, and d = e = 14).

      Like

      • Hugh Casement's avatar

        Hugh Casement 9:09 am on 28 November 2022 Permalink | Reply

        Joined together along the line of their common height h, of course.
        A base a = 21 also allows eight resultant triangles, including reflections.

        Like

  • Unknown's avatar

    Jim Randell 4:12 pm on 25 November 2022 Permalink | Reply
    Tags:   

    Teaser 3140: Enjoy every minute 

    From The Sunday Times, 27th November 2022 [link] [link]

    On rugby international morning, I found myself, along with eight friends, in a pub 5.8 miles from the match ground. We were enjoying ourselves, and so wished to delay our departure for the ground until the last possible minute. The publican, wishing to keep our custom for as long as possible, offered to help us get there by carrying us, one at a time, as pillion passengers on his motorbike.

    We could walk at 2.5mph and the bike would travel at 30mph. We all left the pub together, and arrived at the ground in time for kick-off.

    Ignoring the time taken getting on and off the bike, what was our minimum travelling time in minutes?

    [teaser3140]

     
    • Jim Randell's avatar

      Jim Randell 4:58 pm on 25 November 2022 Permalink | Reply

      (Curiously this week’s New Scientist back page puzzle is a simpler variation on the problem [link]).

      If the friends just walked from pub to the stadium it would take 2.32 hours.

      If the barman ferried each of them individually between the pub and the stadium it would take 3.29 hours.

      But we can do better.

      If the barman takes the first friend on his motorbike, and the rest of the friends start walking towards the stadium. Then the barman drops the first friend off to walk the remainder of the distance to the stadium and returns to the group (now a short distance from the pub) and ferries the next friend to join the first friend, and so on until he collects the final friend and drops him off at the stadium at the same time that all the other friends arrive, then we can achieve a minimum overall time.

      The only thing to work out is how far before the stadium to drop the first friend, then it is just a matter of ferrying friends from the trailing to the leading group.

      If each of the k friends walks a distance x at velocity w, and travels by motorbike a distance (d − x) at a velocity v, then each journey takes:

      t = x/w + (d − x)/v

      And the total time taken for the barman to ferry them all is:

      t = [(2k − 1)d − 2kx]/v

      Everyone arrives at the stadium at the same time, so:

      x/w + (d − x)/v = [(2k − 1)d − 2kx]/v
      vx + (d − x)w = [(2k − 1)d − 2kx]w
      vx + dw − xw = (2k − 1)dw − 2kxw
      x(v + w(2k − 1)) = dw(2k − 2)
      x = 2dw(k − 1) / (v + w(2k − 1))

      or:

      n = 2k − 1
      x = dw(n − 1) / (v + wn)
      t = d(w + vn) / v(v + wn)

      The answer can then be calculated directly.

      Run: [ @replit ]

      from enigma import (fdiv, printf)
      
      k = 9    # number of people in the group
      d = 5.8  # distance between pub and stadium
      w = 2.5  # walking speed
      v = 30   # motorbike speed
      
      # calculate amount of walking per person
      n = 2 * k - 1
      x = fdiv(d * w * (n - 1), v + w * n)
      
      # calculate time taken
      t = fdiv(x, w) + fdiv(d - x, v)
      
      # output solution
      printf("t={t:g} hours (= {m:g} min) [x={x:g} miles]", m=t * 60)
      

      Solution: The minimum travelling time is 82 minutes.

      We calculate:

      x = 16/5 = 32/10
      t = 32/25 + 13/150 = 41/30 = 82/60

      So the first friend is dropped off 3.2 miles from the stadium (and walks the remainder of the way).

      Each friend walks for 76.8 minutes and is on the motorbike for 5.2 minutes. Giving each a total journey time of 82 minutes.

      Like

      • Jim Randell's avatar

        Jim Randell 10:56 pm on 30 November 2022 Permalink | Reply

        (See also: Enigma 977).

        Here is a numerical approach, based on the barman dropping the first friend off after a distance x (from the pub) while the remaining friends set off on foot.

        The barman then returns to the trailing group and ferries a single friend from the trailing to the leading group until everyone has been transferred to the leading group. And we record the maximal journey time for the friends to give us a total time to get all the friends to the stadium.

        We then use the [[ find_min() ]] function from the enigma.py library to determine at what distance x the shortest total journey time occurs.

        Unsurprisingly this gives us the same answer as the analytical approach above (but with considerably more effort). But it does show that the logic used in the analysis does indeed produce the minimum time.

        Run: [ @replit ]

        from enigma import (irange, fdiv, find_min, printf)
        
        k = 9    # number of people in the group
        d = 5.8  # distance between pub and stadium
        w = 2.5  # walking speed
        v = 30   # motorbike speed
        
        # if: A starts at a, velocity v; B starts at b, velocity w
        # return: (<position>, <time>) of meeting
        def meet(a, v, b, w, t0=0):
          t = fdiv(b - a, v - w)
          return (b + t * w, t0 + t)
        
        # run a scenario where the first person is dropped off at distance x
        # return the time taken for everyone to arrive
        def run(x):
          ts = list()
          # ferry friend 1 to x and let them walk the remaining distance (d - x)
          d1 = x
          t1 = fdiv(x, v)
          # and so the total time for the first friend is ...
          ts.append(t1 + fdiv(d - d1, w))
        
          # the position of the trailing group at time t is:
          tr = lambda t: min(t * w, d)
          # the position of the leading group at time t >= t1 is:
          ld = lambda t, t1=t1: min(x + (t - t1) * w, d)
        
          # ferry the remaining friends ...
          for _ in irange(2, k):
            # we return from d1 to the trailing group
            (d2, t2) = meet(d1, -v, tr(t1), w, t1)
            # and then back to the leading group
            (d3, t3) = meet(d2, +v, ld(t2), w, t2)
            if d3 < d:
              # they walk the remaining distance
              ts.append(t3 + fdiv(d - d3, w))
            else:
              # they are dropped off at the stadium
              (d3, t3) = (d, t2 + fdiv(d - d2, v))
              ts.append(t3)
            (d1, t1) = (d3, t3)
          # return the maximum time
          return max(ts)
        
        # find the minimal time
        r = find_min(run, 0, d)
        (t, x) = (r.fv, r.v)
        printf("min time = {t:g} hours (= {m:g} min) [drop 1 at {x:g} miles]", m=t * 60)
        

        Here is a graph of the total journey time against the distance x that the first friend is taken from the pub. We see that the minimum time is achieved when x = 2.6 miles.

        Like

      • NigelR's avatar

        NigelR 4:29 pm on 1 December 2022 Permalink | Reply

        Hi Jim. I very much enjoyed this puzzle (more for the logic than the Python content!) but I’m intrigued to know how you got to the term 2kx in your second equation. I got to it indirectly by looking at the vector sum of all the motorbike journeys out: [(k)(d-x)] +back: [(k)(d-x)-d] given that he finishes up a distance d from where he started. That then reduces to:
        [(2k-1)d -2kx] but is there a more intuitive way of getting to the second term?

        Like

        • Jim Randell's avatar

          Jim Randell 10:50 pm on 1 December 2022 Permalink | Reply

          @Nigel: The way I thought of it the barman makes a journey towards the stadium for each of the k friends. And in between friends he has to make (k − 1) return journeys towards the pub.

          If he made complete journeys between the pub and the stadium this would be (2k − 1) journeys of distance d for a total of (2k − 1)d.

          But if he made complete journeys he would be travelling over the ground that each friend walks, in both directions, so this amount is overcounting the barmans distance by 2x for each of the k friends.

          So the actual overall distance travelled by the barman is:

          (2k − 1)d − 2kx

          And this actual distance is travelled at velocity v, so we can determine the total time taken for the barman.

          Like

          • NigelR's avatar

            NigelR 9:31 am on 3 December 2022 Permalink | Reply

            Thanks Jim. It was the ‘in both directions’ that I was struggling to get my head around. This week’s puzzle is trivial by comparison!

            Like

    • Hugh Casement's avatar

      Hugh Casement 9:45 am on 4 December 2022 Permalink | Reply

      There were some early Brain-Teasers of a similar kind by Brigadier A.G.H. Brousson (who died in 1976), involving the Smart brothers who took part in races with one bicycle between them.
      Numbers 640, 663, 686, 722, 741, and 792 would presumably yield to a variant of Jim’s analysis as given here. 792 (at least) is flawed because it doesn’t specify that they all arrived together — nor, for that matter, whether riding pillion on the rear carrier is permitted (I know from experience that it seriously upsets the balance and steering!).

      Like

      • Jim Randell's avatar

        Jim Randell 10:53 am on 8 December 2022 Permalink | Reply

        @Hugh: Thanks. I’ll have a look at those. None of them are in the published books I’m working from, so it would have been a while before I got round to looking at them.

        Like

  • Unknown's avatar

    Jim Randell 8:38 am on 24 November 2022 Permalink | Reply
    Tags: by: Dr J C Brown   

    Brain-Teaser 665: … Steady … 

    From The Sunday Times, 7th April 1974 [link]

    There were six starters for the special handicap event for the youngest class at the school sports. All started at the gun, and none fell by the wayside or was disqualified.

    Dave started quicker than Ed and finished before Fred. Colin finished two seconds after he would have finished if he had finished two seconds before Ed. Bob and Dave started quicker than Colin, and Bob was faster than Ed.

    Alf, who won third prize, finished two seconds after he would have finished if he had finished two seconds after Colin.

    Who won second prize?

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

    [teaser665]

     
    • Jim Randell's avatar

      Jim Randell 8:38 am on 24 November 2022 Permalink | Reply

      If the total times are A, B, C, D, E, F. Then we derive:

      D < F
      C = (E − 2) + 2 ⇒ C = E
      B < E
      A = (C + 2) + 2 ⇒ A = C + 4 (and A was 3rd)

      From which we get 2 chains (shortest to longest time):

      B < C,E < A
      D < F

      However we are told that A was in 3rd place, but we see there are at least 3 competitors (B, C, E) who finished in a shorter time, which would imply the best A could have placed was 4th.

      So, assuming positions are ranked according to time, either the puzzle is flawed, or the goal of the race is to achieve the longest time, not the shortest.

      In this latter case the finishing positions (longest to shortest time) are:

      1st: Fred
      2nd: Dave
      3rd: Alf
      4th: Colin, Ed (joint)
      6th: Bob

      Solution: Dave won second prize.

      Like

  • Unknown's avatar

    Jim Randell 9:04 am on 22 November 2022 Permalink | Reply
    Tags:   

    Teaser 2688: Mother’s Day 

    From The Sunday Times, 30th March 2014 [link] [link]

    Today we are having a family get-together to celebrate Mother’s Day. My maternal grandmother, my mother and I have each written down our date of birth in the form “ddmmyy”. This gives us three six-digit numbers and, surprisingly, both of the ladies’ numbers are multiples of mine. Furthermore, all of the digits from 0 to 9 occur somewhere in the three six-digit numbers.

    What is my mother’s six-digit date of birth?

    [teaser2688]

     
    • Jim Randell's avatar

      Jim Randell 9:05 am on 22 November 2022 Permalink | Reply

      At first I found many possible solutions to this puzzle.

      But if we require that the 6-digit numbers formed from the date cannot have a leading zero, then this narrows down the solution space considerably (and essentially restricts the three dates to the form 10xxxx, 20yyyy, 30zzzz, and the multiples being 1, 2, 3).

      This Python program runs in 129ms.

      Run: [ @replit ]

      from datetime import (date, timedelta)
      from enigma import (
        fdiv, inc, repeat, nconcat, nsplit, catch,
        subsets, append, tuples, union, join, printf
      )
      
      # extract date as a 6-digit number
      def number(d):
        if d.day < 10: return None
        return nconcat(d.day, d.month, d.year % 100, base=100)
      
      # check viable generation gap, a -> b
      def gap(a, b):
        y = fdiv((b - a).days, 365.2422)
        return not (y < 16 or y > 50)
      
      # consider dates for the setters birthdate
      for d in repeat(inc(timedelta(days=-1)), date(2014, 3, 30)):
        if d.year < 1922: break
        # construct the number "ddmmyy"
        n = number(d)
        if n is None: continue
        # look for proper multiples that also give a valid date
        mn = n
        ds = list()
        while True:
          mn += n
          if mn > 311299: break
          (dd, mm, yy) = nsplit(mn, base=100)
          for y in (1900 + yy, 1800 + yy):
            if y < 1892: continue
            md = catch(date, y, mm, dd)
            if md is None or number(d) is None: continue
            ds.append(md)
      
        # look for a set of 3 plausible ages
        for ss in subsets(sorted(ds), size=2):
          ss = append(ss, d)
          if not all(gap(a, b) for (a, b) in tuples(ss, 2)): continue
          # check all digits are used
          if len(union(nsplit(number(d)) for d in ss)) < 10: continue
          # output dates
          printf("{ss}", ss=join(ss, sep=" -> "))
      

      The program works backwards from 2014 to 1922 looking for sets of 3 dates that make a plausible set of birthdates for the three generations.

      It finds 2 possible situations, as below (ages shown are on the date the puzzle was published (2014-03-30)):

      Grandmother = 1919-08-30 (age = 94.6y)
      Mother = 1946-05-20 (age = 67.9y)
      Setter = 1973-02-10 (age = 41.1y)
      gap = 26.7y
      100273 × 2 = 200546
      100273 × 3 = 300819

      Grandmother = 1892-07-30 (age = 121.7y)
      Mother = 1928-05-20 (age = 85.9y)
      Setter = 1964-02-10 (age = 50.1y)
      gap = 35.7y
      100264 × 2 = 200528
      100264 × 3 = 300792

      The second of these is only just plausible, so presumably the first provides the required answer. (I would have preferred the puzzle eliminated one of these solutions by an explicit condition).

      Solution: The mother’s date of birth is: 200546 (i.e. 20th May 1946).

      To see solutions where the 6-digit number formed from the date is permitted to have a leading zero, the check at line 9 can be removed. In this case the program finds 115 solutions.

      Like

  • Unknown's avatar

    Jim Randell 11:32 am on 20 November 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 204: Logical will 

    From The Sunday Times, 21st March 1965 [link]

    Uncle George occasionally wasted time on Brain-teasers, and it was expected that his will would take an awkward form. But his five nephews were surprised to learn, after his death, that he had dictated his will to a solicitor who had no use for punctuation. The will ran as follows:

    maurice is not to sing at my funeral service if stephen receives the envelope containing three thousand pounds alec is to have five thousand pounds if thomas receives less than maurice alec is to have exactly twice what nigel has if thomas does not receive the envelope containing a thousand pounds stephen is to have exactly four thousand pounds

    The nephews were confident that Uncle George always uttered complete sentences, none of which contained more than one conditional clause. They also felt sure that what he had said to the solicitor allowed one way, and one way only, of distributing the five envelopes (containing £5000, £4000, £3000, £2000, and £1000), which Uncle George had left for them.

    Who receives each envelope? And may Maurice sing at the funeral service?

    This puzzle is included in the book Sunday Times Brain Teasers (1974).

    [teaser204]

     
    • Jim Randell's avatar

      Jim Randell 11:33 am on 20 November 2022 Permalink | Reply

      There are four ways we can parse the will into statements, each consisting of 4 statements, 3 of which are conditional:

      [1]
      (maurice is not to sing at my funeral service)
      (stephen receives £3000) → (alec is to have £5000)
      (thomas receives less than maurice) → (alec is to have exactly twice what nigel has)
      (thomas does not receive £1000) → (stephen is to have exactly £4000)

      [2]
      (maurice is not to sing at my funeral service) ← (stephen receives £3000)
      (alec is to have £5000)
      (thomas receives less than maurice) → (alec is to have exactly twice what nigel has)
      (thomas does not receive £1000) → (stephen is to have exactly £4000)

      [3]
      (maurice is not to sing at my funeral service) ← (stephen receives £3000)
      (alec is to have £5000) ← (thomas receives less than maurice)
      (alec is to have exactly twice what nigel has)
      (thomas does not receive £1000) → (stephen is to have exactly £4000)

      [4]
      (maurice is not to sing at my funeral service) ← (stephen receives £3000)
      (alec is to have £5000) ← (thomas receives less than maurice)
      (alec is to have exactly twice what nigel has) ← (thomas does not receive £1000)
      (stephen is to have exactly £4000)

      This Python program investigates each interpretation, rejecting those that do not have a single way of distributing the money.

      It runs in 54ms. (Internal runtime is 866µs).

      Run: [ @replit ]

      from enigma import (defaultdict, cproduct, subsets, implies, singleton, join, printf)
      
      # evaluate the clauses under interpretation <i>
      def check(i, cs):
        (a, b, c, d, e, f, g) = cs
        # [1] (a, b -> c, d -> e, f -> g)
        if i == 1: return all([a, implies(b, c), implies(d, e), implies(f, g)])
        # [2] (a <- b, c, d -> e, f -> g)
        if i == 2: return all([implies(b, a), c, implies(d, e), implies(f, g)])
        # [3] (a <- b, c <- d, e, f -> g)
        if i == 3: return all([implies(b, a), implies(d, c), e, implies(f, g)])
        # [4] (a <- b, c <- d, e <- f, g)
        if i == 4: return all([implies(b, a), implies(d, c), implies(f, e), g])
      
      # the amounts in the envelopes
      money = [5000, 4000, 3000, 2000, 1000]
      
      # choose an interpretation of the will (1-4)
      for i in (1, 2, 3, 4):
      
        # record outcomes: (A, M, N, S, T) -> sing
        r = defaultdict(list)
      
        # allocate the envelopes, and whether Maurice can sing
        for (k, sing) in cproduct([subsets(money, size=len, select="P"), [0, 1]]):
          (A, M, N, S, T) = k
      
          # the clauses:
          cs = [
            # (a) "M is not to sing ..."
            (not sing),
            # (b) "S gets 3000"
            (S == 3000),
            # (c) "A gets 5000"
            (A == 5000),
            # (d) "T gets less than M"
            (T < M),
            # (e) "A gets twice N"
            (A == 2 * N),
            # (f) "T does not get 1000"
            (T != 1000),
            # (g) "S gets 4000"
            (S == 4000),
          ]
      
          # is this is a valid allocation?
          if check(i, cs):
            r[k].append(sing)
            # do we need to proceed further?
            if len(r.keys()) > 1: break
      
        # there is only one way to distribute the money?
        k = singleton(r.keys())
        if k:
          # output solution
          (A, M, N, S, T) = k
          printf("[{i}] A={A} M={M} N={N} S={S} T={T}; sing={sing}", sing=join(sorted(r[k]), sep=", ", enc="{}"))
      

      Solution: The money is distributed as follows:

      Alec: £2000
      Maurice: £3000
      Nigel: £1000
      Stephen: £4000
      Thomas: £5000

      The solution is provided by interpretation [3] of the will.

      And as Stephen does not get £3000, Maurice is not prohibited from singing at the funeral.

      Like

  • Unknown's avatar

    Jim Randell 4:22 pm on 18 November 2022 Permalink | Reply
    Tags:   

    Teaser 3139: Checkmate 

    From The Sunday Times, 20th November 2022 [link] [link]

    Our chess club is divided into sections, with each section having the same number of players. The two oldest members will soon be retiring from playing and we will change the number of sections. The number of sections will change by one and so will the number of players in each section, but all the sections will still have the same number of players. This will result in there being a grand total of 63 fewer matches per year if each member plays all the other members of their section once per year.

    How many players are in each section at the moment?

    [teaser3139]

     
    • Jim Randell's avatar

      Jim Randell 4:56 pm on 18 November 2022 Permalink | Reply

      If the players are split into k sections, each consisting of n players, then the number of matches in each section is C(n, 2), and the total number of matches is m = k.C(n, 2).

      We can use some analysis to simplify the puzzle to two cases, one of which has no solution for positive integers, and the other that gives us the required solution.

      However, here is a constructive Python program that considers the total numbers of players t (up to 2000), and generates the possible (n, k, m) values for each candidate t value. It then looks for two t values that differ by 2, and give n and k values that differ by 1, and m values that differ by 63.

      This Python program runs in 85ms.

      Run: [ @replit ]

      from enigma import (irange, divisors_pairs, tri, cproduct, tuples, printf)
      
      # generate (k, n, m) values for t players
      def generate(t):
        # divide them into k sections of n players each
        for (k, n) in divisors_pairs(t, every=1):
          if n < 2: continue
          # calculate the total number of matches
          m = k * tri(n - 1)
          yield (t, k, n, m)
      
      # solve for numbers of players that differ by 2
      def solve(N):
        for (zs, ys, xs) in tuples((list(generate(t)) for t in irange(1, N)), 3):
          for ((t, k, n, m), (t_, k_, n_, m_)) in cproduct([xs, zs]):
            # check the differences in the corresponding k, n, m values
            if abs(k - k_) == 1 and abs(n - n_) == 1 and m - m_ == 63:
              # output solution
              printf("{k} sections of {n} players (= {m} matches) -> {k_} sections of {n_} players (= {m_} matches)")
      
      # solve the puzzle (up to 2000 players)
      solve(2000)
      

      Solution: There are 10 players in each section.

      The club starts with 110 players, formed into 11 sections of 10 players each. There are 45 matches among the players in each section, and 495 matches in total.

      When 2 players leave there are 108 players remaining. These are formed into 12 sections of 9 players each. There are 36 matches among the players in each section, and 432 matches in total. This is 63 fewer matches.


      Manually from:

      (n ± 1)(k ± 1) = nk − 2

      we can see that (for n + k > 3) there are 2 cases:

      [Case 1] (n, k)(n + 1, k − 1)k = n − 1.

      k C(n, 2) − (k − 1) C(n + 1, 2) = 63
      n[(n − 1)(n − 1) − (n − 2)(n + 1)] = 126
      n(3 − n) = 126
      n² − 3n + 126 = 0

      Which has no solution in positive integers.

      [Case 2] (n, k)(n − 1, k + 1)k = n + 1.

      k C(n, 2) − (k + 1) C(n − 1, 2) = 63
      (n − 1)[(n + 1)n − (n + 2)(n − 2)] = 126
      (n − 1)(n + 4) = 126
      n² + 3n − 130 = 0
      (n − 10)(n + 13) = 0
      n = 10
      k = 11

      And so there are 11 sections, each with 10 players.

      Like

    • GeoffR's avatar

      GeoffR 3:49 pm on 19 November 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Assumed No. of persons in each section before and after retirement
      var 2..25:persB; var 1..23:persA;
      
      % Assumed No. of sections before and after retirement
      var 2..25:sectB; var 1..25:sectA;
      
      % No of matches before and after retirement
      % UB = 25 * (25 * 24//2)
      var 1..7500: matchesB; var 1..7500: matchesA;
      
      % Change in number of sections is one due to two people retiring
      constraint abs(sectA - sectB) == 1;
      
      % Change in total number of persons is two
      constraint sectB * persB - 2 == sectA * persA
      /\ abs (persB - persA) == 1;
      
      % Find total number of matches before and after two people retire
      constraint 2 * matchesB = sectB * persB * (persB - 1);
      constraint 2 * matchesA = sectA * persA * (persA - 1);
      
      % Change in number of total matches is given as 63
      constraint matchesB - matchesA == 63;
      
      solve satisfy;
      
      output ["Persons per section before two retire = " ++ show(persB)
      ++ "\n" ++ "Number of sections before two retire = " ++ show(sectB)
      ++ "\n" ++ "Persons per section after two retire = " ++ show(persA)
      ++ "\n" ++ "Number of sections after two retire = " ++ show(sectA) ];
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 6:21 pm on 19 November 2022 Permalink | Reply

      
      # Assumed max no players and sections is 24 for both groups
      # Suffix 'B' for before matches and 'A' for after matches
      for playersB in range(1, 25):
        for sectionsB in range(1, 25):
          before_matches = sectionsB * (playersB * (playersB - 1) // 2)
          for playersA in range(1, 25):
            # The number of players will change by one 
            if abs(playersA - playersB) != 1:
              continue
            for sectionsA in range(1, 25):
              # The number of sections will change by one 
              if abs(sectionsA - sectionsB) != 1:
                continue
              # two players retire
              if sectionsB * playersB - 2 != sectionsA * playersA:
                continue
              after_matches = sectionsA * (playersA * (playersA - 1) // 2)
              
              # Differences in total number of matches
              if before_matches - after_matches != 63:
                continue
              # output player and sections data
              print(f"Players before two retire = {playersB} No.")
              print(f"Sections  before two retire = {sectionsB} No.")
              print(f"Players after two retire = {playersA} No.") 
              print(f"Sections  after two retire = {sectionsA} No.")
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:49 am on 17 November 2022 Permalink | Reply
    Tags:   

    Teaser 2680: Yesterday 

    From The Sunday Times, 2nd February 2014 [link] [link]

    In a new design of mobile phone each of the number buttons 1 to 9 is associated with 2 or 3 letters of the alphabet, but not in alphabetical order (and there are no letters on any other buttons). For example: M, T and U are on the same button. Predictive software chooses letters for you as you type. The numbers to type for SUNDAY, TIMES and TEASER are all multiples of 495.

    What number should I type to make SATURDAY?

    [teaser2680]

     
    • Jim Randell's avatar

      Jim Randell 8:52 am on 17 November 2022 Permalink | Reply

      We can solve this puzzle using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      The following run file executes in 67ms. (Internal runtime of the generated program is 1.8ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      --distinct=""
      
      # SUNDAY, TIMES, TEASER are all multiples of 495
      "SUNDAY % 495 = 0"
      "TIMES % 495 = 0"
      "TEASER % 495 = 0"
      
      # M, T, U have the same value
      "M = T"
      "T = U"
      
      # no digit appears more than 3 times
      "all(v < 4 for v in multiset.from_seq([A, D, E, I, M, N, R, S, T, U, Y]).values())"
      
      --answer="SATURDAY"
      --template="SUNDAY TIMES TEASER SATURDAY"
      

      Solution: To type SATURDAY use the following keys: 58225185.

      The letters we are given are assigned to the following keys:

      1: D I
      2: M T U
      5: R S Y
      6: N
      8: A E

      So we have:

      SUNDAY = 526185 = 495 × 1063
      TIMES = 21285 = 495 × 43
      TEASER = 288585 = 495 × 583

      Like

    • GeoffR's avatar

      GeoffR 12:27 pm on 17 November 2022 Permalink | Reply

      # last digit of TIMES, SUNDAY, TEASER
      Y = S = R = 5
      
      digits = set(range(1, 10)).difference({5})
      
      for T in digits:
        M = U = T  # stated condition
        
        # Digits for I and E in TIMES
        for I in digits:
          for E in digits:
            TIMES = 10000*T + 1000*I + 100*M + 10*E + S
            if not (TIMES % 495 == 0):continue
            
            # Add Digit A for TEASER
            for A in digits:
              TEASER = 100000*T + 10000*E + 1000*A + 100*S + 10*E + R
              if not (TEASER % 495 == 0):continue
              
              # Add Digits N and D for SUNDAY
              for N in digits:
                for D in digits:
                  SUNDAY = 100000*S + 10000*U + 1000*N + 100*D + 10*A + Y
                  if not (SUNDAY % 495 == 0):continue
                  
                  print(f"Keys to press for SATURDAY are : ")
                  print(f"{S},{A},{T},{U},{R},{D},{A},{Y}")
                  print(f"SUNDAY = {SUNDAY}, TIMES={TIMES}, TEASER={TEASER}")
                  
      # Keys to press for SATURDAY are : 5,8,2,2,5,1,8,5
      # SUNDAY = 526185, TIMES=21285, TEASER=288585
      
      

      Like

    • GeoffR's avatar

      GeoffR 1:30 pm on 17 November 2022 Permalink | Reply

      This snippet needs to be added to the output to show no more than three digits were associated with
      each button.

      Digits used with each button as follows:

      [A,D,E,I,M,N,R,S,T,U,Y =
      [8,1,8,1,2,6,5,5,2,2,5]

      Like

  • Unknown's avatar

    Jim Randell 9:37 am on 15 November 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 670: A shared legacy 

    From The Sunday Times, 12th May 1974 [link]

    When my neighbour Giles found he could no longer look after his prize herds of cattle and sheep, he planned to divide the lot among his four sons in equal shares.

    But when he started by counting up the cattle he soon found that their total was not exactly divisible by four, so he decided he would juggle with the numbers of beasts and achieve the four shares of equal value by making to the respective recipients quite arbitrary allocations of both cattle and sheep, taking into account that the value per head of the former was four times that of the latter.

    The outcome was that:

    (i) one son received 80% more beasts than another;
    (ii) two sons each received a total of beasts which equalled the aggregate total of two of the other three sons;
    (iii) one son received twice as many of one type of beast as of the other;
    (iv) only one son received over 100 beasts in all.

    How many cattle were included in this last total of over 100 beasts?

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

    [teaser670]

     
    • Jim Randell's avatar

      Jim Randell 9:37 am on 15 November 2022 Permalink | Reply

      If the total number of animals received by each brother is: A, B, C, D (in descending order), then (from (iv)) A is the only one to receive more than 100.

      And from (ii) two of the brothers totals are equal to the sum of the totals of two of the remaining three brothers. These two must be A and B, and so B = C + D, and A = C + 2D or 2C + D.

      This Python program starts by finding possible A, B, C, D values, and then tries to split these totals into numbers of cattle and sheep such that the remaining conditions hold.

      It runs in 62ms. (Internal runtime is 11.2ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, ediv, as_int, printf)
      
      # split total <t> into (<cattle>, <sheep>) with value <v>
      def split(t, v):
        c = ediv(v - t, 3)
        return (as_int(c, "0+"), as_int(t - c, "0+"))
      
      # solve puzzle for given totals: A, B, C, D
      def solve(A, B, C, D):
        # consider number of cattle for A
        for Ac in irange(0, A):
          As = A - Ac
          # total value for A (cattle = 4, sheep = 1)
          v = 4 * Ac + As
          # find splits for B, C, D
          try:
            (Bc, Bs) = split(B, v)
            (Cc, Cs) = split(C, v)
            (Dc, Ds) = split(D, v)
          except ValueError:
            continue
      
          # total number of cattle is not divisible by 4
          if (Ac + Bc + Cc + Dc) % 4 == 0: continue
          # someone has twice as many of one type of animal as the other
          if all(2 * x != y and x != 2 * y for (x, y) in [(Ac, As), (Bc, Bs), (Cc, Cs), (Dc, Ds)]): continue
      
          # output scenario
          printf("[v={v}] A: {Ac}c+{As}s = {A}; B: {Bc}c+{Bs}s = {B}; C: {Cc}c+{Cs}s = {C}; D: {Dc}c+{Ds}s = {D}")
      
      # total number of animals for C (not more than 100)
      for C in irange(1, 100):
        # total number of animals for D (not more than C)
        for D in irange(0, C):
          # B = C + D (not more than 100)
          B = C + D
          if B > 100: break
          # A = C + 2D or 2C + D (more than 100)
          for A in (B + D, B + C):
            if not (A > 100): continue
            # one of these values must be 1.8x a lower value
            if not any(5 * x == 9 * y for (x, y) in subsets((A, B, C, D), size=2)): continue
            # solve the puzzle
            solve(A, B, C, D)
      

      Solution: The son receiving over 100 animals received 6 cattle.

      The full solution is:

      A: 6 cattle + 111 sheep = 117 animals; 117 = 81 (B) + 36 (D)
      B: 18 cattle + 63 sheep = 81 animals; 81 = 45 (C) + 36 (D) = 1.8 × 45 (C)
      C: 30 cattle + 15 sheep = 45 animals; 30 is twice 15
      D: 33 cattle + 3 sheep = 36 animals

      Assigning values of cattle = 4, sheep = 1, each brother receives animals to a value of 135.

      In total there are 279 animals = 87 cattle (not a multiple of 4) + 192 sheep.

      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