Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 10:38 am on 18 July 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 40: Christmas greetings 

    From The Sunday Times, 24th December 1961 [link]

    The greetings above quite literally sum up my festive feelings, for they are, in fact, two simple addition sums in which each  of which each digit has been replaced by an “Adventitious” letter. You are to replace each letter by the digit it stands for.

    Having found the value of a letter in one of the sums, you  must not assume that the letter necessarily retains that same  value in the other sum. The two examples are quite  independent apart from the one condition that the difference between the sum of the digits in MERRY and the sum of the digits in XMAS must be made as small as possible.

    As I have already said — A Very Very Merry Xmas To One And All.

    [teaser40]

     
    • Jim Randell's avatar

      Jim Randell 10:40 am on 18 July 2021 Permalink | Reply

      The following Python program uses the [[ SubstitutedSum ]] solver from the enigma.py library to solve the alphametic sums, and then finds minimal distance solutions.

      It runs in 76ms.

      Run: [ @replit ]

      from itertools import product
      from enigma import SubstitutedSum, group, unpack, printf
      
      # sum 1: group solutions by M+E+R+R+Y
      p1 = SubstitutedSum(["A", "VERY", "VERY"], "MERRY")
      s1s = group(p1.solve(verbose=0), by=(lambda s: sum(s[x] for x in 'MERRY')))
      
      # sum 2: group solutions by X+M+A+S
      p2 = SubstitutedSum(["XMAS", "TOONE"], "ANDALL")
      s2s = group(p2.solve(verbose=0), by=(lambda s: sum(s[x] for x in 'XMAS')))
      
      # choose the pair with the minimal distance
      (k1, k2) = min(product(s1s.keys(), s2s.keys()), key=unpack(lambda x, y: abs(x - y)))
      
      # output solutions
      for (s1, s2) in product(s1s[k1], s2s[k2]):
        printf("(1) {s1}; (2) {s2}",
          s1=p1.substitute(s1, p1.text),
          s2=p2.substitute(s2, p2.text),
        )
      

      Solution: (i) 8 + 7492 + 7492 = 14992; (ii) 7618 + 95504 = 103122.


      The first alphametic sum has 2 solutions:

      2 + 7498 + 7498 = 14998 | M+E+R+R+Y = 31
      8 + 7492 + 7492 = 14992 | M+E+R+R+Y = 25

      The second alphametic sum has 2 solutions:

      7614 + 95508 = 103122 | X+M+A+S = 18
      7618 + 95504 = 103122 | X+M+A+S = 22

      And the minimum distance is between M+E+R+R+Y = 25, and X + M + A + S = 22.

      Like

    • GeoffR's avatar

      GeoffR 4:58 pm on 18 July 2021 Permalink | Reply

      % A Solution in MiniZinc 
      include "globals.mzn";
      
      % 1st sum:  A + VERY + VERY == MERRY
      var 1..9:A; var 1..9:V; var 0..9:E;
      var 1..9:R; var 0..9:Y; var 1..9:M;
      
      constraint all_different([A, V, E, R, Y, M]);
      
      var 1000..9999:VERY = 1000*V + 100*E + 10*R + Y;
      var 10000..99999:MERRY = 10000*M + 1000*E + 110*R + Y;
      
      constraint A + VERY + VERY == MERRY;
      
      % 2nd Sum:  XMAS + TOONE = ANDALL
      % using lower case letters to be different from 1st equation
      var 1..9:x; var 0..9:m; var 1..9:a; var 0..9:s; var 1..9:t;
      var 0..9:o; var 0..9:n; var 0..9:e; var 0..9:d; var 0..9:l;
      
      constraint all_different ([x, m, a, s, t, o, n, e, d,l]);
      
      var 1000..9999:xmas = 1000*x + 100*m + 10*a + s;
      var 10000..99999:toone = 10000*t + 1100*o + 10*n + e;
      var 100000..999999:andall = 100000*a + 10000*n + 1000*d + 100*a + 11*l;
      
      constraint xmas + toone == andall;
      
      % Minimise difference in sum of digits between MERRY and XMAS
      solve minimize(abs(M + E + R + R + Y - x - m - a - s));
      
      output ["Sum 1 is " ++ show(A) ++ " + " ++ show(VERY) ++ " + " 
      ++ show(VERY) ++ " = " ++ show(MERRY) ++ " " ++ "\n" ++ 
      "Sum 2 is " ++show(xmas) ++ " + " ++ 
      show(toone) ++ " = " ++ show(andall)
      ++ "\n" ++ "Difference of sum of digits between MERRY and XMAS  = "   
      ++ show(M + E + R + R + Y - x - m - a - s) ];
      
      % Sum 1 is 8 + 7492 + 7492 = 14992 
      % Sum 2 is 7614 + 95508 = 103122
      % Difference of sum of digits between MERRY and XMAS  = 7
      % ----------
      % Sum 1 is 8 + 7492 + 7492 = 14992 
      % Sum 2 is 7618 + 95504 = 103122
      % Difference of sum of digits between MERRY and XMAS  = 3  << answer
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 4:56 pm on 16 July 2021 Permalink | Reply
    Tags:   

    Teaser 3069: Fit for purpose 

    From The Sunday Times, 18th July 2021 [link] [link]

    George and Martha bought a new toy for their son Clark. It consisted of a rectangular plastic tray with dimensions 15×16cm and eight plastic rectangles with dimensions 1×2cm, 2×3cm, 3×4cm, 4×5cm, 5×6cm, 6×7cm, 7×8cm and 8×9cm. The rectangles had to be placed inside the tray without any gaps or overlaps. Clark found every possible solution and he noticed that the number of different solutions which could not be rotated or reflected to look like any of the others was the same as his age in years.

    How old was Clark?

    [teaser3069]

     
    • Jim Randell's avatar

      Jim Randell 5:19 pm on 16 July 2021 Permalink | Reply

      I reused the code from rectpack.py, originally written for Enigma 1491, and also used in Teaser 2650.

      Each solution also appears rotated, reflected, and rotated and reflected, so the total number of solutions found is 4× the answer.

      The following Python program runs in 199ms.

      from enigma import (irange, ediv, printf)
      from rectpack import (pack, output_grid)
      
      # width/height of the tray
      (X, Y) = (15, 16)
      
      # rectangles
      rs = list((x, x + 1) for x in irange(1, 8))
      
      # count the solutions
      n = 0
      for (i, s) in enumerate(pack(X, Y, rs), start=1):
        printf("{i}: {s}")
        output_grid(X, Y, s, end="")
        n += 1
      
      printf("age = {x}", x=ediv(n, 4))
      

      Solution: Clark was 10.

      The 10 solutions arise due to the following 2 packings:

      The first has 3 (coloured) sub-rectangles that can be flipped over, giving 2×2×2 = 8 packings.

      The second has only 1 sub-rectangle, so gives 2 packings.

      Like

      • Jim Randell's avatar

        Jim Randell 6:32 pm on 16 July 2021 Permalink | Reply

        And here is an alternate version that calculates a canonical form for each solution, and counts the number of different canonical forms.

        from enigma import (irange, printf)
        from rectpack import (pack, output_grid)
        
        # width/height of the tray
        (X, Y) = (15, 16)
        
        # rectangles
        rs = list((x, x + 1) for x in irange(1, 8))
        
        # rotation / reflection
        rotate = lambda s: tuple(sorted((X - x - w, Y - y - h, w, h) for (x, y, w, h) in s))
        reflect = lambda s: tuple(sorted((X - x - w, y, w, h) for (x, y, w, h) in s))
        
        # canonical form of s
        def canonical(s):
          s = tuple(sorted(s))
          r = rotate(s)
          return min(s, r, reflect(s), reflect(r))
        
        # count the canonical forms
        ss = list()
        for (i, s) in enumerate(pack(X, Y, rs), start=1):
          r = canonical(s)
          if r not in ss:
            ss.append(r)    
            printf("{i} -> {x}", x=len(ss))
            output_grid(X, Y, s)
          else:
            printf("{i} -> {x}", x=ss.index(r) + 1)
          printf()
        
        printf("age = {x}", x=len(ss))
        

        Like

    • Frits's avatar

      Frits 12:31 pm on 19 July 2021 Permalink | Reply

      With analysis to place the biggest piece in top left corner.

      This program ran in 181ms.

       
      from itertools import product
      
      (X, Y) = (15, 16)        # width/height of the tray
      M = 9                    # longest side of rectangle
      
      # return grid coordinates (top left, bottom right) where piece w x h will fit
      def grid_coords(grid, w, h):
        res = []
        for i in range(M, X + M):
          for j in range(M, Y + M):
            if grid[i][j] == 0:
              # horizontal and vertical
              for k in range(2):
                if all(grid[x][y] == 0 for x in range(i, i + h)
                                       for y in range(j, j + w)):
                 res.append([(i - M, j - M), (i - M + h - 1, j - M + w - 1)])
      
                (w, h) = (h, w)   # mirror
        return res       
      
      # print grid without borders   
      def grid_print(grid):
        for i, g in enumerate(grid):
          if M <= i < Y + M - 1:
            print(f"{g[M:-M]}")   
        print()   
      
      # fit k pieces into empty spots in the grid   
      def fit(r, k, s):
        if k == 1:
          # check if last piece fits
          coords = grid_coords(s, r[0][0], r[0][1])
          if coords:
            (tl, rb) = (coords[0][0], coords[0][1])
            for (i, j) in product(range(tl[0], rb[0] + 1), range(tl[1], rb[1] + 1)):
              s[i + M][j + M] = k   
            yield [r.copy() for r in s]       # return copy !!
            # backtrack
            for (i, j) in product(range(tl[0], rb[0] + 1), range(tl[1], rb[1] + 1)):
              s[i + M][j + M] = 0
        else:
          (w, h) = r[0]   # pick first remaining piece from list
          for tl, rb in grid_coords(s, w, h):
            if k == N:
              # only allow the biggest piece 9x8 mostly in the top left quadrant
              # in order to prevent rotated or reflected versions
              #
              # if biggest piece is not in the top left corner (not touching (0, 0)):
              # - if biggest piece lies vertical then placing the 8x7 piece
              #   results in a 9x1 stroke --> impossible
              # - if biggest piece lies horizontal there is a stroke 8xa to the left
              #   and a stroke 8xb to the left (where a + b = 7)
              #   strokes of 8x1, 8x2 or 8x3 are impossible to fill --> impossible
            
              if tl != (0, 0): continue
           
            # update piece in the grid s
            for (i, j) in product(range(tl[0], rb[0] + 1), range(tl[1], rb[1] + 1)):
              s[i + M][j + M] = k   
          
            yield from fit(r[1:], k - 1, s)  
            # backtrack
            for (i, j) in product(range(tl[0], rb[0] + 1), range(tl[1], rb[1] + 1)):
              s[i + M][j + M] = 0
             
           
      # rectangles (biggest first)
      rs = list((x, x + 1) for x in range(8, 0, -1))
      N = len(rs)
      
      # initialize grid with borders of size M
      grid = [[9 for i in range(Y + 2 * M)] for j in range(X + 2 * M)]
      # initialize inner grid to 0
      for i in range(X):
        for j in range(Y):
          grid[i + M][j + M] = 0
             
      # generate solutions
      sols = list(fit(rs, N, grid))   
      
      print("Clark was", len(sols), "years old. \n\nexample:\n")
      grid_print(sols[0])
      

      Like

  • Unknown's avatar

    Jim Randell 10:02 am on 15 July 2021 Permalink | Reply
    Tags:   

    Teaser 2801: End of season 

    From The Sunday Times, 29th May 2016 [link] [link]

    In this football league each team plays each of the others once (with three points for a win and one for a draw). The end-of-season table has teams in decreasing order of points (ties being determined by goals scored). Here are some of the entries from three rows of the table, but with digits consistently replaced by letters.

    How many teams are in the league, and what was the score when Villa played Wanderers?

    [teaser2801]

     
    • Jim Randell's avatar

      Jim Randell 10:03 am on 15 July 2021 Permalink | Reply

      If there are n teams in the league, then each team plays (n − 1) matches. And we know at the end of the season Villa has played (E + A + S) matches.

      Villa has T points and Wanderers have T draws (each worth 1 point), so they must have no wins, otherwise they would have more points than Villa. So Wanderers also have T points, so Villa must have scored more goals (E > M).

      Villa won E matches and also has E goals for, so each win must be 1-0 (and each draw 0-0).

      The match between Villa and Wanderers must be either a win for Villa (1-0) or a draw (0-0) (as Wanderers have no wins).

      This Python program uses the [[ SubstitutedExpression ]] solver from the enigma.py library to solve the constraints inherent in the table, and then checks if it is possible for the Villa vs. Wanderers match to be a win (for Villa) or a draw.

      It runs in 55ms.

      Run: [ @replit ]

      from enigma import SubstitutedExpression, printf
      
      # alphametic constraints inherent in the table
      p = SubstitutedExpression([
          # points
          "3 * E + A = T", # points for Villa
          "3 * M + Y >= T", # United has the most points
          # number of matches (= E + A + S)
          "E + A + S >= M + Y",
          "E + A + S == T + I", # W must have 0 wins
          # goals
          "E > M", # W must have conceded more goals than they scored
          "R > S > 0",
          "E > I > 0",
        ],
        answer="(A, E, I, M, R, S, T, Y)",
        verbose=0,
      )                         
      
      # find candidate values
      for (s, (A, E, I, M, R, S, T, Y)) in p.solve():
        # with n teams, each team plays n - 1 matches
        n = E + A + S + 1
        printf("n={n} [A={A} E={E} I={I} M={M} R={R} S={S} T={T} Y={Y}]")
        # V vs W = 1-0 ?
        if E > 0 and I > 0: printf("-> V vs W = 1-0 (win)")
        # V vs W = 0-0 ?
        if A > 0 and T > 0: printf("-> V vs W = 0-0 (draw)")
      

      Solution: There were 11 teams in the league. Villa beat Wanderers 1-0.


      The assignment of letters to digits is:

      A = 0, E = 3, I = 1, M = 2, R = 8, S = 7, T = 9
      Y = 4, 5, 6

      We could use the [[ Football ]] helper class from the enigma.py to investigate scorelines in the V vs. W match, but we see from the solutions to the alphametic constraints that A = 0, so V has no drawn matches, hence the score must be a 1-0 win to V.

      Like

      • Frits's avatar

        Frits 2:02 pm on 15 July 2021 Permalink | Reply

        @Jim,

        The condition on line 26 seems to be unnecessary as it is already one of the expressions in SubstitutedExpression.

        Conditions on line 26 and line 28 regarding V vs W are not immediately clear to me (without explanation)

        Like

        • Jim Randell's avatar

          Jim Randell 2:37 pm on 15 July 2021 Permalink | Reply

          @Frits: We’ve already determined that V vs W match must either be a 1-0 win for V or a 0-0 draw.

          In order for the match to be a win, V must have >0 wins (E > 0) and W must have >0 losses (I > 0).

          Similarly, in order for the the match to be a draw V and W must have >0 draws (A > 0 ∧ T > 0).

          Like

          • Frits's avatar

            Frits 3:07 pm on 15 July 2021 Permalink | Reply

            @Jim, you have explained if win/draw for V-W then p/q. This doesn’t necessarily mean if p/q then win/draw for V-W.

            Like

          • Jim Randell's avatar

            Jim Randell 3:33 pm on 15 July 2021 Permalink | Reply

            In each of the three cases the only possibility is that the match is a win for V (a draw is not possible, as in all cases A = 0).

            Therefore the the match is necessarily a win for V.

            Like

  • Unknown's avatar

    Jim Randell 10:27 am on 13 July 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 808: Placing the top ten 

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

    Last week’s Top Ten consisted of the following performers:

    1. Adda
    2. Bay Leaf Rissolers
    3. Cate Gooseberry
    4. Demi-Sour
    5. Englebert Smith
    6. Fatherhood of Man
    7. Gee-Bees
    8. How
    9. It
    10. John Revolter

    This week the same ten were in the charts, but all their placings were different. Five had gone up and five had gone down. Adda had the biggest single drop of all, but Fatherhood went up.

    When I looked at the products of last week’s and this week’s placings, I found that:

    (a) Gee-Bees’ product was twice Cate Gooseberry’s;
    (b) It’s was three times Demi-Sour’s;
    (c) Englebert’s was even;
    (d) The total of the products was 272.

    What was this week’s order?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book, but modified to remove an error that was introduced in the rewording.

    [teaser808]

     
    • Jim Randell's avatar

      Jim Randell 10:28 am on 13 July 2021 Permalink | Reply

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      It runs in 70ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      # if the positions are:
      # last week = 1  2  3  4  5  6  7  8  9  10
      # this week = A  B  C  D  E  F  G  H  I  J
      
      SubstitutedExpression
      
      --base=11
      --digits="1-10"
      
      # no-one kept their spot
      --invalid="1,A"
      --invalid="2,B"
      --invalid="3,C"
      --invalid="4,D"
      --invalid="5,E"
      --invalid="6,F"
      --invalid="7,G"
      --invalid="8,H"
      --invalid="9,I"
      --invalid="10,J"
      
      # "5 went up, 5 went down"
      "sum([A > 1, B > 2, C > 3, D > 4, E > 5, F > 6, G > 7, H > 8, I > 9, J > 10]) == 5"
      
      # "A had the biggest drop of all"
      "A - 1 > max(B - 2, C - 3, D - 4, E - 5, F - 6, G - 7, H - 8, I - 9, J - 10)"
      
      # "F went up"
      "F < 6"
      
      # "G's product was 2 times C's" [not: 3 times]
      "7 * G == 2 * 3 * C"
      
      # "I's product was 3 times D's"
      "9 * I == 3 * 4 * D"
      
      # "E's product was even"
      "(5 * E) % 2 == 0"
      
      # "the total of the products was 272"
      "1 * A + 2 * B + 3 * C + 4 * D + 5 * E + 6 * F + 7 * G + 8 * H + 9 * I + 10 * J == 272"
      
      # [optional]
      --template=""
      

      Solution: (1) John Revolter; (2) Fatherhood of Man; (3) Demi-Sour; (4) It; (5) Bay Leaf Rissolers; (6) Gee-Bees; (7) Cate Gooseberry; (8) Englebert Smith; (9) Adda; (10) How.

      Like

      • Frits's avatar

        Frits 11:54 am on 13 July 2021 Permalink | Reply

        @Jim, “A had the biggest drop of all” : shouldn’t you use the less equal sign in the formula?

        Like

        • Jim Randell's avatar

          Jim Randell 12:02 pm on 13 July 2021 Permalink | Reply

          @Frits: I don’t think so. It would allow another act to have an equally big drop.

          Like

          • Frits's avatar

            Frits 12:09 pm on 13 July 2021 Permalink | Reply

            @Jim: Semantics again. I thought “of all” would make A stand out.

            Like

          • Jim Randell's avatar

            Jim Randell 12:54 pm on 13 July 2021 Permalink | Reply

            I’ve changed it not use not. I want A’s drop to be larger than anyone else’s.

            Like

    • Frits's avatar

      Frits 2:38 pm on 13 July 2021 Permalink | Reply

      Easy to find: C=7, G=6 and D=3, I=4

      A+2B+5E+6F+8H+10J = 161 so A is odd (as E is even).
      So A=9 (A=5 has same single drop as C).

      Ups: G, D, I, F and J so B, E and H must be down.
      So H=10, E=8 and B=5.

      6F + 10J = 22 so F=2 and J=1

      Like

    • GeoffR's avatar

      GeoffR 3:49 pm on 27 July 2021 Permalink | Reply

      I found three constraints, which were correct, but not essential for the single solution.

      % A Solution in MiniZinc 
      include "globals.mzn";
      
      var 1..10:A; var 1..10:B; var 1..10:C; var 1..10:D; var 1..10:E;
      var 1..10:F; var 1..10:G; var 1..10:H; var 1..10:I; var 1..10:J;
      
      constraint all_different ([A,B,C,D,E,F,G,H,I,J]);
      
      % All their placings were different
      constraint A != 1 /\ B != 2 /\ C != 3 /\ D != 4 /\ E != 5
      /\ F != 6 /\ G != 7 /\ G != 8 /\ H != 9 /\ I != 9 /\ J != 10;
      
      % A had the biggest drop of all
      % *** This constraint was correct but not needed for the single solution ***
      % constraint A - 1 > max([B-2, C-3, D-4, E-5, F-6, G-7, H-8, I-9, J-10]);
       
      % F went up
      % *** This constraint was correct but not needed for the single solution ***
      % constraint F < 6;
       
      % G's product was 2 times C's 
      constraint 7 * G == 2 * 3 * C;
       
      % I's product was 3 times D's
      constraint 9 * I == 3 * 4 * D;
       
      % E's product was even
      % *** This constraint was correct but not needed for the single solution ***
      % constraint (5 * E) mod 2 == 0;
      
      % The total of the products was 272.
      constraint 1 * A + 2 * B + 3 * C + 4 * D + 5 * E + 6 
      * F + 7 * G + 8 * H + 9 * I + 10 * J == 272;
      
      % 5 went up, 5 went down
      constraint sum([A > 1, B > 2, C > 3, D > 4, E > 5, F > 6, 
      G > 7, H > 8, I > 9, J > 10]) == 5; 
       
      solve satisfy;
      
      output ["This week’s order is [A,B,C,D,E,F,G,H,I,J] = " 
      ++ show([A,B,C,D,E,F,G,H,I,J]) ];
      
      % [A, B, C, D, E, F, G, H,  I, J] = 
      % [9, 5, 7, 3, 8, 2, 6, 10, 4, 1]
      % ----------
      % ==========
      
      
      
      

      Any more offers on who the 1977 Top Ten performers were meant to be?
      Here is my assessment:

      1. Adda means Abba
      2. Bay Leaf Rissolers means Bay City Rollers
      3. Cate Gooseberry means Kate Bush
      4. Demi-Sour ?
      5. Englebert Smith means Englebert Humperdink
      6. Fatherhood of Man means Brotherhood of Man
      7. Gee-Bees means Bee Gees
      8. How ?
      9. It ?
      10. John Revolter means John Travolta

      Like

      • Jim Randell's avatar

        Jim Randell 7:25 pm on 27 July 2021 Permalink | Reply

        I thought “Demi-Sour” could be “The Sweet” (or possibly “Demis Roussos”), but I didn’t come up with anything for “How” or “It”.

        Like

    • GeoffR's avatar

      GeoffR 8:19 pm on 27 July 2021 Permalink | Reply

      Yes , ” Demis Roussos” sounds good to me for “Demi-Sour”.

      Maybe “How” could be “The Who” ? Just “It” left.

      Like

    • Jim Randell's avatar

      Jim Randell 4:56 pm on 28 July 2021 Permalink | Reply

      I looked through the charts for 1975 and 1976, and “The Who” had singles in the charts then.

      But nothing leapt out at me for “It”. The closest was “If” by “Telly Sevalas”, but that’s the track name rather than the artist name.

      Although, now I realise the names were made for the rewording of the puzzle in the book, so they could be refer to artists in the charts after the puzzle was in published in the newspaper.

      Like

      • Jim Randell's avatar

        Jim Randell 12:56 pm on 30 July 2021 Permalink | Reply

        In late 1977 – 1979 “Chic” were in the charts.

        Both “chic” and “it” are synonyms for “fashionable”.

        Like

    • GeoffR's avatar

      GeoffR 8:22 am on 31 July 2021 Permalink | Reply

      Yes, that looks a good fit.

      The only 1977 track I found was:
      https://www.discogs.com/Kate-Taylor-Its-In-His-Kiss-The-Shoop-Shoop-Song/release/5413526
      (later made famous by Cher, of course)

      Like

  • Unknown's avatar

    Jim Randell 11:23 am on 11 July 2021 Permalink | Reply
    Tags: by: P. B. Chapman   

    Brain-Teaser 39: [Simultaneous departures] 

    From The Sunday Times, 17th December 1961 [link]

    Two train services run from my station day and night. Each train of each service is scheduled to leave a whole number of minutes after the previous one. I remarked to the station-master on the closeness of the two afternoon trains, 12:17 and 12:18. “Actually”, he replied, “there are two morning trains which leave simultaneously”.

    At what time does this happen?

    This puzzle was originally published with no title.

    [teaser39]

     
    • Jim Randell's avatar

      Jim Randell 11:24 am on 11 July 2021 Permalink | Reply

      I think there is some ambiguity in this puzzle.

      I supposed that there was only time per day that there was a train for each service leaving simultaneously, and that time was in the morning (i.e. between 00:00 and 11:59).

      And also that the interval between trains on the same service must be at least 10 minutes (in order to make trains departing 1 minute apart an occurrence of note). Originally I assumed at least 5 minutes, but this gave me multiple solutions.

      There are 24 × 60 = 1440 minutes in a day so the frequencies of the services must divide this.

      This Python program runs in 59ms.

      Run: [ @replit ]

      from enigma import (irange, divisors, intersect, subsets, peek, printf)
      
      # times for a service, given a reference time <t> and frequency <d>
      times = lambda t, d: set(x % 1440 for x in irange(t, t + 1439, step=d))
      
      # consider possible frequencies for the two services
      ds = (d for d in divisors(1440) if 9 < d < 1440)
      for (dA, dB) in subsets(ds, size=2, select="M"):
        # look for simultaneous times in both services
        ts = intersect([times(737, dA), times(738, dB)])
        # there should be only 1
        if len(ts) == 1:
          # and it should be in the morning
          (h, m) = divmod(peek(ts), 60)
          if h < 12:
            printf("A: {dA}; B: {dB} -> {ts} {h:02d}:{m:02d}")
      

      Solution: The two services leave simultaneously at 08:33.


      The timetable of the trains are as follows:

      A: (32m) … 08:33 | 09:05 | 09:37 | 10:09 | 10:41 | 11:13 | 11:45 | 12:17 …
      B: (45m) … 08:33 | 09:18 | 10:03 | 10:48 | 11:33 | 12:18 …

      My alternatives (with frequencies less than 10m) are:

      A: (5m) … 02:42 … 12:17 …
      B: (4h48m) … 02:42 | 07:30 | 12:18 …

      A: (9m) … 01:38 … 12:17 …
      B: (2h40m) … 01:38 | 04:18 | 06:58 | 09:38 | 12:18 …

      Like

      • Frits's avatar

        Frits 12:38 pm on 11 July 2021 Permalink | Reply

        @Jim, have you considered processing only the first 2 characters of the subsets select parameter? Then you can use select=”Product” instead of select=”M”.

        I first thought was a tuple but it turns out to be a generator.

        Using

        ds = list(d for d in divisors(1440) if 9 < d < 1440)
        

        you can still print the content of without emptying it.

        —————

        I think 04:02 also is a solution, intersection of (dA=45, dB=16) contains 242 and 962 which is valid as 962 >= 720.

        from enigma import SubstitutedExpression, divisors
        
        # the alphametic puzzle
        p = SubstitutedExpression(
          [
          "UVW in divisors(1440) and 9 < UVW < 1440",
          "XYZ in divisors(1440) and 9 < XYZ < 1440",
          
          #"737 - DE * UVW == 738 - FG * XYZ",
          
          "div(1 +  DE * UVW, XYZ) = FG",
          "737 - DE * UVW > 0",
          
          ],
          answer="(UVW, XYZ), (DE, FG), 737 - DE * UVW",
          d2i="",
          distinct="",   # allow variables with same values
          #reorder=0,
          verbose=0,
        )
        
        # print answers
        answs = sorted(y for _, y in p.solve())
        for a in answs: print(a)
        

        Like

    • Hugh Casement's avatar

      Hugh Casement 3:14 pm on 11 July 2021 Permalink | Reply

      The use of the definite article in “the two afternoon trains” rather implies that there are no others until evening, though we’re not told when afternoon ends and evening begins. Better, I feel, would have been “the first two trains after noon”, when we would know that the interval is more than 16 minutes, thus excluding a lot of potential solutions.

      Possibly that’s what the setter wrote, but it was altered by an editor without consultation — there’s been a history of that over the years.

      Like

  • Unknown's avatar

    Jim Randell 4:06 pm on 9 July 2021 Permalink | Reply
    Tags:   

    Teaser 3068: Valued playwrights 

    From The Sunday Times, 11th July 2021 [link] [link]

    I have given each letter of the alphabet a different whole-number value from 1 to 26. For example, P=4, L=8, A=3 and Y=24. With my numbers I can work out the value of any word by adding up the values of its letters, for example the word PLAY has a value of 39.

    It turns out that the playwrights

    BECKETT
    FRAYN
    PIRANDELLO
    RATTIGAN
    SHAKESPEARE
    SHAW

    all have the same prime value.

    Also COWARD, PINERO and STOPPARD have prime values ….

    …what are those three prime numbers?

    [teaser3068]

     
    • Jim Randell's avatar

      Jim Randell 4:15 pm on 9 July 2021 Permalink | Reply

      (See also: Teaser 2884).

      Using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      The following run file executes in 355ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --base=27
      --digits="1-26"
      
      --assign="P,4"
      --assign="L,8"
      --assign="A,3"
      --assign="Y,24"
      
      # equal to SHAW (prime)
      "K + E + S + P + E + A + R + E = W" # SHAKESPEARE
      "S + H + W - F - R - Y = N" # FRAYN
      "S + H + W - P - I - R - N - E - L - L - O = D" # PIRANDELLO
      "S + H + W - R - T - T - I - A - N = G" # RATTIGAN
      "S + H + A + W - E - C - K - E - T - T = B" # BECKETT
      "is_prime(S + H + A + W)"
      
      # COWARD, PINERO, STOPPARD are also prime
      "is_prime(C + O + W + A + R + D)"
      "is_prime(P + I + N + E + R + O)"
      "is_prime(S + T + O + P + P + A + R + D)"
      
      --answer="(C + O + W + A + R + D, P + I + N + E + R + O, S + T + O + P + P + A + R + D)"
      
      # [optional]
      --template=""
      --reorder=0
      

      Solution: COWARD = 67; PINERO = 31; STOPPARD = 53.

      Like

      • Frits's avatar

        Frits 9:35 pm on 9 July 2021 Permalink | Reply

        @Jim, I don’t see why P has to be 4, etc (it says “for example”).

        Like

        • Jim Randell's avatar

          Jim Randell 9:47 pm on 9 July 2021 Permalink | Reply

          @Frits: The “for example” tells us some, but not all, of the assignments.

          If it had said: “if, for example, P=4, L=8, A=3 and Y=24, then PLAY would have the value 39″, that would be different.

          But without the “example” values, there are many possible answers.

          Like

    • GeoffR's avatar

      GeoffR 8:07 pm on 9 July 2021 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..26:A;   var 1..26:B;   var 1..26:C;   var 1..26:D;
      var 1..26:E;   var 1..26:F;   var 1..26:G;   var 1..26:H;   
      var 1..26:I;   var 1..26:J;   var 1..26:K;   var 1..26:L;
      var 1..26:M;   var 1..26:N;   var 1..26:O;   var 1..26:P;   
      var 1..26:Q;   var 1..26:R;   var 1..26:S;   var 1..26:T;   
      var 1..26:U;   var 1..26:V;   var 1..26:W;   var 1..26:X;   
      var 1..26:Y;   var 1..26:Z;
      
      constraint P == 4 /\ L == 8 /\ A == 3 /\ Y == 24;
      
      constraint all_different ([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]);
      
      predicate is_prime(var int: x) = x > 1 /\ 
      forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) ((i < x) -> (x mod i > 0));
      
      % BECKETT, FRAYN, PIRANDELLO, RATTIGAN, SHAKESPEARE, SHAW have same prime value
      constraint is_prime(S + H + A + W);
      constraint S + H + A + W == B + E + C + K + E + T + T;
      constraint S + H + A + W == F + R + A + Y + N;
      constraint S + H + A + W == P + I + R + A + N + D + E + L + L + O;
      constraint S + H + A + W == R + A + T + T + I + G + A + N;
      constraint S + H + A + W == S + H + A + K + E + S + P + E + A + R + E;
      
      % COWARD, PINERO and STOPPARD have prime values
      constraint is_prime(C + O + W + A + R + D);
      constraint is_prime(P + I + N + E + R + O);
      constraint is_prime(S + T + O + P + P + A + R + D);
      
      solve satisfy;
      
      output [ "COWARD = " ++ show(C + O + W + A + R + D) 
      ++ ", PINERO = " ++ show(P + I + N + E + R + O) 
      ++ ", STOPPARD = " ++ show(S + T + O + P + P + A + R + D)
      ++ ", SHAW = " ++ show(S + H + A + W) ];
      
      
      

      Like

    • Frits's avatar

      Frits 5:19 pm on 10 July 2021 Permalink | Reply

      Jim’s code with some extra analysis.

      The following run file executes in approx 15ms.

      #!/usr/bin/env python3 -m enigma -r
       
      SubstitutedExpression
       
      --base=27
      --digits="1-26"
       
      --assign="P,4"
      --assign="L,8"
      --assign="A,3"
      --assign="Y,24"
      
      # W = KESPEARE has 6 distinct characters --> W >= 21 + 2
      --invalid="1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22,W"
      # SHAW >= 47 --> S + H + W >= 44 --> H >= 9
      --invalid="1|2|3|4|5|6|7|8,H"
      # RATTIGAN in {47, 53, 59, 61} --> RTTIGN <= 55 -- > T <= 18
      --invalid="19|20|21|22|23|24|25|26,T"
      # PIRANDELLO in {47, 53, 59, 61} --> IRNDEO <= 38 -- all I,R,N,D,E,O <= 17
      --invalid="18|19|20|21|22|23|24|25|26,INDO"
      # W >= 23 so all letters in KESPEARE have to be smaller than 10, E is 1 or 2
      --invalid="10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26,RKS"
      --invalid="3|4|5|6|7|8|910|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26,E"
      
       
      # equal to SHAW (prime)
      "W - (K + E + S + P + E + A + E) = R"           # SHAKESPEARE
      "S + H + W - F - R - Y = N"                     # FRAYN
      "S + H + W - P - I - R - N - E - L - L - D = O" # PIRANDELLO
      "S + H + W - R - T - T - I - A - N = G"         # RATTIGAN
      "S + H + A + W - E - C - K - E - T - T = B"     # BECKETT
      
      # SHAW >= 45 as PIRANDELLO has 9 distinct characters
      # SHAW <= 63 as S + A <= 12
      "S + H + A + W in {47, 53, 59, 61}"     # is_prime(S + H + A + W)
      
       
      # COWARD, PINERO, STOPPARD are also prime
      # PINERO + ADLL = PIRANDELLO = SHAW -- > PINERO = SHAW - ADLL = SHW - DLL
      "is_prime(S + H + W - D - L - L)"    # is_prime(P + I + N + E + R + O)
      "is_prime(C + O + W + A + R + D)"
      "is_prime(S + T + O + P + P + A + R + D)"
       
      --answer="(C + O + W + A + R + D, 
       P + I + N + E + R + O, 
       S + T + O + P + P + A + R + D)"
       
      # [optional]
      --template=""
      #--reorder=0
      

      Like

    • GeoffR's avatar

      GeoffR 9:57 am on 11 July 2021 Permalink | Reply

      An excellent run-time.

      I do not know if it is possible with SubstitutedExpression, but an irange statement would give shorter code and look much neater than the many consecutive integers in the –invalid statements:

      e.g.
      –invalid = “irange(1,22), W” and
      –invalid = “irange(10,26), RKS” , and other examples.

      Is there a typo in line 23 for E ? ….|8|910|11|12| ….

      Like

      • Jim Randell's avatar

        Jim Randell 10:16 am on 11 July 2021 Permalink | Reply

        Yes. The [[ --invalid ]] parameters can be written more concisely:

        --invalid="1-22,W"
        --invalid="1-8,H"
        --invalid="19-26,T"
        --invalid="18-26,INDO"
        --invalid="10-26,RKS"
        --invalid="3-26,E"
        

        Like

    • Frits's avatar

      Frits 11:26 am on 11 July 2021 Permalink | Reply

      Some more analysis, leading to D being odd.

      My stored version of [ https://s2t2.home.blog/2020/08/13/teaser-2705-in-the-pub/ ] contained:

      --invalid="2|3|4|5|6|7|8|9,abcd"
      

      Later Jim updated it to

      --invalid="2-9,abcd"
      

      2 ranges doesn’t seem to be supported.

      The following run file once executed in 8.30ms.

      #!/usr/bin/env python3 -m enigma -r
       
      SubstitutedExpression
       
      --base=27
      --digits="1-26"
       
      --assign="P,4"
      --assign="L,8"
      --assign="A,3"
      --assign="Y,24"
      
      # W = KESPEARE has 6 distinct characters --> W >= 21 + 2
      --invalid="1-22,W"
      # SHAW >= 59 --> SHW >= 56 --> H >= 21
      --invalid="1-20,H"
      # RATTIGAN in {59, 61} --> RTTIGN <= 55 -- > 9 < T <= 18
      --invalid="1|2|3|4|5|6|7|8|9|19|20|21|22|23|24|25|26,T"
      # PIRANDELLO in {59, 61} --> IRNDEO <= 38 -- all I,R,N,D,E,O <= 17
      # out of I,N,D,O at most 2 may be single digit (besides ERKSPLA)
      # out of I,N,D,O at most 2 may be double digit numbers --> exactly 2
      --invalid="18-26,INO"
      # D must be odd (otherwise S + H + W - D - L - L is even and not prime)
      # if D is 7 or 15 then S + H + W - D - L - L is not prime
      --invalid="2|4|6|7|8|10|12|14|15|16|18|19|20|21|22|23|24|25|26,D"
      # W >= 23 so all letters in KESPEARE have to be smaller than 10, E is 1 or 2
      --invalid="10-26,RKS"
      --invalid="3-26,E"
      # all single digit numbers are already in use (ERKSPLA and 2 out of INDO)
      --invalid="1-9,BCFG"
      
       
      # equal to SHAW (prime)
      "W - (K + E + S + P + E + A + E) = R"            # SHAKESPEARE
      "S + H + W - R - Y - N = F"                      # FRAYN
      "S + H + W - P - I - R - N - E - L - L - D = O"  # PIRANDELLO
      "S + H + W - R - T - T - I - A - N = G"          # RATTIGAN
      "S + H + A + W - E - C - K - E - T - T = B"      # BECKETT
      
      # SHAW >= 45 + 8 as PIRANDELO has 9 distinct characters
      # SHAW > 45 + 8 as PIRANDELO can't use only digits 1-9 (as S < 10)
      # SHAW <= 63 as S + A <= 12
      "S + H + A + W in {59, 61}"     # is_prime(S + H + A + W)
      
       
      # COWARD, PINERO, STOPPARD are also prime
      # PINERO + ADLL = PIRANDELLO = SHAW --> PINERO = SHAW - ADLL = SHW - DLL
      "S + H + W - D - L - L in {23, 29, 31, 37, 41}"    # is_prime(PINERO)
      "is_prime(C + O + W + A + R + D)"
      "is_prime(S + T + O + P + P + A + R + D)"
       
      --answer="(C + O + W + A + R + D, 
       P + I + N + E + R + O, 
       S + T + O + P + P + A + R + D)"
       
      # [optional]
      --template=""
      #--reorder=0
      #--verbose=256    # print generated code
      

      Like

    • Iain Turner's avatar

      Iain Turner 11:41 am on 21 July 2021 Permalink | Reply

      I wrote a python solution just using itertools rather than SubstitutedExpression:

      import itertools
       
      def isprime(x):
        #quick & dirty prime test
        if x%2==0: return(False)
        for i in range(3,1+int(x**0.5),2):
          if x%i==0:return(False)
        return(True)
       
      e=1
      p=4
      a=3
      l=8
      y=24
      count=0
      alpha26={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26}
      for w in [23,25,26]:
        rem=alpha26-{e,w,p,a,l,y}
        #shakespeare = shaw so w = kspar +3e
        #if e=1, min value of kspar is 2+3+4+5+6 = 20, so min value of w is 23
        #if e=2, min value of kspar is 19, so min w is 25
        for ksr in itertools.permutations(rem,3):
           
          k,s,r=ksr
          if w == k+e+s+p+e+a+r+e:
            rem2=rem-set(ksr)
            for prime in [51,53,59,61,67]:
              #shaw
              h=prime-a-w-s
              if h in rem2:
                rem3=rem2-{h}
                #pirandello
                #min value of pirandello is 1+2+2+3+4+5+6+7+8+9 = 47
                #max value of shaw is 26+25+8+9 = 68
                #so prime range is 51 to 67 (see above)
                 
                for indo in itertools.permutations(rem3,4):
                  xx=prime-p-e-a-r-l-l
                  if sum(indo)==xx:
                    i,n,d,o=indo
                    pinero=p+i+n+e+r+o
                    if isprime(pinero):
                      rem4=rem3-set(indo)
                      #frayn
                      for f in rem4:
                        if f+r+a+y+n==prime:
                          rem5=rem4-{f}
                          #beckett
                          for bct in itertools.permutations(rem5,3):
                            xx=prime-e-e-k
                            if sum(bct)+bct[2]==xx:
                              b,c,t=bct
                              rem6=rem5-set(bct)
                              stoppard=s+t+o+p+p+a+r+d
                              coward=c+o+w+a+r+d
                              if isprime(stoppard) and isprime(coward):
                                g=prime-r-a-t-t-i-a-n
                                #rattigan
                                if g in rem6:
                                  print(stoppard,coward,pinero,'prime:',prime,'remaining:',rem6-{g})
                                  count+=1
      print('total count',count)
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 4:49 pm on 21 July 2021 Permalink | Reply

        Thanks for your comment.

        I wrapped your code in [code language="python"]...[/code] tags and changed the indents to 2 spaces (so that it doesn’t disappear off the RHS).

        The [[ SubstitutedExpression ]] solver uses the expressions given to it to write a similar program, and then runs it.

        If you want to know more about how it works see “Solving Alphametics with Python, Part 2” [ link ].

        Like

      • Frits's avatar

        Frits 6:03 pm on 21 July 2021 Permalink | Reply

        Hi Iain,

        Why do you set e to 1?

        51 is not a prime number so it might lead to incorrect answers.

        Min value of pirandello can be set to 53 (2 l’s with value 8).
        Max value of shaw can be set to 26+25+3+9 = 63 (as a is 3 and s less than 10).

        Like

  • Unknown's avatar

    Jim Randell 9:09 am on 8 July 2021 Permalink | Reply
    Tags:   

    Teaser 2800: Promoting rugby 

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

    Six male rugby players and six female rugby players are helping to promote the game. The men are Eastmond, Morgan, Twelvetrees, Webber, Yarde and Youngs. The women are Allen, Clarke, Croker, McGilchrist, McLean and Thompson. The men have paired off with the women and one pair has gone to each of the counties East Sussex, Hampshire, Isle of Wight, Norfolk, Suffolk and Surrey. For each pair, if you look at the name of the man, the name of the woman and the name of their county, then for any two of the three names just two different letters of the alphabet occur in both (possibly more than once).

    The men above are listed in alphabetical order: in that order, who are their female partners?

    [teaser2800]

     
    • Jim Randell's avatar

      Jim Randell 9:10 am on 8 July 2021 Permalink | Reply

      This is another puzzle by Graham Smithers that can be solved using the [[ grouping ]] functionality in the enigma.py library.

      This Python program runs in 52ms.

      Run: [ @replit ]

      from enigma import grouping
      
      male = ('Eastmond', 'Morgan', 'Twelvetrees', 'Webber', 'Yarde', 'Youngs')
      female = ('Allen', 'Clarke', 'Croker', 'McGilchrist', 'McLean', 'Thompson')
      county = ('East Sussex', 'Hampshire', 'Isle of Wight', 'Norfolk', 'Suffolk', 'Surrey')
      
      grouping.solve([male, female, county], grouping.share_letters(2))
      

      Solution: The female partners are: Clarke, Allen, Thompson, Croker, McLean, McGilchrist.

      Like

  • Unknown's avatar

    Jim Randell 9:54 am on 6 July 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 803: Three perfect squares 

    From The Sunday Times, 5th December 1976 [link]

    To code his message, the Agent began by writing three words — each word being a number, e.g. FIVE. In doing this, he did not write any single letter of the alphabet more than once.

    For each of the letters thus written, he then substituted a digit and, in doing so, he used each of the digits 0 to 9 inclusive once (and once only).

    He now had three numbers (all in figures) each of which was a perfect square. By adding these three numbers together, he obtained a total running to five figures.

    In place of the digits in this total he now wrote the letters for which each of them had originally been substituted. This gave the letters NONSO.

    What were the three perfect squares (in figures)?

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

    [teaser803]

     
    • Jim Randell's avatar

      Jim Randell 9:54 am on 6 July 2021 Permalink | Reply

      The three numbers used (when considered as words) must use 10 different letters between them, and the result is 5 letters long. So each of the numbers has at most 5 letters. But if one were to have 5 letters, that would only leave 5 letters for the remaining 2 numbers, and there are no numbers with 2 (or fewer) letters. So the numbers must have (4, 3, 3) letters.

      This Python program collects numbers with fewer than 5 letters, and finds 3 of them that between them use 10 different letters (including the letters of NONSO). We then use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve the alphametic sum.

      It runs in 64ms.

      Run: [ @replit ]

      from enigma import irange, int2words, subsets, union, SubstitutedExpression, sprintf, printf
      
      # collect numbers (as words) with fewer than 5 letters
      words = list(x.upper() for x in map(int2words, irange(0, 10)) if len(x) < 5 and x.isalpha())
      
      # choose three words
      for ws in subsets(words, size=3):
        # check there are 10 different symbols (including NOS)
        ss = union(ws)
        if not(len(ss) == 10 and ss.issuperset('NOS')): continue
      
        # solve the alphametic puzzle
        exprs = list(sprintf("is_square({w})") for w in ws)
        t = sprintf("{ws[0]} + {ws[1]} + {ws[2]} = NONSO")
        exprs.append(t)
        p = SubstitutedExpression(exprs, verbose=0)
        for s in p.solve():
          printf("{t} | {s}", s=p.substitute(s, t))
      

      Solution: The three squares are: 9025, 784, 361.

      These correspond to:

      FOUR = 9025 (= 95²)
      SIX = 784 (= 28²)
      TEN = 361 (= 19²)

      Like

    • GeoffR's avatar

      GeoffR 7:28 pm on 6 July 2021 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % sets of three and four digit squares
      set of int: sq = {n*n | n in 10..99};
      
      % he used each of the digits 0 to 9 inclusive once only
      var 1..9:A; var 0..9:B; var 0..9:C; 
      var 1..9:D; var 0..9:E; var 0..9:F; 
      var 1..9:G; var 0..9:H; var 0..9:I; var 0..9:J; 
      
      constraint all_different([A, B, C, D, E, F, G, H, I, J]);
      
      % 2 no. 3-digit and 1 no. 4-digit squares - ex Jim's analysis
      var 100..999:ABC = 100*A + 10*B + C;
      var 100..999:DEF = 100*D + 10*E + F;
      var 1000..9999:GHIJ = 1000*G + 100*H + 10*I + J;
      
      var 10000..99999:NONSO; % the sum of the three squares
      
      constraint ABC in sq /\ DEF in sq /\ GHIJ in sq;
      
      constraint NONSO ==  ABC + DEF + GHIJ;
      
      % check digit pattern of NONSO for 'N' and 'O'
      constraint NONSO div 10000 == NONSO div 100 mod 10
      /\ NONSO div 1000 mod 10 == NONSO mod 10;
      
      solve satisfy;
      
      output ["Three squares are " ++ show(ABC) ++ ", " ++ show(DEF) 
      ++ ",  " ++ show(GHIJ) ++ "\n" ++ 
      "NONSO, the 5-digit sum = " ++ show(NONSO)];
      
      % Three squares are 361, 784,  9025
      % NONSO, the 5-digit sum = 10170
      % ----------
      % Three squares are 784, 361,  9025
      % NONSO, the 5-digit sum = 10170
      % ----------
      % ==========
      
      
      
      

      Like

      • Frits's avatar

        Frits 6:08 pm on 7 July 2021 Permalink | Reply

        @GeoffR,

        It is not directly stated but I think N, O and S are meant to be different digits. It doesn’t matter for the solution.

        Like

    • John Crabtree's avatar

      John Crabtree 10:31 am on 9 July 2021 Permalink | Reply

      FIVE and NINE only allow one 3-digit number (TWO), and so FOUR is the four-digit number and must contain 0, and, as F = 8 or 9, so must be 9025, 9604 or 9801.
      The sum of the digits of NONSO = 0 mod 9. N = 1. If O = 8, S = 0 which is invalid. If O = 6, S = 4, which is invalid. And so FOUR = 9025, S = 7, ie SIX = 784, which leaves TEN = 361.

      Like

    • GeoffR's avatar

      GeoffR 12:22 pm on 9 July 2021 Permalink | Reply

      # three/four digit squares list
      sq = [x * x for x in range(10, 100)]
      
      digits = set('1234567890')
      
      from itertools import permutations
      
      # 1st stage permutation
      for p1 in permutations('1234567890', 3):
        A, B, C = p1
        if A == '0':
          continue
        ABC = int(A + B + C)
        if ABC not in sq:
          continue
        
        # 2nd stage permutation
        q1 = digits.difference(p1)
        for p2 in permutations(q1, 3):
          X, Y, Z = p2
          if X == '0':
            continue
          XYZ = int(X + Y + Z)
          if XYZ not in sq:
            continue
      
          # 3rd stage permutation
          q2 = q1.difference(p2)
          for p3 in permutations(q2):
            P, Q, R, S = p3
            if P == '0':
              continue
            PQRS = int(P + Q + R + S)
            if PQRS not in sq:
              continue
      
            # form sum of three squares
            NONSO = ABC + XYZ + PQRS
            # check digit pattern of NONSO
            if 10000 < NONSO < 100000:
              if NONSO // 1000 % 10 == NONSO % 10:  # 'O'
                if NONSO // 10000 == NONSO // 100 % 10: # 'N' 
                  print(f"Three Squares are {ABC}, {XYZ}, {PQRS}")
                
      # Three Squares are 361, 784, 9025
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 11:22 am on 4 July 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 38: Digital computer 

    From The Sunday Times, 10th December 1961 [link]

    My nephew Gregory is keen on calendars — an odd interest for a bank clerk.

    “On my next birthday”, he told me yesterday, “I shall take the day off work to celebrate”.

    “To celebrate what?” I asked.

    “To celebrate the fact that it will be the same day of the week as that on which I was born. Moreover, the sum of the digits in the date (e.g. 26/11/1961 = 27) will equal my age, as would also have been the case if had been born just four weeks later than I was”.

    What was Gregory’s date of birth?

    [teaser38]

     
    • Jim Randell's avatar

      Jim Randell 11:23 am on 4 July 2021 Permalink | Reply

      This Python program runs in 54ms.

      Run: [ @replit ]

      from datetime import date, timedelta
      from enigma import dsum, printf
      
      # digit sum of a date
      ddsum = lambda d: dsum(d.day) + dsum(d.month) + dsum(d.year)
      
      # generate dates in a specific range
      inc1 = timedelta(days=1)
      def drange(a, b, step=inc1):
        while not(a > b):
          yield a
          a += step
      
      # consider date of next birthday (within 1 year of the puzzle date)
      for d in drange(date(1961, 12, 10), date(1962, 12, 9)):
        w = d.weekday()
        # must be a workday
        if w == 6: continue
        
        # calculate digit sum (= age)
        n = ddsum(d)
      
        # and calculate the date n years ago
        b = date(d.year - n, d.month, d.day)
      
        # do days of the week match?
        if b.weekday() == w:
      
          # if the birthday was 28 days later ...
          b_ = b + 28 * inc1
          # the digit sum of the nth birthday is also n
          if ddsum(date(b_.year + n, b_.month, b_.day)) == n:
            printf("{d} ({n}) -> born = {b} (+4w = {b_})")
      

      Solution: Gregory was born on Friday, 2nd February 1940.

      So:

      His next birthday, on Friday, 2nd February 1962, is his 22nd.
      And 2+2+1+9+6+2 = 22.

      If he had been born exactly 4 weeks later, it would have been, Friday, 1st March 1940.
      His next birthday, on Thursday, 1st March 1962, is his 22nd.
      And 1+3+1+9+6+2 = 22.

      There is a second candidate solution:

      Gregory was born on Sunday, 11th February 1940.
      His next birthday, on Sunday, 11th February 1962, is his 22nd.
      And 1+1+2+1+9+6+2 = 22.

      If he had been born exactly 4 weeks later, it would have been, Sunday, 10th March 1940.
      His next birthday, on Saturday, 10th March 1962, is his 22nd.
      And 1+0+3+1+9+6+2 = 22.

      But this is eliminated as his next birthday is on a Sunday, so he wouldn’t have to take the day off work.

      Like

    • Jim Olson's avatar

      Jim Olson 9:41 pm on 6 July 2021 Permalink | Reply

      Four weeks after 2/22/1940 would have been 3/21/1940.

      Like

      • Jim Randell's avatar

        Jim Randell 9:51 pm on 6 July 2021 Permalink | Reply

        But if he was born on 22nd February 1940 his next birthday in 1962 would be his 22nd, but the sum of the digits in the date (22/2/1962) would be 24, so it doesn’t work.

        Like

    • Jim Olson's avatar

      Jim Olson 10:06 pm on 6 July 2021 Permalink | Reply

      I think the wording of this Teaser through me off. I see that the 3/21/1962 digits don’t total 22 and that was my problem. 3/1/1940 is not four weeks after his birth. So I didn’t see how this satisfied the requirements of the teaser.

      Like

      • Jim Randell's avatar

        Jim Randell 10:40 pm on 6 July 2021 Permalink | Reply

        1st March 1940 is exactly 4 weeks after 2nd February 1962 because 1940 was a leap year. So the month number goes up 1 and the day number goes down 1, keeping the sum of the digits the same. (Of course the fact that 1962 is not a leap year means that if he had been born 4 weeks later, his next birthday would not be on the same day of the week as the day he was born. But the sum of the digits in the date would be the same as his age, and that is what is important).

        Like

    • Jim Olson's avatar

      Jim Olson 11:50 pm on 6 July 2021 Permalink | Reply

      Apologies I must of misread the posting of the answer as 2/22/1940.

      Like

  • Unknown's avatar

    Jim Randell 4:40 pm on 2 July 2021 Permalink | Reply
    Tags:   

    Teaser 3067: Reciprocal arrangement 

    From The Sunday Times, 4th July 2021 [link] [link]

    Twelve men from our football squad had turned up for training, and I’d promised them a game of six-a-side at the end of the session; so while they were off on a gentle three-mile run I worked out what the two teams would be. They were wearing their squad numbers, which had one or two digits: 2, 3, 4, 5, 6, 7, 8, 9, 15, and three others. It appealed to me when I found that I could divide them into two teams of six, such that the sum of the reciprocals of the squad numbers in each team equalled one exactly.

    What were the squad numbers in the team containing number 2?

    [teaser3067]

     
    • Jim Randell's avatar

      Jim Randell 4:49 pm on 2 July 2021 Permalink | Reply

      (See also: Enigma 348, Enigma 1087, Enigma 1062, Enigma 501).

      I used the [[ reciprocals() ]] function from the enigma.py library (originally written for Enigma 348) to generate the sums of reciprocals. And this lets us keep things entirely in the integer domain.

      This Python program runs in 83ms. (Internal runtime is 21.5ms).

      Run: [ @replit ]

      from enigma import (reciprocals, subsets, union, printf)
      
      # numbers we are given
      numbers = {2, 3, 4, 5, 6, 7, 8, 9, 15}
      
      # look for 6 (different) reciprocals that sum to 1
      ss = reciprocals(6, M=99, g=1)
      
      # choose 2 disjoint sets of numbers
      for (t1, t2) in subsets(ss, size=2):
        ns = union([t1, t2])
        if len(ns) == 12 and ns.issuperset(numbers):
          printf("teams = {t1} vs {t2}")
      

      Solution: The six members of the team containing 2 were: 2, 6, 7, 9, 18, 42.

      And the six members of the other team were: 3, 4, 5, 8, 15, 40.

      Like

      • Jim Randell's avatar

        Jim Randell 5:32 pm on 2 July 2021 Permalink | Reply

        Here’s a slightly longer, but more efficient program.

        It finds three additional shirt numbers that bring the total reciprocal sum for all 12 numbers to 2. And then looks for 5 numbers with a reciprocal sum of 1/2 to go with 2 to make one of the teams.

        Using the [[ rsum() ]] function from the enigma.py library means we don’t have to use rationals.

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

        from enigma import (rsum, reciprocals, subsets, printf)
        
        # numbers we are given
        numbers = {2, 3, 4, 5, 6, 7, 8, 9, 15}
        
        # find the sum of the reciprocals
        (p, q) = rsum(numbers)
        
        # find 3 more numbers that bring the total reciprocal sum to 2
        for xs in reciprocals(3, q, 2 * q - p, m=10, M=99, g=1):
          ns = numbers.union(xs)
          if len(ns) != 12: continue
        
          # choose 5 numbers to go with 2
          for ns1 in subsets(ns.difference([2]), size=5, fn=list):
            if rsum(ns1) == (1, 2):
              t1 = sorted([2] + ns1)
              t2 = sorted(ns.difference(t1))
              printf("teams = {t1} vs {t2}")
        

        Like

    • Frits's avatar

      Frits 1:56 pm on 3 July 2021 Permalink | Reply

      Based on Jim’s second approach (avoiding rational number logic).

      from enigma import lcm, divisors
      
      # pick <k> elements from list <li> that sum to <s>
      def pickElements(k, li, s, ns=[]):
        if k == 1:
          r = s - sum(ns)
          if r in li and r not in ns:
            yield [L // x for x in ns + [r]]
        else:
          for i in range(len(li)):
            yield from pickElements(k - 1, li[i + 1:], s, ns + [li[i]])
      
      # numbers we are given
      numbers = {2, 3, 4, 5, 6, 7, 8, 9, 15}
      
      L = lcm(*numbers)  # Least Common Multiple
      
      # sum(i=1..12, 1 / x) = 2 then also sum(i=1..12, L / x) = 2 * L
      rest = 2 * L - sum(L // x for x in numbers)
      
      # determine candidates for three remaining squad numbers
      cands = [L // x for x in divisors(L) if 9 < x < 100 and x not in numbers]
      
      last2 = cands[-1] + cands[-2]
      # remove big values from list
      cands = [x for x in cands if x + last2 <= rest]
      
      # pick three remaining squad numbers
      for p in pickElements(3, cands, rest):
        twelve = numbers | set(p)
        (s1, s2) = (twelve.difference([7]), [7])
      
        # in the group containing 7 there must be a multiple of 7 as well
        p7 = [x for x in p if x % 7 == 0]
        if len(p7) == 1:
          s1 = s1.difference([p7[0]])
          s2.append(p7[0])
      
        # determine candidates for the group with 7
        cands2 = [L // x for x in twelve if x not in s2]
        rest2 = L - sum(L // x for x in s2)
       
        # pick squad numbers to go with 7 (and multiple of 7)
        for p2 in pickElements(6 - len(s2), cands2, rest2):
          t1 = p2 + s2
          if 2 in t1:
            print("answer:", sorted(t1))
          else:
            print("answer:", sorted(twelve.difference(t1)))
      

      Like

    • GeoffR's avatar

      GeoffR 7:08 pm on 16 September 2021 Permalink | Reply

      Probably not the best idea to mix floating point and integer numbers generally, but with very small difference tests, it gave the correct solution.

      from itertools import combinations
      
      NL = []   # list for squad numbers of two teams
      
      # set of given squad numbers
      NS = {2, 3, 4, 5, 6, 7, 8, 9, 15}
      
      for p1 in combinations(range(10, 100), 3):
        # find three other squad numbers
        a, b, c = p1
        if any(x in NS for x in (a, b, c)):
          continue
        s12num = 1 / 2 + 1 / 3 + 1 / 4 + 1 / 5 + 1 / 6 + 1 / 7 + \
               1 / 8 + 1 / 9 + 1 / 15 + 1 / a + 1 / b + 1 / c
        if abs(s12num - 2) >= 1e-9:
          continue
        NS = NS.union([a, b, c])  # missing squad numbers
        
        # find numbers in 1st squad
        for p2 in combinations(NS, 6):
          d, e, f, g, h, i = p2
          # find reciprocal sum of 1st squad numbers
          t1 = 1 / d + 1 / e + 1 / f + 1 / g + 1 / h + 1 / i
          if abs(t1) - 1 >= 1e-9:
            continue
          Q = NS.difference([d, e, f, g, h, i])
      
          # find numbers in 2nd squad
          for p3 in combinations(Q, 6):
            j, k, m, n, p, q = p3
            # find reciprocal sum of 2nd squad numbers
            t2 = 1 / j + 1 / k + 1 / m + 1 / n + 1 / p + 1 / q
            if abs(t2) - 1 >= 1e-9:
              continue
            if p2 not in NL: NL.append(p2)
            if p3 not in NL: NL.append(p3)
            
      # find team numbers of two teams
      if len(NL) == 2:
        print(f" 1st Team Numbers are {sorted(NL[0])}")
        print(f" 2nd Team Numbers are {sorted(NL[1])}")
          
      # 1st Team Numbers are [2, 6, 7, 9, 18, 42] <<< Ans
      # 2nd Team Numbers are [3, 4, 5, 8, 15, 40]
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:10 am on 1 July 2021 Permalink | Reply
    Tags: ,   

    Teaser 2799: Desert patrol 

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

    Pat is at Base Camp in the desert. There is enough fuel at the base for his vehicle to travel 420 miles, but the vehicle can hold (in its tank and in cans) at most enough fuel for 105 miles. On any journey he can leave some fuel in cans at any point and then pick up some or all of it whenever he passes in future. He has worked out that there is just enough fuel for him to reach the Main Camp.

    How far is it between the two camps?

    [teaser2799]

     
    • Jim Randell's avatar

      Jim Randell 10:11 am on 1 July 2021 Permalink | Reply

      This is a similar idea to Enigma 1732, except that this is for a 1 way journey.

      See: Jeep Problem [ @wikipedia ].

      This Python program runs in 54ms.

      Run: [ @replit ]

      from enigma import (Rational, irange, inf, printf)
      
      Q = Rational()  # choose an implementation of rationals
      
      T = 420  # total amount of fuel available
      R = 105  # range
      
      # consider amount of fuel required for a maximal distance in k trips
      f = d = 0
      for k in irange(1, inf):
        f += R
        d += R * Q(1, 2 * k - 1)
        printf("{k} trips, fuel = {f}, distance = {x:.2f} ({d})", x=float(d))
        # stop when the fuel required reaches (or exceeds) the available fuel
        if not (f < T): break
      

      Solution: The distance between the camps is 176 miles.

      The journey will require 4 trips:

      Trip 1:
      With a full load of fuel (105 miles), drive 15 miles (= 105 / 7).
      Make a fuel dump with 75 miles of fuel, and use the remaining 15 miles to drive back.
      There is 315 miles of fuel remaining at base camp.

      Trip 2:
      With a full load (105 miles), drive 15 miles, and fill up at dump 1 (leaving 60 miles at dump 1).
      Drive a further 21 miles (= 105 / 5), and make a fuel dump with 63 miles of fuel.
      Drive back to dump 1 using the remaining 21 miles of fuel, and refuel for the last 15 miles (leaving 45 miles at dump 1).
      There is 210 miles of fuel remaining at base camp.

      Trip 3:
      With a full load (105 miles), drive 15 miles to dump 1 and refuel (leaving 30 miles at dump 1).
      Drive 21 miles to dump 2 and refuel (leaving 42 miles of fuel at dump 2).
      Drive 35 miles (= 105 / 3) to dump 3 and leave 35 miles of fuel.
      Drive back to dump 2 using the remaining 35 miles of fuel, and refuel for the 21 miles to dump 1 (leaving 21 miles at dump 2).
      Drive back to dump 1 and refuel for the 15 miles to base camp (leaving 15 miles at dump 1).
      There is 105 miles of fuel remaining at base camp.

      Trip 4:
      Fill up with the remaining fuel (105 miles), drive 15 miles to dump 1 and refuel (exhausting dump 1).
      Drive 21 miles to dump 2 and refuel (exhausting dump 2).
      Drive 35 miles to dump 3 and refuel (exhausting dump 3).
      Drive 105 miles (= 105 / 1) and arrive at main camp.
      Total distance = 15 + 21 + 35 + 105 = 176 miles.

      Like

    • Frits's avatar

      Frits 1:44 pm on 8 July 2021 Permalink | Reply

      Similar (next one R=315, T = 5*R)

      T = 420       # total amount of fuel available
      R = 105       # range
      
      k = T // R    # number of trips
      
      # number of times traject <n-1>th depot to the <n>th depot is travelled 
      V = lambda k, n : 2 * (k - n) + 1
      
      # T / R = k = 4 so 4 trips can be made setting up 3 depots.
      # The traject <n-1>th depot to the <n>th depot (n = 1..3) is travelled 
      # V(n) = 2 * (k - n) + 1 times (where 0th depot is the base camp) so we need
      # to split R into V(n) equal parts and store V(n) - 2 parts at the depot 
      # leaving 2 parts to reach and return from the <n>th depot
      # this means the <n>th depot is sum (i=1..n, R / V(n)) miles from base camp
       
      # consider amount of fuel required for a maximal distance in k trips
      print("answer:", R + sum(R // V(k, n) for n in range(1, k)), "miles")
      

      Like

      • Frits's avatar

        Frits 3:39 pm on 8 July 2021 Permalink | Reply

        or

        from math import log
        
        def H(n):
            """Returns an approximate value of n-th harmonic number.
        
               http://en.wikipedia.org/wiki/Harmonic_number
            """
            # Euler-Mascheroni constant
            gamma = 0.57721566490153286060651209008240243104215933593992
            return gamma + log(n) + 0.5 / n - 1 / (12 * n**2) + 1 / (120 * n**4)
            
        R = 105    
        n = 4
        print("answer:", round((H(2 * n - 1) - 0.5 * H(n - 1)) * R), "miles")
        

        Like

    • Frits's avatar

      Frits 1:43 pm on 17 July 2021 Permalink | Reply

      Different order:

      # make 3 return trips to 1st depot (15 miles from Base Camp) and 
      #      each time drop 75 miles of fuel at 1st depot
      # make 4th trip to 1st depot and fill up (210 miles of fuel left at 1st depot)
      # make 2 return trips from 1st to 2nd depot (21 miles from 1st depot) and
      #      each time drop 63 miles of fuel at 2nd depot
      # make 3th trip from 1st to 2nd depot and fill up 
      #    (0 / 105 miles of fuel left at 1st/2nd depot)
      # make a return trip from 2nd to 3rd depot (35 miles from 2nd depot) and
      #      drop 35 miles of fuel at 3rd depot
      # make 2nd trip from 2nd to 3rd depot and fill up
      #    (0 / 0 / 0 miles of fuel left at 1st/2nd/3rd depot)
      # drive 150 miles to Main Camp
      
      # Total miles: 15 + 21 + 35 + 150 = 176 miles
      

      Like

  • Unknown's avatar

    Jim Randell 9:55 am on 29 June 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 790: Multi-value coil 

    From The Sunday Times, 5th September 1976 [link]

    Post Office multi-value coil machines are designed to vend strips of five stamps. Soon after the introduction of the 8½p and 6½p first and second class postal rates the machines sold for 10p a strip of stamps values 6p, 1p, ½p, 2p and ½p respectively (as always with these machines the last stamp being the lowest in case of tearing). While catering for both rates it was impossible to make up either 6½p or 8½p with a joined strip of stamps.

    However an efficiency expert worked out a possible 10p strip which afforded the following:

    (i) from one strip either first or second rate in a joined strip;
    (ii) from two joined strips, three second class rates, each in a joined strip;
    (iii) from three joined strips, two first class rates plus two second class rates,  each in a joined strip.

    Of course the “expert” assumed that all ½p steps from ½p to 10p were available in stamps.

    What was his suggested strip?

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

    [teaser790]

     
    • Jim Randell's avatar

      Jim Randell 9:56 am on 29 June 2021 Permalink | Reply

      Working in units of half-pennies we can keep amounts in integers.

      The value of one complete strip is 20 (= 10p), the first class rate is 17 (= 8.5p), and the second class rate is 13 (= 6.5p).

      This Python 3 program runs in 82ms.

      Run: [ @replit ]

      from enigma import irange, subsets, printf
      
      # decompose total <t> into <k> positive integers
      def decompose(t, k, ns=[]):
        if k == 1:
          yield ns + [t]
        else:
          k -= 1
          for n in irange(1, t - k):
            yield from decompose(t - n, k, ns + [n])
      
      # can values in <vs> be made from segments of <ss>
      def _connected(vs, ss):
        # are we done?
        if not vs:
          return True
        else:
          v = vs[0]
          ss = list(x for x in ss if x)  # drop empty lists
          # consider segments
          for (i, ns) in enumerate(ss):
            if sum(ns) < v: continue  # shortcut
            # and sub-segments
            for (j, k) in subsets(irange(0, len(ns)), size=2):
              if sum(ns[j:k]) == v:
                if _connected(vs[1:], ss[:i] + ss[i + 1:] + [ns[:j], ns[k:]]):
                  return True
          return False
      
      connected = lambda vs, ns: _connected(vs, [ns])
      
      # check a sequence
      def check(ns):
        # (i) 13 and 17 can be made from 1 strip
        if not(connected([13], ns) and connected([17], ns)): return False
        # (ii) three lots of 13 from 2 joined strips
        if not(connected([13] * 3, ns * 2)): return False
        # (iii) two lots of 13 _and_ two lots of 17 from 3 joined strips
        if not(connected([13, 17] * 2, ns * 3)): return False
        # passed
        return True
      
      # look for viable strips
      for ns in decompose(20, 5):
        # lowest value stamp comes last
        if ns[-1] == min(ns) and check(ns):
          # output solution
          printf("strip = {ns}")
      

      Solution: The stamps on the suggested strip were: 1½p, 1½p, 3½p, 3p, ½p.

      Like

  • Unknown's avatar

    Jim Randell 10:28 am on 27 June 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 37: Which page? 

    From The Sunday Times, 3rd December 1961 [link]

    A student desiring to find a reference in a book, could not remember the actual page number, but only the three figures comprising it.

    Before beginning his search, he wrote down in ascending order the six possible variants of the three figures. In writing the second variant his pencil broke on
    a figure which thereafter was illegible. With a fresh pencil he completed the list of variants and then found his reference, the page number being the second variant.

    Some time later he found in his pocket the scrap of paper bearing the six numbers. Idly adding them he obtained a total of 4,796, having unwittingly misread the illegible figure.

    On what page was the reference?

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

    This puzzle was originally published with no title.

    [teaser37]

     
    • Jim Randell's avatar

      Jim Randell 10:29 am on 27 June 2021 Permalink | Reply

      If he had written out each of the 6 numbers correctly (the digits being a, b, c), then each digit would appear twice in each position so the total sum of the six numbers would be:

      T = 222×(a + b + c)

      If the misinterpreted digit is off by x then the total sum is off by 100x if the first digit of the number is misinterpreted, 10x if its the second digit, and x if its the final digit.

      This Python program runs in 49ms.

      Run: [ @replit ]

      from enigma import (irange, subsets, div, nconcat, printf)
      
      M = 4796  # mistaken total
      
      # check to see if "abc" can be misread by d
      def check(a, b, c, d):
        # error in hundreds position? (a)
        x = div(abs(d), 100)
        if x:
          return 0 < (a + x if d > 0 else a - x) < 10
        # error in tens position? (b)
        x = div(abs(d), 10)
        if x:
          return 0 < (b + x if d > 0 else b - x) < 10
        # error in units position? (c)
        return 0 < (c + d) < 10
      
      # consider possible digits in the number
      for (a, b, c) in subsets(irange(1, 9), size=3):
        # the total sum should be...
        T = 222 * (a + b + c)
        # and the difference from the calculated sum
        d = M - T
        # digits of the 2nd number in the list are a, c, b
        if d != 0 and check(a, c, b, d):
          n = nconcat(a, c, b)
          printf("reference = {n} [read as {m}]", m=n + d)
      

      Solution: The reference was on page 198.

      So the sum of the numbers should have been 3996, but the second number was read as 998, adding 800 to the sum.

      Like

      • Frits's avatar

        Frits 10:22 am on 12 August 2025 Permalink | Reply

        from enigma import SubstitutedExpression  
        
        M = 4796  # mistaken total
        vars = "ABC"
        
        # i * j = difference with respect to the correct number 
        for i in range(-9, 10):
          if not i: continue
          for k, j in enumerate([100, 10, 1]):
            t, r = divmod(M - i * j, 222)
            if r: continue
            sum_ABC = str((M - i * j) // 222)
            # illegible figure may also have been a zero if idly adding them
            extra = "0 <= " + vars[k] + " + " + str(i) + " < 10"
            # digits of the 2nd number in the list are a, c, b
            p1 = SubstitutedExpression(["C < B", sum_ABC + " - B - C = A", 
                                        extra, "A < C"],
                                       answer="ABC", 
                                       digits=range(1, 10),
                                       reorder=0, 
                                       verbose=0)
            
            # solve the first alphametic
            for ans in p1.answers():
              print("answer:", ans)
        

        Like

  • Unknown's avatar

    Jim Randell 4:46 pm on 25 June 2021 Permalink | Reply
    Tags:   

    Teaser 3066: Leaning tower of pesos 

    From The Sunday Times, 27th June 2021 [link] [link]

    I have six old coins worth an odd number of pesos, comprising a mixture of one- and two-peso coins. Both denominations are of the same diameter and made of the same metal, but the two-peso coins are twice the thickness of the one-peso coins.

    After making a vertical stack of the coins I then slid each of the top five coins as far as possible to the right, to make the pile lean as much as possible in that direction, without toppling. I managed to get the rightmost edge of the top coin a distance of one and a quarter times its diameter further right than the rightmost edge of the bottom coin.

    Starting from the top, what is the value of each of the six coins?

    [teaser3066]

     
    • Jim Randell's avatar

      Jim Randell 6:04 pm on 25 June 2021 Permalink | Reply

      This is a block-stacking problem [@wikipedia] (or book-stacking, or coin-stacking, …).

      This paper [link] gives a good explanation of the basic idea. (See also this paper [link] for more complicated stackings).

      The lowest coin acts as the table, and we need to balance 5 coins on top of it.

      The following Python program runs in 53ms.

      Run: [ @replit ]

      from enigma import Rational, subsets, printf
      
      Q = Rational()
      
      # stack a collection of equal length blocks, with masses <ms>
      # return  (M, D) = (total mass, total displacement)
      def stack(ms):
        (M, D) = (0, 0)
        for m in ms:
          # considering moments: M' = M + m; M'D' = m(D + 1/2) + MD
          M += m
          D += Q(m, 2 * M)
        return (M, D)
      
      # consider the distribution of the top 5 coins
      for vs in subsets((1, 2), size=5, select="M", fn=list):
        # construct the stack
        (M, D) = stack(vs)
        # the required displacement is 5/4
        if D == Q(5, 4):
          # the bottom coin makes the total value odd
          vs.append((M % 2) + 1)
          # check each denomination is used
          if set(vs) == {1, 2}:
            printf("coins = {vs} [top to bottom]")
      

      Solution: The values of the coins are: 1, 2, 1, 2, 2, 1.

      Here is a diagram of the layout:

      The top coin is displaced from the bottom coin by 1.25 diameters.

      But this is not the maximal possible displacement, the following stacks (top to bottom) give greater displacement:

      (1, 1, 1, 2, 2, x) → 1.260 (529/420)
      (1, 2, 2, 2, 2, x) → 1.287 (811/630)
      (1, 1, 2, 2, 2, x) → 1.292 (31/24)

      Like

    • Tony Brooke-Taylor's avatar

      Tony Brooke-Taylor 11:57 am on 26 June 2021 Permalink | Reply

      I think I have followed a similar approach working from first principles.

      
      #Since the coins have the same diameter we can work in units of one coin radius. The thickness of the coins is irrelevant but we know that their mass is proportionate to their value
      
      #Find the horizontal position of the centre of mass for a list of coins
      #Vector A contains the positions of the centres of the coins and defines the list
      #Vector B contains the values of the coins
      def CoM(A,B):
        C=[a*b for a,b in zip(A[:len(B)],B)]
        return sum(C)/sum(A[:len(B)])
      
      #Loop over possible vectors of values for the top 5 coins (5 binaries = 32)
      for m in range(32):
        vals=[1+int(i) for i in bin(m)[2:].zfill(5)]
        dists=[2.5]#Given the position of the topmost coin...
        for d in range(5):#...find the maximum distance of the rest from the origin
          dists.append(CoM(vals,dists[:d+1])-1)
          #The edge of each coin must be immediately below the centre of mass of those above it
      
        if dists[5] == 0:#The centre of the bottom coin must be at the origin
          vals+=[1-sum(vals)%2]#To make the total value odd
          
          print("The values in order, starting from the top, were:")
          print(vals)
      
      
      

      Like

    • Hugh Casement's avatar

      Hugh Casement 10:04 am on 4 July 2021 Permalink | Reply

      Given that the total value is odd, there must be an odd number of each denomination.
      Admittedly the value of the bottom coin is immaterial.

      I tested it with some parquet-floor slabs. The theoretical overhangs are not achievable in practice, as the stack starts to teeter. But one can get close.

      Like

  • Unknown's avatar

    Jim Randell 12:26 pm on 24 June 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 787: Puzzlers’ office party 

    From The Sunday Times, 15th August 1976 [link]

    Brainbenders and Co., the noted games and puzzles manufacturers, held their office party recently.

    The eight from the planning department shared one round table. Pat Robinson and Ann arrived together. Miss Davis looked sensational in her new dress and Mr Armstrong’s suit was fresh from the cleaners.

    Red-haired John sat on Miss Jones’s right while Mary sat next-but-one to both Miss Brown and Miss Stevens. Joan only had eyes for Mr Evans, opposite her, while her neighbour, Edna, was interested in Fred, who was sitting beside Bill and next-but-one to Miss Stevens.

    Mr Smith was the only man between two girls and Miss Brown the only girl between two fellows. However, only two people sat opposite a person of their own sex.

    What was the seating plan?

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

    [teaser787]

     
    • Jim Randell's avatar

      Jim Randell 12:27 pm on 24 June 2021 Permalink | Reply

      I used the [[ SubstitutedExpression ]] solver from the enigma.py library to find viable assignments of first/last names to positions 0 to 7 around the table.

      The following run file finds the assignments of names to positions:

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # we have 8 first names:
      #  Pat, Ann, John, Mary, Joan, Edna, Fred, Bill
      #  A    B    C     D     E     F     G     H
      #
      # and 8 last names:
      #  Robinson, Jones, Davis, Armstrong, Brown, Stevens, Evans, Smith
      #  S         T      U      V          W      X        Y      Z
      --distinct="ABCDEFGH,STUVWXYZ"
      
      # and we need to assign each to a position 0-7
      --base=8
      
      # start with Pat Robinson in position 0
      --assign="A,0"
      --assign="S,0"
      
      # John sat on Jones' right
      "(C + 1) % 8 = T"
      
      # Mary sat next but 1 to Brown and Stevens
      "abs(D - W) in {2, 6}"
      "abs(D - X) in {2, 6}"
      
      # Joan was opposite Evans
      "(E + 4) % 8 = Y"
      
      # Joan was next to Edna
      "abs(E - F) in {1, 7}"
      
      # Fred was next to Bill, and next but 1 to Stevens
      "abs(G - H) in {1, 7}"
      "abs(G - X) in {2, 6}"
      
      # return the positions for each first name and last name
      --answer="((A, B, C, D, E, F, G, H), (S, T, U, V, W, X, Y, Z))"
      

      We are given the genders associated with 7 of the 8 surnames, so only Robinson remains undecided.

      The following Python program takes the output of the above run file, and performs the gender checks. It runs in 59ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, irange, tuples, printf)
      
      # load the solver for the names
      p = SubstitutedExpression.from_file("teaser787.run")
      
      # perform the gender checks
      def check(gs, Smith, Brown):
      
        # only 2 people sat opposite a person of the same gender
        # (i.e. there is exactly 1 pair)
        if not(sum(gs[i] == gs[i + 4] for i in irange(0, 3)) == 1): return False
      
        # find positions of gender X between gender Y
        def between(X, Y):
          for (j, i, k) in tuples(irange(0, 7), 3, circular=1):
            if gs[i] == X and gs[j] == gs[k] == Y:
              yield i
      
        # Smith is the only male between 2 females
        if not(set(between(0, 1)) == {Smith}): return False
        # Brown is the only female between 2 males
        if not(set(between(1, 0)) == {Brown}): return False
      
        # looks OK
        return True
      
      # find solutions for first/last names
      for (s, (fps, lps)) in p.solve(verbose=0):
      
        # assign the genders we know:
        # m = Mr Armstrong, Mr Evans, Mr Smith
        # f = Miss Davis, Miss Jones, Miss Brown, Miss Stevens
        # and choose the remaining value (Robinson)
        for g in (0, 1):
          # map gs: pos -> gender (0=male, 1=female)
          gs = dict(zip(lps, [g, 1, 1, 0, 1, 1, 0, 0]))
          if not check(gs, s['Z'], s['W']): continue
      
          # output solution
          last = dict(zip(lps, ["Robinson", "Jones", "Davis", "Armstrong", "Brown", "Stevens", "Evans", "Smith"]))
          first = dict(zip(fps, ["Pat", "Ann", "John", "Mary", "Joan", "Edna", "Fred", "Bill"]))
          for i in irange(0, 7):
            printf("{i}: ({x}) {first} {last}", i=i + 1, x="mf"[gs[i]], first=first[i], last=last[i])
          printf()
      

      Solution: The seating plan is (clockwise from Pat Robinson):

      1: Pat Robinson (f)
      2: Fred Armstrong (m)
      3: Bill Evans (m)
      4: Ann Brown (f)
      5: John Smith (m)
      6: Mary Jones (f)
      7: Joan Davis (f)
      8: Edna Stevens (f)

      Like

      • Jim Randell's avatar

        Jim Randell 4:03 pm on 24 June 2021 Permalink | Reply

        Or with all the code in a single file:

        from enigma import SubstitutedExpression, irange, tuples, printf
        
        # perform the gender checks
        def check(ns, gs, Smith, Brown):
          # map d: pos -> gender (0=male, 1=female)
          d = dict(zip(ns, gs))
        
          # only 2 people sat opposite a person of the same gender
          # (i.e. there is exactly 1 pair)
          if not(sum(d[i] == d[i + 4] for i in irange(0, 3)) == 1): return False
        
          # find positions of gender X between gender Y
          def between(X, Y):
            for (j, i, k) in tuples(irange(0, 7), 3, circular=1):
              if d[i] == X and d[j] == d[k] == Y:
                yield i
        
          # Smith is the only male between 2 females
          if not(set(between(0, 1)) == {Smith}): return False
          # Brown is the only female between 2 males
          if not(set(between(1, 0)) == {Brown}): return False
        
          # looks OK
          return True
        
        # we have 8 first names:
        #  Pat, Anne, John, Mary, Joan, Edna, Fred, Bill
        #  A    B     C     D     E     F     G     H
        #
        # and 8 last names:
        #  Robinson, Jones, Davis, Armstrong, Brown, Stevens, Evans, Smith
        #  S         T      U      V          W      X        Y      Z
        p = SubstitutedExpression([    
            "(C + 1) % 8 = T",  # John sat on Jones' right
            "abs(D - W) in {2, 6}",  # Mary sat next but 1 to Brown ...
            "abs(D - X) in {2, 6}",  # ... and Stevens
            "(E + 4) % 8 = Y",  # Joan was opposite Evans ...
            "abs(E - F) in {1, 7}",  # ... and next to Edna
            "abs(G - H) in {1, 7}",  # Fred was next to Bill ...
            "abs(G - X) in {2, 6}",  # ... and next but 1 to Stevens
            "check([S, T, U, V, W, X, Y, Z], [N, 1, 1, 0, 1, 1, 0, 0], Z, W)", # gender checks
          ],
          distinct=("ABCDEFGH", "STUVWXYZ"),
          base=8,  # assign each name a position 0-7
          s2d=dict(A=0, S=0),  # start with Pat Robinson in position 0
          d2i=dict((d, 'N') for d in irange(2, 7)),
          env=dict(check=check),
          answer="((A, B, C, D, E, F, G, H), (S, T, U, V, W, X, Y, Z), N)",
          verbose=0
        )
        
        # find solutions for first/last names, and Robinson's gender
        for (s, (fps, lps, g)) in p.solve():
          # output solution
          gs = dict(zip(lps, [g, 1, 1, 0, 1, 1, 0, 0]))
          last = dict(zip(lps, ["Robinson", "Jones", "Davis", "Armstrong", "Brown", "Stevens", "Evans", "Smith"]))
          first = dict(zip(fps, ["Pat", "Anne", "John", "Mary", "Joan", "Edna", "Fred", "Bill"]))
          for i in irange(0, 7):
            printf("{i}: ({x}) {first} {last}", i=i + 1, x="mf"[gs[i]], first=first[i], last=last[i])
          printf()
        

        Like

      • Frits's avatar

        Frits 11:30 am on 1 July 2021 Permalink | Reply

        Adding the following to the run member limits the possibilities to one.

        # Smith is the only male between 2 females
        # this must be John as Fred was next to Bill
        "C == Z",
        
        # Brown is the only female between 2 males
        # so Brown must be next to John
        "abs(C - W) in {1, 7} and (abs(G - W) in {1, 7} or abs(H - W) in {1, 7})"
        
        # only 2 people sat opposite a person of the same gender
        # so all males didn't sit opposite each other
        "4 not in [abs(C - G), abs(C - H)]"
        
        # male surnames are Armstrong, Evans and Smith
        "sorted([C, G, H]) == sorted([V, Y, Z])"
        

        Like

        • Jim Randell's avatar

          Jim Randell 2:36 pm on 1 July 2021 Permalink | Reply

          I wasn’t sure if the setter intended us to infer gender from first names (although clearly “Pat” is deliberately ambiguous).

          But it turns out that it is not necessary to do so to solve the puzzle, so I didn’t.

          Like

  • Unknown's avatar

    Jim Randell 3:24 pm on 22 June 2021 Permalink | Reply
    Tags:   

    Teaser 2798: Housey-housey 

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

    Philip, Daphne and I live in different houses on Teaser Drive. Philip lives at number 1 and the houses are then numbered consecutively along one side of the road. The sum of the house numbers strictly between Philip’s house and mine is equal to the sum of the house numbers strictly between my house and Daphne’s. Furthermore, the digits of Daphne’s house number add up to the same total as the digits of my house number.

    What is my house number and what is Daphne’s?

    [teaser2798]

     
    • Jim Randell's avatar

      Jim Randell 3:24 pm on 22 June 2021 Permalink | Reply

      We can calculate the sum of the house numbers between two houses (non-inclusive):

      S(a, b) = (a + 1) + … + (b − 1)
      S(a, b) = T(b − 1) − T(a)

      This program finds the first viable solution. It runs in 51ms.

      Run: [ @replit ]

      from enigma import T, irange, inf, dsum, printf
      
      # calculate the "inter-sum" of a and b
      S = lambda a, b: T(b - 1) - T(a)
      
      # generate solutions
      def solve(P):
        # consider values for setters house number (R)
        for R in irange(P + 1, inf):
          s = S(P, R)
          # look for D with an equal value, and equal digit sum
          for D in irange(R + 1, inf):
            s_ = S(R, D)
            if s_ > s: break
            if s_ == s and dsum(D) == dsum(R):
              yield (R, D)
      
      # look for the first solution
      for (R, D) in solve(1):
        printf("R={R}, D={D}")
        break
      

      Solution: Your house number is 64. Daphne’s house number is 91.

      Like

      • Jim Randell's avatar

        Jim Randell 3:27 pm on 22 June 2021 Permalink | Reply

        The program above finds the smallest solution (which is the required answer), but there are larger solutions. The next one is:

        R=85225144, D=120526555

        The numbers involved are too large to be house numbers, but we can write a more efficient program to investigate larger solutions.

        Ignoring the digit sum condition, we can look for (R, D) values that give equal inter-sums:

        S(1, R) = S(R, D)
        2R² = D² − D + 2
        R² − 1 = D(D − 1)/2

        So we can look for triangular numbers that are one less than a square, and this lets us find the larger solution in a few seconds:

        from enigma import (is_square, dsum, printf)
        
        # generate solutions
        def solve():
          t = 0
          D = 1
          # look for triangular numbers that are 1 less than a square
          while True:
            t += D
            R = is_square(t + 1)
            D += 1
            if R is not None and dsum(D) == dsum(R):
              yield (R, D)
        
        # look for the first solution
        for (R, D) in solve():
          printf("R={R} D={D}")
          break
        

        To get a faster program we can look at the sequence of (R, D) values with equal “inter-sums”:

        R = 1, 1, 2, 4, 11, 23, 64, … [= OEIS A006452]
        D = 0, 1, 3, 6, 16, 33, 91, … [= OEIS A006451 + 1]

        After the first 4 values these sequences can be generated by the following recurrence relations:

        R[n] = 6 R[n − 2] − R[n − 4]
        D[n] = 6 D[n − 2] − D[n − 4] − 2

        We can then test the generated (R, D) values to look for pairs with equal digit sums:

        from enigma import (dsum, first, arg, printf)
        
        # find (R, D) pairs
        def solve():
          # first 4 (R, D) pairs
          q = [(1, 0), (1, 1), (2, 3), (4, 6)]
          
          while True:
            # compute the next pair
            (R2, D2) = q[2]
            (R0, D0) = q[0]
            R = 6 * R2 - R0
            D = 6 * D2 - D0 - 2
            if dsum(R) == dsum(D): yield (R, D)
        
            q.pop(0)
            q.append((R, D))
        
        # find the first N solutions
        N = arg(8, 0, int)
        for (i, (R, D)) in enumerate(first(solve(), N, fn=iter), start=1):
          printf("{i}: R={R}, D={D}")
        

        Here are the first 8 solutions:

        % python3 teaser2798g.py 8
        1: R=64, D=91
        2: R=85225144, D=120526555
        3: R=98349742324, D=139087539451
        4: R=151143569481047247784, D=213749285825577237211
        5: R=1844521723207478395861, D=2608547637051808009585
        6: R=2128576470207852702322273, D=3010261712716195592552833
        7: R=2834655085444781980043036964601, D=4008807666485875255750052272225
        8: R=268047403891416806462822495283352, D=379076273942140382330486943077083
        

        The (R, D) values can also be generated using Pell’s Equation ((2D − 1)² − 8R² = −7), which gives the following recurrence relation (as noted by Brian Gladman [link]):

        R = 1, 1, …
        D = 0, 1, …
        R[n] = 2 D[n − 2] + 3 R[n − 2] − 1
        D[n] = 3 D[n − 2] + 4 R[n − 2] − 1

        Like

    • GeoffR's avatar

      GeoffR 8:01 pm on 22 June 2021 Permalink | Reply

      I tested for a solution for 2-digit house numbers up to 100.

      This solution tests for equality of the sum of two arithmetic series of house numbers:

      1) Between Philip and Me (P and M)
      2) Between Me and Daphne (M and D)

      # P..................M..............D  house numbers
      # no. of houses between M and D is (D - M - 1)
      
      P = 1
      
      for M in range(10, 100):
        for D in range(M+1, 100):
          # no. of houses for series between P -> M
          nh1 = M - 2
          # no. of houses for series between M -> D
          nh2 = D - M - 1
          # S1 = arithmetic sum of numbers between P and M
          S1 = nh1 // 2 * (2 * (P + 1) + (nh1 - 1) * 1)
          # S2 = arithmetic sum of numbers between M and D
          S2 = nh2 // 2 * (2 * (M + 1) + (nh2 - 1) * 1)
          if S1 == S2:
            # digits in house numbers M and D
            d1, d2 = M // 10, M % 10
            d3, d4 = D // 10, D % 10
            # the digits of Daphne’s house number add up to
            # ..the same total as the digits of my house number
            if d1 + d2 == d3 + d4:
              print(f"My house number is {M}.")
              print(f"Daphne's house number is {D}.")
      
      # My house number is 64.
      # Daphne's house number is 91.
      

      Like

    • John Crabtree's avatar

      John Crabtree 4:45 pm on 25 June 2021 Permalink | Reply

      Brian Gladman expresses the Pell equation as
      (2D- 1)^2 – 8R^2 = – 7
      Let E = 2D – 1 and then E^2 -8R^2 = -7
      The Wolfram web page on the Pell equation suggests that there may be multiple series derived from multiple basic solutions. That is the case here. One sequence is
      R = 1, 4, 23, …..
      E = 1, 11, 65, …
      The other sequence is:
      R = 1, 2, 11, 64, …..
      E = -1, 5, 31, 181, ….
      In each case R(n) = 6 * R(n-1) – R(n-2) and E(n) = 6 * E(n-1) – E(n-2)
      This makes it easier to solve the difference equations.
      Or R(n) = 3 * R(n – 1) + E(n – 1) and E(n) = 3 * E(n – 1) + 8(n-1)

      One can show that the fact that the digits in D’s and R’s numbers add to the same number means that R = D = 1 mod 3. The reduces the work in a manual (calculator) search.

      Like

    • GeoffR's avatar

      GeoffR 6:46 pm on 26 June 2021 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 10..99:M;  % My house number
      var 10..99:D;  % Daphne's house number
      
      % house number digits for Me and Daphne
      var 1..9:d1; var 1..9:d2; var 1..9:d3; var 1..9:d4;
      
      % Using Brian's equation: (2.D - 1)^2 - 2(2.M)^2 = -7
      constraint (2*D - 1) * (2*D - 1) - 2 * (2 * M)*(2 * M) == -7;
      
      % Equate sum of digits of M and D
      constraint d1 == M div 10 /\ d2 == M mod 10 /\ d3 == D div 10 
      /\ d4 == D mod 10 /\ (d1 + d2) == (d3 + d4);
      
      solve satisfy;
      
      output[ "My house = " ++ show(M) ++ " and Daphne's house = " ++ show(D) ];
      
      % My house = 64 and Daphne's house = 91
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:18 am on 20 June 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 36: [Telephone numbers] 

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

    On one of June’s birthdays her father telephoned some of her friends inviting them to June’s party. He noticed that Joan’s telephone number consisted of the four digits in his own, but in reverse order, and that one phone number divided by the other gave June’s age.

    On a subsequent birthday June rang up Jennifer from a call box and she also noticed that Jennifer’s number and that of the call box were four digit numbers, each the reverse of the other. Dividing these two numbers gave June’s latest age.

    How old was June when the two phone calls were made?

    This puzzle was originally published with no title.

    [teaser36]

     
    • Jim Randell's avatar

      Jim Randell 9:19 am on 20 June 2021 Permalink | Reply

      The phone numbers must be different, so the ages are more than 1 (but less than 10).

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to find candidate phone numbers and ages.

      The following run file executes in 78ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --distinct=""
      
      "div(ABCD, DCBA) > 1"
      
      --answer="(ABCD, DCBA, div(ABCD, DCBA))"
      

      Solution: The calls were made when June was 4 years old and 9 years old.

      The two sets of numbers are:

      8712 / 2178 = 4
      9801 / 1089 = 9

      Like

    • Hugh Casement's avatar

      Hugh Casement 2:33 pm on 20 June 2021 Permalink | Reply

      If they had been five-digit numbers we would have had
      87912 / 21978 = 4
      98901 / 10989 = 9
      It seems we can insert a string of as many 9s in the middle of each number as we like.

      Like

      • Hugh Casement's avatar

        Hugh Casement 3:08 pm on 20 June 2021 Permalink | Reply

        Over the years this has proved surprisingly popular, with slight variants.
        See Enigmas 792 and 1535, Teasers 847, 1663, and 1964.

        Like

    • GeoffR's avatar

      GeoffR 9:01 pm on 21 June 2021 Permalink | Reply

      
      from itertools import permutations
      
      ages = []  # list for June's ages
      
      for p1 in permutations(range(10),4):
        a, b, c, d = p1
        if 0 in (a, d): continue
        abcd = 1000*a + 100*b + 10*c + d
        dcba = 1000*d + 100*c + 10*b + a
        q, r = divmod(abcd, dcba)
        if r == 0:
          ages.append(q)
      
      # two phone calls were made
      if len(ages) == 2:
        print(f"June's ages were {ages[0]} and {ages[1]} years.")
      
      # June's ages were 4 and 9 years.
      
      

      Like

  • Unknown's avatar

    Jim Randell 2:08 pm on 18 June 2021 Permalink | Reply
    Tags:   

    Teaser 3065: Members of the jury 

    From The Sunday Times, 20th June 2021 [link] [link]

    A jury had twelve members, all with different ages (at least 20 but not more than 65), except that two were twins with a common age over 40. The average age was a prime number. A counsel objected to one of the members and he was replaced by another with the result that the average age was reduced to another prime number. Between the two juries, there were twelve different ages and they formed a progression with a common difference (e.g.: 1, 4, 7, 10, 13, etc.; or: 6, 13, 20, 27, 34, etc.). None of the individuals had a perfect square age, and the replacement jury still included both twins.

    How old were the twins?

    [teaser3065]

     
    • Jim Randell's avatar

      Jim Randell 2:38 pm on 18 June 2021 Permalink | Reply

      This Python program runs in 61ms. (Internal runtime is 765µs).

      from enigma import (irange, inf, sq, delete, div, is_prime, seq_items, printf)
      
      # disallowed square ages
      squares = set(sq(i) for i in irange(5, 8))
      
      # consider possible start ages
      for a in irange(20, 54):
        # and possible common differences
        for d in irange(1, inf):
          # the progression consisting of 12 ages
          ns = list(irange(a, a + 11 * d, step=d))
          if ns[-1] > 65: break
          # there are no squares
          if not squares.isdisjoint(ns): continue
      
          # one of the 12 ages is the replacement age (y)
          t = sum(ns)
          for (i, y) in enumerate(ns):
            # the ages of the original jury
            js = delete(ns, [i])
            # duplicate one of them
            for z in reversed(js):
              # duplicate age is over 40
              if z < 41: break
              # and gives a prime mean age
              m1 = div(t - y + z, 12)
              if m1 is None or not is_prime(m1): continue
      
              # replace one of the original ages x with y
              for (_, x) in seq_items(js, i):
                if x == z: continue
                # mean age of the new jury is a lower prime
                m2 = div(t - x + z, 12)
                if m2 is None or not is_prime(m2): continue
      
                # output solution
                printf("twins = {z} [{js} + {z}; avg={m1}; {x} -> {y}; avg={m2}]", js=tuple(js))
      

      Solution: The twins are 41.

      The sequence of ages for both juries are all those ages between 26 and 59 in steps of 3.

      The ages of the original jury are:

      26, 29, 32, 38, 41, 41, 44, 47, 50, 53, 56, 59; mean = 43

      The 59 year old is then replaced by a 35 year old:

      26, 29, 32, 35, 38, 41, 41, 44, 47, 50, 53, 56; mean = 41


      One way to test your program is to remove the constraint that there are no squares. Without this condition you should find 10 different answers for the age of the twins.

      Liked by 1 person

    • Tony Brooke-Taylor's avatar

      Tony Brooke-Taylor 12:05 pm on 19 June 2021 Permalink | Reply

      Similar routine but with a bit less trial and error, so marginally quicker

      
      primes=list(SieveOfEratosthenes(65))#function left out for ease of reading
      squares=[x**2 for x in range(4,9)]
      
      for delta in range(1,5): #possible common differences
        for a_min in range(20,66-11*delta): #consequential possible start ages
          ages=[a_min+delta*i for i in range(12)] #sequence of 12 ages
          if set(ages).intersection(squares):continue #no square ages
      
          twins=[t for t in ages if t>40]#subset of possible ages of the twins
          a_ave=a_min+11*delta/2#average of the 12 ages
          min_ave = a_ave+(40-max(ages))/12#lowest possible average age of a jury
          max_ave = a_ave+(max(ages)-min(ages))/12#highest possible average age of a jury
          poss_aves = [p for p in primes if p>=min_ave and p<max_ave]
          if len(poss_aves)<2:continue
          #possible prime average ages for a jury, must be (at least) 2 of them
      
          for twin in twins: #find twins' age by trial and error
            rejects=[]
            for poss_ave in poss_aves:
              swapped=twin+(a_ave-poss_ave)*12#age of swapped juror relative to twins
              if swapped>a_min+11*delta:break
              if swapped >=20:rejects.append(swapped)
              #reject impossible juror ages
      
            if len(rejects)>1: #solution contains at least two swapped juror ages
              print("The ages of the twins was",twin)
            #happily there is one solution and it has exactly two swapped jurors
          
      
      
      
      

      Liked by 1 person

      • Jim Randell's avatar

        Jim Randell 10:20 am on 20 June 2021 Permalink | Reply

        The [[ Primes ]] class in the enigma.py library implements a prime sieve, so you can use the following to avoid having to include the code in your program, but still posting runnable code.

        from enigma import primes
        primes.expand(66)
        

        And it might be worth placing longer comments on the line above the code, as horizontal space is limited in replies.

        Like

        • Jim Randell's avatar

          Jim Randell 10:57 am on 20 June 2021 Permalink | Reply

          @Tony: It’s an interesting approach.

          Here’s a version using a similar idea, but calculates the separation of the replacement/replaced pair in the progression from the difference between the prime means, and then looks for suitable candidates, from which the repeated age can be determined:

          from enigma import (primes, subsets, irange, inf, sq, div, divc, divf, printf)
          
          primes.expand(60)
          
          # disallowed square ages
          squares = set(sq(i) for i in irange(5, 8))
          
          # consider possible start ages
          for a in irange(20, 54):
            # and possible common differences
            for d in irange(1, inf):
              # the sequence of 12 ages
              ns = list(irange(a, a + 11 * d, step=d))
              if ns[-1] > 65: break
              # there are no squares
              if not squares.isdisjoint(ns): continue
              t = sum(ns)
          
              # consider possible mean ages (m2, m1)
              m_min = divc(t + 41 - ns[-1], 12)
              m_max = divf(t + ns[-1] - ns[0], 12)
              for (m2, m1) in subsets(primes.between(m_min, m_max), size=2):
                # calculate separation between replaced and replacement
                k = div(12 * (m1 - m2), d)
                if k is None: continue
          
                # consider possible replacement pairs (x = ns[j - k] replaces y = ns[j])
                for j in irange(11, k, step=-1):
                  # calculate repeated age (z)
                  z = 12 * m2 + ns[j] - t
                  if z < 41: break
                  if z in ns and z != ns[j] and z != ns[j - k]:
                    # output solution
                    printf("twins = {z} [{ns}; {x} replaces {y}; avg: {m1} -> {m2}]", ns=tuple(ns), x=ns[j - k], y=ns[j])
          

          (The internal runtime is reduced to 247µs).

          There is only one candidate pair of prime ages, and only one of the potential replacement/replaced pairings gives a duplicate age greater than 40.

          (And a bit more analysis fixes the values of the common difference, the mean ages, and the difference between the replaced and replacement ages, which allows us to bring the internal runtime down to just 48µs).

          Like

      • Brian Gladman's avatar

        Brian Gladman 10:27 am on 20 June 2021 Permalink | Reply

        A nice approach, Tony, which I ‘stole’ here. For those who want to run your code, here is a one line replacement for line one:

        primes = {x for x in range(23, 63, 2) if all(x % p for p in {2, 3, 5, 7})}
        

        Like

    • GeoffR's avatar

      GeoffR 1:27 pm on 20 June 2021 Permalink | Reply

      Hi Tony,

      I have re-formatted your code to PEP8, which can easily be done under the Wingware IDE for Python,
      using Source/Reformatting from the main menu – hope you don’t mind, but it does make code much
      easier to read and appreciate by others. Have also replaced end of line comments as separate comments, as suggested by Jim. An interesting solution.

      https://wingware.com/downloads

      #primes = list(SieveOfEratosthenes(65))
      # using Brian's replacement for line above
      primes = {x for x in range(23, 63, 2)
                if all(x % p for p in {2, 3, 5, 7})}
      squares = [x**2 for x in range(4, 9)]
      
      # possible common differences
      for delta in range(1, 5):
        # consequential possible start ages
        for a_min in range(20, 66 - 11 * delta):
          # sequence of 12 ages
          ages = [a_min + delta * i for i in range(12)]
          # no square ages
          if set(ages).intersection(squares):
            continue
      
          # subset of possible ages of the twins
          twins = [t for t in ages if t > 40]
      
          # average of the 12 ages
          a_ave = a_min + 11 * delta / 2
          # lowest possible average age of a jury
          min_ave = a_ave + (40 - max(ages)) / 12
          # highest possible average age of a jury
          max_ave = a_ave + (max(ages) - min(ages)) / 12
          poss_aves = [p for p in primes
                       if p >= min_ave and p < max_ave]
      
          # possible prime average ages for a jury,
          # ...must be (at least) 2 of them
          if len(poss_aves) < 2:
            continue
      
          # find twins' age by trial and error
          for twin in twins:
            rejects = []
            for poss_ave in poss_aves:
              # age of swapped juror relative to twins
              swapped = twin + (a_ave - poss_ave) * 12
              if swapped > a_min + 11 * delta:
                break
              # reject impossible juror ages
              if swapped >= 20:
                rejects.append(swapped)
      
            # solution contains at least two swapped juror ages
            if len(rejects) > 1:
              # happily there is one solution
              #.. and it has exactly two swapped jurors
              print("The ages of the twins was", twin)
      
      
      
      

      Like

    • Tony Brooke-Taylor's avatar

      Tony Brooke-Taylor 12:04 pm on 26 June 2021 Permalink | Reply

      Thanks all for your comments. I apologise for my continued struggles making my code readable in these replies. I’ll have a look at the Wingware IDE.

      Like

    • Hugh Casement's avatar

      Hugh Casement 11:34 am on 27 June 2021 Permalink | Reply

      With the same sequence the twins could have been aged 32, 35, or 38 (had those been allowed), still with the same two average ages. I found no other sequences that work, assuming no square ages .

      Like

    • Frits's avatar

      Frits 4:56 pm on 14 August 2025 Permalink | Reply

      Fast under CPython.

      The program has gotten a bit complex as I tried to also allow the program run for different numbers (m, M, N and minT).

      from math import ceil
      from enigma import primes
      
      # min/max, number of jury members, minimum twin age
      m, M, N, minT = 20, 65, 12, 41
      tri = (N * (N - 1)) // 2
      
      # progression of N ages: total sum(i=0..N-1) (y + i.k) = (N - 1).y + tri.k
      # age p was replaced by q with p > q
      # N.y + tri.k - q + t = N.p1
      # N.y + tri.k - p + t = N.p2   so p - q = N.(p1 - p2) is even
      
      sqs = {i * i for i in range(ceil(m**.5), int(M**.5) + 1)}
      primes = set(primes.irange(m, M))
      
      # possible differences between primes p1 and p2
      for d in range(2, (M - m) // N, 2):
        # valid primes for lower prime (p2) so that p1 is also prime
        primes2 = {p2 for p2 in primes if (p2 + d) in primes}
        # determine possible common differences (m + (N - 1).k <= M)
        ks = [k for k in range(1, (M - m) // (N - 1) + 1) if (N - 1) * k >= d * N]
        # determine (y, k) combinations that avoid a square age
        yks = [(y, k, N * y + tri * k) for k in ks
               for y in range(m, M - (N - 1) * k + 1) 
               if all((y + k * i) not in sqs for i in range(N))]
        for y, k, tot in yks:
          ages = range(y, y + N * k, k)
          # ages for the replaced jury member
          p_ages = [a for a in ages if a >= m + d * N]
          # reduce possible twin ages
          twin_ages = [t for t in ages if t >= minT for p in p_ages 
                       if t not in {p, p + d} and
                       not (dm := divmod(tot - p + t, N))[1] and dm[0] in primes2]
          
          if twin_ages: 
            print("answer:", ' or '.join(str(t) for t in twin_ages))
      

      Like

  • Unknown's avatar

    Jim Randell 9:22 am on 17 June 2021 Permalink | Reply
    Tags:   

    Teaser 2795: No mean feat 

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

    I bought a run of consecutively-numbered raffle tickets, some of which were red and the rest were blue. Amongst my numbers no red number was the average of two other reds, and no blue number was the average of two other blues. I actually won the raffle. My two-figure winning red number was in fact the geometric mean of two other red numbers of mine. [The geometric mean of M and N is the square root of (M×N)].

    What were my blue numbers?

    [teaser2795]

     
    • Jim Randell's avatar

      Jim Randell 9:23 am on 17 June 2021 Permalink | Reply

      The winning ticket w is the geometric mean of two of the other tickets, say a and b:

      w = √(a⋅b)
      w² = a⋅b

      So, the range of tickets must include the interval [a, …, b]. And if this interval can be successfully coloured (with a, w, b red) then we have a solution.

      This Python program runs in 530ms.

      Run: [ @replit ]

      from enigma import (irange, divisors_pairs, empty, peek, filter2, mod, subsets, diff, printf)
      
      # check no number in <ns> is the mean of two other numbers in <ns>
      def check(ns):
        for ps in filter2(mod(2), ns):
          if any((x + y) // 2 in ns for (x, y) in subsets(ps, 2)): return False
        return True
      
      # colour tickets in <ts> to give reds <rs> and blues <bs>
      def colour(ts, rs, bs):
        if not ts:
          yield (rs, bs)
        else:
          (t, ts_) = (ts[0], ts[1:])
          # try red
          rs_ = rs.union([t])
          if check(rs_): yield from colour(ts_, rs_, bs)
          # try blue
          bs_ = bs.union([t])
          if check(bs_): yield from colour(ts_, rs, bs_)
      
      # consider possible 2-digit winning tickets
      for w in irange(10, 99):
        # consider possible other ticket whose geometric mean is w
        for (a, b) in divisors_pairs(w, 2):
          if not (a < b): break
      
          # can we colour the tickets?
          rs = {a, w, b}
          if not check(rs): continue
          ts = diff(irange(a, b), rs)
          try:
            (reds, blues) = peek(colour(ts, rs, empty))
          except ValueError:
            continue
      
          printf("red = {reds}, blue = {blues} [w={w}; a={a} b={b}]", reds=sorted(reds), blues=sorted(blues))
      

      Solution: The blue tickets were numbered: 10, 11, 14, 15.

      And the red tickets were: 9, 12, 13, 16.

      i.e. the sequence is: (R9, B10, B11, R12, R13, B14, B15, R16).

      The winning ticket is 12, which is the geometric mean of 9 and 16.

      And we can check that the sequence cannot be extended (with 8 or 17):

      >>> check([8, 9, 12, 13, 16])
      False
      >>> check([8, 10, 11, 14, 15])
      False
      >>> check([9, 12, 13, 16, 17])
      False
      >>> check([10, 11, 14, 15, 17])
      False
      

      For a faster execution time we can note that if we have a sequence of numbers such that no number is the mean of any other two.

      a[0], a[1], …, a[n]

      Then the sequence:

      a[0] + k, a[1] + k, …, a[n] + k

      also has this property.

      So if we find a pattern of red/blue colourings of consecutive numbers, we can also form another valid pattern by adding a constant to each of the numbers (or by swapping the colours).

      The following program finds all possible patterns (of length 4 or more):

      from enigma import printf
      
      # generate colourings, with at least k elements
      def patterns(s, n, choice=(0, 1)):
        j = len(s)
        if not (j < n): yield s
        for x in choice:
          for (i, y) in enumerate(s):
            if y == x:
              (d, r) = divmod(i + j, 2)
              if r == 0 and s[d] == x: break
          else:
            yield from patterns(s + [x], n, choice)
      
      # output patterns
      for ps in patterns([0], 4):
        printf("{ps}")
      

      And we could modify the original program to check that the sequence of tickets matches one of these patterns, although we can cut the run time of the program down to 51ms by simply rejecting sequences that are longer than the maximum pattern length (which is 8).

      Replacing line 26 with the following does the trick:

      if not (0 < b - a < 8): continue
      

      There are 3 viable patterns of length 8, which we can identify with the following sequences:

      (0, 0, 1, 1, 0, 0, 1, 1)
      (0, 1, 0, 1, 1, 0, 1, 0)
      (0, 1, 1, 0, 0, 1, 1, 0)

      Giving a total of 6 possible red/blue colourings.

      The solution sequence (R9, B10, B11, R12, R13, B14, B15, R16) matches the last of these patterns.

      Like

      • Jim Randell's avatar

        Jim Randell 9:26 am on 17 June 2021 Permalink | Reply

        The following approach is based on an observation by Brian Gladman [link].

        Suppose the winning number (at index w in the sequence), is between the numbers at indices a and b that are the components of the geometric mean.

        Then the sequence is:

        n, …, n + a, …, n + w, …, n + b, …
        (n + a)(n + b) = (n + w)²
        n = (w² − ab) / (a + b − 2w)

        So, given the values of the indices within the sequence a, w, b we can calculate the starting value of the sequence.

        The following Python program generates possible patterns, and then looks for indices that produce a viable value for n. The values of the blue tickets can then be determined.

        It runs in 48ms (but the internal runtime is only 243µs compared to 1.26ms for my previous program (with modification)).

        from enigma import (irange, subsets, div, group, printf)
        
        # generate colourings, with at least k elements
        def patterns(s, n, choice=(0, 1)):
          j = len(s)
          if not (j < n): yield s
          for x in choice:
            for (i, y) in enumerate(s):
              if y == x:
                (d, r) = divmod(i + j, 2)
                if r == 0 and s[d] == x: break
            else:
              yield from patterns(s + [x], n, choice)
        
        # collect patterns by length
        pats = group(patterns([0], 4), by=len)
        m = max(pats.keys())
        
        # consider values for a, w, b
        for (a, w, b) in subsets(irange(0, m - 1), size=3):
          # calculate n
          n = div(w * w - a * b, a + b - 2 * w)
          if n is None or n + w < 10 or n + w > 99: continue
          # look for patterns with length at least b + 1
          for (k, vs) in pats.items():
            if not (k > b): continue
            for ps in vs:
              # look for patterns where a, w, b are red
              red = ps[a]
              if not (red == ps[w] == ps[b]): continue
              # output solution
              blues = sorted(n + j for (j, x) in enumerate(ps) if x != red)
              printf("blues = {blues} [ps={ps} n={n} red={red} w={w}]")
        

        It turns out there is only one possible value for (a, w, b) = (0, 3, 7), which gives n = 9.

        So the sequence is maximal, and of the three candidates only (0, 1, 1, 0, 0, 1, 1, 0) has elements 0, 3, 7 the same. They are all 0, so the 1’s indicate the blue tickets.

        Like

  • Unknown's avatar

    Jim Randell 7:16 am on 15 June 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 776: Christmas shopping 

    From The Sunday Times, 30th May 1976 [link]

    My three nieces Anne, Betty and Carole came to stay with me just before Christmas. When they arrived they handed over their “present” money, and as I wrote down the amounts (in pence) I noticed that they were 3-digit numbers  using all nine digits (zero excluded) and that Anne had more than Betty and Carole had as much as the other two together.

    I drove the girls into town to shop and as they entered the car to return home they again asked me to look after their money. As I jotted down the three  amounts I noticed that they were 3-digit numbers using all nine digits as before.

    Before I drove off they wanted to know how much each had spent. As I told them these amounts I was struck by the fact that they were 3-digit numbers again using all nine digits. I also added that Carole had spent exactly three-fifths of her money while Anne had spent a little more than three-fifths of hers.

    How much did the three girls have for the rest of their stay?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser776]

     
    • Jim Randell's avatar

      Jim Randell 7:19 am on 15 June 2021 Permalink | Reply

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

      The following run file executes in 85ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      --distinct="ABCDEFGHI,JKLMNPQRS,TUVWXYZab"
      
      # initial amounts: Anne = ABC; Betty = DEF; Carole = GHI
      "{A} > {D}"  # ABC > DEF
      "{GHI} - {ABC} = {DEF}"  # GHI = ABC + DEF
      
      # final amounts: Anne = JKL; Betty = MNP; Carole = QRS
      # amounts spent: Anne = TUV; Betty = WXY; Carole = Zab
      "{ABC} - {TUV} = {JKL}"
      "{DEF} - {WXY} = {MNP}"
      "{GHI} - {Zab} = {QRS}"
      
      # Carole had spent 3/5 of her money
      "div(3 * {GHI}, 5) = {Zab}"
      
      # Anne had spent a little more than 3/5 of hers (say: > 60%, < 65%)
      "0.60 < fdiv({TUV}, {ABC}) < 0.65"
      
      # the answer is the final amounts
      --answer="({JKL}, {MNP}, {QRS})"
      

      Solution: The remaining amounts are: Anne = 245p; Betty = 169p; Carole = 378p.

      The initial amounts are:

      Anne: 627p
      Betty: 318p
      Carole: 945p

      And the amounts spent are:

      Anne: 382p (60.9%)
      Betty: 149p (46.9%)
      Carole: 567p (60.0%)

      Like

    • GeoffR's avatar

      GeoffR 10:27 am on 15 June 2021 Permalink | Reply

      
      % A Solution in MiniZinc 
      include "globals.mzn";
      
      % using same notations as Jim's solution 
      % starting amounts - Anne, Betty and Carole
      var 1..9:A; var 1..9:B; var 1..9:C;  
      var 1..9:D; var 1..9:E; var 1..9:F;
      var 1..9:G; var 1..9:H; var 1..9:I;
      var 100..999:ABC = 100*A + 10*B + C; % Anne
      var 100..999:DEF = 100*D + 10*E + F; % Betty
      var 100..999:GHI = 100*G + 10*H + I; % Carole
      
      var set of int: start = {A,B,C,D,E,F,G,H,I};
      constraint start == {1,2,3,4,5,6,7,8,9};
      
      % Anne had more than Betty and 
      % Carole had as much as the other two together.
      constraint ABC > DEF /\ GHI == ABC + DEF;
      
      % final amounts - Anne, Betty and Carole
      var 1..9:J; var 1..9:K; var 1..9:L;
      var 1..9:M; var 1..9:N; var 1..9:P;
      var 1..9:Q; var 1..9:R; var 1..9:S;
      var 100..999:JKL = 100*J + 10*K + L; % Anne
      var 100..999:MNP = 100*M + 10*N + P; % Betty
      var 100..999:QRS = 100*Q + 10*R + S; % Carole
      
      var set of int: final = {J,K,L,M,N,P,Q,R,S};
      constraint final == {1,2,3,4,5,6,7,8,9};
      
      % amounts spent - Anne, Betty and Carole
      var 1..9:T; var 1..9:U; var 1..9:V;
      var 1..9:W; var 1..9:X; var 1..9:Y;
      var 1..9:Z; var 1..9:a; var 1..9:b;
      var 100..999:TUV = 100*T + 10*U + V; % Anne
      var 100..999:WXY = 100*W + 10*X + Y; % Betty
      var 100..999:Zab = 100*Z + 10*a + b; % Carole
      
      var set of int: spent ={T,U,V,W,X,Y,Z,a,b};
      constraint spent == {1,2,3,4,5,6,7,8,9};
      
      % for Anne, Betty and Carole, amount_left = starting_amount - amount_spent
      constraint ABC - TUV == JKL
      /\ DEF - WXY == MNP /\ GHI - Zab == QRS;
      
      % Carole had spent 3/5 of her money
      constraint 3 * GHI == 5 * Zab;
      
      % Anne had spent a little more than 3/5 of her money (say less than 4/5)
      constraint 5 * TUV > 3 * ABC /\ 5 * TUV < 4 * ABC;
      
      solve satisfy;
      
      output ["Starting amounts(p) for Anne, Betty & Carole: " 
      ++ show(ABC) ++ ", " ++ show (DEF) ++ ", " ++ show(GHI)
      ++ "\nFinal amounts(p) for Anne, Betty & Carole: " 
      ++ show(JKL) ++ ", " ++ show(MNP) ++ ", " ++ show(QRS)
      ++ "\nAmounts spent(p) for Anne, Betty & Carole: " 
      ++ show(TUV) ++ ", " ++ show(WXY) ++ ", " ++ show(Zab)];
      
      % Starting amounts(p) for Anne, Betty & Carole: 627, 318, 945
      % Final amounts(p) for Anne, Betty & Carole: 245, 169, 378
      % Amounts spent(p) for Anne, Betty & Carole: 382, 149, 567
      % time elapsed: 0.38 s
      % ----------
      % ==========
      
      
      

      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