Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 4:32 pm on 20 May 2022 Permalink | Reply
    Tags: ,   

    Teaser 3113: The plumber’s buckets 

    From The Sunday Times, 22nd May 2022 [link] [link]

    A plumber was trying to empty a tank containing 100 litres of water using three buckets, each marked with a different whole number of litres capacity between 10 and 20 litres. He calculated that he could exactly empty the tank, but only by using all three buckets and completely filling each bucket a different number of times.

    He filled and emptied each bucket the calculated number of times but the tank still contained 6 litres of water, because the smallest bucket had a dent that reduced its capacity by 3 litres.

    What were the marked capacities of the three buckets?

    The version of this puzzle that appears in the book The Sunday Times Teasers Book 2 (2023) is as follows (with modified sections underlined):

    A plumber was trying to empty a tank containing 100 litres of water using three buckets, each marked with a different whole number of litres capacity between 10 and 20 litres. He calculated that he could exactly empty the tank, but only by using all three buckets.

    He then emptied the tank as calculated, filling each bucket a different number of times, but then found the tank still contained 6 litres of water, because the smallest bucket had a dent that reduced its capacity by 3 litres.

    What were the marked capacities of the three buckets?

    This corresponds to my own re-wording of the puzzle given in the comments.

    [teaser3113]

     
    • Jim Randell's avatar

      Jim Randell 4:51 pm on 20 May 2022 Permalink | Reply

      Perhaps I misread this puzzle, but I couldn’t find a solution that fits the text.

      However, I did find a solution where the dent reduces the capacity of the largest bucket (not the smallest) by 3 litres. This is my preferred way to “rescue” the puzzle, as it is a minimal change to the text, and the dent would be less noticeable in the largest bucket. However, another way to rescue the puzzle (but with a different solution) is if the capacity of the smallest bucket is reduced by 2 litres (not 3 litres).

      This Python program runs in 58ms.

      from enigma import (
        irange, subsets, express, seq_all_different as all_different,
        singleton, printf
      )
       
      # choose the labelled capacities of the buckets
      for bs in subsets(irange(10, 20), size=3):
        # there is only one way to use the buckets
        qs = singleton(express(100, bs))
        if qs is None: continue
        # each bucket is used
        if 0 in qs: continue
        # the number of uses are all different
        if not all_different(qs): continue
        # largest [was: smallest] bucket must be used twice
        if qs[-1] != 2: continue
        printf("bs={bs} qs={qs}")
      

      Solution: The puzzle, as originally worded, has no solution.

      If we look for combinations of (correctly labelled) buckets that can be used (completely filling each bucket a number of times) to empty a 100 litre tank, such that all three buckets must be used, each of them a different number of times, we find there are 5 viable combinations (buckets × uses):

      (11, 18, 19) × (4, 1, 2)
      (12, 17, 18) × (1, 2, 3)
      (12, 17, 18) × (4, 2, 1)
      (13, 15, 19) × (1, 2, 3)
      (15, 18, 19) × (3, 2, 1)

      (And if we require there to be only a single way to use the buckets we can discard the (12, 17, 18) combination, leaving only 3 viable combinations).

      The original text asked for a combination where the smallest bucket was used twice, but as we see there are no such combinations.

      In my rescued version, where the largest bucket (not the smallest) has the dent, the solution is that the buckets are labelled (11, 18, 19) litres (although they are actually (11, 18, 16) litres).


      The published solution was that the buckets are labelled (13, 17, 19) litres (although they are actually (10, 17, 19) litres). Which corresponds to my re-wording of the puzzle given below.

      However we can use (correctly labelled) buckets (13, 17, 19) to empty the tank in the following ways:

      1× 13 + 4× 17 + 1× 19 = 100
      2× 13 + 1× 17 + 3× 19 = 100

      So it is not true to say that it can “only [be done] by using all three buckets and completely filling each bucket a different number of times”. (The first line achieves the required result using two of the buckets the same number of times). Hence the requirement for rewording of the puzzle.

      But if we suppose “the calculated number of times” requires that there is only one possible way to fill the buckets, then the reworded puzzle also has no solutions. (As there are 2 ways to use these (correctly labelled) buckets to empty the tank).

      It is possible that the setter intended that when the plumber sat down to work out how many times to use the buckets, he only considers using each bucket a different number of times. He tried using just one bucket and found it was not possible, then he tried using combinations of two buckets and found none of those were possible, and finally he tried using all three buckets and found that there was a single way they could be used. And he then proceeded to use this calculated number of ways, but when he finished there was 6 litres remaining in the tank, because the smallest bucket’s capacity was reduced by 3 litres from the labelled capacity.

      In this scenario, there are 9 candidate bucket combinations, and only one of them has the smallest bucket having 2 uses, so this gives a unique solution, and it is the published solution.


      The published solution is: “13, 17 and 19 litres”.

      Like

      • Jim Randell's avatar

        Jim Randell 5:58 pm on 20 May 2022 Permalink | Reply

        This might be a stricter interpretation of the puzzle text, which finds a couple more candidate solutions. But still doesn’t find a solution that fits the text.

        Instead I have coded it to find the same solution I found above.

        There remains an issue as to whether “he filled and emptied each bucket the calculated number of times” is intended to imply that there is only a single way of filling the buckets (each a different non-zero number of times), or he chose one from multiple viable ways. Fortunately this does not change the answer to my “rescued” version of the puzzle.

        This Python program runs in 56ms. (Internal run time is 1.5ms).

        from enigma import (irange, subsets, express, seq_all_different as all_different, printf)
        
        # find viable ways to use the buckets
        def buckets(t, bs):
          rs = list()
          for qs in express(t, bs):
            # each bucket is used
            if 0 in qs: return []
            # the numbers of uses are all different
            if not all_different(qs): return []
            # is viable
            rs.append(qs)
          return rs
        
        # choose the labelled capacities of the buckets
        for bs in subsets(irange(10, 20), size=3):
          # find viable ways to use the buckets
          for qs in buckets(100, bs):
            # largest [was: smallest] bucket must be used twice
            if qs[-1] != 2: continue
            # output solution
            printf("bs={bs} qs={qs}")
        

        The interpretation of the puzzle text where there is just a single way to fill the buckets can be implemented at line 13 by only returning the list of candidates if it has a length of 1. But this doesn’t change the answer found.

        Like

      • Frits's avatar

        Frits 5:59 pm on 20 May 2022 Permalink | Reply

        Correct, with SubstitutedExpression I end up with 3 options (the third solution would be if the capacity of the smallest bucket is reduced by 1.5 litres).

        Like

      • Jim Randell's avatar

        Jim Randell 8:26 am on 21 May 2022 Permalink | Reply

        It has been suggested to me that the intended interpretation of this puzzle is more like this (with {{ changed sections }} in double braces):

        A plumber was trying to empty a tank containing 100 litres of water using three buckets, each marked with a different whole number of litres capacity between 10 and 20 litres. {{ He calculated that by completely filling each bucket a number of times, he could exactly empty the tank, but only by using all three buckets. }}

        He filled and emptied each bucket the calculated number of times, {{ each bucket being used a different number of times. But when he finished }} the tank still contained 6 litres of water, because the smallest bucket had a dent that reduced its capacity by 3 litres.

        What were the marked capacities of the three buckets?

        To solve this formulation of the puzzle we can take my program and just move the “different number of times” test (line 10 in the program above is moved to line 18 below):

        Run: [ @replit ]

        from enigma import (irange, subsets, express, seq_all_different as all_different, printf)
        
        # find viable ways to use the buckets
        def buckets(t, bs):
          rs = list()
          for qs in express(t, bs):
            # each bucket is used
            if 0 in qs: return []
            # is viable
            rs.append(qs)
          return rs
        
        # choose the labelled capacities of the buckets
        for bs in subsets(irange(10, 20), size=3):
          # find viable ways to use the buckets
          for qs in buckets(100, bs):
            # the numbers of uses are all different
            if not all_different(qs): continue
            # smallest bucket must be used twice
            if qs[0] != 2: continue
            # output solution
            printf("bs={bs} qs={qs}")
        

        Like

      • Mark Valentine's avatar

        Mark Valentine 12:00 pm on 22 May 2022 Permalink | Reply

        I think the text is absolutely fine. Clearly you need three different bucket sizes, where no two (or one) of which can combine to make exactly 100. Then from these triples you apply the condition for the smallest bucket size.

        Like

        • Jim Randell's avatar

          Jim Randell 12:27 pm on 22 May 2022 Permalink | Reply

          @Mark: You also need to check that the numbers of uses of each bucket are all different. And where this requirement is placed in the puzzle text makes a difference.

          The original puzzle text has no solutions. My revised text does have a unique solution. (And it seems that this revised text corresponds to the intended puzzle).

          Like

          • Mark Valentine's avatar

            Mark Valentine 9:00 pm on 22 May 2022 Permalink | Reply

            Ok to be fair I didn’t check for uniqueness . Just stopped at a solution. so will defer.

            Like

    • Hugh+Casement's avatar

      Hugh+Casement 9:47 am on 21 May 2022 Permalink | Reply

      I don’t see that changing the passages in double braces makes any difference!

      Does the clue lie in “but only by using all three buckets” ?
      That is to say, no (different) integer multiples of any two undented buckets would sum to 100 litres.

      Like

      • Jim Randell's avatar

        Jim Randell 10:03 am on 21 May 2022 Permalink | Reply

        There is a subtle difference between “but only using all three buckets” and “but only using all three buckets, each a different number of times” that stops the puzzle from working in the originally published formulation.

        The “using all three buckets” requirement means we can’t use buckets with capacity 10 or 20, as then we could exactly empty the tank using just one of them. Similarly if a collection of buckets includes a pair that can be used to exactly empty the tank then that is also disallowed.

        Like

        • Hugh+Casement's avatar

          Hugh+Casement 12:23 pm on 21 May 2022 Permalink | Reply

          Yes, but he used the buckets the calculated number of times.

          Like

    • J Slusar's avatar

      J Slusar 6:55 pm on 21 May 2022 Permalink | Reply

      But what is wrong with buckets marked 13, 14 and 15 filling the smallest twice, the middle one once and the larger one 4 times? The total would have been 100, but the reduced capacity of the smaller bucket would mean 6 litres remained in the tank?

      Like

      • Jim Randell's avatar

        Jim Randell 10:01 pm on 21 May 2022 Permalink | Reply

        Thanks for the comment.

        I think this combination is ruled out by the requirement that “he could exactly empty the tank, but only by using all three buckets and completely filling each bucket a different number of times”.

        With (13, 14, 15) there are three ways we can make 100:

        100 = 0×13 + 5×14 + 2×15
        100 = 1×13 + 3×14 + 3×15
        100 = 2×13 + 1×14 + 4×15

        The first of these means that you don’t have to use all three buckets, and the second means that you don’t have to use each bucket a different number of times. So the requirement is not met.

        Like

    • bencooperjamin's avatar

      bencooperjamin 1:46 am on 23 May 2022 Permalink | Reply

      Doesnt work because 5*14 +2*15=100 ie the tank can be emptied with only 2 of the buckets.

      Like

    • GeoffR's avatar

      GeoffR 12:34 pm on 23 May 2022 Permalink | Reply

      I finally managed to find a program solution, which agrees with Jim’s last posting i.e 3rd posting (21 May 2022).

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      int: Tank == 100;   % litres
      
      % Three buckets - capacity ranges in litres
      var 10..20:B1; var 10..20:B2; var 10..20:B3; 
      
      % Three different size buckets are required
      constraint B1 < B2 /\ B2 < B3;
      
      % No of times buckets B1, B2 and B3 are emptied
      var 1..10:n1;  var 1..10:n2;  var 1..10:n3; 
      
      constraint all_different([n1, n2, n3]);
      
      % Smallest bucket B1 must be emptied twice due to a 3L dent
      constraint n1 == 2;
      
      % Tank emptying constraints for one and two buckets
      % Tank cannot be emptied with any single bucket
      constraint forall(i in 1..10) ( Tank mod (B1 * i) != 0 );
      constraint forall(i in 1..10) ( Tank mod (B2 * i) != 0 );
      constraint forall(i in 1..10) ( Tank mod (B3 * i) != 0 );
      
      % Buckets B1 and B2 cannot empty tank on their own
      constraint forall(i in 1..10, j in 1..10) (
            Tank mod (i * B1 + j * B2) != 0);
      
      % Buckets B1 and B3 cannot empty tank on their own
      constraint forall(i in 1..10, j in 1..10) (
            Tank mod (i * B1 + j * B3) != 0);
            
      % Buckets B2 and B3 cannot empty tank on their own
      constraint forall(i in 1..10, j in 1..10) (
            Tank mod (i * B2 + j * B3) != 0);
      
      % Empty the tank with three buckets B1, B2 and B3
      constraint Tank mod (n1 * B1 + n2 * B2 + n3 * B3) == 0;
      
      solve satisfy;
      
      output [" Capacities of three buckets (B1, B2, B3) = " 
      ++ show([B1, B2, B3 ] )
      ++ "\n" ++ " No of times each bucket emptied (n1, n2, n3) = "
       ++ show([n1, n2, n3 ]) ];
      
      

      Like

    • Frits's avatar

      Frits 10:39 am on 25 May 2022 Permalink | Reply

      To simplify restraints you can use:

         
      constraint forall(i in 0..10, j in 0..10, k in 0..10) (
      i * B1 + j * B2 + k * B3 != Tank \/ i * j * k > 0);
      

      Like

  • Unknown's avatar

    Jim Randell 10:16 am on 19 May 2022 Permalink | Reply
    Tags:   

    Teaser 2854: Power surge 

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

    I recently checked my energy bills. I noticed that the account numbers for my electricity and gas are two different six-figure numbers, and that one is a multiple of the other. The electricity account number is a perfect cube and the sum of its digits is a perfect square. The gas account number is a perfect square (and, as it happens, the sum of its digits is a perfect cube!).

    What is the gas account number?

    [teaser2854]

     
    • Jim Randell's avatar

      Jim Randell 10:17 am on 19 May 2022 Permalink | Reply

      I am assuming that leading zeros are not allowed in the 6-figure account numbers.

      The following Python program runs in 58ms. (Internal run time is 442µs).

      Run: [ @replit ]

      from enigma import (irange, dsum, is_cube, is_square, chain, div, printf)
      
      # generate possible other values for G
      def generate(E, fn):
        for d in irange(2, 9):
          G = fn(E, d)
          if G is None: continue
          # G has 6 digits
          if G < 100000: break
          if G > 999999: break
          # G is a perfect square
          if not is_square(G): continue
          # the sum of G's digits is a perfect cube
          if not is_cube(dsum(G)): continue
          # viable value
          yield (G, d)
      
      # E is a 6 digit cube
      for i in irange(47, 99):
        E = i**3
        # and the sum of its digits is a perfect square
        if not is_square(dsum(E)): continue
      
        # look for another 6-digit multiple that is a multiple or divisor of E
        mul = lambda E, d: E * d
        for (G, d) in chain(generate(E, mul), generate(E, div)):
          # output solution
          printf("E={E} G={G} [d={d}]")
      

      Solution: The gas account number is 147456.

      And the electricity account number is 884736 (= 6 × 147456).

      Like

    • Frits's avatar

      Frits 7:39 pm on 19 May 2022 Permalink | Reply

         
      from enigma import SubstitutedExpression, dsum
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          # The electricity account number is a perfect cube and 
          # the sum of its digits is a perfect square
          "is_square(dsum(EL ** 3))",
          # six-figure number
          "99999 < EL ** 3 < 1000000",
          
          # one is a multiple of the other
          "(GAS ** 2) % (EL ** 3) == 0 or (EL ** 3) % (GAS ** 2) == 0",
          
          # two different six-figure numbers
          "EL ** 3 != GAS ** 2",
          
          # The gas account number is a perfect square and
          # the sum of its digits is a perfect cube
          "is_cube(dsum(GAS ** 2))",
          # six-figure number
          "99999 < GAS ** 2 < 1000000",
        ],
        answer="GAS ** 2",
        d2i=dict([(k, "EG") for k in range(0, 3)] + [(3, "E")]),
        distinct="",
        verbose=0,
      )
      
      # print answers
      for (_, ans) in p.solve():
        print(f"{ans}")
      

      Like

    • GeoffR's avatar

      GeoffR 4:31 pm on 20 May 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Elec Account - six digits
      var 1..9:E; var 0..9:F; var 0..9:G; var 0..9:H;
      var 0..9:I; var 0..9:J; 
      
      % Gas Account - six digits
      var 1..9:g; var 0..9:h; var 0..9:i; var 0..9:j;
      var 0..9:k; var 0..9:l;
      
      var 100000..999999:Elec == 100000*E + 10000*F + 1000*G + 100*H + 10*I + J;
       
      var 100000..999999:Gas == 100000*g + 10000*h + 1000*i + 100*j + 10*k + l;
      
      % Two different six-figure account numbers
      constraint Elec != Gas;
      
      % One account number is a multiple of the other
      constraint Elec mod Gas == 0 \/ Gas mod Elec == 0;
      
      % 6-digit squares and cubes
      set of int: sq6 = {n * n | n in 317..999};
      set of int: cb6 = {n * n * n | n in 47..99};
      
      % 2-digit squares and cubes < 54 (i.e. < 6 * 9)
      set of int: cb2 = {n * n * n | n in 2..3};
      set of int: sq2 = {n * n | n in 2..7};
      
      % Square/Cube constraints for both accounts
      constraint Elec in cb6 /\ (E + F + G + H + I + J) in sq2;
      constraint Gas in sq6 /\ (g + h + i + j + k + l) in cb2;
      
      solve satisfy;
      
      output[ "Gas Acc No. = " ++ show(Gas) 
      ++ "\n" ++ "Elec Acc No. = " ++ show(Elec)];
      
      % Gas Acc No. = 147456
      % Elec Acc No. = 884736
      % ----------
      % ==========
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 8:36 am on 24 February 2023 Permalink | Reply

      from enigma import nsplit, is_square, is_cube
      
      sq6 = [ x * x for x in range(316, 999) ]
      cb6 = [ y * y * y for y in range(46, 99) ]
              
      for elec in cb6:
        for gas in sq6:
          if elec > gas and elec % gas == 0 :
            a, b, c, d, e, f = nsplit(elec)
            # electrical account digits sum to a perfect square
            if is_square(a + b + c + d + e + f):
              g, h, i, j, k, l = nsplit(gas)
              # gas account digits sum to a perfect cube
              if is_cube(g + h + i + j + k + l):
                print(f"Electrical account number = {elec}")
                print(f"Gas account number = {gas}")
                break
                  
          elif gas > elec and gas % elec == 0:
            A, B, C, D, E, F = nsplit(gas)
            # gas account digits sum to a perfect cube
            if is_cube(A + B + C + D + E + F):
              G, H, I, J, K, L = nsplit(elec)
              # electrical account digits sum to a perfect square
              if is_square(G + H + I + J + K + L):
                print(f"Electrical account number = {elec}")
                print(f"Gas account number = {gas}")
      
      # Electrical account number = 884736
      # Gas account number = 147456
                
      
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 12:08 pm on 17 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 703: Wheels of fortune 

    From The Sunday Times, 5th January 1975 [link]

    A small gaming machine has proved popular in East Bernia. Inside the machine three wheels spin independently on a common axle and on the “tread” of each wheel are three single-figured numbers. When a handle is pulled the wheels spin, and when they come to rest one number on each wheel is shown at the front of the machine.

    The nine numbers used are 1, 2, 3, 4, 5, 6, 7, 8 and 9. When added together, the three numbers on the first wheel total one less than the three on the second wheel, which total one less than the three on the third wheel.

    The method of scoring is to add the three “disclosed” numbers together, and the numbers are so placed on the wheels that it is possible to make any one of 17 different scores (16 of which are consecutive numbers). It is impossible to get the score of 7.

    What are the numbers on each wheel?

    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.

    [teaser703]

     
    • Jim Randell's avatar

      Jim Randell 12:09 pm on 17 May 2022 Permalink | Reply

      This Python program runs in 48ms. (Internal run time is 838µs).

      Run: [ @replit ]

      from enigma import (irange, div, partitions, cproduct, printf)
      
      # available digits
      digits = list(irange(1, 9))
      
      # calculate the required sums
      x = div(sum(digits), 3)
      ts = { x - 1, x, x + 1 }
      
      # does <ns> contains a <k>-length run of consecutive numbers
      # ns is a sorted sequence of distinct integers
      def is_consecutive(ns, k=2):
        k -= 1
        (i, j) = (0, k - len(ns))
        while j < 0:
          if ns[i] + k == ns[j] : return (i, j)
          i += 1
          j += 1
      
      # allocate the digits to the wheels
      for ws in partitions(digits, 3):
        if set(sum(w) for w in ws) != ts: continue
      
        # construct possible sums
        ss = set(sum(xs) for xs in cproduct(ws))
        if len(ss) != 17 or 7 in ss: continue
      
        # look for a consecutive run of 16
        ss = sorted(ss)
        if not is_consecutive(ss, 16): continue
      
        # output solution
        (w1, w2, w3) = sorted(ws, key=sum)
        printf("{w1} {w2} {w3} -> {ss}")
      

      Solution: The numbers on the wheels are: (3, 5, 6) (2, 4, 9) (1, 7, 8).

      The 17 scores that can be made are: 6 and 8 – 23.

      Like

    • Frits's avatar

      Frits 7:05 pm on 17 May 2022 Permalink | Reply

      Some easy deduction:

      As scores 6, 7, 23, and 24 can only be made with one combination we can conclude that 1, 2 and 3 must be on different wheels (same for 6, 8 and 9). So we know the scores 6 and 8 – 23.

      Also 3 and 4 cannot be on the same wheel as it leads to score 7.

      Like

  • Unknown's avatar

    Jim Randell 11:20 am on 15 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 45: Ben’s big ‘uns 

    From The Sunday Times, 28th January 1962 [link]

    The high spot of the Appleton Harvest Home Fête was the sheep pen building match against the neighbouring village of Beescomb. The test was to build a rectangular pen of 500 ft. perimeter, all hurdles edge to edge and firm.

    Farmer Bull provided the hurdles, mainly standard six-footers, with a sprinkling of odd lengths, seven-foot and four-foot.

    Each crew captain chose the hurdles he required and had them stacked ready on the site. The stack of Albert, the Appleton captain, contained twice as many standard as odd length hurdles, while Ben of Beescomb (big ‘uns be best) had limited his odd lengths to quarter of the total.

    Twenty yards from the stacks the crews lined up. The whistle blew, the crowd roared and the rivals flew forward. So evenly were they matched that each averaged ten seconds to fix a seven-footer, eight for a six-footer and six for a four-footer.

    Amid yells of triumph, the winners banged down the last six-foot hurdle into the last six-foot gap.

    Who won, and by how many hurdles?

    [teaser45]

     
    • Jim Randell's avatar

      Jim Randell 11:20 am on 15 May 2022 Permalink | Reply

      This Python program uses the [[ express() ]] function from the enigma.py library to break the 500ft perimeter down into hurdles of the appropriate lengths, and looks for those with the required “standard” to “odd” ratio.

      It runs in 61ms. (Internal runtime is 3.1ms).

      Run: [ @replit ]

      from enigma import (express, div, product, printf)
      
      # fence panels available
      panels = (4, 6, 7)
      
      # time for a combination of panels
      time = lambda ns, ts=(6, 8, 10): sum(x * y for (x, y) in zip(ns, ts))
      
      # find possible stacks of panels for A and B
      (As, Bs) = (list(), list())
      
      # consider possible breakdowns of 500
      for (n4, n6, n7) in express(500, panels):
        # calculate "standard" to "odd" ratio
        d = div(n6, n4 + n7)
        if d is None: continue
        if d == 2:
          As.append((n4, n6, n7))
        elif d == 3:
          Bs.append((n4, n6, n7))
      
      # run the competition
      for (A, B) in product(As, Bs):
        (a, b) = (time(A), time(B))
        printf("A={A} vs B={B} = {a} vs {b}")
      

      Solution: Beescomb won by 1 hurdle.

      The program finds there is only one possible breakdown of hurdles for each team with the required ratio:

      A: 17× 4ft + 58× 6ft + 12× 7ft = 500ft; time = 686s
      B: 60× 6ft + 20× 7ft = 500ft; time = 680s

      So B wins by 6s (the time taken for A to place their final 4ft hurdle).

      The program doesn’t check that these can be broken down to make the sides of a rectangular pen, but there are many ways that this can be done. For example:

      A: 102ft × 148ft; 102ft = 17× 6ft (×2); 148ft = 16× 4ft + 12× 7ft, 1× 4ft + 24× 6ft
      B: 70ft × 180ft; 70ft = 10× 7ft (×2); 180ft = 30× 6ft (×2)

      Like

  • Unknown's avatar

    Jim Randell 4:36 pm on 13 May 2022 Permalink | Reply
    Tags:   

    Teaser 3112: PIN 

    From The Sunday Times, 15th May 2022 [link] [link]

    Callum has opened a new current account and has been given a telephone PIN that is composed of non-zero digits (fewer than six). He has written down the five possible rearrangements of his PIN. None of these five numbers are prime; they can all be expressed as the product of a certain number of different primes.

    The PIN itself is not prime; it can also be expressed as the product of different primes but the number of primes is different in this case. The sum of the digits of his PIN is a square.

    What is the PIN?

    [teaser3112]

     
    • Jim Randell's avatar

      Jim Randell 5:19 pm on 13 May 2022 Permalink | Reply

      We need to find a collection of digits from which the PIN and exactly 5 other rearrangements can be made. So we need there to be 6 different arrangements in total.

      Selections of digits with the same pattern (e.g. “three different digits”, “two pairs of digits”, which we might write as (1, 1, 1) and (2, 2)) will have the same number of arrangements of the digits. So in the following program, if a pattern produces a number of arrangements that is not 6, we abandon testing that pattern. (Another way to check a pattern would be to check that its multinomial coefficient is 6, see [@replit]).

      This Python program runs in 58ms. (Internal run time is 4.18ms).

      Run: [ @replit ]

      from enigma import (
        defaultdict, irange, nconcat, subsets, decompose, multiset, factor,
        seq_all_different as all_different, is_square_p, singleton, map2str, printf
      )
      
      # group numbers by the number of prime factors, subject to:
      # - a number cannot be prime
      # - a number cannot have a repeated prime factor
      def group(ns):
        d = defaultdict(list)
        for n in ns:
          fs = factor(n)
          # none of the numbers are prime
          if len(fs) == 1: return
          # none of the numbers has a repeated prime factor
          if not all_different(fs): return
          # record this number
          d[len(fs)].append(n)
        return d
      
      # n = number of digits in the PIN
      # k = number of different digits in the PIN
      for (k, n) in subsets(irange(1, 5), size=2, select="R"):
        # calculate repetitions of digits
        for rs in decompose(n, k, increasing=0, sep=0, min_v=1):
          # choose digits
          for ds in subsets(irange(1, 9), size=k):
            # allocate the digits
            m = multiset.from_pairs(zip(ds, rs))
            # the sum of the digits is a square number
            if not is_square_p(m.sum()): continue
            # calculate the number of arrangements
            ns = list(nconcat(*x) for x in subsets(m, size=n, select="mP"))
            # we need 6 arrangements
            if len(ns) != 6: break
            # group the numbers by the number of prime factors
            d = group(ns)
            if d is None: continue
            # check there are only two different numbers of factors
            if len(d.keys()) != 2: continue
            # and one of these corresponds to only one number
            pk = singleton(k for (k, vs) in d.items() if len(vs) == 1)
            if pk is None: continue
            # output solution
            PIN = singleton(d[pk])
            printf("PIN={PIN} {d}", d=map2str(d, arr="->", enc="[]"))
      

      Solution: The PIN is 1771.


      The PIN has a factorisation into 3 different primes: 1771 = 7 × 11 × 13.

      And the other 5 rearrangements of the digits all have factorisations into 2 different primes:

      1177 = 11 × 107
      1717 = 17 × 101
      7117 = 11 × 647
      7171 = 71 × 101
      7711 = 11 × 701

      Like

    • GeoffR's avatar

      GeoffR 3:09 pm on 15 May 2022 Permalink | Reply

      It looks as though the PIN number has to be three, four or five digits long.

      I thought a 3-digit pin would not give enough rearrangements and a 5-digit pin would give more than five rearrangements.

      I assumed only a four digit number, compose of two different digits, would give the requisite five rearrangements, as shown at the start of the code.

      # Assume only  a four-digit number made of two different
      # digits (A,B) can give five possible total rearrangements
      # of the PIN number
      # e.g. AABB, ABAB, ABBA, BAAB, BABA, BBAA
      
      from itertools import permutations
      from enigma import factor, is_square, is_prime
      
      # choose two non-zero digits for the four digit PIN
      for p1 in permutations('123456789', 2):
        a, b = p1
        # check sum of digits is a square
        if not is_square(2 * int(a) + 2 * int(b)):
           continue
        # form six numbers from two different digits
        n1 = int(a + a + b + b)
        n2 = int(a + b + a + b)
        n3 = int(a + b + b + a)
        n4 = int(b + a + a + b)
        n5 = int(b + a + b + a)
        n6 = int(b + b + a + a)
        # check all six numbers are not prime
        if all(not is_prime(x) for x in (n1, n2, n3, n4, n5, n6)):
          # find number of prime factors of the six numbers
          f1 = len(factor(n1))
          f2 = len(factor(n2))
          f3 = len(factor(n3))
          f4 = len(factor(n4))
          f5 = len(factor(n5))
          f6 = len(factor(n6))
          # check there are only two different numbers
          # of factors for the six numbers
          if len(set((f1, f2, f3, f4, f5, f5, f6))) == 2:
            # find the unique number of factors
            # to give the PIN number
            if f1 not in (f2, f3, f4, f5, f6):
              print(f"PIN = {n1}")
            elif f2 not in (f1, f3, f4, f5, f6):
              print(f"PIN = {n2}")
            elif f3 not in (f1, f2, f4, f5, f6):
              print(f"PIN = {n3}")
            elif f4 not in (f1, f2, f3, f5, f6):
              print(f"PIN = {n4}")
            elif f5 not in (f1, f2, f3, f4, f6):
              print(f"PIN = {n5}")
            elif f6 not in (f1, f2, f3, f4, f5):
              print(f"PIN = {n6}")
      
      
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 3:32 pm on 15 May 2022 Permalink | Reply

        @GeoffR: Actually a 3-digit PIN consisting of 3 different digits will have the required 6 arrangements.

        Like

    • GeoffR's avatar

      GeoffR 3:34 pm on 15 May 2022 Permalink | Reply

      @Jim: Yes, I overlooked that when thinking of repeated digits

      Like

    • Frits's avatar

      Frits 1:12 pm on 17 May 2022 Permalink | Reply

      Based on GeoffR’s method.

        
      # assume only a four-digit number made of two different
      # digits (A,B) can give five other rearrangements of the PIN number
      # e.g. AABB, ABAB, ABBA, BAAB, BABA, BBAA
      #
      # or 
      #
      # the PIN number has 3 digits which are all different
      # resulting in 6 arrangements
      # e.g. ABC, ACB, BAC, BCA, CAB, CBA
       
      from itertools import permutations, combinations
      from enigma import factor, is_square
      
      # loop over three-digit and four-digit numbers
      for nd in [3, 4]:
        # choose non-zero digits for the nd-digit PIN
        for comb in combinations('123456789', 6 - nd):
          dgts = list(comb) if nd == 3 else list(comb * 2)
           
          # check sum of digits is a square
          if not is_square(sum(int(x) for x in dgts)):
            continue
            
          nums = [int(''.join(x)) for x in set(permutations(dgts))]  
          fs   = [factor(x) for x in nums]
       
          # none are prime and none have repeated prime factors
          if any(len(x) == 1 or len(x) != len(set(x)) for x in fs): continue
          
          n_fs  = [len(x) for x in fs]
          
          # check for exactly two different numbers of factors
          if len(set(n_fs)) == 2:
            # find the unique number of factors
            uniq = [i for i, x in enumerate(n_fs) if n_fs.count(x) == 1]
      
            if uniq:
              print(f"PIN = {nums[uniq[0]]}")
      

      Like

  • Unknown's avatar

    Jim Randell 9:12 am on 12 May 2022 Permalink | Reply
    Tags:   

    Teaser 2845: Baled out 

    From The Sunday Times, 2nd April 2017 [link] [link]

    In my fantasy Euro football championship the four home teams were in the same group, with each team playing each other team once. Group positions were decided on points, then using “goal differences” and then “goals scored” if necessary.

    After having played two games each, just three goals had been scored in the group, leading to England being ahead with Northern Ireland second, Scotland third and Wales fourth. Wales realised that they had to score at least three goals in their remaining game in order to have a chance of being group leaders. In the final game of the group, Bale scored a third goal in the dying seconds, resulting in Wales being group winners and knocking England into second place.

    What were the scores in the six games (in the order EvN, EvS, EvW, NvS, NvW, SvW)?

    [teaser2845]

     
    • Jim Randell's avatar

      Jim Randell 9:15 am on 12 May 2022 Permalink | Reply

      There are 4 teams, and each team plays each other team once. So there are 6 matches in total, each team playing 3 matches.

      When each team has played each other team twice, 4 of the matches have been played, leaving 2 matches remaining.

      My first programmed solution was quite long and a bit clunky (and relied on a bit of analysis).

      The following program is neater. It considers all possibilities for the 4 played matches, and looks for sets of scorelines that have 3 goals scored in total and place the teams in order (E, N, S, W). It then examines scenarios for the remaining 2 matches, such that W can take 1st place, but must score at least 3 goals in order to do so. Once a viable scenario is found, we look for the actual scenario (where W scores 3 goals in their final match to come 1st, and E finishes in 2nd place). The scenarios are then output (matches and table).

      It runs in 244ms.

      Run: [ @replit ]

      from enigma import (defaultdict, Football, subsets, partitions, irange, inf, diff, peek, update, zip_eq, join, sprintf, printf)
      
      # scoring system
      football = Football(points=dict(w=3, d=1))
      
      # labels for the teams
      teams = (E, N, S, W) = (0, 1, 2, 3)
      
      # keys for the matches
      matches = list(subsets(teams, size=2))
      
      # generate possible scores for <n> matches with <g> total goals scored
      def _scores(n, g, ss=[]):
        if n == 1:
          for x in irange(0, g):
            yield ss + [(x, g - x)]
        else:
          # choose the number of goals scored in this match
          for m in irange(0, g):
            for x in irange(0, m):
              yield from _scores(n - 1, g - m, ss + [(x, m - x)])
      
      # generate sets of <n> matches with the specified range of goals scored
      def scores(n, goals=None, min_goals=0, max_goals=inf):
        if goals is not None: min_goals = max_goals = goals
        for g in irange(min_goals, max_goals):
          yield from  _scores(n, g)
      
      # assign positions to teams, based on scores <ss>
      def table(ss):
        # calculate the points, goals for, goals against for each team
        (pts, gf, ga) = (list(), list(), list())
        for t in teams:
          (ss_, ts_) = football.extract(ss, t)
          table = football.table(football.outcomes(ss_), ts_)
          pts.append(table.points)
          (f, a) = football.goals(ss_, ts_)
          gf.append(f)
          ga.append(a)
        # calculate the positions of the teams (1st, 2nd, 3rd, 4th)
        pos = sorted(teams, key=(lambda t: (pts[t], gf[t] - ga[t], gf[t])), reverse=1)
        return (pos, pts, gf, ga)
      
      # names for the teams
      name = "ENSW"
      
      # output matches
      def output_matches(n, ss):
        fmt = lambda k, v: sprintf("{x}v{y} = {a}-{b}", x=name[k[0]], y=name[k[1]], a=v[0], b=v[1])
        printf("{n} {s}", s=join((fmt(k, v) for (k, v) in sorted(ss.items())), sep="; "))
      
      # output the table
      def output_table(pos, pts, gf, ga):
        for (i, t) in enumerate(pos, start=1):
          printf("  {i}: {n} -> {p} pts, f={f} a={a}", n=name[t], p=pts[t], f=gf[t], a=ga[t])
      
      # choose the 2 unplayed matches
      for unplayed in partitions(teams, 2):
        played = diff(matches, unplayed)
      
        # allocate scores for the 4 played matches, with 3 goals scored
        for ps in scores(len(played), goals=3):
          ss1 = dict(zip(played, ps))
      
          # check the first scenario: E > N > S > W
          (pos1, pts1, gf1, ga1) = table(ss1)
          if not zip_eq(pos1, [E, N, S, W]): continue
      
          # now consider scores for the 2 unplayed matches (say up to 6 goals)
          # and look for situations where W can take 1st place
          uW = peek(x for x in unplayed if W in x)
          Ws = defaultdict(list)
          for us in scores(len(unplayed), min_goals=0, max_goals=6):
            ss2 = update(ss1, unplayed, us)
      
            # check W can take 1st place
            (pos2, pts2, gf2, ga2) = table(ss2)
            if not (pos2[0] == W): continue
      
            # record match results by W's goals
            (_, w) = ss2[uW]
            Ws[w].append(ss2)
            if w < 3: break  # we can stop if w < 3
      
          # W needs to score at least 3 goals to take 1st place
          if not (Ws and min(Ws.keys()) == 3): continue
      
          # now look for the actual situation, where W scores 3 goals, and E takes 2nd place
          for ss2 in Ws[3]:
            # check E is in 2nd place
            (pos2, pts2, gf2, ga2) = table(ss2)
            if not (pos2[1] == E): continue
            # output solution
            output_matches("[1]", ss1)
            output_table(pos1, pts1, gf1, ga1)
            printf("")
            output_matches("[2]", ss2)
            output_table(pos2, pts2, gf2, ga2)
            printf("\n")
      

      Solution: The scores were: EvN = 0-0; EvS = 0-1; EvW = 2-0; NvS = 1-0; NvW = 0-3; SvW = 0-0.


      At first I wasn’t sure whether we were using a “2 points for a win” or a “3 points for a win” scoring regime. (The puzzle text does not mention how the points are allocated).

      In the program I used “3 points for a win, 1 point for a draw”, but the answer is the same if “2 points for a win, 1 point for a draw” is used. This can be changed at line 5.

      When the program generates possible scorelines for the final 2 matches it only looks for matches with up to 6 goals scored in total. You can increase this value at line 74 of the program, but it doesn’t affect the answer found.

      Like

  • Unknown's avatar

    Jim Randell 8:18 am on 10 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 695: Sitting on a gate 

    From The Sunday Times, 10th November 1974 [link]

    Tom’s mother fetches him by car from school every day, starting out from home so that she arrives at school at 4:15 pm. He is waiting for her and gets into the car at once, and is driven home without delay. This is the normal routine.

    One day Tom expects to be delayed at school for an hour and his mother delays her time of leaving home accordingly. The expected delay does not take place so instead of telephoning home Tom sets out at 4:15 to walk towards home. After walking for half an hour he sits on a convenient roadside gate and waits for the car.

    When it comes he gets in at once and is driven straight home, where they arrive 48 minutes later than usual.

    For how many minutes was Tom sitting on the gate?

    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.

    [teaser695]

     
    • Jim Randell's avatar

      Jim Randell 8:19 am on 10 May 2022 Permalink | Reply

      We assume that the car is driven at a constant speed, which does not depend on the time of day.

      Tom sets off walking from the school at 4:15 pm and arrives at the gate at 4:45 pm.

      Mum sets off 60 minutes later than usual, and arrives back home 48 minutes later than usual.

      So Tom’s walking has cut 12 minutes off the car journey. Hence the gate is 6 minutes drive from the school.

      So, on a normal day, Mum would pass the gate 6 minutes before arriving at the school, i.e. at 4:09 pm.

      On the abnormal day, she left an hour later, and so arrives at the gate at 5:09 pm, by which time Tom has been waiting 24 minutes.

      Solution: Tom has been waiting at the gate for 24 minutes.

      Like

  • Unknown's avatar

    Jim Randell 3:58 pm on 6 May 2022 Permalink | Reply
    Tags:   

    Teaser 3111: Don’t miss a second 

    From The Sunday Times, 8th May 2022 [link] [link]

    I have an analogue wall clock with a second hand, and I also have a separate 24-hour hh:mm:ss digital clock. The wall clock loses a whole number of seconds over a two-digit period of seconds. The digital clock gains at a rate 2.5% greater than the wall clock loses. After resetting both clocks to the correct time, I noticed that later in the week, at a time one hour earlier than the time of setting, both clocks were displaying the same wrong time.

    I can reset one of the clocks at an exact hour so that it will show the correct time when the televised rugby kicks off at 19:15:00 on the 31st.

    What is the latest time (hour and date) when I can do this?

    The puzzle text has been reworded to (hopefully) remove an ambiguity.

    [teaser3111]

     
    • Jim Randell's avatar

      Jim Randell 5:39 pm on 6 May 2022 Permalink | Reply

      I assumed that the wall clock is a standard 12 hour clock.

      This Python program looks for possible fractions for the wall clock to run slow, and corresponding values for the digital clock to run fast, until we find a pair of values that allows the clocks to apparently show the same time, on a day one hour ahead of the time they were set.

      We can then look backwards for times when a clock could be set on the hour and report the correct time at 19:15:00 on the 31st. The first value we find is the latest possible time.

      The program runs in 112ms.

      from enigma import (Rational, irange, uniq, sprintf, printf)
      
      Q = Rational()
      
      # digital clock gains (41/40)k seconds for every k seconds lost by the
      # wall clock
      m = Q(41, 40)
      
      # gain for wall clock and digital clock after <t> seconds
      W = lambda f, t: int(t * (1 - f))
      D = lambda f, t: int(t * (1 + f * m))
      
      # convert (days, hours, minutes, seconds) to seconds
      hms2t = lambda d=0, h=0, m=0, s=0: s + (60 * (m + 60 * (h + 24 * d)))
      
      H12 = hms2t(h=12) # 12 hours
      H24 = hms2t(h=24) # 24 hours
      
      # format seconds as: [<days>+]<hh>:<mm>:<ss>
      def t2hms(t):
        (m, s) = divmod(t, 60)
        (h, m) = divmod(m, 60)
        (d, h) = divmod(h, 24)
        x = ('' if d == 0 else sprintf("{d}+"))
        return sprintf("[{x}{h:02d}:{m:02d}:{s:02d}]")
      
      # n = the 2-digit period of seconds
      # k = number of seconds the 12 hour clock loses every n seconds
      for f in uniq(Q(k, n) for n in irange(10, 99) for k in irange(1, n - 1)):
      
        # consider times that are 1 hour earlier than the setting time
        for h in irange(23, 168, step=24):
          t = hms2t(h=h)
          w = W(f, t)
          d = D(f, t)
          # are they separated by a multiple of 12 hours?
          (p, r) = divmod(d - w, H12)
          if r != 0: continue
          printf("f={f}; h={h} w={w} d={d} p={p}", w=t2hms(w), d=t2hms(d))
      
          tgt = hms2t(d=31, h=19, m=15)
          # consider clocks set (x hours + 15 minutes) ago
          for x in irange(0, 1000):
            t = hms2t(h=x, m=15)
            # wall clock (has it lost 12 hours?)
            w = t - W(f, t)
            if w % H12 == 0:
              printf("-> wall clock: set @ {s}", s=t2hms(tgt - t))
              break
            # digital clock (has it gained 24 hours?)
            d = D(f, t) - t
            if d % H24 == 0:
              printf("-> digital clock: set @ {s}", s=t2hms(tgt - t))
              break
          printf()
      

      Solution: The latest time is 11:00:00 (i.e. 11:00 am) on the 26th.


      The wall clock loses 32 seconds every 57 seconds (i.e. it goes forward at a rate of 25 seconds every 57 seconds).

      And the digital clock gains (41/40)×32 seconds every 57 seconds = 164/5 seconds every 57 seconds = 164 seconds every 285 seconds. So it goes forwards at a rate of 449 seconds every 285 seconds.

      These are very inaccurate clocks.

      If we suppose the clocks are put right at midnight (= 00:00:00), then in 95 hours time, it will be 23:00:00, almost 4 days later.

      And the wall clock will have advanced by:

      95 × 3600 × (25/57) = 150000 seconds
      = 1 day, 17 hours, 40 minutes

      So it will be showing a time of 5:40 (pm).

      The digital clock will have advanced by:

      95 × 3600 × (449/285) = 538800 seconds
      = 6 days, 5 hours, 40 minutes

      So it will be showing a time of 05:40:00.

      i.e. both clocks will be showing a time of exactly “twenty to six”.


      Now let us suppose the wall clock is set to the right time at 11:00:00 on the 26th.

      At 19:15:00 on the 31st the elapsed time is 5 days, 8 hours, 15 minutes = 461700 seconds.

      And the wall clock will be showing an elapsed time of:

      461700 × (25/57) = 202500 seconds
      = 2 days, 8 hours, 15 minutes

      i.e. the time showing will be: 7:15 (pm), as required.

      Like

  • Unknown's avatar

    Jim Randell 9:53 am on 5 May 2022 Permalink | Reply
    Tags:   

    Teaser 2856: Solid jellometry 

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

    As a wedding gift, Granny Lucci gave us a set of five jelly moulds, which was odd in many ways. The base of each mould was a different regular polygon (including a triangle) with each side an odd number of centimetres in length. Each polygon had the same length perimeter, an odd two figure number of centimetres.

    The sides of the moulds were vertical and the heights of the moulds were all the same odd number of centimetres. The volumes of the moulds were all between one and two litres.

    What was the height of the moulds?

    [teaser2856]

     
    • Jim Randell's avatar

      Jim Randell 9:55 am on 5 May 2022 Permalink | Reply

      This Python program run in 56ms (internal run time is 476µs).

      Run: [ @replit ]

      from math import (pi, tan)
      from enigma import (fdiv, irange, divisors, divf, divc, subsets, diff, intersect, printf)
      
      # area of a regular polygon with <n> sides and perimeter <p>
      def area(n, p):
        a = fdiv(pi, n)
        return fdiv(p * p, 4 * n * tan(a))
      
      # consider odd values for p = perimeter
      for p in irange(11, 99, step=2):
      
        # look for divisors of p (excluding 1)
        ds = list(d for d in divisors(p) if d > 1)
      
        # it must have at least 5 divisors, one of which is 3
        if not (len(ds) > 4 and 3 in ds): continue
      
        # consider possible odd heights for each divisor
        ps = dict()
        for n in ds:
          a = area(n, p)
          hs = list(h for h in irange(divc(1000, a), divf(2000, a)) if h % 2 == 1)
          if hs:
            ps[n] = hs
        # the smallest prism is triangular
        if 3 not in ps: continue
      
        # choose the 4 prisms with more than 3 sides
        for ns in subsets(diff(sorted(ps.keys()), [3]), size=4, fn=list):
          # add in the triangular prism
          ns.insert(0, 3)
          # find common heights
          for h in intersect(ps[n] for n in ns):
            # output solution
            printf("perimeter = {p}, height = {h}")
            for n in ns:
              printf("  {n} sides -> side = {x}; vol = {v:.2f}", x=(p // n), v=area(n, p) * h)
            printf()
      

      Solution: The moulds are 11 cm high.

      Each of the five moulds has a base with a perimeter of 45 cm.

      3 sides of 15 cm each; volume = 1071.71 cm³
      5 sides of 9 cm each; volume = 1532.95 cm³
      9 sides of 5 cm each; volume = 1700.00 cm³
      15 sides of 3 cm each; volume = 1746.59 cm³
      45 sides of 1 cm each; volume = 1769.71 cm³

      Like

    • Hugh+Casement's avatar

      Hugh+Casement 3:28 pm on 5 May 2022 Permalink | Reply

      There are only four values for the perimeter with enough divisors to give five different polygons.
      Of those, only 45 cm (and height 11 cm) results in all the volumes being between 1 and 2 litres.
      The polygons have sides 3, 5, 9, 15, and 45 sides.

      Like

  • Unknown's avatar

    Jim Randell 7:58 am on 3 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 693: Ready … 

    From The Sunday Times, 27th October 1974 [link]

    The Phytteness School marathon attracted 16 entrants this year. Each of the five houses entered a team of three runners, and the field was made up by the maths master, Des Chappelle. The school houses were competing for the trophy. The number of points by each entrant would be equal to his finishing position.

    The five houses tied for the cup, their totals being equal, although no two competitors tied for the same position. In order to determine the order in which the houses would hold the cup (they had agreed to hold it for 73 days each), they multiplied the finishers’ positions together in each house. The house with the smallest product, Black, would hold the cup first and so on to the house with the largest product, Blue, who hold it last. Unfortunately, Green and White houses still tied, and had to be separated by the toss of a coin.

    Des noted later that no house had had two finishers in consecutive positions, although Green house would have achieved this had he not managed to get between two of their runners right on the line.

    In what positions did the Red house runners finish?

    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.

    [teaser693]

     
    • Jim Randell's avatar

      Jim Randell 8:00 am on 3 May 2022 Permalink | Reply

      There are T(16) = 136 points available, but Des finished between two of the other runners, so he must have finished with 2 – 15 points. And the remaining positions must be shared between the 5 houses (3 positions each) to give the same sum.

      This Python program chooses a position for Des that give viable possible totals for each house, and then partitions the remaining positions among the houses. The groupings are then sorted by product, and the 3 groups in the middle of the field must have 2 products between them. The group with the unique product are Red.

      The program runs in 59ms (internal run time is 312µs).

      Run: [ @replit ]

      from enigma import (tri, tuples, irange, div, diff, multiply, group, filter2, singleton, printf)
      
      # total number of points
      T = tri(16)
      
      # check increasing integer sequence <s> has no consecutive elements
      check = lambda s: all(y - x > 1 for (x, y) in tuples(s, 2))
      
      # partition the values into <k>-length groups, each sums to <t>
      def partition(vs, k, t, ss=[]):
        if len(vs) == k:
          if sum(vs) == t and check(vs):
            yield ss + [tuple(vs)]
        else:
          v0 = vs[0]
          for (i, v1) in enumerate(vs):
            if i == 0: continue
            v2 = t - (v0 + v1)
            if not (v2 > v1): break
            if v2 in vs:
              s = (v0, v1, v2)
              if check(s):
                yield from partition(diff(vs, s), k, t, ss + [s])
      
      # consider position for D (between 2 others)
      for D in irange(2, 15):
        # calculate points for each house
        H = div(T - D, 5)
        if H is None: continue
      
        # partition the remaining positions into groups of 3
        for ss in partition(diff(irange(1, 16), [D]), 3, H):
          # sort the groups by product (largest first)
          ss.sort(key=multiply, reverse=1)
          # find the products of the middle three (G, W, R)
          p = group(ss[1:-1], by=multiply)
          if len(p.keys()) != 2: continue
          Rs = Ws = Gs = None
          for (k, vs) in p.items():
            if len(vs) == 1:
              # red has a unique product
              Rs = vs[0]
            else:
              # green/white have shared products; green has D - 1 and D + 1
              (Gs, Ws) = filter2((lambda s: D - 1 in s and D + 1 in s), vs, fn=singleton)
          if Rs and Ws and Gs:
            # output solutions
            printf("D={D}; H={H}")
            printf("houses = {ss}")
            printf("-> blue = {ss[0]}")
            printf("-> green = {Gs}")
            printf("-> white = {Ws}")
            printf("-> red = {Rs}")
            printf("-> black = {ss[-1]}")
            printf()
      

      Solution: Red house finished 2nd, 9th, 14th.

      The points awarded were:

      Des = 11
      Blue = (5, 7, 13); sum = 25; product = 455
      Green = (3, 10, 12); sum = 25; product = 360
      White = (4, 6, 15); sum = 25; product = 360
      Red = (2, 9, 14); sum = 25; product = 252
      Black = (1, 8, 16); sum = 25; product = 128

      Like

    • GeoffR's avatar

      GeoffR 10:32 am on 3 May 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 2..15: des;  % Maths master - Des Chappelle
      
      var 1..16: a; var 1..16: b;var 1..16: c;  % Blue's points
      constraint b - a != 1 /\ c - b != 1 /\ b > a /\ c > b;
      
      var 1..16: d; var 1..16: e;var 1..16: f;  % Green's points
      constraint e - d != 1 /\ f - e != 1 /\ e > d /\ f > e;
      
      var 1..16: g; var 1..16: h;var 1..16: i;  % White's points 
      constraint h - g != 1 /\ i - h != 1 /\ h > g /\ i > h;
      
      var 1..16: j; var 1..16: k;var 1..16: l;  % Red's points 
      constraint k - j != 1 /\ l - k != 1 /\ k > j /\ l > k;
      
      var 1..16: m; var 1..16: n;var 1..16: o;  % Black's points
      constraint n - m != 1 /\ o - n != 1 /\ n > m /\ o > n;
      
      % No two competitors tied for the same position
      constraint all_different ([des,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o]);
      
      % The five houses tied for the cup, their totals being equal
      constraint a + b + c == d + e + f /\ a + b + c == g + h + i
      /\ a + b + c == j + k + l /\ a + b + c == m + n + o;
      
      % Black's points have the smallest product
      constraint m * n * o < j * k * l /\  m * n * o < g * h * i
      /\ m * n * o < d * e * f /\ m * n * o < a * b * c;
      
      % Blue's points have the largest product
      constraint a * b * c > d * e * f /\ a * b * c > g * h * i
      /\ a * b * c > j * k * l /\ a * b * c > m * n * o;
      
      % The product of Green's points equals the product of White's points
      constraint d * e * f == g * h * i;
      
      % Location of Des' points in relation to the Green house points
      constraint (des - d == 1 /\ e - des == 1 ) 
      \/ (des - e == 1 /\ f - des == 1);
      
      solve satisfy;
      
      output ["Des = " ++ show(des) ++ "\n" ++
      "Blue:   [a,b,c] = " ++ show([a,b,c]) ++ "\n" ++
      "Green:  [d,e,f] = " ++ show([d,e,f]) ++ "\n" ++
      "White:  [g,h,i] = " ++ show([g,h,i]) ++ "\n" ++
      "Red:    [j,k,l] = " ++ show([j,k,l]) ++ "\n" ++
      "Black:  [m,n,o] = " ++ show([m,n,o]) ];
      
      % Des = 11
      % Blue:   [a,b,c] = [5, 7, 13]
      % Green:  [d,e,f] = [3, 10, 12]
      % White:  [g,h,i] = [4, 6, 15]
      % Red:    [j,k,l] = [2, 9, 14]  <<< Answer
      % Black:  [m,n,o] = [1, 8, 16]
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 4:33 pm on 29 April 2022 Permalink | Reply
    Tags:   

    Teaser 3110: Many a slip 

    From The Sunday Times, 1st May 2022 [link] [link]

    I have written down three 3-figure numbers in decreasing order and added them up to give their 4-figure sum, which is a perfect square: the digit 0 occurred nowhere in my sum.

    Now I have attempted to replace digits consistently by letters and I have written the sum as:

    CUP + AND + LIP = SLIP

    However, there’s “many a slip twixt cup and lip” and unfortunately one of those thirteen letters is incorrect. If you knew which letter was incorrect then you should be able to work out the three 3-figure numbers.

    What are they?

    [teaser3110]

     
    • Jim Randell's avatar

      Jim Randell 5:15 pm on 29 April 2022 Permalink | Reply

      I used a variation on the [[ bungled_sum() ]] function that I have used before in other puzzles (see: Puzzle 56, Teaser 2952).

      When the setter transcribed the sum to an alphametic there are two ways a mistake could be made. Firstly a symbol could be assigned to more than one digit. And secondly a digit could be assigned to more than one symbol (assuming the setter is aiming for each letter standing for a different digit – although this is not explicitly stated in the puzzle text)).

      We consider each symbol in the alphametic sum in turn, and replace it by a “wild card” symbol to create a modified sum. If the puzzle created with this modified sum has a single solution then we have found the answer to the original puzzle.

      The following Python program runs in 276ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, irange, union, update, singleton, join, printf)
      
      # the terms in the alphametic sum
      terms = ['CUP', 'AND', 'LIP', 'SLIP']
      
      # we use X to denote the bungled letter
      assert 'X' not in union(terms)
      
      # solve the alphametic sum and return the values of the terms
      def solve(terms, i, j):
        # create terms for the modified sum
        ts = update(terms, [(i, update(t, [(j, 'X')]))])
        # split the terms into the summands and the result
        ss = list("{" + x + "}" for x in ts)
        r = ss.pop(-1)
        # make the expressions
        exprs = [
          # the sum
          join(ss, sep=" + ") + " = " + r,
          # summands are in descending order
          join(ss, sep=" > "),
          # result is a square
          "is_square_p(" + r + ")",
        ]
        # distinct symbols
        syms = union(ts).difference("X")
        distinct = [ join(syms) ]
        # is the replaced symbol still present in the modified sum?
        x = terms[i][j] # replaced symbol
        if x in syms:
          # X must be different from the replaced symbol
          distinct.append('X' + x)
        else:
          # X must be the same as one of the other symbols
          exprs.append("{X} in {{" + join(syms, sep="}, {") + "}}")
        # create the modified puzzle (digits used are 1-9)
        p = SubstitutedExpression(exprs, digits=irange(1, 9), distinct=distinct, verbose=0)
        # find solutions
        for s in p.solve():
          # return the values of the terms
          yield tuple(int(p.substitute(s, t)) for t in ts)
      
      # consider the suspect term
      for (i, t) in enumerate(terms):
        # and the suspect symbol in the term
        for (j, x) in enumerate(t):
          # is there a unique solution to a modified puzzle?
          vs = singleton(solve(terms, i, j))
          if vs is None: continue
          # return: bungled symbol (term, index) and the values of the terms
          printf("term {i} [{t}], index {j} -> {vs}")
      

      Solution: The numbers are: 826, 574, 536.

      So we have:

      826 + 574 + 536 = 1936 (= 44²)

      Which when substituted as:

      CUP + AND + LIP = SLIP

      Makes the A and the first L map to 5, and the second L maps to 9.

      This corresponds to a single mistake in transcribing the 5 in 536 to L, when it should be mapped to A. (i.e. a correct transcription is: CUP + AND + AIP = SLIP).

      And this is the only sum that correspond to a mistake at the 1st digit of the 3rd summand (i.e. the L of LIP).


      The other potential sums, which are discounted as there is more than one sum corresponding to a mistake at the same position, are:

      522 + 478 + 369 = 1369 (= 37²)
      572 + 428 + 369 = 1369 (= 37²)

      These two sums correspond to mistakes in transcribing the 3rd digit of the 1st summand (i.e. the P of CUP), or the 3rd digit of the 3rd summand (i.e. the P of LIP).

      And (possibly):

      529 + 471 + 369 = 1369 (= 37²)
      579 + 421 + 369 = 1369 (= 37²)

      These two sums correspond to mistakes in transcribing the 3rd digit if the second summand (i.e. the D in AND), or the 1st digit of the result (i.e. the S in SLIP).

      Although if the setter is only attempting to “replace digits consistently by letters”, but not have “each letter standing for a different digit”, then this last scenario would not count as having a mistake in it.

      Like

    • GeoffR's avatar

      GeoffR 8:19 pm on 29 April 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Four digit squares list
      set of int: sq4 = {x*x | x in 32..99}; 
      
      % Alternative substituted sum - to compare digits.
      % abc + efg + hij = kmnp
      var 1..9:a; var 1..9:b; var 1..9:c;
      var 1..9:e; var 1..9:f; var 1..9:g;
      var 1..9:h; var 1..9:i; var 1..9:j;
      var 1..9:k; var 1..9:m; var 1..9:n; var 1..9:p;
      
      % letters in original sum [C,U,P,A,N,D,L,I,S]
      var 1..9:C; var 1..9:U; var 1..9:P;
      var 1..9:A; var 1..9:N; var 1..9:D;
      var 1..9:L; var 1..9:I; var 1..9:S;
      
      % Components of the substituted sum
      var 100..999:abc == 100*a + 10*b + c;
      var 100..999:efg == 100*e + 10*f + g;
      var 100..999:hij == 100*h + 10*i + j;
      var 1000..9999:kmnp == 1000*k + 100*m + 10*n + p;
      
      % The substituted sum
      constraint abc + efg + hij = kmnp;
      
      % Three 3-digit numbers are in descending order
      constraint abc > efg /\ efg > hij;
      
      % 4-figure sum is a perfect square:
      constraint kmnp in sq4;
      
      % Substituted sum has 9 correct digits (1..9) from 13 digits
      constraint card({a,b,c,e,f,g,k,m,n,p}) == 9;
      
      % Teaser sum has 8 correct digits (1..9) from 13 digits
      %... since 1 digit is repeated
      constraint card( {C,U,P,A,N,D,L,I,S} ) == 8;
      
      % Teaser Sum: CUP + AND + LIP = SLIP
      %
      % Only 1 letter is repeated in capital letters sum
      % ... so compare letters in original and substituted sums
      constraint sum ([ a != C, b != U, c != P,
                        e != A, f != N, g != D,
                        h != L, i != I, j != P,
                        k != S, m != L, n != I,
                        p != P]) == 1;   
      
      solve satisfy;
      
      output [ show(abc) ++ " + " ++ show(efg) ++ " + " ++ 
      show(hij) ++ " = " ++ show(kmnp) ];
      
      % There was one unique sum from 11 total multiple outputs
      % This unique sum gives the values of CUP, AND and LIP
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:33 am on 28 April 2022 Permalink | Reply
    Tags:   

    Teaser 2853: Marble marvel 

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

    I had a cuboid (or rectangular block) of marble whose volume in cubic centimetres was divisible by each of the numbers 1, 2, 3, 4, 5, 6, 7, 8 and 9. Unfortunately I dropped the block and it broke into two cuboids, and surprisingly the two new blocks were of similar shape to each other (i.e. one was a magnification of the other). The lengths of the sides of the original block and the two new blocks were all two-figure numbers of centimetres.

    What were the lengths of the sides of the original block?

    [teaser2854]

     
    • Jim Randell's avatar

      Jim Randell 10:34 am on 28 April 2022 Permalink | Reply

      The block splits along a plane parallel to the face with the smallest area, to give two similar blocks with dimensions (a, b, c) and (b, c, d) (with dimensions in increasing order).

      The blocks are similar, so we have:

      b / a = c / b = d / c

      From which we can calculate c and d given values for a and b:

      c = b² / a
      d = c² / b = b³ / a²

      The original block is (b, c, a + d) and the volume of this is divisible by 2520 (= lcm(1, …, 9)).

      And each of (a, b, c, d, a + d) must be 2-digit integers.

      This Python program runs in 53ms (internal run time is 507µs).

      Run: [ @replit ]

      from enigma import (irange, mlcm, call, printf)
      
      # total volume must be divisible by m
      m = call(mlcm, irange(1, 9))
      
      # choose values for a, b
      for a in irange(10, 99):
        a2 = a * a
        for b in irange(a, 99):
          b2 = b * b
          # calculate d
          (d, r) = divmod(b2 * b, a2)
          if a + d > 99: break
          if r != 0: continue
          # calculate c
          (c, r) = divmod(b2, a)
          if r != 0: continue
          # check total volume is divisible by m
          if (b * c * (a + d)) % m != 0: continue
          # output solution
          printf("({a}, {b}, {c}) + ({b}, {c}, {d}) = ({b}, {c}, {ad})", ad=a + d)
      

      Solution: The original block measured 24 cm × 36 cm × 70 cm.

      Like

    • GeoffR's avatar

      GeoffR 6:28 pm on 28 April 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % All dimensions must be 2-digit integers
      % The original block has dimensions (B, C, A + D)
      var 10..99:A; var 10..99:B; var 10..99:C; var 10..99:D;
      
      constraint all_different ([A, B, C, D]) /\ A + D >= 20 /\ A + D <=99;
      constraint B > A /\ C > B /\ D > C;
      
      % Volume of block was divisible by 1, 2, 3, 4, 5, 6, 7, 8 and 9.
      constraint B * C * (A + D) mod 2520  == 0;
       
      % Reusing Jim's equations
      constraint A * C == B * B;
      constraint D * B == C * C /\ D * A * A == B * B * B;
      
      solve satisfy;
      
      output ["Original Block Dimensions were " ++ show(B) ++ " by " 
      ++ show(C) ++ " by " ++ show(A + D) ++ " cm." ];
      
      % Original Block Dimensions were 24 by 36 by 70 cm.
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:39 am on 26 April 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 690: … And two poles 

    From The Sunday Times, 6th October 1974 [link]

    In my garden, which is level, there are two vertical flagpoles and although there is little apparent difference in their respective heights I know that their actual heights are each an exact but different number of inches.

    As part of their anchoring taut wires stretch from the very top of each to the foot of the other. The crossing-point of these two wires is exactly eleven feet from the ground.

    What are the heights of the flagpoles?

    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.

    [teaser690]

     
    • Jim Randell's avatar

      Jim Randell 9:42 am on 26 April 2022 Permalink | Reply

      When resistors with values r1 and r2 are connected in parallel, the value of the combined resistance R is given by:

      1/R = 1/r1 + 1/r2

      (See: Teaser 3058).

      The value of R can also be calculated using the following diagram:

      Where the height of the crossing point gives the combined resistance.

      The flagpoles in the question also correspond to this diagram. Their heights being r1 and r2 and the crossing point being 11ft = 132in above the ground corresponds to R.

      So we need to find two different values whose reciprocals sum to 1/132. And the [[ reciprocals() ]] function in the enigma.py library can do this for us.

      The following Python program runs in 59ms.

      Run: [ @replit ]

      from enigma import (Accumulator, reciprocals, sprintf, printf)
      
      # find (a, b) such that 1/a + 1/b = 1/132
      # record the smallest difference (i.e. the final candidate)
      r = Accumulator(fn=min)
      for (a, b) in reciprocals(2, 132):
        # a, b must be different
        if a == b: continue
        r.accumulate_data(b - a, (a, b))
        printf("[a={a} b={b}]")
      
      # format <x> inches as "<f>ft <i>in"
      def fmt(x):
        (d, r) = divmod(x, 12)
        return sprintf("{d}ft {r}in")
      
      # output solution
      (a, b) = r.data
      printf("A = {a}; B = {b}", a=fmt(a), b=fmt(b))
      

      Solution: The heights of the flagpoles are: 21ft 1in, 23ft 0in.

      This is the solution where the flagpoles are as close as possible in height (we have: 1/253 + 1/276 = 1/132).

      But there are other candidate solutions where the difference in height is greater, e.g. the next solution would be 1/231 + 1/308 = 1/132 to give values of 19ft 3in and 25ft 8in.

      So to narrow the solution down to a single candidate we could say the flagpoles had a difference in height of less than 6ft.

      Like

    • GeoffR's avatar

      GeoffR 11:12 am on 26 April 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Assume Max flagpole height = 30 ft.
      var 1..360:a;
      var 1..360:b;
      
      % 1/a + 1/b = 1/132
      constraint 132 * (b + a) == a * b;
      
      % extra constraint for a single solution
      % ...the flagpoles had a difference in height of less than 6ft
      constraint a > b /\ a - b  < 72;
      
      solve minimize(a - b);
      
      output ["Heights of flagpoles = " ++ show(a) ++ 
      " and " ++ show(b) ++  " inches." ];
      
      % Heights of flagpoles = 276 and 253 inches.
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:48 am on 24 April 2022 Permalink | Reply
    Tags: by: S. T. Crump   

    Brain Teaser: Crossing the desert 

    From The Sunday Times, 29th March 1959 [link]

    David and Henry live on opposite sides of a desert which is 2,020 miles across. David has a lorry which can carry sufficient petrol for 1,008 miles, and Henry has a lorry which can carry petrol for 504 miles. They wish to meet in the desert and then return to their original starting-points. They can, of course, dump or exchange petrol.

    Assuming that Henry’s lorry can carry as much petrol as David’s, but consumes petrol at twice the rate, where should Henry and David meet if between the two of them they are to use the least amount of petrol?

    This is 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 was offered.

    [teaser-1959-03-29] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 9:50 am on 24 April 2022 Permalink | Reply

      (See also: Enigma 1732, Teaser 2799 and [ @wikipedia ]).

      I am assuming that each driver fills up their vehicle with 1008 units of fuel at their base, and may establish fuel dumps in the desert using this fuel for later use.

      For the meeting each driver fills up and drives their vehicle to the meeting point, possibly using fuel from dumps they have previously established. At the meeting point fuel may be exchanged between the two, and then they return to their respective bases (again, possibly using fuel from dumps they have previously established).

      And I’m assuming there are no tricks, like abandoning their vehicles and walking some distance to a base, fuel dump or meeting point.

      When they are “exploring the desert” they each end up at their final dump with a full complement of fuel, and there is enough fuel to get them back to their home base if they return to the final base with no fuel remaining.

      So if they do not exchange any fuel, then D could travel 504 miles from his final dump and H could travel 252 miles from his final dump and they would each have enough fuel to get back to their respective final dumps. So, if no fuel is exchanged, the final dumps could be a maximum of 756 miles apart.

      However if they were to exchange fuel this distance can be increased. D’s vehicle is more efficient, so if H stays at his final dump, D can use his full tank to drive 1008 miles to H’s final dump, and arrive empty. D’s vehicle is fully refuelled at the dump, and both vehicles then have enough fuel to return to their respective bases.

      So we need to look for a minimum number of trips for H and D to establish final dumps 1008 miles apart.

      This Python program runs in 61ms.

      Run: [ @replit ]

      from enigma import (fdiv, irange, Accumulator, cache, printf)
      
      Q = fdiv  # floats are good enough
      
      R = 1008  # range (for D; H is half this)
      T = 2020  # distance between bases
      S = R  # max separation of final dumps
      
      # consider number of trips for D and total distance
      # (H can only go half as far in k trips)
      
      # consider distance to dump k (dump 1 = base)
      @cache
      def D(k):
        return (Q(R, 2 * k) + D(k - 1) if k > 1 else 0)
      
      # find minimum fuel use
      r = Accumulator(fn=min)
      
      # consider number of trips for H
      # (one trip is 0 length, as the fuel is given to D)
      for h in irange(1, 10):
        # position of H's final dump
        dh = Q(D(h), 2)
        # consider trips for D to reach H's final dump
        for d in irange(1, 20):
          # position of D's final dump
          dd = D(d)
          # separation between final dumps
          s = T - dh - dd
          if s > S: continue
          printf("D makes {d} trips; H makes {h} trips; total = {t}", t=d + h)
          printf("-> D's final dump is {dd:.2f} miles from base")
          printf("-> H's final dump is {dh:.2f} miles from base")
          printf("-> separation is {s:.2f} miles")
          # accumulate by minimum units of fuel
          r.accumulate_data(R * (d + h) - (S - s), (d, h, s))
          break
      
      (u, (d, h, s)) = (r.value, r.data)
      printf()
      printf("min = {t} trips, {u:.2f} units; final separation = {s:.2f} miles", t=d + h)
      

      Solution: They should meet 210 miles from Henry’s base.

      David should set up 6 fuel dumps at (72, 156, 256.8, 382.8, 550.8, 802.8) miles, and can then proceed to the final dump. This requires filling up at base 7 times.

      Henry should set up 2 fuel dumps at (84, 210) miles, and then proceed to the final dump. This requires filling up at base 3 times.

      Both David and Henry are at the final dumps with 1008 units of fuel each, and enough fuel at their respective dumps to get them back home if they arrive at the final dump with no fuel.

      The distance between the final dumps is 1007.2 miles, so David can drive between them with 0.8 units of fuel to spare. He can then replenish his supply, using Henry’s fuel, so he has enough to return to his final dump (and then there is enough fuel in his dumps to get him back to his base). And Henry has enough fuel in his dumps to return to his base.

      Altogether there are 10 fillings at the bases (7 at David’s and 3 at Henry’s), accounting for 10080 units of fuel. And when the two return to their bases there is 0.8 units of fuel remaining.

      Like

  • Unknown's avatar

    Jim Randell 4:38 pm on 22 April 2022 Permalink | Reply
    Tags:   

    Teaser 3109: Hole numbers 

    From The Sunday Times, 24th April 2022 [link]

    In theoretical golf, you have a set of “clubs” each of which hits a ball an exact number of yards forwards or backwards. You own the following set: 3, 8, 17, 19 and 35. For example, if a hole is 31 yards long, you can reach it in three strokes, with two forward hits of the 17 and a backward hit of the 3. In the next competition, you are only allowed to use three clubs, and the course consists of three holes whose lengths are 101, 151 and 197 yards. In order to get round the course in the fewest possible strokes, you must make a wise choice of clubs.

    Which three clubs (in ascending order) should you choose, and what will the individual hole scores be (in order)?

    [teaser3109]

     
    • Jim Randell's avatar

      Jim Randell 4:59 pm on 22 April 2022 Permalink | Reply

      This Python program runs in 59ms. (Internal runtime is 3.7ms).

      Run: [ @replit ]

      from enigma import ordered, subsets, Accumulator, printf
      
      clubs = [3, 8, 17, 19, 35]
      holes = [101, 151, 197]
      
      # generate hole lengths and strokes using the specified clubs
      # m = max number of strokes
      def generate(clubs, m):
        # record total distance and individual strokes
        d = { 0: () }
        # distances to extend
        ts = list(d.keys())
        while ts:
          ts_ = list()
          for t in ts:
            # choose a club
            for n in clubs:
              # forwards or backwards?
              for x in (n, -n):
                t_ = t + x
                # is this a new distance?
                if t_ > 0 and t_ not in d:
                  ns_ = ordered(x, *d[t], reverse=1)
                  yield (t_, ns_)
                  d[t_] = ns_
                  if len(ns_) < m:
                    ts_.append(t_)
          # process the next batch
          ts = ts_
      
      # find lowest total number of strokes
      r = Accumulator(fn=min, collect=1)
      
      # choose 3 clubs
      for cs in subsets(clubs, size=3):
        # attempt to make the required holes
        rs = list()
        for (t, ns) in generate(cs, 30):
          if t in holes:
            rs.append((t, ns))
            if len(rs) == 3:
              # find total number of strokes
              ts = sum(len(ns) for (t, ns) in rs)
              printf("[cs={cs} -> {ts} strokes]")
              r.accumulate_data(ts, (cs, rs))
              break
      
      # output solution
      printf()
      printf("min = {r.value} strokes")
      for (cs, rs) in r.data:
        printf("-> clubs = {cs}")
        for (t, ns) in sorted(rs):
          printf("--> hole {t} = {ns}; {n} strokes", n=len(ns))
        printf()
      

      Solution: The optimal set of clubs is (3, 17, 35). Which achieves the 101 hole in 5 strokes, the 151 hole in 7 strokes, the 197 hole in 9 strokes, for a total of 21 strokes.

      hole 101 = (35, 35, 17, 17, −3)
      hole 151 = (35, 35, 35, 35, 17, −3, −3)
      hole 197 = (35, 35, 35, 35, 17, 17, 17, 3, 3)

      If the selection of clubs is not restricted the holes can be achieved in 18 strokes (although only 3 clubs are used per hole):

      hole 101 = (35, 35, 17, 17, −3)
      hole 151 = (35, 35, 35, 35, 8, 3)
      hole 197 = (35, 35, 35, 35, 35, 19, 3)

      Like

    • GeoffR's avatar

      GeoffR 4:39 pm on 24 April 2022 Permalink | Reply

      This solution uses MiniZinc’s minimize constraint to minimise the total strokes for the clubs used for the three holes.

      It finds five sets of minimum total strokes in decreasing magnitude,
      The first four sets use clubs(3,8,35)

      The last (5th) print out is the answer to this teaser and uses a different set of three clubs to the first four solutions.

      % A Solution in MiniZinc
      include "globals.mzn";
       
      % Fixed golf hole lengths (yd), specified in teaser
      int: H1 = 101;  
      int: H2 = 151; 
      int: H3 = 197; 
      
      % a set of “clubs”
      set of int: clubs = {3, 8, 17, 19, 35};
      
      % Three clubs used from a set of six clubs
      var clubs: c1; var clubs: c2; var clubs: c3;
      constraint all_different([c1, c2, c3]);
      
      % Multiples of club strokes for each hole for three clubs
      % .. allowing for 3rd multiple as possible backward strokes
      var 1..5: m1; var 1..5: m2; var -5..5: m3;  % Hole 101 yd
      var 1..5: m4; var 1..5: m5; var -5..5: m6;  % Hole 151 yd
      var 1..5: m7; var 1..5: m8; var -5..5: m9;  % Hole 197 yd
      
      % 1st hole(101 yd)
      constraint H1 == m1*c1 + m2*c2 + m3*c3;
      % 2nd hole(151 yd)
      constraint H2 == m4*c1 + m5*c2 + m6*c3;
      % 3rd hole(197 yd)
      constraint H3 == m7*c1 + m8*c2 + m9*c3;
      
      % use abs function for 3rd club possible negative value
      solve minimize(m1 + m2 + abs(m3) + m4 + m5 + abs(m6) + m7 + m8 + abs(m9));
      
      output ["Total strokes = " ++ 
      show (m1 + m2 + abs(m3) + m4 + m5 + abs(m6) + m7 + m8 + abs(m9))
      ++ ", Clubs used = " ++ show([c1, c2, c3]) 
      ++ "\n" ++ "Hole 101 yd = " ++ show([m1, c1, m2, c2, m3, c3])
      ++ "\n" ++ "Hole 151 yd = " ++ show([m4, c1, m5, c2, m6, c3])
      ++ "\n" ++ "Hole 197 yd = " ++ show([m7, c1, m8, c2, m9, c3])
      ];
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:46 am on 21 April 2022 Permalink | Reply
    Tags:   

    Teaser 2857: Significant errors 

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

    Last year George and Martha were going to visit one of their daughters. Her house number was a two-figure number but unfortunately Martha wrote the number incorrectly by making the first digit less than it should be. When George discovered the error he commented that the incorrect number was a whole number percentage of the correct one. If you knew that percentage then you should be able to work out their daughter’s house number.

    Then the daughter moved to a different two-figure house number, Martha again wrote the number incorrectly by making the first digit less than it should be, and again the incorrect number was a whole number percentage of the correct one. If you knew that percentage then you should be able to work out their daughter’s new house number.

    What was the daughter’s original house number, and what is the new one?

    [teaser2857]

     
    • Jim Randell's avatar

      Jim Randell 8:46 am on 21 April 2022 Permalink | Reply

      This Python program collects possible (house-number, wrong-number, percentage-reduction) values, and then uses the [[ filter_unique() ]] function from the enigma.py library to find the required values for the first and second house numbers.

      It runs in 55ms. (Internal runtime is 333µs).

      Run: [ @replit ]

      from enigma import (irange, div, filter_unique, unpack, printf)
      
      # record (n, w, p) results
      rs = list()
      
      # consider two digit house numbers
      for n in irange(20, 99):
        # consider different numbers with the first digit less than the
        # correct digit, but the second digit is correct
        for w in irange(10 + (n % 10), n - 1, step=10):
          # calculate the percentage w / n
          p = div(100 * w, n)
          if p is None: continue
          rs.append((n, w, p))
      
      # if you knew the percentage you would know the correct house number
      fn_n = unpack(lambda n, w, p: n)
      fn_p = unpack(lambda n, w, p: p)
      for (n1, w1, p1) in filter_unique(rs, fn_p, fn_n).unique:
        printf("p1={p1}% -> n1={n1} w1={w1}")
      
        # look for results with a different house number
        rs1 = filter(unpack(lambda n2, w2, p2: n2 != n1), rs)
        for (n2, w2, p2) in filter_unique(rs1, fn_p, fn_n).unique:
          printf("  p2={p2}% -> n2={n2} w2={w2}")
        printf()
      

      Solution: The first house number was 50. The second house number was 75.

      There are two possible scenarios:

      Daughter’s first house was 50, but she wrote 20 (= 40% of 50).
      Daughter’s second house was 75, but she wrote 15 (= 20% of 75).

      Daughter’s first house was 50, but she wrote 40 (= 80% of 50).
      Daughter’s second house was 75, but she wrote 15 (= 20% of 75).

      But in both cases the first house number is 50 and the second house number is 75.


      Here are the 15 possible (house number, wrong number) pairs, grouped by percentage:

      20%: (50 → 10) (75 → 15)
      25%: (40 → 10) (80 → 20)
      40%: (50 → 20)
      50%: (20 → 10) (40 → 20) (60 → 30) (80 → 40)
      60%: (25 → 15) (50 → 30) (75 → 45)
      75%: (40 → 30) (80 → 60)
      80%: (50 → 40)

      From which we see the only values with a unique percentage are:

      40%: (50 → 20)
      80%: (50 → 40)

      Both cases give a first house number of 50.

      If we remove pairs with a first house number of 50 from those listed above we get:

      20%: (75 → 15)
      25%: (40 → 10) (80 → 20)
      50%: (20 → 10) (40 → 20) (60 → 30) (80 → 40)
      60%: (25 → 15) (75 → 45)
      75%: (40 → 30) (80 → 60)

      And we see there is only one value with a unique percentage:

      20%: (75 → 15)

      And so the second house number is 75.

      Like

  • Unknown's avatar

    Jim Randell 9:38 am on 19 April 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 668: Sweepstakes sixfold 

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

    A party of six racegoers arrived on the course with precisely £6 each with which to speculate. There were six races on the card and six runners in each race, so they decided to hold sweepstakes among themselves on each of the six races, the stake being £1 per race per person.

    The runners in each race were numbered 1, 2, 3, 4, 5 and 6, and each of the racegoers drew one of these numbers out of a hat. Each player’s number remained the same throughout the six races. There were thus, in effect, six separate sweepstakes, the holder of the winning number drawing £6 on each race. To add a little interest to the proceedings it was arranged that the winner on any one of the races would be permitted to buy (at cost price of £1) one additional chance in one or more of the races subsequent to his win, from one of the other players. Only a player who had not yet had a win could sell his chance.

    At the conclusion of the events it transpired that three players had made a net profit; the holder of No. 1 who won £4, the holder of No. 5 who won £1, and the holder of No. 4. The holder of No. 2 lost £3, and the holder of No. 6 lost £6.

    Three winning chances were sold, but none of these by the holders of Nos 1, 4 and 5. The holder of No. 1 did not have any transaction with the holders of Nos 2 and 3. There were no dead-heats and no number appeared in the winner’s frame in consecutive races.

    What, in order, were the numbers of the six winning horses?

    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.

    [teaser668]

     
    • Jim Randell's avatar

      Jim Randell 9:40 am on 19 April 2022 Permalink | Reply

      At the start of the races each participant has a profit of −6 (having bought the 6 tickets with their own number on).

      And in each race, the outcome for each participant is:

      +6 = no transaction, win (with own ticket)
      +5 = buy ticket, win (with own or bought ticket)
      +1 = sell ticket
      0 = no transaction, don’t win
      −1 = buy ticket, don’t win (with either ticket)

      And there are some additional restrictions: a +5 or −1 can only come after a +6, and a +1 cannot come after a +6.

      And we can use these facts to check that there is a sequence of viable outcomes for each participant (without this the following code takes a lot longer to run, but you can change valid() to always return True to try the slower version, but it will take a lot longer).

      This Python program looks for a viable sequence of winners and ticket transactions for each race. It runs in 4.28s.

      Run: [ @replit ]

      from enigma import (irange, subsets, product, diff, first, printf)
      
      # update a dictionary with given deltas
      def update(d, *kvs):
        d = d.copy()
        for (ks, v) in kvs:
          for k in ks:
            d[k] += v
        return d
      
      # find possible length k sequences that give total t
      def complete(k, t, f=0, ss=[]):
        xs = [[0, 1, 6], [-1, 0, 5, 6]][f]
        if k == 0:
          if t == 0:
            yield ss
        elif k == 1:
          if t in xs:
            yield ss + [t]
        else:
          # choose an outcome
          for x in xs:
            yield from complete(k - 1, t - x, f or (x == 6), ss + [x])
      
      # is there a valid completion?
      valid = lambda k, t, f: bool(first(complete(k, t, f)))
      
      # totals we are given (also #3 not >0; #4 >0)
      totals = { 1: 4, 5: 1,  2: -3, 6: -6 }
      
      # solve the puzzle
      # n = remaining races
      # vs = ticket values
      # m = money for each participant
      # ws = winning tickets for each race
      # ps = winning players for each race
      # bs = potential buyers
      def solve(n, vs, m, ws=list(), ps=list(), bs=set()):
        if n == 0:
          # check totals
          if not (m[4] > 0 and not (m[3] > 0) and all(m[k] == v for (k, v) in totals.items())): return
          # check three winning tickets were sold
          if not (sum(x != y for (x, y) in zip(ws, ps)) == 3): return
          # viable solution
          yield (m, ws, ps)
        else:
          # choose some buyers and sellers
          b = len(bs)
          for k in irange(0, min(b, 6 - b)):
            for (bs1, ss1) in product(subsets(bs, size=k), subsets(diff(vs, bs), size=k, select="P")):
              # determine who gets the winnings
              d = dict(zip(ss1, bs1))
              # but 1 did not have any transactions with 2 or 3
              if any(d.get(k, 0) == v for (k, v) in [(1, 2), (1, 3), (2, 1), (3, 1)]): continue
              # perform the transactions
              m1 = update(m, (bs1, -1), (ss1, +1))
              # at this point we can check the targets of any sellers are reachable
              if any(not valid(n - 1, totals[k] - m1[k], k in bs) for k in ss1 if k in totals): continue
      
              # choose a winner for the race
              for w in vs:
                # no consecutive winners
                if ws and ws[-1] == w: continue
                # has the winning ticket been sold?
                p = d.get(w, w)
                if p != w:
                  # winning ticket was sold, but not by 1, 4, 5
                  if w in {1, 4, 5}: continue
                  # and only 3 winning tickets were solve
                  if sum(x != y for (x, y) in zip(ws, ps)) > 2: continue
                # allocate the winnings
                m_ = update(m1, ([p], +6))
                bs_ = bs.union([p])
                # check all (non-seller) targets are reachable
                if any(not valid(n - 1, v - m_[k], k in bs_) for (k, v) in totals.items() if k not in ss1): continue
                # solve for the remaining races
                yield from solve(n - 1, vs, m_, ws + [w], ps + [p], bs_)
      
      # initial values
      vs = list(irange(1, 6)) # available tickets
      m = dict((k, -6) for k in vs) # initial amounts
      for (m, ws, ps) in solve(6, vs, m):
        printf("winning tickets = {ws}; profits = {m}", m=list(m[i] for i in vs))
      

      Solution: The numbers of the winning horses were: 5, 4, 2, 3, 2, 1.


      One scenario is:

      Initially: All on −6
      Race 1: 5 wins; #5 → 0
      Race 2: #5 buys 1; 4 wins; #1 → −5, #4 → 0, #5 → −1
      Race 3: #4 buys 1, #5 buys 2; 2 wins; #1 → −4, #2 → −5, #4 → −1, #5 → +4
      Race 4: #4 buys 3, #5 buys 1; 3 wins; #1 → −3, #3 → −5, #4 → +4, #5 → +3
      Race 5: #4 buys 2, #5 buys 1; 2 wins; #1 → −2, #2 → −4, #4 → +9, #5 → +2
      Race 6: #4 buys 3, #5 buys 2; 1 wins; #1 → +4, #2 → −3, #3 → −4, #4 → +8, #5 → +1

      There are alternative scenarios, in each we have (#3, #4) → (−4, +8) or (−5, +9).

      But in each scenario the winning tickets are (5, 4, 2, 3, 2, 1).

      Like

  • Unknown's avatar

    Jim Randell 9:42 am on 17 April 2022 Permalink | Reply
    Tags:   

    An Easter Brain-Teaser: Indoor sports 

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

    We know them familiarly as Jennifer, Judith and Jean, but in age order they are the doctor’s wife, the dentist’s widow and the draper’s daughter, and they form the new Committee of our Indoor Sports and Pastimes Club.

    These are the election results, 200 members having each voted for three [different] names:

    Elected:
    Mrs. Battledaw, 148 votes
    Mrs. Shuttlecox, 137 votes
    Mrs. Bowles, 126 votes

    Not elected:
    Mr. D’Artz, 125 votes
    Mr. Snewker, 64 votes

    Total votes cast:
    600 votes

    Those who voted for both Jean and Judith were the same in number as those who didn’t vote for the doctor’s wife and if even one of Snewker’s supporters had voted for D’Artz he might have gained the third place. Jean is more interested in Mr. Bowles than in her own husband, who is sorry the draper’s daughter didn’t come top.

    Who are Jennifer, Judith and Jean?

    This is 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 was offered.

    [teaser-1958-04-06] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 9:43 am on 17 April 2022 Permalink | Reply

      There are 5 candidates (let’s number them 1, 2, 3, 4, 5), and each member votes for three different candidates, so possible voting patterns are:

      a = (1, 2, 3)
      b = (1, 2, 4)
      c = (1, 2, 5)
      d = (1, 3, 4)
      e = (1, 3, 5)
      f = (1, 4, 5)
      g = (2, 3, 4)
      h = (2, 3, 5)
      i = (2, 4, 5)
      j = (3, 4, 5)

      If we suppose a, …, j are the numbers who voted for their respective patterns, then their sum is 200.

      a + b + c + d + e + f + g + h + i + j = 200

      And from the votes cast (assuming the numbers are the order that the candidates finished in), we have:

      a + b + c + d + e + f = 148
      a + b + c + g + h + i = 137
      a + d + e + g + h + j = 126
      b + d + f + g + i + j = 125
      c + e + f + h + i + j = 64

      We are told “if even one of Snewker’s [5] supporters had voted for D’Artz [4] he might have gained the third place”, which implies that no-one voted for both 4 and 5, so:

      f = 0
      i = 0
      j = 0

      Hence:

      a + b + c + d + e = 148
      a + b + c + g + h = 137
      a + d + e + g + h = 126
      b + d + g = 125
      c + e + h = 64

      We can use one of these equations to select seed values (I used: c + e + h = 64), and then determine the remaining values using the remaining equations, and verify that they are non-negative integers, and that the additional conditions hold.

      The following Python program runs in 241ms.

      Run: [ @replit ]

      from enigma import (Matrix, decompose, as_int, subsets, multiset, printf)
      
      # record solutions, as: ((jen, jud, jea), (doc, den, dra))
      ss = multiset()
      
      # initial values
      f = i = j = 0
      
      # consider values for c, e, h
      for (c, e, h) in decompose(64, 3, increasing=0, sep=0, min_v=0):
      
        # solve for the remaining values
        eqs = [
          # a  b  d  g = k
          ((1, 1, 1, 0), 148 - c - e),
          ((1, 1, 0, 1), 137 - c - h),
          ((1, 0, 1, 1), 126 - e - h),
          ((0, 1, 1, 1), 125),
        ]
      
        # look for non-negative integers
        try:
          (a, b, d, g) = Matrix.linear(eqs, valid=(lambda x: as_int(x, "0+")))
        except ValueError:
          continue
      
        # consider positions by names
        for (jen, jud, jea) in subsets((1, 2, 3), size=len, select="P"):
          # Jean is not Mrs Bowles
          if jea == 3: continue
          # v1 = votes for both Jean and Judith
          v1 = { 1: a + g + h, 2: a + d + e, 3: a + b + c }[jen]
      
          # consider related occupations by position
          for (doc, den, dra) in subsets((1, 2, 3), size=len, select="P"):
            # Jean's husband was not the dentist
            if den == jea: continue
            # Draper's daughter didn't come top
            if dra == 1: continue
            # v2 = didn't vote for doctor's wife
            v2 = { 1: g + h + i + j, 2: d + e + f + j, 3: b + c + f + i }[doc]
            if v1 != v2: continue
            # record this solution
            ss.add(((jen, jud, jea), (doc, den, dra)))
      
      # output solutions
      for (((jen, jud, jea), (doc, den, dra)), n) in ss.most_common():
        name = { 1: "Battledaw", 2: "Shuttlecox", 3: "Bowles" }
        rel = { doc: "doctor", den: "dentist", dra: "draper" }
        printf("[{n} solutions]")
        printf("-> Jennifer = Mrs {n} [{r}]", n=name[jen], r=rel[jen])
        printf("-> Judith = Mrs {n} [{r}]", n=name[jud], r=rel[jud])
        printf("-> Jean = Mrs {n} [{r}]", n=name[jea], r=rel[jea])
        printf()
      

      Solution: Jennifer is Mrs Battledaw, the dentist’s widow (1st); Judith is Mrs Bowles, the draper’s daughter (3rd); Jean is Mrs Shuttlecox, the doctor’s wife (2nd).

      There are many distributions of votes, but they all satisfy:

      a = 11
      b = 10..74
      c = 74 − b
      d = 0..63
      e = 63 − e
      f = 0
      g = 125 − (b + d)
      h = (b + d) − 73
      i = 0
      j = 0

      And lead to the solution given above.

      Like

    • John+Crabtree's avatar

      John+Crabtree 6:44 pm on 18 April 2022 Permalink | Reply

      Nobody votes for both Dr. A and Mr. S, meaning that 11 people vote for all three women,
      It can then be shown that 84 people vote for both Mrs. Ba. and Mrs. Sh, 74 people for both Mrs. Ba. and Mrs. Bo, and 63 people for both Mrs. Sh. and Mrs. Bo.

      The Doctor’s wife must get 137 or 126 votes, and so must the Draper’s daughter, who is not in first place. And so the Dentist’s widow must get 148 votes, and cannot be Jean, who is married.
      Jean cannot be Mrs. Bowles and so cannot get 126 votes, and so must get 137 votes.
      Then Judith must get 126 votes, and so Jean is the Doctor’s wife, Judith is the Draper’s daughter, and Jennifer is the Dentist’s widow.

      Like

  • Unknown's avatar

    Jim Randell 6:22 pm on 14 April 2022 Permalink | Reply
    Tags:   

    Teaser 3108: My Grandfather’s coins 

    From The Sunday Times, 17th April 2022 [link] [link]

    When my grandfather died, he left his fine collection of coins, not more than 2500 of them, to his children, a different number to each of them, and in decreasing amounts according to their ages.

    To the eldest of his children, he left one fifth of the coins, while the youngest inherited just one eleventh of them. Gaby, my mother, and third oldest of the children, received one tenth of the collection. All the other children [each] received a prime number of coins.

    How many coins did my aunt Isabel, second oldest in the family, inherit?

    [teaser3108]

     
    • Jim Randell's avatar

      Jim Randell 10:24 pm on 14 April 2022 Permalink | Reply

      This Python program runs in 59ms.

      Run: [ @replit ]

      from enigma import (primes, mlcm, irange, rev, subsets, printf)
      
      # total must be divisible by m
      m = mlcm(5, 10, 11)
      
      # consider possible t = total
      for t in irange(m, 2500, step=m):
        # calculate a = 1st, c = 3rd, z = last
        a = t // 5
        c = t // 10
        z = t // 11
        # find primes between [z + 1, c - 1]
        ps = rev(primes.between(z + 1, c - 1))
        # choose a prime for b = 2nd
        for b in primes.between(c + 1, a - 1):
          # decompose remaining amount into primes between [z + 1, c - 1]
          r = t - (a + b + c + z)
          for rest in subsets(ps, fn=list):
            if sum(rest) == r:
              # output solution
              printf("t={t} -> {xs}", xs=[a, b, c] + rest + [z])
      

      Solution: Isabel inherited 311 coins.

      There were 2420 coins in total, and the inheritance of the 9 children (eldest to youngest) was:

      484 coins = 1/5 of the total
      311 coins = prime (Isabel)
      242 coins = 1/10 of the total (Gaby)
      241 coins = prime
      239 coins = prime
      233 coins = prime
      227 coins = prime
      223 coins = prime
      220 coins = 1/11 of the total

      Like

  • Unknown's avatar

    Jim Randell 9:19 am on 14 April 2022 Permalink | Reply
    Tags:   

    Teaser 2847: Easter parade 

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

    Following the success of last year’s Easter parade our village is going to make it an annual event. To celebrate today’s parade I have taken three numbers and I have consistently replaced digits by letters to give:

    ANNUAL
    EASTER
    PARADE

    In fact the third number is the sum of the other two.

    What is the number PARADE?

    [teaser2847]

     
    • Jim Randell's avatar

      Jim Randell 9:20 am on 14 April 2022 Permalink | Reply

      This puzzle can be solved using the [[ SubstitutedSum ]] solver from the enigma.py library.

      The following command executes in 85ms.

      Run: [ @replit ]

      % python3 -m enigma SubstitutedSum "ANNUAL + EASTER = PARADE"
      (ANNUAL + EASTER = PARADE)
      (300632 + 138719 = 439351) / A=3 D=5 E=1 L=2 N=0 P=4 R=9 S=8 T=7 U=6
      (300732 + 138619 = 439351) / A=3 D=5 E=1 L=2 N=0 P=4 R=9 S=8 T=6 U=7
      

      Solution: PARADE = 439351.

      T and U are interchangeable, taking on the values 6 and 7.

      Like

    • GeoffR's avatar

      GeoffR 11:24 am on 14 April 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:A;  var 0..9:D;  var 1..9:E;  var 0..9:L; var 0..9:N;  
      var 1..9:P;  var 0..9:R;  var 0..9:S;  var 0..9:T; var 0..9:U;
      
      constraint all_different([A, D, E, L, N, P, R, S, T, U]);
      
      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;
      
      var 100000..999999: ANNUAL = num6(A,N,N,U,A,L);
      var 100000..999999: EASTER = num6(E,A,S,T,E,R);
      var 100000..999999: PARADE = num6(P,A,R,A,D,E);
      
      constraint ANNUAL + EASTER == PARADE;
      
      solve satisfy;
      output ["PARADE" ++ " = " ++ show(PARADE) ];
      
      % PARADE = 439351
      % ----------
      % ==========
      
      
      

      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