Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 11:53 am on 3 December 2020 Permalink | Reply
    Tags: ,   

    Teaser 1956: Moving the goalpost 

    From The Sunday Times, 12th March 2000 [link]

    We have a large rectangular field with a wall around its perimeter and we wanted one corner of the field fenced off. We placed a post in the field and asked the workment to make a straight fence that touched the post and formed a triangle with parts of two sides of the perimeter wall. They were to do this in such a way that the area of the triangle was as small as possible. They worked out the length of fence required (less than 60 metres) and went off to make it.

    Meanwhile, some lads played football in the field and moved the post four metres further from one side of the field and two metres closer to another.

    Luckily when the men returned with the fence it was still the right length to satisfy all the earlier requirements. When they had finished erecting it, the triangle formed had each of its sides equal to a whole number of metres.

    How long was the fence?

    This puzzle is included in the book Brainteasers (2002). The puzzle text above is taken from the book.

    [teaser1956]

     
    • Jim Randell's avatar

      Jim Randell 11:54 am on 3 December 2020 Permalink | Reply

      I thought the wording in this puzzle was a bit confusing. Moving the post 4 metres further from one side of the field is necessarily moving it 4 meters closer to another side of the field. I think we are to suppose the sides of the field are those that are used in forming the perimeter of the triangle, but in my code I considered all 8 potential positions.

      If the post is at (a, b), then we can show that the minimal area triangle (made with the x– and y-axes) is achieved when the fence runs from (2a, 0) to (0, 2b). So the post is at the mid-point of the fence.

      The final triangle is a Pythagorean triple with hypotenuse z, less than 60, and, if the hypotenuse passes through the point (a′, b′), then the other sides are, 2a′ and 2b′.

      So we need to look for points (a, b), where a and a′ differ by 2 or 4, and b and b′ differ by 4 or 2, such that (2a)² + (2b)² = z².

      This Python program runs in 46ms.

      Run: [ @replit ]

      from enigma import (pythagorean_triples, cproduct, Rational, printf)
      
      # choose a rational implementation
      Q = Rational()
      
      # consider pythagorean triples for the final triangle
      for (x, y, z) in pythagorean_triples(59):
        # and the position of the post
        (a1, b1) = (Q(x, 2), Q(y, 2))
        # consider the original position of the post
        for ((dx, dy), mx, my) in cproduct([((2, 4), (4, 2)), (1, -1), (1, -1)]):
          (a, b) = (a1 + mx * dx, b1 + my * dy)
          if a > 0 and b > 0 and 4 * (a * a + b * b) == z * z:
            printf("({x}, {y}, {z}) -> (a1, b1) = ({a1}, {b1}), (a, b) = ({a}, {b})")
      

      Solution: The fence is 25m long.

      The triangles are a (7, 24, 25) and a (15, 20, 25) triangle.

      The program finds 2 solutions as we don’t know which is the starting position and which is the final position:

      However, if the post is moved 2m closer to one of the axes and 4m further from the other axis, then blue must be the starting position and red the final position.

      Like

    • John Crabtree's avatar

      John Crabtree 3:30 pm on 4 December 2020 Permalink | Reply

      As shown above, the post is at the midpoint of the fence.
      Let the initial sides be A and B, and the new sides be A + 8 and B – 4.
      One can show that B = 2A + 10, and if the hypotenuse = B + n then A = 2n + sqrt(5n^2 + 20n),
      n = 1 gives A = 7, ie (7, 24, 25) and (15, 20, 25)
      n = 5 gives A = 25, ie (25, 60, 65) and (33, 56, 65)
      This explains the limit on the length of the fence being less than 60 metres
      BTW, n = 16 gives (72, 154, 170) and (80, 150, 170)

      Like

  • Unknown's avatar

    Jim Randell 9:17 am on 1 December 2020 Permalink | Reply
    Tags:   

    Teaser 2769: Coming home 

    From The Sunday Times, 18th October 2015 [link] [link]

    George, Martha and their daughter all drive at their own steady speeds (whole numbers of mph), the daughter’s speed being 10mph more than Martha’s. One day George left home to drive to his daughter’s house at the same time as she left her house to visit her parents: they passed each other at the Crossed Keys pub. Another time Martha left her daughter’s to return home at the same time as her daughter started the reverse journey: they too passed at the Crossed Keys. The distance from George’s to the pub is a two-figure number of miles, and the two digits in reverse order give the distance of the pub from their daughter’s.

    How far is it from George’s home to the Crossed Keys?

    [teaser2769]

     
    • Jim Randell's avatar

      Jim Randell 9:17 am on 1 December 2020 Permalink | Reply

      This Python program considers candidate 2-digit distances from the parent’s house to the pub. It runs in 43ms.

      Run: [ @repl.it ]

      from enigma import irange, nreverse, div, printf
      
      # consider 2 digit distance from parent's house to X
      for p in irange(10, 99):
        # distance to the daughter's house to X is the reverse
        d = nreverse(p)
        if not(d < p): continue
      
        # in the second journey the mother's speed is...
        vm = div(10 * d, p - d)
        if vm is None: continue
        vd = vm + 10
      
        # in the first journey the father's speed is...
        vf = div(p * vd, d)
        if vf is None: continue
      
        printf("p={p} d={d}, vm={vm} vd={vd} vf={vf}")
      

      Solution: It is 54 miles from George’s house to the Crossed Keys.

      So it is 45 miles from the daughter’s house.

      The speeds are: mother = 50 mph, daughter = 60 mph, father = 72 mph.

      Like

  • Unknown's avatar

    Jim Randell 9:27 am on 29 November 2020 Permalink | Reply
    Tags:   

    Teaser 1958: Pent up 

    From The Sunday Times, 26th March 2000 [link]

    The schoolchildren run around in a walled regular pentagonal playground, with sides of 20 metres and with an orange spot painted at its centre. When the whistle blows each child has to run from wherever they are to touch each of the five walls, returning each time to their starting point, and finishing back at the same point.

    Brian is clever but lazy and notices that he can minimize the distance he has to run provided that his starting point is within a certain region. Therefore he has chalked the boundary of this region and he stays within in throughout playtime.

    (a) How many sides does Brian’s region have?
    (b) What is the shortest distance from the orange spot to Brian’s chalk line?

    This puzzle is included in the book Brainteasers (2002). The puzzle text above is taken from the book.

    [teaser1958]

     
    • Jim Randell's avatar

      Jim Randell 9:28 am on 29 November 2020 Permalink | Reply

      I wrote a program to plot points with a path distance that is the same as that of the central point, and you get a plot like this:

      This makes sense, as the shortest distance from a point to the line representing any particular side is a line through the point, perpendicular to the side. So for each side, if we sweep it towards the opposite vertex, we cover a “house shaped” region of the pentagon, that excludes two “wings” on each side. (The regions that overhang the base).

      Sweeping each of the five sides towards the opposite vertex, the intersection of these five “house shaped” regions is the shaded decagon:

      A line from the centre of one of the sides of this decagon, through the orange spot to the centre of the opposite side, is a minimal diameter of the decagon, and we see that this distance is the same as the length of one of the sides of the original pentagon. (It corresponds to the side swept towards the opposite vertex, in the position when it passes through the exact centre of the pentagon).

      So the minimum distance from the centre to the perimeter of the decagon is half this distance.

      Solution: (a) The region has 10 sides; (b) The shortest distance is 10 metres.


      If the playground had been a regular 4-gon (square) or 3-gon (equilateral triangle), then every interior point would correspond to a minimal path.

      For a 6-gon (hexagon) we get a smaller 6-gon inside as the minimal area, and for a 7-gon the area is the shape of a 14-gon.

      In general for a k-gon we get an area in the shape of a smaller k-gon (when k is even) or 2k-gon (when k is odd).

      The areas get smaller and smaller as k increases. In the limit we have a circular playground with a single point at the centre corresponding to the minimal path.

      Like

    • Hugh Casement's avatar

      Hugh Casement 1:23 pm on 29 November 2020 Permalink | Reply

      I think he chalks a line that runs (for example) from the north vertex to a point roughly two thirds of the way down the ESE wall, then to the midpoint of the S wall, to a point roughly a third of the way up the WSW wall, and back to the N vertex. He then stays on that line rather than within it. When the whistles blows he can run right round the line, clockwise or anticlockwise, touching two walls at the N vertex. His total distance is about 81.75 m.

      Like

      • Jim Randell's avatar

        Jim Randell 2:39 pm on 29 November 2020 Permalink | Reply

        @Hugh: Except that after touching each wall you have to return to your starting point before you can set off to touch the next wall.

        Like

        • Hugh Casement's avatar

          Hugh Casement 3:17 pm on 29 November 2020 Permalink | Reply

          Oh, I misread it, thinking he just has to return to the start point at the end.
          Sorry to introduce a red herring.
          Anyway in my version I was wrong that the intermediate points are about a third of the way along the walls from the south: they’re precisely two thirds of the way.

          Like

          • Hugh Casement's avatar

            Hugh Casement 4:50 pm on 29 November 2020 Permalink | Reply

            Oops! Got it wrong again. Precisely one third, six and 2/3 metres.
            I’m really not awake today. Feel free to delete all my posts on this subject.

            Like

    • Hugh Casement's avatar

      Hugh Casement 2:31 pm on 29 November 2020 Permalink | Reply

      Put t = tan(72°) = sqrt{5 + 2 sqrt(5)}.
      Then if the midpoint of the south wall has coordinates (0,0) and the north vertex is at (0, 10t),
      the lower right wall is y = t(x – 10) and the lower left wall is y = t(10 – x).

      Then a bit of calculus — or some trial & error by program — shows that the intermediate points that he touches are at roughly (±12.060113, 6.340375) and the length of his path is about 81.75134 metres. If he starts at a point off the line (on either side) his overall path is longer.

      Of course his chalk line could start at any vertex and go via the midpoint of the opposite side. He could draw five such overlapping quadrilaterals, and stick to any of them, swapping between them at will, so long as when the whistle blows he is not confused as to which line he is on.

      Like

  • Unknown's avatar

    Jim Randell 1:54 pm on 27 November 2020 Permalink | Reply
    Tags:   

    Teaser 3036: That old chestnut 

    From The Sunday Times, 29th November 2020 [link] [link]

    Clearing out an old drawer I found a wrinkled conker. It was my magnificent old 6709-er, a title earned by being the only survivor of a competition that I had had with friends. The competition had started with five conkers, veterans of many campaigns; each had begun at a different value between 1300 and 1400.

    We used the rule that if an m-er beat an n-er in an encounter (by destroying it, of course!) the m-er would become an m+n+1-er; in effect, at any time the value of a conker was the number of destroyed conkers in all confrontations in its “ancestry”.

    I recall that at the beginning of, and throughout, the competition, the value of every surviving conker was a prime number.

    What were the values of the five conkers at the start?

    [teaser3036]

     
    • Jim Randell's avatar

      Jim Randell 2:10 pm on 27 November 2020 Permalink | Reply

      There are 5 conkers (with values, say A, B, C, D, E), and there are 4 matches where one conker is destroyed in each match. The ultimate winner ending up with a value of (A + B + C + D + E + 4), and we know this value is 6079.

      This Python 3 program runs in 49ms.

      from enigma import Primes, subsets, printf
      
      # target for the champion
      T = 6709
      
      # for checking primes
      primes = Primes(T)
      
      # play values against each other until there is an ultimate winner
      def solve(vs, ss=[]):
        # are we done?
        if len(vs) == 1:
          yield ss
        # choose 2 conkers to play
        for ((i, m), (j, n)) in subsets(enumerate(vs), size=2):
          v = m + n + 1
          if v in primes:
            yield from solve(vs[:i] + vs[i + 1:j] + vs[j + 1:] + [v], ss + [(m, n)])
            
      
      # choose 5 initial primes values between 1300 and 1400
      for vs in subsets(primes.irange(1300, 1400), size=5):
        if sum(vs) + 4 != T: continue
        # check for a possible sequence of matches
        for ss in solve(list(vs)):
          # output solution
          printf("{vs} -> {ss}")
          break # we only need one possibility
      

      Solution: The values of the five starting conkers were: 1301, 1303, 1361, 1367, 1373.

      The conkers are:

      A = 1301
      B = 1303
      C = 1361
      D = 1367
      E = 1373

      And one possible sequence of matches is:

      A vs C → AC = 2663
      D vs E → DE = 2741
      AC vs B → ABC = 3967
      ABC vs DE → ABCDE = 6709

      An alternative sequence is:

      B vs E → BE = 2677
      C vs D → CD = 2729
      BE vs CD → BCDE = 5407
      A vs BCDE → ABCDE = 6709

      Note that by selecting pairs of conkers for battle by index (rather than value) we ensure that the program works even if more than one conker has the same value. It turns out that in the solution sequence all the conkers do have different values, so it is possible to get the correct answer with a less rigorous program.

      Like

      • Jim Randell's avatar

        Jim Randell 12:49 pm on 14 December 2020 Permalink | Reply

        Here’s a solution using [[ multiset() ]] from the enigma.py library, which is a bit neater than using indices (and also more efficient).

        This Python 3 program runs in 44ms.

        from enigma import Primes, subsets, multiset, ordered, printf
        
        # target for the champion
        T = 6709
        
        # for checking primes
        primes = Primes(T)
        
        # play values against each other until there is an ultimate winner
        def solve(vs, ss=[]):
          # are we done?
          if len(vs) == 1:
            yield ss
          # choose 2 conkers to play
          for (m, n) in subsets(vs, size=2, select="mC"):
            v = m + n + 1
            if v in primes:
              yield from solve(vs.difference((m, n)).add(v), ss + [ordered(m, n)])
        
        # choose 5 initial primes values between 1300 and 1400
        for vs in subsets(primes.irange(1300, 1400), size=5):
          if sum(vs) + 4 != T: continue
          # check for a possible sequence of matches
          for ss in solve(multiset(vs)):
            # output solution
            printf("{vs} -> {ss}")
            break # we only need one possibility
        

        Like

    • Frits's avatar

      Frits 5:58 pm on 28 November 2020 Permalink | Reply

      2 encounters are enough to filter out a unique solution (we have been told there is a solution).
      Coding more encounters would have resulted in an even more messy code.

      from enigma import SubstitutedExpression, is_prime
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [ 
        # 5 increasing prime numbers between 1300 and 1400
        "is_prime(1300 + AB)",
        "CD > AB",
        "is_prime(1300 + CD)",
        "EF > CD",
        "is_prime(1300 + EF)",
        "GH > EF",
        "is_prime(1300 + GH)",
        "IJ > GH",
        "is_prime(1300 + IJ)",
        
        # winner has value of (A + B + C + D + E + 4), has to be 6709.
        "AB + CD + EF + GH + IJ == 205",
       
       # 1st encounter 
        "is_prime(2601 + V*AB + W*CD + X*EF + Y*GH + Z*IJ)",
        
        # 1st encounter : pick 2
        "V + W + X + Y + Z == 2",
        
        # 2nd encounter 
        "is_prime(1301 + \
                  (1 - P)*1300 + P*(2601 + V*AB + W*CD + X*EF + Y*GH + Z*IJ) + \
                  Q*(1-V)*AB + R*(1-W)*CD + S*(1-X)*EF + T*(1-Y)*GH + U*(1-Z)*IJ)",
                  
        # 2nd encounter: pick exactly 2          
        "P + Q*(1-V) + R*(1-W) + S*(1-X) + T*(1-Y) + U*(1-Z) == 2",    
        
        # limit number of same solutions
        "Q * V == 0",        # force Q to be 0 if V equals 1
        "R * W == 0",        # force R to be 0 if W equals 1
        "S * X == 0",        # force S to be 0 if X equals 1
        "T * Y == 0",        # force T to be 0 if Y equals 1
        "U * Z == 0",        # force U to be 0 if Z equals 1
        ],
        answer="AB, CD, EF, GH, IJ, 2601 + V*AB + W*CD + X*EF + Y*GH + Z*IJ, \
                1301 + (1 - P)*1300 + P*(2601 + V*AB + W*CD + X*EF + Y*GH + Z*IJ) + \
                Q*(1-V)*AB + R*(1-W)*CD + S*(1-X)*EF + T*(1-Y)*GH + U*(1-Z)*IJ, \
                P,Q,R,S,T,U,V,W,X,Y,Z",
        d2i={k:"PQRSTUVWXYZ" for (k) in range(2,10)},
        distinct="",
        verbose=0)   
      
      # Print answers 
      prev = ""
      print("   I    II    III   IV     V    e1    e2   "
            "P, Q, R, S, T, U, V, W, X, Y, Z]")
      for (_, ans) in p.solve():
        if ans[:5] != prev:
          print([x if i > 4 else x + 1300 for (i, x) in enumerate(ans)])
        prev = ans[:5]
      

      Like

  • Unknown's avatar

    Jim Randell 9:10 am on 26 November 2020 Permalink | Reply
    Tags:   

    Teaser 2766: Two by two 

    From The Sunday Times, 27th September 2015 [link] [link]

    Animals board the ark in pairs.

    EWE and RAM
    HEN and COCK

    In fact these are numbers with letters consistently replacing digits; one pair of the numbers being odd, the other pair being even, and both pairs have the same sum. The three digits of the number ARK are consecutive digits in a muddled order. All this information uniquely determines the number NOAH.

    What is the number NOAH?

    [teaser2766]

     
    • Jim Randell's avatar

      Jim Randell 9:10 am on 26 November 2020 Permalink | Reply

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

      The following run file executes in 136ms.

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      # one pair is even, the other is odd
      "E % 2 == M % 2"
      "N % 2 == K % 2"
      "E % 2 != N % 2"
      
      # each pair has the same sum
      "EWE + RAM == HEN + COCK"
      
      # A, R, K are consecutive (in some order)
      "max(A, R, K) - min(A, R, K) == 2"
      
      # answer
      --answer="NOAH"
      

      Solution: NOAH = 2074.

      The pairs are:

      (EWE, RAM) = (595, 873)
      (HEN, COCK) = (452, 1016)

      Both pairs sum to 1468.

      And (A, R, K) rearranged gives the consecutive numbers (6, 7, 8).

      Like

    • Frits's avatar

      Frits 10:13 am on 26 November 2020 Permalink | Reply

      Trying not to copy all Jim’s equations.

      from itertools import permutations
      
      c = 1
      digits = set(range(10)).difference({1})
      
      for Q in permutations(digits):
        e,w,r,a,m,h,n,o,k = Q
        # letter combinations don't start with zero
        if 0 in [e, r, h, n, a]: continue
        # pairs EWE, RAM and HEN, COCK are even or odd
        if (e + m) % 2 == 1 : continue
        if (n + k) % 2 == 1 : continue
        # one pair of the numbers being odd, the other pair being even
        if (e + n) % 2 == 0: continue
        #  both pairs have the same sum
        s1 = (e + r) * 100 + (w + a) * 10 + e + m
        s2 = c * 1000 + (h + o) * 100 + (e + c) * 10 + n + k
        if s1 != s2: continue
        # a,r,k are three consecutive digits in a muddled order
        s3 = sum(abs(x - y) for (x, y) in zip([a, r, k, a], [r, k, a]))
        if s3 != 4: continue
        print(f"NOAH = {n}{o}{a}{h}")
      

      Like

    • GeoffR's avatar

      GeoffR 12:24 pm on 26 November 2020 Permalink | Reply

      
      % A Solution in MiniZinc 
      include "globals.mzn";
      
      var 0..9:E; var 0..9:W; var 0..9:R;
      var 0..9:A; var 0..9:M; var 0..9:C;
      var 0..9:O; var 0..9:K; var 0..9:N;
      var 0..9:H;
      
      constraint E > 0 /\ R > 0 /\ H > 0 /\ C > 0 /\ N > 0;
      constraint all_different ([E, W, R, A, M, C, O, K, N, H]);
      
      var 100..999:EWE = 100*E + 10*W + E;
      var 100..999:RAM = 100*R + 10*A + M;
      var 100..999:HEN = 100*H + 10*E + N;
      var 1000..9999:COCK = 1000*C + 100*O + 10*C + K;
      var 1000..9999:NOAH = 1000*N + 100*O +10*A + H;
      
      % One pair of the numbers being odd, the other pair being even
      constraint 
      ((EWE mod 2 == 0 /\ RAM mod 2 == 0) /\ (HEN mod 2 == 1 /\ COCK mod 2 == 1))
      \/ 
      ((EWE mod 2 == 1 /\ RAM mod 2 == 1) /\ (HEN mod 2 == 0 /\ COCK mod 2 == 0));
      
      % Both pairs of numbers have the same sum
      constraint EWE + RAM == HEN + COCK;
      
      % The three digits of the number ARK are consecutive digits in a muddled order
      % Re-using Jim's equation i.e. "max(A, R, K) - min(A, R, K) == 2"
      constraint max([A,R,K]) - min([A,R,K]) == 2;
      
      solve satisfy;
      
      output ["NOAH = " ++ show(NOAH) ++ "\n"
      ++ "EWE = " ++ show(EWE) ++ " , " ++ "RAM == " ++ show(RAM) ++ "\n"
      ++ "HEN = " ++ show(HEN) ++ " , " ++ "COCK = " ++ show(COCK) ];
      
      % NOAH = 2074
      % EWE = 595 , RAM == 873
      % HEN = 452 , COCK = 1016
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

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

    Teaser 2765: Prime points 

    From The Sunday Times, 20th September 2015 [link] [link]

    In my fantasy football league each team plays each other once, with three points for a win and one point for a draw. Last season Aberdeen won the league, Brechin finished second, Cowdenbeath third, and so on, in alphabetical order. Remarkably each team finished with a different prime number of points. Dunfermline lost to Forfar.

    In order, what were Elgin’s results against Aberdeen, Brechin, Cowdenbeath, and so on (in the form WWLDL…)?

    [teaser2765]

     
    • Jim Randell's avatar

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

      This Python program considers possible numbers of teams (up to a maximum of 26, as each team is named for a letter of the alphabet), and looks for possible sequences of primes that could correspond to the points. Once a candidate sequence is found we try to fill out a table of match outcomes (win, lose, draw) that gives the desired number of points for each team.

      It runs in 49ms.

      Run: [ @repl.it ]

      from enigma import irange, T, Primes, subsets, express, update, join, printf
      
      points = { 'w': 3, 'd': 1 } # points for win, draw
      swap = { 'w': 'l', 'l': 'w', 'd': 'd' } # how a match went for the other team
      
      # complete the table by filling out row i onwards
      def complete(k, table, ps, i=0):
        # are we done?
        if not(i < k):
          yield table
        else:
          # collect: (total, empty squares)
          (t, js) = (ps[i], list())
          for (j, x) in enumerate(table[i]):
            if x == ' ':
              js.append(j)
            else:
              t -= points.get(x, 0)
          # find ways to express t using 3 (= w) and 1 (= d)
          for (d, w) in express(t, (1, 3)):
            # and the remaining games are lost
            l = len(js) - d - w
            if l < 0: continue
            # look for ways to fill out the row
            for row in subsets('w' * w + 'd' * d + 'l' * l, size=len, select="mP"):
              # make an updated table
              t = update(table, [(i, update(table[i], js, row))])
              for (j, x) in zip(js, row):
                t[j] = update(t[j], [(i, swap[x])])
              # and solve for the remaining points
              yield from complete(k, t, ps, i + 1)
      
      # list of primes
      primes = Primes(75)
      
      # consider the number of teams in the league
      for k in irange(6, 26):
      
        # total number of matches
        n = T(k - 1)
      
        # maximum possible number of points
        m = 3 * (k - 1)
      
        # choose a set of k distinct primes for the points
        for ps in subsets(primes.irange(2, m), size=k):
          # calculate the total number of points scored
          t = sum(ps)
          # number of drawn and won/lost matches
          d = 3 * n - t
          w = n - d
          if d < 0 or w < 0 or d > n or w > n: continue 
      
          # make the initial table
          table = list()
          for i in irange(k):
            row = [' '] * k
            row[i] = '-'
            table.append(row)
          # given data (D (= 3) lost to F (= 5))
          for (r, c, v) in [(3, 5, 'l')]:
            table[r][c] = v
            table[c][r] = swap[v]
      
          # complete the table for the list of points
          for t in complete(k, table, ps[::-1]):
            # output the table
            printf("[{k} teams, {n} matches ({d} drawn, {w} won/lost)]")
            ts = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[:k]
            printf("   {ts}", ts=join(ts, sep=' '))
            for (n, r) in zip(ts, t):
              p = sum(points.get(x, 0) for x in r)
              printf("{n}: {r}  ->  {p:2d} pts", r=join(r, sep=' '))
            printf()
      

      Solution: Elgin’s results were: L L L L W D W.

      The complete table looks like this:

      [8 teams, 28 matches (7 drawn, 21 won/lost)]
         A B C D E F G H
      A: - d w w w w w w  ->  19 pts
      B: d - w d w w w w  ->  17 pts
      C: l l - d w w w w  ->  13 pts
      D: l d d - w l w w  ->  11 pts
      E: l l l l - w d w  ->   7 pts
      F: l l l w l - d d  ->   5 pts
      G: l l l l d d - d  ->   3 pts
      H: l l l l l d d -  ->   2 pts
      

      Like

    • Frits's avatar

      Frits 11:48 am on 25 November 2020 Permalink | Reply

      from itertools import product
      
      P = [2, 3, 5, 7]
      P += [x for x in range(11, 100, 2) if all(x % p for p in P)]
      P += [x for x in range(101, 104, 2) if all(x % p for p in P)]
      
      SCORES = [0, 1, 3]
      LDW = "LD-W"
      LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      
      # determine the outcome of 0.5 * n * (n - 1) games
      def solve(n, i, pzl):
        if i < 0:
          yield pzl
       
        # determine number of unknown values
        cnt = sum(pzl[i][j] == -1 for j in range(n))
        
        for p in product(SCORES, repeat=cnt):
          save = list(pzl[i])
      
          x = 0
          for j in range(n):
            if pzl[i][j] == -1:
              pzl[i][j] = p[x]
              pzl[j][i] = (1 if p[x] == 1 else abs(p[x] - 3))
              x += 1
      
          if sum(pzl[i]) == P[n - i - 1]:    # correct total
            yield from solve(n, i - 1, pzl)  # solve next team
          pzl[i] = save                      # backtrack
      
      # generate cumulative sum list of primes
      cumsum = [sum(P[:i+1]) for i in range(len(P))]
      nlist = []
      
      # primes for teams must be minimal (2,3,5 ..) as otherwise the highest prime
      # exceeds the maximum score (n * 3)
      #
      # Dunfermline lost to Forfar so Forfar must have at least 3 points
      # and this can't happen in a league with 6 teams (6th team has 2 points)
      # so n > 6
      
      print("teams  games  draws  points max    high     reject reason")  
      for n in range(7, 27):
        games = int(0.5 * n * (n - 1)) 
        reason = ""
        if (n-1) * 3 - 1 == P[n-1]:
          reason = "high = max - 1"
        if (n-1) * 3 < P[n-1]:
          reason = "high > max"  
        print(f"{n:<6} {games:<6} {games * 3 - cumsum[n-1]:<6} {cumsum[n-1]:<6} "
              f"{(n-1) * 3:<6} {P[n-1]:<6}   {reason}")
        if reason == "":
          nlist.append(n)
       
      # solve and print table 
      for n in nlist:
        # create matrix
        pzl = [[-1 for i in range(n)] for j in range(n)]
        pzl[3][5] = 0      # Dunfermline lost to Forfar
        pzl[5][3] = 3      # Forfor won from Dunfermline
        
        for i in range(n): 
          pzl[i][i] = 0    # don't calculate x against x
       
        # solve which games have been played
        sol = solve(n, n-1, pzl)
        
        # print table
        print(f"\nnumber of teams: {n} \n")
        for s in sol: 
          print("   " + "  ".join(list(LETTERS[:n])))   # column header
          for i in range(len(s)):
            r = " " + "  ".join(str(x) if k != i else "-" 
                                for k, x in enumerate(s[i]))
            print(LETTERS[:n][i], r)   # row qualifier
          
          print("\nElgin's results:", 
                " ".join([LDW[x] for k, x in enumerate(s[4]) if k != 4]))  
         
      # teams  games  draws  points max    high     reject reason
      # 7      21     5      58     18     17       high = max - 1
      # 8      28     7      77     21     19
      # 9      36     8      100    24     23       high = max - 1
      # 10     45     6      129    27     29       high > max
      # 11     55     5      160    30     31       high > max
      # 12     66     1      197    33     37       high > max
      # 13     78     -4     238    36     41       high > max
      # 14     91     -8     281    39     43       high > max
      # 15     105    -13    328    42     47       high > max
      # 16     120    -21    381    45     53       high > max
      # 17     136    -32    440    48     59       high > max
      # 18     153    -42    501    51     61       high > max
      # 19     171    -55    568    54     67       high > max
      # 20     190    -69    639    57     71       high > max
      # 21     210    -82    712    60     73       high > max
      # 22     231    -98    791    63     79       high > max
      # 23     253    -115   874    66     83       high > max
      # 24     276    -135   963    69     89       high > max
      # 25     300    -160   1060   72     97       high > max
      # 26     325    -186   1161   75     101      high > max
      #
      # number of teams: 8
      #
      #    A  B  C  D  E  F  G  H
      # A  -  1  3  3  3  3  3  3
      # B  1  -  3  1  3  3  3  3
      # C  0  0  -  1  3  3  3  3
      # D  0  1  1  -  3  0  3  3
      # E  0  0  0  0  -  3  1  3
      # F  0  0  0  3  0  -  1  1
      # G  0  0  0  0  1  1  -  1
      # H  0  0  0  0  0  1  1  -
      # 
      # Elgin's results: L L L L W D W
      

      Like

  • Unknown's avatar

    Jim Randell 9:08 am on 22 November 2020 Permalink | Reply
    Tags:   

    Teaser 1948: Square ladder 

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


    Your task is to place a non-zero digit in each box so that:

    • the number formed by reading across each row is a perfect square, with the one in the top row being odd;
    • if a digit is used in a row, then it is also used in the next row up;
    • only on one occasion does the same digit occur in two boxes with an edge in common.

    Fill in the grid.

    This puzzle is included in the book Brainteasers (2002). The puzzle text above is taken from the book.

    [teaser1948]

     
    • Jim Randell's avatar

      Jim Randell 9:08 am on 22 November 2020 Permalink | Reply

      This Python 3 program constructs “ladders” such that the digits used in each row are a superset of the digits used in the smaller row below.

      We then check the ladders produced to find ones that satisfy the remaining conditions.

      It runs in 47ms.

      from enigma import (irange, group, grid_adjacency, join, printf)
      
      # collect 1-5 digit squares
      squares = group((str(i * i) for i in irange(0, 316)), by=len, st=(lambda s: '0' not in s))
      
      # construct a ladder of squares
      def ladder(k, ss):
        n = len(ss)
        # are we done?
        if n == k:
          yield ss
        else:
          # add an (n+1)-digit square whose digits are a superset of the previous square
          ps = set(ss[-1])
          for s in squares[n + 1]:
            if ps.issubset(s):
              yield from ladder(k, ss + [s])
      
      # adjacency matrix for 5x5 grid
      adj = grid_adjacency(5, 5)
      
      # choose a starting 1 digit square
      for s in squares[1]:
        for ss in ladder(5, [s]):
          # the final square is odd
          if ss[-1][-1] not in '13579': continue
      
          # form the digits into a square array (linearly indexed)
          g = join(s.ljust(5) for s in ss)
      
          # count the number of edges between 2 identical digits
          n = sum(any(i < j and d == g[j] for j in adj[i]) for (i, d) in enumerate(g) if d != ' ')
          if n != 1: continue
      
          # output solution
          for s in ss[::-1]:
            printf("{s}", s=join(s, sep=" "))
          printf()
      

      Solution: The completed grid is:

      The squares are: (95481, 5184, 841, 81, 1) = (309, 72, 29, 9, 1)².

      Like

    • Frits's avatar

      Frits 3:49 pm on 22 November 2020 Permalink | Reply

      I didn’t use recursion as depth is limited.

      from itertools import permutations as perm
      from enigma import is_square
      
      to_num = lambda li: int("".join(li))
      
      # check if new value is correct
      def check(n, new, old):
        # as 5th row must be a square
        if not any(j in new for j in ('1', '4', '9')): 
          return False
        # check how many digits are same in same position 
        same[n] = sum(a == b for (a, b) in zip(tuple(old), new))
        if sum(same[:n+1]) > 1: 
          return False
        # new must be a square number
        if not is_square(to_num(new)): 
          return False   
        return True
       
      # generate 5 digit squares
      s5 = [i2 for i in range(101, 317) if '0' not in (i2 := str(i * i)) and 
                                           i2[-1] in ('1', '3', '5', '7', '9') and
                                           any(j in i2 for j in ('1', '4', '9'))] 
                 
      same = [0] * 4 
      for p5 in s5: 
        for p4 in perm(p5, 4):
          if not check(0, p4, p5): continue
          for p3 in perm(p4, 3):
            if not check(1, p3, p4): continue
            for p2 in perm(p3, 2):
              if not check(2, p2, p3): continue
              for p1 in perm(p2, 1):
                if not check(3, p1, p2): continue
                if sum(same) != 1: continue
                print(f"{p5}\n{to_num(p4)}\n{to_num(p3)}\n"
                      f"{to_num(p2)}\n{to_num(p1)}")
                
      # 95481
      # 5184
      # 841
      # 81
      # 1
      

      Like

      • Jim Randell's avatar

        Jim Randell 4:33 pm on 22 November 2020 Permalink | Reply

        I used a more literal interpretation of: “if a digit is used in a row, then it is also used in the next row up”.

        Which would allow (for example), 4761 to appear above 144.

        Like

        • Frits's avatar

          Frits 4:46 pm on 22 November 2020 Permalink | Reply

          You are right.

          from enigma import SubstitutedExpression, is_square
          
          # ABCDE
          # FGHI
          # JKL
          # MN
          # O
          
          # the alphametic puzzle
          p = SubstitutedExpression(
            [
            "is_square(ABCDE)",
            "is_square(FGHI)",
            "is_square(JKL)",
            "is_square(MN)",
            "is_square(O)",
            
            # connect 2 rows
            "all(j in str(ABCDE) for j in str(FGHI))",
            "all(j in str(FGHI) for j in str(JKL))",
            "all(j in str(JKL) for j in str(MN))",
            "str(O) in str(MN)",
            
            # check how many digits are equal in same position
            "sum([A==F, B==G, C==H, D==I, F==J, G==K, H==L, J==M, K==N, M==O]) == 1",
            ],
            answer="ABCDE, FGHI, JKL, MN, O",
            digits=range(1, 10),
            # top row is odd
            d2i=dict([(k, "E") for k in {2, 4, 6, 8}]),
            distinct="",
            verbose=0)   
          
          # Print answers 
          for (_, ans) in p.solve():
            print(f"{ans}")
          
          # (95481, 5184, 841, 81, 1)
          

          Like

    • GeoffR's avatar

      GeoffR 12:07 pm on 10 December 2020 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      %  Grid
      %  a b c d e
      %  f g h i
      %  j k l
      %  m n
      %  p
      
      var 0..9: a; var 0..9: b; var 0..9: c; var 0..9: d;
      var 0..9: e; var 0..9: f; var 0..9: g; var 0..9: h; 
      var 0..9: i; var 0..9: j; var 0..9: k; var 0..9: l; 
      var 0..9: m; var 0..9: n; var 0..9: p; 
      
      constraint a > 0 /\ f > 0 /\ j > 0 /\ m > 0 /\ p > 0;
      
      var 10..99:mn = 10*m + n;
      var 100..999:jkl = 100*j + 10*k + l;
      var 1000..9999:fghi = 1000*f + 100*g + 10*h + i;
      var 10000..99999: abcde = 10000*a + 1000*b + 100*c + 10*d + e;
      
      % sets of 2,3,4 and 5-digit squares
      set of int: sq2 = {n*n | n in 4..9};
      set of int: sq3 = {n*n | n in 10..31};  
      set of int: sq4 = {n*n | n in 32..99};  
      set of int: sq5 = {n*n | n in 100..316};
              
      % form five rows of required squares
      constraint p == 1 \/ p == 4 \/ p == 9;
      constraint mn in sq2 /\ jkl in sq3 /\ fghi in sq4;
      % square in the top row is odd
      constraint abcde in sq5 /\ abcde mod 2 == 1;
      
      % if a digit is used in a row, then it is also used
      % in the next row up - starting at bottom of ladder 
      constraint {p} subset {m, n};
      constraint {m, n} subset {j, k, l};
      constraint {j, k, l} subset {f, g, h, i};
      constraint {f, g, h, i} subset {a, b, c, d, e};
      
      % assume same digit occurs in two adjacent vertical boxes only
      constraint (sum ([a - f == 0, b - g == 0, c - h == 0, 
      d - i == 0, f - j == 0, g - k == 0, h - l == 0, 
      j - m == 0, k - n == 0, m - p == 0]) == 1)
      /\ % and check same digit does not occur in any two horizontal boxes
      (sum ([a - b == 0, b - c == 0, c - d == 0, d - e == 0,
      f - g == 0, g - h == 0, h - i == 0,
      j - k == 0, k - l == 0, m - n == 0]) == 0); 
      
      solve satisfy;
      
      output ["Grid: " ++ "\n" ++  show(abcde) ++ "\n" ++ show(fghi) ++ "\n" 
      ++ show(jkl) ++ "\n" ++ show(mn) ++ "\n" ++ show(p) ];
      % Grid: 
      % 95481
      % 5184
      % 841
      % 81
      % 1
      % time elapsed: 0.04 s
      % ----------
      % ==========
      % Finished in 477msec
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 4:25 pm on 20 November 2020 Permalink | Reply
    Tags:   

    Teaser 3035: Friend of the Devil 

    From The Sunday Times, 22nd November 2020 [link] [link]

    My friend, “Skeleton” Rose, rambled on with me and my uncle (“The Devil” and “Candyman”) about Mr Charlie, who gave, between us, three identical boxes of rainbow drops.

    Each identical box’s card template had a white, regular convex polygonal base section with under ten sides, from each of which a similar black triangular star point extended. All these dark star points folded up to an apex, making an enclosed box.

    The number of sweets per box equalled the single-figure sum of its own digits times the sum of the star points and the box’s faces and edges. If I told you how many of the “star point”, “face” and “edge” numbers were exactly divisible by the digit sum, you would know this number of sweets.

    How many sweets were there in total?

    [teaser3035]

     
    • Jim Randell's avatar

      Jim Randell 4:52 pm on 20 November 2020 Permalink | Reply

      If the base of the box is a k-gon (so 3 ≤ k < 10), then there are k “star points” and the closed box forms a polyhedron with (k + 1) faces and 2k edges.

      So if there are n sweets in the box, we have:

      n = dsum(n) × (k + (k + 1) + 2k)
      ⇒ k = ((n / dsum(n)) − 1) / 4

      The largest possible digit sum is 9, and the largest possible value for k is also 9, so the maximum value for n is 9×(4×9 + 1) = 333.

      This Python program considers sweet numbers (up to 333) and finds numbers unique by the given measure. It runs in 45ms.

      Run: [ @repl.it ]

      from enigma import irange, nsplit, div, unpack, filter_unique, printf
      
      # generate possible numbers of sweets
      def sweets(N=333):
        # consider numbers of sweets (n)
        for n in irange(1, N):
          # calculate the digit sum of n
          d = sum(nsplit(n))
          if d > 9: continue
          # calculate the number of sides of the polygon
          k = div(n - d, 4 * d)
          if k is None or k < 3 or k > 9: continue
          # measure = how many of (k, k + 1, 2k) are divisible by d
          m = sum(x % d == 0 for x in (k, k + 1, 2 * k))
          yield (n, m)
      
      # find values for n, unique by measure m
      r = filter_unique(sweets(), unpack(lambda n, m: m), unpack(lambda n, m: n))
      
      # output solution
      for (n, m) in r.unique:
        printf("n={n} [m={m}]")
      

      Solution: There were 666 sweets in total.

      There are 222 sweets in each of the three boxes.

      The digit sum of 222 is 6.

      The base of each box is a regular nonagon (9 sides), so the number of star points is 9, the number of faces of the shape formed by the (closed) box is 10, and the number of edges is 18.

      The digit sum (6), multiplied by the sum of the star points (9), faces (10), and edges (18) = 6×(9 + 10 + 18) = 6×37 = 222, the same as the number of sweets in the box.

      And this is the only case where just one the three summands (edges = 18) is divisible by the digit sum (6).

      There are 8 candidate numbers of sweets, grouped by the shape of the boxes:

      k=3: n=117
      k=4: n=153
      k=6: n=150, 225
      k=7: n=261
      k=9: n=111, 222, 333

      Like

      • Jim Randell's avatar

        Jim Randell 9:08 am on 21 November 2020 Permalink | Reply

        Here is a more efficient version, that also does not need the upper bound on the number of sweets.

        Run: [ @repl.it ]

        from enigma import irange, nsplit, unpack, filter_unique, printf
        
        # generate possible numbers of sweets
        def sweets():
          # consider number of sides of the polygon (k)
          for k in irange(3, 9):
            # consider possible digit sums (d)
            for d in irange(1, 9):
              # calculate number of sweets (n)
              n = d * (4 * k + 1)
              # check the digit sum of n
              if sum(nsplit(n)) != d: continue
              # measure = how many of (k, k + 1, 2k) are divisible by d
              m = sum(x % d == 0 for x in (k, k + 1, 2 * k))
              yield (n, m)
        
        # find values for n, unique by measure m
        r = filter_unique(sweets(), unpack(lambda n, m: m), unpack(lambda n, m: n))
        
        # output solution
        for (n, m) in r.unique:
          printf("n={n} [m={m}]")
        

        Like

  • Unknown's avatar

    Jim Randell 9:59 am on 19 November 2020 Permalink | Reply
    Tags:   

    Teaser 2764: Three little lists 

    From The Sunday Times, 13th September 2015 [link] [link]

    I have chosen five different numbers, each less than 20, and I have listed these numbers in three ways. In the first list the numbers are in increasing numerical order. In the second list the numbers are written in words and are in alphabetical order. In the third list they are again in words and as you work down the list each word uses more letters than its predecessor. Each number is in a different position in each of the lists.

    What are my five numbers?

    [teaser2764]

     
    • Jim Randell's avatar

      Jim Randell 9:59 am on 19 November 2020 Permalink | Reply

      This Python program runs in 68ms.

      Run: [ @repl.it ]

      from enigma import irange, subsets, int2words, join, printf
      
      # the numbers (in numerical order)
      nums = list(irange(1, 19))
      
      # map numbers to words
      word = dict((n, int2words(n)) for n in nums)
      
      # map number to word length
      length = dict((n, len(word[n])) for n in nums)
      
      # choose 5 numbers for the first list
      for s1 in subsets(nums, size=5):
      
        # make the third list by ordering the numbers by word length
        k3 = lambda n: length[n]
        # check all word lengths are distinct
        if len(set(k3(n) for n in s1)) < 5: continue
        s3 = sorted(s1, key=k3)
      
        # make the second list by ordering the numbers alphabetically
        k2 = lambda n: word[n]
        s2 = sorted(s1, key=k2)
      
        # no number is the same place in 2 or more lists
        if any(len(set(x)) < 3 for x in zip(s1, s2, s3)): continue
      
        # output solution (lists 2 and 3 formatted as words)
        fmt = lambda ns: join((word[n] for n in ns), sep=", ", enc="()")
        printf("1: {s1}")
        printf("2: {s2}", s2=fmt(s2))
        printf("3: {s3}", s3=fmt(s3))
        printf()
      

      Solution: The 5 numbers are: 3, 6, 9, 17, 19.

      Like

  • Unknown's avatar

    Jim Randell 10:27 am on 17 November 2020 Permalink | Reply
    Tags:   

    Brain-Teaser 13: [Fill out the grid] 

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

    The numbers 1 to 9, in any order and using each once only, are to be placed one at a time in the nine squares A to J. As each number replaces a letter in a square, any numbers standing at that moment in adjacent squares (left, right, up or down, but not diagonally) are to be multiplied by three.

    Thus, if we decided to begin with 4 in A, then 9 in E, 7 in B and 2 in D, etc., we should have:

    and so on. On completion, the nine final numbers are added together to find the score.

    There are obviously 81 ways of making the first move, and there are 131,681,894,400 ways of completing the array; yet the number of possible scores in quite small.

    What is the smallest possible score?

    This puzzle was originally published with no title.

    [teaser13]

     
    • Jim Randell's avatar

      Jim Randell 10:28 am on 17 November 2020 Permalink | Reply

      We can drastically reduce the number of possibilities we have consider:

      When the game is played the squares are filled out in a particular order. If, when we play the game, instead of filling in numbers we just note how many times the value in each particular square is trebled, then at the end we can find the minimum score for that particular ordering of squares by assigning 9 to the square that is trebled the fewest number of times (i.e. the final square filled out), 8 to the next lowest number of treblings, and so on until 1 is assigned to the square with the largest number of treblings.

      This Python program considers all possible orderings for filling out the squares. It runs in 420ms.

      Run: [ @repl.it ]

      from enigma import grid_adjacency, subsets, irange, printf
      
      # numbers to fill out
      nums = list(irange(1, 9))
      
      # grid adjacency matrix
      adj = grid_adjacency(3, 3)
      
      # multipliers (for number of treblings)
      mul = [1, 3, 9, 27, 81]
      
      # find the minimal score for a ordering of squares
      def play(js):
        # initial grid
        grid = [None] * 9
        # place the numbers
        for j in js:
          # place the number
          grid[j] = 0
          # and increase adjacent counts
          for i in adj[j]:
            if grid[i] is not None:
              grid[i] += 1
        # determine the minimum score
        return sum(n * mul[k] for (n, k) in zip(nums, sorted(grid, reverse=1)))
      
      # choose an ordering for the squares, and find the minimum score
      r = min(play(js) for js in subsets(irange(9), size=len, select="P"))
      printf("min score = {r}")
      

      Solution: The smallest possible score is 171.

      One way of getting this total is:

      [ A, B, C ] [ D, E, F ] [ G, H, J ]
      [ 2, B, C ] [ D, E, F ] [ G, H, J ]
      [ 6, 3, C ] [ D, E, F, ] [ G, H, J ]
      [ 6, 9, 4 ] [ D, E, F ] [ G, H, J ]
      [ 6, 27, 4 ] [ D, 1, F ] [ G, H, J ]
      [ 18, 27, 4, ] [ 5, 3, F ] [ G, H, J ]
      [ 18, 27, 12 ] [ 5, 9, 6 ] [ G, H, J ]
      [ 18, 27, 12 ] [ 15, 9, 6 ] [ 7, H, J ]
      [ 18, 27, 12 ] [ 15, 27, 6 ] [ 21, 8, J ]
      [ 18, 27, 12 ] [ 15, 27, 18 ] [ 21, 24, 9 ] = 171

      We can make the program run faster by exploiting the symmetry of the grid. For example, for the first move, we only need to consider one corner square, one edge square and the central square. It does make the program longer though.

      Like

    • Frits's avatar

      Frits 11:02 am on 19 November 2020 Permalink | Reply

      The first part finds the minimum score for different kind of treblings under the condition that they add up to 12. It turns out this minimum can also be achieved by our number placing rules.

      I didn’t immediately see how I could check if a list [A,B,C,D,E,F,G,H,J] from the result can be achieved so I reused Jim’s code to find the first permutation which has a score equal to this minimum.

      from enigma import SubstitutedExpression, grid_adjacency, subsets
      
      # numbers to fill out
      nums = list(range(1, 10))
      
      # grid adjacency matrix
      adj = grid_adjacency(3, 3)
      
      # multipliers (for number of treblings)
      mul = [1, 3, 9, 27, 81]
      
      score = lambda li: sum(n * mul[k] 
                         for (n, k) in zip(nums, sorted(li, reverse=1)))
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
        # if field x has adjacent fields x1...xn and k of them have already been set
        #
        # then k fields will now get trebled which previously didn't treble field x
        # and (n - k) fields will not get trebled now but later on will treble 
        # field x (n - k) times. So for every trebling there is an instance of 
        # non-trebling.
        #
        # adjacent field matrix;
        #
        #  2  3  2    sum of all elements is 24  
        #  3  4  3    half of them will not get trebled
        #  2  3  2
        "sum([A,B,C,D,E,F,G,H,J]) == 12", 
        ],
        answer="score([A,B,C,D,E,F,G,H,J]), A,B,C,D,E,F,G,H,J",
        digits=range(0, 5),
        env=dict(score=score),
        accumulate=min,  
        d2i={3 : "ACGJ", 4: "ACGJBDFH"},
        distinct="",
        verbose=0)   
        
      # solve the puzzle
      r = p.run()
      min = list(r.accumulate)[0]
      
      lst = []
      prt = ["A", "B", "C", "D", "E", "F", "G", "H", "J"]
      
      # check if this minimum can be achieved
      for js in subsets(range(9), size=len, select="P"):
        grid = [None] * 9
        # place the numbers
        for j in js:
          # place the number
          grid[j] = 0
          # and increase adjacent counts
          for i in adj[j]:
            if grid[i] is not None:
              grid[i] += 1
              
        if score(grid) == min:
          print("smallest possible score:", min, "\n\nexample:\n")
         
          # sort grid (descending)
          lst = sorted(grid, reverse=True)
         
          # print all the steps
          for i in range(9):  
            for k, j in enumerate(js):
              if j != i: continue
              g = grid[k]
              ind = lst.index(g) 
              lst[ind] = -1          # set to "processed"
              prt[k] = ind + 1       # set field
              
              # treble adjacent fields
              for m in adj[k]:
                if type(prt[m]) == int:
                  prt[m] *= 3
                
              print(f"{prt[:3]} {prt[3:6]} {prt[6:]}")
          break  # we only print one example
          
      # smallest possible score: 171
      #
      # example:
      #
      # [2, 'B', 'C'] ['D', 'E', 'F'] ['G', 'H', 'J']
      # [6, 3, 'C'] ['D', 'E', 'F'] ['G', 'H', 'J']
      # [6, 9, 4] ['D', 'E', 'F'] ['G', 'H', 'J']
      # [6, 27, 4] ['D', 1, 'F'] ['G', 'H', 'J']
      # [18, 27, 4] [5, 3, 'F'] ['G', 'H', 'J']
      # [18, 27, 12] [5, 9, 6] ['G', 'H', 'J']
      # [18, 27, 12] [15, 9, 6] [7, 'H', 'J']
      # [18, 27, 12] [15, 27, 6] [21, 8, 'J']
      # [18, 27, 12] [15, 27, 18] [21, 24, 9]    
      

      Like

  • Unknown's avatar

    Jim Randell 9:26 am on 15 November 2020 Permalink | Reply
    Tags:   

    Teaser 1946: Not the millennium bug 

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

    Do you remember all that fuss over the “Millennium bug”?

    On that New Year’s Day I typed a Teaser on my word processor. When I typed in 2000 it actually displayed and printed 1900. This is because whenever I type a whole number in figures the machine actually displays and prints only a percentage of it, choosing a random different whole number percentage each time.

    The first example was bad enough but the worrying this is that is has chosen even lower percentages since then, upsetting everything that I prepare with numbers in it. Luckily the percentage reductions have not cut any number by half or more yet.

    What percentage did the machine print on New Year’s Day?

    This puzzle is included in the book Brainteasers (2002). The puzzle text above is taken from the book.

    [teaser1946]

     
    • Jim Randell's avatar

      Jim Randell 9:26 am on 15 November 2020 Permalink | Reply

      See also: Enigma 1419.

      We assume that both the original puzzle (prepared on 2000-01-01), and the puzzle text presented above were created using the faulty program.

      So, on 2000-01-01 the setter typed a whole number, X, which was replaced by the value aX, where a is some whole number percentage less than 100% and greater than 50%.

      In relating the story for this puzzle, the setter has typed the values X and aX, and these have been replaced by values multiplied by b and c respectively, where b and c are different whole number percentages, less than a and greater than 50%.

      So, we have:

      bX = 2000
      c.aX = 1900

      This Python program runs in 44ms.

      Run: [ @replit ]

      from enigma import (irange, div, printf)
      
      # choose a percentage for b
      for b in irange(51, 98):
        X = div(2000 * 100, b)
        if X is None: continue
      
        # a > b
        for a in irange(b + 1, 99):
          aX = div(a * X, 100)
          if aX is None: continue
          c = div(1900 * 100, aX)
          if c is None or c == b or not (a > c > 50): continue
      
          # output solution
          printf("a={a} b={b} c={c} -> X={X} aX={aX}")
      

      Solution: On 2000-01-01 the machine used a value of 80%.

      So the setter was attempting to enter 3125, but instead the program printed 80% of this = 2500.

      In relating the story, the setter typed: “When I typed in 3125 it actually printed 2500”, but these values were replaced using percentages of 64% and 76%, to appear as: “When I typed in 2000 it actually printed 1900”, as in the puzzle text.

      Like

  • Unknown's avatar

    Jim Randell 5:07 pm on 13 November 2020 Permalink | Reply
    Tags:   

    Teaser 3034: Reservoir development 

    From The Sunday Times, 15th November 2020 [link] [link]

    A straight track from an observation post, O, touches a circular reservoir at a boat yard, Y, and a straight road from O meets the reservoir at the nearest point, A, with OA then extended by a bridge across the reservoir’s diameter to a disembarking point, B. Distances OY, OA and AB are whole numbers of metres, with the latter two distances being square numbers.

    Following development, a larger circular reservoir is constructed on the other side of the track, again touching OY at Y, with the corresponding new road and bridge having all the same properties as before. For both reservoirs, the roads are shorter than 500m, and shorter than their associated bridges. The larger bridge is 3969m long.

    What is the length of the smaller bridge?

    [teaser3034]

     
    • Jim Randell's avatar

      Jim Randell 5:44 pm on 13 November 2020 Permalink | Reply

      We can solve this puzzle using applications of Pythagoras’ Theorem.

      This Python program runs in 47ms.

      from enigma import (powers, div, is_square, printf)
      
      # difference of 2 squares
      diff_sq = lambda x, y: (x + y) * (x - y)
      
      # length of the larger bridge (= B)
      B = 3969
      
      # consider the length of the road to the larger reservoir (= R)
      for R in powers(1, 22, 2):
        # calculate distance OY (= t)
        t2 = is_square(diff_sq(B + 2 * R, B))
        if t2 is None: continue
        t = div(t2, 2)
        if t is None: continue
      
        # consider length of the road to the smaller reservior (= r)
        for r in powers(1, 22, 2):
          # calculate the length of the bridge over the smaller reservoir (= b)
          b = div(diff_sq(t, r), r)
          if b is None or not (r < b < B and is_square(b)): continue
      
          # output solution
          printf("B={B} R={R}, t={t}, r={r} b={b}")
      

      Solution: The smaller bridge is 2304 m long.

      The layout looks like this:

      And the puzzle can be solved by considering the right-angled triangles OYX’ and OYX.

      The calculated distances are:

      OY = 1040 m
      OA = 400 m (20²)
      AB = 2304 m (48²)
      OA’ = 256 m (16²)
      A’B’ = 3969 m (63²)

      Like

      • Jim Randell's avatar

        Jim Randell 2:29 pm on 14 November 2020 Permalink | Reply

        Or, with a bit more analysis:

        from enigma import (irange, inf, is_square, div, printf)
        
        # length of the larger bridge (= B)
        B = 3969 # = 63^2
        
        # consider increasing squares (larger than B)
        for z in irange(64, inf):
          # calculate length of the road to the larger reservoir (= R)
          R = z * z - B
          if not (R < 500): break
          # calculate the length of the track OY (= t)
          t = is_square(R)
          if t is None: continue
          t *= z
        
          # consider length of the road to the smaller reservoir (= r)
          for j in irange(1, 22):
            r = j * j 
            # calculate the length of the bridge over the smaller reservoir (= b)
            b = div((t + r) * (t - r), r)
            if b is None or not (r < b < B and is_square(b)): continue
        
            # output solution
            printf("B={B} R={R}, t={t}; r={r} b={b}")
        

        Like

    • Tony Brooke-Taylor's avatar

      Tony Brooke-Taylor 11:34 am on 17 November 2020 Permalink | Reply

      I first did this assuming that (using your notation) R+B also had to be a square. If I interpret your code correctly, I think that is also what you have done. I got the same value for R as your code produces. Following this through you get a unique solution for r. However, on reflection I don’t think the puzzle applies that constraint. In the puzzle’s notation, that would require OB and its equivalent to be squares, which I cannot see in the puzzle. If I relax the constraint I get three solutions. I am guessing that either the puzzle requires the addition of that constraint or I cannot read, but the more interesting question for me is whether or not it is more than a coincidence that the unique solution has that additional property.

      Like

      • Jim Randell's avatar

        Jim Randell 11:56 am on 17 November 2020 Permalink | Reply

        @Tony: Thanks for your comment.

        We don’t need to assume that (R + B) is a perfect square (and the puzzle doesn’t tell us that), but it follows from considering the right-angled triangle OYX’ (where X’ is the centre point of the larger reservoir).

        We have (where integer t is the length of the track OY):

        t² + (B/2)² = (R + B/2)²
        ⇒ t² = R(R + B)

        Now, we are told that R is a square number, and obviously t² is, so it follows that (R + B) must also be a perfect square.

        Like

    • Tony Brooke-Taylor's avatar

      Tony Brooke-Taylor 6:31 pm on 17 November 2020 Permalink | Reply

      Thanks Jim. The constraint I had failed to apply was that t must be an integer.

      Like

    • Frits's avatar

      Frits 9:20 pm on 17 November 2020 Permalink | Reply

      from enigma import SubstitutedExpression
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
         # PQRS = track OY, AE = sqrt(road OA), BX = sqrt(AB), FGH = road OC 
         # X is center of smaller reservoir
         
         # roads are shorter than 500m
         "AE > 0 and AE < 23",
         
         # roads are shorter than their associated bridges 
         "AE < BX",
         
         # one diameter is larger
         "BX > 0 and BX**2 < 3969",
       
         "PQRS > 0",
         
         # Pythagoras: OY^2 + YX^2 = (OA + YX)^2
         # so OY^2 = OA^2 + OA*AB
         "PQRS**2 == AE**4 + AE**2 * BX**2",
         
         "FGH < 500",
         # one road is longer
         "FGH < AE**2",
            
         # Pythagoras: OY^2 + (3969/2)^2 = ((3969/2) + OC)^2
         "PQRS**2 + 3938240.25 == (1984.5 + FGH)**2", 
        ],
        answer="BX**2, AE**2, PQRS, FGH",
        d2i={},
        distinct="",
        reorder=0,
        verbose=256)   
      
      # Print answers 
      print("Large bridge  small bridge  long road  track  short road")
      for (_, ans) in p.solve():
        print(f"    3969          {ans[0]}         {ans[1]}       {ans[2]}"
              f"     {ans[3]}")
      

      Like

  • Unknown's avatar

    Jim Randell 9:17 am on 12 November 2020 Permalink | Reply
    Tags:   

    Teaser 2763: Golf challenge 

    From The Sunday Times, 6th September 2015 [link] [link]

    Mark and John played 18 holes of golf: the holes consisting of six each of par 3, par 4 and par 5. Each player finished the round in 72, consisting of six 3s, six 4s and six 5s. In fact each of them had six birdies (one under par), six on par, and six bogies (one over par). At no hole did the two players take the same number of strokes, and Mark beat John on ten of the holes.

    How many of Mark’s winning holes were:

    (a) on par 3 holes?
    (b) on par 4 holes?
    (c) on par 5 holes?

    [teaser2763]

     
    • Jim Randell's avatar

      Jim Randell 9:17 am on 12 November 2020 Permalink | Reply

      The players scored one of (3, 4, 5) on each hole, and each score was one of (par − 1, par, par + 1).

      So on the par 3 holes their scores were 3 or 4. On the par 5 holes their scores were 4 or 5. (And the scores for the par 4 holes could be 3, 4, 5).

      So, a par 3 hole is won 3-4, and a par 5 hole is won 4-5. (And the par 4 holes are won: 3-4, 3-5, or 4-5).

      This Python program considers possible numbers of wins for Mark on the par 3 and par 5 holes, and then checks whether it is possible for the remaining scores to let Mark win the required number of par 4 holes.

      It runs in 44ms.

      Run: [ @repl.it ]

      from enigma import irange, printf
      
      # can mark win k of n par 4 games?
      def win4(k, n, ms, js):
        # are we done?
        if n == 0: return (k == 0)
        # mark wins with a score of 3, and john loses with a score of 5
        if ms.count(3) > k or js.count(5) > k: return False
        # mark loses with a score of 5, and john wins with a score of 3
        if ms.count(5) > n - k or js.count(3) > n - k: return False
        # otherwise choose scores for a hole and check the rest
        m = ms[0]
        for j in set(js).difference([m]):
          i = js.index(j)
          if m < j and win4(k - 1, n - 1, ms[1:], js[:i] + js[i + 1:]): return True
          if m > j and win4(k, n - 1, ms[1:], js[:i] + js[i + 1:]): return True
        # no solution found
        return False
      
      # choose number of wins for M on the par 3 holes
      for w3 in irange(0, 6):
        # and the number of wins for M on the par 5 holes
        for w5 in irange(0, min(6, 10 - w3)):
          # the remaining wins are on the par 4 holes
          w4 = 10 - w3 - w5
          if w4 > 6: continue
      
          # count number of 3, 4, 5 scores for mark
          m3 = w3
          m4 = (6 - w3) + w5
          m5 = (6 - w5)
          if m3 > 6 or m4 > 6 or m5 > 6: continue
      
          # and number of 3, 4, 5 scores for john
          j3 = (6 - w3)
          j4 = w3 + (6 - w5)
          j5 = w5
          if j3 > 6 or j4 > 6 or j5 > 6: continue
      
          # can mark win the required number of par 4 games?
          scores = lambda n3, n4, n5: [3] * n3 + [4] * n4 + [5] * n5
          if not win4(w4, 6, scores(6 - m3, 6 - m4, 6 - m5), scores(6 - j3, 6 - j4, 6 - j5)): continue
      
          # output solution
          printf("mark wins = ({w3}, {w4}, {w5}) @ par (3, 4, 5)")
      

      Solution: Mark’s wins for par 3, 4, 5 were (respectively): 4, 2, 4.

      So Mark won 4 par 3 holes 3-4 (and lost 2 par 3 holes 4-3), and won 2 par 4 holes 3-5 (and lost 4 par 4 holes 3-5), and won 4 par 5 holes 4-5 (and lost 2 par 5 holes 5-4).

      Like

      • Frits's avatar

        Frits 12:53 pm on 13 November 2020 Permalink | Reply

        Hi Jim,

        It is not easy for me to understand what is going on purely based on the code.
        Assuming M means Mark it seems more plausible to me that j3,j4,j5 belong to Mark iso John.
        The formulae of j3,j4,j5 and m3,m4,m5 are not yet clear to me.

        Also lines 7 and 9 seem to contain false statements (you can never win with a score of 5 unless you mean with score the number of shots your opponent has played)

        I’ll do some analysis of the data structures you have used to see why you have coded it like this.

        Like

        • Jim Randell's avatar

          Jim Randell 2:31 pm on 13 November 2020 Permalink | Reply

          @Frits: Yes, I’ve got it round backwards. Lowest score wins in golf. But the code will still solve the puzzle, if you think of awarding 5 points for 3 strokes, 4 points for 4 strokes and 3 points for 5 strokes on any hole. Then the highest number of points wins. But I should probably redo the code in terms of strokes.

          Like

        • Jim Randell's avatar

          Jim Randell 2:51 pm on 13 November 2020 Permalink | Reply

          @Frits: OK. I’ve updated my solution to use the actual number of strokes played on each hole. Lowest number of strokes wins the hole. So it should make more sense now.

          Like

      • Frits's avatar

        Frits 7:19 pm on 16 November 2020 Permalink | Reply

        I didn’t realize that j3,j4,j5 and m3,m4,m5 only have to do with the par 3 and 5 holes (they add up to 12).

        I made a program with SubstitutedExpression which checks every combination (approx 191 million). The program ran for 2.5 hours. A more efficient program ran for 25 seconds finding 3375 combinations (all 4-2-4).

        ——- Manual Solution ——————–
        A 3 score can only happen on par 3 and par 4 holes.
        Mark and John have six 3 scores each.
        So on the par 3 and 4 holes Mark wins half of the number of holes.

        so (a) + (b) = 6; (c) = 4 ( (a) + (b) + (c) = 10 )

        A 5 score can only happen on par 4 and par 5 holes.
        Mark and John have six 5 scores each.
        So on the par 4 and 5 holes John loses half of the number of holes and Mark wins half of the number of holes.

        so (b) + (c) = 6; (b) = 2 and (a) = 4

        Like

  • Unknown's avatar

    Jim Randell 11:19 am on 10 November 2020 Permalink | Reply
    Tags:   

    Teaser 1942: Eurosceptics 

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

    Ruritania is reluctant to adopt the euro as it has a sensible currency of its own. The mint issues the coins in four denominations, the value of each being proportional to its radius. The total value of the four, in euros, is 28.

    The four coins are available in a clever presentation pack. It consists of a triangular box of sides 13 cm, 14 cm and 15 cm. The largest coin just fits into the box, touching each of its sides, roughly as shown:

    Then there are three straight pieces of thin card inside the box. Each touches the large coin and is parallel to a side of the box. This creates three smaller triangles in the corners of the box. The three remaining coins just fit into the box, with one in each of these small triangles. Each coin touches all three sides of the triangle.

    Unfortunately I have lost the smallest coin from my presentation pack.

    What, in euros, is its value?

    This puzzle is included in the book Brainteasers (2002). The puzzle text above is taken from the book.

    [teaser1942]

     
    • Jim Randell's avatar

      Jim Randell 11:20 am on 10 November 2020 Permalink | Reply

      See: [ @Wikipedia ] for more details on incircles and excircles.

      Suppose a, b, c, d are the radii of the four coins (from largest to smallest).

      The inradius of a triangle can be calculated as the area of the triangle divided by the semi-perimeter.

      So for the large triangle we have:

      a = A / S

      where:

      S = semi-perimeter = (13 + 14 + 15) / 2 = 21
      A = area = sqrt(21.8.7.6) = 84

      So:

      a = 84/21 = 4

      Then considering the triangle containing the next smallest coin (radius = b), this is a version of the large triangle scaled down by a factor of (say) q.

      So it has a semi-perimeter of 21q, and an area of 84q² so:

      b = 84q² / 21q = 4q
      q = b / 4

      But the largest coin is an excircle of this smaller triangle and so its radius is given by:

      a = b.21q / (21q − 13q)
      a = 21b / (21 − 13) = b(21/8)
      b = (8/21)a = 32/21

      Similarly, for coins in the other corners:

      c = (7/21)a = 4/3
      d = (6/21)a = 8/7

      Now, if the radii are multiplied by factor f to get the value in euros we have:

      (4 + 32/21 + 4/3 + 8/7)f = 28
      8f = 28
      f = 7/2

      So the coins are worth:

      f.a = 14 euros
      f.b = 5 + 1/3 euros
      f.c = 4 + 2/3 euros
      f.d = 4 euros

      Which do indeed give a total of 28 euros.

      So, we have worked out the radii and value of each of the four coins.

      Solution: The smallest coin is worth 4 euros.

      The four coins are:

      radius = 40.0 mm; value = 14.00 euro
      radius = 15.2 mm; value = 5.33 euro
      radius = 13.3 mm; value = 4.67 euro
      radius = 11.4 mm; value = 4.00 euro

      Here’s a program that performs the same calculations:

      Run: [ @repl.it ]

      from enigma import fdiv, sqrt, multiply, printf
      
      # total value of the coins
      T = 28
      
      # the sides of the large triangle
      sides = (13, 14, 15)
      
      # calculate the radius of the largest coin
      S = fdiv(sum(sides), 2)
      A = sqrt(S * multiply(S - x for x in sides))
      a = fdiv(A, S)
      
      # and the three coins in the corners
      (b, c, d) = (fdiv(a * (S - x), S) for x in sides)
      
      # multiplier f: radius -> value
      f = fdiv(T, a + b + c + d)
      
      # output the values of the coins
      printf("[S={S} A={A} f={f}]")
      for (r, n) in zip((a, b, c, d), "abcd"):
        printf("{n}: {r:.1f} mm -> {v:.2f} euro", r= r * 10, v=r * f)
      

      Like

  • Unknown's avatar

    Jim Randell 10:27 am on 8 November 2020 Permalink | Reply
    Tags:   

    Teaser 1935: Turner painting 

    From The Sunday Times, 17th October 1999 [link]

    “Yet more storms” is a gigantic painting in the State Gallery. It is currently on the wall of the 27-foot-wide “Modern masters” corridor, but the curator feels that it would look better on the 64-foot-wide “Britain’s impressionists” corridor, which meets the “Modern masters” one at right angles.

    So he instructs his staff to slide the painting around the corner without tilting it. His staff manage to turn the painting as requested, but had it been any wider it would not have fitted around the corner.

    How wide is the painting?

    This puzzle is included in the book Brainteasers (2002). The puzzle text above is taken from the book.

    [teaser1935]

     
    • Jim Randell's avatar

      Jim Randell 10:27 am on 8 November 2020 Permalink | Reply

      We examined the general case of this problem in Enigma 34.

      And maximum width of the painting is given by:

      z = (a^(2/3) + b^(2/3))^(3/2)

      z = \left( a^{2/3} + b^{2/3} \right)^{3/2}

      In particular this means that if we have a Pythagorean triple (x, y, z) (so: x² + y² = z²), then the maximum width painting for corridors of width x³ and y³ is z³.

      In this case: a = 27 = 3³ and b = 64 = 4³, so z = 5³ = 125.

      Solution: The painting is 125 ft wide.

      Like

      • Jim Randell's avatar

        Jim Randell 10:20 pm on 11 November 2020 Permalink | Reply

        Or a numerical solution:

        from math import (cos, sin, pi)
        from enigma import (fdiv, find_min, printf)
        
        # width of the legs
        (a, b) = (27, 64)
        
        # length of rod
        z = lambda theta: fdiv(a, cos(theta)) + fdiv(b, sin(theta))
        
        # find the minimum length
        r = find_min(z, 0, 0.5 * pi)
        
        # output solution
        printf("width = {r.fv:g} ft")
        

        Like

    • Frits's avatar

      Frits 4:13 pm on 10 November 2020 Permalink | Reply

      #    -----+-----------+
      #     27  .\          |
      #    -----+---\  W    |
      #           x |  \    |
      #             |y    \ |
      #             |.......+
      #             |       |
      #             |  64   |
      #
      # W =  Width painting
      #
      # W^2 = (x + 64)^2 + (y + 27)^2
      #
      # x / 27 = 64 / y --> xy = 27*64 --> y = 27*64/x
      #
      # W = ((x + 64)^2 + (27*64/x + 72)^2)^0.5
      #
      # W is minimal if W' = 0
      #
      # W' = 0.5(((x + 64)^2 + (27*64/x + 72)^2)^-0.5 * 
      #      (2 * (x + 64) + 2 * (27*64/x + 27)) * 
      #      -27*64/x^2  
      #
      # term 2, 3 = x + 64 + (27*64/x + 27)) * -27*64/x^2 
      #           = x + 64 - 27*27*64/x^2 - (27*64)^2/x^3
      #           = x^4 + 64 * x^3 - 27*27*64 * x - (27*64)^2
      #           = x^3 * (x + 64) - 27*27*64 * (x + 64)
      #           = (x^3 - 27*27*64) * (x + 64)
      #
      # W' = 0 if x = -64 or x = 36, disregard negative x
      #
      # x = 36 --> y = 48
      #
      # W^2 = (36 + 64)^2 + (48 + 27)^2 = 15625 --> W = 125
      

      Like

    • Frits's avatar

      Frits 7:58 pm on 10 November 2020 Permalink | Reply

      from enigma import SubstitutedExpression
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
         # find the length of the shortest line segment from (UVW, 0) to (0, XYZ)
         # that passes through (27, 64).
         #
         # 64 / (UVW - 27) = (XYZ - 64) / 27
         
         "UVW*XYZ - 64*UVW == 27*XYZ",
         "UVW > 0",
         "XYX > 0",
        ],
        answer="((UVW)**2 + (XYZ)**2)**0.5, UVW, XYZ",
        d2i={},
        distinct="",
        accumulate=min,
        verbose=0)   
          
      # solve the puzzle
      r = p.run()
      
      ans = list(r.accumulate)
      print("answer =",ans[0])
      
      # answer =  125.0
      

      Like

  • Unknown's avatar

    Jim Randell 4:38 pm on 6 November 2020 Permalink | Reply
    Tags:   

    Teaser 3033: Goldilocks and the bear commune 

    From The Sunday Times, 8th November 2020 [link] [link]

    In the bears’ villa there are three floors, each with 14 rooms. The one switch in each room bizarrely toggles (on ↔ off) not only the single light in the room but also precisely two other lights on the same floor; moreover, whenever A toggles B, then B toggles A.

    As Goldilocks moved from room to room testing various combinations of switches, she discovered that on each floor there were at least two separate circuits and no two circuits on a floor had the same number of lights. Furthermore, she found a combination of 30 switches that turned all 42 lights from “off” to “on”, and on one floor she was able turn each light on by itself.

    (a) How many circuits are there?
    (b) How many lights are in the longest circuit?

    [teaser3033]

     
    • Jim Randell's avatar

      Jim Randell 5:47 pm on 6 November 2020 Permalink | Reply

      (See also: Puzzle #51, Enigma 1137, Enigma 1127).

      I think there are multiple solutions to this puzzle. Although if no floor is allowed to have the same configuration of circuits as another floor, then I think we can find a unique solution:

      If each light is connected to two other lights, then they must form a circular arrangement (like the candles in Puzzle #51).

      In a circuit where the number of lights is a multiple of 3, the lights can be toggled by switching the central light in each non-overlapping consecutive group of three. Otherwise the lights can be toggled by operating each switch once. Each light is toggled 3 times, so ends up in the opposite state.

      Only in those circuits where the number of lights is not a multiple of 3 can a single light be illuminated. And if one single light can be illuminated, then all the others in that circuit can also be illuminated singly.

      This Python program finds decompositions of 14 into 2 or more different circular circuits; finds those sets that require 30 switches to be operated to toggle every light; and also one of the sets must be composed entirely of circuits that do not consist of a multiple of 3 lights.

      It runs in 49ms.

      Run: [ @repl.it ]

      from enigma import irange, subsets, flatten, printf
      
      # decompose t into increasing numbers
      def decompose(t, m=3, ns=[]):
        if t == 0:
          if len(ns) > 1:
            yield ns
        else:
          for n in irange(m, t):
            yield from decompose(t - n, n + 1, ns + [n])
      
      # number of switches required for circuit with k lights
      def count(k):
        (n, r) = divmod(k, 3)
        return (n if r == 0 else k)
      
      # find 3 splits of 14
      for ss in subsets(decompose(14), size=3, select="C"):
        # sum the number of switches required
        ks = flatten(ss)
        t = sum(count(k) for k in ks)
        if t != 30: continue
        # and one floor must not contain a multiple of 3
        if all(any(k % 3 == 0 for k in s) for s in ss): continue
        # output solution
        a = len(ks)
        b = max(ks)
        printf("{a} circuits; longest = {b}; {ss}")
      

      Solution: (a) There are 7 circuits; (b) There are 10 lights in the longest circuit.

      The three configurations are: (3, 5, 6), (4, 10), (5, 9).

      The (4, 10) configuration requires switches to be operated 14 times to toggle the state of all lights, and in each circuit it is possible to illuminate the lights individually.

      The (3, 5, 6) and (5, 9) configurations, both require switches to be operated 8 times to toggle the state of all lights, and it is not possible to illuminate single lights in the 3, 6 or 9 circuits.

      If we are allowed to use the same configuration of circuits on 2 different floors, then there are two further solutions:

      (3, 5, 6), (3, 5, 6), (4, 10)
      (5, 9), (5, 9), (4, 10)

      The other solutions can be found by changing the select parameter at line 18 to [[ select="R" ]].

      The number of lights in the longest circuit is the same for all solutions. But the total number of circuits differs in each case.

      Like

    • Frits's avatar

      Frits 12:44 pm on 8 November 2020 Permalink | Reply

      @Jim,

      “and on one floor she was able turn each light on by itself” and line 24.

      I don’t see this as “on at least one floor”.

      Like

      • Jim Randell's avatar

        Jim Randell 1:02 pm on 8 November 2020 Permalink | Reply

        @Frits: Unless the puzzle says “exactly one” (or “precisely one”, or “only one”, etc) I usually treat such statements as “at least one”. In this case “at least one” or “exactly one” will lead to the same solution(s).

        Like

  • Unknown's avatar

    Jim Randell 2:36 pm on 5 November 2020 Permalink | Reply
    Tags:   

    Teaser 2762: What a gem! 

    From The Sunday Times, 30th August 2015 [link] [link]

    A friend showed me a beautiful gem with shiny flat faces and lots of planes of symmetry. After a quick examination I was able to declare that it was “perfectly square”. This puzzled my friend because none of the faces had four edges. So I explained by pointing out that the gem’s number of faces was a perfect square, its number of edges was a perfect square, and its number of vertices was a perfect square.

    How many faces did it have, and how many of those were triangular?

    [teaser2762]

     
    • Jim Randell's avatar

      Jim Randell 2:37 pm on 5 November 2020 Permalink | Reply

      This is a similar puzzle to Enigma 1376, and we can use a similar program to find possible (vertex, face, edge) counts.

      from enigma import irange, subsets, printf
      
      # some squares
      squares = set(i * i for i in irange(2, 15))
      
      # for a simple polyhedron V + F - E = 2
      for (V, F) in subsets(squares, size=2, select="M"):
        E = V + F - 2
        # also 2E >= 3F and 3V
        if 3 * max(V, F) > 2 * E: continue
        # E should be a square
        if E not in squares: continue
      
        printf("E={E} V={V} F={F}")
      

      We find the candidates are the same, i.e.: (V=9, F=9, E=16).

      So we are looking at an elongated square pyramid or an octagonal pyramid. And only the octagonal pyramid has no quadrilateral faces.

      Solution: The gem has 9 faces. 8 of the faces are triangular.

      And the other face is octagonal:

      Like

  • Unknown's avatar

    Jim Randell 10:55 am on 3 November 2020 Permalink | Reply
    Tags:   

    Teaser 1929: Square trip 

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

    My car has an odometer, which measures the total miles travelled. It has a five-figure display (plus two decimal places). There is also a “trip” counter with a three-figure display.

    One Sunday morning, when the car was nearly new, the odometer showed a whole number which was a perfect square and I set the trip counter to zero. At the end of that day the odometer again showed a whole number that was a perfect square, and the trip counter showed an odd square.

    Some days later, the display on the odometer was four times the square which had been displayed on that Sunday evening, and once again both displays were squares.

    What were the displays on that last occasion?

    This puzzle is included in the book Brainteasers (2002). The puzzle text above is taken from the book.

    [teaser1929]

     
    • Jim Randell's avatar

      Jim Randell 10:55 am on 3 November 2020 Permalink | Reply

      I supposed that the initial reading was less than 1000 miles, and the car travels less than 1000 miles on the particular Sunday.

      This Python program runs in 49ms.

      Run: [ @repl.it ]

      from enigma import irange, inf, is_square, printf
      
      def solve():
        # consider initial readings (when the car was nearly new)
        for a in irange(1, 31):
          r1 = a * a
      
          # at the end of the day
          for b in irange(a + 1, inf):
            r2 = b * b
            # the trip reading is an odd square
            t2 = r2 - r1
            if t2 > 1000: break
            if not(t2 % 2 == 1 and is_square(t2)): continue
      
            # some days later the odometer reads 4 times as far
            r3 = 4 * r2
            # and the trip reading is also a square
            t3 = (r3 - r1) % 1000
            if not is_square(t3): continue
      
            yield ((r1, 0), (r2, t2), (r3, t3))
      
      # find the first solution
      for ((r1, t1), (r2, t2), (r3, t3)) in solve():
        printf("{r1} + {t1}; {r2} + {t2}; {r3} + {t3}")
        break
      

      Solution: The displays were 2500, and 100.

      The trip was set to zero when the car had done 400 miles (= 20²), so it was still fairly new.

      Then after another 225 miles, the odometer would read 625 miles (= 25²), and the trip would read 225 (= 15²).

      The final reading was taken after another 1875 miles. The odometer reads 2500 miles (= 50²), and the trip reads the 3 least significant digits of 2100, which are 100 (= 10²).

      Like

      • Jim Randell's avatar

        Jim Randell 2:19 pm on 3 November 2020 Permalink | Reply

        As r1, r2, t2 are all squares, such that: r1 + t2 = r2 we can solve this puzzle using Pythagorean triples.

        Run: [ @repl.it ]

        from enigma import pythagorean_triples, is_square, printf
        
        # consider pythagorean triples
        for (x, y, z) in pythagorean_triples(100):
          # readings
          (r1, t1) = (y * y, 0)
          (r2, t2) = (z * z, x * x)
          if not(r1 < 1000 and t2 < 1000 and t2 % 2 == 1): continue
          r3 = 4 * r2
          t3 = (r3 - r1) % 1000
          if not is_square(t3): continue
          # output solution
          printf("{r1} + {t1}; {r2} + {t2}; {r3} + {t3} [{x}, {y}, {z}]")
        

        And it is probably not a great surprise that there is a (3, 4, 5) triangle hiding in the solution.

        Like

  • Unknown's avatar

    Jim Randell 11:17 am on 1 November 2020 Permalink | Reply
    Tags:   

    Brain-Teaser 12: [Birthdays] 

    From The Sunday Times, 14th/21st May 1961 [link] [link]

    My wife and I, my son and daughter, my two grandsons, and my granddaughter (the youngest of the family, who was fifteen last birthday) were all born on the same day of the week, and we all have our birthdays on the same date, but all in different months. [I won’t be able to say this if there are any further additions to the family.]

    My grandsons were born nine months apart, my daughter eighteen months after my son, and I am forty-one months older than my wife.

    What are all our birth dates?

    This puzzle was originally published in the 14th May 1961 edition of The Sunday Times, however the condition in square brackets was omitted, and the corrected version (and an apology) was published in the 21st May 1961 edition.

    This puzzle was originally published with no title.

    [teaser12]

     
    • Jim Randell's avatar

      Jim Randell 11:18 am on 1 November 2020 Permalink | Reply

      I assume the missing condition means that it is not possible for an 8th member of the family to have the same “day of week” and “day of month” birthday, but not share a birthday month with one of the group of 7.

      The current calendar repeats itself every 400 years, so this program looks for sets of dates in a 400 years span that share the same “day of week” and “day of month” values, but that only involve 7 different months. (So that any further additions to the family could not be born of the same day of the week and day of the month, but a month that has not yet been used. (The condition that was missing when the puzzle was originally published)).

      It then looks for dates that satisfy the required differences, and checks the all use different months.

      It runs in 199ms.

      Run: [ @repl.it ]

      import datetime
      from enigma import group, catch, printf
      
      # generate dates between years a and b
      def dates(a, b):
        d = datetime.date(a, 1, 1)
        i = datetime.timedelta(days=1)
        while d.year < b:
          yield d
          d += i
      
      # the date n months earlier than d
      def earlier(d, n):
        (yy, mm, dd) = (d.year, d.month, d.day)
        (y, m) = divmod(mm - n - 1, 12)
        return catch(datetime.date, yy + y, m + 1, dd)
      
      # group 400 years of dates by (<day of week>, <day of month>)
      d = group(dates(1850, 2250), by=(lambda d: (d.weekday(), d.day)))
      
      # now look for keys that involve exactly 7 different months
      for ((dow, dom), vs) in d.items():
        ms = set(d.month for d in vs)
        if len(ms) != 7: continue
      
        # collect possible birthdates for the granddaughter
        (gda, gdb) = (datetime.date(1945, 5, 15), datetime.date(1946, 5, 14))
        for gdd in vs:
          if gdd < gda: continue
          if not(gdd < gdb): break
      
          # find birthdates for the grandsons (earlier than gd)
          for gsd2 in vs:
            if not(gsd2 < gdd): break
            gsd1 = earlier(gsd2, 9)
            if gsd1 is None or gsd1 not in vs: continue
      
            # find birthdates for the son and daughter (earlier than gs1 - 15 years)
            dx = gsd1 - datetime.timedelta(days=5479)
            for dd in vs:
              if not(dd < dx): break
              sd = earlier(dd, 18)
              if sd is None or sd not in vs: continue
      
              # find birthdates for the husband and wife (earlier than sd - 15 years)
              wx = sd - datetime.timedelta(days=5479)
              for wd in vs:
                if not(wd < wx): break
                hd = earlier(wd, 41)
                if hd is None or hd not in vs: continue
      
                # check the months are all different
                if len(set(d.month for d in (gdd, gsd2, gsd1, dd, sd, wd, hd))) != 7: continue
      
                printf("{dow} {dom}", dow=["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dow])
                printf("-> husband = {hd}, wife = {wd}")
                printf("-> son = {sd}, daughter = {dd}")
                printf("-> grandsons = {gsd1}, {gsd2}")
                printf("-> granddaughter = {gdd}")
                printf()
      

      Solution: The birthdates are all Mondays. The full list is (by generation):

      husband = 31st October 1898; wife = 31st March 1902
      son = 31st January 1921; daughter = 31st July 1922
      grandson1 = 31st August 1942; grandson2 = 31st May 1943; granddaughter = 31st December 1945

      The only “day of month” that allows exactly 7 months to be used is the 31st of the month, as there are only 7 months that have 31 days.

      Like

  • Unknown's avatar

    Jim Randell 9:49 am on 30 October 2020 Permalink | Reply
    Tags:   

    Teaser 3032: Darts display 

    From The Sunday Times, 1st November 2020 [link] [link]

    I noticed a dartboard in a sports shop window recently. Three sets of darts were positioned on the board. Each set was grouped as if the darts had been thrown into adjacent numbers (e.g., 5, 20, 1) with one dart from each set in a treble. There were no darts in any of the doubles or bulls.

    The darts were in nine different numbers but the score for the three sets was the same. If I told you whether the score was odd or even you should be able to work out the score. The clockwise order of numbers on a dartboard is:

    20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5

    What was the score that all three sets of darts made?

    [teaser3032]

     
    • Jim Randell's avatar

      Jim Randell 10:05 am on 30 October 2020 Permalink | Reply

      This Python program runs in 49ms.

      Run: [ @repl.it ]

      from collections import defaultdict
      from enigma import tuples, unpack, subsets, union, filter_unique, printf
      
      # the scores on the dartboard
      board = (20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5)
      
      # group possible scores together
      d = defaultdict(list)
      for (a, b, c) in tuples(board, 3, circular=1):
        for k in (3 * a + b + c, a + 3 * b + c, a + b + 3 * c):
          d[k].append((a, b, c))
      
      # find scores with three disjoint sets of sectors
      rs = list()
      for (k, vs) in d.items():
        for sss in subsets(vs, size=3):
          if len(union(sss)) == 9:
            rs.append((k, sss))
      
      # find scores unique by parity
      rs = filter_unique(rs, unpack(lambda k, sss: k % 2), unpack(lambda k, sss: k)).unique
      
      # output solution
      for (k, sss) in rs:
        printf("score = {k} {sss}")
      

      Solution: The score was 56.

      The three groups of darts are:

      2; treble 17; 3
      19; treble 7; 16
      treble 11; 14; 9

      This is the only disjoint collection with an even score.

      It is possible to make odd scores of 43, 47, 61.

      And the score of 61 can be made in 4 different ways, so is the answer to a variation on the puzzle where: “if I told you the score, you still wouldn’t be able to work out the set of sectors where the darts were placed”.

      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