Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 4:17 pm on 20 October 2023 Permalink | Reply
    Tags: ,   

    Teaser 3187: Wired squares 

    From The Sunday Times, 22nd October 2023 [link] [link]

    Sitting at his study desk one morning, Ted noticed a paperclip poking out of his notebook, unbent to a straight thin wire. Opening it up he found that his granddaughter Jessica had drawn a square grid (less than 14 cm side width) on one of the pages, with vertical and horizontal grid lines at 1 cm intervals.

    Musing this, Ted numbered each cell consecutively from 1, working left to right along each row from top to bottom in turn. Moving the wire over the page, he rested the wire over (or touching the corner of) some cells containing a square number. Placing the wire carefully, he was able to connect as many square-numbered cells as possible in this way. No square grid of less than 14 cm side width could have allowed the connection of a larger number of squares.

    What squares did he connect?

    When originally posted on The Sunday Times website the upper limit was “less than 15 cm”, but this was revised down to “less than 14 cm” when the paper was published.

    [teaser3187]

     
    • Jim Randell's avatar

      Jim Randell 8:06 am on 21 October 2023 Permalink | Reply

      Initially I drew out the various grids and looked for straight lines that could connect cells. (I am assuming the wire forms a sufficiently long straight line).

      This program examines lines that pass through the corners of square-numbered cells (so it may not find all possible lines), but it does better than I did on my manual attempt.

      It runs in 664ms.

      Run: [ @replit ]

      from enigma import (fdiv, inf)
      
      # find the slope and intercept of a line defined by points p1, p2
      def line_slope_intercept(p1, p2, div=fdiv):
        ((x1, y1), (x2, y2)) = (p1, p2)
        if x1 == x2: return (inf, x1)
        m = div(y2 - y1, x2 - x1)
        c = y1 - m * x1
        return (m, c)
      
      
      from enigma import (
        Accumulator, irange, subsets, tuples, line_intersect, catch, uniq,
        unpack, rdiv, seq2str, printf
      )
      
      # return the corners of a square
      corners = lambda x, y: [(x, y), (x + 1, y), (x + 1, y + 1), (x, y + 1)]
      
      # solve for an n x n grid
      # return max hits
      def solve(n, verbose=1):
        # calculate the corners of each square cell
        sqs = dict()
        pts = set()
        for i in irange(1, n):
          k = i * i
          (y, x) = divmod(k - 1, n)
          sqs[k] = (x, y)
          pts.update(corners(x, y))
      
        # find maximal hits
        ss = Accumulator(fn=max, collect=1)
      
        # consider lines that pass through two of the points
        fn = unpack(lambda p1, p2: line_slope_intercept(p1, p2, div=rdiv))
        for (p1, p2) in uniq(subsets(pts, size=2), fn=fn):
          # which squares are hit
          hit = set()
          for (k, (x, y)) in sqs.items():
            for (c1, c2) in tuples(corners(x, y), 2, circular=1):
              r = catch(line_intersect, p1, p2, c1, c2, internal=2, div=rdiv)
              if r is None: continue
              hit.add(k)
              break
      
          ss.accumulate_data(len(hit), tuple(sorted(hit)))
      
        # output information
        if verbose:
          printf("[n={n}]")
          printf("max hits = {ss.value}")
          for hit in uniq(ss.data):
            printf("-> {hit}", hit=seq2str(hit, enc="[]"))
          printf()
      
        # return max hits
        return ss.value
      
      # consider grids up to size 13
      r = Accumulator(fn=max, collect=1)
      for n in irange(1, 13):
        h = solve(n)
        r.accumulate_data(h, n)
      printf("max hits = {r.value} on grids {r.data}")
      

      Solution: The connected cells are: 1, 4, 16, 36, 49, 64.

      My answer to the originally posted puzzle (with an upper limit of “less than 15 cm”) was the same, and is as follows:

      The program finds two grids (up to 14×14) that allow a maximal 6 square-numbered cells to be connected, shown below:

      (Although the program looks for “reasonable” solutions, it is possible there may be a line with more than 6 connections that is not found by the program).

      However the puzzle is looking for a single unique solution.

      The first (13×13) grid needs a wire of around 11.3 cm in length to link the cells, and the second (14×14) grid needs a wire of around 14 cm to link the cells.

      So if the length of wire is between these values, we can link the cells in the 13×13 grid, but not the 14×14 grid.

      I unbent a couple of paperclips, and found the smaller one used around 10 cm of wire, and the big one about 16 cm. So it is plausible that a paperclip could fall between the two required values, leaving the 13×13 grid as the unique answer.

      In the revised puzzle, only the 13×13 grid is considered, so this must provide the answer. (Which makes me wonder why the puzzle has all the stuff about straightening the paperclip at all).


      The [[ line_slope_intercept() ]] function is included in the latest version of enigma.py.

      Like

  • Unknown's avatar

    Jim Randell 9:21 am on 19 October 2023 Permalink | Reply
    Tags:   

    Teaser 2643: Pack points 

    From The Sunday Times, 19th May 2013 [link] [link]

    Five cubs were awarded points for effort. Enid’s son and Master Smith had 17 points between them, Masters Jones and Robinson had a total of 16, Ivan and Master Robinson together had 14, and the two youngest of the cubs had a total of 13. John and Master Brown had a total of 12 points, Brenda’s son and Mike had 11 points between them, Ken and his best friend had a total of 10, Ann’s son and Debbie’s son together had 9, Christine’s son and Nigel had a total of 8, and Master Perkins and Debbie’s son together had 6.

    In alphabetical order of their mothers’ Christian names, what were the names of the five cubs (Christian name and surname)?

    [teaser2643]

     
    • Jim Randell's avatar

      Jim Randell 9:21 am on 19 October 2023 Permalink | Reply

      If we use variables A, B, C, D, E to correspond to the points allocated (according to mothers name), then we can allocate the first names and surnames of the cubs to the same set of variables to give us a set of simultaneous linear equations, which we can attempt to solve to give the points values.

      I used the [[ Matrix.linear() ]] solver from the enigma.py library to find assignments that give non-negative points values. (Although there are no further solutions if negative values are permitted).

      I also ensure that the pairings mentioned in the text all consist of distinct cubs. (Although, again, there are no further solutions if a pairing can refer to the same cub twice).

      The following Python program runs in 375ms.

      Run: [ @replit ]

      from enigma import (Matrix, subsets, catch, printf)
      
      # suppose the points are: A, B, C, D, E (according to mothers name)
      # then we can map the first and surnames to these values
      
      # variables
      vs = "ABCDE"
      
      # a function to construct equations
      def eq(vs, k, fn=Matrix.equation(vs)):
        r = fn(vs, k)
        assert {0, 1}.issuperset(r[0])  # reject coefficients that aren't 0 or 1
        return r
      
      # validate point values
      def valid(x):
        assert not x < 0
        return x
      
      # for firstnames I, J, K, M, N
      for (I, J, K, M, N) in subsets(vs, size=len, select='P'):
      
        # for surnames P, R, S, T (= Brown), U = (Jones)
        for (P, R, S, T, U) in subsets(vs, size=len, select='P'):
      
          # try to solve the equations
          r = None
          try:
            # construct equations
            eqs = [
              eq(['E', S], 17),  # Enid + Smith = 17
              eq([U, R], 16),  # Jones + Robinson = 16
              eq([I, R], 14),  # Ivan + Robinson = 14
              eq([J, T], 12),  # John + Brown = 12
              eq(['B', M], 11),  # Brenda + Mike = 11
              eq(['A', 'D'], 9),  # Ann + Debbie = 9
              eq(['C', N], 8),  # Christine + Nigel = 8
              eq([P, 'D'], 6),  # Perkins + Debbie = 6
            ]
      
            # solve the equations
            r = Matrix.linear(eqs, valid=valid)
          except (ValueError, AssertionError):
            pass
          if r is None: continue
      
          # check there is a pair = 13 (two youngest), and a pair = 10 (Ken + friend)
          v = dict(zip(vs, r))
          if not any(v[x] + v[K] == 10 for x in vs if x != K): continue
          if not any(v[x] + v[y] == 13 for (x, y) in subsets(vs, size=2)): continue
      
          # output solution
          mn = { 'A': 'Anne', 'B': 'Brenda', 'C': 'Christine', 'D': 'Debbie', 'E': 'Enid' }
          fn = { I: 'Ivan', J: 'John', K: 'Ken', M: 'Mike', N: 'Nigel' }
          sn = { P: 'Perkins', R: 'Robinson', S: 'Smith', T: 'Brown', U: 'Jones' }
          for k in vs:
            printf("{fn} {sn} = {v} points [{mn}]", mn=mn[k], fn=fn[k], sn=sn[k], v=v[k])
          printf()
      

      Solution: The names of the cubs are:

      Mike Smith = 7 points [Anne]
      Ivan Perkins = 4 points [Brenda]
      Ken Jones = 6 points [Christine]
      Nigel Brown = 2 points [Debbie]
      John Robinson = 10 points [Enid]

      So the youngest two are Mike and Ken (= 13 points in total), and Ivan must be Ken’s best friend (= 10 points in total).

      Like

  • Unknown's avatar

    Jim Randell 7:46 am on 15 October 2023 Permalink | Reply
    Tags:   

    Teaser 2644: Route canal 

    From The Sunday Times, 26th May 2013 [link] [link]

    Stephen is planning a 70-mile canal trip from the lock at Aytown to the lock at Beeswick, stopping at a pub at a lock over half way along the route. He has listed the number of miles from each lock to the next, the largest being the stretch from the pub to the next lock. He has also noted the distances from Aytown to the pub and from the pub to Beeswick. All the numbers that he has written down are different primes.

    Stephen can use his figures to work out the number of miles from any lock to any other. He’s found that, whenever that number of miles between locks is odd, then it is also a prime.

    What (in order) are the numbers of miles between consecutive locks?

    [teaser2644]

     
    • Jim Randell's avatar

      Jim Randell 7:46 am on 15 October 2023 Permalink | Reply

      This Python program runs in 55ms. (Internal runtime is 806µs).

      Run: [ @replit ]

      from enigma import (irange, primes, express, diff, cproduct, subsets, flatten, printf)
      
      # decompose total <t> into different values from <ns>
      def decompose(t, ns):
        if not ns: return
        for qs in express(t, ns, qs=(0, 1)):
          yield list(n for (n, q) in zip(ns, qs) if q > 0)
      
      # check all odd distances are prime
      def check(ds):
        for (i, j) in subsets(irange(len(ds)), size=2):
          d = sum(ds[i:j + 1])
          if d % 2 == 1 and d not in primes: return False
        return True
      
      # consider possible distances from P to B (prime, < 35)
      for d2 in primes.between(2, 34):
        # distance from A to P (prime, > 35)
        d1 = 70 - d2
        if d1 not in primes: continue
      
        # decompose the distances into individual sections
        for d2s in decompose(d2, list(primes.between(2, d2 - 1))):
          max_d = d2s[-1]
          for d1s in decompose(d1, list(diff(primes.between(2, max_d - 1), d2s))):
      
            # construct possible ordered sequences
            for (s1, s2) in cproduct(subsets(s, size=len, select='P') for s in (d1s, d2s[:-1])):
              ds = flatten([s1, [max_d], s2])
              if check(ds):
                # output solution
                printf("{ds}")
      

      Solution: The distances between consecutive locks (from A to B) are (in miles): 17, 13, 11, 19, 7, 3.

      Like

  • Unknown's avatar

    Jim Randell 4:25 pm on 13 October 2023 Permalink | Reply
    Tags:   

    Teaser 3186: Contemporary classmates 

    From The Sunday Times, 15th October 2023 [link] [link]

    In our local school, the year begins on September 1 and ends on August 31, and class one contains all the children who reach the age of five during the school year. I was looking back at the school records and discovered that one year during the 1980s, it so happened that the birthdays of a group of children in class one fell on the same-numbered day of different months and also on the same day of the week. The size of this group was the largest possible for these circumstances, and by coincidence the number of classmates in the group was also the number of the day in the month for their birthday.

    The oldest member of that group, whose birthday was in January, was born in the 1980s.

    What was the full date of birth of the youngest member of that group?

    [teaser3186]

     
    • Jim Randell's avatar

      Jim Randell 4:54 pm on 13 October 2023 Permalink | Reply

      This Python program finds maximal groups of birthdays sharing day-of-month and day-of-week for academic years starting in the 1980s, and then looks for maximal groups satisfying the remaining conditions.

      It runs in 76ms. (Internal runtime is 4.6ms).

      Run: [ @replit ]

      from datetime import (date, timedelta)
      from enigma import (defaultdict, Accumulator, irange, repeat, inc, seq2str, printf)
      
      # collect all maximal groups
      gs = Accumulator(fn=max, collect=1)
      
      # consider school years that start in the 80s
      for y in irange(1980, 1989):
      
        # record (day-of-month, day-of-week) -> [date, ...]
        g = defaultdict(list)
        for d in repeat(inc(timedelta(days=1)), date(y, 9, 1)):
          if d.year > y and d.month > 8: break
          g[(d.day, d.isoweekday())].append(d)
      
        # look for the longest lists
        r = Accumulator(fn=max, collect=1)
        for (k, vs) in g.items():
          r.accumulate_data(len(vs), k)
      
        # record them
        for k in r.data:
          gs.accumulate_data(r.value, g[k])
      
      # maximal group size
      n = gs.value
      printf("max group = {n}")
      
      # look for maximal groups where day-of-month = n
      # and the oldest member of the group has a birthday in January
      for ds in gs.data:
        d = ds[0]
        if not (d.day == n and d.month == 1): continue
        # calculate the dates of birth
        bs = list(d.replace(year=d.year - 5) for d in ds)
        # check the eldest is born in the 80s
        if not bs[0].year // 10 == 198: continue
        # output solution
        printf("birthdates = {bs}", bs=seq2str(bs))
      

      Solution: The youngest member of the group was born on: 3rd July 1983.

      The largest group of dates in a year that share the same day of the week and day of the month is 3.

      The relevant school year starts on 1st September 1987, and the 3 birthdays are:

      Sunday, 3rd January 1988 (DOB: Monday, 3rd January 1983)
      Sunday, 3rd April 1988 (DOB: Sunday, 3rd April 1983)
      Sunday, 3rd July 1988 (DOB: Sunday, 3rd July 1983)

      Like

  • Unknown's avatar

    Jim Randell 12:31 pm on 12 October 2023 Permalink | Reply
    Tags:   

    Teaser 2656: Wrong adding 

    From The Sunday Times, 18th August 2013 [link] [link]

    Here is an addition sum in which digits have consistently been replaced by letters, with different letters used for different digits. The six-figure total is even:

    Unfortunately, once again I have made a mistake. In just one place in the display I have written an incorrect letter.

    What is the correct numerical value of the six-figure total?

    [teaser2656]

     
    • Jim Randell's avatar

      Jim Randell 12:31 pm on 12 October 2023 Permalink | Reply

      I’ve added the [[ bungled_sum() ]] solver (from Puzzle 56 etc.) to the enigma.py library as a class method on [[ SubstitutedSum ]], and also allowed it to be called from the command line.

      Using this solver we find there are two possibilities for as single letter mistake in the translation.

      The following command line executes in 270ms.

      Run: [ @replit ]

      % python3 -m enigma -r SubstitutedSum.bungled_sum "AGAIN + WRONG = ADDING"
      
                  G
      AGAIN + WRONI = ADDING / @[1,4] G -> I
      14195 + 86759 = 100954 / A=1 D=0 G=4 I=9 N=5 O=7 R=6 W=8
      
                           G
      AGAIN + WRONG = ADDINA / @[2,5] G -> A
      16195 + 84756 = 100951 / A=1 D=0 G=6 I=9 N=5 O=7 R=4 W=8
      

      But we are told that in the original numerical sum the result is even, so we are looking at the first possibility.

      Solution: The result of the sum is 100954.

      The original sum was: 14195 + 86759 = 100954.

      And it should have been encoded as: AGAIN + WRONI = ADDING.

      Like

  • Unknown's avatar

    Jim Randell 1:24 pm on 10 October 2023 Permalink | Reply
    Tags:   

    Brainteaser 1702: And now not one is… 

    From The Sunday Times, 30th April 1995 [link]

    In what follows, digits have been replaced by letters, a different letter being used consistently for each different digit. There are no zeros.

    Each of the recurring decimals:

    .ANDANDAND
    .NOWNOWNOW
    .NOTNOTNOT
    .ONEONEONE
    .ISISISIS

    equals a fraction with a denominator less than 50. And now not one is easy to find.

    Please find what is DONE.

    [teaser1702]

     
    • Jim Randell's avatar

      Jim Randell 1:25 pm on 10 October 2023 Permalink | Reply

      We have:

      0.(ab)… = ab / 99
      0.(abc)… = abc / 999

      Here is a run file that uses the [[ SubstitutedExpression ]] solver from the enigma.py library to solve the puzzle.

      It runs in 86ms. (Internal runtime of the generated program is 14ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      
      # valid fractions have denominators less than 50
      --code="valid = lambda a, b: Fraction(a, b).den < 50"
      
      "valid(AND, 999)"
      "valid(NOW, 999)"
      "valid(NOT, 999)"
      "valid(ONE, 999)"
      "valid(IS, 99)"
      
      --answer="DONE"
      

      Solution: DONE = 4925.

      The fractions are:

      0.(AND)… = 324/999 = 12/37
      0.(NOW)… = 296/999 = 8/27 [*]
      0.(NOT)… = 297/999 = 11/37 [*]
      0.(ONE)… = 925/999 = 25/27
      0.(IS)… = 81/99 = 9/11 [*]
      0.(IS)… = 18/99 = 2/11 [*]

      [*] NOW and NOT can be interchanged, and the digits of IS can appear in either order, which accounts for the four solutions found by the program.

      Like

  • Unknown's avatar

    Jim Randell 12:28 pm on 8 October 2023 Permalink | Reply
    Tags:   

    Teaser 1866: Which Sunday paper? 

    From The Sunday Times, 21st June 1998 [link]

    Today you have to do a usual digits-for-letters substitution to make sense of the sum:

    SUN + DAYS = ?????

    I have not yet decided which Sunday paper the right-hand side is going to be. It will be one of GRAPH, MATCH, SCENE, SEEDY, SPORT, TIMES, TODAY, WORLD and WYNDY.

    But I must choose it so that the five-figure total is uniquely determined.

    Which word should be the right-hand side, and then what would the numerical total be?

    [teaser1866]

     
    • Jim Randell's avatar

      Jim Randell 12:29 pm on 8 October 2023 Permalink | Reply

      This Python program uses the [[ SubstitutedExpression.split_sum() ]] solver from the enigma.py library to try each of the candidate alphametic sums, and looks for ones that have a single solution.

      It runs in 82ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, singleton, sprintf, printf)
      
      # possible words
      words = "GRAPH MATCH SCENE SEEDY SPORT TIMES TODAY WORLD WYNDY".split()
      
      # choose a result
      for w in words:
        # construct a puzzle using the word
        expr = sprintf("SUN + DAYS = {w}")
        p = SubstitutedExpression.split_sum(expr).solver()
        # that has a single solution
        s = singleton(p.solve(verbose=0))
        if s is None: continue
        # output solution
        printf("{expr} / {s}", s=p.substitute(s, expr))
      

      Solution: The alphametic total is: WYNDY, and the numerical total is: 10390.

      So we have:

      SUN + DAYS = WYNDY → 783 + 9607 = 10390

      The remaining result words have no solutions, apart from WORLD, which has 6.

      Like

  • Unknown's avatar

    Jim Randell 4:42 pm on 6 October 2023 Permalink | Reply
    Tags:   

    Teaser 3185: Multiple squares 

    From The Sunday Times, 8th October 2023 [link] [link]

    My grandson likes to compile 3×3 magic squares, where each of the three rows of numbers, each of the three columns of numbers and both of the straight diagonals, add up to the same total. This he did without repeating any of the nine numbers in the square.

    He has now progressed to compiling similar 3×3 squares, which instead of the eight rows, columns and diagonals of numbers adding to the same total, they instead multiply to produce the same product.

    In his first such square this product was 32,768. He was able to find every square of nine different whole numbers that gives this product, excluding identical rotational and mirrored squares.

    What, in ascending order, were the totals of the nine numbers in each of his different squares?

    [teaser3185]

     
    • Jim Randell's avatar

      Jim Randell 5:09 pm on 6 October 2023 Permalink | Reply

      32768 = 2^15, so each multiplicative square is filled with numbers that are powers of 2. And we can treat it as an additive magic square of the corresponding exponents.

      So we just need to find how many essentially different additive magic squares there are (modulo rotation and reflection) with a magic constant of 15. And we can then transform the exponents back to powers of 2 to give the required answers. (Note we allow an exponent of 0 to give a corresponding value of 2^0 = 1).

      This Python program uses the [[ SubstitutedExpression() ]] solver from the enigma.py library to generate all possible additive magic squares of exponents, and then collects those that are different, and we then transform the exponents back to powers of two and sum the numbers in the corresponding multiplicative magic square to give the required total.

      It runs in 77ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, irange, uniq, printf)
      
      # orientations of a square
      squares = [
        # rotations
        (0, 1, 2, 3, 4, 5, 6, 7, 8),
        (2, 5, 8, 1, 4, 7, 0, 3, 6),
        (8, 7, 6, 5, 4, 3, 2, 1, 0),
        (6, 3, 0, 7, 4, 1, 8, 5, 2),
        # reflections
        (2, 1, 0, 5, 4, 3, 8, 7, 6),
        (8, 5, 2, 7, 4, 1, 6, 3, 0),
        (6, 7, 8, 3, 4, 5, 0, 1, 2),
        (0, 3, 6, 1, 4, 7, 2, 5, 8),
      ]
      
      # find the canonical form of a square
      def canonical(sq):
        return min(tuple(sq[j] for j in js) for js in squares)
      
      # find possible squares
      p = SubstitutedExpression(
        [
          # rows
          "A + B + C == 15",
          "D + E + F == 15",
          "G + H + I == 15",
          # columns
          "A + D + G == 15",
          "B + E + H == 15",
          "C + F + I == 15",
          # diagonals
          "A + E + I == 15",
          "C + E + G == 15",
        ],
        base=11,
        answer="(A, B, C, D, E, F, G, H, I)"
      )
      
      # collect results
      ts = list()
      
      # find different squares
      for sq in uniq(canonical(sq) for sq in p.answers(verbose=0)):
        # transform the powers to numbers, and sum them
        t = sum(2**n for n in sq)
        printf("[{sq} -> {t}]")
        ts.append(t)
      
      # output solution
      printf("totals = {ts}", ts=sorted(ts))
      

      Solution: The totals of the numbers in the different squares are: 1022, 1533, 1911.

      And the corresponding squares are:

      Like

    • Hugo's avatar

      Hugo 9:26 am on 15 October 2023 Permalink | Reply

      If we halve each number in the first of those squares, we get one with magic product 4096.
      But that’s certainly not the smallest possible value, as Dudeney showed:
      see the solution and comments to Puzzle no. 203.

      Like

  • Unknown's avatar

    Jim Randell 8:59 am on 4 October 2023 Permalink | Reply
    Tags:   

    Teaser 2496: [Letter values] 

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

    I have chosen 10 different letters of the alphabet before U to represent the digits 0 to 9. I can tell you that 0’s letter is in the word NOUGHT, 1’s letter is in the word ONE, 2’s letter is in TWO, and so on up to 9’s letter being in the word NINE.

    Furthermore, I can write down a particular five-figure number that is divisible by 8, and replacing the digits by their letters I get EIGHT. Similarly, a four-figure number divisible by 9 translates to NINE.

    What number is represented by TEN?

    This puzzle was originally published with no title.

    [teaser2496]

     
    • Jim Randell's avatar

      Jim Randell 9:00 am on 4 October 2023 Permalink | Reply

      We only need to consider the letters AT, and of these only ten of them appear in the words mentioned. So we can assign the digits 0-9 to the letters EFGHINORST (in some order).

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to do this, subject to the necessary conditions.

      The following run file executes in 126ms. (Internal runtime is 949µs).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "EIGHT % 8 = 0"
      "NINE % 9 = 0"
      
      "0 in {N, O, G, H, T}"
      "1 in {O, N, E}"
      "2 in {T, O}"
      "3 in {T, H, R, E}"
      "4 in {F, O, R}"
      "5 in {F, I, E}"
      "6 in {S, I}"
      "7 in {S, E, N}"
      "8 in {E, I, G, H, T}"
      "9 in {N, I, E}"
      
      --answer="TEN"
      --template=""
      

      Solution: TEN = 271.

      The assignment of digits to (bold) letters is:

      0 → (N) O (UGHT)
      1 → (O) N (E)
      2 → T (WO)
      3 → (T) H (REE)
      4 → (FOU) R
      5 → F (IVE)
      6 → S (IX)
      7 → (S) E (V) E (N)
      8 → (EI) G (HT)
      9 → (N) I (NE)

      And:

      EIGHT = 79832 = 8 × 9979
      NINE = 1917 = 9 × 213

      Like

  • Unknown's avatar

    Jim Randell 4:19 pm on 29 September 2023 Permalink | Reply
    Tags:   

    Teaser 3184: Four away 

    From The Sunday Times, 1st October 2023 [link] [link]

    The rectangular snooker table had four corner pockets and two centre pockets (one each in the middle of the left and right sides, which were 12ft long). A ball always left the cushions at the same angle as it struck them. The cue ball was by the bottom right corner pocket and Joe hit it off the left cushion; it bounced off another seven cushions without hitting a ball or entering a pocket, then entered the right centre pocket. Four away!

    Joe replayed the shot but the cue ball hit the left cushion further up the table than before, bounced another ten times without hitting a ball or entering a pocket, then entered the right centre pocket. Four away!

    How much further up the table did the second shot hit the left cushion?

    [teaser3184]

     
    • Jim Randell's avatar

      Jim Randell 5:02 pm on 29 September 2023 Permalink | Reply

      (See also: Teaser 1917 and Teaser 3129).

      Fairly straightforward with some graph paper to find paths between the bottom right and centre right pockets that hit exactly 8 and 11 cushions.

      These give the angle of the shot, from which we can calculate the distance between the respective first bounces, and this gives the answer to the puzzle.

      Run: [ @replit ]

      from enigma import (Rational, irange, is_coprime, printf)
      
      Q = Rational()
      
      # g: bounce -> [((x, y), angle), ...]
      g = { 8: [], 11: [] }
      
      # consider bounces between (0, 0) and (x, y)
      for y in irange(1, 24, step=2):
        for x in irange(2, 12, step=2):
          # check ball doesn't hit a pocket early
          if not is_coprime(x, y): continue
          # calculate number of bounces from (0, 0)
          b = (x - 1) + y // 2
          if b not in g: continue
          # calculate the angle
          t = Q(y, x)
          if t < 2:
            g[b].append(((x, y), t))
      
      # look for 8 bounces
      for ((x1, y1), t1) in g[8]:
        # look for 11 bounces, with a higher angle
        for ((x2, y2), t2) in g[11]:
          if not (t2 > t1): continue
          printf("(0, 0) -> ({x1}, {y1}) = 8 bounces [t = {t1}]")
          printf("-> (0, 0) -> ({x2}, {y2}) = 11 bounces [t = {t2}]")
          # calculate distance between first bounces
          d = 6 * (t2 - t1)
          printf("--> answer = {d:.2f} ft", d=float(d))
      

      Solution: The second shot hit the left cushion 4.5 feet further up the table than the first shot.

      In the following diagram the original table is in the bottom right corner, and the other rectangles are reflections of the table. We want to find paths between the red pocket in the extreme bottom right and a green pocket, that cross the boundaries of the table (the black lines) 8 and 11 times, but do not hit any intermediate pockets.

      The two paths are shown as dotted lines.

      We can denote the 8-bounce path is (8, 3) (i.e. 8 units horizontally, and 3 units vertically), and the 11-bounce path is (8, 9).

      There is a further 8-bounce path at (6, 7) and further 11-bounce path at (12, 1), but these are not involved in the answer to the puzzle.

      And here are the two paths (8-bounce = red, 11-bounce = green) plotted on the table:

      Like

  • Unknown's avatar

    Jim Randell 9:48 am on 28 September 2023 Permalink | Reply
    Tags:   

    Teaser 2641: Exchange rate 

    From The Sunday Times, 5th May 2013 [link] [link]

    I am going on holiday to Tezarland. So at our local Bureau de Change I exchanged a three-figure number of pounds for Tezar dollars. From the pounds I paid a £12 fee and received a whole number of Tezar dollars for each remaining pound.

    This was an unremarkable transaction except that it was an exchange in more ways than one: the number of Tezar dollars I received was like the number of pounds that I started with but with the digits in the reverse order.

    How many Tezar dollars did I receive?

    [teaser2641]

     
    • Jim Randell's avatar

      Jim Randell 9:49 am on 28 September 2023 Permalink | Reply

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

      The following run files executes in 68ms. (Internal runtime of the generated program is 703µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      # (ABC - 12) * rate = CBA
      
      SubstitutedExpression
      
      --distinct=""
      
      # rate is a whole number
      "div(CBA, ABC - 12) > 0"
      
      # answer is the number of dollars received
      --answer="CBA"
      

      Solution: You received 612 Tezar dollars.

      Like

  • Unknown's avatar

    Jim Randell 12:12 pm on 26 September 2023 Permalink | Reply
    Tags: ,   

    Teaser 2653: Tough guys 

    From The Sunday Times, 28th July 2013 [link] [link]

    The SS Thomas More has two identical vertical masts mounted on the centre line of the deck, the masts being a whole number of feet tall and seven feet apart. Two straight guy ropes secure the top of each mast to a single anchor point on the centre line of the deck some distance forward of the masts. The total length of the two ropes is a whole number of feet, with one rope being two feet longer than the other.

    What is the height of the masts?

    [teaser2653]

     
    • Jim Randell's avatar

      Jim Randell 12:13 pm on 26 September 2023 Permalink | Reply

      If the ropes are length x and (x + 2), then the total length (= 2x + 2) is a whole number of feet, so x is a whole number of semi-feet (i.e. units of 6 inches).

      So let’s do everything in semi-feet.

      From the 2 right-angled triangles we have:

      r² = d² + h²
      (r + 4)² = (d + 14)² + h²

      r = (7d + 45) / 2

      And as r is an integer, d must be an odd integer.

      This Python program looks for the smallest viable solution:

      It runs in 58ms. (Internal runtime is 200µs)

      Run: [ @replit ]

      from enigma import (irange, inf, div, ircs, printf)
      
      # generate candidate solutions
      def generate():
        # consider d values
        for d in irange(1, inf, step=2):
          r = div(7 * d + 45, 2)
          h = ircs(r, -d)
          x = div(h, 2)
          if h is None or x is None: continue
          # return solution
          yield (d, r, h, x)
      
      # find the first solution
      for (d, r, h, x) in generate():
        printf("height = {x} ft [d={d} r={r} -> h={h}]")
        break
      

      Solution: The masts are 30 ft tall.

      The ropes are 30.5 ft and 32.5 ft long (giving a total rope length of 63 ft), and they are anchored 5.5 ft away from the nearest mast.

      The next solution is found when the masts are 540 ft tall, which seems a bit impractical.


      Analytically we are looking for integer solutions to the equation:

      h² = [(7d + 45) / 2]² − d²

      where h is an even integer, say h = 2z (so z is the mast height in feet):

      (2z)² = (45/4)(d + 7)² − 45
      16z² − 45(d + 7)² = −180

      writing X = 4z (= 2h), Y = d + 7 we get a form of Pell’s equation:

      X² − 45 Y² = −180

      And we can use the pells.py module (from the py-enigma-plus repository – see: Teaser 2994) to quickly find larger solutions to the original equation:

      from enigma import (div, arg, ifirst, printf)
      import pells
      
      # generate (h, d, r, height) values
      def solve():
        # find solutions to: 16z^2 - 45(d + 7)^2 = -180
        for (z, d) in pells.diop_quad(16, -45, -180):
          d -= 7
          r = div(7 * d + 45, 2)
          if r and r > 0 and d > 0 and z > 0:
            yield (2 * z, d, r, z)
      
      # find the first N solutions
      N = arg(8, 0, int)
      for (i, (h, d, r, height)) in enumerate(ifirst(solve(), N), start=1):
        printf("[{i}] h={h} d={d} r={r} -> height = {height} ft")
      
      % time python3 teaser2653pells.py 8
      [1] h=60 d=11 r=61 -> height = 30 ft
      [2] h=1080 d=315 r=1125 -> height = 540 ft
      [3] h=19380 d=5771 r=20221 -> height = 9690 ft
      [4] h=347760 d=103675 r=362885 -> height = 173880 ft
      [5] h=6240300 d=1860491 r=6511741 -> height = 3120150 ft
      [6] h=111977640 d=33385275 r=116848485 -> height = 55988820 ft
      [7] h=2009357220 d=599074571 r=2096761021 -> height = 1004678610 ft
      [8] h=36056452320 d=10749957115 r=37624849925 -> height = 18028226160 ft
      python3 teaser2653pells.py 8  0.06s user 0.03s system 89% cpu 0.098 total
      

      Like

    • Frits's avatar

      Frits 7:49 pm on 26 September 2023 Permalink | Reply

       
      # 16z^2 - 45(d + 7)^2 = -180 
      # with X = 4z (= 2h), Y = d + 7 we get a form of Pell's equation:
      # X^2 - 45 Y^2 = -180 
      # the first soltion being the trivial one, X=0, Y=2 
      
      # Using Brian's method: https://brg.me.uk/?page_id=1468
      #
      # These solutions can also be generated recursively using:
      # x(n+1) = 9 x(n) + 60 y(n)
      # y(n+1) = 4/3 x(n) + 9 y(n)
      
      x, y = 0, 2
      for n in range(8):
        x, y = 9 * x + 60 * y, (4 * x) // 3 + 9 * y
        print(f"[{n + 1}] h={x // 2} d={y - 7} r={(7 * (y - 7) + 45 ) // 2} "
              f"-> height = {x // 4} ft")  
      

      Like

  • Unknown's avatar

    Jim Randell 12:50 pm on 24 September 2023 Permalink | Reply
    Tags:   

    Brainteaser 1095: “Seven anagrams” 

    From The Sunday Times, 31st July 1983 [link]

    Members of TOPIQ (the society with membership limited to those with a tested I.Q. above a specified figure), are required to test one another’s mental quickness with suitable problems from time to time. Once, when I was asked to set a problem, I recalled an old verse which holds several anagrams of the same six letters. The verse went something like this:

    Hushed was the RUSTLE of leaves in the breeze,
    Faded, the LUSTRE of light on the trees,
    As the SUTLER sat in his ULSTER grey,
    Quaffing his ale at the close of the day.
    Then he came to the end of his drinking song:
    “Thou RULEST the weak; thou LUREST the strong,
    John Barleycorn, my Jo!”

    In my problem the six anagrams were omitted. Members firstly had to find these. They were told that, if each of the six letters in the anagrams represented a different one of digits 1 to 6, then the sum of all the six-digit numbers of the anagrams was a 7-digit figure starting with LS and ending with UT. They were then asked the following question, solved by most members in under 10 minutes:

    Having, from the sum, solved the number-letter relationship, what number do you get for RESULT?

    What do you get?

    [teaser1095]

     
    • Jim Randell's avatar

      Jim Randell 12:51 pm on 24 September 2023 Permalink | Reply

      I used the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      I introduced additional symbols x, y, z, which can take on any value (not just 1-6) for the result of the sum.

      This run file executes in 71ms. (Internal runtime of the generated program is 1.4ms – or 467µs if you use [[ SubstitutedExpression.split_sum() ]]).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --symbols="ELRSTUxyz"
      --distinct="ELRSTU"
      --invalid="0|7|8|9,ELRSTU"
      
      "RUSTLE + LUSTRE + SUTLER + ULSTER + RULEST + LUREST = LSxyzUT"
      
      --answer="RESULT"
      

      Solution: RESULT = 254316.

      The numbers (in the order given) give the following sum:

      234615 + 134625 + 436152 + 314652 + 231546 + 132546 = 1484136

      Like

  • Unknown's avatar

    Jim Randell 4:27 pm on 22 September 2023 Permalink | Reply
    Tags:   

    Teaser 3183: Partial eclipse 

    From The Sunday Times, 24th September 2023 [link] [link]

    Alf, Bert and Charlie were comparing their experiences of the eclipse from different locations. The moon appeared to be exactly the same size as the sun as it passed in front of part of the sun’s disc. The magnitude of an eclipse measures the maximum part of the diameter of the sun’s disc that is obscured. Taking the apparent diameter of the sun as 100, they noted the following:

    Alf: “My magnitude was a whole number”.
    Bert: “If I took the part of the sun’s circumference that was obscured, multiplied it by the radius and then subtracted the area of the sun’s disc that was obscured, I also got a whole number”.
    Charlie: “Both of those statements are true for all of us, but we all saw different magnitudes greater than 10”.

    In increasing order, what were those three magnitudes?

    As well as this puzzle being Teaser 3183 there are now 3183 puzzles posted in total between the Enigmatic Code and S2T2 sites.

    [teaser3183]

     
    • Jim Randell's avatar

      Jim Randell 4:55 pm on 22 September 2023 Permalink | Reply

      If we suppose the apparent radius of the two discs is R, and we consider the part of the circumference of the sun that is obscured, then it forms the arc of a circular sector that subtends an angle of at the centre of the sun’s disc. The length of the obscured circumference is then: 2Rθ.

      The area of the part of the sun’s disc that is obscured A can be calculated as the sum of two equal obscured segments, each being the area of the circular sector less the area of the triangle formed by replacing the arc of the sector with a chord.

      A = 4 [(𝛑R²)(θ/2𝛑) − (R cos(θ))(R sin(θ))/2)
      A = 2R²θ − 2R²sin(θ)cos(θ)
      A = R²(2θ − sin(2θ))

      (See also: [@wikipedia])

      And so B’s measure is:

      B = 2R²θ − A

      B = 2R²sin(θ)cos(θ)
      B = R²sin(2θ)

      And A’s measure (the magnitude) m is given by:

      m = 2R(1 − cos(θ))

      So given an integer magnitude, we can calculate a rational value for cos(θ), and using the identity cos²(θ) + sin²(θ) = 1 we can calculate a value for sin(θ), which must also be rational if B is to have an integer value.

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

      from enigma import (Rational, irange, is_square_q, sq, as_int, printf)
      
      Q = Rational()
      
      # apparent radius
      R = 50
      
      # consider possible magnitudes from 11 to 99
      for m in irange(11, 99):
        # calculate cos(theta), sin(theta)
        cost = 1 - Q(m, 2 * R)
        sint = is_square_q(1 - sq(cost), F=Q)
        if sint is None: continue
        # calculate B's measure
        B = 2 * sq(R) * sint * cost
        # is it an integer?
        B = as_int(B, default=None)
        if B is None: continue
        # output solution
        printf("m={m} -> B={B}")
      

      Solution: The magnitudes are: 20, 40, 72.

      And we have:

      m = 20 → B = 2400
      m = 40 → B = 2400
      m = 72 → B = 1344

      There are only 3 values because the “partner” value for m = 72 (which also gives B = 1344) is m = 4.

      Like

      • Frits's avatar

        Frits 8:47 pm on 22 September 2023 Permalink | Reply

        I had to look up some formula and came to the same formula for B.

           
        # apparent radius
        R = 50
        
        # consider possible magnitudes from 11 to 99
        for m in range(11, 100):
          # calculate cos(theta)
          c = 1.0 - m / (2 * R)
          # calculate sin(theta), c^2 + s^2 = 1
          s = (1 - c**2)**.5
          # B = r^2.sin(2 * theta), sin(2x) = 2.sin(x).cos(x) 
          B = 2 * R * R * s * c
          
          # is it close to an integer?
          if abs(B - round(B)) > 1e-6: continue
         
          # output solution
          print(f"m={m} -> B={B:.2f}")
        

        Like

      • Jim Randell's avatar

        Jim Randell 2:38 pm on 23 September 2023 Permalink | Reply

        With a bit more work we can simplify the calculation of B to:

        B = (1/2)(2R − m)√(m(4R − m))

        Which gives a simpler program, that only uses integers:

        Run: [ @replit ]

        from enigma import (irange, is_square, div, printf)
        
        # apparent radius
        R = 50
        
        # consider possible magnitudes from 11 to 99
        for m in irange(11, 99):
          r = is_square(m * (4 * R - m))
          if r is None: continue
          B = div((2 * R - m) * r, 2)
          if B is None: continue
          # output solution
          printf("m={m} -> B={B}")
        

        Like

  • Unknown's avatar

    Jim Randell 9:32 am on 21 September 2023 Permalink | Reply
    Tags: by: A. H. R. Grimsey   

    Brainteaser 1485: Safe depository 

    From The Sunday Times, 24th February 1991 [link]

    The number of my safe deposit box in the Norsex Bank has four different digits, decreasing from left to right. Curiously, that of my fiancée has the same figures but in the reverse order. The bank manager, Mr K. Aprekar, has said that when we are married next month, he will give us a presently-vacant box number which not only contains the four digits we now share, but is in fact the difference between our two numbers.

    What is it?

    [teaser1485]

     
    • Jim Randell's avatar

      Jim Randell 9:32 am on 21 September 2023 Permalink | Reply

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

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

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # suppose the current numbers are: ABCD and DCBA
      
      # numbers are decreasing
      "A > B" "B > C" "C > D"
      
      # the new number (= the difference between the original two numbers)
      --macro="@new = ABCD - DCBA"
      # is composed of the same digits
      "set(nsplit(@new, 4)) == {A, B, C, D}"
      
      --answer="@new"
      

      Solution: The new box number is 6174.

      We have:

      7641 − 1467 = 6174

      Like

  • Unknown's avatar

    Jim Randell 12:33 pm on 19 September 2023 Permalink | Reply
    Tags: ,   

    Teaser 2642: New world order 

    From The Sunday Times, 12th May 2013 [link] [link]

    In maths class Pat has been learning about progressions from his “New World” text book. The book has three sections, namely “Arithmetic”, “Algebra” and “Geometry” which overall run from page 1 to page 500 inclusive — with each section starting on a new page. The geometry section is over twice as long as the arithmetic section.

    As an exercise Pat calculated the sum of the page numbers in each section and he was surprised to find that the sum of the page numbers in “Algebra” was double that of the sum of the page numbers in “Geometry”.

    How many pages are there in the “Arithmetic” section?

    [teaser2642]

     
    • Jim Randell's avatar

      Jim Randell 12:34 pm on 19 September 2023 Permalink | Reply

      The total sum of all the page numbers = T = tri(500).

      If the Arithmetic section is pages 1 .. x, the Algebra section pages (x + 1) .. y, and the Geometry section pages (y + 1) .. 500, then the sums of the page numbers in each section are:

      Ar = tri(x)
      Al = tri(y) − tri(x)
      Ge = T − tri(y)

      tri(y) = T − Ge

      and:

      Al = 2 Ge

      Ge = (T − Ar) / 3

      So, by choosing a value for x, we can calculate the values of Ar, Al, Ge and hence y.

      This Python program runs in 55ms. (Internal runtime is 267µs).

      Run: [ @replit ]

      from enigma import (irange, tri, div, is_triangular, printf)
      
      # Ar: sum(1 .. x) = tri(x)
      # Al: sum(x + 1 .. y) = tri(y) - tri(x)
      # Ge: sum(y + 1 .. 500) = tri(500) - tri(y)
      #
      # Al = 2 * Ge
      
      # total sum of all pages
      T = tri(500)
      
      # consider the end page for arithmetic = x
      for x in irange(2, 165):
        Ar = tri(x)
        Ge = div(T - Ar, 3)
        if Ge is None: continue
        # calculate y
        y = is_triangular(T - Ge)
        if y is None or not x < y < 500: continue
        if not (500 - y > 2 * x): continue
        # output solution
        printf("Ar = 1..{x}; Al = {x1}..{y}; Ge = {y1}..500", x1=x + 1, y1=y + 1)
      

      Solution: There are 45 pages in the Arithmetic section.

      So the sections are:

      Arithmetic: pages 1 – 45 (= 45 pages); sum = 1035
      Algebra: pages 46 – 409 (= 364 pages); sum = 82810 (= Geometry × 2)
      Geometry: pages 410 – 500 (= 91 pages); sum = 41405

      Like

  • Unknown's avatar

    Jim Randell 9:32 am on 17 September 2023 Permalink | Reply
    Tags:   

    Brain-Teaser 919: Golden age of steam 

    From The Sunday Times, 2nd March 1980 [link]

    Ever since the dawn of history, mankind has looked back to a Golden Age when everybody and everything was so much better.

    As far as our railway system is concerned, the Golden Age was the prewar era of the great steam locomotives. In spite of their noise, steam, and smoke pollution, (or perhaps even because of these characteristics), the older generation look back with nostalgia to that golden era. Let us therefore pose a not-too-difficult problem concerning those wonderful days.

    Two steam locomotives, each travelling at 50 mph, are driven by Tom and Dick. They are approaching Harry’s signal box from opposite directions.

    Tom sounds his whistle. Immediately upon hearing it, Dick replies. Harry hears both whistles with an interval of seven seconds between them. The two engines later pass the signal box at the same instant.

    You may assume that the speed of sound was 1100 feet per second relative to the ground. One mile has 5280 feet.

    How far (in feet) was Tom from the box when he sounded his whistle?

    This puzzle is included in the book The Sunday Times Book of Brainteasers (1994). The puzzle text above is taken from the book.

    [teaser919]

     
    • Jim Randell's avatar

      Jim Randell 9:33 am on 17 September 2023 Permalink | Reply

      The trains are travelling at the same speed, and (presumably on different tracks) pass the signal box at the same time, so they must start the same distance from the signal box.

      Working in feet per second, a speed of 50 mph = 220/3 ft/s.

      Suppose at time t = 0 both T and D are a distance x feet from H, and T sounds his whistle.

      At a time t = x / 1100 H hears T’s whistle.

      And if D hears the whistle when he is a distance y from H, then he hears T’s whistle at time t = (x + y) / 1100.

      D instantaneously responds (still at distance y), and H hears D’s whistle at time t = (x + 2y) / 1100.

      H hears the whistles 7 seconds apart, so:

      (x + 2y) / 1100 = 7 + x / 1100
      y = (7/2)1100 = 3850

      So D sounds his whistle when he is 3850 ft from H at time t = (x + 3850) / 1100.

      And at this time T must also be 3850 ft from H.

      Hence:

      x = 3850 + (220/3)(x + 3850) / 1100
      ⇒ x = 4400

      Solution: Tom was 4400 ft from the signal box when he sounded his whistle.

      Like

    • Hugo's avatar

      Hugo 9:49 am on 17 September 2023 Permalink | Reply

      It must have been a cold day: the speed of sound is about 1100 ft/s at 5°C and 60% relative humidity
      (about 3 ft/s less in dry air at that temperature).

      Like

  • Unknown's avatar

    Jim Randell 4:45 pm on 15 September 2023 Permalink | Reply
    Tags:   

    Teaser 3182: Stand and be counted 

    From The Sunday Times, 17th September 2023 [link] [link]

    When bad light ended the first day’s play, the total score was 182 with three batters having been “out” during the day.

    In the successive stands, the lower-numbered and the higher-numbered batters scored in the ratios 7:3, 10:7, 1:11 and 11:19, in that order. The total score was the sum of the scores of the five batters.

    The third batter was the highest scorer.

    [In cricket the batting team starts with batters 1 and 2 together, and each is able to score. When one of them is “out” the third batter comes “in” to replace them, when one of that pair is out the fourth batter comes in, and so on. The amount by which the team score has increased while a particular pair is together is called the “stand”].

    What were the scores of all the batters, in their batting order?

    [teaser3182]

     
    • Jim Randell's avatar

      Jim Randell 5:14 pm on 15 September 2023 Permalink | Reply

      This Python program works through each partnership, considering stands of the specified ratios.

      It runs in 60ms. (Internal runtime is 1.5ms).

      Run: [ @replit ]

      from enigma import (irange, inf, div, map2str, printf)
      
      # total score
      T = 182
      
      # add deltas <ds> to keys <ks> of dict <d>
      def add(d, ks, ds):
        d = dict(d)
        for (k, i) in zip(ks, ds):
          d[k] += i
        return d
      
      # n = number of next bat
      # A = current lower numbered bat
      # B = current higher numbered bat
      # rs = remaining stand ratios
      # ss = current scores per bat
      # t = total runs scored
      def solve(n, A, B, rs, ss, t):
        # choose scores for this stand
        (a, b) = rs[0]
        # remaining ratios
        rs_ = rs[1:]
        if rs_:
          # minimum remaining total
          rt = sum(a + b for (a, b) in rs_)
          # consider possible multipliers for this stand
          for k in irange(1, inf):
            (rA, rB) = (a * k, b * k)
            t_ = t + rA + rB
            if t_ + rt > T: break
            ss_ = add(ss, [A, B], [rA, rB])
            # each batsman may be dismissed
            yield from solve(n + 1, A, n, rs_, ss_, t_)
            yield from solve(n + 1, B, n, rs_, ss_, t_)
        else:
          # can we achieve the total?
          k = div(T - t, a + b)
          if k is not None:
            yield add(ss, [A, B], [a * k, b * k])
      
      rs = [(7, 3), (10, 7), (1, 11), (11, 19)]  # stand ratios
      ss = dict((k, 0) for k in irange(1, 5))  # scores
      
      # find totals for each batsman
      for ss in solve(3, 1, 2, rs, ss, 0):
        # batsman 3 scored the most
        if max(ss.values()) != ss[3]: continue
        # output solution
        printf("{ss}", ss=map2str(ss, sep=" ", enc=""))
      

      Solution: The totals are: bat 1 = 21; bat 2 = 49; bat 3 = 52; bat 4 = 22; bat 5 = 38.

      The stands are:

      1: bat 1 = 21; bat 2 = 9; ratio = 7:3; bat 1 dismissed
      2: bat 2 = 40; bat 3 = 28; ratio = 10:7; bat 2 dismissed
      3: bat 3 = 2; bat 4 = 22; ratio = 1:11; bat 4 dismissed
      4: bat 3 = 22; bat 5 = 38; ratio = 11:19; total = 182 runs

      Like

      • Jim Randell's avatar

        Jim Randell 7:18 pm on 15 September 2023 Permalink | Reply

        Here’s an alternative (faster) version.

        We can generate possible batting pairings for each stand. And the denominators of the ratios are all different, so we can use the [[ express() ]] function to determine the multipliers for each stand.

        We then use the ratios and the multiplies to calculate the scores for each batsman.

        This Python program runs in 58ms, and has an internal runtime of 386µs.

        from enigma import (irange, subsets, express, map2str, printf)
        
        # generate possible pairings
        def _pairs(k, A, B, n, ds=[]):
          ds = ds + [(A, B)]
          # are we done?
          if k < 1:
            yield ds
          else:
            # one of A and B is dismissed
            yield from _pairs(k - 1, B, n, n + 1, ds)
            yield from _pairs(k - 1, A, n, n + 1, ds)
        
        pairs = list(_pairs(3, 1, 2, 3))
        
        # calculate scores with a sequence of multipliers and a sequence of pairs
        def scores(ks, ps):
          ss = dict((i, 0) for i in irange(1, 5))
          for (k, (a, b), (x, y)) in zip(ks, ps, [(7, 3), (10, 7), (1, 11), (11, 19)]):
            ss[a] += k * x
            ss[b] += k * y
          return ss
        
        # generate possible multipliers for each stand
        for (k1, k3, k2, k4) in express(182, [10, 12, 17, 30], min_q=1):
          for ps in pairs:
            ss = scores((k1, k2, k3, k4), ps)
            if ss[3] == max(ss.values()):
              printf("{ss}", ss=map2str(ss, sep=" ", enc=""))
        

        Like

    • Frits's avatar

      Frits 6:01 pm on 15 September 2023 Permalink | Reply

      from enigma import SubstitutedExpression
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          # 10 * W + 17 * X + 12 * Y + 30 * Z = total score 182
          "div(182 - (10 * W + 17 * X + 12 * Y), 30) == Z",
          
          # who whon the first session
          "F < 3",
          # who whon the third session
          "2 < T < 5",
          
          # third batter must have won in 2nd session
          
          # possible score for the first  batter 7 * W + (F == 1) * 10 * X
          # possible score for the second batter 3 * W + (F == 2) * 10 * X 
          # possible score for the third  batter 7 * X + Y + 11 * (T == 3) * Z
          # possible score for the fourth batter 11 * Y + (T == 4) * 11 * Z
          #          score for the fifth  batter 19 * Z
          
          # the third batter was the highest scorer
          "max(sc := [7 * W + (F == 1) * 10 * X, \
                      3 * W + (F == 2) * 10 * X , \
                      7 * X + Y + 11 * (T == 3) * Z, \
                      11 * Y + (T == 4) * 11 * Z, \
                      19 * Z]) == sc[2]", 
        ],
        answer="sc",
        distinct="",
        digits=range(1, 10),
        reorder=0,
        verbose=0,    # use 256 to see the generated code
      )
      
      # print answers
      for ans in p.answers():
        print(f"answer: {ans}")   
      

      Like

      • Frits's avatar

        Frits 6:17 pm on 15 September 2023 Permalink | Reply

        @Jim, Thank you for leaving the easy solution (SubstitutedExpression) for me.

        Like

      • Frits's avatar

        Frits 6:30 pm on 15 September 2023 Permalink | Reply

        Theoretically I should have used base=13 and digits=range(1, 13) to allow for high W and Y’s.

        Like

    • Frits's avatar

      Frits 11:49 am on 16 September 2023 Permalink | Reply

      A more general version without analysis.

      from enigma import SubstitutedExpression
      
      # ratio sums
      rs = [10, 17, 12, 30]
      multipliers = "WXYZ"
      
      # maxima per multiplier
      d = {multipliers[i]: (182 - sum(rs) + rs[i]) // rs[i] for i in range(4)}    
      mx = max(d.values())
      
      # invalid digit / symbol assignments
      d2i = {n: set(k for k, v in d.items() if n > v) for n in range(1, mx + 1)} 
      # F/S/T were not "out" in the 1st/2nd/3rd play
      for i, v in enumerate("FST"):
        for j in range(3 + i, mx + 1):
          d2i[j].add(v)
      
      # the alphametic puzzle, use multipliers W, X, Y and Z
      p = SubstitutedExpression(
        [ 
          # 10 * W + 17 * X + 12 * Y + 30 * Z = total score 182
          "div(182 - (17 * X + 12 * Y + 30 * Z), 10) = W",
          
          # S wasn't "out" in the second play
          "S in {F, 3}",
          # T wasn't "out" in the third play
          "T in {S, 4}",
          
          # score for the first  batter 7W + (F == 1).10X + (S == 1).Y + (T == 1).11Z
          # score for the second batter 3W + (F == 2).10X + (S == 2).Y + (T == 2).11Z
          # score for the third  batter                7X + (S == 3).Y + (T == 3).11Z
          # score for the fourth batter                            11Y + (T == 4).11Z
          # score for the fifth  batter                                           19Z
          
          # the third batter was the highest scorer
          "max(sc := [7 * W + (F == 1) * 10 * X + (S == 1) * Y + (T == 1) * 11 * Z, \
                      3 * W + (F == 2) * 10 * X + (S == 2) * Y + (T == 2) * 11 * Z, \
                                          7 * X + (S == 3) * Y + (T == 3) * 11 * Z, \
                                                        11 * Y + (T == 4) * 11 * Z, \
                                                                            19 * Z])\
               == sc[2]", 
        ],
        answer="sc",
        d2i=d2i,
        distinct="",
        base=mx + 1,
        digits=range(1, mx + 1),
        reorder=0,
        verbose=0,    # use 256 to see the generated code
      )
      
      # print answers
      for ans in p.answers():
        print(f"answer: {ans}")   
      

      Like

    • Jim Olson's avatar

      Jim Olson 3:46 am on 26 September 2023 Permalink | Reply

      What is the correct answer?

      Like

  • Unknown's avatar

    Jim Randell 8:35 am on 14 September 2023 Permalink | Reply
    Tags: by: J David Jones   

    Teaser 2008: Divide and add 

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

    In the following arithmetic the fractions have four digits in the numerator and denominator, and the digits have been replaced consistently by letters. with different letters used for different digits.

    ETON/NOTE + TANG/GNAT

    Each fraction is a whole number.

    What is the total?

    [teaser2008]

     
    • Jim Randell's avatar

      Jim Randell 8:36 am on 14 September 2023 Permalink | Reply

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

      The following run file executes in 72ms. (Internal runtime of the generated program is 1.2ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "ETON % NOTE = 0"
      "TANG % GNAT = 0"
      
      --answer="(ETON // NOTE) + (TANG // GNAT)"
      

      Solution: The total is: 13.

      The expression is:

      9801/1089 + 8712/2178 = 9 + 4 = 13

      Like

  • Unknown's avatar

    Jim Randell 10:12 am on 12 September 2023 Permalink | Reply
    Tags:   

    Teaser 2651: Parsnip Farm 

    From The Sunday Times, 14th July 2013 [link] [link]

    On Parsnip Farm there was a triangular field with one of its boundaries running north to south. From that boundary’s southernmost point Farmer Giles built a fence in an easterly direction, thus partitioning the field into two smaller triangular fields, with the area of the northern field being forty per cent of the area of the southern field. Each side of each field was a whole number of furlongs in length, one of the sides of the southern field being twenty-five furlongs in length.

    What are the lengths of the other two sides of the southern field?

    [teaser2651]

     
    • Jim Randell's avatar

      Jim Randell 10:12 am on 12 September 2023 Permalink | Reply

      If the area of the original field is A, then it is split such that the northern field has an area N = (2/7)A and the southern field S = (5/7)A.

      The boundaries of N are all integers and so form a Pythagorean triple (x, y, z).

      And we can expand the northern field by a factor of f until its hypotenuse exactly covers the boundary of the original field, and we get this:

      The we can then write the area of the original field in two ways:

      A = (7/2)N = 7xy / 4
      A = fxy / 2

      f = 7/2

      And we can calculate the unknown sides of S:

      z′ = (f − 1)z = 5z/2
      y′ = hypot(7x/2, 5y/2) = hypot(7x, 5y) / 2

      This Python program runs in 58ms. (Internal runtime is 334µs).

      Run: [ @replit ]

      from enigma import (pythagorean_triples, div, ihypot, ordered, printf)
      
      for (a, b, z) in pythagorean_triples(100):
        z_ = div(5 * z, 2)
        if z_ is None: continue
        N = ordered(a, b, z)
        for (x, y) in [(a, b), (b, a)]:
          y_ = div(ihypot(7 * x, 5 * y), 2)
          if y_ is None: continue
          S = ordered(x, z_, y_)
          if 25 in S:
            # output solution
            printf("N = {N}; S = {S}")
      

      Solution: The other two sides of the southern field are 6 and 29 furlongs.

      N is a (6, 8, 10) triangle, (area = 24 square furlongs ≈ 0.97 sq km).

      S is a (6, 25, 29) triangle, (area = 60 square furlongs ≈ 2.43 sq km).

      So the original field was a (8, 29, 35) triangle, (area = 84 square furlongs ≈ 3.40 sq km).

      Like

    • Frits's avatar

      Frits 5:44 pm on 12 September 2023 Permalink | Reply

      Avoiding imports.

       
      is_square = lambda n: int(rt := n**.5) == rt
      
      # generate Pythagoren triples for a fixed hypothenuse
      def triples(h):
        sqs = {i * i for i in range(1, h)}
        return [(int(s1**.5), int(s2**.5)) for s1 in sqs 
                 if (s2 := h**2 - s1) >= s1 and s2 in sqs]
        
      side = 25
      
      # can x be 25?  
      # then f.x = 87.5, y' can never be a whole number  (f = 3.5)
      # if other side (2.5y) is k.0 or k.5, so x != 25
      
      # can y' be 25?
      for c, d in triples(side): 
         # are these lenghts multiples of 2.5 and 3.5?
        x, y = -1, -1
        if (c % 2.5 == 0 and d % 3.5 == 0):
          y, x = c // 2.5, d // 3.5    
        if ((c % 3.5 == 0 and d % 2.5 == 0)):
          x, y = c // 3.5, d // 2.5    
        if x == -1: continue
        
        # y' can be 25
        if is_square(z2 := x**2 + y**2):
          z = int(z2**.5)
          if z % 2 == 0:
            print(f"answer {int(x)} and {int(2.5 * z)} furlongs")
        
      # can z' be 25?
      if side % 5: exit(0)
      z = int(side * 2 / 5)
      
      ts = triples(z)
      # determine z'
      for x, y in ts + [x[::-1] for x in ts]:
        if is_square(y1 := 3.5**2 * x**2 + 2.5**2 * y**2):
          print(f"answer {int(x)} and {int(y1**.5)} furlongs")  
      

      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