Updates from July, 2020 Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 6:00 pm on 27 July 2020 Permalink | Reply
    Tags:   

    Brainteaser 1312: A Times Teaser is … 

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

    The five teams in our local league each play each other once in the course of the season. After seven of the games this season a league table was calculated, and part of it is shown below (with the teams in alphabetical order). Three points are awarded for a win and point [to each side] for a draw. In our table the digits 0 to 6 have been consistently replaced by letters.

    Over half the matches so far have had a score of 1-1.

    List all the results of the games (teams and scores).

    [teaser1312]

     
    • Jim Randell's avatar

      Jim Randell 6:01 pm on 27 July 2020 Permalink | Reply

      This Python program uses the [[ Football() ]] helper class from the enigma.py library. It runs in 84ms.

      Run: [ @replit ]

      from enigma import (Football, irange, update)
      
      # scoring system
      football = Football(points=dict(w=3, d=1))
      
      # available digits
      digits = set(irange(0, 6))
      
      # the columns of the table
      (table, gf, ga) = (dict(w="??T??", d="?TE??", l="AIA??", points="?SR?S"), "?MS?I", "?EE??")
      
      # allocate match outcomes
      for (ms, d) in football.substituted_table(table, vs=digits):
      
        # 7 of the (10) games have been played
        if not (sum(m != 'x' for m in ms.values()) == 7): continue
      
        # choose a value for M
        for M in digits.difference(d.values()):
          d_ = update(d, [('M', M)])
      
          # allocate scorelines
          for ss in football.substituted_table_goals(gf, ga, ms, d=d_, teams=[1, 2]):
      
            # over half (4 or more) of the matches were 1-1
            if not (sum(s == (1, 1) for s in ss.values()) > 3): continue
      
            # output solution
            football.output_matches(ms, ss, teams="CFRTU", d=d_)
      

      Solution: The results in the 7 played matches are: C vs F = 1-1; C vs R = 1-1; F vs R = 0-1; F vs T = 4-0; F vs U = 0-1; R vs T = 1-1; R vs U = 1-1.

      The C vs T, C vs U, T vs U matches are yet to be played.

      Like

    • Frits's avatar

      Frits 2:01 pm on 29 July 2020 Permalink | Reply

      I tried to solve the problem with a SubstitutedExpression.
      I succeeded but had to solve all major logic myself.

      #          WON  DRAWN LOST FOR AGAINST POINTS
      # CITY      C*    G*   A    N*    P*     V*
      # FOREST    B*    T    I    M     E      S 
      # ROVERS    T     E    A    S     E      R
      # TOWN      D*    H*   K*   O*    Q*     W*
      # UNITED    F*    J*   L*   I     U*     S
      #
      # TEASRCBDFGHIJKLMNOPQUVW
      # 13046010121211052125121
      #
      
      # T * 3 + E = R and E != R ---> T > 0 
      # A+I+A < 4 --> A = 0 or I = 0 --> E > 0
      # E > 0 and R < 7 and A < 2 
      # --> T = 1 and A = 0 --> sorted(I,E) = (2,3)
      # --> sorted(S,R,M) = (4,5,6)
      # sorted(I,E) = (2,3) but also E >= I --> I = 2, E = 3
      # B * 3 + T = S --> B = 1, S = 4 --> sorted(R,M) = (5,6) 
      # Forest: 1 won, 1 drawn, 2 lost, against 3 
      # --> they lost twice with 0-1 and drew 1-1 --> they won (M-1)-0
      # --> 1 won, 2 lost thus in total we have 4 draws and 3 wins/losses
      # Rovers: 1 won, 3 drawn, 0 lost, goals made 4, points R 
      # --> R = 6, M = 5
      # Rovers - Forest: no draw (otherwise there are 4 win/loss games)
      # --> Rovers - Forest: 1-0 
      # --> Rovers - City: 1-1
      # --> Rovers - Town: 1-1
      # --> Rovers - United: 1-1
      # United drew 1-1 against Rovers, made 2 goals and has 4 points 
      # --> they also had a win
      # --> United - Forest: 1-0, F = 1, J = 1, L = 0, U = 1
      # Forest and Rovers both played 4 times (total 7 games)
      # --> City, Town and United didn't play each other but played twice
      # --> City didn't lose (A = 0) 
      # --> City - Forest 1-1 --> N = 2, P = 2
      # Town played 2 games, one draw only 
      # --> Town - Forest: 0-4
      

      I wonder if in MiniZinc core van be achieved.

      Like

  • Unknown's avatar

    Jim Randell 10:41 am on 26 July 2020 Permalink | Reply
    Tags: by: P. J. Kellner (aged 14)   

    Brain-Teaser 3: People on a bus 

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

    A number of people got on a bus at a bus terminal.

    At the first stop one third of the passengers got off, and eight got on.

    At the second stop one third of the passengers got off, and twelve got on.

    At the third stop half the passengers got off, and fourteen got on.

    At the fourth stop half the passengers got off, and twelve got on.

    At the fifth stop, which was the other terminal, all the passengers got off.

    The fare was 2d. per adult per stop, and 1d. per child per stop. During the journey the conductor took four times as much money from adults as from children. The number of shillings taken equalled the number of passengers who got on at the start.

    How many were there?

    Note: 1 shilling = 12d.

    [teaser3]

     
    • Jim Randell's avatar

      Jim Randell 10:41 am on 26 July 2020 Permalink | Reply

      This Python program runs in 51ms.

      Run: [ @repl.it ]

      from enigma import irange, div, printf
      
      # consider the stops in ss, specified as (a, b, k)
      # where at each stop a/b of the passengers get off and k more get on
      # return a list of the number of passengers arriving at each stop
      def stops(ss, M=100):
        # consider initial 
        for n in irange(1, M):
          ns = [n]
          try:
            for (a, b, k) in ss:
              n -= div(n * a, b)
              n += k
              ns.append(n)
            yield ns
          except TypeError:
            continue
      
      # consider possible stops given in the puzzle
      for ns in stops([(1, 3, 8), (1, 3, 12), (1, 2, 14), (1, 2, 12)]):
        # so the number of chargeable stops <t> is...
        t = sum(ns)
        # if there are <a> adult stops and <b> child stops then:
        #   t = a + b
        # and 4 times as much money was taken from adults as children
        # when the adults pay 2d/stop and children 1d/stop
        #   2a = 4b -> a = 2b
        #   -> t = 3b
        b = div(t, 3)
        if b is None: continue
        a = 2 * b
        # the number of shillings (= 12d) taken is the same as the initial
        # number of passengers
        s = div(2 * a + b, 12)
        if not(s == ns[0]): continue
        # output solution
        printf("{ns} -> {t} stops = {a} adult + {b} child; {s} shillings")
      

      Solution: The initial number of passengers (and the number of shillings taken) is 15.

      The numbers of passengers arriving at each stop are:

      Stop 1: 15
      Stop 2: 18
      Stop 3: 24
      Stop 4: 26
      Stop 5: 25

      Like

    • Frits's avatar

      Frits 7:02 pm on 26 July 2020 Permalink | Reply

      from enigma import SubstitutedExpression, printf, irange
       
      # the alphametic puzzle
      p = SubstitutedExpression([
          # No. people arriving at stop 1: AB 
          # No. people arriving at stop 2: CD = 2/3*AB + 8   
          "(2 * AB) + 24  == 3 * CD",
          # No. people arriving at stop 3: EF = 4/9*AB + 52/3  
          "(4 * AB) + 156 == 9 * EF",
          # No. people arriving at stop 4: GH = 2/9*AB + 68/3  
          "(2 * AB) + 204 == 9 * GH",
          # No. people arriving at stop 5: IJ = 1/9*AB + 70/3  
          "AB + 210       == 9 * IJ",
          # Total fare (MN: no. children, OP: no. adults)
          "12 * AB == (MN + 2 * OP)",
          # 2.4 * AB is the amount of pennies children had to pay
          # thus AB has to be a multiple of 5
          "AB == 5 * KL",
          # AB + CD + EF + GH + IJ sums up to 22/9 * AB + 214/3
          # Total number of people on the bus is 22/9 * AB + 214/3
          "22 * AB + 642 == 9 * (MN + OP)",
        ],
        answer="AB,CD,EF,GH,IJ",
        d2i="",          # allow number representations to start with 0
        distinct="",     # allow variables with same values
      )
      
      # Print answers
      for (_, ans) in p.solve(verbose=0): 
        for i in irange(0, 4):
          printf("Number of people arriving at stop {i+1}: {ans[i]}")
        printf("answer = {ans[0]}")
      

      Please let me know how to reply without losing the formatting of the pasted code.
      My previous attempt to add comments in the p = SubstitutedExpression([…]) construct failed as I put them between the double quotes.

      Like

      • Frits's avatar

        Frits 7:24 pm on 26 July 2020 Permalink | Reply

        I forgot I don’t have to solve the equations myself.
        Another simpler version.

        from enigma import SubstitutedExpression, printf, irange
         
        # the alphametic puzzle
        p = SubstitutedExpression([
            # No. people arriving at stop 1: AB 
            # No. people arriving at stop 2: CD = 2/3*AB + 8   
            "(2 * AB) + 24 == 3 * CD",
            # No. people arriving at stop 3: EF = 2/3*CD + 12  
            "(2 * CD) + 36 == 3 * EF",
            # No. people arriving at stop 4: GH = 1/2*EF + 14  
            "EF + 28 == 2 * GH",
            # No. people arriving at stop 5: IJ = 1/2*GH + 12  
            "GH + 24 == 2 * IJ",
            # Total fare (MN: no. children, OP: no. adults)
            "12 * AB == (MN + 2 * OP)",
            # 2.4 * AB is the amount of pennies children had to pay
            # thus AB has to be a multiple of 5
            "AB == 5 * KL",
            # Total number of people on the bus is AB + CD + EF + GH + IJ
             "AB + CD + EF + GH + IJ == MN + OP",
          ],
          answer="AB,CD,EF,GH,IJ",
          d2i="",          # allow number respresentations to start with 0
          distinct="",     # allow variables with same values
        )
        
        # Print answers
        for (_, ans) in p.solve(verbose=0): 
          for i in irange(0, 4):
            printf("Number of people arriving at stop {i+1}: {ans[i]}")
          printf("answer = {ans[0]}")
        

        @Jim: I prefer to have all the code in one module iso a main module and a run module.
        Does it matter (like for performance)?

        Like

      • Jim Randell's avatar

        Jim Randell 9:52 pm on 26 July 2020 Permalink | Reply

        @Frits: When posting code in WordPress you can use:

        [code language="python"]
        ...
        [/code]
        

        I wrote some notes up on the site I manage for New Scientist Enigma puzzles [ @EnigmaticCode ], the same notes hold true for the S2T2 site.

        Like

  • Unknown's avatar

    Jim Randell 4:59 pm on 24 July 2020 Permalink | Reply
    Tags:   

    Teaser 3018: Debased 

    From The Sunday Times, 26th July 2020 [link] [link]

    Sarah writes down a four-digit number then multiplies it by four and writes down the resultant five-digit number. She challenges her sister Jenny to identify anything that is special about these numbers. Jenny is up to the challenge as she identifies two things that are special. She sees that as well as both numbers being perfect squares she also recognises that if the five-digit number was treated as being to base 7 it would, if converted to a base 10 number, be the same as the original four-digit number.

    What is the four-digit number?

    [teaser3018]

     
    • Jim Randell's avatar

      Jim Randell 5:08 pm on 24 July 2020 Permalink | Reply

      It is straightforward to implement this directly.

      The following Python program runs in 52ms.

      Run: [ @replit ]

      from enigma import (powers, int2base, printf)
      
      # consider 4-digit squares, where 4n is a 5-digit number
      for n in powers(50, 99, 2):
        # what is n in base 7?
        n7 = int2base(n, base=7)
        # which if interpreted in base 10 is 4n
        if int2base(4 * n) == n7:
          printf("n={n} [= {n7} (base 7)]")
      

      Solution: The four digit number is: 2601.

      2601 = 51², so the answer is found on the second number tested by the program.

      4×2601 = 10404, and 10404 (base 7) = 2601 (base 10).

      Or, as a single Python expression:

      >>> peek(n for n in powers(50, 99, 2) if int2base(n, base=7) == int2base(4 * n))
      2601
      

      Like

      • Jim Randell's avatar

        Jim Randell 4:40 pm on 30 July 2020 Permalink | Reply

        Or, using the [[ SubstitutedExpression() ]] solver from the enigma.py library:

        Run: [ @replit ]

        #! python3 -m enigma -rr
        
        SubstitutedExpression
        
        --answer="ABCD"
        --distinct=""
        
        # the 4-digit number is ABCD, and it is a square
        "is_square(ABCD)"
        
        # multiplied by 4 gives a 5 digit number
        "9999 < 4 * ABCD"
        
        # interpreting 4 * ABCD as a base 7 number gives ABCD
        "int2base(ABCD, base=7) == int2base(4 * ABCD, base=10)"
        

        Like

    • GeoffR's avatar

      GeoffR 7:54 am on 26 July 2020 Permalink | Reply

      
      % A Solution in MiniZinc 
      include "globals.mzn";
       
      % digits for 4 digit number to base 10
      var 1..9:A; var 0..9:B; var 0..9:C; var 0..9:D; 
      var 1000..9999: ABCD = 1000*A + 100*B + 10*C + D;
      
      % digits for 5 digit number in base 7
      var 1..6:E; var 0..6:F; var 0..6:G; var 0..6:H; var 0..6:I;
      var 10000..99999: EFGHI = 10000*E + 1000*F + 100*G + 10*H + I;
      
      predicate is_sq(var int: y) =
        let {
           var 1..ceil(sqrt(int2float(ub(y)))): z
        } in
         z*z = y;
      
      % 5-digit number is four times the 4-digit number
      constraint 4 * ABCD == EFGHI;
      
      % Both 4-digit and 5-digit numbers are squares
      constraint is_sq(ABCD) /\ is_sq(EFGHI);
      
      % ABCD (base 10) = EFGHI (base 7)
      constraint ABCD == I + 7*H + 7*7*G + 7*7*7*F  + 7*7*7*7*E;
      
      solve satisfy;
      
      output [ "Four digit number (base 10) is " ++ show(ABCD) 
      ++ "\nFive digit number (base 7) is "++ show(EFGHI) ]; 
      
      

      Like

    • GeoffR's avatar

      GeoffR 8:20 pm on 27 July 2020 Permalink | Reply

      
      from enigma import is_square, nsplit
      
      for n in range(50,100):
        if is_square(n*n):
          N4 = n*n      # 4-digit number (base 10)
          N5 = 4*n*n    # 5-digit number (base 7)
          
          # split 5-digit number N5 into digits
          a, b, c, d, e = nsplit(N5)
          
          # check N4 = N5 in specified bases
          if N4 == e + d*7 + c*7**2 + b*7**3 + a*7**4:
            print(f"4-digit number(base 10)= {n**2}")
            print(f"5-digit number(base 7)= {4*n**2}")
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 8:07 am on 28 July 2020 Permalink | Reply

      I have updated my programme to include a check that the digits (7,8,9) are not included in the 5-digit number before it is converted to base 7. In practice, the answer is found very early.

      
      from enigma import is_square, nsplit
      
      for n in range(50, 100):
        if is_square(n * n):
          N4 = n * n      # 4-digit number (base 10)
          N5 = 4 * n * n    # 5-digit number (base 7)
      
          # split 5-digit number N5 into digits
          a, b, c, d, e = nsplit(N5)
      
          # check digits 7,8,9 are not in N5, base 7
          if any(x in (a,b,c,d,e) for x in (7,8,9)):
            continue
          
          # check N4 = N5 in specified bases
          if N4 == e + d*7 + c*7**2 + b*7**3 + a*7**4:
            print(f"4-digit number(base 10)= {n**2}")
            print(f"5-digit number(base 7)= {4*n**2}")
      
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 2:53 pm on 29 July 2020 Permalink | Reply

        @GeoffR: That’s why in my code I interpret a base 7 representation as base 10. There is no need to check for invalid digits, as every base 7 digit is a valid base 10 digit.

        Like

  • Unknown's avatar

    Jim Randell 3:43 pm on 23 July 2020 Permalink | Reply
    Tags:   

    Teaser 2684: How safe? 

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

    Fed up with being burgled, George and Martha have bought a new safe. They remember its six-figure combination as three two-figure primes. Martha noted that the product of the six different digits of the combination was a perfect square, as was the sum of the six. George commented further that the six-figure combination was actually a multiple of the difference between that product and sum.

    What is the six-figure combination?

    [teaser2684]

     
    • Jim Randell's avatar

      Jim Randell 3:43 pm on 23 July 2020 Permalink | Reply

      If the combination can be considered as three 2-digit primes, then it cannot contain 0 (as a 2-digit prime cannot begin or end with 0).

      This Python program looks for 6 different (non-zero) digits that have a square for both their sum and product, and looks for 6-digit multiples of the difference between these that use the same digits, and can be split into three 2-digit prime numbers. It runs in 52ms.

      from enigma import (subsets, irange, multiply, is_square, divc, is_prime, nsplit, printf)
      
      # look for 6 different digits
      for ds in subsets(irange(1, 9), size=6, select="C"):
        # the sum and product must both be squares
        s = sum(ds)
        p = multiply(ds)
        if not (is_square(s) and is_square(p)): continue
        (d, ds) = (p - s, set(ds))
        # consider 6-digit multiples of d
        for n in irange(d * divc(100000, d), 999999, step=d):
          # does it use the same digits?
          if ds.difference(nsplit(n)): continue
          # does it split into primes?
          if not all(is_prime(p) for p in nsplit(n, base=100)): continue
          # output solution
          printf("{n} [s={s} p={p} d={d}]")
      

      Solution: The combination is 296143.

      There is only one set of 6 different digits that have a square for both their sum and product: 1, 2, 3, 4, 6, 9 (sum = 5², product = 36²). So the combination is a rearrangement of these digits that is a multiple of 1271, that can be split into three 2-digit primes. In fact 296143 is the only multiple of 1271 that uses the required digits, so the prime test is superfluous.

      Like

    • GeoffR's avatar

      GeoffR 8:38 pm on 23 July 2020 Permalink | Reply

      % A Solution in MiniZinc 
      include "globals.mzn";
      
      var 1..9:A; var 1..9:B; var 1..9:C; 
      var 1..9:D; var 1..9:E; var 1..9:F; 
      
      constraint all_different([A,B,C,D,E,F]);
      
      % three two-figure primes
      var 11..97: AB = 10*A + B;
      var 11..97: CD = 10*C + D;
      var 11..97: EF = 10*E + F;
      
      var 720..60480: dig_prod = A * B * C * D * E * F;
      var 21..39: dig_sum = A + B + C + D + E + F;
      
      % Safe combination is ABCDEF
      var 100000..999999: comb = 100000*A + 10000*B + 1000*C
      + 100*D + 10*E + F;
      
      predicate is_prime(var int: x) = 
      x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) 
      ((i < x) -> (x mod i > 0));
      
      predicate is_sq(var int: y) =
        let {
           var 1..ceil(sqrt(int2float(ub(y)))): z
        } in
         z*z = y;
      
      % the combination is split into three 2-digit primes
      constraint is_prime(AB) /\ is_prime(CD) /\ is_prime(CD);
      
      % digit product and digit sum are both squares
      constraint is_sq(A * B * C * D * E * F);
      constraint is_sq(A + B + C + D + E + F);
      
      % the six-figure combination was actually a multiple of 
      % the difference between that product and sum.
      constraint comb mod (dig_prod - dig_sum) == 0;
      
      solve satisfy;
      
      output["Safe combination is " ++ show(comb)
      ++ "\nDigits product = " ++ show(dig_prod)];
      
      % Safe combination is 296143
      % Digits product = 1296
      % ----------
      % ==========
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 4:44 pm on 24 July 2020 Permalink | Reply

      I carried out some further tests to see if there were any constraints which were not essential to get the correct answer.

      I found any one of the following MiniZinc constraints could be removed, and the single correct answer obtained:

      1) requirement for digits to be all different
      2) requirement for three 2-digit primes
      3) requirement for sum of digits to be a square
      4) requirement product of digits to be a square.

      However, I could not remove more than one of these constraints and get the correct answer.

      Like

  • Unknown's avatar

    Jim Randell 1:23 pm on 21 July 2020 Permalink | Reply
    Tags:   

    Teaser 2683: House-to-house 

    From The Sunday Times, 23rd February 2014 [link] [link]

    Tezar Road has houses on one side only, numbered consecutively. The Allens, Browns, Carrs and Daws live in that order along the road, with the Allens’ house number being in the teens. The number of houses between the Allens and the Browns is a multiple of the number of houses between the Browns and the Carrs. The number of houses between the Allens and the Carrs is the same multiple of the number of houses between the Carrs and the Daws. The Daws’ house number consists of the same two digits as the Allens’ house number but in the reverse order.

    What are the house numbers of the four families?

    [teaser2683]

     
    • Jim Randell's avatar

      Jim Randell 1:23 pm on 21 July 2020 Permalink | Reply

      In order to get a unique solution we require “multiple” to be a “proper multiple” (i.e. not ×1).

      This Python program runs in 51ms.

      Run: [ @replit ]

      from enigma import (irange, divisors, div, printf)
      
      # A is in [13, 19]
      for d in irange(3, 9):
        A = 10 + d
        # D is the reverse of A
        D = 10 * d + 1
        # C divides AD in to m-1:1
        n = D - A - 2
        for m in divisors(n):
          if not (2 < m < n): continue
          C = D - (n // m) - 1
          # B divides AC into the same ratio
          x = div(C - A - 2, m)
          if x is None: continue
          B = C - x - 1
          # output solution
          printf("A={A} B={B} C={C} D={D} [ratio = {m}:1]", m=m - 1)
      

      Solution: The house numbers are: 19, 64, 76, 91.

      There are 44 houses between A and B, and 11 between B and C.

      There are 56 houses between A and C, and 14 between C and D.

      Allowing m = 1 also gives the following solutions:

      A=15, B=24, C=33, D=51
      A=19, B=37, C=55, D=91

      Like

  • Unknown's avatar

    Jim Randell 4:30 pm on 17 July 2020 Permalink | Reply
    Tags:   

    Teaser 3017: Mr. Green’s scalene mean machine 

    From The Sunday Times, 19th July 2020 [link] [link]

    My maths teacher, Mr. Green, stated that the average of the squares of any two different odd numbers gives the hypotenuse of a right-angled triangle that can have whole-number unequal sides, and he told us how to work out those sides.

    I used my two sisters’ ages (different odd prime numbers) to work out such a triangle, then did the same with my two brothers’ ages (also different odd prime numbers). Curiously, both triangles had the same three-figure palindromic hypotenuse. However, just one of the triangles was very nearly a 45° right-angled triangle (having a relative difference between the adjacent side lengths of less than 2%).

    In ascending order, what were my siblings’ ages?

    [teaser3017]

     
    • Jim Randell's avatar

      Jim Randell 5:34 pm on 17 July 2020 Permalink | Reply

      We don’t need to work out how to calculate the non-hypotenuse sides of the triangle (although I have worked out a rule which works).

      This Python program groups together Pythagorean triples by the hypotenuse, then looks for groups that have a palindromic hypotenuse, and more than one corresponding triangle. It then works out pairs of odd squares that average to the hypotenuse, and looks for two of these pairs where each number in the pair is prime. Then we need to look for a corresponding triangle where the non-hypotenuse sides are almost equal. And that gives us our solution.

      This Python program runs in 55ms.

      Run: [ @replit ]

      from enigma import (pythagorean_triples, is_palindrome, nsplit, group, unpack, isqrt, compare, subsets, flatten, is_prime, printf)
      
      # generate decompositions of n into the sum of two squares
      def sum_of_squares(n):
        (i, j) = (isqrt(n), 0)
        while not (i < j):
          r = compare(i * i + j * j, n)
          if r == 0: yield (j, i)
          if r != -1: i -= 1
          if r != 1: j += 1
      
      # collect pythagorean triples grouped by hypotenuse
      tris = group(
        pythagorean_triples(999),
        st=unpack(lambda x, y, z: z > 99 and is_palindrome(nsplit(z))),
        by=unpack(lambda x, y, z: z)
      )
      
      # look for multiple triangles that share a palindromic hypotenuse
      for (z, xyzs) in tris.items():
        if not (len(xyzs) > 1): continue
        # and look for multiple pairs of squares that average to the hypotenuse
        avgs = list((x, y) for (x, y) in sum_of_squares(2 * z) if x < y)
        if not (len(avgs) > 1): continue
        printf("[{z} -> {xyzs} -> {avgs}]")
        # look for triangles with x, y within 2% of each other
        t45ish = list((x, y, z) for (x, y, z) in xyzs if 100 * y < 102 * x)
        if not (0 < len(t45ish) < len(xyzs)): continue
        # collect two of the pairs for the ages
        for ages in subsets(avgs, size=2):
          ages = sorted(flatten(ages))
          # and check they are odd primes
          if not all(x % 2 == 1 and is_prime(x) for x in ages): continue
          # output solution
          printf("z={z} -> t45ish = {t45ish} -> ages = {ages}")
      

      Solution: The ages are: 13, 17, 29, 31.

      Removing the test for the ages being odd and prime does not give any additional solutions.

      If the two odd numbers are a and b (where a < b), then the following is a Pythagorean triple:

      x = (b² − a²) / 2
      y = ab
      z = (a² + b²) / 2

      where z is the hypotenuse. (All we actually require is that a and b have the same parity).

      Applying this rule to the two pairs (13, 31) and (17, 29) we get the following triangles:

      (13, 31) → sides = (396, 403, 565); angles = (44.5°, 45.5°, 90.0°)
      (17, 29) → sides = (276, 493, 565); angles = (29.2°, 60.8°, 90.0°)

      The first of these triangles is close to being a (45°, 45°, 90°) triangle, and the other one is quite close to being a (30°, 60°, 90°) triangle – the standard shapes for set squares.

      Like

    • GeoffR's avatar

      GeoffR 8:15 am on 21 July 2020 Permalink | Reply

      
      % A Solution in MiniZinc 
      include "globals.mzn";
      
      predicate is_prime(var int: x) = 
      x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) 
      ((i < x) -> (x mod i > 0));
      
      % Siblings ages in teaser
      var 2..97:S1; var 2..97:S2; var 2..97:B1; var 2..97:B2;
      constraint all_different ([S1, S2, B1, B2]);
      
      constraint S1 < S2 /\ B1 < B2;
      
      constraint is_prime(S1) /\ is_prime(S2)
      /\ is_prime(B1) /\ is_prime(B2);
      
      % Palindromic hypotenuse
      var 100..999: HYP;
      
      constraint HYP div 100 == HYP mod 10 
      /\ HYP div 10 mod 10 != HYP mod 10;
      
      % Two other sides with a palindromic hypotenuse
      var 100..999: side1; var 100..999: side2;
      
      constraint side1 > side2
      /\ all_different([HYP, side1, side2]);
      
      % Mr Green's triangle formula
      constraint B1*B1 + B2*B2 = 2 * HYP
      /\ S1*S1 + S2*S2 = 2 * HYP;
      
      % The triangle sides are less than 2% different in size
      constraint side1 * side1 + side2 * side2 == HYP * HYP
      /\ 100 * (side1 - side2) < 2 * side1;
      
      solve satisfy;
      
      output [ "Sisters' ages are " ++ show(S1) ++ ", " ++ show(S2)]
      ++ [ "\nBrothers' ages are " ++ show(B1) ++ ", " ++ show(B2)]
      ++ [ "\nTriangle solution sides are " ++ show(HYP) ++ ", " ++ show(side1)]
      ++ [", " ++ show(side2) ];
      
      % Ascending siblings ages are 13,17,29,31
      
      % Sisters' ages are 17, 29
      % Brothers' ages are 13, 31
      % Triangle solution sides are 565, 403, 396
      % time elapsed: 0.05 s
      % ----------
      % Sisters' ages are 13, 31
      % Brothers' ages are 17, 29
      % Triangle solution sides are 565, 403, 396
      % time elapsed: 0.05 s
      % ----------
      % ==========
      
      

      I found the prime age constraint of the siblings was not essential for this solution.

      Like

  • Unknown's avatar

    Jim Randell 9:01 am on 16 July 2020 Permalink | Reply
    Tags:   

    Teaser 2682: 007 

    From The Sunday Times, 16th February 2014 [link] [link]

    Eight villains Drax, Jaws, Krest, Largo, Morant, Moth, Sanguinette and Silva have been ordered to disrupt the orbits of the planets Earth, Jupiter, Mars, Mercury, Neptune, Saturn, Uranus and Venus, with each villain disrupting a different planet. Our agents Brosnan, Casenove, Connery, Craig, Dalton, Dench, Lazenby and Moore have each been assigned to thwart a different villain. For any villain/planet, villain/agent or planet/agent combinations just two different letters of the alphabet occur in both names.

    List the eight agents in alphabetical order of their assigned villains.

    [teaser2682]

     
    • Jim Randell's avatar

      Jim Randell 9:02 am on 16 July 2020 Permalink | Reply

      This is another puzzle that can be solved using [[ grouping ]] routines from the enigma.py library.

      Run: [ @repl.it ]

      from enigma import grouping
      
      # villains (in alphabetical order), planets, agents
      V = ('Drax', 'Jaws', 'Krest', 'Largo', 'Morant', 'Moth', 'Sanguinette', 'Silva')
      P = ('Earth', 'Jupiter', 'Mars', 'Mercury', 'Neptune', 'Saturn', 'Uranus', 'Venus')
      A = ('Brosnan', 'Casenove', 'Connery', 'Craig', 'Dalton',  'Dench', 'Lazenby', 'Moore')
      
      grouping.solve([V, P, A], grouping.share_letters(2))
      

      Solution: The ordering of the agents is: Dalton, Casenove, Connery, Lazenby, Craig, Moore, Dench, Brosnan.

      The groups are:

      Drax, Uranus, Dalton
      Jaws, Mars, Casenove
      Krest, Neptune, Connery
      Largo, Saturn, Lazenby
      Morant, Jupiter, Craig
      Moth, Earth, Moore
      Sanguinette, Mercury, Dench
      Silva, Venus, Brosnan

      Like

  • Unknown's avatar

    Jim Randell 12:11 pm on 14 July 2020 Permalink | Reply
    Tags:   

    Teaser 2672: 11, 12, 13 … 

    From The Sunday Times, 6th December 2013 [link] [link]

    On Wednesday the date will be 11.12.13. To celebrate this fact I have found three numbers with the lowest divisible by 11, another divisible by 12, and the remaining number divisible by 13. Furthermore, appropriately, the sum of the three numbers is 2013. Overall the three numbers use nine digits with no digit used more than once.

    What (in increasing order) are my three numbers?

    [teaser2672]

     
    • Jim Randell's avatar

      Jim Randell 12:13 pm on 14 July 2020 Permalink | Reply

      The following Python program runs in 61ms.

      Run: [ @replit ]

      from enigma import (irange, is_duplicate, printf)
      
      # choose the first number (a multiple of 11)
      for n1 in irange(11, 2013, step=11):
        # it has no repeated digits
        s1 = str(n1)
        if is_duplicate(s1): continue
        # and choose a larger second number (a multiple of 12)
        for n2 in irange(n1 + 12 - (n1 % 12), 2013 - n1, step=12):
          # n1 and n2 don't share digits
          s2 = str(n2)
          if is_duplicate(s1, s2): continue
          # and determine n3
          n3 = 2013 - (n1 + n2)
          # n3 is larger than n1
          if not (n1 < n3): continue
          # and a multiple of 13
          if n3 % 13: continue
          # and between them the numbers have 9 digits, all different
          s = s1 + s2 + str(n3)
          if len(s) != 9 or is_duplicate(s): continue
          # output solution
          printf("{ns}", ns=sorted([n1, n2, n3]))
      

      Solution: The three numbers are: 495, 702, 816.

      Like

    • GeoffR's avatar

      GeoffR 5:44 pm on 14 July 2020 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
       
      var 0..9:A; var 0..9:B; var 0..9:C; 
      var 0..9:D; var 0..9:E; var 0..9:F; 
      var 0..9:G; var 0..9:H; var 0..9:I; 
      
      % Three numbers are ABC, DEF and GHI
      var 100..999: ABC = 100*A + 10*B + C;
      var 100..999: DEF = 100*D + 10*E + F;
      var 100..999: GHI = 100*G + 10*H + I;
      
      constraint A != 0 /\ D != 0 /\ G != 0;
      constraint all_different ([A,B,C,D,E,F,G,H,I]);
      
      constraint ABC mod 11 == 0 /\ DEF mod 12 == 0  
      /\ GHI mod 13 == 0;
      
      constraint ABC + DEF + GHI == 2013;
      
      solve minimize (ABC);
      
      output ["ABC = " ++ show (ABC) ++ ", DEF = " ++ show(DEF) 
      ++ ", GHI = " ++ show(GHI)];
      
      % ABC = 495, DEF = 816, GHI = 702  
      % or 495, 702, 816 in increasing order
      % ----------
      % ==========
      
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 5:12 pm on 15 July 2020 Permalink | Reply

        Providing we know the numbers all have 3 digits.

        But the smallest number is a multiple of 11, with no repeated digits, so must be at least 132 (unless it is 0). So the other two numbers must both be at least 3 digits, but between them they have only 9 digits, so they must indeed be all 3-digit numbers.

        (If the first number is 0, the other two must both be 4-digit numbers, but to add up to 2013, they would both have to start with 1, so this is disallowed).

        #! python -m enigma -rr
        
        SubstitutedExpression
        
        "ABC % 11 = 0"
        "DEF % 12 = 0"
        "GHI % 13 = 0"
        
        "ABC < DEF"
        "ABC < GHI"
        
        "ABC + DEF + GHI = 2013"
        
        --answer="ordered(ABC, DEF, GHI)"
        

        Like

  • Unknown's avatar

    Jim Randell 9:55 am on 12 July 2020 Permalink | Reply
    Tags:   

    Brainteaser 1835: Dominic’s dominoes 

    From The Sunday Times, 16th November 1997 [link]

    Dominic thought up a variation of the domino jigsaw game. He laid out a standard set of dominoes and produced the pattern below by copying the numbers of spots in each place. He put a 0 for all the blanks except for the one blank which stuck out of a side of the rectangle:

    Find the positions of all the dominoes.

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

    [teaser1835]

     
    • Jim Randell's avatar

      Jim Randell 9:56 am on 12 July 2020 Permalink | Reply

      I used the [[ DominoGrid() ]] solver from the enigma.py library (see: Teaser 1590). We pair up one of the edge squares with a 0 to make a domino, then attempt to fit the rest into the remainder of the grid. The following program runs in 88ms.

      Run: [ @repl.it ]

      from enigma import DominoGrid, update, printf
      
      # the grid
      (X, Y, grid) = (5, 11, [
        6, 2, 6, 2, 0,
        6, 3, 1, 2, 0,
        3, 5, 1, 2, 0,
        3, 5, 3, 3, 1,
        6, 6, 5, 3, 0,
        4, 3, 0, 3, 2,
        2, 5, 4, 4, 4,
        4, 2, 4, 6, 6,
        5, 5, 4, 1, 1,
        2, 4, 1, 5, 0,
        1, 1, 0, 5, 6,
      ])
      
      # remove one of the border squares and solve for the rest
      for (i, b) in enumerate(grid):
        (y, x) = divmod(i, X)
        if x == 0 or x == X - 1 or y == 0 or y == Y - 1:
          # place the 0-b domino and solve for the remaining dominoes
          p = DominoGrid(X, Y, update(grid, [(i, None)]))
          for s in p.solve(used=[(0, b)]):
            printf("[0-{b} placed @ {x},{y}]\n")
            p.output_solution(s, prefix="  ")
      

      Solution: The layout of the dominoes is shown below:

      So the missing square is part of 0-2 domino.

      Like

  • Unknown's avatar

    Jim Randell 5:11 pm on 10 July 2020 Permalink | Reply
    Tags: ,   

    Teaser 3016: Eureka 

    From The Sunday Times, 12th July 2020 [link] [link]

    Archimedes’ Principle states that the upward force on a floating body equals the weight of water displaced by the body. In testing this, Liam floated a toy boat in a cylindrical drum of water. The boat had vertical sides and took up one tenth of the surface area of the water. He also had some identical ball bearings (enough to fit snugly across the diameter of the drum), which were made of a metal whose density is a single-figure number times that of water.

    Liam loaded the boat with the ball bearings and noted the water level on the side of the drum. When he took the ball bearings out of the boat and dropped them in the water, the water level changed by an amount equal to the radius of a ball bearing.

    How many ball bearings did Liam have?

    This puzzle was not included in the book The Sunday Times Teasers Book 1 (2021).

    [teaser3016]

     
    • Jim Randell's avatar

      Jim Randell 6:26 pm on 10 July 2020 Permalink | Reply

      Suppose there are n ball-bearings, each with a radius of r, then the radius of the cylindrical tank is nr.

      If we take the water level in the tank when the the unladen boat is floating in it to be “low water”, then when the balls are placed in the tank they displace an amount of water equal to their volume: n (4/3) 𝛑 r³.

      The boat still has the same draught, so the water level in the tank increases by:

      [1] (n(4/3)𝛑r³) / (𝛑(nr)²) = (4/3)(r/n)

      \frac{n\left( \frac{4}{3}\pi r^{3} \right)}{\pi \left( nr \right)^{2}}\; =\; \frac{4}{3}\frac{r}{n}

      [Note that the following step is flawed (although appears to be what the setter expects), see my follow-on comment below].

      When the balls are instead placed in the boat, then the weight of the boat is increased by: kn(4/3)𝛑r³ (where the metal of the balls is k times the density of water), and so this weight of water is displaced, so the level of water in the tank (above low water) is:

      [2] (kn(4/3)𝛑r³) / ((9/10)𝛑(nr)²) = (40/27)(kr/n)

      \frac{kn\left( \frac{4}{3}\pi r^{3} \right)}{\frac{9}{10}\pi \left( nr \right)^{2}}\; =\; \frac{40}{27}\frac{kr}{n}

      (the relative density of water is 1).

      The difference between these water levels is r:

      [2] − [1] (40/27)(kr/n) − (4/3)(r/n) = r
      n = (4/27)(10k − 9)

      Only one value of k = 1..9 has (10k − 9) divisible by 27.

      This Python program looks for values of k that give an integer value for n:

      from enigma import (irange, div, printf)
      
      for k in irange(1, 9):
        n = div(40 * k - 36, 27)
        if n is None: continue
        printf("k={k} n={n}")
      

      Solution: There are 12 ball bearings.

      And density of the ball bearings is 9 times that of water. So perhaps they are mostly made of copper (relative density = 8.96).

      Writing:

      40k − 27n = 36
      40k = 9(3n + 4)

      We see that k must be a multiple of 9, and as it must be a single digit the only possibility is k = 9, and so n = 12.

      [This seems to be the expected answer, but see my follow on comment for a more realistic approach].

      Like

      • Jim Randell's avatar

        Jim Randell 9:22 am on 14 July 2020 Permalink | Reply

        In my original treatment, when the water was displaced from loading the boat, it was then added back in to the space around the boat. But unless the water is actually removed, and the boat drops anchor before the displaced water is added back in, the boat will rise as the water is added (we can think of the displaced water being added to the bottom of the tank, rather than the top). So the second water level calculated above is higher than it would be in “real life”.

        The correct calculation is:

        [2] = k×[1] (kn(4/3)𝛑r³) / (𝛑(nr)²) = (4/3)(kr/n)

        \frac{kn\left( \frac{4}{3}\pi r^{3} \right)}{\pi \left( nr \right)^{2}}\; =\; \frac{4}{3}\frac{kr}{n}

        (The cross-sectional area of the boat is immaterial, as the boat rises with the water).

        We then calculate the difference in water levels:

        [2] − [1] (4/3)(kr/n) − (4/3)(r/n) = r
        4(k − 1) = 3n

        So, n is a multiple of 4, and (k − 1) is a multiple of 3:

        n = 4, k = 4
        n = 8, k = 7
        n = 12, k = 10

        For a single digit value of k there are two solutions (although k = 7 would be a more likely relative density for ball bearings [link], so the most reasonable solution would be that there are 8 ball bearings).

        But the relative cross-sectional areas of the boat and the tank don’t seem to matter, so perhaps the setter was expecting the same approach as that I originally gave above.

        Like

      • Jim Randell's avatar

        Jim Randell 10:09 am on 25 March 2023 Permalink | Reply

        The published solution is:

        Teaser 3016
        We apologise that there was not a unique answer.
        4, 8 and 12 were all acceptable.

        Like

    • GeoffR's avatar

      GeoffR 9:02 am on 14 July 2020 Permalink | Reply

      I did a Python programme for a range of ball bearing sizes and the result (as expected) was a constant number of ball bearings. The programme confirmed the difference in water height was the radius of the ball bearing in each case.

      
      from math import pi
      
      # Let rho be density factor of ball bearings c.f. water
      # Let N = number of ball bearings
      
      # ball bearing radius range considered is from 0.25 cm to 2.0 cm
      for r in (0.25, 0.5, 1.0, 1.5, 2.0):  
        for rho in range(1, 10):   
          for N in range(1, 50):
            # calculate radius of tank, area of tank and boat
            tank_rad = N * r
            tank_area = round((pi * tank_rad**2), 3)
            boat_area = round((tank_area / 10), 3)
            
            # calculate weight and volume of N ball bearings 
            wt = rho * 4/3 * pi * r**3 * N
            vol = 4/3 * pi * r**3 * N
      
            # Let h1 be water height change after balls put in tank
            h1 = round((vol / tank_area), 3)
      
            # Let h2 = water height change after balls put in boat
            h2 = round ((wt / (tank_area - boat_area)), 3)
            # check radius of ball bearings = change in water height
            if r == h2 - h1:
              print(f"Ball Bearing Radius = {r} cm. Number of balls = {N} no.")
              print(f"1st height = {h1} cm., 2nd height = {h2} cm., Difference = {h2-h1} cm")
              print()
      
      # Ball Bearing Radius = 0.25 cm. Number of balls = 12 no.
      # 1st height = 0.028 cm., 2nd height = 0.278 cm., Difference = 0.25 cm
      
      # Ball Bearing Radius = 0.5 cm. Number of balls = 12 no.
      # 1st height = 0.056 cm., 2nd height = 0.556 cm., Difference = 0.5 cm
      
      # Ball Bearing Radius = 1 cm. Number of balls = 12 no.
      # 1st height = 0.111 cm., 2nd height = 1.111 cm., Difference = 1.0 cm
      
      # Ball Bearing Radius = 1.5 cm. Number of balls = 12 no.
      # 1st height = 0.167 cm., 2nd height = 1.667 cm., Difference = 1.5 cm
      
      # Ball Bearing Radius = 2 cm. Number of balls = 12 no.
      # 1st height = 0.222 cm., 2nd height = 2.222 cm., Difference = 2.0 cm
      
      
      
      

      Like

    • Dilwyn Jones's avatar

      Dilwyn Jones 11:02 am on 17 July 2020 Permalink | Reply

      I agree with the corrected calculation and a set of possible answers. I wondered if there is an additional constraint – the cross sectional area of a ball bearing must be smaller than the area of the boat, but that is true for all the answers. Then I wondered if all the ball bearings must touch the flat bottom of the boat, which means there must be at least 10 of them ignoring the spaces in between. If instead each bearing fills a square of side 2r, there must be at least 13. However I am speculating, as this information is not given in the problem.

      Like

      • Jim Randell's avatar

        Jim Randell 2:37 pm on 18 July 2020 Permalink | Reply

        I hadn’t given a great deal of thought to the shape of the boat, as I’d reasoned that if the boat was vertical tube with a hole down the middle to stack the balls into, then as long as n² ≥ 10 we could create a boat with the appropriate cross-sectional area. And this is true for all proposed values of n. Although there might be a problem with stability with this design of boat.

        However we know the balls will fit exactly across the diameter of the tank, so if we throw a convex hull around the balls (which would have to be infinitely thin for it to work), then we get a boat that holds all the balls in a line, and it has the added advantage that the boat would be unable to tip over.

        Then the cross-sectional area of the boat as a ratio of the cross-sectional area of the tank is:

        a = (𝛑 + 4(n − 1)) / (n²𝛑)

        Which has the following values:

        n = 4; a = 30.1%
        n = 8; a = 15.5%
        n = 12; a = 10.4%

        Which gets us close to the 10% stipulation at n = 12.

        The same idea, but stacking the balls two rows high gives a boat that will actually fit in the tank (but may tip over):

        a = (𝛑 + 2(n − 2)) / (n²𝛑)

        n = 4; a = 14.2%
        n = 8; a = 7.5%
        n = 12; a = 5.1%

        So with n=8 we can make a boat with a non-zero thickness hull around a cargo hold that can take the balls in a line stacked 2 high.

        Like

  • Unknown's avatar

    Jim Randell 8:44 am on 9 July 2020 Permalink | Reply
    Tags:   

    Brain-Teaser 2: Stop watch 

    From The Sunday Times, 5th March 1961 [link]

    Setting one’s watch can be a tricky business, especially if it has a sweep second hand; for, unlike the hour and minute hands, the second hand is independent of the winder. The other day, when trying to set my watch by the midday time signal, I managed to get the hour hand and minute hand accurately aligned at 12 o’clock just as the pips signalled noon, but the second hand escaped me — in fact, on the pip of twelve it was just passing the 5-second mark. I can’t say that it was exactly on the 5-second mark, but it was within a second or two of it one way or the other. I didn’t bother to adjust it further in case I should finish up with the other hands wrong as well.

    That night I forgot to wind my watch and, the next morning, found that (not unnaturally) it had stopped. I noticed that the hour hand, the minute hand and the second hand were all exactly aligned one above another.

    (1) At exactly what time had my watch stopped?
    (2) Where exactly was the second hand pointing (to the very fraction of a second) on the pip of twelve noon the previous day?

    [teaser2]

     
    • Jim Randell's avatar

      Jim Randell 8:45 am on 9 July 2020 Permalink | Reply

      See also: Enigma 1761, Enigma 383, Enigma 404, Enigma 409.

      In each 12 hour period there are 11 times when the hour and minute hand coincide.

      This Python program looks at each of these eleven times, and works out far the second hand is ahead of its expected position if it is also coincident with the hour and minute hands. And if the second hand is between 3 and 7 seconds ahead of where it should be, then we have a solution to the puzzle.

      Run: [ @repl.it ]

      from fractions import Fraction as F
      from enigma import irange, sprintf as f, printf
      
      # format fractional seconds
      def fmt(s):
        (n, r) = divmod(s, 1)
        return f("{n:02d} + {r} s")
      
      # number of seconds in a period of the clock (12 hours)
      T = 12 * 60 * 60
      
      # find times at which the hour and minute hands coincide
      for i in irange(0, 10):
        t = F(T * i, 11)
        (h, ms) = divmod(t, 60 * 60)
        (m, s) = divmod(ms, 60)
        # the second hand should be at s, but is actually at ms/60
        # the difference should be between 3 and 7 seconds
        d = (F(ms, 60) - s) % 60
        if not(d < 3 or d > 7):
          printf("i={i} -> t={t} = {h:02d}:{m:02d}:{s}; diff = {d}", s=fmt(s), d=fmt(d))
      

      Solution: (1) The watch stopped at 8:43:38 + 2/11. (2) At exactly 12 noon the second hand was showing 5 + 5/11 seconds.

      Like

  • Unknown's avatar

    Jim Randell 9:36 am on 7 July 2020 Permalink | Reply
    Tags:   

    Teaser 2663: Missed the plot 

    From The Sunday Times, 6th October 2013 [link] [link]

    My friend has a triangular vegetable plot, all sides being whole numbers of metres. Coincidentally, the dimensions of the plot are such that its perimeter (in metres) is the same as its area (in square metres). Also, the length of one of the sides is the average of the lengths of the other two sides.

    What are the lengths of the sides of the plot?

    [teaser2663]

     
    • Jim Randell's avatar

      Jim Randell 9:37 am on 7 July 2020 Permalink | Reply

      The triangle has sides of integer lengths (a, b, c) such that b = (a + c)/2.

      We can use Heron’s Formula [ @wikipedia ] to equate the area of the triangle with the perimeter, which gives us some restrictions on the sides of the triangle.

      However, we have encountered “equable” triangles before in Enigma 364, and generated a list of the 5 equable triangles, from which we can easily pick out the one that has one side that is an average of the other two.

      I also wrote code to generate k-equable triangles (integer sided triangles where the area is k times the perimeter), and that code can be reused here:

      Run: [ @repl.it ]

      from enigma import irange, isqrt, divc, divf, printf
      
      # find all triangles with integer sides where area = k . perimeter
      def triangles(k=1):
        K = 4 * k * k
        for p in irange(1, isqrt(3 * K)):
          for q in irange(max(p, divc(K + 1, p)), divf(3 * K, p)):
            (r, z) = divmod(K * (p + q), p * q - K)
            if r < q: break
            if z == 0: yield (p + q, p + r, q + r)
      
      # consider 1-equable triangles
      for (a, b, c) in triangles(1):
        if b - a == c - b:
          printf("a={a} b={b} c={c}")
      

      Solution: The lengths of the sides of the plot are: 6m, 8m, 10m.

      Like

    • GeoffR's avatar

      GeoffR 10:54 am on 8 July 2020 Permalink | Reply

      
      for a in range (3, 25):
        for b in range (a+1, 25):
          for c in range(b+1, 25):
            # a < b < c and 2 * b = a + c
            if 2 * b != a + c: continue
            # Square of perimeter
            perim_sq = (a + b + c) ** 2
            # Area squared formula is based in Heron's formula
            area_sq = 1/16 *( 4*a*a*b*b - (a*a + b*b - c*c)**2)
            if perim_sq == area_sq:
              print(f"Length of sides are {a}, {b} and {c}m")
      
      # Length of sides are 6, 8 and 10m
      

      A (5,12,13) triangle has the area equal to the perimeter numerically, but does not have one side as the average of the other two sides.

      Like

  • Unknown's avatar

    Jim Randell 8:31 am on 5 July 2020 Permalink | Reply
    Tags:   

    Brainteaser 1823: The special seven 

    From The Sunday Times, 24th August 1997 [link]

    A “special seven” fraction is one whose decimal expansion uses only 7s and/or 0s. So examples of special sevens are:

    0.7707
    0.70707070…

    which are the decimal expansions of:

    7707/10000
    70/99

    respectively.

    You can set yourself all sorts of tasks with the special sevens. For example, is it possible to find some which add up to 1? In fact this is possible in many different ways, but…

    … what is the smallest number of special sevens adding to 1?

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

    [teaser1823]

     
    • Jim Randell's avatar

      Jim Randell 8:32 am on 5 July 2020 Permalink | Reply

      Each “special seven” number corresponds to a “special one” number, which can be derived by dividing by 7.

      So if we find a collection of special sevens that sum to 1, there is a corresponding set of special ones that sum to 1/7.

      The decimal expansion of 1/7 = 0.(142857)…

      Which if we write it as a sum of special ones will require at least 8 of them to construct the 8 digit.

      Solution: The smallest number of special sevens that sum to 1 is 8.


      We can construct a collection of 8 special ones that sum to 1/7 as follows:

      0.(000100)...  [h]
      0.(000101)...  [g] = [f]
      0.(000101)...  [f]
      0.(000111)...  [e]
      0.(010111)...  [d] = [c]
      0.(010111)...  [c]
      0.(011111)...  [b]
      0.(111111)...  [a]
      -------------
      0.(142857)...
      

      (This is not the only set, the 0’s and 1’s in each column can be re-ordered to give other sets, and there is no reason why each recurring section should use the same arrangement of columns).

      Writing these as fractions (from [a] to [h]) we have:

      [a] = 0.(111111)... = 111111/999999 = 1/9
      [b] = 0.(011111)... = 11111/999999
      [c] = 0.(010111)... = 10111/999999
      [d] = 0.(010111)... = 10111/999999
      [e] = 0.(000111)... = 111/999999 = 1/9009
      [f] = 0.(000101)... = 101/999999
      [g] = 0.(000101)... = 101/999999
      [h] = 0.(000100)... = 100/999999
      

      Which sum to: 142857/999999 = 1/7 as required.

      Multiplying these values by 7 gives us a set of 8 special sevens that sum to 1:

      [a] = 0.(777777)... = 777777/999999 = 7/9
      [b] = 0.(077777)... = 77777/999999
      [c] = 0.(070777)... = 70777/999999
      [d] = 0.(070777)... = 70777/999999
      [e] = 0.(000777)... = 777/999999 = 7/9009
      [f] = 0.(000707)... = 707/999999
      [g] = 0.(000707)... = 707/999999
      [h] = 0.(000700)... = 700/999999
      

      Like

  • Unknown's avatar

    Jim Randell 5:14 pm on 3 July 2020 Permalink | Reply
    Tags:   

    Teaser 3015: Quid pro quo 

    From The Sunday Times, 5th July 2020 [link] [link]

    In Readiland the unit of currency is the quid. Notes are available in two denominations and with these notes it is possible to make any three-figure number of quid. However, you need a mixture of the denominations to make exactly 100 quid. Furthermore, there is only one combination of denominations that will give a total of 230 quid.

    What are the two denominations?

    [teaser3015]

     
    • Jim Randell's avatar

      Jim Randell 5:32 pm on 3 July 2020 Permalink | Reply

      See also: Enigma 228, Enigma 1194, Enigma 1154.

      If we suppose the denominations are x and y (where gcd(x, y) = 1). Then the largest amount that cannot be made using the denominations is given by F(x, y) = xy − x − y.

      Both denominations are needed to make 100, so they must both be less than 100, and neither can be a divisor of 100.

      The following Python 3 program runs in 67ms.

      Run: [ @repl.it ]

      from enigma import (coprime_pairs, express, printf)
      
      # consider denominations x, y
      for (x, y) in coprime_pairs(97):
        if 100 % x == 0 or 100 % y == 0: continue
      
        # largest inexpressible amount is < 100
        if not (x * y - x - y < 100): continue
      
        # there is exactly 1 way to express 230
        e230s = list(express(230, (x, y)))
        if len(e230s) != 1: continue
      
        # output solution
        printf("x={x} y={y}; 100 -> {e100s}, 230 -> {e230s}", e100s=list(express(100, (x, y))))
      

      Solution: The two denominations are 3 quid and 49 quid.

      The largest amount that cannot be made using these denominations is 95 quid. All larger amounts are possible.

      The only way to make 100 quid is 17× 3 quid + 1× 49 quid.

      The only way to make 230 quid is 44× 3 quid + 2× 49 quid.


      Manually:

      The largest integer that is not expressible in k different ways using denominations x, y is given by:

      F[k](x, y) = kxy − x − y

      So we need to find co-prime values for x, y, less than 100 that are not divisors of 100, and the following hold:

      F[1](x, y) < 100
      F[2](x, y) ≥ 230

      This will ensure that all 3-digit amounts are expressible, and that to make 100 requires both denominations. All that remains for each pair of candidate denominations is to determine if there is a unique expression for 230.

      Given a value for x we can use the inequalities to find a range of viable values for y.

      Starting with x = 3, gives y = 47 .. 51 and only y = 47, 49 are co-prime with x.

      230 can be expressed in two ways using (3, 47): 230 = 14× 3 + 4× 47 = 61× 3 + 1× 47.

      230 can be expressed in only one way using (3, 49): 230 = 44× 3 + 2× 49.

      So (3, 49) is a viable solution.

      For x = 4, we get only y = 34, but x divides 100, so this is not a candidate solution.

      And for larger values of x there are no possibilities for y, so we are done.

      Like

  • Unknown's avatar

    Jim Randell 8:37 am on 2 July 2020 Permalink | Reply
    Tags:   

    Teaser 2748: I go up and up 

    From The Sunday Times, 24th May 2015 [link] [link]

    I have in mind an arithmetic progression; ie, a sequence which goes up and up by a fixed amount (such as 1, 10, 19, 28, …) and — as in that example — one of the terms of my progression will eventually be 10000. But I have coded the numbers by consistently replacing digits by letters and in this way the arithmetic progression has become

    I, GO, UP, AND, …

    Find the value of POUND.

    This puzzle is not included in the book The Sunday Times Brain Teasers Book 1 (2019).

    [teaser2748]

     
    • Jim Randell's avatar

      Jim Randell 8:38 am on 2 July 2020 Permalink | Reply

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

      The following run file executes in 93ms.

      Run: [ @replit ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      # the given terms must be evenly spaced
      "GO - I == UP - GO"
      "UP - GO == AND - UP"
      
      # and eventually we reach 10000
      "div(10000 - I, GO - I) > 0"
      
      --answer="POUND"
      

      Solution: POUND = 28706.

      The first 4 terms in the progression are:

      4, 38, 72, 106, …

      Each term being derived from the previous term by adding 34. So the sequence is:

      S(n) = 4 + 34n
      S(294) = 10000

      Like

    • GeoffR's avatar

      GeoffR 8:07 pm on 21 October 2021 Permalink | Reply

      
      from itertools import permutations
      
      for p1 in permutations('1234567890', 8):
        A, D, G, I, N, O, U, P = p1
        if '0' in (I, G, U, A, P): continue
        I = int(I)
        GO, UP, AND = int(G + O), int(U + P), int(A + N + D)
        if GO - I == UP - GO == AND - UP:
          POUND = int(P + O + U + N + D)
          # Look for a term with a value of 10000
          for n in range (1,500):
            # nth term of an arithmetic series
            if I + (n - 1)*(UP - GO) == 10000:
              print(f"POUND = {POUND}")
              print(f"I={I}, GO={GO}, UP={UP}, AND={AND}")
                           
      # POUND = 28706
      # I=4, GO=38, UP=72, AND=106   
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:25 am on 30 June 2020 Permalink | Reply
    Tags:   

    Brain-Teaser 524: [Digit sums] 

    From The Sunday Times, 27th June 1971 [link]

    At a party at my friend Smith’s house recently, we were watching a rather attractive couple who were evidently enjoying each other’s company.

    “I don’t know those two”, I said, “but I should be interested to know the lady’s age”.

    “Well, I’m not the man to be explicit about that subject”, said Smith, “even though I know she’s rather proud of it. But her age is the sum of the digits in the sum of the digits in all the numbers from one to a hundred thousand inclusive”.

    “Certainly not very explicit”, I said, “but then, how old is her companion?”

    “Believe it or not”, said Smith, “but his age is the sum of the digits in the sum of the digits in all the numbers from one to a million inclusive”.

    What was the difference between their ages?

    This puzzle was originally published with no title.

    [teaser524]

     
    • Jim Randell's avatar

      Jim Randell 9:26 am on 30 June 2020 Permalink | Reply

      It is not too onerous to tackle the problem directly using Python.

      This program counts the digits in each of the required sequences. It runs in 405ms.

      Run: [ @replit ]

      from enigma import (fcompose, nsplit, irange, printf)
      
      # sum the digits in a number
      dsum = fcompose(nsplit, sum)
      
      # sum the digits of the numbers in a sequence
      dsums = lambda s: sum(map(dsum, s))
      
      # age of the lady
      a1 = dsum(dsums(irange(1, 100_000)))
      
      # age of the gentleman
      a2 = dsum(dsums(irange(1, 1_000_000)))
      
      printf("a1={a1} a2={a2}, diff={d}", d=abs(a1 - a2))
      

      Solution: They are the same age, so the difference is 0.


      But it is also fairly straightforward so solve analytically:

      If we suppose DS(k) is the sum of the digits in the integers (represented in base 10), starting from 0, that are less than k, then:

      The sum of the digits in 0 .. 9 is 45.

      DS(10^1) = 45

      If we then consider numbers from 0 .. 99, the units digits comprise 10 lots of DS(10), and the tens digits comprise another 10 lots of DS(10):

      DS(10^2) = 20 DS(10^1) = 900

      For the digits from 0 .. 999, the tens and units digits together comprise 10 lots of DS(100), and the hundreds digits comprise 100 lots of DS(10).

      DS(10^3) = 10 DS(10^2) + 100 DS(10^1) = 13,500

      Carrying on this construction we find:

      DS(10^n) = 45n × 10^(n − 1)

      (See: OEIS A034967 [ @oeis ]).

      The sums we are interested in are therefore:

      DS(10^5) = 45×5×(10^4) = 2,250,000
      DS(10^6) = 45×6×(10^5) = 27,000,000

      The extra 0 at the beginning doesn’t make any difference, but we need to add in the digits for the end points, which are both powers of 10, so only contribute an extra 1.

      So we are interested in the digit sums of 2,250,001 and 27,000,001, both of which are 10.

      So both ages are 10, and their difference is 0.

      Like

  • Unknown's avatar

    Jim Randell 8:10 am on 28 June 2020 Permalink | Reply
    Tags: by: Colin   

    Brainteaser 1822: Teasing triangles 

    From The Sunday Times, 17th August 1997 [link]

    Each of the ten line segments in the picture have been given a different whole number value from 0 to 9. A figure shown in a triangle equals the sum of the three values assigned to the sides of the triangle. The figures which have not been given from the other two triangles are the same as each other and the two add together to give a perfect square.

    What are the numbers assigned to each side?

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

    [teaser1822]

     
    • Jim Randell's avatar

      Jim Randell 8:11 am on 28 June 2020 Permalink | Reply

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

      The following run file executes in 96ms.

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      # regions given
      "A + F + G = 9"
      "E + F + H = 7"
      "C + I + J = 23"
      
      # remaining regions have the same value...
      "B + G + J == D + H + I"
      # ... and sum to a square
      "is_square(B + D + G + H + I + J)"
      
      # suppress verbose output
      --template=""
      

      Solution: The numbers assigned to each line are shown below:

      Like

      • Jim Randell's avatar

        Jim Randell 12:38 pm on 30 June 2020 Permalink | Reply

        The puzzle as originally published in The Sunday Times was worded as follows:

        Each of the ten line segments in the picture have been given a different whole number value from 0 to 9. A figure shown in a triangle equals the sum of the three values assigned to the sides of the triangle. The figures which have not been given from the other two triangles when added together to give 30. What is the value assigned to CD?

        CD being the external edge of the 23 triangle (which is the value C in my solution).

        We can adjust the run-file accordingly:

        #! python -m enigma -rr
        
        SubstitutedExpression
        
        # regions given
        "A + F + G = 9"
        "E + F + H = 7"
        "C + I + J = 23"
        
        # the other two triangles added together to give 30
        "B + D + G + H + I + J = 30"
        
        --answer="C"
        
        # suppress verbose output
        --template=""
        

        And we find that there are 24 ways to fill out the numbers, but the value on the required edge is always 9.

        Like

    • GeoffR's avatar

      GeoffR 4:09 pm on 29 June 2020 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % using the vertex labels A,B,C,D and E (as original diagram in link)
      % plus F for the central point
      
      % outside edges of the pentagon
      var 0..9:AB; var 0..9:BC; var 0..9:CD; var 0..9:DE; var 0..9:EA;
      
      % internal lines to central point F
      var 0..9:AF; var 0..9:BF; var 0..9:CF; var 0..9:DF; var 0..9:EF;
      
      constraint all_different ([AB, BC, CD, DE, EA, AF, BF, CF, DF, EF]);
      
      % three given triangles are AEF, ABF and CDF
      constraint AF + EF + EA == 7;
      constraint AB + AF + BF == 9;
      constraint CF + DF + CD == 23;
      
      % the other two triangle sides (BCF and DEF) are the same total 
      % and the two add together to give a perfect square
      
      constraint DE + EF + DF == BF + CF + BC;
      
      set of int: sq = {16, 25, 36, 49, 64, 81};
      constraint (DE + EF + DF + BF + CF + BC) in sq;
      
      solve satisfy;
      
      % AB = 0; BC = 3; CD = 6; DE = 5; EA = 1;
      % AF = 2; BF = 7; CF = 8; DF = 9; EF = 4;
      % % time elapsed: 0.06 s
      % ----------
      % ==========
      
      

      Like

    • GeoffR's avatar

      GeoffR 1:46 pm on 8 July 2020 Permalink | Reply

      My solution below is updated for the original Sunday Times description, the solution above being the solution being based on the teaser in the book Brainteasers (2002, edited by Victor Bryant).

      
      % A Solution in MiniZinc - original Sunday Times teaser
      include "globals.mzn";
       
      % using the vertex labels A,B,C,D and E (as original ST diagram in link)
      % plus F for the central point
       
      % outside edges of the pentagon
      var 0..9:AB; var 0..9:BC; var 0..9:CD; var 0..9:DE; var 0..9:EA;
       
      % internal lines to central point F
      var 0..9:AF; var 0..9:BF; var 0..9:CF; var 0..9:DF; var 0..9:EF;
       
      constraint all_different ([AB, BC, CD, DE, EA, AF, BF, CF, DF, EF]);
       
      % three given triangles are AEF, ABF and CDF
      constraint AF + EF + EA == 7;
      constraint AB + AF + BF == 9;
      constraint CF + DF + CD == 23;
      
      % for figures in triangles not given ie triangles DEF and BCF
      constraint DE + EF + DF + BF + CF + BC == 30; 
       
      solve satisfy;
      output ["CD = " ++ show(CD) ];
      
      % CD = 9
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 5:08 pm on 26 June 2020 Permalink | Reply
    Tags:   

    Teaser 3014: Family business 

    From The Sunday Times, 28th June 2020 [link] [link]

    George and Martha run a company with their five daughters. The telephone extensions all have four positive unequal digits and strangely only four digits appear in the seven extensions:

    Andrea: ABCD
    Bertha: ACDB
    Caroline: BACD
    Dorothy: DABC
    Elizabeth: DBCA
    George: CABD
    Martha: CDAB

    They noticed the following:

    Andrea’s and Bertha’s add up to Dorothy’s.
    Bertha’s and Elizabeth’s add up to George’s.
    Caroline’s and Dorothy’s add up to Martha’s.

    What is Andrea’s extension?

    [teaser3014]

     
    • Jim Randell's avatar

      Jim Randell 5:14 pm on 26 June 2020 Permalink | Reply

      Any one of the expressions is sufficient to determine the answer.

      I used the [[ SubstitutedExpression.split_sum() ]] solver from the enigma.py library to evaluate the alphametic expressions.

      The following run file executes in 77ms. (Internal runtime of the generated code is just 39µs).

      #! python3 -m enigma -rr
      
      SubstitutedExpression.split_sum
      
      --invalid="0,ABCD"
      
      "ABCD + ACDB = DABC"
      "ACDB + DBCA = CABD"
      "BACD + DABC = CDAB"
      
      --answer="ABCD"
      

      Solution: Andrea’s extension is 2385.


      Here’s my manual solution:

      We can split each of the three sums into columns to give us partial equations, that may have a carry out or a carry in.

      B + D = C appears at both the left- and right-hand side of a sum, so can’t have a carry in or a carry out, so it is a complete equation.

      We then see that the partial sum BC + CD = AB cannot have a carry in, so the sum A + B = D is also a complete equation.

      The sum A + A = D cannot have a carry out, and if it does not have a carry in then we deduce that B = A, which is not possible. So it does have a carry in, and B = A + 1 is a complete equation.

      Using these three complete equations we derive:

      B = A + 1
      D = 2A + 1
      C = 3A + 2

      For digits in the range 1-9 the only viable values are:

      A = 1, B = 2, C = 5, D = 3
      A = 2, B = 3, C = 8, D = 5

      Only one of these assignment works in the original three sums, so we have the answer.

      Or, we can use the partial equation BC + CD = AB, which we now know must have a carry out, to give a fourth complete equation: 9B + 11C + D = 10A + 100. We can then substitute in the other equations to get the value of A, and from that the remaining values:

      9B + 11C + D = 10A + 100
      9(A + 1) + 11(3A + 2) + (2A + 1) = 10A + 100
      44A + 32 = 10A + 100
      34A = 68

      A = 2, B = 3, C = 8, D = 5

      Like

    • GeoffR's avatar

      GeoffR 9:19 am on 27 June 2020 Permalink | Reply

      I simulated a normal addition sum in MiniZinc for the first equation to find an answer.

      The values obtained confirmed the other two equations.

      % A Solution in MiniZinc
      include "globals.mzn";
       
      % Using the first equation only ie Andrea + Bertha = Dorothy
      % 
      %  A B C D
      %  A C D B
      %  -------
      %  D A B C
      %  -------
      
      var 1..9:A; var 1..9:B; var 1..9:C; var 1..9:D;
      var 0..1:carry1; var 0..1:carry2; var 0..1:carry3; 
      
      constraint C == (B + D) mod 10 
      /\ carry1 = (B + D) div 10;
      
      constraint B = (carry1 + C + D) mod 10 /\ 
      carry2 = (carry1 + C + D) div 10;
      
      constraint A = (carry2 + B + C) mod 10
      /\ carry3 = (carry2 + B + C) div 10;
      
      constraint D = carry3 + 2*A;
      
      solve satisfy;
      
      output ["A,B,C,D = " ++ show([A,B,C,D]) ];
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 2:41 pm on 27 June 2020 Permalink | Reply

      A short Python programme confirms that the second and third equations give the same value for Andrea’s extension as my MiniZinc programme for the first equation:

      
      from itertools import permutations
      
      # Bertha’s and Elizabeth’s extensions add up to George’s extn.
      # 2nd Equation: ACDB + DBCA = CABD
      
      for p in permutations((1,2,3,4,5,6,7,8,9),4):
          a, b, c, d = p
          acdb = 1000*a + 100*c + 10*d + b
          dbca = 1000*d + 100*b + 10*c + a
          cabd = 1000*c + 100*a + 10*b + d
          if acdb + dbca == cabd:
              # Find Andrea's extension
              abcd = 1000*a + 100*b + 10*c + d
              print("2. Andrea's extension =", abcd)
      
      # Caroline’s and Dorothy’s extensions add up to Martha’s extn.
      # 3rd Equation : BACD + DABC = CDAB
      
      for p in permutations((1,2,3,4,5,6,7,8,9),4):
          a, b, c, d = p
          bacd = 1000*b + 100*a + 10*c + d
          dabc = 1000*d + 100*a + 10*b + c
          cdab = 1000*c + 100*d + 10*a + b
          if bacd + dabc == cdab:
              # Find Andrea's extension
              abcd = 1000*a + 100*b + 10*c + d
              print("3. Andrea's extension =", abcd)
      
      

      Like

  • Unknown's avatar

    Jim Randell 7:16 am on 25 June 2020 Permalink | Reply
    Tags:   

    Brain-Teaser 1: Tall story 

    From The Sunday Times, 26th February 1961 [link]

    “We’re a biggish family in more ways than one”, said Jeremy. “Five, including triplets, and three of us the same height. I’m 6 ft. and aged 72; Jim is three inches taller than John and three years older; and John and Julian’s combined heights are 5 ft. 11 in. more than Joe’s and their combined ages 71 years more than his. Our aggregate height in inches equals our aggregate age in years, but no one’s age in years equals Joe’s height in inches”.

    What were the name, age and height of the tallest of the triplets?

    This was the first of the regular Teaser puzzles published in The Sunday Times. It was accompanied by the following introduction:

    Brain-Teasers: a Weekly Feature

    Great numbers of our readers have found entertainment and interest in the Brain-Teasers, or mathematical problems, which we have published from time to time, usually at holiday week-ends. Now we intend to make them a weekly feature of The Sunday Times.

    Readers themselves have supplied many of the best of the problems in the past, and we invite them to continue to do so. A fee of £10 will be paid for each problem accepted.

    Problems should fulfil the following conditions: Both the problem and the solution must be expressible in reasonably brief compass. No advanced or specialist mathematical techniques should be necessary. Solutions should be unique, or otherwise admit of no uncertainty in judging. Problems must be original. Diagrams are admissible if they are not too complicated.

    The problem below is the invention of a reader who observes: “Any who are stumped by it after the expiry of an hour should feel cause for concern”.

    A prize of £3 was offered.

    [teaser1]

     
    • Jim Randell's avatar

      Jim Randell 7:17 am on 25 June 2020 Permalink | Reply

      We can associate values (A, B, C, D, E) with (Jez, Jim, Jon, Jul, Joe), that correspond to either their heights in inches or their ages in years, both give rise to the same set of equations:

      A = 72
      B = C + 3
      C + D = E + 71
      x = y = z

      where (x, y, z) are some three of (A, B, C, D, E).

      This gives us a set of 5 simultaneous equations in 5 variables.

      This Python program finds solutions to these equations (using the [[ Matrix.linear() ]] solver from the enigma.py library), groups the solutions by the sum of the set of values, and then looks for two sets with the same sum (one set for the ages, and another set for the height) that satisfy the condition that Joe’s height does not appear in the set of ages.

      It runs in 55ms.

      Run: [ @replit ]

      from enigma import (Matrix, subsets, as_int, multiset, group, printf)
      
      # labels for the people involved
      # A = Jez, B = Jim, C = Jon, D = Jul, E = Joe
      labels = (A, B, C, D, E) = (0, 1, 2, 3, 4)
      
      # solve equations specified as: (ps, ms, k)
      # for integer solutions
      def solve(eqs):
        (A, B) = (list(), list())
        for (ps, ms, k) in eqs:
          eq = [0] * 5
          for (i, n) in multiset.from_seq(ps).items(): eq[i] += n
          for (i, n) in multiset.from_seq(ms).items(): eq[i] -= n
          A.append(eq)
          B.append(k)
        try:
          # find values that are positive integers
          return Matrix.linear(A, B, valid=(lambda x: as_int(x, include='+')))
        except ValueError:
          return
      
      # find values that satisfy the equations
      def generate():
        # choose the three with the same value
        for (x, y, z) in subsets(labels, size=3):
          # solve the equations
          r = solve([
            ([A], [], 72), # A = 72
            ([B], [C], 3), # B = C + 3
            ([C, D], [E], 71), # C + D = E + 71
            ([x], [y], 0), # x = y
            ([y], [z], 0), # y = z
          ])
          if r is None: continue
          # check there is a set of triplets
          if not (3 in multiset.from_seq(r).values()): continue
          # return candidate solutions
          yield r
      
      # group sets of solutions by the sum of the values
      d = group(generate(), by=sum)
      
      # consider values for the sum
      for (t, rs) in d.items():
        # choose two sets of values for the ages and heights
        for (age, height) in subsets(rs, size=2, select="M"):
          # no one's age is the same as Joe's height
          if height[E] in age: continue
          for (n, a, h) in zip("Jez Jim Jon Jul Joe".split(), age, height):
            printf("{n}: age {a} yr, height {h} in")
          printf()
      

      Solution: Jim is the tallest of the triplets. His age is 72, his height is 6ft 2in.

      The complete characteristics are:

      Jez: age 72 yr, height 72 in
      Jim: age 72 yr, height 74 in
      Jon: age 69 yr, height 71 in
      Jul: age 74 yr, height 71 in
      Joe: age 72 yr, height 71 in

      So: Jeremy, Jim, Joe are the triplets (same age), and: John, Julian, Joe are the same height.

      There are 7 ways to solve the equations, but there is only one pair that has the same sum, and this gives rise to the solution.

      Like

    • GeoffR's avatar

      GeoffR 4:10 pm on 25 June 2020 Permalink | Reply

      I found three solutions in MinZinc.

      The first solution was invaiid as there was no single maximum height.

      The second solution was the same as Jim’s solution above.

      There appears to be another solution, which gives the maximum height of Jim as 6ft 3 in, with an age of 75 years. If the puzzle had stated that age and height could not be the same, then this last solution could be ruled out – it appears to conform to the stated conditions in the puzzle.

      
      % A Solution in MiniZinc
      include "globals.mzn";
       
      var 10..99: JezAge; var 10..99: JimAge; var 10..99: JonAge; 
      var 10..99: JulAge; var 10..99: JoeAge; 
      
      var 50..80: JezHt; var 50..80: JimHt; var 50..80: JonHt; 
      var 50..80: JulHt; var 50..80: JoeHt; 
      
      % Three of us the same height
      % (A,B,C,D,E) = (Jez, Jim, Jon, Jul, Joe)
      constraint (JezHt == JimHt /\ JimHt == JonHt)  % ABC
      \/ (JezHt == JimHt /\ JimHt == JulHt)   % ABD
      \/ (JezHt == JimHt /\ JimHt == JoeHt)   % ABE
      \/ (JezHt == JonHt /\ JonHt == JulHt)   % ACD
      \/ (JezHt == JonHt /\ JonHt == JoeHt)   % ACE
      \/ (JezHt == JulHt /\ JulHt == JoeHt)   % ADE
      \/ (JimHt == JonHt /\ JonHt == JulHt)   % BCD
      \/ (JimHt == JonHt /\ JonHt == JoeHt)   % BCE
      \/ (JimHt == JulHt /\ JulHt == JoeHt)   % BDE
      \/ (JonHt == JulHt /\ JulHt == JoeHt);  % CDE
      
      % Three of us the same age(triplets)
      constraint (JezAge == JimAge /\ JimAge == JonAge)  % ABC
      \/ (JezAge == JimAge /\ JimAge == JulAge)   % ABD
      \/ (JezAge == JimAge /\ JimAge == JoeAge)   % ABE
      \/ (JezAge == JonAge /\ JonAge == JulAge)   % ACD
      \/ (JezAge == JonAge /\ JonAge == JoeAge)   % ACE
      \/ (JezAge == JulAge /\ JulAge == JoeAge)   % ADE
      \/ (JimAge == JonAge /\ JonAge == JulAge)   % BCD
      \/ (JimAge == JonAge /\ JonAge == JoeAge)   % BCE
      \/ (JimAge == JulAge /\ JulAge == JoeAge)   % BDE
      \/ (JonAge == JulAge /\ JulAge == JoeAge);  % CDE
      
      % Jez is 6 ft. and aged 72
      constraint JezHt == 72 /\ JezAge == 72;
      
      % Jim is three inches taller than John and three years older
      constraint JimHt - 3 == JonHt /\ JimAge - 3 == JonAge;
      
      %  John and Julian’s combined heights are 5 ft. 11 in. more than Joe’s
      %  and their combined ages 71 years more than his
      constraint JonHt + JulHt - 71 == JoeHt;  
      constraint JonAge + JulAge - 71 == JoeAge;
      
      % Our aggregate height in inches equals our aggregate age in years
      constraint  JimHt + JezHt + JulHt + JonHt + JoeHt 
      == JimAge + JezAge + JulAge  + JonAge + JoeAge;
      
      % No one’s age in years equals Joe’s height in inches
      constraint JoeHt != JezAge /\ JoeHt != JimAge
      /\ JoeHt != JonAge /\ JoeHt != JulAge;
      
      solve satisfy;
      
      output ["Ages: " ++ show([JezAge, JimAge, JonAge, JulAge, JoeAge])]++
      ["\nHeights: " ++ show([JezHt, JimHt, JonHt, JulHt, JoeHt])];
      
      %           Jez Jim Jon Jul Joe
      % Ages:    [72, 72, 69, 72, 70]  << no single tallest member 
      % Heights: [72, 72, 69, 72, 70]  << Therefore, an invalid solution
      % ----------
      % Ages:    [72, 72, 69, 74, 72]  <<  Jim is tallest member at 6 ft. 2 in.
      % Heights: [72, 74, 71, 71, 71]  <<  with an age of 72 years
      % ----------
      % Ages:    [72, 75, 72, 72, 73]  << Jim is tallest member at 6 ft. 3 in.
      % Heights: [72, 75, 72, 72, 73]  <<  with an age of 75 years
      % ----------
      % ==========
      
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 4:15 pm on 25 June 2020 Permalink | Reply

        @Geoff: The condition “no one’s age in years equals Joe’s height in inches”, means that Joe’s age is not equal to Joe’s height. So this disposes of two of your solutions. (And effectively means that the same set of solutions cannot be used for both ages and heights).

        Like

    • GeoffR's avatar

      GeoffR 4:38 pm on 25 June 2020 Permalink | Reply

      @Jim: Fair point. I was interpreting only other people’s ages only could not be equal to Joe’s height. It could have been more clearly stated that this condition also applied to Joe himself!

      Like

    • Frits's avatar

      Frits 3:51 pm on 28 July 2020 Permalink | Reply

      from enigma import SubstitutedExpression, icount #, multiset
      
      # suppose Jez not part of three --> Also either Jon or Jim not part of three 
      #   --> Joe and Jul part of three --> Jez = 72, Jon = 71, Jim 74, Joe = 71, Jul = 71 or
      #       (Jon has to be 71)            Jez = 72, Jon = 71, Jim 74, Joe = 74, Jul = 74 
      # suppose Jez part of three 
      #   --> if both Jim and Jon not part of three --> Jon = 71, Jim = 74
      #       else Jim or Jon is 72 --> Jez = 72, Jon = 72, Jim 75, Jul == Joe - 1 or
      #                                 Jez = 72, Jon = 69, Jim 72, Jul == Joe + 2
      # --> Joe 70-74
      # --> Jim 72,74,75
      # --> Jul 71,72,74
      # --> Jon 69,71,72
          
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          #  Jez Age    Joe Age    Jim Age    Jul Age    Jon Age
          #    AB         CD         EF         GH         IJ     
          #  Jez Hght   Joe Hght   Jim Hght   Jul Hght   Jon Hght
          #    KL         MN         OP         QR         ST   
          
          # Jez is 6 ft. and aged 72
          "AB = 72", "KL = 72",
        
          # Jim is three inches taller than John and three years older
          "IJ + 3 == EF" ,"ST + 3 = OP",
         
          # Jul and Jon's combined heights are 5 ft 11 in more than Joe's
          # and their combined ages 71 years more than his
          "QR + ST == MN + 71", "GH + IJ == CD + 71",
          
          # aggregate height in inches equals aggregate age in years
          "AB + CD + EF + GH + IJ == KL + MN + OP + QR + ST",
          
          # No one's age in years equals Joe's height in inches
          "AB != MN", "CD != MN", "EF != MN", "GH != MN", "IJ != MN",
          
          # Three have the same age, three have same height
          "icount([AB, CD, EF, GH, IJ], (lambda d: v([AB, CD, EF, GH, IJ]) == d)) == 3",
          "icount([KL, MN, OP, QR, ST], (lambda d: v([KL, MN, OP, QR, ST]) == d)) == 3",
          #"sorted(multiset.from_seq([AB, CD, EF, GH, IJ]).values()) == [1, 1, 3]",
          #"sorted(multiset.from_seq([KL, MN, OP, QR, ST]).values()) == [1, 1, 3]",
          
          # ----------------------------
          # try to speed up 
          # ----------------------------
          
          #"CD in ([70,71,72,73,74])",
          #"EF in ([72,74,75])",
          #"GH in ([71,72,74])",
          #"IJ in ([69,71,72])",
          
          #"MN in ([70,71,72,73,74])",
          #"OP in ([72,74,75])",
          #"QR in ([71,72,74])",
          #"ST in ([69,71,72])",
        ],
        distinct="", 
        code="v = lambda x: max(x,key=x.count)",
        answer="AB, CD, EF, GH, IJ, KL, MN, OP, QR, ST"
      )      
                                
      # Print answers
      
      for (_, ans) in p.solve(verbose=0): 
         print("        Age        |       Height") 
         print("Jez Joe Jim Jul Jon Jez Joe Jim Jul Jon")
         print(ans)
         
      # Output:   
      #         Age        |       Height
      # Jez Joe Jim Jul Jon Jez Joe Jim Jul Jon
      # (72, 72, 72, 74, 69, 72, 71, 74, 71, 71) 
      

      Remarks regarding speed:

      "IJ + 3 == EF" ,"ST + 3 = OP",  # Approx 1.3 secs
      "IJ + 3 = EF" ,"ST + 3 == OP",  # Approx 14 secs
      "IJ + 3 = EF" ,"ST + 3 = OP",   # Approx 14 secs
      "IJ + 3 == EF" ,"ST + 3 == OP", # SyntaxError: too many statically nested blocks
      

      The speed up logic I had to invent before I discovered the “IJ + 3 == EF” ,”ST + 3 = OP” equations.
      In theory the multiset equations are too restrictive

      Like

  • Unknown's avatar

    Jim Randell 8:52 am on 23 June 2020 Permalink | Reply
    Tags:   

    Teaser 2525: [Sandwich numbers] 

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

    “Sandwich numbers” are those of the form BFB, where B is the bread and is a single digit, and F is the filling of any length: BFB has to be divisible by F. For example, 11371 is a sandwich with filling 137 since 11371 = 83 × 137. Incidentally, all of 21372, 31373, … , 91379 are also sandwich numbers, and these nine are said to make up a “sandwich box”.

    The filling in my sandwich box today is the smallest beginning with a 3.

    What is my filling?

    This puzzle was originally published with no title.

    [teaser2525]

     
    • Jim Randell's avatar

      Jim Randell 8:58 am on 23 June 2020 Permalink | Reply

      My first thought was to generate integers that start with a 3, and then check to see if they make a “sandwich box”. But this was taking a long time, so here’s some analysis to produce a faster program:

      If f is an n digit “filling” number, then the corresponding sandwich numbers in a sandwich box are:

      b.10^(n + 1) + 10.f + b

      for b = 1 .. 9. And each of these must be divisible by f.

      Clearly f will divide the 10.f part, which leaves b(10^(n + 1) + 1), when b = 1, we must have f divides (10^(n + 1) + 1), and if this is the case f will divide the remaining sandwich numbers with b > 1.

      So, f must be a divisor of (10^(n + 1) + 1), and we are looking for the smallest f that starts with a 3.

      f.g = 10^(n + 1) + 1

      and f is an n digit number that starts with 3, so g = 26 .. 33.

      This Python program runs in 49ms. (Internal runtime is 46µs).

      Run: [ @replit ]

      from enigma import (irange, inf, printf)
      
      def solve():
        # consider n digit fillings
        x = 101
        for n in irange(1, inf):
          # f is an n digit divisor of x
          for g in irange(33, 26, step=-1):
            (f, r) = divmod(x, g)
            if r == 0:
              yield f
          x *= 10
          x -= 9
      
      for f in solve():
        printf("f = {f}")
        break
      

      Solution: The smallest filling beginning with 3 is: f = 3448275862069.

      Where: 29f = 10^14 + 1.

      So we see why the simple program was taking so long. (In the end it took 32 minutes).

      In fact there is a family of filling numbers that start with 3 that take the form:

      344827586206 [8965517241379310344827586206] 9

      where the section in brackets can be included 0 or more times, to give fillings with length (13 + 28k) digits.

      We can adapt the program to generate all filling numbers without the restriction that the leading digit is 3.

      from enigma import (irange, inf, match, printf)
      
      # generate "filling" numbers
      def filling():
        # consider n digit fillings
        x = 101
        for n in irange(1, inf):
          # f is an n digit divisor of x
          M = x // 10
          for g in irange(101, 11, step=-1):
            (f, r) = divmod(x, g)
            if r == 0 and f < M <= 10 * f:
              yield f
          x *= 10
          x -= 9
      
      for f in filling():
        printf("f = {f}")
        if match(f, '3*'): break
      

      See: OIES A116436 [ @oeis.org ]

      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

Why are you reporting this comment?

Report type
Design a site like this with WordPress.com
Get started