Recent Updates Page 50 Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 8:08 am on 8 September 2020 Permalink | Reply
    Tags:   

    Brainteaser 1108: Field fare 

    From The Sunday Times, 30th October 1983 [link]

    The field near to our home is rectangular. Its longer sides run east to west. In the south-east corner there is a gate. In the the southern fence there is a stile a certain number of yards from the south-west corner. In the northern fence there is a stile, the same certain number of yards from the north-east corner. A straight path leads across the field from one stile to the other. Another straight path from the gate is at right angles to the first path and meets it at the old oak tree in the field.

    When our elder boy was ten, and his brother five years younger, they used to race from opposite ends of a long side of the field towards the stile in that as the target. But later, when they were a little less unevenly matched, they raced from opposite stiles towards the oak as the target. Of course the younger boy raced over the shorter distances. This change of track gave the younger boy 9 yards more and the elder boy 39 yards less than before to reach the target.

    The sides of the field and the distances of the oak tree from each of the stiles are all exact numbers of yards.

    How far apart are the two stiles, and how far is the oak tree from the gate?

    [teaser1108]

     
    • Jim Randell's avatar

      Jim Randell 8:09 am on 8 September 2020 Permalink | Reply

      If we suppose the north and south boundaries of the field are separated by a distance of a, and the stiles divide the these boundaries into sections with length x and y, and the distances from the tree to the stiles are b and c, and the distance from the tree to the corner is z. Then we have a layout like this:

      When the children were younger they would run distances x and y.

      And when they were older the distances are b and c.

      Hence:

      b = x + 9
      c = y − 39
      ⇒ y − x = c − b + 48

      The distance between the two stiles, (b + c) forms the hypotenuse of a Pythagorean triangle with other sides of (y − x) = (c − b + 48) and a (triangle: S1 Y S2).

      So we can consider Pythagorean triples for this triangle. One of the non-hypotenuse sides corresponds to a, and the other when added to the hypotenuse gives:

      (b + c) + (c − b + 48) = 2c + 48

      So choosing one of the sides for a, allows us to calculate the value for c, and then b, x, y, z.

      The distance G S2 is the hypotenuse of the two triangles (G T S2) and (G X S2), and we can check that these hypotenuses match.

      The following Python program runs in 58ms.

      Run: [ @replit ]

      from enigma import (pythagorean_triples, div, sqrt, printf)
      
      # consider pythagorean triples corresponding to (a, c - b + 48, b + c)
      
      for (X, Y, Z) in pythagorean_triples(1000):
        # choose a side for a
        for (a, other) in [(X, Y), (Y, X)]:
          # and: other + Z = 2c + 48
          c = div(other + Z - 48, 2)
          if c is None or c < 1: continue
          b = Z - c
          x = b - 9
          y = c + 39
          if not (0 < b < c and 0 < x < y): continue
          # calculate z^2
          z2 = y * y - c * c
          if not (b * b + z2 == a * a + x * x): continue
          # output solution
          printf("({X}, {Y}, {Z}) -> a={a} b={b} c={c}, x={x} y={y} z={z:g}", z=sqrt(z2))
      

      Solution: The stiles are 200 yards apart. The oak tree is 117 yards from the gate.

      The short side of the field is 120 yards.

      The long side is 230 yards, and this is split into sections of 35 and 195 yards by the stiles.

      The oak tree is 44 yards from one stile and 156 yards from the other.


      This puzzle appeared in the 1986 book Microteasers (p 4) by Victor Bryant, in which he deals with solving puzzles using the BBC Micro. (See: [ @EnigmaticCode ]).

      Here is Victor Bryant’s BBC BASIC program (slightly modified by me to stop after the first solution is encountered, and print the run time for the program):

      >LIST
         10 REM Victor Bryant program for Teaser 1108
         15 start% = TIME
         20 FOR c = 26 TO 500  
         30   FOR b = 25 TO c - 1
         40     a = SQR((c + 39)^2 - c^2 + b^2 - (b - 9)^2)
         50     IF a <> INT(a) THEN 70
         60     IF a^2 <> (b + c)^2 - (c - b + 48)^2 THEN 70
         65     PRINT "Distances: stiles - tree = "; b; " and "; c; " & short side = "; a
         66     GOTO 85
         70   NEXT b
         80 NEXT c
         85 PRINT "(Time = "; (TIME - start%) / 100; "s)"
      
      >RUN
      Distances: stiles - tree = 44 and 156 & short side = 120
      (Time = 250.2s)
      
      >
      

      Here is a Python program using the same approach. It takes 65ms to find the first solution, or 137ms to run to completion. (On an emulated BBC Micro Victor’s program takes about an hour to run to completion).

      from enigma import (irange, subsets, is_square, printf)
      
      for (b, c) in subsets(irange(25, 500), size=2):
        a = is_square((c + 39)**2 - c**2 + b**2 - (b - 9)**2)
        if a is None: continue
        if a**2 + (c - b + 48)**2 != (b + c)**2 : continue
        printf("a={a} b={b} c={c}")
        break
      

      Comparing internal runtimes we find that my program runs in 2.4ms (to completion), and the port of Victor’s program in 17.3ms (to first solution; 219ms to completion). They search a similar solution space, (i.e. distance S1 S2 less than 1000 yards).

      Like

      • Jim Randell's avatar

        Jim Randell 3:08 pm on 14 September 2020 Permalink | Reply

        Here’s my analytical solution:

        We have:

        x = b − 9
        y = c + 39

        and

        y² = c² + z²
        (c + 39)² = c² + z²
        2.39.c + 39.39 = z²
        c = (z² − 39.39) / 2.39
        c = (z²/39 − 39)/2

        Now, c is an integer, so z²/39 is an odd integer:

        z²/39 = 2k + 1
        z² = 39(2k + 1)

        and:

        c = (2k + 1 − 39)/2
        c = k − 19

        and: c > b > 9, so k > 29.

        Also, we have:

        a² = (b + c)² − (c − b + 48)²
        a² = (b² + 2bc + c²) − (b² + c² − 2bc − 96b + 96c + 2304)
        a² = 4bc − 96b + 96c + 2304
        a² = 4(b − 24)(c + 24)
        a² = 4(b − 24)(k + 5)

        So: b > 24k > 44.

        And:

        b² + z² = a² + (b − 9)²
        b² + 39(2k + 1) = 4(b − 24)(k + 5) + (b² − 18b + 81)
        78k + 39 = 4bk + 20b − 96k − 480 − 18b + 81
        2b(2k + 1) = 174k + 438
        b = (87k + 219) / (2k + 1)

        As k → ∞, b → 87/2 = 43.5, so: b ≥ 44 and k ≤ 175.

        This gives us a limited range of values for k, which we can check:

        from enigma import (irange, div, is_square, sqrt, printf)
        
        # consider possible values for k
        for k in irange(45, 175):
          # calculate a, b, c, x, y, z
          c = k - 19
          b = div(87 * k + 219, 2 * k + 1)
          if b is None or not (0 < b < c): continue
          a = is_square(4 * (b - 24) * (c + 24))
          if a is None: continue
          x = b - 9
          y = c + 39
          z = sqrt(78 * k + 39)
          if not (0 < x < y): continue
          # output solution
          printf("[k={k}] a={a} b={b} c={c}; x={x} y={y} z={z:g}")
        

        Or we can narrow down the values with further analysis:

        Now:

        (b − 24) = 39(k + 5) / (2k + 1)

        and so:

        a² = [2(k + 5)]² (39 / (2k + 1))

        The value of (2k + 1) is in the range 91 to 351, so the (39 / (2k + 1)) part, must contribute pairs of factors (which divide into the pairs of factors provided by the [2(k + 5)]² part).

        So (2k + 1) is some odd square multiple of 39 (which also means that z must be an integer).

        The only option in the required range is:

        (2k + 1) = 39×3² = 351
        k = 175
        a = 120, b = 44, c = 156
        x = 35, y = 195, z = 117

        Like

    • John Crabtree's avatar

      John Crabtree 6:11 am on 9 September 2020 Permalink | Reply

      (b + c)^2 = a^2 +(y – x)^2
      a^2 + x^2 = b^2 +z^2
      y^2 = c^2 + z^2
      Adding the three equations and simplifying gives bc + xy = z^2
      But z^2 = 39(2y – 39) = (39k)^2
      And so y = 39(k^2 + 1)/2
      Using b = x + 9 and c = y – 39, leads to x = 69/2 + 9/(2.k^2)
      For integral solutions, either k = 1 and then x = y = z = 39 which is invalid,
      or k = 3, x = 35, y = 195 and z = 117, etc with a = 120 as a check

      Like

      • John Crabtree's avatar

        John Crabtree 4:08 am on 10 September 2020 Permalink | Reply

        I should have included: a = 39k + 9/k = z + 9/k

        Like

        • Frits's avatar

          Frits 10:37 am on 10 September 2020 Permalink | Reply

          Nice solution, I checked your equations.

          I think you have to proof k (and z?) is an integer
          before only only considering k=1 and k=3.

          fi, k = 3^0.5 would also lead to an integral solution for x.

          We only know for sure that a, b, c and x,y are natural numbers.

          Like

          • John Crabtree's avatar

            John Crabtree 4:02 pm on 11 September 2020 Permalink | Reply

            Frits, thank you. I had assumed that z was a whole number, which we are not told. We also need to consider a, =(2y -30)/k before determining possible values of k. y is a whole number, and so k must be rational, = n/m. But then for y to be a whole number, m= 1, and so k is an integer.
            I think that this makes the manual solution, which should exist, complete.

            Like

        • Frits's avatar

          Frits 11:31 pm on 12 September 2020 Permalink | Reply

          Hi John,

          Unfortunately some of your text was lost during formatting. I can follow the logic if (2y -30)/k equals an integer expression. What formula or variable did you intend to use as left hand side of ” (2y -30)/k”?

          I now used opening and closing pre tags for this section.

          Like

  • Unknown's avatar

    Jim Randell 4:06 pm on 6 September 2020 Permalink | Reply
    Tags:   

    Brain-Teaser 7: The Golden Wedding 

    From The Sunday Times, 9th April 1961 [link]

    Many years ago there lived in Addison Street four sisters, June, Alice, Mary and Dawn. In that street there lived also four young men. Harry, Graham, Richard and Tom. On a bright, spring day in 1911 each of the four sisters married the young man of her choice. Each of the four couples had children: June and Mary together had as many children as Alice.

    When all the children grew up and, in the course of time, married to become parents themselves each of them had the same number of children as there had been in his or her own family (for instance, had June borne five children, each of these five would have had five, too, and so on).

    Last week, the four original couples celebrated their Golden Wedding at a party attended by all their 170 grandchildren. Richard, who despite the fact that his was the smallest family had always been fascinated by numbers, pointed out that June and Alice together had as many grandchildren, as Mary and Dawn. He also noticed that Harry had four times as many grandchildren as Graham had children.

    Which sisters married which young men on that bright spring day in 1911?

    [teaser7]

     
    • Jim Randell's avatar

      Jim Randell 4:07 pm on 6 September 2020 Permalink | Reply

      The (children, grandchildren) pairs are of the form (n, n²).

      We know in total there are 170 grandchildren, and that (J and A) have as many grandchildren as (M and D). So each bracketed pair has 85 grandchildren in total.

      This Python program runs in 51ms.

      Run: [ @replit ]

      from enigma import (Record, irange, subsets, printf)
      
      # record for each sister: w=initial, c=children, g=grandchildren
      ss = (J, A, M, D) = tuple(Record(w=x) for x in "JAMD")
      
      # squares less than 85 (map: square -> root)
      sq = dict((n * n, n) for n in irange(0, 9))
      
      # find pairs with squares that sum to 85
      pairs = list(s for s in subsets(sq.keys(), size=2, select="M") if sum(s) == 85)
      
      # choose 4 values for the children/grandchildren of each sister
      for (x1, x2) in subsets(pairs, size=2, select="M"):
        xs = x1 + x2
        (J.g, A.g, M.g, D.g) = xs
        (J.c, A.c, M.c, D.c) = (sq[x] for x in xs) 
        # "J and M together have as many children as A"
        if not (J.c + M.c == A.c): continue
        # assign the sisters to husbands
        for (H, G, R, T) in subsets(ss, size=4, select="P"):
          # "R has the smallest family"
          if not (R.c == min(J.c, A.c, M.c, D.c)): continue
          # "H has 4 times as many grandchildren as G has children"
          if not (H.g == 4 * G.c): continue
          # output solution
          printf("H+{H.w}={H.c}+{H.g}; G+{G.w}={G.c}+{G.g}; R+{R.w}={R.c}+{R.g}; T+{T.w}={T.c}+{T.g}")
      

      Solution: The married couples are: Dawn & Harry; Alice & Graham; June & Richard; Mary & Tom.

      Dawn & Harry: 6 children; 36 grandchildren.
      Alice & Graham: 9 children; 81 grandchildren.
      June & Richard: 2 children; 4 grandchildren.
      Mary & Tom: 7 children; 49 grandchildren.

      Like

      • Jim Randell's avatar

        Jim Randell 9:52 pm on 6 September 2020 Permalink | Reply

        Manually we see that (J and A) and (M and D) both have 85 grandchildren, so they correspond to the 2 ways to represent 85 as the sum of 2 squares:

        85 = 2² + 9² = 6² + 7²

        Also (J and M) have as many children as A.

        So J has 2 children, M has 7 and A has 9, leaving D with 6.

        R has the smallest family. So:

        J & R have 2 children and 4 grandchildren.

        And H has 4 times as many grandchildren as G has children, so:

        D & H have 6 children and 36 grandchildren.
        A & G have 9 children and 81 grandchildren.

        Leaving:

        M & T have 7 children and 49 grandchildren.

        Like

    • Frits's avatar

      Frits 5:50 pm on 6 September 2020 Permalink | Reply

       
      from enigma import  SubstitutedExpression, irange, seq_all_same
      
      p = SubstitutedExpression([
          # each sister married a brother
          # so similar number of children for sisters and brothers
          "sorted([J,A,M,D]) == sorted([H,G,R,T])",
          
          # in total 170 grandchildren 
          "J*J + A*A + M*M + D*D == 170",
          "H*H + G*G + R*R + T*T == 170",
          
          #(J and A) have as many grandchildren as (M and D). 
          "J*J + A*A == M*M + D*D",
          
          # "J and M together have as many children as A"    
          "J + M = A",
          
          # "R has the smallest family"
          "min(H,G,R,T) == R", 
          
          # "H has 4 times as many grandchildren as G has children"
          "H*H == 4 * G"
         ],
          verbose=0,
          # example with 2 code functions
          answer="(J, A, M, D, H, G, R, T)",
          distinct="",
          digits=irange(1, 9),
      )
      
      sis = ['June', 'Alice', 'Mary', 'Dawn']
      bro = ['Harry', 'Graham', 'Richard',  'Tom']
      
      # Print answers
      for (_, ans) in p.solve():
        for i in range(4):
          for k in range(4,8):
            if ans[k] == ans[i]:
              print(f"{sis[i]:5} & {bro[k-4]:7}: \
      {ans[i]} children, {ans[i]*ans[i]} grandchildren")
        
      # Output:
      #
      # June  & Richard: 2 children, 4 grandchildren
      # Alice & Graham : 9 children, 81 grandchildren
      # Mary  & Tom    : 7 children, 49 grandchildren
      # Dawn  & Harry  : 6 children, 36 grandchildren
      

      Like

  • Unknown's avatar

    Jim Randell 4:42 pm on 4 September 2020 Permalink | Reply
    Tags:   

    Teaser 3024: Triple jump 

    From The Sunday Times, 6th September 2020 [link] [link]

    From a set of playing cards, Tessa took 24 cards consisting of three each of the aces, twos, threes, and so on up to the eights. She placed the cards face up in single row and decided to arrange them such that the three twos were each separated by two cards, the threes were separated by three cards and so forth up to and including the eights, duly separated by eight cards. The three aces were numbered with a one and were each separated by nine cards. Counting from the left, the seventh card in the row was a seven.

    In left to right order, what were the numbers on the first six cards?

    [teaser3024]

     
    • Jim Randell's avatar

      Jim Randell 5:03 pm on 4 September 2020 Permalink | Reply

      (See also: Enigma 369).

      I assume that for each set of three cards with value n (the aces have a value of 9): the leftmost one is separated from the middle one by n other cards, and the middle one is separated from the rightmost one by n other cards. (So the leftmost and rightmost instances are not separated by n cards).

      I treat the puzzle as if cards with values 2 to 9 were used (3 copies of each). Then when we have found a solution we can replace the value 9 cards with aces.

      This Python program runs in 50ms.

      Run: [ @repl.it ]

      from enigma import update, join, printf
      
      # generate sequences in <s> where 3 occurrences of the numbers in <ns> are
      # separated by <n> other slots
      def generate(s, ns):
        if not ns:
          yield s
        else:
          n = ns[0]
          for (i, x) in enumerate(s):
            j = i + n + 1
            k = j + n + 1
            if not(k < len(s)): break
            if not(x is None and s[j] is None and s[k] is None): continue
            yield from generate(update(s, [i, j, k], [n, n, n]), ns[1:])
      
      # start with 24 slots
      s = [None] * 24
      # we are told where the 7's are:
      s[6] = s[14] = s[22] = 7
      # place the remaining cards
      for ss in generate(s, [9, 8, 6, 5, 4, 3, 2]):
        # output solution
        printf("{ss}", ss=join(("??23456781"[x] for x in ss), sep=" "))
      

      Solution: The first 6 cards are: 1, 2, 6, 8, 2, 5.

      Without pre-placing the 7’s there are four sequences that work (or two sequences, and their reverses):

      (1 2 6 8 2 5 7 2 4 6 1 5 8 4 7 3 6 5 4 3 1 8 7 3)
      (3 1 7 5 3 8 6 4 3 5 7 1 4 6 8 5 2 4 7 2 6 1 2 8)
      (8 2 1 6 2 7 4 2 5 8 6 4 1 7 5 3 4 6 8 3 5 7 1 3)
      (3 7 8 1 3 4 5 6 3 7 4 8 5 1 6 4 2 7 5 2 8 6 2 1)
      

      The answer to the puzzle is the first of these sequences.

      Like

      • Jim Randell's avatar

        Jim Randell 11:15 am on 5 September 2020 Permalink | Reply

        Here is a MiniZinc model to solve the puzzle:

        %#! minizinc -a
        
        include "globals.mzn";
        
        % index of middle cards with values 2 to 9
        var 11..14: i9;
        var 10..15: i8;
        var  9..16: i7;
        var  8..17: i6;
        var  7..18: i5;
        var  6..19: i4;
        var  5..20: i3;
        var  4..21: i2;
        
        % each card is in a different slot
        constraint all_different([
          i9, i9 - 10, i9 + 10,
          i8, i8 - 9, i8 + 9,
          i7, i7 - 8, i7 + 8,
          i6, i6 - 7, i6 + 7,
          i5, i5 - 6, i5 + 6,
          i4, i4 - 5, i4 + 5,
          i3, i3 - 4, i3 + 4,
          i2, i2 - 3, i2 + 3,
        ]);
        
        % there is a 7 in slot 7
        constraint i7 - 8 = 7;
        
        solve satisfy;
        

        Like

        • Jim Randell's avatar

          Jim Randell 4:32 pm on 5 September 2020 Permalink | Reply

          And here’s an alternative implementation in Python that collects the indices of the central cards of each value, rather than filling out the slots:

          from enigma import irange, update, join, printf
          
          # generate indices for the central number in <ns> where the 3
          # occurrences are separated by <n> other slots
          def solve(ns, m=dict(), used=set()):
            if not ns:
              yield m
            else:
              n = ns[0]
              # consider possible indices for the middle card with value n
              d = n + 1
              for i in irange(d, 23 - d):
                (j, k) = (i - d, i + d)
                if not used.intersection((i, j, k)):
                  yield from solve(ns[1:], update(m, [(n, i)]), used.union((i, j, k)))
          
          # place 7 (at 6, 14, 22), and solve for the remaining cards
          for m in solve([9, 8, 6, 5, 4, 3, 2], { 7: 14 }, set([6, 14, 22])):
            # construct the solution sequence
            s = [0] * 24
            for (n, i) in m.items():
              d = n + 1
              s[i] = s[i - d] = s[i + d] = (1 if n == 9 else n)
            # output solution
            printf("{s}", s=join(s, sep=" "))
          

          Like

    • Frits's avatar

      Frits 8:57 pm on 5 September 2020 Permalink | Reply

      Based on Jim’s MiniZinc model.

      Only the last”v” call is necessary, the rest is added for performance., it’s a bit messy.

       
      from enigma import SubstitutedExpression, irange, seq_all_different
      
      p = SubstitutedExpression([
          "K < 3",
          "M < 3",
          "O < 3",
          "O > 0",
          "A < 3 and AB > 3 and AB < 22",       # i2
          "C < 3 and CD > 4 and CD < 21",       # i3
          "E < 3 and EF > 5 and EF < 20",       # i4
          "G < 3 and GH > 6 and GH < 19",       # i5
          "IJ > 7 and IJ < 18",                 # i6
          "KL = 15",                            # i7
          "MN > 9 and MN < 16",                 # i8
          "OP > 10 and OP < 15",                # i9
          "v([KL, KL - 8, KL + 8, \
              MN, MN - 9, MN + 9])",
          "v([KL, KL - 8, KL + 8, \
              MN, MN - 9, MN + 9, \
              OP, OP - 10, OP + 10])",
          "v([AB, AB - 3, AB + 3, \
              KL, KL - 8, KL + 8, \
              MN, MN - 9, MN + 9, \
              OP, OP - 10, OP + 10])",
          "v([AB, AB - 3, AB + 3, \
              CD, CD - 4, CD + 4, \
              KL, KL - 8, KL + 8, \
              MN, MN - 9, MN + 9, \
              OP, OP - 10, OP + 10])",
          "v([AB, AB - 3, AB + 3, \
              CD, CD - 4, CD + 4, \
              EF, EF - 5, EF + 5, \
              KL, KL - 8, KL + 8, \
              MN, MN - 9, MN + 9, \
              OP, OP - 10, OP + 10])",
          "v([AB, AB - 3, AB + 3, \
              CD, CD - 4, CD + 4, \
              EF, EF - 5, EF + 5, \
              GH, GH - 6, GH + 6, \
              IJ, IJ - 7, IJ + 7, \
              KL, KL - 8, KL + 8, \
              MN, MN - 9, MN + 9, \
              OP, OP - 10, OP + 10])",
         ],
          verbose=0,
          d2i="",          # allow leading zeroes
          code="v = lambda x: seq_all_different(x)",
          answer="(AB, CD, EF, GH, IJ, KL, MN, OP)",
          distinct="",
      )
      
      out = [2, 3, 4, 5, 6, 7, 8, 1]
      
      # Print answers
      for (_, ans) in p.solve(verbose=0):
        print("Numbers on first 6 cards: ", end="")
        for k in range(1,7):
          for i in range(8):
            if ans[i] == k: print(f"{out[i]} ", end="")
            if ans[i] - i - 3 == k: print(f"{out[i]} ", end="")
        print()
      

      Like

  • Unknown's avatar

    Jim Randell 8:53 am on 3 September 2020 Permalink | Reply
    Tags:   

    Teaser 2741: Neater Easter Teaser 

    From The Sunday Times, 5th April 2015 [link] [link]

    In my latest effort to produce a neater Easter Teaser I have once again consistently replaced each of the digits by a different letter. In this way:

    NEATER
    LATEST
    EASTER
    TEASER

    represent four six-figure numbers in increasing order.

    Furthermore, the following addition sum is correct:

    What is the value of BONNET?

    [teaser2741]

     
    • Jim Randell's avatar

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

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

      The following run file executes in 249ms.

      Run: [ @replit ]

      #! python -m enigma -rr
      
      SubstitutedExpression "FLORAL + EASTER = BONNET" "N < L < E < T"
      

      Solution: BONNET = 961178.


      Or for a faster solution we can use a 1-line Python program that uses the [[ SubstitutedSum ]] solver from the enigma.py library:

      from enigma import SubstitutedSum
      
      SubstitutedSum(['FLORAL', 'EASTER'], 'BONNET').go(lambda s: s['N'] < s['L'] < s['E'] < s['T'])
      

      Like

      • Jim Randell's avatar

        Jim Randell 10:05 am on 28 September 2022 Permalink | Reply

        For an even faster solution we can use the [[ SubstitutedExpression.split_sum ]] solver.

        The following run file executes in 72ms. And the internal run time of the generated program is 234µs).

        Run: [ @replit ]

        #! python3 -m enigma -rr
        
        SubstitutedExpression.split_sum
        
        "FLORAL + EASTER = BONNET"
        
        --extra="N < L < E < T"
        --answer="BONNET"
        

        Like

    • Frits's avatar

      Frits 7:10 pm on 11 October 2020 Permalink | Reply

      Another Python constraint solver.

      Maybe this is not the most efficient way to do it as it takes 3 seconds.

       
      # https://pypi.org/project/python-constraint/
      
      from constraint import Problem, AllDifferentConstraint
      from functools import reduce
      
      to_num = lambda seq: reduce(lambda x, y: 10 * x + y, seq)
      
      # "FLORAL + EASTER = BONNET" "N < L < E < T"
      
      letters = "FLORAESTBN"
      
      problem = Problem()
      
      for x in letters:
        problem.addVariable(x, range(0,10))
      
      problem.addConstraint(AllDifferentConstraint())
      
      problem.addConstraint(lambda a, b, c, d: a < b and b < c and c < d,
                            ("N", "L", "E", "T"))
                            
      problem.addConstraint(lambda F,L,O,R,A,E,S,T,B,N: to_num([F,L,O,R,A,L]) +
                            to_num([E,A,S,T,E,R]) == to_num([B,O,N,N,E,T]),
                            ("F","L","O","R","A","E","S","T","B","N"))
                            
      for ans in problem.getSolutions():
        print(f"{ans['B']}{ans['O']}{ans['N']}{ans['N']}{ans['E']}{ans['T']}")
      

      Like

      • Frits's avatar

        Frits 11:03 am on 12 October 2020 Permalink | Reply

        A better model to use for future puzzles.

        “N < L" "L < E" "E < T" is a little bit faster than "N < L < E < T"

        # https://pypi.org/project/python-constraint/
        
        from constraint import Problem, AllDifferentConstraint
        from functools import reduce
        from re import findall
        
        # generate numerical expression   inp: A,B,C  output: A*100+B*10+C
        expa = lambda w: ''.join(w[i]+'*'+str(10**(len(w)-i-1))+'+'
                         for i in range(len(w)-1))+w[-1]
        
        # variables in constraint have to be uppercase
        def addC(constraint):
          vars = set(findall('([A-Z])', constraint))
          nbrs = set(findall('([A-Z]+)', constraint))
          
          for n in nbrs:
            constraint = constraint.replace(str(n), expa(n))
          
          parms = "lambda "+', '.join(vars) + ': ' + constraint + \
                  ', ("' + '", "'.join(vars) + '")'
          
          exec("p.addConstraint("+parms+")")  
          
        def report(ans, vars):
          print(f"{vars} = ", end="")
          for x in vars:
            print(f"{ans[x]}", end="")
          print()  
         
        p = Problem()
        
        p.addVariables(("LORASTN"), range(0,10))  
        p.addVariables(("FEB"), range(1,10))  
        
        p.addConstraint(AllDifferentConstraint())
        
        addC("N < L") 
        addC("L < E") 
        addC("E < T")         
        addC("FLORAL + EASTER == BONNET")
        
        for ans in p.getSolutions():
          report(ans, "BONNET")
        

        Like

    • GeoffR's avatar

      GeoffR 8:45 am on 12 October 2020 Permalink | Reply

      
      % A Solution in MiniZinc
      include "alldifferent.mzn";
      
      var 0..9: N; var 1..9: E; var 0..9: A;
      var 0..9: T; var 0..9: L; var 0..9: S;
      var 0..9: R; var 1..9: B; var 0..9: O;
      var 1..9: F;
      
      constraint alldifferent ([N,E,A,T,L,S,R,B,O,F]);
      
      constraint 
        (L + A*10 + R*100 + O*1000 + L*10000 + F*100000)
      + (R + E*10 + T*100 + S*1000 + A*10000 + E*100000)
      = (T + E*10 + N*100 + N*1000 + O*10000 + B*100000) ;
      
      solve satisfy;
      
      output["BONNET = " ++ show(T + E*10 + N*100 + N*1000 + O*10000 + B*100000)];
      
      % Answer: 961178
      % time elapsed: 0.05s
      % ----------
      % ==========
      % time elapsed: 0.18 s
      % Finished in 436msec
      
      
      

      A standard MiniZinc solution produced a reasonable run-time, but what is the correct run-time to quote in a posting
      :
      a) time to print first solution?
      b) time elapsed ?
      c) Finished time?

      I like to think it is the time it takes to print out the first time quoted!

      Like

      • Jim Randell's avatar

        Jim Randell 12:43 pm on 12 October 2020 Permalink | Reply

        @GeoffR:

        When I report the timings of my programs I use the complete execution time of the command that runs the program presented.

        So I hardly ever report the time taken to find the first solution, as I am usually more interested in the time taken to exhaustively search the solution space and find all possible solutions. (The exception being if the program is only looking for the first answer it finds, and terminates once it is found).

        I can collect this data using the time builtin of my shell which reports the total runtime of a command. I perform multiple executions, and take the shortest time. I call this the “external runtime”.

        For example comparing my SubstitutedSum() based solution above against a MiniZinc model (I added in a constraint to ensure N < L < E < T) I get:

        % time python3.9 teaser2741.py
        python3.9 teaser2741.py  0.04s user 0.01s system 91% cpu 0.055 total
        
        % time minizinc -a teaser2741geoff.mzn
        minizinc -a teaser2741geoff.mzn  0.07s user 0.02s system 96% cpu 0.095 total
        

        So I would report a runtime of 55ms for the Python program, and 95ms for the MiniZinc model.

        This lets me compare programs using different languages, but makes it less easy to compare programs in the same language that run very quickly. For example on my system run a Python program that consists of the single statement pass, takes between 30ms and 50ms (depending on the version of Python). So to compare Python programs that take less than 50ms I sometimes use the “internal runtime”. I do this by using the [[ Timer ]] class from the enigma.py library, putting a [[ timer.start() ]] statement before the first statement of the program, and a [[ timer.stop() ]] statement at the end. This removes some of the overhead involved in using Python, but means the values are not necessary useful when comparing with non-Python programs, and each language will have a different way of reporting internal runtimes.

        For example, the internal runtime of the Python program above is 13.65ms.

        MiniZinc has a --statistics option which reports: solveTime=0.002176, so it may have an internal runtime of 2.176ms.

        Like

      • Frits's avatar

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

        @GeoffR, I only quote timings if I think the program runs (too) slow. Elapsed times more than one second (PyPy) seem suspicous to me for relatively simple puzzles.

        Normally a) is not important to me for Python programs as you might be lucky to get a solution quickly.

        I use enigma.run(“xxxx.py”, timed=1) for Python programs (normally running with PyPy).

        Unfortunately I was not able to used function toNum in the output statement.

        % A Solution in MiniZinc
        include "alldifferent.mzn";
         
        var 0..9: N; var 1..9: E; var 0..9: A;
        var 0..9: T; var 0..9: L; var 0..9: S;
        var 0..9: R; var 1..9: B; var 0..9: O;
        var 1..9: F;
        
        var 102234..987765: bonnet;
         
        function var int: toNum(array[int] of var int: a) =
                  let { int: len = length(a) }
                  in
                  sum(i in 1..len) (
                    ceil(pow(int2float(10), int2float(len-i))) * a[i]
                  );
        
        constraint alldifferent ([N,E,A,T,L,S,R,B,O,F]);
        
        constraint bonnet == toNum([B, O, N, N, E, T]);
        constraint toNum([F, L, O, R, A, L]) + toNum([E, A, S, T, E, R]) == bonnet;
           
        solve satisfy;
        output["BONNET = " ++ show(bonnet)];
         
        % Answer: 961178
        

        Like

  • Unknown's avatar

    Jim Randell 8:31 am on 1 September 2020 Permalink | Reply
    Tags:   

    Teaser 1885: Sacred sign 

    From The Sunday Times, 1st November 1998 [link]

    The “Sacred Sign of Solomon” consists of a pentagon whose vertices lie on a circle, roughly as shown:

    Starting at one of the angles and going round the circle the number of degrees in the five angles of the large pentagon form an arithmetic progression; i.e., the increase from the first to the second equals the increase from the second to the third, etc.

    As you can see, the diagonals form a small inner pentagon and in the case of the sacred sign one of its angles is a right angle.

    What are the five angles of the larger pentagon?

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

    [teaser1885]

     
    • Jim Randell's avatar

      Jim Randell 8:32 am on 1 September 2020 Permalink | Reply

      If the five sides of the pentagon are A, B, C, D, E, then the angles subtended by each side at the circumference of the circle are all equal, say, a, b, c, d, e.

      (The angles in the small pentagon, such as ace denote the sum of the three symbols, i.e. (a + c + e) etc).

      We see that: a + b + c + d + e = 180° (from many triangles, cyclic quadrilaterals, and the angles subtended at the centre of the circle).

      The internal angles of the pentagon are an arithmetic progression, say: (x − 2y, x − y, x, x + y, x + 2y).

      So, let’s say:

      a + b + e = x − 2y
      a + b + c = x − y
      b + c + d = x
      c + d + e = x + y
      a + d + e = x + 2y

      And these angles sum to 540°.

      5x = 540°
      x = 108°

      And if: x = b + c + d = 108°, then: a + e = 72°

      So starting with: a + d + e = x + 2y, we get:

      72° + d = 108° + 2y
      d = 36° + 2y

      Similarly we express each of the angles a, b, c, d, e in terms of y:

      a = 36° + y
      b = 36° − 2y
      c = 36°
      d = 36° + 2y
      e = 36° − y

      The internal angles of the small pentagon are:

      a + c + e = 108°
      a + b + d = 108° + y
      b + c + e = 108° − 3y
      a + c + d = 108° + 3y
      b + d + e = 108° − y

      And one of these is 90°.

      Our candidates are (b + c + e) and (b + d + e).

      b + c + e = 90° ⇒ y = 6°
      a = 42°
      b = 24°
      c = 36°
      d = 48°
      e = 30°

      And the angles of the large pentagon are then:

      a + b + e = 96°
      a + b + c = 102°
      b + c + d = 108°
      c + d + e = 114°
      a + d + e = 120°

      For the other candidate:

      b + d + e = 90° ⇒ y = 18°
      a = 54°
      b = 0°
      c = 36°
      d = 72°
      e = 18°

      This is not a viable solution, so:

      Solution: The angles of the larger pentagon are: 96°, 102°, 108°, 114°, 120°.

      Like

      • Jim Randell's avatar

        Jim Randell 11:54 am on 1 September 2020 Permalink | Reply

        Here’s a drawing of the symbol, with the two perpendicular lines indicated:

        Like

  • Unknown's avatar

    Jim Randell 8:45 am on 30 August 2020 Permalink | Reply
    Tags: ,   

    Teaser 1858: Cash flow 

    From The Sunday Times, 26th April 1998 [link]

    We play a variation of a famous puzzle game using coins instead of rings. We start with a pile of coins consisting of at least one of each of the 1p, 2p, 5p, 10p, 20p, 50p and £1 coins, with no coin on top of another that is smaller in diameter. So the 5p coins are on the top, then the 1p, 20p, £1, 10p, 2p and 50p coins respectively, with the 50p coins being on the bottom. One typical pile is illustrated:

    The object of the game is to move the pile to a new position one coin at a time. At any stage there can be up to three piles (in the original position, the final position, and one other). But in no pile can any coin ever be above another of smaller diameter.

    We did this with a pile of coins recently and we found that the minimum number of moves needed equalled the value of the pile in pence. We then doubled the number of coins by adding some 1p, 5p and 50p coins totalling less than £3, and we repeated the game. Again the minimum number of moves equalled the value of the new pile in pence.

    How many coins were in the pile for the first game?

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

    [teaser1852]

     
    • Jim Randell's avatar

      Jim Randell 8:46 am on 30 August 2020 Permalink | Reply

      (See also: Enigma 1254, Enigma 14).

      In a standard Towers of Hanoi problem there are n discs of different sizes.

      In the optimal solution the largest disc is moved 1 time, the next largest 2 times, then 4 times, 8 times, 16 times, …

      So we get a total number of moves: H(n) = 2^n − 1.

      In this version there are multiple discs the same size, but we can treat a block of k discs the same size as a single disk that requires k moves, and solve the puzzle in the same way.

      So, the stack shown in the diagram (which I denote (1, 2, 1, 1, 3, 1, 1), by counting the number of coins of each size, starting at the bottom), will require: 1×1 + 2×2 + 1×4 + 1×8 + 3×16 + 1×32 + 1×64 = 161 moves.

      In general if there number of coins of each size is (A, B, C, D, E, F, G) then the number of moves is:

      A + 2B + 4C + 8D + 16E + 32F + 64G

      and the monetary value of the coins is:

      50A + 2B + 10C + 100D + 20E + F + 5G

      and if these values are equal, we get:

      49A + 6C + 92D + 4E = 31F + 59G

      In the puzzle, the number of coins is doubled by increasing A by a, F by f, G by g, and their monetary value is less than £3:

      50a + f + 5g < 300

      but the monetary value of the augmented stack is still equal to the number of moves required, so:

      49a = 31f + 59g

      We can work out possible values for a, f, g, and their sum gives us the initial number of coins (which is the answer to the puzzle).

      We can then look for stacks of coins of this size with the monetary value and number of moves, and then add the extra coins to the stack and see if the condition still holds.

      This Python 3 program runs in 54ms.

      from enigma import (irange, div, printf)
      
      # denominations of coins by size (largest diameter to smallest diameter)
      ds = (50, 2, 10, 100, 20, 1, 5)
      
      # number of moves for sequence of discs (largest to smallest)
      def H(ns):
        t = 0
        m = 1
        for n in ns:
          t += m * n
          m *= 2
        return t
      
      # decompose <t> into <k> positive numbers
      def decompose(t, k, s=[]):
        if k == 1:
          yield s + [t]
        else:
          for x in irange(1, t + 1 - k):
            yield from decompose(t - x, k - 1, s + [x])
      
      # choose a value for a (= 50p, so there must be 1-5)
      for a in irange(1, 5):
        # choose a value for g (= 5p)
        for g in irange(1, (299 - 50 * a) // 5):
          # calculate f
          f = div(49 * a - 59 * g, 31)
          if f is None or f < 1 or not(50 * a + 5 * g + f < 300): continue
          # the total number of extra coins = the number of original coins
          n = a + f + g
          # output solution
          printf("n={n} [a={a} f={f} g={g}]")
      
          # allocate the number of original coins (n in total)
          for ns in decompose(n, 7):
            # calculate the total value of the stack
            t = sum(n * d for (n, d) in zip(ns, ds))
            # calculate the number of moves for the stack
            m = H(ns)
            if m != t: continue
      
            # make the final stack
            ns2 = list(ns)
            ns2[0] += a
            ns2[5] += f
            ns2[6] += g
            # calculate the total value of the stack
            t2 = sum(n * d for (n, d) in zip(ns2, ds))
      
            # output solution
            printf("-> {ns} [{t}] -> {ns2} [{t2}]")
      

      Solution: There were 12 coins in the original pile.

      There is only one possible size for the original pile, and it must be composed of: (2× 50p, 1× 2p, 1× 10p, 1× £1, 3× 20p, 1× 1p, 3× 5p), making a total value of 288p and requiring 288 moves.

      We then add: (5× 50p, 6× 1p, 1× 5p) = 261p to make a pile with total value 549p and requiring 549 moves.

      Like

  • Unknown's avatar

    Jim Randell 7:09 pm on 28 August 2020 Permalink | Reply
    Tags:   

    Teaser 3023: Timely coincidence 

    From The Sunday Times, 30th August 2020 [link] [link]

    George and Martha possess two digital “clocks”, each having six digits. One displays the time on a 24-hour basis in the format hh mm ss, typically 15 18 45, and the other displays the date in the format dd mm yy, typically 18 07 14.

    On one occasion, George walked into the room to find that the two “clocks” displayed identical readings. Martha commented that the long-term (400-year) average chance of that happening was 1 in just over a six-digit number. That six-digit number gives the birth date of one their daughters.

    On what date was that daughter born?

    [teaser3023]

     
    • Jim Randell's avatar

      Jim Randell 7:35 pm on 28 August 2020 Permalink | Reply

      On any particular day (ignoring jumps in time, such as leap seconds, daylight savings time, calendar reforms, etc.) there is either a 1/86400 chance of observing the clocks displaying the same an any given second (assuming they clocks are equally likely to be observed at any particular second of the day – which is unlikely), or a 0/86400 chance if the date does not correspond to a valid time.

      The following Python program runs in 100ms.

      Run: [ @repl.it ]

      import datetime
      from enigma import int2base, printf
      
      # consider a 400 year period, and count dates that give a valid time
      d = datetime.date(day=1, month=1, year=1900)
      i = datetime.timedelta(days=1)
      n = 0
      t = 0
      while d.year < 2300:
        if d.day < 24 and d.year % 100 < 60: n += 1
        d += i
        t += 1
      
      # output solution
      t *= 86400
      x = t // n
      printf("p = {n} / {t} (~ 1 / {x}); date = {b}", b=int2base(x, group=2, sep=".", width=6))
      

      Solution: The daughter was born on 19th May 1961.


      In order for the date to be read as a valid time we need the day of the month to be in the range 1-23, and the last 2 digits of the year to be in the range 0-59.

      In each year of the required range there are 23×12 = 276 viable days.

      In each 100 year period there are 60×276 = 16560 viable days.

      In each 400 year period there are 4×16560 = 66240 viable days.

      And in a 400 year period there is a total of 400×365 + 100 − 3 = 400×365.2425 = 146097 days.

      Converting to seconds we get a chance of 1 in (146097 × 86400 / 66240) = 190561.304….

      Truncating this result to an integer and reading as a date gives: Friday, 19th May 1961.

      Like

  • Unknown's avatar

    Jim Randell 9:10 am on 27 August 2020 Permalink | Reply
    Tags:   

    Teaser 2681: Inconsequential 

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

    An “arithmetic” sequence is one in which each number is a fixed amount more than the previous one. So, for example, 10, 29, 48, … is an arithmetic sequence. In this case its ninth number is 162, which happens to be divisible by 9. I have in mind another arithmetic sequence whose ninth number is divisible by 9. This time it starts with two three-figure numbers, but in this case I have consistently replaced digits by letters, with different letters used for different digits.

    The arithmetic sequence then begins ONE, TWO, …, and its ninth number is NINE.

    To win, find the number to WIN.

    [teaser2681]

     
    • Jim Randell's avatar

      Jim Randell 9:10 am on 27 August 2020 Permalink | Reply

      The common difference is (TWOONE), and there are 8 instances of the common difference between ONE and NINE.

      So, we can solve this puzzle straightforwardly using the [[ SubstitutedExpression() ]] solver from the enigma.py library.

      The following run file executes in 110ms.

      Run: [ @replit ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      "div(NINE - ONE, TWO - ONE) = 8"
      "div(NINE, 9) > 0"
      
      --answer="WIN"
      

      Solution: WIN = 523.

      The progression is: ONE = 631, TWO = 956, …, NINE = 3231.

      The common difference is: TWOONE = 325.

      Like

    • GeoffR's avatar

      GeoffR 10:39 am on 27 August 2020 Permalink | Reply

      % A Solution in MiniZinc 
      include "globals.mzn";
      
      var 1..9: O; var 1..9: N; var 1..9: E;
      var 1..9: T; var 1..9: W; var 1..9: I;
      
      constraint all_different ([O, N, E, T ,W, I]);
      
      var 100..999: ONE = 100*O + 10*N + E;
      var 100..999: TWO = 100*T + 10*W + O;
      var 100..999: WIN = 100*W + 10*I + N;
      var 1000..9999: NINE = 1000*N + 100*I + 10*N + E;
      
      % Define 9th term of series
      constraint NINE = ONE + 8 * (TWO - ONE)
       /\ NINE mod 9 == 0;
      
      solve satisfy;
      
      output [ "Series is " ++ show(ONE) ++ "," ++ show(TWO) ++ 
      "..." ++ show(NINE) ++ "\n" ++ "WIN = " ++ show(WIN)]
      
      % Series is 631,956...3231
      % WIN = 523
      % ----------
      % ==========
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 11:02 am on 27 August 2020 Permalink | Reply

      
      from itertools import permutations
      
      for Q in permutations(range(1,10), 6):
        O, N, E, T, W, I = Q
        ONE, TWO = 100*O + 10*N + E, 100*T + 10*W + O
        WIN = 100*W + 10*I + N
        NINE = 1010*N + 100*I + E
        if NINE == ONE + (TWO - ONE) * 8 and NINE % 9 == 0:
          print(f"ONE = {ONE}, TWO = {TWO}, NINE = {NINE}, WIN = {WIN}")
      
      # ONE = 631, TWO = 956, NINE = 3231, WIN = 523
      
      

      Like

      • Frits's avatar

        Frits 1:26 pm on 27 August 2020 Permalink | Reply

        @GeoffR, shouldn’t you allow for E, I and W to be zero as well?

        Like

    • Frits's avatar

      Frits 1:20 pm on 27 August 2020 Permalink | Reply

       
      # elements in range <r> unequal to values of D[i]
      def domain(i, r):
        vals = set()
        for s in D[i]:
          # use globals() iso eval()
          vals.add(globals()[s])
        return [x for x in r if x not in vals]
      
      # set up dictionary of for-loop iterator names
      def init(li):
        for i in range(1,len(li)):
          D[li[i]] = li[0:i]
          
      # concatenate list of integers 
      to_num = lambda *args: int("".join(map(str, args)))    
      
      
      D = {}
      init(['O','N','E','T','W'])
      
      
      for O in range(1,10):
        for N in domain('N', range(1,10)):
          for E in domain('E', range(0,10)):
            ONE = to_num(O,N,E)
            for T in domain('T', range(1,10)):
              if T > O:  
                for W in domain('W', range(0,10)):
                  TWO = to_num(T,W,O)
                  unit = TWO - ONE
                  units8 = 8*unit + ONE
                  if units8 % 9 != 0 or units8 < 1000: continue
                  
                  s = str(units8)
                  if s[3] != str(E): continue 
                  if s[0] != str(N) or s[2] != str(N): continue 
                  # check letter I
                  if int(s[1]) in {O,N,E,T,W}: continue
            
                  WIN = str(W)+s[1]+str(N)
                  print("WIN ONE TWO NINE")
                  print(WIN, ONE, TWO, units8)
      
      # WIN ONE TWO NINE
      # 523 631 956 3231
      

      Like

    • GeoffR's avatar

      GeoffR 1:47 pm on 27 August 2020 Permalink | Reply

      @Frits: In theory maybe, but it would not have made any difference in practice.
      As the title of the teaser says – “Inconsequential” ?

      Like

  • Unknown's avatar

    Jim Randell 9:06 am on 25 August 2020 Permalink | Reply
    Tags: ,   

    Teaser 2677: One for each day 

    From The Sunday Times, 12th January 2014 [link] [link]

    George and Martha have been looking into tests for divisibility,  including one for the elusive number seven. George wrote down a thousand-figure number by simply writing one particular non-zero digit a thousand times. Then he replaced the first and last digits by another non-zero digit to give him a thousand-figure number using just two different digits. Martha commented that the resulting number was divisible by seven. George added that it was actually divisible by exactly seven of 2, 3, 4, 5, 6, 7, 8, 9 and 11.

    What were the first two digits of this number?

    Note: This puzzle is marked as flawed, as under the intended interpretation there is no solution.

    [teaser2677]

     
    • Jim Randell's avatar

      Jim Randell 9:06 am on 25 August 2020 Permalink | Reply

      Although the puzzle is flawed, I didn’t have a problem solving it, as I interpreted the “actually” in George’s statement to mean that George was correcting Martha’s statement, so it didn’t really matter if Martha’s statement was true or false. All we needed to do was find a number divisible by exactly seven of the given divisors. Which is what my code does, and it finds a unique solution, so I was happy enough with the puzzle.

      Python’s support for large integers means this one can be solved in a straightforward way. The following program runs in 55ms.

      Run: [ @replit ]

      from enigma import (subsets, printf)
      
      digits = "123456789"
      divisors = (2, 3, 4, 5, 6, 7, 8, 9, 11)
      
      for (a, b) in subsets(digits, size=2, select="P"):
        n = int(a + b * 998 + a)
        ds = list(d for d in divisors if n % d == 0)
        if len(ds) == 7:
          printf("a={a} b={b} [ds={ds}]")
      

      Solution: The first two digits of George’s number are 6 and 3.

      So the 1,000-digit number is 6333…3336. Which is divisible by 2, 3, 4, 6, 8, 9, 11.


      However, apparently the setter intended Martha’s statement to be true, and the number to be divisible by 7 and exactly six of the other divisors. Unfortunately, there is no such number. (As we can see by adding [[ and 7 in ds ]] to the condition on line 9).

      The intended solution was 2777…77772, and the setter mistakenly determined that this was divisible by 8. In fact the number is divisible by only six of the divisors: 2, 3, 4, 6, 7, 11.

      Like

    • Hugh+Casement's avatar

      Hugh+Casement 11:33 am on 26 December 2021 Permalink | Reply

      The puzzle could have stated that the number is an integer multiple of seven of the integers 2 to 12 inclusive.

      However, we don’t need the ability to handle huge numbers (or even a computer).

      We can regard the number as jk followed by 498 blocks of kk followed by kj.
      The test for divisibility by 11 involves taking the alternating positive and negative sum of the digits. jk and kj cancel out, and each kk cancels, so the alternating digit sum is 0: the number is an integer multiple of 11 whatever the values of j and k.

      It cannot be a multiple of 10 since we were told the digits are non-zero.

      A test for divisibility by 7 is to take the alternating positive and negative sum of groups of three digits from the right. If and only if the result is a multiple of 7 is the overall number also a multiple of 7.

      In this case we have a number with j on the left, then 332 blocks of kkk, and kkj on the right.
      The even number of blocks cancel out, leaving kkj − j = kk0 = k × 110.
      That is an integer multiple of 7 only if k = 7.

      If the number is not a multiple of 2 then it cannot be a multiple of 4, 6, 8, or 12.
      If it is not a multiple of 3 then it cannot be a multiple of 6, 9, or 12.
      In order to have seven factors in the list it must be a multiple of both 2 and 3.

      If j = 2 the number is an integer multiple of 2 and 4 (but, as Jim says, not 8).

      So now we know j and k.
      We can regard the number as 27 followed by 332 blocks of 777 followed by 72.
      777 is a multiple of 3, so the digit sum is a multiple of 3 and the number is an integer multiple of 3 (but not of 9). Being a multiple of 3 and 4 it is also a multiple of 12.

      The number is not a multiple of 5, 8, or 9 (nor 10).

      Liked by 1 person

  • Unknown's avatar

    Jim Randell 11:18 am on 23 August 2020 Permalink | Reply
    Tags: by: D. A. Hammersley   

    Brain-Teaser 6: [Family products] 

    From The Sunday Times, 2nd April 1961 [link]

    Living next door to each other are two families each having three children. The product of the three ages in one family is equal to the product of those in the other family. Next month three of the six children, including the eldest, have birthdays, and at the end of the month the two products will again be equal.

    None of the children has the same age either now or at the end of next month, and no child is older than twelve.

    What are the ages in each family now?

    This puzzle was originally published with no title.

    [teaser6]

     
    • Jim Randell's avatar

      Jim Randell 11:19 am on 23 August 2020 Permalink | Reply

      This Python program runs in 55ms.

      from enigma import (group, subsets, irange, multiply, printf)
      
      # collect triples of ages from 1 to 12, by their product
      ts = group(subsets(irange(1, 12), size=3), by=multiply)
      
      # we are only interested in products with multiple values
      ts = dict((k, v) for (k, v) in ts.items() if len(v) > 1)
      
      # choose the first product
      for (k1, vs1) in ts.items():
        # choose the initial sets
        for (a1, b1) in subsets(vs1, size=2):
          # the ages are all different
          s1 = a1 + b1
          if len(set(s1)) < 6: continue
      
          # choose the second product
          for (k2, vs2) in ts.items():
            if not (k1 < k2): continue
            # choose the final sets
            for (a2, b2) in subsets(vs2, size=2, select="P"):
              # ages are still all different
              s2 = a2 + b2
              if len(set(s2)) < 6: continue
              # and three of them differ by 1
              ds = tuple(y - x for (x, y) in zip(s1, s2))
              if not (ds.count(1) == 3 and ds.count(0) == 3): continue
              # including the max
              if not (max(s1) + 1 == max(s2)): continue
              # output solution
              printf("{k1} -> {a1} {b1}; {k2} -> {a2} {b2}")
      

      Solution: The current ages are: (1, 8, 9) and (3, 4, 6).

      And 1×8×9 = 3×4×6 = 72.

      At the end of next month the ages will be: (1, 9, 10) and (3, 5, 6), both giving products of 90.

      Like

      • Jim Randell's avatar

        Jim Randell 12:13 pm on 24 August 2020 Permalink | Reply

        A slightly shorter version:

        from enigma import (irange, subsets, multiply, is_square, partitions, printf)
        
        # find 6 ages with a product that is square
        for s1 in subsets(irange(1, 12), size=6):
          p = is_square(multiply(s1))
          if p is None: continue
          # split the ages into 2 sets
          for (a1, b1) in partitions(s1, 3):
            if multiply(a1) != p: continue
            # now increment 3 of the values
            for js in subsets(irange(0, 5), size=3):
              s2 = list(a1 + b1)
              for j in js: s2[j] += 1
              # check the ages are still all different
              if len(set(s2)) < 6: continue
              # check the eldest is incremented
              if not (max(s2) == max(s1) + 1): continue
              # split the ages
              (a2, b2) = (s2[:3], s2[3:])
              # check the products are still the same
              if not (multiply(a2) == multiply(b2)): continue
              # output solution
              printf("{p} -> {a1}, {b1}; {q} -> {a2}, {b2}", q=multiply(a2))
        

        Like

    • GeoffR's avatar

      GeoffR 5:27 pm on 23 August 2020 Permalink | Reply

      A part programmatic / part manual solution:

      
      from itertools import permutations
      
      from collections import defaultdict
      D = defaultdict(list)
      
      # Family 1 ages are a,b,c and Family 2 ages are d,e,f
      # Allow for no child older than 12 after a 1 year increase
      for Q in permutations(range(1,12), 6):
        a, b, c, d, e, f = Q
        # make c the eldest child (is either c or f)
        if a < b and b < c and c > f and d < e and e < f:
          if a * b * c == d * e * f:
            # add result to dictionary, with product as key
            D[a * b * c] += [(a, b, c, d, e, f)]
      
      for k,v in D.items():
          print(k,v)
      
      # Manual Assessment
      # A print-out of Dictionary D gives
      # the product first and the group of 6 family ages
      # 36 [(1, 4, 9, 2, 3, 6)]
      # 60 [(1, 6, 10, 3, 4, 5)]
      # 72 [(1, 8, 9, 3, 4, 6)]
      # 90 [(1, 9, 10, 3, 5, 6)]
      # 120 [(2, 6, 10, 3, 5, 8)] 
      # 180 [(3, 6, 10, 4, 5, 9)]
      
      # The only group of three ages which can all be
      # increased by 1 are (8,9,4) in product 72
      # to give (9,10,5) in product 90 with
      # eldest child's age increased by i year
      #
      # This gives (1,9,10) and (3,5,6) as the ages
      # of the two families now
      #
      # Note;
      # It appears that 120/180 products could have three 1 year
      # age increments ie (2,3,8) to become (3,4,9),
      # but there is no increase in the eldest
      # child's age, which is a condition, so invalid
      
      

      Like

    • Frits's avatar

      Frits 7:29 pm on 23 August 2020 Permalink | Reply

       
      from enigma import  SubstitutedExpression, irange, seq_all_different
      
      p = SubstitutedExpression([
        "A < D",                           # avoid reporting same solution twice
        "A < B",
        "B < C",
        "D < E",
        "E < F",
        "A*B*C == D*E*F",
        "b + c + d + e + f + g = 3",       # addition bits
        "max(b,c,d,e,f,g) == 1",           # bits are 0 or 1
        "max(C, F) + 1 == max(C+d, F+g)",  # eldest birthday
        "v([A+b,B+c,C+d,D+e,E+f,F+g])",    # all different
        "(A+b)*(B+c)*(C+d) == (D+e)*(E+f)*(F+g)",
        ], 
        verbose=0, 
        digits=irange(0, 11),
        symbols="ABCDEFbcdefg",
        code="v = lambda x: seq_all_different(x)",
        distinct="ABCDEF",     
        answer="(A,B,C,D,E,F,A+b,B+c,C+d,D+e,E+f,F+g,A*B*C,D*E*F, \
                (A+b)*(B+c)*(C+d),(D+e)*(E+f)*(F+g))"
      )
          
        
      # Print answers
      for (_, ans) in p.solve(verbose=0):
        print(f"Current:\nAges \
      ({ans[0]},{ans[1]},{ans[2]}) product {ans[12]}, \
      ({ans[3]},{ans[4]},{ans[5]}) product {ans[13]} \
      \nEnd of the month: \nAges \
      ({ans[6]},{ans[7]},{ans[8]}) product {ans[14]}, \
      ({ans[9]},{ans[10]},{ans[11]}) product {ans[15]}")
        
        
      # Current:
      # Ages (1,8,9) product 72, (3,4,6) product 72
      # End of the month:
      # Ages (1,9,10) product 90, (3,5,6) product 90
      

      Like

    • GeoffR's avatar

      GeoffR 10:05 am on 24 August 2020 Permalink | Reply

      Frits’ solution has given me an idea for a solution in MiniZinc.

      i.e limiting the six addition digits to (0..1) range and the sum of the six additions to three

      
      % A Solution in MiniZinc 
      include "globals.mzn";
      
      % First Family - three children's ages 
      var 1..11: A; var 1..11: B; var 1..11: C; 
      
      % Second Family - three children's ages 
      var 1..11: D; var 1..11: E; var 1..11: F; 
      
      % Six children's ages are all different
      constraint all_different([A, B, C, D, E, F]);
      
      % Order ages 
      constraint A < B /\ B < C /\  C > F
      /\ D < E /\ E < F;
      
      % Possible digit increases for A, B, C, D, E, F
      var 0..1:a; var 0..1:b; var 0..1:c; 
      var 0..1:d; var 0..1:e; var 0..1:f; 
      
      % Digit increases are for three children only
      % including the eldest
      constraint c == 1 /\  a + b + c + d + e + f == 3;
      
      % Current Product of Family 1's children = 
      % Product of Family 2 's children
      constraint A * B * C == D * E * F;
      
      % Family age products after one month
      constraint (A+a) * (B+b) * (C+c)
      == (D+d) * (E+e) * (F+f);
      
      solve satisfy;
      
      output [ "Family ages now:    " ++ show([A,B,C]) ++ 
      " and "++ show([D,E,F]) ++ "\nFuture family ages: " ++ 
      show([A+a, B+b, C+c]) ++ " and " ++
      show([D+d, E+e, F+f]) ];
      
      % Family ages now:    [1, 8, 9] and [3, 4, 6]
      % Future family ages: [1, 9, 10] and [3, 5, 6]
      % time elapsed: 0.05 s
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 4:38 pm on 21 August 2020 Permalink | Reply
    Tags:   

    Teaser 3022: Sporty set 

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

    There are 100 members of my sports club where we can play tennis, badminton, squash and table tennis (with table tennis being the least popular). Last week I reported to the secretary the numbers who participate in each of the four sports. The digits used overall in the four numbers were different and not zero.

    The secretary wondered how many of the members were keen enough to play all four sports, but of course he was unable to work out that number from the four numbers I had given him. However, he used the four numbers to work out the minimum and the maximum possible numbers playing all four sports. His two answers were two-figure numbers, one being a multiple of the other.

    How many played table tennis?

    [teaser3022]

     
    • Jim Randell's avatar

      Jim Randell 11:26 pm on 21 August 2020 Permalink | Reply

      If the numbers that participate in each sport are A, B, C, D (in increasing order), and the sum of these values is T.

      Then the maximum size of their intersection is easily determined – it is the size of the smallest set, in this case A; (or as John points out below, if all 100 members participate in at least one of the available sports, the maximum is: floor((T − 100) / 3), if this is smaller than A).

      So our maximum is: min(A, floor((T − 100) / 3).

      The minimum size of the intersection is trickier to determine. But if we have a family of subsets S[i] of some universal set U, then the minimum size of the intersection is given by:

      X = \left|\bigcap_{i=1}^{n}S_i\right| \medskip \newline N = |U| \medskip \newline T = \sum_{i=1}^{n}|S_i| \medskip \newline \newline X \geq 0 \medskip \newline X \geq T - (n - 1)N

      So in this case the minimum size is: max(0, T − 300).

      The following Python program runs in 138ms.

      Run: [ @repl.it ]

      from enigma import subsets, irange, diff, nconcat, divf, div, multiset, printf
      
      # generate k increasing 2-digit numbers from the supplied (increasing) digits
      def generate(k, ds, ns=[]):
        # choose the tens digits
        for d10s in subsets(ds, size=k, select="C"):
          # choose the units digits
          for d1s in subsets(diff(ds, d10s), size=k, select="P"):
            # construct the numbers
            yield tuple(nconcat(z) for z in zip(d10s, d1s))
      
      # collect solutions (size of the smallest set)
      ss = multiset()
      
      # choose increasing numbers of participants for each of the 4 sports
      for ns in generate(4, irange(1, 9)):
        T = sum(ns)
        # maximum size of intersection
        M = min(ns[0], divf(T - 100, 3))
        # minimum size of intersection
        m = max(0, T - 300)
      
        # min is a 2-digit numbers
        if m < 10: continue
        # M is a multiple of m
        k = div(M, m)
        if k is None or not(k > 1): continue
      
        printf("[ns={ns}; min={m} max={M} k={k}]")
        ss.add(ns[0])
      
      # output solutions
      for (k, v) in ss.most_common():
        printf("min(ns) = {k} [{v} solutions]")
      

      Solution: 65 played table tennis.

      The other three sports are represented by the numbers (70, 80, 90) + (1, 3, 4) assembled in some order.

      The minimum size of the intersection is 13, and the maximum size is 65 (= 5×13).

      Like

      • Jim Randell's avatar

        Jim Randell 8:54 am on 23 August 2020 Permalink | Reply

        We don’t need to work out the actual sizes of the 3 larger sets, to calculate the sum of their sizes. We only need to know what the tens and units digits are.

        So here is a faster version of my program. You can also pass a command line argument to specify if members playing zero sports are allowed (1) or not (0). It runs in 54ms.

        Run: [ @repl.it ]

        from enigma import irange, subsets, diff, divf, div, arg, printf
        
        # set z to:
        # 0 - if members playing 0 sports are not allowed
        # 1 - if members playing 0 sports are allowed
        z = arg(0, 0, int)
        
        # allowable digits
        digits = irange(1, 9)
        
        # choose the 10s digits for the sets
        for d10s in subsets(digits, size=4):
          t10 = sum(d10s) * 10
          if t10 < 280: continue
        
          # choose the units digits
          for d1s in subsets(diff(digits, d10s), size=4):
            # size of the union of the sets
            T = t10 + sum(d1s)
            # minimum size of intersection
            m = T - 300
            # is a 2 digit number
            if not(9 < m < 100): continue
        
            # choose a units digit for the smallest set
            for d1 in d1s:
              # maximum size of intersection
              A = 10 * d10s[0] + d1
              M = (A if z else min(A, divf(T - 100, 3)))
              # is a multiple of m
              k = div(M, m)
              if k is None or not(k > 1): continue
        
              # output solution
              printf("min(ns) = {A} [min={m} max={M} k={k}; ns = {d10s} x {d1s}] [z={z}]")
        

        Like

        • Frits's avatar

          Frits 10:11 pm on 24 August 2020 Permalink | Reply

          Very nice solution.

          “if t10 Don’t we already know d10s must always be equal to (6, 7, 8, 9)?

          if it is (5, 7, 8, 9) then t10 = 290 and sum(d1s) <= 15 (fi 2,3,4,6)
          So T <= 305 which is not acceptable.

          Like

          • Frits's avatar

            Frits 12:12 pm on 25 August 2020 Permalink | Reply

             
            from enigma import irange, diff, div, printf
            
            # minimum A + B + C + D - 300 > 9 
            # so 10s digits must be (6,7,8,9)
            
            # (A + B + C + D - 100) / 3 >= 70
            # which is bigger than A, so maximum M equals A
            
            # allowable digits for units
            digits = irange(1, 5)
            
            # d is the units digit which is not used in A, B, C and D
            for d in digits:
              # sum of A, B, C and D
              T = 315 - d
              # minimum size of intersection
              m = T - 300
              # choose a units digit for the smallest set
              d1s = diff(digits, (d,))
              for d1 in d1s:
                # maximum size of intersection (is A)
                M = 60 + d1    
                # is a multiple of m
                k = div(M, m)
                if k is None or not(k > 1): continue
            
                # output solution
                printf("min(ns) = {M} [min={m} max={M} k={k}; ns = (6,7,8,9) x {d1s}]")
            

            Like

          • Jim Randell's avatar

            Jim Randell 12:51 pm on 25 August 2020 Permalink | Reply

            @Frits: It looks like your comment didn’t make it through WordPress unscathed. Usually this happens when you use < and > in a comment. WordPress sees everything in between as an HTML tag, and then doesn’t recognise it, so it throws it away. You can use &lt; and &gt; to make < and > appear correctly. (If you want to send me a corrected version, I’ll fix it up).

            It looks like you are asking about line 9 of my code. It’s really just a quick bit of early rejection so the code doesn’t go on to evaluate cases that can’t possibly work, and the program works just as well (and not much slower) without it.

            I reasoned that if (T − 300) is to be a 2-digit number, then (T − 300) > 9, so: T > 309. The maximum possible value of the units digits of the four numbers that make up T is 6+7+8+9 = 30, so the sum of the tens parts of the numbers must be more than 309 − 30 = 279, which is what that test does, and it cuts the number of cases considered drastically.

            With a bit more work we can see that there is only one possible set of values for the tens digits, and we are half way towards a manual solution.

            When programming a solution there is always an amount of choice on how much analysis is necessary to get a program that runs in a reasonable time. But normally if it doesn’t make much difference to the run time I let the computer do the checks.

            Like

            • Frits's avatar

              Frits 6:26 pm on 25 August 2020 Permalink | Reply

              @Jim, Thanks.

              I like to make use of –> which WordPress doesn’t like.

              Yes, I was trying to understand line 9 of your code and wondered why you had chosen this (for me suboptimal) limit.

              I still need to verify for myself that the minimum and maximum formula are indeed correct.

              Is there not an easy way of allowing us to edit our own posts (as it is linked to an email address)?
              Many times directly after posting I see errors/improvements.

              Like

              • Jim Randell's avatar

                Jim Randell 10:37 am on 26 August 2020 Permalink | Reply

                @Frits: Sadly WordPress doesn’t treat comments at the same level as posts. Even for logged-in users. So the only way they can be edited is by a site administrator (who can make changes to any aspect of the site).

                It is the main reason I sometimes think about moving away from WordPress, but I have not yet found a suitable alternative.

                At one point I had hoped that WordPress would implement some level of editing (or even just the basic courtesy of previewing a comment before it is posted), but it has become clear that they really have no interest in this.

                On the upside the system does make it relatively easy to manage a site, and has remained (mostly) problem free for the last 8 years.

                In the meantime, the best I can offer as site administrator is to make amendments to comments if you send me any revisions.

                Like

    • Frits's avatar

      Frits 2:38 pm on 22 August 2020 Permalink | Reply

      Making use of Jim’s analysis (and Brian’s at [{broken link removed}]) .

       
      from enigma import  SubstitutedExpression, irange 
      
      # AB = table tennis
      # minimum played is AB + CD + EF + GH - 300
      # maximum played is AB
      # maximum = X * minimum
      
      p = SubstitutedExpression([
          "GH > EF", 
          "EF > CD", 
          "CD > AB", 
          "AB % (AB + CD + EF + GH - 300) == 0", 
          "(AB + CD + EF + GH - 300) > 10",
         ], 
          verbose=0, 
          answer="AB,CD,EF,GH",
          digits=irange(1, 9))
        
      # Print answers
      for (_, ans) in p.solve(verbose=0):
        print(f"{ans} minimum={sum(ans)-300} maximum={ans[0]}")
      
      # Output:
      #
      # (65, 74, 83, 91) minimum=13 maximum=65
      # (65, 73, 84, 91) minimum=13 maximum=65
      # (65, 74, 81, 93) minimum=13 maximum=65
      # (65, 71, 84, 93) minimum=13 maximum=65
      # (65, 73, 81, 94) minimum=13 maximum=65
      # (65, 71, 83, 94) minimum=13 maximum=65
      

      Like

    • GeoffR's avatar

      GeoffR 3:25 pm on 22 August 2020 Permalink | Reply

      @Frits: You probably don’t know, but there is a sort of convention in Brian/Jim’s groups for programmed solutions with the answer not to be published early. This helps to minimise people entering the Sunday Times Prize Draw competition if they are just looking for the answer to take part in the draw, without attempting the puzzle.

      Like

      • Frits's avatar

        Frits 4:46 pm on 22 August 2020 Permalink | Reply

        @GeoffR.
        Thanks. I keep that in mind.

        Like

    • John Crabtree's avatar

      John Crabtree 6:22 pm on 23 August 2020 Permalink | Reply

      The maximum is not A, but min(A, (A + B + C + D – 100) / 3)

      Like

      • Jim Randell's avatar

        Jim Randell 8:45 pm on 23 August 2020 Permalink | Reply

        @John: You are, of course, quite right. Thanks.

        I originally came up with the maximum without making the assumption that every member participated in at least one sport. But then I used that assumption to determine the minimum, and I neglected to revisit the maximum.

        I have provided code above that allows you to select whether participation in zero sports is allowed or not.

        Like

        • Jim Randell's avatar

          Jim Randell 10:08 pm on 26 August 2020 Permalink | Reply

          Actually, it looks like it doesn’t matter if we allow members who participate in 0 sports or not. The possible numbers presented to the secretary are the same in both cases, and so is the answer to the puzzle.


          The minimum intersection size is given by: max(0, T − 300).

          And we require this to be a 2 digit number.

          So we must have:

          T − 300 ≥ 10
          T ≥ 310

          And T is the sum of four 2-digit numbers, all with different non-zero digits.

          The largest we can make the units digits of our four numbers is 6 + 7 + 8 + 9 = 30.

          So the tens digits have to sum to at least 28, so the numbers are of the form:

          4a + 7b + 8c + 9d = 280 + (a + b + c + d)
          5a + 6b + 8c + 9d = 280 + (a + b + c + d)
          5a + 7b + 8c + 9d = 290 + (a + b + c + d)
          6a + 7b + 8c + 9d = 300 + (a + b + c + d)

          In the first case the maximum value of (a + b + c + d) is (6 + 5 + 3 + 2) = 16, giving a maximum possible total of 296, so this is not feasible.

          In the second case the maximum value of(a + b + c + d) is (7 + 4 + 3 + 2) = 16, giving a maximum possible total of 296, so this is not feasible.

          In the third case the maximum value of(a + b + c + d) is (6 + 4 + 3 + 2) = 15, giving a maximum possible total of 305, so this is not feasible.

          In the fourth case the maximum value of (a + b + c + d) is (5 + 4 + 3 + 2) = 14, giving a maximum possible total of 314, so this is feasible.

          So the four numbers must be of the form: 6a, 7b, 8c, 9d

          The size of the smallest set is thus: 61, 62, 63, 64, or 65.

          And the smallest possible value of floor((T − 100) / 3) = floor((310 − 100) / 3) = 70.

          So, in this case, the maximum intersection size is always the same as the size of the smallest set, whether we allow participants of zero sports or not.

          Like

  • Unknown's avatar

    Jim Randell 12:05 pm on 20 August 2020 Permalink | Reply
    Tags: ,   

    Teaser 2551: Take nothing for granted 

    From The Sunday Times, 14th August 2011 [link] [link]

    In arithmetic, the zero has some delightful properties. For example:

    ANY + NONE = SAME
    X . ZERO = NONE

    In that sum and product, digits have been replaced with letters, different letters being used for different digits. But nothing should be taken for granted: here the equal signs are only approximations as, in each case, the two sides may be equal or differ by one.

    What is your NAME?

    [teaser2551]

     
    • Jim Randell's avatar

      Jim Randell 12:06 pm on 20 August 2020 Permalink | Reply

      We can use the [[ SubstitutedExpression() ]] solver from the enigma.py library to tackle this puzzle. Unfortunately there are two possible solutions.

      The following run file executes in 159ms.

      Run: [ @repl.it ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      "abs(ANY + NONE - SAME) < 2"
      "abs(X * ZERO - NONE) < 2"
      
      --answer="NAME"
      #--answer="SYNONYM" # for a unique answer
      

      Solution: The two possible values for NAME are: 7146 and 7543.

      The possible sums are:

      170 + 7976 = 8146; 6 × 1329 ≈ 7973
      570 + 7973 = 8543; 3 × 2659 ≈ 7976

      In both cases the addition is exact, but the multiplication sums are off by 1.

      If instead, the puzzle had asked the value of SYNONYM the answer would have been unique (= 8079704).

      Like

    • GeoffR's avatar

      GeoffR 3:07 pm on 20 August 2020 Permalink | Reply

      % A Solution in MiniZinc 
      include "globals.mzn";
      
      var 1..9:A; var 1..9:N; var 0..9:Y; 
      var 0..9:O; var 0..9:E; var 1..9:X;
      var 1..9:Z; var 0..9:R; var 1..9:S;  
      var 0..9:M;
      
      constraint all_different([A,N,Y,O,E,X,Z,R,S,M]);
      
      var 100..999: ANY = 100*A + 10*N + Y;
      var 1000..9999: NONE = 1010*N + 100*O + E;
      var 1000..9999: SAME = 1000*S + 100*A + 10*M + E;
      var 1000..9999: ZERO = 1000*Z + 100*E + 10*R + O;
      var 1000..9999: NAME = 1000*N + 100*A + 10*M + E;
      
      constraint ANY + NONE == SAME \/ANY + NONE == SAME + 1
      \/ ANY + NONE == SAME - 1;
      
      constraint X * ZERO == NONE \/  X * ZERO == NONE + 1
      \/  X * ZERO == NONE - 1;
      
      solve satisfy;
      
      output ["NAME = " ++ show(NAME)
      ++ "\n Sum is " ++ show(ANY) ++ " + " ++ show(NONE) 
      ++ " = " ++ show(SAME)
      ++ "\n Product is " ++ show(X) ++ " * " ++ show(ZERO)
      ++ " = " ++ show(NONE)
      ++ "\n SYNONYM = " ++ show(S),show(Y),show(N),show(O),
      show(N),show(Y),show(M)];
      
      % NAME = 7543
      %  Sum is 570 + 7973 = 8543
      %  Product is 6 * 1329 = 7973
      %  SYNONYM = 8079704
      % ----------
      % NAME = 7146
      %  Sum is 170 + 7976 = 8146
      %  Product is 3 * 2659 = 7976
      %  SYNONYM = 8079704
      % ----------
      % ==========
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:37 am on 18 August 2020 Permalink | Reply
    Tags:   

    Brainteaser 1847: Home’s best 

    From The Sunday Times, 8th February 1998 [link]

    Four football teams — Albion, Borough, City and District — each play each other twice, once at home and once away. They get three points for a win and one for a draw. Last season, each team did worse in the away match than in the corresponding home match, scoring fewer goals and getting fewer points. The final position was as follows:

    What were the two scores when Borough played District?

    This is the final puzzle to use the title Brainteaser. The following puzzle is Teaser 1848.

    [teaser1847]

     
    • Jim Randell's avatar

      Jim Randell 8:39 am on 18 August 2020 Permalink | Reply

      This Python program uses the [[ Football() ]] helper class from the enigma.py library.

      It runs in 452ms.

      Run: [ @repl.it ]

      from enigma import Football, printf
      
      # scoring system
      football = Football(games="wdl", points=dict(w=3, d=1))
      
      # check home matches have more points and more goals than away matches
      def check(Hs, As):
        for (H, A) in zip(Hs, As):
          if not(H[0] > A[1]): return False
          (h, a) = football.outcomes([H, A], [0, 1])
          if not(football.points(h) > football.points(a)): return False
        return True
      
      # choose C's six games (home, away)
      for (CA, CB, CD, AC, BC, DC) in football.games(repeat=6):
        # table for C
        tC = football.table([CA, CB, CD, AC, BC, DC], [0, 0, 0, 1, 1, 1])
        if not(tC.points == 7): continue
      
        # remaining matches for D
        for (DA, DB, AD, BD) in football.games(repeat=4):
          # table for D
          tD = football.table([DA, DB, DC, AD, BD, CD], [0, 0, 0, 1, 1, 1])
          if not(tD.points == 5): continue
      
          # remaining matches (for A and B)
          for (AB, BA) in football.games(repeat=2):
            # table for A, B
            tA = football.table([AB, AC, AD, BA, CA, DA], [0, 0, 0, 1, 1, 1])
            tB = football.table([BA, BC, BD, AB, CB, DB], [0, 0, 0, 1, 1, 1])
            if not(tA.points == 12 and tB.points == 8): continue
      
            # make possible scores (for C)
            for (sCA, sCB, sCD, sAC, sBC, sDC) in football.scores([CA, CB, CD, AC, BC, DC], [0, 0, 0, 1, 1, 1], 3, 5):
              # check the home matches were better than the away matches
              if not check([sCA, sCB, sCD], [sAC, sBC, sDC]): continue
      
              # remaining scores (for D)
              for (sDA, sDB, sAD, sBD) in football.scores([DA, DB, AD, BD], [0, 0, 1, 1], 9, 13, [sCD, sDC], [1, 0]):
                # check home matches were better than away matches
                if not check([sDA, sDB, sDC], [sAD, sBD, sCD]): continue
      
                # remaining scores (for A)
                for (sAB, sBA) in football.scores([AB, BA], [0, 1], 15, 8, [sAC, sAD, sCA, sDA], [0, 0, 1, 1]):
                  # check for/against B
                  (fB, aB) = football.goals([sBA, sBC, sBD, sAB, sCB, sDB], [0, 0, 0, 1, 1, 1])
                  if not(fB == 12 and aB == 13): continue
                  # check home matches were better than away matches
                  if not check([sAB, sAC, sAD], [sBA, sCA, sDA]): continue
                  if not check([sBA, sBC, sBD], [sAB, sCB, sDB]): continue
      
                  printf("AB={AB}:{sAB} AC={AC}:{sAC} AD={AD}:{sAD} / BA={BA}:{sBA} CA={CA}:{sCA} DA={DA}:{sDA}")
                  printf("BA={BA}:{sBA} BC={BC}:{sBC} BD={BD}:{sBD} / AB={AB}:{sAB} CB={CB}:{sCB} DB={DB}:{sDB}")
                  printf("CA={CA}:{sCA} CB={CB}:{sCB} CD={CD}:{sCD} / AC={AC}:{sAC} BC={BC}:{sBC} DC={DC}:{sDC}")
                  printf("DA={DA}:{sDA} DB={DB}:{sDB} DC={DC}:{sDC} / AD={AD}:{sAD} BD={BD}:{sBD} CD={CD}:{sCD}")
                  printf()
      

      Solution: The scores in the Borough vs. District matches were: B (home) vs. D (away) = 4 – 2; D (home) vs. B (away) = 3 – 3.

      The scores in the matches are: (home vs. away)

      A vs B = 4 – 1; A vs C = 2 – 0; A vs D = 3 – 1
      B vs A = 3 – 3; B vs C = 1 – 0; B vs D = 4 – 2
      C vs A = 1 – 1; C vs B = 1 – 0; C vs D = 1 – 0
      D vs A = 2 – 2; D vs B = 3 – 3; D vs C = 1 – 0

      Like

  • Unknown's avatar

    Jim Randell 9:26 am on 16 August 2020 Permalink | Reply
    Tags: by: PL Havard   

    Teaser 1852: Fits to a T 

    From The Sunday Times, 15th March 1998 [link]

    We wish to just completely cover a 6-by-6 square grid with T-shaped pieces made up of 4 squares as shown. No overlapping is allowed. We wish to do this in as many different ways as possible. Any pattern that can be obtained from another by rotation or reflection is regarded as the same pattern.

     

    (a) In how many ways can the 6-by-6 grid be filled?
    (b) In how many ways can a 10-by-10 grid be filled?

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

    [teaser1852]

     
    • Jim Randell's avatar

      Jim Randell 9:27 am on 16 August 2020 Permalink | Reply

      Each T-shaped piece consists of 4 smaller squares, so to tile an n×n grid we require n² to be divisible by 4 (i.e. n to be divisible by 2).

      For a 6×6 grid we would need 9 T’s, and for a 10×10 grid we would need 25 T’s.

      If we colour the grid chessboard-style with alternating black and white squares, then we see when we place a T it will cover either 3 black + 1 white (type A), or 3 white + 1 black (type B).

      So, for an n×n grid, if we choose k type A shapes (for k = 0..(n²/4)) we will need ((n²/4) − k) type B shapes to give the required number of squares.

      And we need the number of black squares and the number of white squares to both be n²/2:

      3k + ((n²/4) − k) = n²/2
      2k = n²/4
      k = n²/8

      So n² must be divisible by 8, and hence n must be divisible by 4.

      Therefore we cannot tile a 6×6 grid or a 10×10 grid using T4 shapes.

      Solution: There are no ways to fill either the 6×6 grid or the 10×10 grid.

      But we can tile a (4n)×(4n) grid by repeating the following pattern:

      Like

  • Unknown's avatar

    Jim Randell 9:34 pm on 14 August 2020 Permalink | Reply
    Tags:   

    Teaser 3021: Field for thought 

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

    Farmer Giles had a rectangular field bordered by four fences that was 55 hectares in size. He divided the field into three by planting two hedges, from the mid-point of two fences to two corners of the field. He then planted two more hedges, from the mid-point of two fences to two corners of the field. All four hedges were straight, each starting at a different fence and finishing at a different corner.

    What was the area of the largest field when the four hedges had been planted?

    [teaser3021]

     
    • Jim Randell's avatar

      Jim Randell 10:24 pm on 14 August 2020 Permalink | Reply

      The x and y co-ordinates of the intersections of the hedges helpfully divide the edges of the rectangle into equal divisions.

      The area of the central quadrilateral is then easily calculated.

      Solution: The central field has an area of 11 hectares.

      In the first diagram, each of the four coloured triangular areas has an area of xy/5, so the central region also has an area of xy/5.

      Another way to think of it is that there are five quadrilaterals each identical to the central one, but four of them have had bits sliced off them, and then the sliced off bits have been repositioned to make the original rectangle.

      This gives a handy practical construction achievable using folding and straight edge to divide a rectangle into 5 equal strips (or a 5×5 grid of identical smaller rectangles).


      This is a bit like Routh’s Theorem (see: Enigma 1313Enigma 320Enigma 1076, Teaser 2865) but for a rectangle instead of a triangle.

      In general, if the the lines are drawn from a corner to a point a fraction f along a side we can determine the area of the central quadrilateral (as a fraction of the overall parallelogram) as:

      A = (1 − f)² / (1 + f²)

      In the case of f = 1/2 we get:

      A = (1/2)² / (1 + (1/2)²) = (1/4) / (5/4) = 1/5

      Like

  • Unknown's avatar

    Jim Randell 5:43 pm on 13 August 2020 Permalink | Reply
    Tags:   

    Teaser 2705: In the pub 

    From The Sunday Times, 27th July 2014 [link] [link]

    George and Martha are in the pub. He has ordered a glass of lager for her and some ale for himself. He has written three numbers in increasing order, none involving a zero, then consistently replaced digits by letters to give:

    DRANK
    GLASS
    LAGER

    George explains this to Martha and tells her that the third number is in fact the sum of the previous two. From this information she is able to work out the value of each letter.

    What is the number of George’s ALE?

    [teaser2705]

     
    • Jim Randell's avatar

      Jim Randell 5:43 pm on 13 August 2020 Permalink | Reply

      We can solve this straightforwardly using the [[ SubstitutedExpression() ]] solver from the enigma.py library.

      The following run file executes in 270ms.

      Run: [ @replit ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      --answer="ALE"
      
      "DRANK + GLASS = LAGER"
      "G > D"
      

      Solution: ALE = 392.


      For a faster execution time, we can use the [[ SubstitutedSum() ]] solver to solve the alphametic sum, and then check the solutions it finds to ensure D < G.

      from enigma import (SubstitutedSum, irange, printf)
      
      # all digits are non-zero
      p = SubstitutedSum(['DRANK', 'GLASS'], 'LAGER', digits=irange(1, 9))
      
      for s in p.solve(check=(lambda s: s['D'] < s['G']), verbose=1):
        printf("ALE={ALE}", ALE=p.substitute(s, 'ALE'))
      

      By itself the alphametic sum has 3 solutions (when 0 digits are disallowed), but only one of these has D < G.

      Like

      • Frits's avatar

        Frits 11:26 am on 14 August 2020 Permalink | Reply

        @Jim

        You might also add “D + G == L or D + G + 1 == L” to speed things up.

        Like

        • Jim Randell's avatar

          Jim Randell 2:53 pm on 14 August 2020 Permalink | Reply

          Yes. If we break down the sum into individual columns, with carries of 0 or 1 between them we can use the following run file to solve the problem:

          #! python -m enigma -rr
          
          SubstitutedExpression
          
          --answer="{ALE}"
          --distinct="ADEGKLNRS"
          --invalid="0,ADEGKLNRS"
          --invalid="2-9,abcd"
          
          # breaking the sum out into individual columns
          "{D} + {G} + {a} =  {L}"
          "{R} + {L} + {b} = {aA}"
          "{A} + {A} + {c} = {bG}"
          "{N} + {S} + {d} = {cE}"
          "{K} + {S}       = {dR}"
          
          # and G > D
          "{G} > {D}"
          
          --template="(DRANK + GLASS = LAGER) (G > D)"
          

          If we specify the [[ --verbose=32 ]] to measure the internal run-time of the generated code, we find that it runs in 170µs. (The total execution time for the process (which is what I normally report) is 97ms).

          The question is whether the fraction of a second saved in run time is paid off by the amount of additional time (and effort) taken to write the longer run file.

          Like

          • Frits's avatar

            Frits 8:18 pm on 15 August 2020 Permalink | Reply

            @Jim,

            If I use [[ –verbose=32 ]] on your code (with pypy) the internal run-time is 20% of the total execution time (15ms / 75ms). A lot more than you reported.

            I rewrote the generated code.
            It runs approx 3 times faster (elapsed time).

            PS Currently I am more interested in efficient Python code than the easiest/shortest way of programming.

            # breaking the sum out into individual columns
            # "{D} + {G} + {a} =  {L}"
            # "{R} + {L} + {b} = {aA}"
            # "{A} + {A} + {c} = {bG}"
            # "{N} + {S} + {d} = {cE}"
            # "{K} + {S}       = {dR}"
            
            notIn = lambda w: [x for x in range(1,10) if x not in w]
            
            for A in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
              for c in [0, 1]:
                x = 2*A + c; G = x % 10
                if G != A and G in {2,3,4,5,6,7,8}:
                  b = x // 10
                  for D in [1, 2, 3, 4, 5, 6, 7]:
                    if D != A and G > D:
                      for a in [0, 1]:
                        L = D + G + a
                        if L != A and L < 10:
                          for R in notIn({D,G,L,A}):
                            x = R + L + b
                            if x % 10 == A and x // 10 == a:
                              for K in notIn({D,G,L,A,R}):
                                for S in notIn({D,G,L,A,R,K}):
                                  x = K + S
                                  if x % 10 == R:
                                    d = x // 10
                                    for N in notIn({D,G,L,A,R,K,S}):
                                      x = N + S + d
                                      E = x % 10
                                      if E not in {D,G,L,A,R,K,S,N,0}:
                                        if x // 10 == c:
                                          print(" ALE DRANK GLASS LAGER\n",
                                                " ---------------------\n",
                                                A*100+L*10+E,
                                                D*10000+R*1000+A*100+N*10+K,
                                                G*10000+L*1000+A*100+S*11,
                                                L*10000+A*1000+G*100+E*10+R)
             

            Like

          • Jim Randell's avatar

            Jim Randell 10:00 am on 24 March 2021 Permalink | Reply

            I’ve added the [[ SubstitutedExpression.split_sum() ]] method to enigma.py, so you don’t have to split out the columns manually:

            from enigma import SubstitutedExpression
            
            # split the sum into columns
            args = SubstitutedExpression.split_sum("DRANK + GLASS = LAGER")
            
            # none of the symbols in the original sum can be zero
            args.d2i.update((0, x) for x in args.symbols)
            
            # solve the sum, with additional condition: (G > D)
            SubstitutedExpression(
              args.exprs + ["{G} > {D}"],
              distinct=args.symbols,
              d2i=args.d2i,
              answer="{ALE}",
              template=args.template + " ({G} > {D}) ({ALE})",
              solution=args.symbols,
            ).run()
            

            Like

          • Jim Randell's avatar

            Jim Randell 2:55 pm on 22 October 2022 Permalink | Reply

            We can now call the [[ SubstitutedExpression.split_sum ]] solver directly from a run file. Giving us a solution that is both short and fast.

            The following run file executes in 68ms, and the internal runtime of the generated program is just 139µs.

            Run: [ @replit ]

            #! python3 -m enigma -rr
            
            SubstitutedExpression.split_sum
            
            --invalid="0,ADEGKLNRS"  # none of the digits are 0
            --answer="ALE"
            
            "{DRANK} + {GLASS} = {LAGER}"
            
            --extra="{G} > {D}"
            

            Like

    • Frits's avatar

      Frits 11:28 am on 14 August 2020 Permalink | Reply

      from enigma import join
      
      print("ALE DRANK GLASS LAGER")
      print("---------------------")
      for G in range(1,10):
        for D in range(1,10):
          if D in {G}: continue
          if G <= D: continue
          for L in range(1,10):
            if L in {G,D}: continue
            if D + G != L and D + G + 1 != L: continue
            for A in range(1,10):
              if A in {G,D,L}: continue
              if A + A != 10 + G and A + A != G and A + A != 9 + G and A + A + 1 != G: continue
              for S in range(1,10):
                if S in {G,D,L,A}: continue
                GLASS = G*10000+L*1000+A*100+S*10+S
                for K in range(1,10):
                  if K in {G,D,L,A,S}: continue
                  for R in range(1,10):
                    if R in {G,D,L,A,S,K}: continue
                    if K + S != 10 + R and K + S != R: continue
                    for N in range(1,10):
                      if N in {G,D,L,A,S,K,R}: continue
                      DRANK = D*10000+R*1000+A*100+N*10+K
                      for E in range(1,10):
                        if E in {G,D,L,A,S,K,R,N}: continue
                        LAGER = L*10000+A*1000+G*100+E*10+R
                        if DRANK + GLASS != LAGER: continue
                        print(join([A,L,E]),join([D,R,A,N,K]),join([G,L,A,S,S]),join([L,A,G,E,R]))
      

      Like

      • Frits's avatar

        Frits 12:52 pm on 14 August 2020 Permalink | Reply

        Fastest solution so far and more concise:

        # range 1,2,..,9 without elements in set w
        notIn = lambda w: [x for x in range(1,10) if x not in w]
        
        print("ALE DRANK GLASS LAGER")
        print("---------------------")
        for D in range(1,10):
          for G in notIn({D}):
            if G < D: continue
            for L in notIn({D,G}):
              if D + G != L and D + G + 1 != L: continue
              for A in notIn({D,G,L}):
                if A + A != 10 + G and A + A != G and \
                   A + A != 9 + G and A + A + 1 != G: continue
                for S in notIn({D,G,L,A}):
                  GLASS = G*10000+L*1000+A*100+S*10+S
                  for K in notIn({D,G,L,A,S}):
                    for R in notIn({D,G,L,A,S,K}):
                      if K + S != 10 + R and K + S != R: continue
                      for N in notIn({D,G,L,A,S,K,R}):
                        DRANK = D*10000+R*1000+A*100+N*10+K
                        for E in notIn({D,G,L,A,S,K,R,N}):
                          LAGER = L*10000+A*1000+G*100+E*10+R
                          if DRANK + GLASS != LAGER: continue
                          print(A*100+L*10+E, DRANK, GLASS, LAGER)
        
        # ALE DRANK GLASS LAGER
        # ---------------------
        # 392 14358 79366 93724
        

        Like

        • Jim Randell's avatar

          Jim Randell 1:12 pm on 14 August 2020 Permalink | Reply

          @Frits: You should be able to do without the loop for E.

          Once you have determined the other letters there is only one possible value for E determined by the fact: DRANK + GLASS = LAGER.

          This is one of the things the [[ SubstitutedExpression() ]] solver does to improve its performance. (See: Solving Alphametics with Python, Part 2.

          Not a huge win in this case (as there is only a single value left to be E), but generally quite useful technique.

          Like

          • Jim Randell's avatar

            Jim Randell 3:31 pm on 14 August 2020 Permalink | Reply

            … and if we use this technique on the equivalent expression:

            LAGERGLASS = DRANK

            We only need to consider the 6 symbols on the left, which gives a much more respectable execution time of 164ms for the following short run file:

            #! python -m enigma -rr
            
            SubstitutedExpression
            
            --digits="1-9"
            --answer="ALE"
            
            # instead of: "DRANK + GLASS = LAGER"
            "LAGER - GLASS = DRANK"
            
            "G > D"
            

            Like

    • GeoffR's avatar

      GeoffR 3:56 pm on 14 August 2020 Permalink | Reply

      The following link is an alphametic solver :

      http://www.tkcs-collins.com/truman/alphamet/alpha_solve.shtml

      (Enter DRANK and GLASS in the left hand box and LAGER in the right hand box)

      It outputs eight solutions, five of whch contain zeroes, which are invalid for this teaser.

      It also outputs three non-zero solutions, only one of which has GLASS > DRANK, which is the answer:
      i.e. 14358 + 79366 = 93724, from which ALE = 392

      Like

    • GeoffR's avatar

      GeoffR 4:42 pm on 14 August 2020 Permalink | Reply

      import time
      start = time.time()
      
      from itertools import permutations
      
      for Q in permutations('123456789'):
          d,r,a,n,k,l,s,g,e = Q
          drank = int(d+r+a+n+k)
          glass = int(g+l+a+s+s)
          if glass > drank:
              lager = int(l+a+g+e+r)
              if drank + glass == lager:
                  ale = int(a+l+e)
                  print(f"ALE = {ale}")
                  t1 = time.time() - start
                  print(t1, "sec") 
                  print(f"Sum is {drank} + {glass} = {lager}")
      
      # ALE = 392
      # 0.0311 sec
      # Sum is 14358 + 79366 = 93724
      
      
      
      % A Solution in MiniZinc 
      include "globals.mzn";
      
      var 1..9:D; var 1..9:R; var 1..9:A; 
      var 1..9:N; var 1..9:K; var 1..9:G;
      var 1..9:L; var 1..9:S; var 1..9:E; 
      
      constraint all_different([D,R,A,N,K,G,L,S,E]);
      
      var 100..999: ALE = 100*A + 10*L + E;
      
      var 10000..99999: DRANK = 10000*D + 1000*R + 100*A + 10*N + K;
      var 10000..99999: GLASS = 10000*G + 1000*L + 100*A + 11*S;
      var 10000..99999: LAGER = 10000*L + 1000*A + 100*G + 10*E + R;
      
      constraint GLASS > DRANK /\ DRANK + GLASS == LAGER;
      
      solve satisfy;
      
      output ["Sum is " ++ show(DRANK) ++ " + " ++ show(GLASS)
      ++ " = " ++ show(LAGER) ++ "\nALE = " ++ show(ALE) ];
      
      % Sum is 14358 + 79366 = 93724
      % ALE = 392
      % time elapsed: 0.04 s
      %----------
      %==========
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 10:06 pm on 18 August 2020 Permalink | Reply

      % A Solution in MiniZinc - adding columns with carry digits
      include "globals.mzn";
      
      % D R A N K
      % G L A S S +
      %----------
      % L A G E R
      
      var 1..9:d; var 1..9:r; var 1..9:a; 
      var 1..9:n; var 1..9:k; var 1..9:g;
      var 1..9:l; var 1..9:s; var 1..9:e;
      
      var 0..1: carry1; var 0..1: carry2;
      var 0..1: carry3; var 0..1: carry4;
      
      var 100..999: ale = 100*a + 10*l + e;
      
      constraint all_different([d,r,a,n,k,g,l,s,e]);
      
      % adding columns, working from right side
      constraint (k + s) mod 10 == r           % column 1
      /\ (k + s) div 10 == carry1;
      
      constraint (n + s + carry1) mod 10 == e  % column 2
      /\ (n + s + carry1) div 10 == carry2;
      
      constraint (a + a + carry2) mod 10 == g  % column 3
      /\ (a + a + carry2) div 10 == carry3;
      
      constraint (r + l + carry3) mod 10 == a  % colmun 4
      /\ (r + l + carry3) div 10 == carry4;
      
      constraint (d + g + carry4) == l;        % column 5
      
      % number glass > drank
      constraint (10000*g + 1000*l + 100*a + 11*s)
      > (10000*d + 1000*r + 100*a + 10*n + k);
      
      solve satisfy;
      
      output ["ALE = " ++ show(ale) ];
      
      % ALE = 392
      % time elapsed: 0.04 s
      % ----------
      % ==========
      
      
      

      Like

      • John Crabtree's avatar

        John Crabtree 7:32 pm on 20 August 2020 Permalink | Reply

        I solved this by hand. Looking at the first three columns, if NK + SS < 100, by digital roots, D + R + A = 0 mod 9. If NK + SS > 100, D + R + A = 8 mod 9.
        For each condition and each A, G is fixed. Then for each possible D (<G and G + D < 10), R is fixed, and then so is L. I only found four sets of (A, G, D, R and L), ie (3, 6, 1, 5 8), (3, 6, 2, 4, 9), (1, 3, 2, 5, 6) and (3, 7, 1, 4, 9) for which it was necessary to evaluate the other letters.
        Could this be the basis for a programmed solution?

        Like

  • Unknown's avatar

    Jim Randell 11:43 am on 11 August 2020 Permalink | Reply
    Tags:   

    Brainteaser 1839: It’s a knockout 

    From The Sunday Times, 14th December 1997 [link]

    Sixteen teams, A-P, entered a knockout football competition. In none of the matches did any team score more than five goals, no two matches had the same score, and extra time ensured that there were no draws.

    The table shows the total goals, for and against, for each team:

    My own team, K, was knocked out by the team who were the eventual champions.

    Who played whom in the semi-finals, and what were the scores.

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

    [teaser1839]

     
    • Jim Randell's avatar

      Jim Randell 11:44 am on 11 August 2020 Permalink | Reply

      When we look at the table we can spot teams that might have been knocked out in the first round. The number of goals for and against must be different (no draws) and neither must be more than 5. We can then choose 8 such teams, and their values in the table give the scores in the matches. We then choose 8 of the remaining teams to be their opponents and see if we can construct a new table for the next round of the tournament (which will have half as many matches). And so on until we reach the last 2 teams.

      This Python 3 program runs in 1.2s.

      Run: [ @repl.it ]

      from enigma import subsets, diff, ordered, flatten, join, printf
      
      # the total goals scored/conceded
      gs0 = dict(
        A=(5, 6), B=(14, 0), C=(15, 8), D=(2, 4), E=(1, 3), F=(0, 5), G=(4, 6), H=(1, 4),
        I=(1, 2), J=(12, 12), K=(6, 5), L=(0, 1), M=(4, 5), N=(1, 5), O=(2, 3), P=(7, 6),
      )
      
      # do the list of <ls>, <ws> make a set of matches using goals scored/conceded in <gs>?
      # return list of (<winner> + <loser>, (<scored>, <conceded>))
      # and a new version of <gs> for the next round
      def matches(gs, ls, ws):
        ms = list()
        d = dict()
        for (w, l) in zip(ws, ls):
          ((fw, aw), (fl, al)) = (gs[w], gs[l])
          (f, a) = (fw - al, aw - fl)
          if f < 0 or a < 0: return (None, None)
          ms.append((w + l, (al, fl)))
          d[w] = (f, a)
        return (ms, d)
      
      # allowable score:
      # no side scores more than 5 goals, and no draws
      score = lambda x, y: not(x > 5 or y > 5 or x == y)
      
      # play out a tournament
      # k = number of matches in this round
      # gs = total number of goals scored in the remaining rounds
      # ms = match outcomes in each round
      # ss = scores used so far
      def tournament(k, gs, ms=list(), ss=set()):
        # how many matches?
        if k == 1:
          # there should be 2 teams
          (x, y) = gs.keys()
          ((fx, ax), (fy, ay)) = (gs[x], gs[y])
          if fx == ay and ax == fy and score(fx, ax):
            yield ms + [[(x + y, (fx, ax)) if fx > ax else (y + x, (fy, ay))]]
        else:
          # teams that can be knocked out in this round
          teams = list(k for (k, (f, a)) in gs.items() if score(f, a))
          # choose the losers
          for ls in subsets(teams, size=k):
            # choose the winners from the remaining teams
            for ws in subsets(diff(gs.keys(), ls), size=k, select="P"):
              (ms_, gs_) = matches(gs, ls, ws)
              if not gs_: continue
              ss_ = set(ordered(*xs) for (ts, xs) in ms_)
              if ss_.intersection(ss): continue
              yield from tournament(k // 2, gs_, ms + [ms_], ss.union(ss_))
              
      
      # there are 8 matches in the first round
      for ms in tournament(8, gs0):
        # check K is knocked out by the champions
        champ = ms[-1][0][0][0]
        if not(champ + 'K' in (ts for (ts, xs) in flatten(ms))): continue
        # output the tournament
        for (i, vs) in enumerate(ms, start=1):
          printf("Round {i}: {vs}", vs=join((f"{t[0]}v{t[1]} = {x}-{y}" for (t, (x, y)) in sorted(vs)), sep="; "))
        printf()
      

      Solution: The semi-finals were: B vs K = 3-0; C vs J = 5-3.

      So the final was: B vs C = 2-0, and B were the champions.

      The strategy given above finds two possible tournaments, but only in one of them is K knocked out by the eventual champions.

      Like

      • Frits's avatar

        Frits 8:13 pm on 18 August 2020 Permalink | Reply

        @Jim

        Did you also solve the puzzle manually?

        It is not so difficult to determine who is playing whom in the semi-finals.
        The scores are not easy to determine.

        Like

        • Jim Randell's avatar

          Jim Randell 12:00 pm on 19 August 2020 Permalink | Reply

          @Frits: I didn’t do a manual solution for this one. But I imagine the fact there are only 15 possible scores for the 15 games must make things a bit easier. (This is something I didn’t use in my program).

          Like

  • Unknown's avatar

    Jim Randell 8:53 am on 9 August 2020 Permalink | Reply
    Tags: by: T. J. Gaskell   

    Brain-Teaser 5: Independent witness 

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

    This problem and its result illustrate why in courts of Law leading questions are frowned on and why two independent witnesses constitute strong evidence.

    A pack of cards is shuffled and cut at random. The cut card is shown to two persons who tell the truth with probability of a half. They are asked to name the card, and independently, the both answer: “Five of Diamonds”.

    What is the probability that the card is the Five of Diamonds?

    [teaser5]

     
    • Jim Randell's avatar

      Jim Randell 8:54 am on 9 August 2020 Permalink | Reply

      For any particular card there is a 1/52 probability of it being chosen.

      If the witnesses are independent their behaviours can be: both true (probability 1/4), both false (probability 1/4), one true and one false (probability 1/2).

      If the card chosen is 5D, the only way they will both say 5D is if they are both true (probability 1/4).

      So the probability of 5D being chosen and both witnesses saying 5D is (1/52)×(1/4) = 1/208 = 51/10608

      If the card chosen is not 5D, then the only way they will both say 5D is if they are both false (probability 1/4), and they both choose 5D as a random incorrect response (probability 1/51 for each witness).

      So the probability of a card other than 5D being chosen and both witnesses saying 5D is (51/52)×(1/4)×(1/51)² = 1/10608.

      So if we observe both witnesses saying 5D it is 51 times more likely that that the card is 5D than that it isn’t.

      Solution: The probability that the card is the Five of Diamonds is 51/52 (i.e. about 98.1%).


      The following program simulates the scenario.

      import random
      from itertools import product
      from enigma import irange, diff, fdiv, printf
      
      # make a pack of cards
      cards = list(v + s for (v, s) in product("A123456789XJQK", "CDHS"))
      
      # count outcomes
      n = [0, 0]
      
      for _ in irange(1, 10000000):
        # choose a card
        X = random.choice(cards)
        # choose a behaviour for the witnesses
        w1 = random.choice((True, False))
        w2 = random.choice((True, False))  
        # choose a response from the witness
        W1 = random.choice([X] if w1 else diff(cards, [X]))
        W2 = random.choice([X] if w2 else diff(cards, [X]))
        # record outcomes where both witnesses respond 5D
        if W1 == W2 == "5D":
          n[X == "5D"] += 1
      
      (x, t) = (n[1], sum(n))
      printf("p = {x}/{t} ({p:.2%})", p=fdiv(x, t))
      

      It takes 10.6s to run 10,000,000 random trials, and produces the following result:

      % python teaser5.py
      p = 44827/45647 (98.20%)
      

      Like

  • Unknown's avatar

    Jim Randell 4:35 pm on 7 August 2020 Permalink | Reply
    Tags:   

    Teaser 3020: Baz’s bizarre arrers 

    From The Sunday Times, 9th August 2020 [link] [link]

    “Bizarrers” dartboards have double and treble rings and twenty sectors ordered as on this normal dartboard [shown above]. However, a sector’s central angle (in degrees) is given by (100 divided by its basic score). The 20 sector incorporates the residual angle to complete 360º.

    Each player starts on 501 and reduces this, eventually to 0 to win. After six three-dart throws, Baz’s seventh could win. His six totals were consecutive numbers. Each three-dart effort lodged a dart in each of three clockwise-adjacent sectors (hitting, in some order, a single zone, a double zone and a treble zone). [Each] three-sector angle sum (in degrees) exceeded that total.

    The sectors scored in are calculable with certainty, but not how many times hit with certainty, except for one sector.

    Which sector?

    This puzzle uses a different spelling for “arraz” from the previous puzzle involving Baz (Teaser 2934).

    [teaser3020]

     
    • Jim Randell's avatar

      Jim Randell 5:21 pm on 7 August 2020 Permalink | Reply

      This Python program runs in 52ms.

      Run: [ @replit ]

      from fractions import Fraction as F
      from collections import defaultdict
      from itertools import product
      from enigma import tuples, subsets, multiset, seq_all_same, intersect, join, printf
       
      # sectors of the dartboard
      board = [ 20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5 ]
      
      # map scores from 1 to 19 to sector angles (in degrees)
      angle = dict((n, F(100, n)) for n in board if n != 20)
      # the 20 sector completes the board
      angle[20] = 360 - sum(angle.values())
       
      # consider 3 adjacent sectors
      d = defaultdict(list) # store the results by the score (single, double, treble)
      for ss in tuples(board + board[:2], 3):
        # calculate the sector angle sum
        a = sum(angle[s] for s in ss)
        # choose multipliers for the scores
        for ms in subsets(ss, size=len, select="P"):
          # calculate the total
          t = sum(s * m for (m, s) in enumerate(ms, start=1))
          # which is less than the angle sum
          if not (t < a): continue
          d[t].append(ms)
      
      # look for 6 consecutive scores
      for ts in tuples(sorted(d.keys()), 6):
        if not (ts[0] + 5 == ts[-1]): continue
        # produce possible sets of scoring sectors
        mss = list(multiset.from_seq(*ss) for ss in product(*(d[t] for t in ts)))
        # each set of sectors should be the same
        if not seq_all_same(set(m.keys()) for m in mss): continue
        # look for common (sector, count) pairs for all possible scores
        ps = intersect(m.items() for m in mss)
        if len(ps) == 1:
          # output solution
          for (s, n) in ps:
            printf("sector = {s} [count = {n}]")
          printf("  scores = {ts}")
          for t in ts:
            printf("    {t}: {ss}", ss=join(d[t], sep=" "))
      

      Solution: We can say for certain that the 4 sector was hit twice.

      Baz’s 6 scores were (in consecutive order): 58, 59, 60, 61, 62, 63.

      So he has 138 left to score. (Which is achievable in many ways with 3 darts (assuming standard rules of darts apply), for example: treble 20, treble 20, double 9).

      Possible ways to make the first 6 scores with consecutive sectors are:

      58 = (single 3, double 2, treble 17)
      59 = (single 20, double 18, treble 1); (single 10, double 2, treble 15); (single 2, double 3, treble 17)
      60 = (single 4, double 1, treble 18)
      61 = (single 18, double 20, treble 1)
      62 = (single 2, double 15, treble 10)
      63 = (single 1, double 4, treble 18)

      We don’t know which of the alternatives makes up the 59, but whichever is chosen the 4 sector is used twice (in scoring 60 and 63), and the full list of sectors used is: 1, 2, 3, 4, 10, 15, 17, 18, 20.

      There is only one other possible consecutive set of consecutive scores: (41, 42, 43, 44, 45, 46), but this leaves a 240 finish which is not possible with 3 darts (at least not using the standard rules of darts, but maybe there is a way to score at least 80 with one dart on this strange dartboard, or another rule that would allow Baz to win that we don’t know about). But it also turns out that with this set of scores we cannot be certain which sectors were definitely used to make the scores either, so this possibility is excluded without us needing to know the exact rules for the variation of darts being used in the puzzle.

      Like

      • Frits's avatar

        Frits 12:17 pm on 9 August 2020 Permalink | Reply

        It wasn’t easy for me to understand this puzzle. Only after seeing the code it became clear.

        “After six three-dart throws, Baz’s seventh could win”.

        You don’t seem to check that after six three-dart throws Baz has scored at least 331 points.

        With this extra check the 41-46 range can be eliminated earlier (ts[0] ≥ 52).

        Like

        • Jim Randell's avatar

          Jim Randell 12:24 pm on 9 August 2020 Permalink | Reply

          @Frits: There are two possible sets of six consecutive scores, but one is eliminated by the requirement that we can be certain of the sectors that were definitely used to make the scores. It also turns out that it would not be possible to win with three darts from this position (at least on a normal dart board), and it is possible for the other set of six consecutive scores (in many ways, which can easily be seen), so I didn’t incorporate that test into my code, as it seemed to be superfluous (although probably useful in a manual solution).

          Like

          • Frits's avatar

            Frits 5:17 pm on 9 August 2020 Permalink | Reply

            Agree.

            If the six totals had come out as f.i. 54-59 the score after six three-dart throws would have been 339 leaving 162 which is not a finish at darts.

            If the program is intended to find a quick solution which has to be checked manually that’s OK with me.

            Like

            • Jim Randell's avatar

              Jim Randell 8:30 am on 12 August 2020 Permalink | Reply

              Here is a modified version of my program that ensures that Baz can finish on his 7th throw [ @replit ].

              Although as previously noted, this doesn’t change the solutions found.

              The puzzle text only states that the remaining total should be reduced to 0, so that’s what I’ve implemented. No requirement for the final dart to be a double, and as no inner or outer bull is mentioned I have not included them either.

              New Scientist Puzzle #06 explores scores achievable with n darts (on a standard dartboard).

              Like

    • Robert Brown's avatar

      Robert Brown 1:07 am on 10 August 2020 Permalink | Reply

      That total doesn’t work. You need 58-63, which can be made 3 different ways. Total is 363 leaving 138 which can be finished with any 3 darts that sum to 46, all of which are trebles. It’s quite quick to manually check the 3 different sequences: there’s one sector that appears the same number of times in each sequence, which is the required answer.

      Like

      • Jim Randell's avatar

        Jim Randell 8:55 am on 10 August 2020 Permalink | Reply

        @Robert: I’m not sure what total you are objecting to. Also I understood that in standard rules of darts you have to finish on a double. (Although in the version of the game used in the puzzle we are not told what the rules for finishing are. Fortunately though, of the two possible consecutive sequences of scores one of them is ruled out for reasons that are explained in the puzzle, so we don’t need to know the rules for finishing. We are just told that Baz could finish on his next throw, which is nice for him, but inconsequential for us).

        Like

    • Robert Brown's avatar

      Robert Brown 8:22 pm on 10 August 2020 Permalink | Reply

      I don’t know the rules of darts! The total that I found (58,59,60,61,62,63) needs 138 to finish, so I assumed finishing on trebles was ok. But they tell me that a bull’s eye counts as 50, so you can finish with 2 bull’s eyes & a double 19.
      I was objecting to Frits’s 6 totals (54-59) which I don’t see as a possibility. The important thing about my sequence is that there are 3 way to make 59, which changes the number of times that each sector is called, except for one which is the answer.

      Like

      • Jim Randell's avatar

        Jim Randell 10:23 pm on 10 August 2020 Permalink | Reply

        @Robert: Right. I believe Frits only gave those numbers as a hypothetical example to illustrate his point.

        You have found the right set of scores that leads to the answer. And fortunately (as I said above) you don’t need to know the rules of darts to eliminate the only other possible consecutive sequence of scores.

        Like

    • GeoffR's avatar

      GeoffR 8:40 am on 12 August 2020 Permalink | Reply

      I had the idea of defining a valid function for three adjacent sectors – i.e.valid if the six totals were less than the sum of the three angles. This enabled the code below to find the unique six consecutive totals, but not the single requested sector.

      
      from itertools import permutations
      from collections import defaultdict
      D = defaultdict(set)
      
      L, L2 = [], []
      
      # function to check if a three-sector is valid
      # a, b, c are adjacent sector numbers
      def is_valid(a, b, c):
        asum = sum(100 / x for x in (a, b, c))
        ps = []
        for p3 in permutations((a, b, c)):
          score = sum(m * x for m, x in zip((1, 2, 3), p3))
          if score < asum:
            ps.append(score)
        return ps
      
      # the scores starting at north and running clockwise
      db = (20, 1, 18, 4, 13, 6, 10, 15,  2, 17,
            3, 19, 7, 16, 8, 11, 14, 9, 12, 5)
      
      for p in range(20):
        p3 = tuple((p + i) % 20 for i in (0, 1, 2))
        sc = tuple(db[i] for i in p3)
        L.append(sc)
      
      for x in range(20):
        a, b, c = L[x]
        s6 = is_valid(a, b, c)
        if s6:
          L2.append(s6)
      
      for p in permutations(L2,6):
        L1, L2, L3, L4, L5, L6 = p
        
        L22 = sorted(L1 + L2 + L3 + L4 + L5 + L6)
        L22 = sorted(list(set(L22)))
        
        for b in range(0, len(L22)-6):
          b1  = L22[b:b+6]
          # split slice into six totals
          a0, b0, c0, d0, e0, f0 = b1
          # total of six scores
          s = a0 + b0 + c0 + d0 + e0 + f0
          # total must be less than 180 to close with the 7th set of 3 darts
          # else there is another set of 6 consecutive numbers in the forties
          if 501 - s > 180:
            continue
          # check six numbers are consecutive
          if b0-a0 == c0-b0 == d0-c0 == e0-d0 == f0-e0 == 1:
            D[s].add ((a0,b0,c0,d0,e0,f0))
      
      for k,v in D.items():
        print(f"Sum of six 3 dart totals = {k}")
        print(f"Unique six totals are {v}")
        sc_left = 501 - k
        print(f"Seventh group of three darts score = {sc_left}")
        print(f"7th 3 darts group could be 3*18 + 3*20 + 2*12 = 138")
        print(f"Finishing on a double!")
      
      # Sum of six 3 dart totals = 363
      # Unique six totals are {(58, 59, 60, 61, 62, 63)}
      # Seventh group of three darts score = 138
      # 7th 3 darts group could be 3*18 + 3*20 + 2*12 = 138
      # Finishing on a double!
      
      
      

      I looked at a print out of Jim’s dictionary which gives the same six consecutive totals and the triples used in each of the numbers 58,59,60,61, 62 and 63.

      scores = (58, 59, 60, 61, 62, 63)
      58: (3, 2, 17)
      59: (20, 18, 1) (10, 2, 15) (2, 3, 17)
      60: (4, 1, 18)
      61: (18, 20, 1)
      62: (2, 15, 10)
      63: (1, 4, 18)

      The number 59 has three triples and all these digits appear in the numbers 58,60,61,62 and 63. The total of 363 can therefore be made up three ways. All three ways require the numbers 60 and 63, which also contain the digit ‘4’, so we can say thet the digit ‘4’ definitely occurs twice.

      A minor variation to my programme found the only eight valid sectors were:
      (20, 1, 18), (1, 18, 4), (4, 13, 6), (10, 15, 2),
      (15, 2, 17), (2, 17, 3), (3, 19, 7), (5, 20, 1)

      Jim’s dictionary above show that only four of these eight triples were needed for the final solution;
      i.e (2,3,17), (20,18,1), (10,2,15), (4,1,18)

      Like

  • Unknown's avatar

    Jim Randell 9:26 am on 6 August 2020 Permalink | Reply
    Tags:   

    Teaser 2678: Map snap 

    From The Sunday Times, 19th January 2014 [link] [link]

    I have two rectangular maps depicting the same area, the larger map being one metre from west to east and 75cm from north to south. I’ve turned the smaller map face down, turned it 90 degrees and placed it in the bottom corner of the larger map with the north-east corner of the smaller map touching the south-west corner of the larger map. I have placed a pin through both maps, a whole number of centimetres from the western edge of the larger map. This pin goes through the same geographical point on both maps. On the larger map 1cm represents 1km. On the smaller map 1cm represents a certain whole number of kilometres …

    … how many?

    [teaser2678]

     
    • Jim Randell's avatar

      Jim Randell 9:27 am on 6 August 2020 Permalink | Reply

      See also: Enigma 1177.

      If we suppose the smaller map has a scale of k kilometres per centimetre, then the dimensions of the smaller map are (100 / k) (west to east) and (75 / k) (south to north).

      If the point we are interested on the map has an easting of x and a northing of y (measured from the SW corner of the maps), then we have the following equations:

      k.x = 75 − y
      k.y = 100 − x

      If we consider possible integer values for k, we can determine values for x and y.

      y = 75 − k.x
      k.y = 75k − k².x
      100 − x = 75k − k².x
      x(k² − 1) = 75k − 100
      x = 25 (3k − 4) / (k² − 1)

      So we can look for values of k that give an integer value for x.

      This Python program runs in 49ms.

      Run: [ @replit ]

      from enigma import (irange, div, fdiv, printf)
      
      for k in irange(2, 75):
        x = div(25 * (3 * k - 4), k * k - 1)
        if x is None: continue
        y = fdiv(100 - x, k)
        printf("k={k} x={x} y={y:g}")
      

      Solution: The scale of the smaller map is 1cm = 6km.

      So the smaller map measures 16.67 cm by 12.5 cm.

      The marked point is 10 km from the western edge of the maps, and 15 km from the southern edge of the maps.

      On the larger map the distances are (10cm, 15cm) on the smaller map the distances are (1.67cm, 2.5cm).

      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