Tagged: by: C Higgins Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 11:19 am on 26 December 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2465 : [Christmas bonus] 

    From The Sunday Times, 20th December 2009 [link]

    Pat’s company employs six staff: they joined on six consecutive Christmases and have stayed ever since. This Christmas, he is giving them each a bonus in the form of vouchers, each worth a whole number of pounds, with one red voucher for each year of service for a man and one green voucher (worth £1 more) for each year of service for a woman. The value of all the year’s vouchers is £500. Ms Jones joined more than two years after Mr Smith, but their bonuses have the same total value.

    What is that common value?

    This puzzle was originally published with no title.

    [teaser2465]

     
    • Jim Randell's avatar

      Jim Randell 11:20 am on 26 December 2025 Permalink | Reply

      This Python program runs in 70ms. (Internal runtime is 1.7ms).

      from enigma import (irange, inf, subsets, div, printf)
      
      # choose male (= 0) and female (= 1) values (longest serving -> shortest serving)
      for vs in subsets([0, 1], size=6, select='M'):
        # find possible (i, j) values for Mr S and Ms J
        ijs = list((i, j) for (i, j) in subsets(irange(6), size=2) if j >= i + 3 and vs[i] == 0 and vs[j] == 1)
        if not ijs: continue
      
        # consider increasing values for the male voucher
        for x in irange(1, inf):
          # calculate the years of service for the longest serving employee (>= 6)
          n = 500 + sum(i * (x + v) for (i, v) in enumerate(vs))
          d = 6 * x + sum(vs)
          if n < 6 * d: break
          y = div(n, d)
          if y is None: continue
      
          # calculate actual gift amounts
          gs = list((y - i) * (x + v) for (i, v) in enumerate(vs))
      
          # find shared values for Mr S and Ms J
          for (i, j) in ijs:
            if gs[i] == gs[j]:
              # output solution
              printf("vs={vs}, y={y}, x={x}, gs={gs}, i={i}, j={j}")
      

      Solution: The common value is £80.

      The red vouchers (male) are worth £4 each. The green vouchers (female) are worth £5 each.

      The amounts given to the employees are:

      21 years, female → £105
      20 years, male → £80 (Mr Smith)
      19 years, female → £95
      18 years, male → £72
      17 years, male → £68
      16 years, female → £80 (Ms Jones)
      total = £500

      Like

    • Ruud's avatar

      Ruud 1:17 pm on 26 December 2025 Permalink | Reply

      Extreme brute force:

      import peek
      import itertools
      import istr
      
      for n in range(1, 30):
          for bonus in range(100):
              for is_womans in itertools.product((False, True), repeat=6):
                  if sum(k * (bonus + is_woman) for k, is_woman in enumerate(is_womans, n)) == 500:
                      for jones, smith in itertools.product(range(6), repeat=2):
                          if not is_womans[jones] and is_womans[smith] and jones > smith + 2 and (jones + n) * bonus == (smith + n) * (bonus + 1):
                              peek((jones + n) * bonus, (smith + n) * (bonus + 1))
      

      Like

  • Unknown's avatar

    Jim Randell 11:00 am on 7 November 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2480: [I saw three ships] 

    From The Sunday Times, 4th April 2010 [link]

    At the start of the computer war game Midway, the three Japanese aircraft carriers Akagi, Kaga and Soryu are in positions 120 kilometres from each other. At a signal, all three ships set sail at the same speed. Akagi always sails directly towards Kaga until they meet. Similarly, Kaga always sails towards Soryu, whilst Soryu always sails towards Akagi.

    How far does Akagi sail before she meets Kaga?

    This puzzle was originally published with no title.

    [teaser2480]

     
    • Jim Randell's avatar

      Jim Randell 11:01 am on 7 November 2025 Permalink | Reply

      See my comments on: Puzzle #13.

      If the objects start at the vertices of a regular n-gon with sides L, then the distance and they each travel before they meet is given by:

      d = L / (1 – cos(2𝛑/n))

      In this puzzle we have n = 3 and L = 120 km.

      d = 120 / (1 − (−0.5))
      d = 120 / 1.5
      d = 80 km

      Solution: The ships meet after travelling 80 km.

      Like

  • Unknown's avatar

    Jim Randell 8:10 am on 17 October 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2493: [Driving competition] 

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

    Pat won the driving competition. The course was a whole number of miles long and he completed the course in a whole number of minutes (less than two hours). His speed was a two-digit number of miles per hour. The time taken to complete the course was 17 minutes less than the time he would have taken to drive 17 miles less than the distance he would have gone if he had driven for 17 minutes less than the time he would have taken to complete the course at two-thirds of his speed.

    How long was the course?

    This puzzle was originally published with no title.

    [teaser2493]

     
    • Jim Randell's avatar

      Jim Randell 8:11 am on 17 October 2025 Permalink | Reply

      It is straightforward to consider the possible velocities (2-digits) and course times (< 2 hours).

      This Python program runs in 85ms. (Internal runtime is 11.4ms).

      from enigma import (Rational, irange, cproduct, printf)
      
      Q = Rational()
      
      # speed was a 2-digit mph, time was a whole number of minutes < 120
      for (v, t) in cproduct([irange(10, 99), irange(1, 119)]):
      
        # the course was a whole number of miles long
        d = Q(v * t, 60)
        if d % 1 != 0: continue
      
        # t1 = time taken at 2/3 of the speed
        t1 = Q(3, 2) * t
      
        # d1 = distance travelled in time (t1 - 17)
        if not (t1 > 17): continue
        d1 = Q(v * (t1 - 17), 60)
      
        # t2 = time to drive (d1 - 17)
        if not (d1 > 17): continue
        t2 = Q(d1 - 17, v) * 60
      
        # time take to complete the course was (t2 - 17)
        if not (t2 - 17 == t): continue
      
        # output solution
        printf("v={v} t={t} d={d} [t1={t1} d1={d1} t2={t2}]")
      

      Solution: The course was 102 miles long.

      Pat’s speed was 60 mph (i.e. 1 mile per minute), so he took 102 minutes to complete the course.

      If he had driven at 2/3 of his actual speed (i.e. 40 mph), then it would have taken 153 minutes to complete the course.

      And in 153 − 17 = 136 minutes, driving at 60 mph, he would have been able to complete 136 miles.

      The time taken to drive 136 − 17 = 119 miles at 60 mph is 119 minutes.

      And 119 − 17 = 102 minutes is the time actually taken to complete the course.


      With more analysis we can derive the following equations for t and d:

      t = 68 + 2040/v
      d = 34 + 17v/15

      from which we see v must be a multiple of 15.

      This allows a manual solution by considering just 6 cases:

      v = 15 → t = 204
      v = 30 → t = 136
      v = 45 → t = 113.333…
      v = 60 → t = 102
      v = 75 → t = 95.2
      v = 90 → t = 90.666…

      And only one of these gives an integer in the required range (< 120).

      Hence:

      d = 34 + 17×60/15 = 102

      Or, programatically:

      from enigma import (irange, div, printf)
      
      # v is a 2-digit multiple of 15
      for v in irange(15, 99, step=15):
      
        # calculate time taken (< 120)
        t = div(2040, v)
        if t is None: continue
        t += 68
        if not (t < 120): continue
      
        # calculate course distance
        d = div(17 * v, 15) + 34
      
        # output solution
        printf("v={v} t={t} d={d}")
      

      Like

  • Unknown's avatar

    Jim Randell 1:57 pm on 16 September 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2513: [Golf ball stacking] 

    From The Sunday Times, 21st November 2010 [link] [link]

    At the golf ball factory 10,000 balls are stored in a stack for some weeks to harden. The stack has several layers, the top layer being a single row of balls. The second layer has two rows, each with one ball more than the top row. The third layer has three rows each with two balls more than the top row, and so on.

    Of the several shapes of stack possible, the one used takes up the smallest floor area.

    How many balls are on the bottom layer?

    This puzzle was originally published with no title.

    [teaser2513]

     
    • Jim Randell's avatar

      Jim Randell 1:57 pm on 16 September 2025 Permalink | Reply

      If we consider a stack that is k layers deep and has a line of n balls along the top.

      First we see that the number of balls in the base layer is: A = k(n + k − 1).

      And we can chop a slice one ball thick from the end to get tri(k) balls, and we can do this n times (once for each ball on the top layer).

      The the number of “extra” ball in each layer is: 1×0, 2×1, 3×2, 4×3, …, k×(k − 1).

      So in an arrangement with k layers we can calculate a value of n that would give 10,000 balls in total, and if we get an integer out of the calculation, then this is a viable arrangement.

      This Python program runs in 68ms. (Internal runtime is 56µs).

      from enigma import (Accumulator, irange, inf, tri, printf)
      
      # total number of balls
      T = 10000
      
      # collect minimal base
      sol = Accumulator(fn=min)
      
      # consider increasing numbers of layers, k
      x = 0  # extra balls to complete the pile
      for k in irange(1, inf):
        # add in the extra balls
        x += k * (k - 1)
        # how many repeats of tri(k) can we make?
        (n, r) = divmod(T - x, tri(k))
        if n < 1: break
        if r != 0: continue
        # calculate area of base
        A = k * (n + k - 1)
        # output configuration
        printf("[{k} layers -> n={n} base={A}]")
        sol.accumulate(A)
      
      # output solution
      printf("minimal base = {sol.value}")
      

      Solution: There are 984 balls in the bottom layer.

      There are 24 layers:

      1: 1×18
      2: 2×19
      3: 3×20

      23: 23×40
      24: 24×41 = 984 balls

      We can check the total number of balls is 10,000, by adding the number of balls in each layer:

      >>> dot(irange(1, 24), irange(18, 41))
      10000
      

      We can determine a formula for the total number of balls T[n, k], starting with a line of n balls and constructing k layers:

      T[n, k] = n × tri(k) + (0×1 + 1×2 + … + (k − 1)×k)
      T[n, k] = n × k(k + 1)/2 + (k − 1)k(k + 1)/3
      T[n, k] = k(k + 1)(3n + 2k − 2)/6

      Like

  • Unknown's avatar

    Jim Randell 4:23 pm on 28 August 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2486: [Meet at the station] 

    From The Sunday Times, 16th May 2010 [link] [link]

    Pat drove to the station at his regular speed to meet his brothers, John and Joe, whose train was due at 6pm. But John had caught an earlier train and, arriving half an hour early, decided to walk home at a steady pace. Pat waved as he passed John (a whole number of minutes before 6pm), but drove on to the station. Pat collected Joe on time and, a whole number of minutes later, they set off for home. Pat and Joe overtook John 13 minutes after Pat had waved to him.

    At what time did Pat and Joe overtake John?

    This puzzle was originally published with no title.

    [teaser2486]

     
    • Jim Randell's avatar

      Jim Randell 4:23 pm on 28 August 2025 Permalink | Reply

      Suppose John walks at a speed of 1 unit per minute.

      At a minutes before 6pm Pat passes John. At this time John has walked a distance of (30 − a) units from the station. And in 13 minutes time John will have walked an additional 13 distance units, so will be a distance (43 − a) units from the station.

      After Pat passes John he travels the remaining distance (30 − a) to the station, in a time of a minutes (to arrive at 6pm).

      So his velocity is: (30 − a)/a.

      If the Pat and John leave the station at b minutes after 6pm, then the time taken to catch up with John is:

      c = (43 − a) / (30 − a)/a

      and:

      a + b + c = 13

      (where a, b, c are all positive integer values).

      So we can consider possible integer values for a and calculate the corresponding values for c and b.

      This can be done manually or programatically.

      This Python program runs in 75ms. (Internal runtime is 33µs).

      from enigma import (irange, div, printf)
      
      for a in irange(1, 5):
        c = div((43 - a) * a, (30 - a))
        if c is None: continue
        b = 13 - (a + c)
        if b > 0:
          printf("a={a} -> c={c} b={b}")
      

      Solution: Pat and Joe caught up with John at 6:09 pm.

      Pat initially passed John at 5:56 pm, before arriving at the station 4 minutes later (at 6 pm) to pick up Joe.

      They left the station at 6:03 pm and 6 minutes later (at 6:09 pm) passed John again.

      Like

  • Unknown's avatar

    Jim Randell 8:10 am on 29 July 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2502: [Swimming lengths] 

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

    John and Pat were swimming at the pool. John started at the deep end at the same time as Pat started at the shallow end, each swimming lengths at their own steady speed. They passed each other on their first lengths and then passed each other again on their second lengths. It turned out that the length of the pool was a multiple of the distance between those first two passing points. John got out of the water after completing 48 lengths. At that moment Pat had also completed a whole number of lengths.

    How many?

    This puzzle was originally published with no title.

    [teaser2502]

     
    • Jim Randell's avatar

      Jim Randell 8:11 am on 29 July 2025 Permalink | Reply

      (See also: Enigma 799).

      Suppose J takes 1 unit of time to swim a length, and P takes p units of time to swim a length.

      And after J has completed 48 lengths, P has completed n = 48/p lengths, which is a whole number.

      For them to pass on their first and then second lengths we have:

      p > 1/2
      p < 2

      Hence:

      24 < n < 96

      For a given n value, we can calculate the crossing positions using a time/distance graph:

      And then look for values where the distance between the positions divides exactly into the pool length.

      This Python program runs in 74ms. (Internal runtime is 4.6ms).

      from enigma import (irange, rdiv, line_intersect, catch, printf)
      
      # positions for J after lengths 0, 1, 2; P after length 0
      (J0, J1, J2, P0) = ((0, 1), (1, 0), (2, 1), (0, 0))
      
      # consider number of lengths completed by P at t=48
      for n in irange(25, 95):
        p = rdiv(48, n)
        # positions for P after lengths 1, 2
        (P1, P2) = ((p, 1), (2*p, 0))
      
        # calculate crossing position during first two lengths
        i1 = catch(line_intersect, J0, J1, P0, P1, internal=1, div=rdiv)
        i2 = catch(line_intersect, J1, J2, P1, P2, internal=1, div=rdiv)
        if i1 is None or i2 is None: continue
        ((t1, d1), (t2, d2)) = (i1.pt, i2.pt)
      
        # pool length is an exact multiple of the distance between the positions
        if d1 == d2: continue
        k = rdiv(1, abs(d1 - d2))
        if k % 1 != 0: continue
      
        # output solution
        printf("n={n} [p={p} k={k}]")
      

      Solution: Pat had completed 80 lengths.

      So Pat completes a length in 48/80 = 3/5 the time it takes John. And the distance between the crossing positions is exactly 1/2 the length of the pool.

      We have:

      (t1, d1) = (3/8, 5/8)
      (t2, d2) = (9/8, 1/8)

      Like

    • Frits's avatar

      Frits 2:27 pm on 30 July 2025 Permalink | Reply

      # L = length of the pool
      
      # 1st passing
      # 0     j1*L                  p1*L            L   j1 + p1 = 1
      # |---------------+---------------------------|
      
      # speed ratio r1 = p1 / j1
      
      # 2nd passing
      # 0                                           L  
      # |-------------------------------------------|
      #                  j2*L                  p2*L   j2 + p2 = 1
      # |------------------------------------+------|
      
      # speed ratio Pat / John
      # r = n / 48
      # j1 = 1 / (r + 1)
      # 2nd passing: distance that P swam = r * (distance that J swam)
      # L + j2.L = r.(L + p2.L) or 1 + j2 = r.(2 - j2) or j2 = (2.r - 1) / (r + 1)
      # j2 = (2 * r - 1) / (r + 1)
      
      #  24 < n < 96
      # n must be a multiple of 4 as denominator in formula below is a multiple of 4
      for n in range(28, 96, 4):
        # distance between those first two passing points > 0
        if n == 48: continue
        # L = multiple of the distance passing points = m * (abs(j1 - j2) * L)
        # if (r + 1) % (2 * r - 2): continue  
        if (n + 48) % (2 * n - 96): continue  
        print("answer:", n)
      

      Like

      • Frits's avatar

        Frits 3:05 pm on 30 July 2025 Permalink | Reply

        “n” can even be proven to be a multiple of 16.

        Like

  • Unknown's avatar

    Jim Randell 9:43 am on 24 July 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2477: [Shamrock in a box] 

    From The Sunday Times, 14th March 2010 [link]

    At the St Patrick’s Day Feis, Pat exhibits his painting “Shamrock in a Box”. This shows a square containing two touching circles of the same diameter (less than a metre). One circle also touches two adjacent sides of the square; the other touches the remaining two sides. For the middle leaf of the shamrock, Pat drew a third circle in the square: it touches each of the other circles, is the largest that will fit in the available space, and its radius is almost exactly a whole number of centimetres, that number being the same as the radius of the larger circles, but with the two digits in reverse order.

    What is the radius of the larger circles?

    This puzzle was originally published with no title.

    [teaser2477]

     
    • Jim Randell's avatar

      Jim Randell 9:43 am on 24 July 2025 Permalink | Reply

      The layout looks like this:

      Suppose the large circles have a radius 1.

      The semi-diagonal of the square is: 1 + √2.

      And if the smaller circle has a radius f (where 0 < f < 1), then we can also write the length of the semi-diagonal as: f√2 + h.

      Where: f + 1 = hypot(h, 1).

      And so we have two expressions for h:

      h = 1 + √2 − f√2
      h^2 = (f + 1)^2 − 1

      We can solve these equations to find the value of f, and then consider the actual radius of the large circle, R (a 2-digit integer, less than 50). And then calculate the radius of the smaller circle r = f . R.

      And we are look for cases where r is very close to the reverse of R.

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

      from enigma import (Polynomial, sqrt, sq, irange, nrev, printf)
      
      r2 = sqrt(2)  # "root 2"
      
      # we have 2 expressions for h^2, which we can express as polynomials
      p1 = sq(Polynomial([1 + r2, -r2]))  # h^2 = (1 + sqrt(2) - f.sqrt(2))^2
      p2 = sq(Polynomial([1, 1])) - 1  # h^2 = (f + 1)^2 - 1
      
      # find real roots of p(f) = p1 - p2
      for f in (p1 - p2).roots(domain='F'):
        if not (0 < f < 1): continue
        printf("[f = {f}]")
      
        # consider 2-digit numbers for R
        for R in irange(10, 49):
          # the reverse should be a smaller 2-digit number
          rR = nrev(R)
          if rR < 10 or rR >= R: continue
      
          # check size of smaller circle is close to rev(R)
          r = f * R
          if abs(r - rR) < 0.05:
            printf("R={R} -> r={r}")
      

      Solution: The larger circles have a radius of 32 cm.

      And the smaller circles have a radius of 22.998 cm, which is very close to 23 cm.

      f is a root of the following quadratic equation:

      f^2 − (6 + 2√2)f + (3 + 2√2) = 0

      And this allows us to give a formula for f:

      f = 3 + √2 − 2√(2 + √2)
      f = 0.718695432327948…

      Hence:

      R = 32

      r = f . R = 22.99825383449434…

      Like

    • Frits's avatar

      Frits 12:49 pm on 24 July 2025 Permalink | Reply

      Using Jim’s formulas.

      # h = 1 + sqrt(2) - f.sqrt(2)
      # h^2 = (f + 1)^2 - 1 
      h1 = lambda f: 1 + (1 - f) * 2**.5 
      h2 = lambda f: ((f + 1)**2 - 1)**.5
      
      sols = set()
      for a in range(1, 5):
        for b in range(1, a):
          R, r = 10 * a + b, 10 * b + a
          f = r / R
          # store difference between 2 calculations for "h"
          sols.add((abs(h1(f) - h2(f)), R))
      
      if (mn := min(sols))[0] < 0.01:
        print("answer:", mn[-1])
      

      Like

  • Unknown's avatar

    Jim Randell 10:12 am on 11 July 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2475: [Stopped clock] 

    From The Sunday Times, 28th February 2010 [link]

    “I see that our long case has stopped”, said Watson, who had been reading Holmes’s monograph on chronometers. “Is there anything significant about the time?” Holmes replied: “Apart from the obvious facts that just one hand is precisely on 12, and clockwise the hands are in the order “second” , “minute”, “hour”, I see that the time read as a 3- or 4-digit number is the product of two whole numbers, the larger of which is the number of minutes clockwise from the minute hand to the hour hand”.

    At what time did the clock stop?

    This puzzle was originally published with no title.

    [teaser2473]

     
    • Jim Randell's avatar

      Jim Randell 10:12 am on 11 July 2025 Permalink | Reply

      If one of the hands is on the 12, then the clock must be showing an exact number of minutes, and so the second hand must be on 12.

      Then as we go clockwise from the second hand we encounter the minute hand (which is on an exact minute marking), and then the hour hand (which must also be on an exact minute marking, so the number of minutes must be a multiple of 12).

      This Python program considers possible hours and minutes, calculates the number of minute divisions the hour hand is ahead of the minute hand and then checks that this divides into the time (read as a 3- or 4-digit number) to give a smaller number.

      It runs in 60ms. (Internal runtime is 46µs).

      from enigma import (irange, cproduct, divc, div, printf)
      
      # possible hours and minutes (must be a multiple of 12)
      for (h, m) in cproduct([irange(1, 11), irange(12, 59, step=12)]):
      
        # number of minutes pointed to by the hour hand
        p = (5 * h) + (m // 12)
        # minute divisions the hour hand is ahead of the minute hand
        d = p - m
        if not (d > 0): continue
      
        # time read as a 3- or 4-digit number
        n = 100 * h + m
        r = div(n, d)
        if r is None or not (d > r): continue
      
        # output solution
        printf("{h:02d}:{m:02d} -> {n} = {d} * {r}")
      

      Solution: The clock stopped at 8:12.

      The second hand points to 12 (= 0 minutes), the minute hand to 12 minutes, and the hour hand to 8 + 12/60 hours (= 41 minutes).

      The hour hand is 29 minute divisions ahead of the minute hand, and: 812 = 29 × 28.

      Like

  • Unknown's avatar

    Jim Randell 8:28 am on 27 June 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2471: [Cutting a rod] 

    From The Sunday Times, 31st January 2010 [link]

    In the woodwork class, students were being taught how to measure and cut accurately. Pat was given a rod that was a whole number of feet long and was told to saw it into three pieces, each a different whole number of inches long. While he was cutting his set of three pieces, Pat calculated how many different sets it would be possible to make. He discovered that the answer was equal to his father’s year of birth.

    What year was that?

    This puzzle was originally published with no title.

    [teaser2471]

     
    • Jim Randell's avatar

      Jim Randell 8:29 am on 27 June 2025 Permalink | Reply

      We assume the cuts themselves make a negligible difference to the length of the pieces.

      Here is a constructive program that counts the number of dissections of an n-foot rod into 3 dissimilar whole inch lengths.

      I decided that as the puzzle was set in 2010, viable years are in the range [1940, 1985].

      The program runs in 71ms. (Internal runtime is 6.1ms).

      from enigma import (irange, inf, decompose, icount, printf)
      
      # generate number of dissections
      def generate():
        # consider number of feet
        for n in irange(1, inf):
          # divide into different sets of inches
          k = icount(decompose(12 * n, 3, increasing=1, sep=1))
          printf("[{n} ft -> {k} sets]")
          yield (n, k)
      
      # find lengths that give up to 1985 sets
      ss = list()
      for (n, k) in generate():
        ss.append((n, k))
        if k > 1985: break
      
      # output solution (between 1940 and 1985)
      for (_, y) in ss:
        if 1939 < y < 1986:
          printf("year = {y}")
      

      Solution: Pat’s father was born in 1951.

      1951 is the number of dissections of a 13-foot rod.


      We can use the values accumulated by the program to determine a polynomial equation to generate the sequence:

      % python3 -i teaser/teaser2471.py
      [1 ft -> 7 sets]
      [2 ft -> 37 sets]
      [3 ft -> 91 sets]
      [4 ft -> 169 sets]
      [5 ft -> 271 sets]
      [6 ft -> 397 sets]
      [7 ft -> 547 sets]
      [8 ft -> 721 sets]
      [9 ft -> 919 sets]
      [10 ft -> 1141 sets]
      [11 ft -> 1387 sets]
      [12 ft -> 1657 sets]
      [13 ft -> 1951 sets]
      [14 ft -> 2269 sets]
      year = 1951
      
      >>> from enigma import Polynomial
      >>> p = Polynomial.interpolate(ss)
      >>> print(p)
      Polynomial[(+12)x^2 (-6)x (+1)]
      >>> print(p(13))
      1951
      >>> print(p(5280))
      334509121
      

      So, for an n-foot rod (integer n ≥ 1), the number of dissections D(n) is given by:

      D[n] = 12n^2 − 6n + 1

      This corresponds to OEIS A154105 [@oeis.org].

      Like

      • Frits's avatar

        Frits 3:32 pm on 27 June 2025 Permalink | Reply

        It is not too difficult to find this formula for the sum 1 + 2+4 + 5+7 + 8+10 + 11+13 + 14+16 + ….

        Like

      • Frits's avatar

        Frits 4:08 pm on 27 June 2025 Permalink | Reply

        Another formula for D(n) is:

        sum(6 * n – (k + 3) // 2 – k – 1 for k in range(4 * n – 1))

        Like

    • Jim Randell's avatar

      Jim Randell 8:10 am on 28 June 2025 Permalink | Reply

      See also: BrainTwister #25.

      Where we had:

      t[n] = intr(sq(n) / 12)

      Then D[n] appears as a subsequence of t[n] taking every 12th element (as there are 12 inches in 1 foot).

      Specifically:

      D[n] = t[12n − 3]

      And this gives rise to the quadratic equation given above.

      Like

  • Unknown's avatar

    Jim Randell 9:11 am on 5 June 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2536: ELEMENTARY 

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

    “Every number has some significance”, said Holmes, referring to his monograph on the subject. “Then what do you make of this?”, asked Watson, scribbling a seven-digit number on the desk diary. “Apart from the obvious fact that it is your old Indian army number”, replied Holmes, “I see that it is the largest seven-digit number where the difference between it and its reverse is the cube of a factor of the number itself”.

    What was Watson’s number?

    [teaser2536]

     
    • Jim Randell's avatar

      Jim Randell 9:11 am on 5 June 2025 Permalink | Reply

      A simple search yields the largest possible number in a reasonable time.

      This Python program runs in 194ms. (Internal runtime is 122ms).

      from enigma import (irange, nrev, is_cube, printf)
      
      # consider 7-digit numbers
      for n in irange(9999999, 1000000, step=-1):
      
        # difference between n and its reverse is the cube of a divisor of n
        d = abs(n - nrev(n))
        if d == 0: continue
        x = is_cube(d)
        if x is None or n % x != 0: continue
      
        # output soultion
        printf("{n} [diff = {d} = {x}^3]")
        break  # we want the largest number
      

      Solution: Watson’s number is: 9841788.

      We have:

      abs(9841788 − 8871489) = 970299 = 99³
      9841788 = 99 × 99412


      Because the number is quite large the search doesn’t have to look too far, but we can speed it up.

      Writing the number as ABCDEFG then the difference between it and its reverse is a cube, so:

      abs(ABCDEFGGFEDCBA) = x³
      where x divides ABCDEFG

      And we can rewrite the argument to abs() as:

      99 × (10101(AG) + 1010(BF) + 100(CE))

      From which we see x must have factors of 3 and 11.

      And the original number is divisible by x, so it must be a multiple of 33, which means we can skip candidates that are not divisible by 33. And this gives us a nice compact program, with a much more acceptable runtime.

      The following Python program runs in 76ms. (Internal runtime is 12.4ms).

      from enigma import (irange, nrev, is_cube, printf)
      
      # consider 7-digit numbers
      for n in irange.round(9999999, 1000000, step=-33, rnd='I'):
      
        # difference between n and its reverse is a cube of a divisor of n
        d = abs(n - nrev(n))
        if d == 0: continue
        x = is_cube(d)
        if x is None or n % x != 0: continue
      
        # output solution
        printf("{n} [diff = {d} = {x}^3]")
        break  # we want the largest number
      

      There are 198 such 7-digit numbers, of which the largest is the required answer. The rest can be seen by removing the final [[ break ]] from the program.

      The smallest number with this property is 10164:

      abs(10164 − 46101) = 35937 = 33³
      10164 = 33 × 308

      Like

    • Frits's avatar

      Frits 1:19 pm on 5 June 2025 Permalink | Reply

      I have been discussing this Teaser with Brian Gladman for the last couple of days.
      This program has an interal runtime of 12 resp 135 microseconds (Py/PyCPython).

      from functools import reduce
      
      d2n = lambda *s: reduce(lambda x,  y: 10 * x + y, s)
      
      # the number's digits: abcdefg
      #
      # dif =   99^3.(a - g)                   (credit: Brian Gladman)
      #       + 99^2.{3.(a - g) + 10.(b - f) + (c - e)}
      #       + 99^1.(3.(a - g) + 20.(b - f) + (c - e)}
      #
      # dif must be a multiple of 99. If abs(dif) also must be a cube then
      # dif must be at least a multiple of 33^3.
      mx = int((10**7)**(1/3))
      
      # 4th digit from the end of the difference must be 0 or 9
      cbs = [i**3 for i in range(33, mx + 1, 33) if (cb := i**3) % 99 == 0 and 
                                                     str(cb)[-4] in "09"]
      
      # formula (10^6 - 1).(a - g) + 10.(10^4 - 1).(b - f) + 100.(10^2 - 1).(c - e)
      # equals <cb> if abcdefg > gfedcba otherwise it equals -<cb>
      mx = 0
      # process all possible differences (cubes)
      for cb in cbs:
        cr = round(cb ** (1/ 3))
        # if n % cr = 0 then abc9efg % cr must be one of 10 values 
        # (we can know them in advance)
        d9mods = [(i * (1000 % cr)) % cr for i in range(10)]
        s_d9mods = set(d9mods)
        for a in range(9, 0, -1):
          if mx and a < int(str(mx)[0]): break
          for b in range(9, -1, -1):
            if mx and b < int(str(mx)[1]): break
            for g in range(9, -1, -1):
              # a != g otherwise difference = ...0 and a cube root = ...0 
              #        which is impossible as a != 0 (and thus g != 0)
              if g == a: continue
              p1 = 999999 * (a - g)
              for f in range(9, -1, -1):
                p1p2 = p1 + 99990 * (b - f)
                # 99.(c - e) = (+/-)cb - p1p2  
                c_min_e, r = divmod((cb if a > g else -cb) - p1p2 , 9900)
                if not r and -10 < c_min_e < 10:
                  # list of possible c's
                  cs = (range(c_min_e, 10) if c_min_e > 0 else range(10 + c_min_e))
                  for c in reversed(cs):
                    e = c - c_min_e
                    r = (n9 := d2n(a, b, c, 9, e, f, g)) % cr
                    if r in s_d9mods:
                      d = 9 - d9mods.index(r)
                      n = n9 + 1000 * (d - 9)
                      if n > mx:
                        mx = n
                      break # propagate this break till we have a new <cb>
                  else: continue
                  break  
              else: continue
              break  
            else: continue
            break  
          else: continue
          break  
      
      print(f"answer: {mx}")    
      

      Like

      • Frits's avatar

        Frits 4:03 pm on 5 June 2025 Permalink | Reply

        Instead of using d9mods to determine “d” we can also use:

        d = ((a + c + e + g) - (b + f)) % 11
        

        Like

    • Frits's avatar

      Frits 1:49 pm on 5 June 2025 Permalink | Reply

      Less efficient.

      This program has an interal runtime of 1.1 resp 3.2 ms (PyPy/CPython).

      from collections import defaultdict
      
      # difference must be a multiple of 99. If difference also is a cube then
      # the difference must be at least a multiple of 33^3.
      mx = int((10**7)**(1/3))
      
      # 4th digit from the end of the difference must be 0 or 9
      cbs = {i**3 for i in range(33, mx + 1, 33) if (cb := i**3) % 99 == 0 and 
                                                    str(cb)[-4] in "09"}
      # 7-digit number = abcdefg
      # make a dictionary of possible <fg>'s per <ab> 
      d = defaultdict(set)
      for cube in cbs:
        last2 = (cube % 100)
        for ab in range(10, 100):
          ba = int(str(ab)[::-1])
          # 2 situations: fg < ba or fg > ba
          for i, fg in enumerate([(100 + ba - last2) % 100, (ba + last2) % 100]):
            gf = int(str(fg).zfill(2)[::-1]) 
            if i: 
              if ab > gf: 
                d[ab] |= {fg}
            else: 
              if ab < gf: 
                d[ab] |= {fg}   
            # ab = gf implies dif = ...00 which can't be a multiple of 33^3  
      
      for ab in reversed(range(10, 100)):
        ab_ = 100000 * ab 
        for cde in reversed(range(1000)):
          abcde = ab_ + 100 * cde
          # check all possible fg's
          for fg in sorted(d[ab], reverse=True):
            n = abcde + fg
            r = int(str(n)[::-1])
            # find whether the cube root of the difference
            # is an integer that is a divisor of the number
            if abs(n - r) in cbs:
              # calculate cube root of the difference
              cr = round(abs(n - r) ** (1/3))
              if n % cr == 0: 
                print("answer:", n)
                exit(0)
      

      Like

    • Jim Randell's avatar

      Jim Randell 3:01 pm on 6 June 2025 Permalink | Reply

      I’ve obviously not spent as much time analysing this as Frits. But here is an alternative approach that uses the [[ express() ]] function from the enigma.py library to express the difference between the original number and its reverse in terms of the differences between digits of the number (using the expression given in my original comment).

      It then generates all 198 possible 7-digit numbers with the property, and selects the largest of them.

      It has an internal runtime of 2.3ms.

      from enigma import (Accumulator, irange, inf, cb, express, cproduct, nconcat, group, subsets, unpack, printf)
      
      # collect pairs of digits by their difference
      diff = group(subsets(irange(0, 9), size=2, select='M'), by=unpack(lambda x, y: x - y))
      
      # find n for difference d, must be divisible by x (a multiple of 11)
      def solve(d, x):
        # find d1 = (A - G), d2 = (B - F), d3 = (C - E) values
        ms = [100, 1010, 10101]  # multiples of (C - E), (B - F), (A - G)
        # solve for quantities -9 ... +9
        for (q1, q2, q3) in express(d // 99, ms, min_q=-9, max_q=9):
          # consider +d and -d
          for (d3, d2, d1) in [(q1, q2, q3), (-q1, -q2, -q3)]:
            # find possible digit values
            for (A, G) in diff[d1]:
              if A == 0: continue
              for ((B, F), (C, E)) in cproduct([diff[d2], diff[d3]]):
                # x is a multiple of 11, so there is only one possible value for D
                D = (A - B + C + E - F + G) % 11
                if D > 9: continue
                n = nconcat(A, B, C, D, E, F, G)
                if n % x == 0:
                  yield n
      
      # find the largest value
      r = Accumulator(fn=max)
      
      # consider possible differences
      for x in irange(33, inf, step=33):
        d = cb(x)
        if d > 9999999: break
        # find possible n values
        for n in solve(d, x):
          r.accumulate_data(n, (d, x))
      
      # output solution
      (n, (d, x), t) = (r.value, r.data, r.count)
      printf("max(n) = {n} [diff = {d} = {x}^3] [from {t} values]")
      

      Like

      • Frits's avatar

        Frits 12:15 pm on 7 June 2025 Permalink | Reply

        @Jim,

        An interesting idea to use “express”.

        It looks like “n” is ordered within the (A, G) loop.

        If you use “irange(9, 0, -1)” in the diff formula you can break after the yield and propagate the break until you are out of the (A, G) loop.

        Like

    • ruudvanderham's avatar

      ruudvanderham 1:24 pm on 7 June 2025 Permalink | Reply

      import peek
      
      for i in range(9999999, 1000000 - 1, -1):
          if (diff := i - int("".join(*[reversed(str(i))]))) > 0:
              if (c := round(diff ** (1 / 3))) ** 3 == diff:
                  if i % c == 0:
                      peek(i, diff, c)
                      break

      Like

  • Unknown's avatar

    Jim Randell 8:35 am on 3 June 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2541: Lotto luck 

    From The Sunday Times, 5th June 2011 [link]

    Chris was lucky in the lotto, winning a whole number of pounds (under £ 5,000), which he shared with his five children. John got £ 1 more than a fifth of the total; Ciaran got £ 1 more than a fifth of the remainder. After Ciaran got his share, Fergal got £ 1 more than a fifth of the remainder, and after Fergal got his share, Brendan got £ 1 more than a fifth of what was left. After Brendan got his share, Chris gave Grainne £ 1 more than a fifth of what was left and kept the remainder (a whole number of pounds).

    How much did Chris keep?

    [teaser2541]

     
    • Jim Randell's avatar

      Jim Randell 8:36 am on 3 June 2025 Permalink | Reply

      For a starting amount X, the next child’s share is X/5 + 1, and the remainder is 4X/5 − 1.

      Each of the shares and remainders must be an integer, because if we ever get a share that has a fractional part, then the fractional part of the remainder will be further subdivided into a smaller fraction. And as both the starting amount and the final remainder are whole numbers, then all the intermediate numbers are too.

      The following Python program runs in 71ms. (Internal runtime is 3.7ms).

      from enigma import (irange, ediv, printf)
      
      # calculate share and remainder from total X
      def calc(X):
        share = ediv(X, 5) + 1
        return (share, X - share)
      
      # consider possible winnings
      for W in irange(1, 4999):
      
        # calculate share and remainder for each child
        try:
          (J, R) = calc(W)  # John
          (C, R) = calc(R)  # Ciaran
          (F, R) = calc(R)  # Fergal
          (B, R) = calc(R)  # Brendan
          (G, R) = calc(R)  # Grainne
        except ValueError:
          continue
      
        # output solution
        printf("W={W} -> J={J} C={C} F={F} B={B} G={G} -> R={R}")
      

      Solution: Chris kept £ 1019.

      The initial winnings were £ 3120, and they are distributed as:

      John = £ 625
      Ciaran = £ 500
      Fergal = £ 400
      Brendan = £ 320
      Grainne = £ 256
      Chris = £ 1019
      total = £ 3120

      It is easy to see that the initial amount won must be a multiple of 5, which allows us to skip 80% of the cases checked. (And brings the internal runtime down to 1.0ms).


      And with further analysis we can get an even faster solution:

      We find the remainder after step k is:

      R[k] = (4^k W − 5(5^k − 4^k)) / (5^k)

      So after the shares for the 5 sons have been distributed we have:

      R[5] = (1024 W − 10505) / 3125

      1024 W − 3125 R = 10505

      where: 0 < R < W < 5000.

      This is a Linear Diophantine Equation in 2 variables, and can be solved using the Extended Euclidean Algorithm [@wikipedia], implemented as [[ egcd() ]] in the enigma.py library.

      Here is a general solver for this kind of equation:

      from enigma import (egcd, div, divc)
      
      # solve linear diophantine equations in 2 variables:
      def diop_linear(a, b, c, mX=0, fn=0):
        """
        solve the linear Diophantine equation a.X + b.Y = c for integers X, Y
        (where a, b are non-zero, and gcd(a, b) divides c).
      
        return ((X0, Y0), (Xd, Yd)) to give solutions of the form:
      
          (X, Y) = (X0 + t.Xd, Y0 + t.Yd) for integer t
      
        the value of X0 returned is the smallest integer possible above (or equal
        to) mX, and Xd is positive.
      
        however, if <fn> is set, then a function f: t -> (X, Y) is returned instead.
        """
        if a == 0 or b == 0: raise ValueError("diop_linear: invalid equation")
        (X, Y, g) = egcd(a, b)
        if g > 1:
          (a, b, c) = (a // g, b // g, div(c, g))
          if c is None: raise ValueError("diop_linear: no solutions")
        # calculate particular solution (X0, Y0) and deltas (Xd, Yd)
        (X0, Y0) = (c * X, c * Y)
        (Xd, Yd) = ((-b, a) if b < 0 else (b, -a))
        # adjust X0 to be the smallest possible value
        t = divc(mX - X0, Xd)
        X0 += t * Xd
        Y0 += t * Yd
        if fn: return (lambda t: (X0 + t * Xd, Y0 + t * Yd))
        return ((X0, Y0), (Xd, Yd))
      

      Which we can then use to solve the puzzle:

      from enigma import (irange, inf, printf)
      
      # k = number of sons
      k = 5
      # solve the Diophantine equation: 4^k W - 5^k R = 5(5^k - 4^k)
      (p4k, p5k) = (4**k, 5**k)
      fn = diop_linear(p4k, -p5k, 5 * (p5k - p4k), fn=1)
      
      # consider increasing solutions where 0 < W < 5000
      for t in irange(0, inf):
        (W, R) = fn(t)
        if not (W < 5000): break
        if R < 0: continue
        # output solution
        printf("W={W} R={R}")
      

      This program has an internal runtime of just 35µs.

      Like

    • John Crabtree's avatar

      John Crabtree 8:03 pm on 4 June 2025 Permalink | Reply

      This teaser is a variation of the Monkey and the Coconuts puzzle which Martin Gardner wrote about decades ago in his column in the Scientific American. The puzzle is older than that.
      R[1] = 4W/5-1 = 4(W+5)/5 – 5
      And so R[k] = 4^k (W + 5) / (5^k) – 5
      R[5] = 1024(W+5) / 3125 – 5, and so (W+5) = 3125, and the answer follows.

      Like

  • Unknown's avatar

    Jim Randell 7:32 am on 13 May 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2350: [Circles and tangents] 

    From The Sunday Times, 7th October 2007 [link]

    A puzzle in a magazine was flawed: it had two different answers. The puzzle showed two circles that did not meet. It gave the radii (prime numbers of centimetres) and the distance between the centres (a whole number of centimetres less than 50).

    It then said:

    “Imagine a straight line that is a tangent to both the circles. The distance between the two points where that line touches the circles is. a whole number of centimetres.

    How many?”

    What was the radius of the smaller circle?

    This puzzle was originally published with no title.

    [teaser2350]

     
    • Jim Randell's avatar

      Jim Randell 7:32 am on 13 May 2025 Permalink | Reply

      We are to construct the tangents to two non-touching circles. See: [ @wikipedia ]

      I am assuming that, as we are asked to find the radius of the smaller circle, then the two circles must be of different sizes. (And without this condition the Teaser puzzle itself is flawed).

      If the circles have radii r and R (where r < R), and the separation between their centres is d, then the tangents have length:

      outer tangent, length T: d^2 = T^2 + (R − r)^2
      inner tangent, length t: d^2 = t^2 + (R + r)^2

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

      from enigma import (primes, irange, ircs, printf)
      
      # r = radius of smaller circle (a prime)
      for r in primes.between(2, 24):
        # R = radius of the larger circle (a prime)
        for R in primes.between(r + 1, 48 - r):
          # d = separation of centres (< 50)
          for d in irange(r + R + 1, 49):
            # calculate: T = length of outer tangent, t = length of inner tangent
            (T, t) = (ircs(d, -(R - r)), ircs(d, -(R + r)))
            if T is None or t is None or t == T: continue
            # output viable configurations
            printf("r={r} R={R} d={d} -> T={T} t={t}")
      

      Solution: The smaller circle has a radius of 7 cm.

      There are two possible scenarios that provide tangents with two different integer lengths:

      d=26, r=7, R=17 → T=24, t=10
      d=34, r=7, R=23 → T=30, t=16

      And so one of these must be the scenario presented in the magazine puzzle.

      The first of these solutions looks like this:


      If the circles were allowed to be the same size, then we can find the following additional viable configurations, that give two different integer tangent lengths:

      d=5, r=2, R=2 → T=5, t=3
      d=10, r=3, R=3 → T=10, t=8
      d=26, r=5, R=5 → T=26, t=24

      And the Teaser puzzle would not have a unique solution.

      These additional solutions can be seen by changing line 6 in the program to start the search for R from the value of r.

      Like

      • Frits's avatar

        Frits 7:13 pm on 14 May 2025 Permalink | Reply

        In both scenarios we have (not a general rule):

        d^2 = T^2 + t^2 = 2.(r^2 + R^2)

        Like

    • Frits's avatar

      Frits 11:02 am on 16 May 2025 Permalink | Reply

      from enigma import primes, pythagorean_triples, subsets
      from collections import defaultdict
      
      prms = set(primes.between(2, 48))
      
      # collect Pythagorean triples for each distance between the centres
      pt = defaultdict(list)
      for x, y, h in pythagorean_triples(49):
        pt[h] += [x]
        pt[h] += [y]
      
      for d, vs in pt.items():  
        for t, T in subsets(sorted(vs), 2):
          # make sure r and R are integers
          if t % 2 != T % 2: continue
          r, R = (T - t) // 2, (T + t) // 2
          if any(x not in prms for x in (r, R)): continue 
          print(f"{r=} {R=} {d=} -> {T=} {t=}")
      

      Like

  • Unknown's avatar

    Jim Randell 8:04 am on 29 April 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2526: [Lucky dates] 

    From The Sunday Times, 20th February 2011 [link] [link]

    Orla was married in this century, on her lucky day of the week, and her twin sister Enya’s lucky-number day of the month. Enya told Orla on that big day that she intended her own wedding to be on the same lucky day of the week and lucky-number day of the month. She realised that it could not be in the same year as Orla’s, but Orla added that Enya could not be married in the following year, either. Enya married months later, the actual number of months being the sum of the six digits of Orla’s wedding date.

    What was Orla’s wedding date?

    This puzzle was originally published with no title.

    [teaser2526]

     
    • Jim Randell's avatar

      Jim Randell 8:04 am on 29 April 2025 Permalink | Reply

      I am assuming that “this century” means the 21st century (which started on 2001-01-01).

      And “the sum of the six digits of Orla’s wedding date” refers to the sum of date expressed as “DD/MM/YY” (i.e. using the final 2 digits of the year, rather than indicating that Orla’s wedding date is of the form “D/M/YYYY”).

      This Python program runs in 67ms. (Internal runtime is 9.0ms).

      from datetime import (date, timedelta)
      from enigma import (irange, repeat, inc, dsum, catch, printf)
      
      # increment date (<d>, <m>, <y>) by <i> months
      def incmonth(d, m, y, i=1):
        m += i
        while m > 12:
          m -= 12
          y += 1
        return catch(date, y, m, d)
      
      # consider dates in the 21st century for Orla's wedding
      for d1 in repeat(inc(timedelta(days=1)), date(2001, 1, 1)):
        if d1.year > 2009: break
        # calculate the day of the week
        wd = d1.isoweekday()
        valid = lambda x: x is not None and x.isoweekday() == wd
        # calculate the digit sum of the date (6 digits)
        (d, m, y) = (d1.day, d1.month, d1.year)
        n = dsum(d) + dsum(m) + dsum(y % 100)
        # consider a date <n> months in advance
        d2 = incmonth(d, m, y, n)
        if not valid(d2): continue
        if not (d2.year - d1.year > 1): continue
        # and check no intervening date would do
        if any(valid(incmonth(d, m, y, i)) for i in irange(1, n - 1)): continue
        # output solution
        printf("Orla = {d1}, Enya = {d2} [{n} months later]")
      

      Solution: Orla’s wedding day was: Wednesday, 31st December 2008.

      The next (Wednesday, 31st) occurs on: Wednesday, 31st March 2010, and so this is Enya’s wedding day.

      Enya’s wedding day occurs 15 months after Orla’s, and the sum of the digits in Orla’s date expressed as: 31/12/08 is 15.


      If we were to use an 8-digit format for the dates (e.g. “DD/MM/YYYY” or “YYYY-MM-DD”), we can get a solution of:

      Orla = Tuesday, 31st July 2007
      Enya = Tuesday, 31st March 2009 (20 months later)

      Like

    • Frits's avatar

      Frits 12:32 pm on 30 April 2025 Permalink | Reply

      from enigma import SubstitutedExpression
      from datetime import date
      
      # check date format and possible other checks
      def check(AB, CD, EF, extra=False):
        try:
          wd = date(2000 + EF, CD, AB).weekday()
        except ValueError:
          return False
        
        if extra:
          # check if in the next year there are any dates with day number AB
          # on the same weekday as Orla's
          for m in range(1, 13):
            try:
              if date(2000 + EF + 1, m, AB).weekday() == wd: 
                return False
            except ValueError:
              continue
        return True
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [ # Orla : AB-CD-EF  Enya: AB-GH-IJ  (dd-mm-yy)
          "AB < 32",
          "CD < 13",
          # Enya married months later but not in the following year
          "(sd := A + B + C + D + E + F) > 24 - CD",
          # check validity of Orla's date and that Enya could not be married 
          # in the following year
          "check(AB, CD, EF, 1)",
          
          "IJ <= 11",   # 2011
          # Enya could not be married in the following year
          "IJ > EF + 1",
          
          # Enya married months later, the actual number of months being the sum 
          # of the six digits of Orla's wedding date
          "sd - 12 * (IJ - EF) + CD = GH",
          "0 < GH < 13",
          
          # check validity of Enya's date
          "check(AB, GH, IJ)",
          # Enya married on the same day of the week as Orla
          "date(2000 + EF, CD, AB).weekday() == date(2000 + IJ, GH, AB).weekday()"
        ],
        answer="(AB, CD, 2000 + EF)",
        d2i=dict([(k, "ICGE") for k in range(2, 4)] +
                 [(k, "AICGE") for k in range(4, 10)]),
        distinct="",
        s2d=dict(E=0),
        env=dict(date=date, check=check),
        reorder=0,
        verbose=0,    # use 256 to see the generated code
      )
      
      # print answers
      for ans in p.answers():
        print(f"answer: {'-'.join(str(x) for x in ans)}")
      

      Like

  • Unknown's avatar

    Jim Randell 10:30 am on 4 April 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2558: Round table 

    From The Sunday Times, 2nd October 2011 [link] [link]

    Pat likes to demonstrate his favourite trick shot on the pub pool table, which is 90 inches long and 48 inches wide. Pat hits the ball so that it strikes the long side of the table first, then (after a perfect bounce) it hits the short side, then the other long side, and then finally the other short side, from which it returns to the starting point, making its path the perimeter of a quadrilateral.

    What is the length of the perimeter of that quadrilateral?

    [teaser2558]

     
    • Jim Randell's avatar

      Jim Randell 10:31 am on 4 April 2025 Permalink | Reply

      (See also: Teaser 3184, Teaser 3073, Teaser 1917).

      Wherever the ball starts tracing out the quadrilateral, we can consider its movement in the x and y directions.

      In the x direction it travels to the cushion on one end, back to the other cushion, and then returns to its original position.

      i.e. the total distance travelled in the x direction is twice the length of the table (= 2 × 90 in = 180 in).

      Similarly the distance travelled in the y direction is twice the width of the table (= 2 × 48 in = 96 in).

      Hence the total distance travelled by the ball is: hypot(180, 96):

      >>> hypot(180, 96)
      204.0
      

      Solution: The length of the perimeter of the quadrilateral is 204 in.

      Like

  • Unknown's avatar

    Jim Randell 10:39 am on 31 January 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2581: Resplendency 

    From The Sunday Times, 11th March 2012 [link] [link]

    Our local Hibernian band will lead the St Patrick’s Day parade next Saturday, resplendent in their new tunics and finery. The total cost of this was one thousand pounds and it was generously paid for by the parish council. Each tunic cost the same two-figure number of pounds (and that number happens to be double the number of girls in the band). In addition, each girl in the band has some extra finery, and the cost of this for each girl was the same as the cost of the tunic but with the two digits in reverse order.

    How many boys and how many girls are there in the band?

    [teaser2581]

     
    • Jim Randell's avatar

      Jim Randell 10:40 am on 31 January 2025 Permalink | Reply

      If:

      b = number of boys
      g = number of girls
      t = cost of tunic (pounds)
      f = cost of finery (pounds) = reverse of t

      we have:

      t = 2 × g
      t × b + (t + f) × g = 1000

      Hence b, g, f can all be expressed in terms of t, which is a 2-digit even number.

      This Python program runs in 66ms. (Internal runtime is 57µs).

      from enigma import (irange, cproduct, div, printf)
      
      # the cost of each tunic is XY, a 2-digit even number
      # the cost of each finery is YX, a 2 digit number
      for (X, Y) in cproduct([irange(1, 9), [2, 4, 6, 8]]):
        (t, f) = (10 * X + Y, 10 * Y + X)
      
        # calculate the number of girls and boys
        g = t // 2
        b = div(1000 - (t + f) * g, t)
        if b is None or b < 0: continue
      
        # output solution
        printf("b={b} g={g}; t={t} f={f}")
      

      Solution: There are 24 boys and 8 girls.

      The tunics cost £ 16, and the finery costs £ 61.

      £16 × 24 + £(16 + 61) × 8 = £1000

      Like

      • Ruud's avatar

        Ruud 3:58 pm on 2 February 2025 Permalink | Reply

        Very straightforward:

        import peek
        import istr
        
        for n_girls in istr(range(5, 50)):
            tunic_cost = 2 * n_girls
            finery_cost = tunic_cost.reversed()
            girls_total = n_girls * (tunic_cost + finery_cost)
            boys_total = 1000 - girls_total
            if finery_cost[0] != 0 and boys_total.is_divisible_by(tunic_cost):
                n_boys = boys_total / tunic_cost
                peek(girls_total, boys_total, n_girls, n_boys, tunic_cost, finery_cost)
        

        Like

    • GeoffR's avatar

      GeoffR 9:03 am on 1 February 2025 Permalink | Reply

      # the cost of each tunic is XY, a 2-digit even number
      # the cost of each finery is YX, a 2 digit number
      
      for X in range(1, 9):
        for Y in range(2, 10, 2):
          XY, YX = 10*X + Y, 10*Y + X
          for girls in range(1, 100):
            # cost of a tunic is double the number of girls
            if XY != 2 * girls:continue
            for boys in range(1, 100):
              tunics = (boys + girls) * XY
              finery = girls * YX
              # total cost = £1,000
              if tunics + finery == 1000:
                print(f"Band = {boys} boys and {girls} girls.")
      
      # Band = 24 boys and 8 girls.
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 7:42 am on 17 January 2025 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2588: On-line 

    From The Sunday Times, 29th April 2012 [link] [link]

    Linesmen Alf and Bert were checking the railway line from Timesborough to Teaserton. Alf started at Timesborough, Bert at Teaserton, and they walked towards each other at their own steady speeds. A train from Timesborough, travelling at constant speed, reached Alf at noon and passed him ten seconds later. At 12:06 pm the train reached Bert and passed him eight seconds later.

    At what time did the two men meet?

    [teaser2588]

     
    • Jim Randell's avatar

      Jim Randell 7:42 am on 17 January 2025 Permalink | Reply

      This puzzle is solved with a straightforward bit of analysis.

      Suppose A is walking with a speed of a (units/s) and B with speed of b, and the train travels with a speed of v.

      The train takes 10s to pass A, so in that 10s A walks a distance 10a, and the front of the train travels a distance 10v (in the same direction), so the length of the train t is:

      t = 10(v − a)

      The train takes 8s to pass B, and in that 8s B walks a distance 8a, and the front of the train travels a distance 8v (in the opposite direction), and so the length of the train can also be expressed as:

      t = 8(v + b)

      So:

      10(v − a) = 8(v + b)
      v = 5a + 4b

      The front of the train is level with A at noon, and at 12:06pm (i.e. 360 seconds later) it is level with B.

      So the train has travelled a distance of 360v in that time, and A has travelled a distance of 360a, so at 12:06 pm the distance between A and B is:

      d = 360(v − a)

      And the time taken for A and B to reduce this distance to zero is:

      360(v − a) / (a + b)
      = 360 (4a + 4b) / (a + b)
      = 360 × 4

      So it takes another 4× 6 minutes = 24 minutes after 12:06 pm for A and B to meet.

      Solution: Alf and Bert met at 12:30 pm.

      Like

  • Unknown's avatar

    Jim Randell 10:26 am on 10 December 2024 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2560: Three trees 

    From The Sunday Times, 16th October 2011 [link] [link]

    Our front garden is circular, with a diameter less than 100 metres. Three trees grow on the perimeter: an ash, a beech and a cherry, each a different whole number of metres from each of the other two. A straight trellis 84 metres long joins the ash to the beech, and a honeysuckle grows at the point on the trellis nearest to the cherry tree. The distance from the cherry to the ash plus the distance from the ash to the honeysuckle equals the distance from the cherry to the beech.

    What is the diameter of the garden?

    [teaser2560]

     
    • Jim Randell's avatar

      Jim Randell 10:26 am on 10 December 2024 Permalink | Reply

      We represent the trees by points A, B, C on the perimeter of the circle.

      If the sides of the triangle (opposite A, B, C) are a, b, c, then the diameter of the circumcircle d is given by:

      d = (a . b . c) / 2T

      where T is the area of the triangle.

      In this case we have:

      T = c . h / 2

      d = a . b / h

      and also:

      a^2 = h^2 + c2^2
      b^2 = h^2 + c1^2
      a = b + c1

      equating values for h^2 allows us to calculate a (and b) knowing c1 and c2:

      a^2 − c2^2 = (a − c1)^2 − c1^2
      c2^2 = 2a . c1

      a = c2^2 / (2 . c1)

      This Python program considers possible splits of c into integers c1, c2, and then checks that the resulting a, b values form a viable triangle.

      It runs in 66ms. (Internal runtime is 84µs).

      from enigma import (decompose, div, sq, rcs, fdiv, printf)
      
      # split c into 2 integers
      c = 84
      for (c1, c2) in decompose(c, 2):
      
        # calculate a and b, and check (a, b, c) form a viable triangle
        a = div(sq(c2), 2 * c1)
        if a is None or not (a < 100): continue
        b = a - c1
        if not (b > 0 and a + b > c): continue
      
        # output solution
        h = rcs(a, -c2)
        d = fdiv(a * b, h)
        printf("c1={c1} c2={c2} -> a={a} b={b} c={c} -> h={h} d={d}")
      

      Solution: The diameter of the garden is 85 metres.

      The 84m trellis (AB) is split into 60m and 24m lengths by the honeysuckle. And the distances AC and BC are 51m and 75m.

      And it turns out the height of the triangle is also an integer (h = 45), so we have two Pythagorean triples that share a non-hypotenuse length:

      (24, 45, 51) = 3× (8, 15, 17)
      (45, 60, 75) = 15× (3, 4, 5)

      But the next smallest solution (which is eliminated by the d < 100 requirement) does not have an integer height (or diameter):

      a = 121, b = 103, c1 = 18, c2 = 66

      h = 11√85 ≈ 101.41, d = 1133/√85 ≈ 122.89

      Like

    • Frits's avatar

      Frits 12:18 pm on 11 December 2024 Permalink | Reply

      Trellis length 84 is cleverly chosen.
      For D=100 we only have to investigate c1 values 22, 24 and 26.

      The following program shows solutions with a integer diameter less than 1000 meters.

      from math import ceil
      D = 1000  #  with a diameter less than D metres
      
      # a = c2**2 / (2 * c1) < D, (c - c1)**2 < 2 * D * c1, c1**2 - 2 * (D + c1) * c1 + c**2 < 0  
      # (c1 - (D + c))**2 < (D + c)**2 - c**2
      # c1 > D + c - ((D + c)**2 - c**2)**.5  
      # c1 > 20.29
      
      # a + b > 84 or c2**2 / c1 - c1 > 84 or (84 - c1)**2 / c1 - c1 > 84
      # (84 - c1)**2 > c1**2 + 84 * c1 or 252 * c1 < 84**2
      # c1 < 28
      
      prt = 0
      
      hdr = " (c1,  c2)   c2^2 2*c1 (        a,         b)     a + b          h          d   "
      sols = []
      for c in range(3, D):
        if prt: print(hdr) 
        min_c1 = int(D + c - ((D + c)**2 - c**2)**.5) + 1
        # c2 must be even if a is an integer
        #for c1 in range(1 + (c % 2 == 0), ceil(c / 3), 2): 
        for c1 in range(min_c1 + ((c - min_c1) % 2), ceil(c / 3), 2): 
          c2 = c - c1
          a = round(c2**2 / (2 * c1), 2)
          b = round(a - c1, 2)
          h = round((b**2 - c1**2)**.5 if b >= c1 else -1, 2)
          d = round(a * b / h if h > 0 else -1, 2)
          OK = (' OK' if a + b > c and int(a) == a and int(d) == d and 
                        all (x < D for x in [a, b, d]) else '')
          txt = (f"({c1:>3}, {c2:>3}) " + 
                f"{c2**2:>6} " + 
                f"{2 * c1:>3}  ({a:>9}, {b:>9}) " + 
                f"{round(a + b, 2):>9} " +
                f"{h:>10} " +
                f"{d:>10} " +
                f"{round(D + c - ((D + c)**2 - c**2)**.5, 2) :>8} < c1 < " + 
                f"{round(c / 3, 2):>6} " + 
                f"{OK}")
          if prt: print(txt)      
          if OK: 
            sols.append(txt)
          if h == -1: break   
          
      print("solutions with a integer diameter:\n")  
      print(hdr) 
      for s in sols: 
        print(s)  
      
      '''
      solutions with a integer diameter:
      
       (c1,  c2)   c2^2 2*c1 (        a,         b)     a + b          h          d
      (  1,  16)    256   2  (    128.0,     127.0)     255.0      127.0      128.0     0.14 < c1 <   5.67  OK
      (  1,  18)    324   2  (    162.0,     161.0)     323.0      161.0      162.0     0.18 < c1 <   6.33  OK
      (  1,  20)    400   2  (    200.0,     199.0)     399.0      199.0      200.0     0.22 < c1 <    7.0  OK
      (  1,  22)    484   2  (    242.0,     241.0)     483.0      241.0      242.0     0.26 < c1 <   7.67  OK
      (  1,  24)    576   2  (    288.0,     287.0)     575.0      287.0      288.0      0.3 < c1 <   8.33  OK
      (  1,  26)    676   2  (    338.0,     337.0)     675.0      337.0      338.0     0.35 < c1 <    9.0  OK
      (  1,  28)    784   2  (    392.0,     391.0)     783.0      391.0      392.0     0.41 < c1 <   9.67  OK
      (  1,  30)    900   2  (    450.0,     449.0)     899.0      449.0      450.0     0.47 < c1 <  10.33  OK
      (  1,  32)   1024   2  (    512.0,     511.0)    1023.0      511.0      512.0     0.53 < c1 <   11.0  OK
      (  1,  34)   1156   2  (    578.0,     577.0)    1155.0      577.0      578.0     0.59 < c1 <  11.67  OK
      (  1,  36)   1296   2  (    648.0,     647.0)    1295.0      647.0      648.0     0.66 < c1 <  12.33  OK
      (  1,  38)   1444   2  (    722.0,     721.0)    1443.0      721.0      722.0     0.73 < c1 <   13.0  OK
      (  1,  40)   1600   2  (    800.0,     799.0)    1599.0      799.0      800.0     0.81 < c1 <  13.67  OK
      (  1,  42)   1764   2  (    882.0,     881.0)    1763.0      881.0      882.0     0.89 < c1 <  14.33  OK
      (  2,  42)   1764   4  (    441.0,     439.0)     880.0      439.0      441.0     0.93 < c1 <  14.67  OK
      (  1,  44)   1936   2  (    968.0,     967.0)    1935.0      967.0      968.0     0.97 < c1 <   15.0  OK
      (  2,  44)   1936   4  (    484.0,     482.0)     966.0      482.0      484.0     1.01 < c1 <  15.33  OK
      (  2,  46)   2116   4  (    529.0,     527.0)    1056.0      527.0      529.0      1.1 < c1 <   16.0  OK
      (  2,  48)   2304   4  (    576.0,     574.0)    1150.0      574.0      576.0     1.19 < c1 <  16.67  OK
      (  2,  50)   2500   4  (    625.0,     623.0)    1248.0      623.0      625.0     1.29 < c1 <  17.33  OK
      (  2,  52)   2704   4  (    676.0,     674.0)    1350.0      674.0      676.0     1.38 < c1 <   18.0  OK
      (  2,  54)   2916   4  (    729.0,     727.0)    1456.0      727.0      729.0     1.49 < c1 <  18.67  OK
      (  2,  56)   3136   4  (    784.0,     782.0)    1566.0      782.0      784.0     1.59 < c1 <  19.33  OK
      (  2,  58)   3364   4  (    841.0,     839.0)    1680.0      839.0      841.0      1.7 < c1 <   20.0  OK
      (  2,  60)   3600   4  (    900.0,     898.0)    1798.0      898.0      900.0     1.81 < c1 <  20.67  OK
      (  2,  62)   3844   4  (    961.0,     959.0)    1920.0      959.0      961.0     1.93 < c1 <  21.33  OK
      ( 24,  60)   3600  48  (     75.0,      51.0)     126.0       45.0       85.0     3.26 < c1 <   28.0  OK
      ( 36, 120)  14400  72  (    200.0,     164.0)     364.0      160.0      205.0    10.57 < c1 <   52.0  OK
      ( 48, 120)  14400  96  (    150.0,     102.0)     252.0       90.0      170.0    12.15 < c1 <   56.0  OK
      ( 46, 138)  19044  92  (    207.0,     161.0)     368.0     154.29      216.0    14.38 < c1 <  61.33  OK
      ( 32, 192)  36864  64  (    576.0,     544.0)    1120.0     543.06      577.0    20.67 < c1 <  74.67  OK
      ( 42, 210)  44100  84  (    525.0,     483.0)    1008.0     481.17      527.0    25.62 < c1 <   84.0  OK
      ( 72, 180)  32400 144  (    225.0,     153.0)     378.0      135.0      255.0    25.62 < c1 <   84.0  OK
      ( 81, 180)  32400 162  (    200.0,     119.0)     319.0      87.18      273.0    27.31 < c1 <   87.0  OK
      ( 36, 228)  51984  72  (    722.0,     686.0)    1408.0     685.05      723.0    27.88 < c1 <   88.0  OK
      ( 72, 240)  57600 144  (    400.0,     328.0)     728.0      320.0      410.0    37.64 < c1 <  104.0  OK
      ( 96, 240)  57600 192  (    300.0,     204.0)     504.0      180.0      340.0    42.94 < c1 <  112.0  OK
      ( 92, 276)  76176 184  (    414.0,     322.0)     736.0     308.58      432.0    50.43 < c1 < 122.67  OK
      (120, 300)  90000 240  (    375.0,     255.0)     630.0      225.0      425.0    63.53 < c1 <  140.0  OK
      (108, 360) 129600 216  (    600.0,     492.0)    1092.0      480.0      615.0     76.6 < c1 <  156.0  OK
      (144, 360) 129600 288  (    450.0,     306.0)     756.0      270.0      510.0    86.96 < c1 <  168.0  OK
      (162, 360) 129600 324  (    400.0,     238.0)     638.0     174.36      546.0    92.31 < c1 <  174.0  OK
      (168, 420) 176400 336  (    525.0,     357.0)     882.0      315.0      595.0   112.87 < c1 <  196.0  OK
      (144, 480) 230400 288  (    800.0,     656.0)    1456.0      640.0      820.0   124.67 < c1 <  208.0  OK
      (192, 480) 230400 384  (    600.0,     408.0)    1008.0      360.0      680.0   140.99 < c1 <  224.0  OK
      (198, 528) 278784 396  (    704.0,     506.0)    1210.0     465.65      765.0   160.11 < c1 <  242.0  OK
      (216, 540) 291600 432  (    675.0,     459.0)    1134.0      405.0      765.0   171.07 < c1 <  252.0  OK
      (240, 600) 360000 480  (    750.0,     510.0)    1260.0      450.0      850.0   202.93 < c1 <  280.0  OK
      (264, 660) 435600 528  (    825.0,     561.0)    1386.0      495.0      935.0    236.4 < c1 <  308.0  OK
      '''
      

      Like

      • Jim Randell's avatar

        Jim Randell 9:30 am on 12 December 2024 Permalink | Reply

        @Frits: Your code gives solutions that are close to a whole number of metres, rather than exactly a whole number of metres.

        For example, the first configuration you give:

        a = 128; b = 127; c1 = 1; c2 = 16

        h ≈ 126.996; d ≈ 128.004

        Here is some code that looks for pairs of Pythagorean triples that share a non-hypotenuse side to find solutions where h and d are (exact) integers:

        from enigma import (defaultdict, pythagorean_triples, subsets, div, arg, printf)
        
        D = arg(100, 0, int)
        printf("[d < {D}]")
        
        # collect pythagorean triples by non-hypotenuse sides
        d = defaultdict(list)
        for (x, y, z) in pythagorean_triples(D - 1):
          d[x].append((y, z))
          d[y].append((x, z))
        
        # look for pairs of triangles
        for (h, ts) in d.items():
          if len(ts) < 2: continue
        
          for ((c1, b), (c2, a)) in subsets(sorted(ts), size=2):
            c = c1 + c2
            if not (a == b + c1 and c < D): continue
            d = div(a * b, h)
            if d is None: continue
        
            printf("a={a} b={b} c={c} -> c1={c1} c2={c2} h={h} d={d}")
        

        Note that if h is an integer, then d will be rational (but not necessarily an integer).

        Like

        • Frits's avatar

          Frits 11:26 am on 12 December 2024 Permalink | Reply

          @Jim, you are right.

          I get the same output as your program if I only do rounding during printing (which I should have done).

          Like

    • Brian Gladman's avatar

      Brian Gladman 6:56 pm on 13 December 2024 Permalink | Reply

      Finding all solutions:

      # (a - b).(a + b + 2.c) =  c^2 = p.q with p < q
      # 
      # a - b = p        )  a = (p + q) / 2 - c
      # a + b + 2.c = q  )  b = a - p
      
      from enigma import divisor_pairs
      from fractions import Fraction as RF
      
      c = 84
      print('   a    b              d^2')
      for p, q in divisor_pairs(c * c):
        t, r = divmod(p + q, 2)
        a = t - c
        b = a - p
        if not r and a > 0 and 2 * b > a:
          d2 = RF(a * b * b, b + b - a)
          print(f"{a:4} {b:4} {d2:16}")
      

      Like

    • GeoffR's avatar

      GeoffR 10:59 am on 14 December 2024 Permalink | Reply

      
      % A Solution in MiniZinc - using Jim's variable notations
      include "globals.mzn";
      
      int: c == 84; % given distance between ash and beech trees
      
      var 1..100:a; var 1..100:b; var 1..100:c1; 
      var 1..100:c2; var 1..100:h; 
      var 1..5000: T; % UB = 100 * 100/2
      var 1..99:d; % circle diameter < 100
      
      constraint all_different ([a, b, c]);
      constraint c == c1 + c2;
      constraint 2 * T * d == a * b * c;
      constraint a == b + c1;
      constraint c1 * c1 + h * h == b * b;
      constraint c2 * c2 + h * h == a * a;
      constraint 2 * T == c * h;
      
      % Triangle formation constraints
      constraint a + b > c /\ b + c > a /\ a + c > b;
      
      solve satisfy;
      
      output ["[a, b, c] = " ++ show([a, b, c]) ++
      "\n" ++ "[c1, c2, h] = " ++ show( [c1, c2, h]) ++
      "\n" ++ "Circle diameter = " ++ show(d) ++ " metres"];
      
      % [a, b, c] = [75, 51, 84]
      % [c1, c2, h] = [24, 60, 45]
      % Circle diameter = 85 metres
      % ----------
      % ==========
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:43 am on 14 November 2024 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2545: Emblematic 

    From The Sunday Times, 3rd July 2011 [link] [link]

    Pat’s latest art installation consists of a large triangle with a red border and, within it, a triangle with a green border. To construct the green triangle, he drew three lines from the vertices of the red triangle to points one third of the way (clockwise) along their respective opposite red sides. Parts of these lines formed the sides of the green triangle. In square centimetres, the area of the red triangle is a three-digit number and the area of the green triangle is the product of those three digits.

    What is the area of the red triangle?

    [teaser2545]

     
    • Jim Randell's avatar

      Jim Randell 8:46 am on 14 November 2024 Permalink | Reply

      See also: Teaser 3233, Teaser 2865Enigma 1076Enigma 320Enigma 1313.

      This is another puzzle that can be solved using Routh’s Theorem [@wikipedia], which I have made notes on here [ rouths-theorem.pdf ].

      This Python program runs in 70ms. (Internal runtime is 3.5ms).

      from enigma import (irange, inf, rdiv, multiply, nsplit, printf)
      
      # ratio XYZ/ABC in Routh's theorem
      def routh(x, y, z):
        a = x * y * z - 1
        b = (x * y + y + 1) * (y * z + z + 1) * (z * x + x + 1)
        return rdiv(a * a, b)
      
      # compute the ratio r = green / red
      r = routh(2, 2, 2)
      
      # consider possible areas for the smaller (green) triangle
      for G in irange(1, inf):
        # calculate the area of the larger (red) triangle
        R = rdiv(G, r)
        # R is a 3-digit number
        if R > 999: break
        if R < 100 or R % 1 > 0: continue
        # the product of R's digits is G
        if not (multiply(nsplit(R)) == G): continue
      
        # output solution
        printf("G={G} R={R} [r={r}]")
      

      Solution: The area of the red triangle is: 735 cm².

      And the area of the green triangle is: 105 cm².

      7 × 3 × 5 = 105


      From Routh’s Theorem we determine that area of the green triangle is 1/7 that of the red triangle.

      So we are looking for a 3-digit number ABC such that:

      A × B × C = ABC / 7

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

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "7 * A * B * C = ABC"
      
      --answer="ABC"
      

      Like

  • Unknown's avatar

    Jim Randell 10:47 am on 8 November 2024 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2616: Elevenses 

    From The Sunday Times, 11th November 2012 [link] [link]

    On 11/11 it is perhaps appropriate to recall the following story. In the graphics class students were illustrating pairs of similar triangles. In Pat’s larger triangle the sides were all even whole numbers divisible by 11. In fact they were 22 times the corresponding sides of his smaller triangle. As well as this, in the smaller triangle the digits used overall in the lengths of the three sides were all different and did not include a zero. Miraculously, exactly the same was true of the larger triangle.

    What were the sides of Pat’s smaller triangle?

    [teaser2616]

     
    • Jim Randell's avatar

      Jim Randell 10:47 am on 8 November 2024 Permalink | Reply

      We can place some bounds on the sides of the triangles:

      The smaller triangle cannot have sides less than 6, as the corresponding side in the larger triangle would have repeated digits.

      And so the smallest side in the larger triangle must have at least 3 digits, and as there are only 9 digits available, each side of the larger triangle must have 3 digits, and so the sides of the smaller triangle are between 6 and 43.

      This Python program collects possible sides for the large and small triangles, and then chooses three pairs that can form viable triangles.

      It runs in 66ms. (Internal runtime is 1.04ms).

      from enigma import (irange, is_duplicate, subsets, join, printf)
      
      # check for allowable digits
      def check(*vs):
        s = join(vs)
        if '0' in s or is_duplicate(s): return False
        return True
      
      # find multiples of 22 with distinct non-zero digits
      d = dict()  # d maps n -> n / 22
      for i in irange(6, 43):
        if not check(i): continue
        j = i * 22
        if not check(j): continue
        d[j] = i
      
      # choose the two smaller sides
      for (a, b) in subsets(d.keys(), size=2):
        if not (check(a, b) and check(d[a], d[b])): continue
      
        # choose the longer side
        for c in d.keys():
          # check for viable triangle
          if not (b < c): continue
          if not (c < a + b): break
          # check the digits
          if not (check(a, b, c) and check(d[a], d[b], d[c])): continue
          # output solution
          printf("{t1} -> {t2}", t2=(a, b, c), t1=(d[a], d[b], d[c]))
      

      Solution: The sides of the smaller triangle are: 18, 26, 37.

      And the corresponding sides of the larger triangle are: 396, 572, 814.

      Like

    • Frits's avatar

      Frits 1:46 pm on 8 November 2024 Permalink | Reply

      from enigma import SubstitutedExpression
       
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          "div(ABC, 22) = PQ", 
          "A < D",
          "div(DEF, 22) = RS",
          "D < G",
          "div(GHI, 22) = TU",
       
          # allow for single digit sides of smaller triangle   
          "P == 0 or P not in {Q, R, S, T, U}",
          "R == 0 or R not in {P, Q, S, T, U}",
          "T == 0 or T not in {P, Q, R, S, U}",
          
          # viable triangle
          "TU < PQ + RS",
          
          "0 not in {A, B, C, D, E, F, G, H, I, Q, S, U}",
        ],
        answer="PQ, RS, TU",
        d2i="",
        distinct=("ABCDEFGHI","QSU"),
        verbose=0,    # use 256 to see the generated code
      ) 
      
      # print answers
      for ans in p.answers():
        print(f"answer: {ans}")
      

      Like

    • GeoffR's avatar

      GeoffR 11:16 am on 9 November 2024 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Smaller triangle - assumed 2 digit sides
      var 1..9:a; var 1..9:b;  var 1..9:c; var 1..9:d; var 1..9:e; var 1..9:f;
      
      constraint all_different([a, b, c, d, e, f]);
      var 12..98:ab == 10*a + b;
      var 12..98:cd == 10*c + d;
      var 12..98:ef == 10*e + f;
      constraint ab < cd /\ cd < ef;  % order sides
      
      % Larger triangle - assumued 3 digit sides
      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]);  
      var 123..987:ABC == 100*A + 10*B + C;
      var 123..987:DEF == 100*D + 10*E + F;
      var 123..987:GHI == 100*G + 10*H + I;
      constraint ABC < DEF /\ DEF < GHI;  % order sides
      
      % Larger triangle has even sides and all sides are divisible by 11
      constraint sum([ABC mod 2 == 0, DEF mod 2 == 0, GHI mod 2 == 0]) == 3;
      constraint sum([ABC mod 11 == 0, DEF mod 11 == 0, GHI mod 11 == 0]) == 3;
      
      % larger triangle's sides are 22 times smaller triangle's sides
      constraint ABC == 22 * ab /\ DEF == 22 * cd /\ GHI == 22 * ef;
      
      solve satisfy;
      
      output ["Smaller Triangle Sides = " ++ show(ab) ++ ", " ++ show(cd) ++ ", " ++ show(ef)
      ++ "\n" ++ "Larger Triangle Sides = " ++ show(ABC) ++ ", " ++ show(DEF) ++ ", " ++ show(GHI)
      ];
      
      % Smaller Triangle Sides = 18, 26, 37
      % Larger Triangle Sides = 396, 572, 814
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 7:44 am on 31 October 2024 Permalink | Reply
    Tags: by: C Higgins,   

    Teaser 2597: Ages ago 

    From The Sunday Times, 1st July 2012 [link] [link]

    Bert’s age in years is one less than one-and-a-half times the age Alf was a whole number of years ago. Cal’s age in years is one less than one-and-a-half times the age Bert was, the same number of years ago.

    Dave’s age in years is one less than one-and-a-half times the age Cal was, again the same number of years ago. All four ages are different two-figure numbers, Cal’s age being Bert’s age with the order of the digits reversed.

    What (in alphabetical order) are their ages?

    [teaser2597]

     
    • Jim Randell's avatar

      Jim Randell 7:45 am on 31 October 2024 Permalink | Reply

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

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

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # ages now:
      #
      #   AB = Alf
      #   CD = Bert
      #   DC = Cal
      #   EF = Dave
      #
      # let the number of years ago be XY
      
      --distinct="CD"
      --invalid="0,ACDE"
      
      # "B's age is 1 less than 1.5 times A's age XY years ago"
      "3 * (AB - XY) == 2 * (CD + 1)"
      
      # "C's age is 1 less than 1.5 times B's age XY years ago"
      "3 * (CD - XY) == 2 * (DC + 1)"
      
      # "D's age is 1 less than 1.5 times C's age XY years ago"
      "3 * (DC - XY) == 2 * (EF + 1)"
      
      # ages are all different
      "all_different(AB, CD, DC, EF)"
      
      --template="(AB CD DC EF) (XY)"
      --solution=""
      

      Solution: The ages now are: Alf = 98; Bert = 86; Cal = 68; Dave = 41.

      And Cal’s age is the reverse of Bert’s.

      40 years ago, Alf was 58. And 1.5× this age is 87, and Bert’s current age is one less than this.

      40 years ago, Bert was 46. And 1.5× this age is 69, and Cal’s current age is one less than this.

      40 years ago, Cal was 28. And 1.5× this age is 42, and Dave’s current age is one less than this.

      Like

    • Ruud's avatar

      Ruud 8:38 am on 31 October 2024 Permalink | Reply

      Brute force:

      import itertools
      
      for a, b, d in itertools.permutations(range(10, 100), 3):
          c = int(str(b)[::-1])
          if b != c and c >= 10:
              for n in range(1, min(a, b, c, d) + 1):
                  if b == (a - n) * 1.5 - 1:
                      if c == (b - n) * 1.5 - 1:
                          if d == (c - n) * 1.5 - 1:
                              print(f"Alf={a} Bert={b} Cal={c} Dave={d} Number of years ago={n}")
      

      Liked by 1 person

    • ruudvanderham's avatar

      ruudvanderham 9:26 am on 31 October 2024 Permalink | Reply

      Quite different and much more efficient than my previous one:

      for a in range(10, 100):
          for n in range(a):
              b = (a - n) * 1.5 - 1
              c = (b - n) * 1.5 - 1
              d = (c - n) * 1.5 - 1
              if all(x == round(x) and n < x < 100 for x in (b, c, d)):
                  if len({a, b, c, d}) == 4:
                      if str(round(b)) == str(round(c))[::-1]:
                          print(a, b, c, d, n)
      

      Like

    • GeoffR's avatar

      GeoffR 9:35 am on 1 November 2024 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % All ages are different 2-digit numbers
      var 10..99:Alf; var 10..99:Bert; var 10..99:Cal; var 10..99:Dave;
      constraint all_different([Alf, Bert, Cal, Dave]);
      
      var 1..99:Y;  % the number of years ago
      
      constraint (2 * Bert ==  (Alf - Y) * 3 - 2)
      /\ (2 * Cal  ==  (Bert - Y) * 3 - 2)
      /\ (2 * Dave ==  (Cal - Y) * 3 - 2);
      
      % Cal’s age being Bert’s age with the order of the digits reversed.
      constraint Cal div 10 == Bert mod 10 /\ Cal mod 10 == Bert div 10;
      
      solve satisfy;
      
      output ["Alf = " ++ show(Alf) ++ ", Bert = " ++ show(Bert) ++
      ", Cal = " ++ show(Cal) ++ ", Dave = " ++ show(Dave) ++ ", Y = " ++ show(Y)];
      
      % Alf = 98, Bert = 86, Cal = 68, Dave = 41, Y = 40 
      % ----------
      % ==========
      
      
      
      

      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