Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • 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).

      Run: [ @replit ]

      #! 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

  • Unknown's avatar

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

    Brain-Teaser 928: Confused contracts 

    From The Sunday Times, 4th May 1980 [link]

    The results of the hands in a bridge tournament were written like this: “Joe Smith, two ♥”. Before I could calculate the scores, my dog tore up the results of the last four players. Fortunately the writing remained legible. By manoeuvring the fragments, and knowing there had been one contract in each suit, and one at each of the one, two, three and four levels, I was able to construct various feasible combinations.

    I first tried putting ♦ opposite Ted, Pete and Reg in turn. Ted could have ♦ only when Mr Green had the two contract; Pete only when ♠ were four; and Reg only when Mr Black had the one. Similarly, if Reg or Mr Black had ♣, then Mr Green had four; If Mr White had ♥ or ♣ then Mr Black had the other; if Reg had ♥ then Pete had ♦; if Mr Brown had ♥ then Mr Black had four; if Mr Black had ♥ then Mr Green had two; and if Mr Black didn’t have ♥ then ♠ were in less than four.

    Mr Black could have one only when Pete had three ♠; Mr Green could have two only when ♥ were four; Mr Black had four only when Reg had ♦; Mr Green had four only when ♦ were in three; and ♣ could be in three or four only when Reg had ♥.

    Finally I noted that Ted and Vic had the same coloured suits whenever I tried putting ♠ with the three, but different coloured suits if Mr White did not have ♥.

    Luckily, I then remembered that ♦ had actually been bid at the four level.

    What was the suit of the three contract, and what was the full name of the player who bid it?

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

    [teaser928]

     
    • Jim Randell's avatar

      Jim Randell 8:06 am on 10 September 2023 Permalink | Reply

      I know nothing of Bridge, so at first the puzzle text made no sense to me, but there is a handy hint in the 1974 book:

      Ignoring the Bridge jargon, you have to match each of the numbers 1 to 4 with one of the suits, a forename, and a surname.

      I used the [[ SubstitutedExpression ]] solver to allocate the values among the three groups, so all we need to do is translate the text into a collection of constraints which we can represent by Python expressions.

      I translated: “If A had X or Y, then B had the other” into: “(A = X) ⇒ (B = Y)” and “(A = Y) ⇒ (B = X)”.

      Note that we might expect to deduce “Reg != Black” from the statement “if Reg or Mr Black had …”, but if we try this then there are no solutions.

      The following run file executes in 66ms. (Internal runtime of the generated code is 185µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      # allocate values 1-4 to the following:
      #
      #  suits    : C D S H [Clubs, Diamonds, Spades, Hearts]
      #  forenames: T R P V [Ted, Reg, Pete, Vic]
      #  surnames : B K G W [Brown, blacK, Green, White]
      
      SubstitutedExpression
      
      --digits="1-4"
      --distinct="CDSH,TRPV,BKGW"
      
      # specify the conditions
      "implies(T == D, G == 2)"
      "implies(P == D, S == 4)"
      "implies(R == D, K == 1)"
      "implies(R == C or K == C, G == 4)"
      "implies(W == H, K == C)" "implies(W == C, K == H)"
      "implies(R == H, P == D)"
      "implies(B == H, K == 4)"
      "implies(K == H, G == 2)"
      "implies(K != H, S != 4)"
      "implies(K == 1, P == 3 == S)"
      "implies(G == 2, H == 4)"
      "implies(K == 4, R == D)"
      "implies(G == 4, D == 3)"
      "implies(C == 3 or C == 4, R == H)"
      "implies(S == 3, {T, V} in [{C, S}, {D, H}])"
      "implies(W != H, {T, V} not in [{C, S}, {D, H}])"
      
      # diamonds was the 4th contract
      --assign="D,4"
      
      # mention all the variables
      "true(C, D, S, H, T, R, P, V, B, K, G, W)"
      
      --template="(C D S H) (T R P V) (B K G W)"
      --solution=""
      

      Solution: The 3 contract was in hearts. It was bid by Pete Green.

      The contracts are fully defined:

      1♣ = Ted Brown
      2♠ = Reg Black
      3♥ = Pete Green
      4♦ = Vic White

      And we see that Reg = Black.

      Like

    • Frits's avatar

      Frits 4:06 pm on 11 September 2023 Permalink | Reply

      More than 200 lines of code.

      from itertools import combinations 
      
      # allocate values 1-4 to the following:
      #
      #  suits    : C D S H [Clubs, Diamonds, Spades, Hearts]
      #  forenames: T R P V [Tom, Reg, Pete, Vic]
      #  surnames : B K G W [Brown, blacK, Green, White]
      
      # 0/1/2 for no/some/all details
      detail = 1
      
      rev = lambda x: "neq" if x == "eq" else "eq"
      
      # a specific print format
      p_frz = lambda x, y: f"{x:>3}-{''.join(sorted(y))}"
      
      # add implication if <x> then <y> 
      def implies(x, y):
        if "=" in x:
          op1 = "eq" if "==" in x else "neq"
          op2 = "eq" if "==" in y else "neq"
          # use frozenset to make it hashable
          var1, var2 = frozenset([x[0], x[-1]]), frozenset([y[0], y[-1]])
          imp[(op1, var1)] = imp.get((op1, var1), []) + [(op2, var2)]
          # if a implies b then not b implies not a
          imp[(rev(op2), var2)] = imp.get((rev(op2), var2), []) + [(rev(op1), var1)]
        else:
          if x in imp:
            if y not in imp[x]:
              # check if y results in a falsehood
              if y == (rev(x[0]), x[1]):
                print(f"ERROR {x}-{y} is false")
               
              if y[1] in fb[x[1]] and x[0] == y[0] == "eq":  
                if detail: 
                  print(f"{p_frz(x[0], x[1])} is false as it leads to "
                        f"eq-{''.join(y[1])} which is not allowed")
                add_truth("neq", x[1])
              imp[x] += [y]
          else:
             imp[x] = [y]
      
      # print implications
      def print_imp(d):
        print()  
        for (a, b), vs in sorted(d.items()):
          print(f"{p_frz(a, b)}  : ", end=' ')
          for (c, d) in vs:
            print(f"{p_frz(c, d)}", end=', ')
          print()
        print()  
            
      # add entries to truth list <t>
      def add_truth(c, s):
        s_ = frozenset(s)
        if (c, s_) in t: return
        l_ = list(s)
        if detail: print("add to t:", p_frz(c, s_))
        t.add((c, s_))
        if c != "eq": return
        
        # add forbidden entries
        for f in fb[s_]:
          if detail > 1: print("add inherited rules to t:", p_frz("neq", f))
          t.add(("neq", f)) 
      
        toadd = []
        # propagate 'eq-ab': if 'neq-ac' exists then also 'neq-bc' must be true
        for d, f in t:
          if d != "neq": continue
          for i, x in enumerate(l_):
            if x not in f: continue
            o, = f.difference([x])
            if o not in bd[l_[1 - i]]:
              toadd.append(frozenset([l_[1 - i], o]))
      
        for x in toadd:
          add_truth("neq", x)
                  
      imp, fb, bd = dict(), dict(), dict()
      
      # specify the conditions
      implies("T == D", "G == 2")
      implies("P == D", "S == 4")
      implies("R == D", "K == 1")
      implies("W == H", "K == C") 
      implies("W == C", "K == H")
      implies("R == H", "P == D")
      implies("B == H", "K == 4")
      implies("K == H", "G == 2")
      implies("K != H", "S != 4")
      implies("K == 1", "P == 3")
      implies("K == 1", "P == S")
      implies("K == 1", "S == 3")
      implies("G == 2", "H == 4")
      implies("K == 4", "R == D")
      implies("G == 4", "D == 3")
      
      #implies("R == C or K == C", "G == 4")
      implies("R == C", "G == 4")
      implies("K == C", "G == 4")
      #implies("C == 3 or C == 4", "R == H")
      implies("C == 3", "R == H")
      implies("C == 4", "R == H")
      
      #implies("S == 3", "{T, V} in [{C, S}, {D, H}]")
      #implies("W != H", "{T, V} not in [{C, S}, {D, H}]")
      implies("S == 3", "W == H")
      
      types = [x.split() for x in ["C D S H", "T P R V", "B K G W", "1 2 3 4"]]
      # buddies
      bd = {x: [y for y in types[i] if y != x] 
                for i, tp in enumerate(types) for x in tp}
      # forbidden values
      for a, b in combinations(bd.keys(), 2):
        if b in bd[a]: continue
        fb[frozenset([a, b])] = [frozenset([a, x]) for x in bd[b]] + \
                                [frozenset([b, x]) for x in bd[a]] 
      
      if detail > 1:
        print("forbidden\n---------")
        for k, vs in sorted(fb.items()):
          print(''.join(k), end="  :  ")
          for v in vs:
            print(''.join(v), end=", ")
          print()
        print()  
      
      t = set()
      # diamonds was the 4th contract
      add_truth('eq', frozenset(['D', '4']))
      
      loop, ln = 1, len(t)
      while True:
      
        if detail:
          print(f"\nloop {loop}\n------")
          if loop < 3: print_imp(imp)
      
        if detail > 1:
          print(f"\ntruths\n------")
          for x in sorted(t, key = lambda x: (x[0], sorted(x[1]) ) ):
            print(p_frz(x[0], ''.join(sorted(x[1]))))
        
        # expand implications by chaining
        for k, vs in imp.items():
          for v in vs:
            # check if v is allowed ...
            if (rev(v[0]), v[1]) in t:
              # ... if not disallow k
              add_truth(rev(k[0]), k[1])
            
            if v in imp:
              # add implications of v to k
              for i in imp[v]:
                implies(k, i)
      
        # check for 3 non-equalities in a group
        toadd = []
        for c, f in t:
          if c != "neq": continue
          lst = list(f)
          # for both elements within f
          for i in range(2):
            y = [("neq", frozenset([x, lst[1-i]])) in t for x in bd[lst[i]]]
            if sum(y) != 2: continue
            # we have 3 neq's so the remaining entry in group must be eq
            toadd.append(frozenset([lst[1-i], bd[lst[i]][y.index(0)]]))
        
        for x in toadd:
          add_truth("eq", x)
        
        # only if normal logic has been exhausted
        if len(t) == ln:
          lookup = lambda d, x: [f for c, f in d if c == "eq" and x in f and 
                                 len(f | set("1234")) == 5]
              
          # implies("S == 3", "{T, V} in [{C, S}, {D, H}]")
          # implies("W != H", "{T, V} not in [{C, S}, {D, H}]")
          s3 = ("eq", frozenset(["S", "3"])) in t
          ns3 = ("neq", frozenset(["S", "3"])) in t
          if not s3 and not ns3: break
          
          # D is 4 so try to find H
          tH = lookup(t, "H")      
          if not tH: break
      
          tT, tV = lookup(t, "T"), lookup(t, "V")    
          if not tV and not tT: break
      
          H, = tH[0].difference(["H"]) 
          DH = {'4', H}
          
          vr, ot = "V" if tV else "T", "T" if tV else "V"
         
          v, = (tV[0] if tV else tT[0]).difference([vr]) 
      
          n = DH.difference([v]).pop() if v in DH else (set("1234") - DH - {v}).pop()
          add_truth("neq" if ns3 else "eq", frozenset([ot, n]))
        
        r3 = [''.join(f.difference(["3"])) for c, f in t if c == "eq" and '3' in f]
        # is there enough data in row 3 for the answer?
        if len(r3) == 3: 
          print(f"\nanswer: {r3[0]}, {r3[1]} and {r3[2]}") 
          exit(0)
        
        ln = len(t)  
        loop += 1     
      

      Like

  • Unknown's avatar

    Jim Randell 4:51 pm on 8 September 2023 Permalink | Reply
    Tags:   

    Teaser 3181: “No slack, Alice!” says Grace 

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

    Grace mimicked Mr Dodgson: “Take two different positive odd numbers with no shared prime factor. Multiply the larger by their sum, giving the perimeter of a distinct right-angled triangle with whole-number sides. Only such values and their multiples work”.

    Alice created a loop from part of a 1m thread. She was able to pull it tightly on a pinboard into a right-angled triangle with whole-number cm sides in two ways (with different-shaped triangles).

    Alice then pulled the same loop tight over the thin polar spike of the classroom globe, down two meridians and along the equator. Thinking “Logic’s dead!”, she saw 90° equatorial angles and a non-zero polar angle, which obviously didn’t add to 180°.

    Grace calculated the polar angle to the nearest degree. Curiously, transposing its two digits gave the globe’s whole-number centimetre radius.

    Find this radius.

    [teaser3181]

     
    • Jim Randell's avatar

      Jim Randell 5:09 pm on 8 September 2023 Permalink | Reply

      If the string has length p and angle (in radians) made by the loop at the pole is 𝛂 on a globe of radius r, then we have:

      p = r(𝛑 + 𝛂)

      This Python program finds perimeters (that are whole numbers of centimetres, less than 100), such that (at least) two dissimilar Pythagorean triangles can be constructed, and then looks for a corresponding radius for a globe that fits the remaining conditions.

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

      from math import (pi, degrees)
      from enigma import (pythagorean_triples, group, item, fdiv, irange, intr, nrev, printf)
      
      # generate possible pythagorean triangles, return (<perimeter>, <triangles>)
      def generate(M):
        for ss in pythagorean_triples(M // 2):
          p = sum(ss)
          if p < M:
            yield (p, ss)
      
      # group pythagorean triangles by their perimeter
      g = group(generate(100), by=item(0), f=item(1))
      
      # consider possible perimeters
      for (p, ts) in g.items():
        if len(ts) < 2: continue
        printf("[p={p} -> {ts}]")
      
        # consider possible globe radii
        for r in irange(10, 99):
          # calculate the polar angle (to the nearest degree)
          a = intr(degrees(fdiv(p, r) - pi))
          if a > 99: continue
          if a < 10: break
          if a == nrev(r):
            printf("-> r={r} a={a}")
      

      Solution: The globe has a radius of 19 cm.

      The loop used 90 cm of thread.

      Which allows (planar) right-angled triangles with sides of (15, 36, 39) cm and (9, 40, 41) cm to be formed.

      It can also be used on a 19cm radius globe to form a triangle on the globe with approx. 30.31 cm running along the equator and 29.85 cm running up meridians to the north pole, meeting at an angle of approx. 91.4°. So the three sides are each close to 30 cm, and the angles are all about 90°.

      Like

  • Unknown's avatar

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

    Teaser 2645: One square 

    From The Sunday Times, 2nd June 2013 [link] [link]

    I have consistently replaced digits by letters, using different letters for different digits, and in this way I have found that:

    (FIVEFOUR)² = ONE

    Now, if I were to tell you whether or not THREE is divisible by 3, and whether or not FOUR is divisible by 4, then you could work out FIVE.

    What number is FIVE?

    Note: As originally published this puzzle has no solution (and was reproduced in this flawed form in the book The Sunday Times Brain Teasers Book 2 (2020)).

    However the additional information that “FIVE is greater than FOUR” allows the published answer to be found.

    [teaser2645]

     
    • Jim Randell's avatar

      Jim Randell 9:29 am on 7 September 2023 Permalink | Reply

      With the extra condition that “FIVE is greater than FOUR” we find the published answer.

      So maybe the setter intended to say: “… I have found that FIVEFOUR is a positive number, whose square is ONE” (or: “the square root of ONE is equal to FIVEFOUR“).

      This Python program runs in 147ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, group, unpack, item, peek, seq2str, printf)
      
      # the alphametic problem (with an additional condition)
      p = SubstitutedExpression(
        ["sq(FIVE - FOUR) = ONE", "FIVE > FOUR"],
        answer="(THREE, FOUR, FIVE)",
      )
      
      # collect the answers, and record values of FIVE (= item(2))
      # by (THREE divisible by 3, FOUR divisible by 4).
      fn = unpack(lambda n3, n4, n5: (int(n3 % 3 == 0), int(n4 % 4 == 0)))
      d = group(p.answers(verbose=0), by=fn, f=item(2), fn=set)
      
      # output the collections, and find the solution
      for (k, vs) in d.items():
        if len(vs) == 1:
          printf("(d3, d4) = {k} -> FIVE = {v} [*** SOLUTION ***]", v=peek(vs))
        else:
          printf("(d3, d4) = {k} -> FIVE = {vs}", vs=seq2str(vs, sort=1, enc="{}"))
      

      Solution: FIVE = 5901.

      If we are told THREE is not divisible by 3, and FOUR is divisible by 4, then the only possible assignments are:

      FIVE = 5901, FOUR = 5872, ONE = 841, THREE = 36211
      FIVE = 5901, FOUR = 5872, ONE = 841, THREE = 63211

      Any of the other of the divisibility possibilities lead to multiple candidate values for FIVE, so this gives the answer.


      However if we do not require FIVE > FOUR, then we find many further possible solutions to the alphametic problem:

      FIVE = 1496, FOUR = 1520, ONE = 576, THREE = 38066
      FIVE = 1496, FOUR = 1520, ONE = 576, THREE = 83066

      FIVE = 3791, FOUR = 3820, ONE = 841, THREE = 56011
      FIVE = 3791, FOUR = 3820, ONE = 841, THREE = 65011

      FIVE = 5791, FOUR = 5820, ONE = 841, THREE = 36011
      FIVE = 5791, FOUR = 5820, ONE = 841, THREE = 63011

      FIVE = 6791, FOUR = 6820, ONE = 841, THREE = 35011
      FIVE = 6791, FOUR = 6820, ONE = 841, THREE = 53011

      FIVE = 8496, FOUR = 8520, ONE = 576, THREE = 13066
      FIVE = 8496, FOUR = 8520, ONE = 576, THREE = 31066

      And so we cannot work out the value of FIVE.

      We can see these candidates by removing the [[ "FIVE > FOUR" ]] on line 5 of the program.

      Like

      • Frits's avatar

        Frits 12:32 pm on 8 September 2023 Permalink | Reply

        The reported run time 147ms was probably with PyPy (I get 423ms with CPython).

        With this program CPython performs better than PyPY.

        from enigma import (SubstitutedExpression, group, unpack, item, peek, seq2str, printf)
        
        # invalid digit / symbol assignments
        d2i = dict()
        for d in range(0, 10):
          vs = set()
          if d == 0: vs.update('OFT')
          # if E is odd then R is even and if E is even then R is even as well
          if d % 2: vs.update('R')
          # a number that ends in 2, 3, 7 or 8 is not a perfect square
          if d in {2, 3, 7, 8}: vs.update('E')
          d2i[d] = vs
         
        # the alphametic problem (with an additional condition)
        p = SubstitutedExpression(
          [ # FIVE > FOUR
            "I > O", 
            "sq(IVE - OUR) = ONE",],
          answer="(FOUR, FIVE, THREE)",
          #answer="(FOUR, FIVE, \
          #         R + 2*E + sum(i for i in range(10) if i not in {F,I,V,E,O,U,R,N}))",
          d2i=d2i,
          verbose=0
        )
         
        # collect the answers, and record values of FIVE (= item(1))
        # by (THREE divisible by 3, FOUR divisible by 4).
        fn = unpack(lambda n4, n5, n3: (int(n3 % 3 == 0), int(n4 % 4 == 0)))
        d = group(p.answers(), by=fn, f=item(1), fn=set)
         
        # output the collections, and find the solution
        for (k, vs) in d.items():
          if len(vs) == 1:
            printf("(d3, d4) = {k} -> FIVE = {v} [*** SOLUTION ***]", v=peek(vs))
          else:
            printf("(d3, d4) = {k} -> FIVE = {vs}", vs=seq2str(vs, sort=1, enc="{}"))   
        

        Like

        • Jim Randell's avatar

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

          @Frits: Yes, 147ms was with PyPy 7.3.12. With CPython 3.12 I get an overall runtime of 255ms.

          Although, if you are solving the rescued problem you can use the expression [[ "is_square(ONE) + FOUR = FIVE" ]] which is a bit faster, and it is even faster to use [[ "is_square(ONE) + OUR = IVE" ]] (93ms with CPython 3.12).

          Like

  • Unknown's avatar

    Jim Randell 9:09 am on 5 September 2023 Permalink | Reply
    Tags:   

    Teaser 2637: Powerful team 

    From The Sunday Times, 7th April 2013 [link] [link]

    George and Martha support the local football team. The squad is numbered from 1 to 22, with 11 playing at any one time and the remaining 11 sitting on the bench. At the start of this week’s match Martha commented that among their 11 players on the pitch there were no three consecutive numbers, and that the same was true of the 11 sitting on the bench. She also commented that the sum of the 11 players’ numbers on the pitch was a perfect square. At half-time one substitution was made and in the second half George noted that all that Martha had said was still true, but now the perfect square was higher.

    What (in increasing order) were the numbers of the 11 players in the first half?

    [teaser2637]

     
    • Jim Randell's avatar

      Jim Randell 9:10 am on 5 September 2023 Permalink | Reply

      This Python program runs in 349ms. (Internal runtime is 279ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, tuples, is_square, diff, cproduct, update, printf)
      
      # available players
      players = list(irange(1, 22))
      
      # check for <k> consecutive numbers in sorted list <ns>
      is_consecutive = lambda ns, k: any(t[0] + k == t[-1] + 1 for t in tuples(ns, k))
      
      # choose 11 of the players to form the team
      for ps in subsets(players, size=11):
        # the sum is a perfect square
        s = sum(ps)
        if not is_square(s): continue
        # no 3 consecutive numbers are used
        if is_consecutive(ps, 3): continue
      
        # and the unused players
        xs = diff(players, ps)
        # no 3 consecutive numbers
        if is_consecutive(xs, 3): continue
      
        # substitute a player from ps with one from xs
        for ((i, p), (j, x)) in cproduct([enumerate(ps), enumerate(xs)]):
          s_ = s - p + x
          if not (s_ > s and is_square(s_)): continue
          # perform the substitution
          ps_ = sorted(update(ps, [(i, x)]))
          if is_consecutive(ps_, 3): continue
          xs_ = sorted(update(xs, [(j, p)]))
          if is_consecutive(xs_, 3): continue
      
          # output solution
          printf("{ps} = {s} -> {ps_} = {s_}")
      

      Solution: The players in the first half were: 1, 2, 4, 5, 7, 8, 10, 12, 14, 17, 20.

      The sum of the numbers in the first half is 100 (= 10²).

      In the second half player 1 was substituted by player 22, causing the sum to increase to 121 (= 11²).


      We can easily see that the smallest possible square is 100, and the largest possible square is 144.

      The difference in the squares is also the difference in the numbers of the substituting and substituted players.

      So the only possible difference is 21, going from 100 → 121.

      And the substitution must be that player 1 was substituted by player 22.

      Like

  • Unknown's avatar

    Jim Randell 8:54 am on 3 September 2023 Permalink | Reply
    Tags: by: Ian Adamson   

    Brain-Teaser 937: Number please? 

    From The Sunday Times, 6th July 1980 [link]

    It was so stifling that afternoon that I had been dozing on my bed with the curtains closed, the air conditioning full on, and a bottle of Vichy water on my bedside table.

    The harsh ring of the telephone brought me to my full consciousness. “This is X9 calling”, said the voice, “it’s been a hot afternoon”. This was the message I had been waiting for, so I replied, “Yes, but it’s cooler in Stockholm.”

    “Now listen carefully”, continued X9, “I want you to telephone me at eleven o’clock tonight. The first digit of my number is twice the first digit of your age; the second digit of my number is three times the second (and last) of your age. Have you got that?”

    “Yes”, I acknowledged, “but why this secrecy?”

    “There may be people listening in. My third digit is the difference of my first two digits — and the fourth is the sum of my first two digits. The final digit of my number is the sum of my third and fourth digits.”

    At first, I worked out an incorrect telephone number, as I had forgotten that I had just recently celebrated my birthday — the difference between the two telephone numbers being a multiple of my age.

    What number should I telephone?

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

    [teaser937]

     
    • Jim Randell's avatar

      Jim Randell 8:54 am on 3 September 2023 Permalink | Reply

      This Python program runs in 53ms. (Internal runtime is 95µs).

      Run: [ @replit ]

      from enigma import (cproduct, nconcat, tuples, printf)
      
      # generate possible (<age>, <number>) pairs
      def generate():
        # for a 2-digit age we have:
        # 1st digit = 1, 2, 3, 4
        # 2nd digit = 0, 1, 2, 3
        for (x, y) in cproduct([(1, 2, 3, 4), (0, 1, 2, 3)]):
          # calculate the 5-digit number <abcde>
          a = 2 * x
          b = 3 * y
          c = abs(a - b)
          d = a + b
          e = c + d
          if d > 9 or e > 9: continue
          # return viable pair
          yield (nconcat(x, y), nconcat(a, b, c, d, e))
      
      # collect the pairs in a dict
      d = dict(generate())
      
      # look for consecutive ages
      for (k1, k2) in tuples(sorted(d.keys()), 2):
        if k1 + 1 != k2: continue
        (v1, v2) = (d[k1], d[k2])
        # difference in numbers is a multiple of the current age
        if (v1 - v2) % k2 > 0: continue
        # output solution
        printf("{k2} -> {v2} [{k1} -> {v1}]")
      

      Solution: The telephone number is 43178.

      This is derived from an age of 21.

      21 → (4 = 2×2)(3 = 3×1)(1 = 4 − 3)(7 = 4 + 3)(8 = 1 + 7) = 43178

      So the incorrectly calculated number is 40448, derived from age 20:

      20 → (4 = 2×2)(0 = 3×0)(4 = 4 − 0)(4 = 4 + 0)(8 = 4 + 4) = 40448.

      And 43178 − 40448 = 2730 = 130×21.


      In fact the only other ages that give viable phone numbers are:

      10 → 20224
      11 → 23156

      But in this case 23156 − 20224 = 2932 is not divisible by 11.

      Like

    • Frits's avatar

      Frits 3:51 pm on 5 September 2023 Permalink | Reply

       
      # formula for X9's telephone number
      x9 = lambda a, b: (20224 - (f := 2*a < 3*b) * 404) * a + (2730 + f * 606) * b
      
      # Assume that for the incorrect telephone number the same rules must appply.
      # This means that the 2nd digit of current age can't be 0, 
      # also 2nd digit of current age can't be 3 (then 4th digit > 9)
      
      for n in [11, 12, 21, 31]:
        d1, d2 = n // 10, n % 10 
        
        correct = x9(d1, d2)
        incorrect = x9(d1, d2 - 1)
        # sum of 2nd and 3rd digit must be less than 10
        if any(int(x[2]) + int(x[3]) > 9 for x in (str(correct), str(incorrect))):
          continue
        
        # difference in numbers is a multiple of the current age
        if (correct - incorrect) % n: continue
        print("answer:", correct)  
      

      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