Recent Updates Page 48 Toggle Comment Threads | Keyboard Shortcuts

  • 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

  • Unknown's avatar

    Jim Randell 12:17 pm on 29 October 2020 Permalink | Reply
    Tags:   

    Teaser 1928: Prime of life 

    From The Sunday Times, 29th August 1999 [link]

    Today my daughter was looking through her old scrapbook and came across a rhyme I had written about her when she was a little girl:

    Here’s a mathematical rhyme:
    Your age in years is a prime;
    Mine is too,
    And if you add the two
    The answer’s a square — how sublime!

    She was surprised to find that this is also all true today. Furthermore is will all be true again some years hence.

    How old are my daughter and I?

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

    [teaser1928]

     
    • Jim Randell's avatar

      Jim Randell 12:18 pm on 29 October 2020 Permalink | Reply

      When rhyme was written the daughter’s age was some prime d, the father’s age some prime f, and (d + f) is a square number.

      Unless they were born on the same day at the same time one of them have their next birthday before the other.

      If it is the daughter, the ages progress like this:

      (d, f) → (d + 1, f) → (d + 1, f + 1) → (d + 2, f + 1) → (d + 2, f + 2) …

      And if the father has the next birthday, the ages progress like this:

      (d, f) → (d, f + 1) → (d + 1, f + 1) → (d + 1, f + 2) → (d + 2, f + 2) …

      So we just need to examine these sequences to look for two more occurrences of the ages being prime, and their sum being a square.

      This Python program runs in 44ms.

      from enigma import (primes, is_square, printf)
      
      # primes for ages
      primes.expand(150)
      
      # check for viable ages
      check = lambda a, b: a in primes and b in primes and is_square(a + b)
      
      # extend the list to length k, with birthdays a then b
      def extend(a, b, k=3):
        rs = [(a, b)]
        while a < 150 and b < 150:
          a += 1
          if check(a, b): rs.append((a, b))
          b += 1
          if check(a, b): rs.append((a, b))
          if not (len(rs) < k): return rs
      
      # consider the daughter's at the time of the rhyme
      for d in primes.between(2, 16):
      
        # now consider possible ages for the father
        for f in primes.between(d + 16, 50):
          # together they make a square
          if not is_square(d + f): continue
      
          # solutions where daughters birthday is next
          rs = extend(d, f)
          if rs: printf("d -> f: {rs}")
      
          # solutions where fathers birthday is next
          rs = extend(f, d)
          if rs: printf("f -> d: {rs}")
      

      Solution: Today the daughter is 7 and the father is 29.

      The three pairs of ages are:

      d = 2, f = 23: d + f = 25 = 5²
      d = 7, f = 29: d + f = 36 = 6²
      d = 61, f = 83: d + f = 144 = 12²

      So father’s birthday came first after the rhyme was written.

      Like

    • Frits's avatar

      Frits 1:44 pm on 29 October 2020 Permalink | Reply

      from enigma import SubstitutedExpression, is_prime, is_square
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [# father (age CDE) is a lot older than daughter (age AB)
         "AB + 15 < CDE",
         "PQ > 0",
         "RS > 0",
         # daughter must have already been born PQ years ago (and prime)
         # "when she was a little girl"
         "0 < AB - PQ < 10",
         # maximum age father
         "CDE < 125",
         "CDE + RS < 125",
         
         # PQ years ago
         "is_prime(AB - PQ - w)",
         "is_prime(CDE - PQ - x)",
         "is_square(AB + CDE - 2 * PQ - w - x)",
         # now
         "is_prime(AB)",
         "is_prime(CDE)",
         "is_square(AB + CDE)",
         # RS years later
         "is_prime(AB + RS + y)",
         "is_prime(CDE + RS + z)",
         "is_square(AB + CDE + 2 * RS + y + z)",
         
         # uncertainty bits, not both of them may be true
         "x + w < 2",
         "y + z < 2",
        ],
        answer="AB, CDE, PQ, RS, w, x, y, z",
        symbols="ABCDEPQRSwxyz",
        verbose=0,
        d2i=dict([(k, "wxyz") for k in {2,3,4,5,6,7,8,9}]),
        distinct="",   # allow variables with same values
        #reorder=0,
      )
      
      # Print answer
      for (_, ans) in p.solve():
        print(f"ages daughter {ans[0] - ans[2] - ans[4]} {ans[0]} "
              f"{ans[0] + ans[3] + ans[6]}")
        print(f"ages father   {ans[1] - ans[2]  - ans[5]} "
              f"{ans[1]} {ans[1] + ans[3]  + ans[7]}")
        print(f"squares       {ans[0] + ans[1] - 2*ans[2]  - ans[4] - ans[5]} "
              f"{ans[0] + ans[1]} {ans[0] + ans[1] + 2*ans[3] + ans[6] + ans[7]}")
        
        
      # ages daughter 2 7 61
      # ages father   23 29 83
      # squares       25 36 144
      

      Like

    • Frits's avatar

      Frits 4:16 pm on 29 October 2020 Permalink | Reply

      from enigma import printf
      
      # Prime numbers up to 124
      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, 125, 2) if all(x % p for p in P)]
      
      # report two prime numbers (ascending) which sum to <n>
      def twoprimes(n, dif=0): 
        li = []
        i = 1
        p1 = 2 
        while(p1 < n - p1): 
          if n - p1 in P:
            # difference between primes is not exact
            if dif == 0 or (dif - 1 <= abs(n - 2 * p1) <= dif + 1): 
              li.append([p1, n - p1])
          p1 = P[i]
          i += 1
        return li  
      
      # square candidates for when daughter was a little girl   
      for i in range(4, 11):  # also allowing for people like B. Ecclestone
        for t1 in twoprimes(i * i): 
          # "when she was a little girl"
          if t1[0] > 9: continue
          dif1 = int(t1[1]) - int(t1[0])
          # square candidates for now
          for j in range(i + 1, 15):
            for t2 in twoprimes(j * j, dif1): 
              # square candidates for in te future
              for k in range(j + 1, 16):
                for t3 in twoprimes(k * k, dif1): 
                  printf("{t1[0]} + {t1[1]} = {su}", su = int(t1[0]) + int(t1[1]))
                  printf("{t2[0]} + {t2[1]} = {su}", su = int(t2[0]) + int(t2[1]))
                  printf("{t3[0]} + {t3[1]} = {su}", su = int(t3[0]) + int(t3[1]))
      

      Like

    • GeoffR's avatar

      GeoffR 9:23 pm on 29 October 2020 Permalink | Reply

      % A Solution in MiniZinc     
      include "globals.mzn";
      
      % Three Daughters' ages
      % D1 = young girl, D2 = age now, D3 = future age
      var 1..10:D1; var 1..50:D2; var 1..80:D3;
      
      % Three corresponding Fathers ages
      var 13..40:F1; var 13..70:F2; var 13..105:F3;
      
      % All Fathers and Daughters ages are different
      constraint all_different ([F1, F2, F3, D1, D2, D3]);
      
      set of int: primes = {2, 3, 5, 7, 11, 13, 17,
      19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
      71, 73, 79, 83, 89, 97, 101, 103};
      
      set of int : squares = {16, 25, 36, 49, 64, 81,
       100, 121, 144, 169};
      
      % Age group constraints
      constraint F1 - D1 > 12 /\ D1 < 10 ;
      constraint D2 > D1 /\ D3 > D2;
      constraint F2 > F1 /\ F3 > F2;
      constraint F1 > D1 /\ F2 > D2 /\ F3 > D3;
      
      % 1st age differences between Father and Daughter
      constraint D2 - D1 = F2 - F1 + 1
      \/ D2 - D1 = F2 - F1 - 1;
      
      % Age difference is the same between the 2nd and 3rd age groups
      constraint F2 - D2 == F3 - D3;
      constraint F3 - F2 == D3 - D2;
      
      % All ages are prime numbers
      constraint F1 in primes /\ F2 in primes /\ F3 in primes;
      constraint D1 in primes /\ D2 in primes /\ D3 in primes;
      
      % All father and daughter group sums are squares
      constraint (F1 + D1) in squares /\ (F2 + D2) in squares 
      /\ (F3 + D3) in squares;
      
      solve satisfy;
      
      output[ "Father's ages in sequence are " ++ show(F1) ++
       ", " ++ show(F2) ++ ", " ++ show(F3)  
      ++"\nDaughters ages in sequence are " ++ show(D1) ++
       ", " ++ show(D2) ++  ", " ++ show(D3) ];
      
      % Father's ages in sequence are 23, 29, 83
      % Daughters ages in sequence are 2, 7, 61
      % ----------
      % ==========
      
      
      

      Like

      • Frits's avatar

        Frits 10:25 am on 30 October 2020 Permalink | Reply

        I feel lines 11 and 12 are too restrictive. However, removing them gives the same result.

        Like

    • GeoffR's avatar

      GeoffR 5:16 pm on 30 October 2020 Permalink | Reply

      @Frits:
      The constraint programming approach is to search for a state of the world in which a large number of constraints are satisfied at the same time. But we cannnot predict in advance which constraints are required for a solution. It is, of course, a different approach to imperative programming e.g. Python.

      It is therefore possible to cancel one or two constraints and still get a solution.
      We soon know if we have not got sufficient constraints when we don’t get a solution.

      Lines 11 and 12 are logical in that we know over several time scales that all the fathers and daughters ages will be different, so it is quite OK to make this a constraint, in my view.

      As an experiment, I remmed out Lines 22, 23 and 24, leaving Line 12 functional. This still gave the correct solution. I then remmed out Line 12 and I got three solutions – one correct and two wrong.
      This shows that Line 12 constraint is related to the constraints on Lines 22,23 and 24 in functionality.

      So we could equally say that Lines 22,23 and 24 could be removed, providing Line 12 was not removed.

      I think all the constraints I used have a reasonable basis, so it is probably best not to tinker with individual constraints and let constraint programming use its own internal procedures to find a solution.

      Like

    • Frits's avatar

      Frits 11:26 am on 31 October 2020 Permalink | Reply

      @GeoffR,

      My only point is that this particular constraint is not part of the requirements and can potentially give cause to miss certain solutions.

      You say that “we know over several time scales that all the fathers and daughters ages will be different”. I think it is possible the daughter “today” can have the same age as her father at the time she was a little girl (same for later on).

      Example (if we forget about primes):

      Father’s ages in sequence are 23, 44, 83
      Daughters ages in sequence are 2, 23, 62

      You may be right if the line 11/12 constraint emerges from all the requirements but that is not immediately clear to me.

      Like

  • Unknown's avatar

    Jim Randell 2:58 pm on 27 October 2020 Permalink | Reply
    Tags:   

    Teaser 2761: Digital shuffle 

    From The Sunday Times, 23rd August 2015 [link] [link]

    George and Martha have nine cards with a different non-zero digit on each. To teach their nephew to count they lined up the cards in increasing order. He then rearranged the order of the line and Martha was impressed when she noticed that no digit was in its original position. George was even more impressed when he found that the six-figure number formed by the last six cards was the square of the three-figure number formed by the first three.

    What was that three-figure number?

    [teaser2761]

     
    • Jim Randell's avatar

      Jim Randell 2:59 pm on 27 October 2020 Permalink | Reply

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

      The following run file executes in 99ms.

      Run: [ @repl.it ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      # non-zero digits
      --digits="1-9"
      
      # no digit is in its original position
      --invalid="1,A"
      --invalid="2,B"
      --invalid="3,C"
      --invalid="4,D"
      --invalid="5,E"
      --invalid="6,F"
      --invalid="7,G"
      --invalid="8,H"
      --invalid="9,I"
      
      "sq(ABC) = DEFGHI"
      
      --answer="ABC"
      

      Solution: The three-figure number was 854.

      Giving:

      854² = 729316

      If we remove the condition that “no digit is in its original position”, we find there is a further solution to the alphametic of:

      567² = 321489

      But this is disallowed as the digits 8 and 9 are in positions 8 and 9.

      Like

    • GeoffR's avatar

      GeoffR 4:23 pm on 27 October 2020 Permalink | Reply

      % A Solution in MiniZinc 
      include "globals.mzn";
      
      var 1..9:A; var 1..9:B; var 1..9:C; var 1..9:D; 
      var 1..9:E; var 1..9:F; var 1..9:G; var 1..9:H;
      var 1..9:I;
      
      constraint all_different ([A,B,C,D,E,F,G,H,I]);
      
      % Nine cards not in original position
      constraint A != 1 /\ B != 2 /\ C != 3
      /\ D != 4 /\ E != 5 /\ F != 6 
      /\ G != 7 /\ H != 8 /\ I != 9;
      
      % Number formed by 1st three cards
      var 100..999: ABC = 100*A + 10*B + C;
      
      % Number formed by last six cards
      var 100000..999999: DEFGHI = 100000*D + 10000*E
      + 1000*F + 100*G + 10*H + I;
      
      %  the six-figure number formed by the last six cards was
      % the square of the three-figure number formed by the first three
      constraint DEFGHI = ABC * ABC;
      
      solve satisfy;
      
      output ["Three figure number was " ++ show(ABC)
      ++"\nSix figure number was " ++ show(DEFGHI) ];
      
      % Three figure number was 854
      % Six figure number was 729316
      % ----------
      % ==========
      
      
      

      Like

    • Frits's avatar

      Frits 10:47 am on 28 October 2020 Permalink | Reply

      from itertools import dropwhile, count
       
      # return True if not all rules are met 
      def wrongSquare(trueList):
        
        # check if number meets the rules
        def OK(x):
          # booleans
          bs = trueList.copy()
          
          i = 0
          while x and i < 9:
            y = x % 10
            # no derangement
            if y != 9 - i:
              bs[y] = False
            x //= 10
            i += 1
          # all digits 1-9 used and no zero   
          return sum(bs) == 1 and bs[0]
          
        return lambda n: not OK(1000000 * n + (n * n)) 
       
      trueList = [True] * 10
      sol = next(dropwhile(wrongSquare(trueList), count(317)))
      print(f"{sol}^2 = {sol*sol}") 
      

      Like

    • GeoffR's avatar

      GeoffR 1:43 pm on 28 October 2020 Permalink | Reply

      
      from itertools import permutations
      
      for p1 in permutations('123456789'):
          a, b, c, d, e, f, g, h, i = p1
          # check numbers not in increasing order
          # no digit is in its original position
          if (a == '1' or b == '2' or c == '3' or
          d == '4' or e == '5' or f == '6' or
          g == '7' or g == '8' or i == '9'):
              continue
          # form 3-digit and 6-digit numbers
          abc = int(a + b + c)
          defghi = int(d + e + f + g + h + i)
          if abc ** 2 == defghi:
              print(f"Three digit number is {abc}")
              print(f"Six digit number is {defghi}")
              
      # Three digit number is 854
      # Six digit number is 729316
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:57 am on 25 October 2020 Permalink | Reply
    Tags:   

    Teaser 1923: Get back to me 

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

    I met a nice girl at a party and asked for her phone number. To prove that she was no pushover she made me work for it.

    “My number has seven digits, all different”, she told me. “If you form the largest number you can with those seven digits and subtract from it the reverse of that largest number, then you get another seven-digit number”, she added.

    “Then if you repeat the process with that new seven-digit number, you get another seven-digit number”, she added. “And if you repeat the process enough times you’ll get back to my phone number”.

    This information did enable me to get back to her!

    What is her telephone number?

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

    [teaser1923]

     
    • Jim Randell's avatar

      Jim Randell 8:58 am on 25 October 2020 Permalink | Reply

      When we apply the process to a number the order of the digits does not matter, as we immediately reorder the digits to make the largest possible number, and it’s reverse. So we only need to consider the possible collections of 7 distinct digits. If the same set of digits pops out of the process after several applications we have found a solution.

      As we keep applying the process we must eventually get to a collection of digits we have seen before (as there are only a finite number of collections of 7 digits). So if we haven’t found a solution by the time we get a repeated set of digits, then we are never going to find one.

      This Python program runs in 53ms.

      Run: [ @repl.it ]

      from enigma import subsets, irange, inf, nconcat, nsplit, ordered, printf
      
      # check the process, using a sequence of 7 ordered digits
      def check(ds):
        seen = set()
        ss = ds
        for i in irange(1, inf):
          if ss in seen: return None
          seen.add(ss)
          # make the largest number, and it's reverse
          n = nconcat(ss[::-1])
          r = nconcat(ss)
          # and subtract them
          s = n - r
          # are the digits in s the starting digits?
          ss = ordered(*(nsplit(s)))
          if len(ss) != 7: return None
          if i > 2 and ss == ds: return s
      
      # choose the 7 different digits in the phone number
      for ds in subsets(irange(0, 9), size=7):
        n = check(ds)
        if n is not None:
          printf("number = {n:07d}")
      

      Solution: The telephone number is: 7509843.

      Repeated application of the process yields:

      7509843: 9875430 − 0345789 = 9529641
      9529641: 9965421 − 1245699 = 8719722
      8719722: 9877221 − 1227789 = 8649432
      8649432: 9864432 − 2344689 = 7519743
      7519743: 9775431 − 1345779 = 8429652
      8429652: 9865422 − 2245689 = 7619733
      7619733: 9776331 − 1336779 = 8439552
      8439552: 9855432 − 2345589 = 7509843

      So we have to apply the process 8 times.

      Like

    • Frits's avatar

      Frits 2:24 pm on 27 October 2020 Permalink | Reply

      # nice girl's phone number abcdefg
      #
      # last transformation:
      #
      # h1h2h3ml3l2l1 = abcdefg + l1l2l3mh3h2h1
      #
      # c1...c5 carry bits
      #
      # h1 = a + l1
      # h2 = b + l2 + c5
      # h3 = c + l3 + c4 - 10*c5
      # m  = d + m  + c3 - 10*c4
      # l3 = e + h3 + c2 - 10*c3
      # l2 = f + h2 + c1 - 10*c2
      # l1 = g + h1 - 10*c1
      #
      # l1 < h1 --> c1 = 1
      # l2 < h2 + 1 --> c2 = 1
      # l3 < h3 + 1 --> c3 = 1
      # m = d + m + 1 - 10*c4 --> d = 10*c4 - 1 --> c4 = 1 --> d = 9
      #
      # h1 = a + l1 = a + g + h1 - 10 --> a + g = 10
      # h2 = b + l2 + c5 = b + f + h2 - 9 + c5 --> b + f = 9 - c5
      # h3 = c + l3 + 1 - 10*c5 = c + e + h3 - 9 + 1 - 10*c5 --> c + e = 8 + 10*c5
      # --> c5 = 0 and c + e = 8
      

      With A+G = 10, B+F = 9, C+E = 8, D = 9 enigma [[SubstitutedExpression]] leaves 64 ABCDEFG combinations to check.

      Like

      • Frits's avatar

        Frits 2:47 pm on 27 October 2020 Permalink | Reply

        Don’t know why I didn’t use carry bit c6 but the result is the same (c6 = 0).

        Like

  • Unknown's avatar

    Jim Randell 4:43 pm on 23 October 2020 Permalink | Reply
    Tags:   

    Teaser 3031: End of the beginning 

    From The Sunday Times, 25th October 2020 [link] [link]

    Jenny is using her calculator, which accepts the input of numbers of up to ten digits in length, to prepare her lesson plan on large numbers. She can’t understand why the results being shown are smaller than she expected until she realizes that she has entered a number incorrectly.

    She has entered the number with its first digit being incorrectly entered as its last digit. The number has been entered with its second digit first, its third digit second etc. and what should have been the first digit entered last. The number she actually entered into her calculator was 25/43rds of what it should have been.

    What is the correct number?

    [teaser3031]

     
    • Jim Randell's avatar

      Jim Randell 4:59 pm on 23 October 2020 Permalink | Reply

      See also: Enigma 1036, Enigma 1161, Teaser 2565.

      If we have a number, where the leading digit is a and the remaining k digits are bcdefg… = r, then we have:

      r.10 + a = (25/43)(a.10^k + r)

      The following Python program runs in 45ms.

      Run: [ @replit ]

      from enigma import (irange, div, int2base, printf)
      
      # consider k digit numbers
      for k in irange(1, 9):
        m = 10 ** k
        # consider possible leading digit
        for a in irange(1, 9):
          r = div(a * (25 * m - 43), 405)
          if r is None or not (r < m): continue
          # output solution
          printf("{a}{r} -> {r}{a}", r=int2base(r, width=k))
      

      Solution: The correct number is: 530864197.

      So the number entered is: 308641975.

      >>> fraction(308641975, 530864197)
      (25, 43)
      

      For this particular puzzle we can do some analysis can reduce the solution space further. (From the 9×9 = 81 cases to consider in the general case).

      In the equation:

      43(r.10 + a) = 25(a.10^k + r)

      we see that that each side is divisible by 5, so a must be 5 (as it cannot be 0).

      Which leaves:

      r = (25.10^k − 43) / 81

      Which we can then check with the 9 possible values for k manually or with an even shorter program:

      from enigma import (irange, div, int2base, printf)
      
      for k in irange(1, 9):
        r = div(25 * 10 ** k - 43, 81)
        if r is not None:
          printf("5{r} -> {r}5", r=int2base(r, width=k))
      

      Or, for a complete manual solution:

      Numbers of the form: (25.10^k − 43) / 9, look like this:

      k=1: 23
      k=2: 273
      k=3: 2773
      k=4: 27773

      To divide by 9 again we need the number to have a digital root of 9, and the only one in the required range is:

      k=8: 277777773

      Dividing this by 9 gives:

      r = 277777773 / 9 = 30864197

      Hence the correct number is 530864197, and the incorrect number 308641975.

      Like

      • hans droog's avatar

        hans droog 10:01 am on 6 November 2020 Permalink | Reply

        Hi Jim. would be obliged if you could explain formula in teaser 3031. Many thanks, Hans Droog

        Like

        • Jim Randell's avatar

          Jim Randell 10:10 am on 6 November 2020 Permalink | Reply

          @hans:

          As an example, if we had an 7 digit number abcdefg which was entered incorrectly on the calculator as bcdefga, then we would have:

          bcdefga = (25/43)(abcdefg)

          If we represent the 6 digit number bcdefg as r we can write this expression as:

          r.10 + a = (25/43)(a.10^6 + r)

          The expression I give in my solution is the general case when r is a k digit number.

          (“.” is multiplication. “^” is exponentiation).

          Is that clear?

          Like

    • Frits's avatar

      Frits 1:42 pm on 24 October 2020 Permalink | Reply

      Similar.

      print(["5"+str(lastpart // 81) for k in range(2, 10) 
            if (lastpart := (25 * 10**k - 43)) % 81 == 0][0])
      

      Like

    • Hugh Casement's avatar

      Hugh Casement 4:15 pm on 7 November 2020 Permalink | Reply

      Hans may have been put off by Jim’s use of a decimal point to mean multiplication.
      The international convention is a raised dot.

      Like

      • Jim Randell's avatar

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

        If I were handwriting a decimal number I would use a “middle dot” for the decimal point, and a dot on the line to denote multiplication. Of course when typing the “.” (full stop) symbol has to do for both.

        Fortunately we don’t often have to deal with numbers with decimal points in puzzles.

        Like

    • hans droog's avatar

      hans droog 9:01 am on 8 November 2020 Permalink | Reply

      thats right Hugh

      Like

  • Unknown's avatar

    Jim Randell 8:59 am on 22 October 2020 Permalink | Reply
    Tags:   

    Teaser 2760: Prime location 

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

    I have a rectangular garden of area just over one hectare. It is divided exactly into three parts — a lawn, a flower bed, and a vegetable patch. Each of these three areas is a right-angled triangle with sides a whole numbers of metres in length. A fence runs along two adjacent sides of the rectangular garden. The length of the fence is a prime number of metres.

    What are the dimensions of the rectangular garden?

    Note: 1 hectare = 10,000 square metres.

    [teaser2760]

     
    • Jim Randell's avatar

      Jim Randell 9:00 am on 22 October 2020 Permalink | Reply

      (See also: Enigma 1032).

      In order to fit together the three right-angled triangles have to be similar. So the larger triangles are scaled up versions of the smallest triangle.

      If we have three triangles with sides:

      (x, y, z)
      (y, y²/x, yz/x)
      (z, yz/x, z²/x)

      They can be fitted together to give two different rectangles:

      The total area of the three triangles is: yz² / x.

      and the dimensions of the rectangle are: (yz/x, z) or (y, z²/x).

      This Python program scales up Pythagorean Triples to find three triangles with the required area that fit together to give a prime semi-perimeter.

      It runs in 45ms.

      Run: [ @repl.it ]

      from enigma import pythagorean_triples, is_prime, div, as_int, fcompose, printf
      
      # make div throw an exception on inexact division
      ediv = fcompose(div, as_int)
      
      # generate pythagorean triples
      for (x, y, z) in pythagorean_triples(1000):
      
        # construct the triangles
        try:
          (yy_x, yz_x, zz_x) = (ediv(y * y, x), ediv(y * z, x), ediv(z * z, x))
        except ValueError:
          continue
      
        # check area
        A = y * zz_x
        if not(10000 < A < 12000): continue
      
        # consider the two possible arrangements
        for (i, (a, b)) in enumerate([(z, yz_x), (y, zz_x)], start=1):
          if is_prime(a + b):
            printf("rectangle = {a} x {b} [arrangement {i}]")
      

      Solution: The garden is 60m × 169m.

      The triangles are: (25, 60, 65), (60, 144, 156), (65, 156, 169), each of which is a (5, 12, 13) triangle, and they are scaled up by factors of (5, 12, 13).

      And they are arranged as in the second diagram, to give a 60 × 169 rectangle, with area 10140, and 10,140m² is 1.014 hectares. The semi-perimeter 60 + 169 = 229 is prime.

      The alternate arrangement has the same area (of course), but the semi-perimeter 65 + 156 = 221 is composite.

      Like

    • Frits's avatar

      Frits 1:00 pm on 22 October 2020 Permalink | Reply

      Assuming the garden is not very narrow (with sides more than 9 meters).
      We can then use variables AB and CDE as sides.

      We also assume there are only 2 arrangements as stated by Jim.

      from enigma import  SubstitutedExpression, is_prime
      
      p = SubstitutedExpression([
          # Area AB x CDE just over 10000
          "AB * CDE > 10000",
          "AB * CDE <= 11000",
      
          # sum of 2 adjacent sides is prime
          "is_prime(AB + CDE)",
          
          # setup 1
          #
          #        CDE
          #    +------------+
          #    | \LM        |
          #    |  / \ NOP   |
          # AB | /JK   \    |
          #    |/         \ |
          #    +------------+
          #    
          # force variables to be zero if no solution exists
          "a * FGHI == FGHI",
          "a * NOP == NOP",
          "a * JK == JK",
          "a * LM == LM",
          # JK >= 1
          "a * JK >= a", 
      
          # diagonal FGHI
          "a*(AB**2 + CDE**2) == a*(FGHI**2)",
          "a*(LM**2 + JK**2)  == a*(AB**2)",
          "a*(NOP**2 + JK**2) == a*(CDE**2)",
          
          # setup 2
          #
          #        CDE
          #    +-----------+
          #    |\       ./ | 
          # AB | \XYZ ./QRST
          #    |  \ ./     |
          #    +---v-------+
          #     UVW   
          #
          # UVW >= 1
          "b * UVW >= b",
          # force variables to be zero if no solution exists
          "b * UVW == UVW",
          "b * QRST == QRST",
          "b * XYZ == XYZ",
          
          # UVW is the smaller part of CDE
          "b*(2 * UVW) <= b*(CDE)",
      
          "b*(AB**2 + UVW**2)         == b*(XYZ**2)",
          "b*((CDE - UVW)**2 + AB**2) == b*(QRST**2)",
          "b*(XYZ**2 + QRST**2)       == b*(CDE**2)",
          
          # we need at least one solution
          "a + b > 0",
          ],
          verbose=0,
          symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZab",
          d2i=dict([(0, "AC")]  + [(k, "ab") for k in range(2,10)]),
          answer="AB * CDE, AB, CDE, (JK, LM, NOP, FGHI), (UVW, XYZ, QRST), a, b",
          distinct="",
          #reorder=0
      )
      
      # Print answers
      print("  area  AB  CDE       JK  LM NOP FGHI  UVW XYZ QRST")
      for (_, ans) in p.solve():
        ans = list(ans)
        print(f"{ans[:3]}", end="")
        if ans[5] == 1:
          print(f"{str(ans[3]):>22}")
          if ans[6] == 1:
            print(f" {ans[4]}")
        else:
          print(f"{'':<22} {ans[4]}")
        print()  
        
      #   area  AB  CDE       JK  LM NOP FGHI  UVW XYZ QRST
      # [10140, 60, 169]                       (25, 65, 156)  
      

      Like

      • Frits's avatar

        Frits 2:39 pm on 22 October 2020 Permalink | Reply

        Triangles AB, UVW, XYZ and AB, QRST, (CDE -UVW) have similar angles.
        So UVW / AB is equal to AB / (CDE – UVW).

        The following can be added for faster execution:

        "b*(UVW * (CDE - UVW)) == b * AB**2",
        

        Like

  • Unknown's avatar

    Jim Randell 9:02 am on 20 October 2020 Permalink | Reply
    Tags:   

    Teaser 1917: Trick shots 

    From The Sunday Times, 13th June 1999 [link]

    Joe’s billiard table is of high quality but slightly oversized. It is 14 ½ feet long by 7 feet wide, with the usual six pockets, one in each corner and one in the middle of each long side.

    Joe’s ego is also slightly oversized and he likes to show off with his trick shots. One of the most spectacular is to place a ball at a point equidistant from each of the longer sides and 19 inches from the end nearest to him. He then strikes the ball so that it bounces once off each of the four sides and into the middle pocket on his left.

    He has found that he has a choice of directions in which to hit the ball in order the achieve this effect.

    (a) How many different directions will work?
    (b) How far does the ball travel in each case?

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

    [teaser1917]

     
    • Jim Randell's avatar

      Jim Randell 9:03 am on 20 October 2020 Permalink | Reply

      As with beams of light, it is easier to mirror the table and allow the ball to travel in a straight line. (See: Enigma 1039, Enigma 1532, Teaser 2503).

      If we mirror the table along all four sides we get a grid-like pattern of tables and the path of the ball becomes a straight line between the source and the target pocket.

      The line must cross each colour side once. So vertically it must cross a green and a red (in some order) before ending up in the pocket. Which means only an upward line will do. Horizontally it must cross an orange and a blue line before hitting the pocket. The two possible paths are indicated in this diagram:

      We can then calculate the distances involved. In both cases the vertical distance is 2.5 widths = 210 inches.

      And the horizontal distances are: 2.5 lengths − 19 inches = 416 inches, and: 1.5 lengths + 19 inches = 280 inches.

      The required distances are then:

      >>> hypot(210, 416)
      466.0
      >>> hypot(210, 280)
      350.0
      

      Solution: (a) There are 2 directions which will work; (b) In once case the ball travels: 38 feet, 10 inches; in the other case: 29 feet, 2 inches.

      The paths of the ball on the table look like this:


      Like

  • Unknown's avatar

    Jim Randell 10:34 am on 18 October 2020 Permalink | Reply
    Tags:   

    Brain-Teaser 11: Circulation poor 

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

    Lazy Jack was engaged to deliver a circular to every house in the district. He found the supply of circulars would divide into fourteen equal batches of rather more than 300 each, so he decided to deliver one batch each day and thus spread the job over a fortnight.

    On the first day he faithfully distributed circulars one to a house, but that proved very tiring, so the next day he delivered two at each house he visited. With a fine sense of fairness, he never delivered to the same house twice, and one each succeeding day he chose the next smaller number of houses to visit that would enable him exactly to exhaust the day’s batch by delivering an equal number of circulars at each house. The fourteenth day’s batch of circulars all went through one letter box.

    To how many houses was delivery made?

    [teaser11]

     
    • Jim Randell's avatar

      Jim Randell 10:35 am on 18 October 2020 Permalink | Reply

      On each of 14 days, Jack delivers n leaflets:

      On the first day 1 leaflet to each of n houses.
      On the second day 2 leaflets to each of(n / 2) houses.

      On the 14th day n leaflets to 1 house.

      So we are looking for n, an even number, greater than 300, with exactly 14 divisors.

      We can then calculate the total number of houses visited by determining the sum of the divisors.

      This Python program runs in 45ms.

      Run: [ @repl.it ]

      from enigma import (irange, inf, divisors, printf)
      
      for n in irange(302, inf, step=2):
        ds = divisors(n)
        if len(ds) == 14:
          t = sum(ds)
          printf("t={t} [n={n} ds={ds}]")
          break
      

      Solution: Delivery was made to 762 houses.

      And there were 14×320 = 4480 leaflets to deliver.


      We can find numbers with exactly k divisors by considering factorisations of k (see: Enigma 1226).

      In this case, 14 = 1 × 14 = 2 × 7, so any number of the form:

      n = p^13
      n = p^1 × q^6

      for primes, p and q, will have exactly 14 divisors. [*]

      And we know one of the primes is 2.

      2^13 = 8192, which is too big.

      So we know: n = p × q^6.

      If p = 2, then the smallest possible values for n is 2 × 3^6 = 1458, which is also too big.

      So q = 2, and possible values for n are:

      3 × 2^6 = 192
      5 × 2^6 = 320
      7 × 2^6 = 448

      The only sensible candidate is: n = 320, the 14 divisors are:

      1, 2, 4, 5, 8, 10, 16, 20, 32, 40, 64, 80, 160, 320

      and their sum is 762.

      The sum can also be worked out directly from the prime factorisation of n:

      If σ(n) = the sum of the divisors of n, we have:

      σ(5 × 2^6) = σ(5) × σ(2^6)
      = (1 + 5) × (2^7 − 1)
      = 6 × 127
      = 762


      [*] In the published solution it is claimed that only numbers of the form (p^6 × q) have exactly 14 divisors.

      Like

    • Frits's avatar

      Frits 10:28 pm on 18 October 2020 Permalink | Reply

      Only to check if I could solve it with [[SubstitutedExpression]] without using external functions.

      from enigma import  SubstitutedExpression
      
      p = SubstitutedExpression([
          # Consider first 7 divisors (1, 2, .....)
          "XYZ > 300",
          "XYZ % 2 == 0",   # as specified
          "XYZ % IJ == 0",  # 7th divisor
          "XYZ % GH == 0",  # 6th divisor
          "XYZ % EF == 0",  # 5th divisor
          "XYZ % CD == 0",  # 4th divisor
          "XYZ % AB == 0",  # 3rd divisor
          # put them in order
          "GH < IJ",
          "EF < GH",
          "CD < EF", 
          "AB < CD", 
          "2 < AB ",
          # limit 7th divisor 
          "IJ < (XYZ // IJ)",
          # make sure there are only 7 divisors before 8th divisor
          "sum(1 for x in range(1, (XYZ // IJ)) if XYZ % x == 0) == 7",
          ],
          verbose=0,
          d2i={k:"X" for k in [x for x in range(0,10) if x != 3]},
          #accumulate=min,
          answer="(1, 2, AB, CD, EF, GH, IJ, XYZ)",
          distinct="",
          #reorder=0
      )
      
      # Solve and print answers
      
      for (_, ans) in p.solve():
       hs = 0
       # sum the divisors
       for i in range(0,7):
         hs += ans[i] + ans[7]//ans[i]
       print(f"{hs} houses") 
      

      Like

  • Unknown's avatar

    Jim Randell 4:44 pm on 16 October 2020 Permalink | Reply
    Tags:   

    Teaser 3030: Pot success 

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

    In snooker, pot success (PS) is the percentage of how many pot attempts have been successful in that match (e.g. 19 pots from 40 attempts gives a PS of 47.5%). In a recent match, my PS was a whole number at one point. I then potted several balls in a row to finish a frame, after which my improved PS was still a whole number. At the beginning of the next frame, I potted the same number of balls in a row, and my PS was still a whole number. I missed the next pot, my last shot in the match, and, remarkably, my PS was still a whole number.

    If I told you how many balls I potted during the match, you would be able to work out those various whole-number PS values.

    How many balls did I pot?

    [teaser3030]

     
    • Jim Randell's avatar

      Jim Randell 5:03 pm on 16 October 2020 Permalink | Reply

      This Python program runs in 46ms.

      Run: [ @replit ]

      from enigma import (irange, div, peek, printf)
      
      # calculate PS
      PS = lambda p, t: div(100 * p, t)
      
      # generate scenarios with p balls potted (at the end)
      def generate(p):
        # consider total number of attempts (at the end)
        for t in irange(p + 1, 100):
          # final PS (ps4)
          ps4 = PS(p, t)
          if ps4 is None: continue
          # last shot was a miss, and the PS before it (ps3)
          ps3 = PS(p, t - 1)
          if ps3 is None: continue
          # before that 2 lots of k balls were potted
          for k in irange(2, (p - 1) // 2):
            ps2 = PS(p - k, t - 1 - k)
            if ps2 is None: continue
            ps1 = PS(p - 2 * k, t - 1 - 2 * k)
            if ps1 is None or not (ps1 < ps2): continue
            # return (t, k, PS) values
            yield (t, k, (ps1, ps2, ps3, ps4))
      
      # consider number of balls potted (at the end)
      for p in irange(5, 99):
        # accumulate ps values
        s = set(ps for (t, k, ps) in generate(p))
        # is there only one?
        if len(s) == 1:
          # output solution
          printf("balls potted = {p} -> PS = {ps}", ps=peek(s))
      

      Solution: You potted 13 balls.

      This corresponds to the following scenario:

      13 balls potted (2 runs of 5), PS = (3/15 = 20%; 8/20 = 40%; 13/25 = 52%; 13/26 = 50%)

      The other possible scenarios are:

      12 balls potted (2 runs of 5), PS = (2/5 = 40%; 7/10 = 70%; 12/15 = 80%; 12/16 = 75%)
      12 balls potted (2 runs of 4), PS = (4/16 = 25%; 8/20 = 40%; 12/24 = 50%; 12/25 = 48%)

      57 balls potted (2 runs of 15), PS = (27/45 = 60%; 42/60 = 70%; 57/75 = 76%; 57/76 = 75%)
      57 balls potted (2 runs of 25), PS = (7/28 = 25%; 32/50 = 64%; 57/75 = 76%; 57/76 = 75%)

      It seems plausible that these could correspond to a snooker match (it is possible to pot 10 reds + 10 colours + 6 colours = 26 balls in one frame, and we know at least 2 frames are involved). Although if just one of them did not, then the other scenario would provide a further solution.

      The program ensures that the PS values we are considering are non-zero, but allowing the initial PS to be zero gives a further solution:

      18 balls potted (2 runs of 9), PS = (0/6 = 0%; 9/15 = 60%; 18/24 = 75%; 18/25 = 72%)

      Like

    • Frits's avatar

      Frits 11:06 am on 17 October 2020 Permalink | Reply

      @Jim, I think you should also consider p=4 as ps1 might be zero and zero also is a whole number .

      Also if t would be (p + 1) then ps1, ps2 and ps3 would be 100 and ps2 would not be higher than ps1.
      If you start t from (p+ 2) you don’t have to code the ps1 < ps2 check as it will always be the case.

      Of course there always is a balance between efficiency and seeing right away that the code solves the requirements.

      Like

      • Jim Randell's avatar

        Jim Randell 11:50 am on 17 October 2020 Permalink | Reply

        @Frits: Unfortunately the term “whole number” isn’t precise. It can be used to mean the integers, the non-negative integers, or just the positive integers.

        For this puzzle I took it to be the positive integers (which gets around a possible problem when considering PS values of 0), so I didn’t have to consider PS values of zero. Which is probably what the setter intended, as I’m sure the puzzle is meant to have a unique solution.

        I also wanted to make the [[ ps1 < ps2 ]] condition explicit in the code (as without it there would be further solutions – which can be seen by removing the test).

        Like

    • Frits's avatar

      Frits 1:28 pm on 17 October 2020 Permalink | Reply

      Assuming whole numbers are in the range (1, …, 100) and with the “improved PS” check (which is not needed if we use “AB < CD").

      If we include zero as a whole number there are 2 solutions.

      from enigma import  SubstitutedExpression, multiset
      
      r = multiset()
      
      p = SubstitutedExpression([
          # we start with AB balls potted from CD attempts
          "AB <= CD",
          "CD > 0",
          # "I then potted several balls (XY > 1) in a row"
          # Assume CD + 2 * XY + 1 is roughly less than 100
          "XY > 1",
          "XY < (99 - CD) // 2",
           # the 4 pot successes being whole numbers (meaning 1..100 ???)
          "div(100 * AB, CD) > 0",
          "div(100 * (AB + XY), CD + XY) > 0",
          "div(100 * (AB + 2 * XY), CD + 2 * XY) > 0",
          "div(100 * (AB + 2 * XY), CD + 2 * XY + 1) > 0",
          # improved PS
          "div(100 * (AB + XY), CD + XY) > div(100 * AB, CD)",
          ],
          verbose=0,
          d2i={},
          answer="AB + 2 * XY, AB, CD, XY, " 
                 "(100 * AB / CD, 100 * (AB + XY) / (CD + XY),"
                 " 100 * (AB + 2 * XY) / (CD + 2 * XY),"
                 " 100 * (AB + 2 * XY) / (CD + 2 * XY + 1))",
          distinct="",
      )
      
      # Solve and print answers
      
      print("potted   start  several     pot success" )
      for (_, ans) in p.solve():
        print(f" {ans[0]:>2}     {str(ans[1:3]):<9}  {str(ans[3]):<3}  {ans[4]}")
        r.add(ans[0])
      
      for (k, v) in r.items():
        if v == 1:
          print(f"\n{k} balls potted")
      

      Like

  • Unknown's avatar

    Jim Randell 4:21 pm on 15 October 2020 Permalink | Reply
    Tags:   

    Teaser 2758: Square dance 

    From The Sunday Times, 2nd August 2015 [link]

    Dancers numbered from 1 to 9 were about to perform a square dance: five were dressed in red and the rest in blue. They stood in a 3-by-3 array with all three dancers in the first row wearing red and all three in another row wearing blue. Their numbers formed a magic square (i.e. the sum of the three numbers in any row, column or diagonal was the same). One of the dancers in red looked around and noted that the sum of the numbers of the four other dancers in red was the same as the sum of the numbers of the four dancers in blue.

    One of the dancers in blue was number 8, what were the numbers of the other three dancers in blue?

    [teaser2758]

     
    • Jim Randell's avatar

      Jim Randell 4:22 pm on 15 October 2020 Permalink | Reply

      The Magic Square uses the numbers 1 – 9 so we know that the magic sum is 15, and the central square is 5. (See: Enigma 1680, Enigma 1080).

      We can then choose two numbers (other than 5 and 8) for the reds on the top row, and that lets us work out all the remaining squares.

      This Python program runs in 46ms.

      Run: [ @replit ]

      # consider the square:
      #
      #   a b c
      #   d e f
      #   g h i
      #
      # where each latter stands for a number from 1 to 9
      #
      # the magic sum is: s = (1 + 2 + ... + 9) / 3 = 15
      #
      # and e = s / 3 = 5
      
      from enigma import (irange, div, subsets, printf)
      
      # the numbers
      numbers = set(irange(1, 9))
      
      # total sum, magic sum, centre square
      t = sum(numbers)
      s = div(t, 3)
      e = div(s, 3)
      
      # generate magic squares
      # 8 is a blue, so can't be in the first row
      for (a, b) in subsets(numbers.difference((e, 8)), size=2, select="P"):
        c = s - (a + b)
        if c in {8, a, b, e}: continue
      
        # fill out the rest of the square
        i = s - (a + e)
        f = s - (c + i)
        d = s - (e + f)
        g = s - (a + d)
        h = s - (g + i)
      
        # check it uses the numbers 1 - 9
        if numbers.difference({a, b, c, d, e, f, g, h, i}): continue
      
        # choose the row with mixed colours
        for row in ((d, e, f), (g, h, i)):
          # and choose two from that row to be red
          for red in subsets(row, size=2):
            if 8 in red: continue
            # so the reds are...
            reds = set((a, b, c) + red)
            # and if the observant red dancer is x...
            # we need 2(sum(reds) - x) = (t - x)
            x = 2 * sum(reds) - t
            if x not in reds: continue
            # so the blues are...
            blues = numbers.difference(reds)
            printf("{a} {b} {c} / {d} {e} {f} / {g} {h} {i}, reds={reds}, x={x}, blues={blues}")
      

      Solution: The other dancers wearing blue had numbers 1, 3 and 6.

      The dancers are arranged like this:

      (or mirrored about a vertical axis).

      Dancer number 9 notices that the sum of the other red dancers (2 + 4 + 7 + 5 = 18) is the same as the sum of the blue dancers (1 + 3 + 6 + 8 = 18).

      Like

    • Frits's avatar

      Frits 12:35 pm on 16 October 2020 Permalink | Reply

      # consider the square:
      #
      #   A B C
      #   D E F
      #   G H I
      #
      # where each latter stands for a number from 1 to 9
      #
      # the magic sum is: MS = (1 + 2 + ... + 9) / 3 = 15
      #
      # and E = MS / 3 = 5
      
      from enigma import  SubstitutedExpression, irange, is_prime
      
      p = SubstitutedExpression([
          # magic square
          "A+B+C == MS",
          "D+E+F == MS",
          "G+H+I == MS",
          "A+D+G == MS",
          "B+E+H == MS",
          "C+F+I == MS",
          "A+E+I == MS",
          "C+E+G == MS",
          # for red : pick 2 out of 1st row, 2 out of middle row
          # for blue: pick 1 out of middle row and 3 out of 3rd row 
          "a*A + b*B + c*C + (1-d)*D + (1-e)*E + (1-f)*F == G+H+I + d*D + e*E + f*F",
          # pick 1 out of middle row
          "d + e + f == 1",
          # pick 2 out of first row
          "a + b + c == 2",
       
          ],
          verbose=0,
          symbols="ABCDEFGHIMSabcdef",
          d2i=dict([(0, "ABCDEFGHI")]  + [(8, "ABCabcdef")] + [(k, "abcdef") for k in {2,3,4,5,6,7,9}]),
          answer="A,B,C,D,E,F,G,H,I, a, b, c, d, e, f, d*D + e*E + f*F  ",
          distinct=("ABCDEFGHI"),
          digits=range(0, 10)
      )
      
      # Print answers
      cnt = 0
      for (_, ans) in p.solve():
         sol = list(ans[6:9]) + [ans[15]]
         sol.remove(8) 
         print(f"Answer = {sol}\n")
         print(f"{ans[:3]} a,b,c {ans[9:12]}\n{ans[3:6]} d,e,f {ans[12:15]}\n{ans[6:9]}\n")
         
         
      # Answer = [6, 1, 3]
      # 
      # (2, 9, 4) a,b,c (1, 0, 1)
      # (7, 5, 3) d,e,f (0, 0, 1)
      # (6, 1, 8)
      # 
      # Answer = [1, 6, 3]
      # 
      # (4, 9, 2) a,b,c (1, 0, 1)
      # (3, 5, 7) d,e,f (1, 0, 0)
      # (8, 1, 6)
      

      Like

      • Jim Randell's avatar

        Jim Randell 11:58 am on 17 October 2020 Permalink | Reply

        @Frits: How do we know the mixed row is the second row and not the bottom row?

        Like

    • Frits's avatar

      Frits 2:57 pm on 17 October 2020 Permalink | Reply

      You are right, mixed row can be 2nd or 3rd row.

      # consider the square:
      #
      #   A B C 
      #   D E F    magic sum A + B + C
      #   G H I
      #
      # where each latter stands for a number from 1 to 9
      #
      # a, ..., i are bits
      
      from enigma import  SubstitutedExpression, irange, is_prime
      
      p = SubstitutedExpression([
          # magic square with magic sum A + B + C
          "D + E + F == A + B + C",
          "G + H + I == A + B + C",
          "A + D + G == A + B + C",
          "B + E + H == A + B + C",
          "C + F + I == A + B + C",
          "A + E + I == A + B + C",
          "C + E + G == A + B + C",
         
          # pick 4 out of 2nd row or 3rd row, including a whole row
          "d + e + f + g + h + i == 4",
          "d + e + f == 3 or g + h + i == 3",
          # pick 2 out of first row for 
          "a + b + c == 2",
          
          # for blue: pick 4 out of 2nd row and 3rd row, including a whole row 
          # for red : pick 2 out of 1st row, 2 out of 2nd row or 3rd row
          # (which is same as:  pick 2 out of 1st row plus
          #  2 times magic sum - the 4 items picked for blue)
          "(a+2)*A + (b+2)*B + (c+2)*C == 2 * (d*D + e*E + f*F + g*G + h*H + i*I)",
          
          # One of the dancers in blue was number 8
          "sum([d*D == 8, e*E == 8, f*F == 8, g*G == 8, h*H == 8, i*I == 8]) == 1",
          ],
          verbose=0,
          symbols="ABCDEFGHIMSabcdefghi",
          d2i=dict([(0, "ABCDEFGHI")]  + 
                   [(8, "ABCabcdefghi")] + 
                   [(k, "abcdefghi") for k in {2,3,4,5,6,7,9}]),
          answer="A, B, C, D, E, F, G, H, I, a, b, c, d, e, f, g, h, i, \
                  d*D + e*E + f*F + g*G + h*H + i*I",
          distinct=("ABCDEFGHI"),
          reorder=0
      )
      
      # Print answers
      for (_, ans) in p.solve():
        blue = [y[0] for (x,y) in enumerate(zip(ans[3:9], ans[12:18])) 
                if y[1] == 1 and y[0] != 8]
        print(f"Answer = {blue}\n")
        print(f"{ans[:3]} a,b,c: {ans[9:12]}\n{ans[3:6]} d,e,f: {ans[12:15]}\n"
              f"{ans[6:9]} g,h,i: {ans[15:18]}\n")
         
         
      # Answer = [3, 1, 6]
      #
      # (4, 9, 2) a,b,c: (1, 0, 1)
      # (3, 5, 7) d,e,f: (1, 0, 0)
      # (8, 1, 6) g,h,i: (1, 1, 1)
      #
      # Answer = [3, 6, 1]
      #
      # (2, 9, 4) a,b,c: (1, 0, 1)
      # (7, 5, 3) d,e,f: (0, 0, 1)
      # (6, 1, 8) g,h,i: (1, 1, 1)
      

      Like

      • Frits's avatar

        Frits 3:29 pm on 17 October 2020 Permalink | Reply

        @Jim,

        Instead of the sum() equation I could have used any() where it not for the fact that “any” contains an “a” which is a variable. Is there an easy way to use normal Python functions without having to worry about lowercase variables?

        It would be nice if the SubstitutedExpression solver could ignore consecutive lowercase characters as variables.

        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