Updates from November, 2019 Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 1:45 pm on 10 November 2019 Permalink | Reply
    Tags: ,   

    Brainteaser 1742: Not-so-safe deposit 

    From The Sunday Times, 4th February 1996 [link]

    The safe deposit boxes at our bank are arranged in a huge pyramid, the top of which is illustrated:

    When the were first opened the top box had a quantity of gold coins placed in it and the rest were empty. One night a burglar broke into that box, emptied it, and (cleverly, he thought) instead of running off with the coins, he found the two empty boxes in the row below and put some of his haul in one and the rest in the other.

    Word spread through the underworld about the bank’s lax security, and a sequence of burglaries took place. Each burglar broke into one box containing some coins, emptied it, found two empty boxes in the row below, placed some of his haul in one of these boxes and the rest in the other.

    Ages later the manager of the bank opened up the top box and was horrified to find it empty. So he tried the boxes in the next row and found them empty too. He carried on like this until he found some coins in one particular row. In fact, no matter how many burglaries had taken place, there could not have been more empty rows at the top of the pyramid. And indeed if any fewer burglaries had taken place all those rows could not have been empty.

    (a) How many empty rows at the top were there?
    (b) How many burglaries were there?

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

    [teaser1742]

     
    • Jim Randell's avatar

      Jim Randell 1:45 pm on 10 November 2019 Permalink | Reply

      We can try to empty the uppermost rows as quickly as possible.

      Initially, in the top row there is 1 box, initially containing the complete amount:

      [0] 1/1 = 1

      The first burglar breaks in, and splits the amount between the two boxes in the second row. So each of the 2 boxes contains 1/2 of the complete amount:

      [1] 0/1 + 2/2 = 1

      The second burglar breaks in and splits the amount from one of the boxes in the second row between 2 (of the 3) boxes in the third row:

      [2] 0/1 + 1/2 + 2/4 = 1

      The third burglar can’t burgle the remaining box in the second row, as there aren’t enough empty boxes in the third row to hide the spoils. So he splits the amount from one of the boxes in the third row into 2 (of the 4) boxes in the fourth row:

      [3] 0/1 + 1/2 + 1/4 + 2/8 = 1

      The fourth burglar can burgle the box in the second row, as there are now 2 empty boxes in the third row:

      [4] 0/1 + 0/2 + 3/4 + 2/8 = 1

      And so the process continues:

      [5] 0/1 + 0/2 + 2/4 + 4/8 = 1
      [6] 0/1 + 0/2 + 2/4 + 3/8 + 2/16 = 1
      [7] 0/1 + 0/2 + 2/4 + 2/8 + 4/16 = 1
      [8] 0/1 + 0/2 + 1/4 + 4/8 + 4/16 = 1
      [9] 0/1 + 0/2 + 1/4 + 4/8 + 3/16 + 2/32 = 1
      [10] 0/1 + 0/2 + 1/4 + 3/8 + 5/16 + 2/32 = 1
      [11] 0/1 + 0/2 + 1/4 + 3/8 + 4/16 + 4/32 = 1
      [12] 0/1 + 0/2 + 1/4 + 3/8 + 3/16 + 6/32 = 1
      [13] 0/1 + 0/2 + 1/4 + 2/8 + 5/16 + 6/32 = 1
      [14] 0/1 + 0/2 + 0/4 + 4/8 + 5/16 + 6/32 = 1

      So we’ve managed to empty the first 3 rows (in 14 burglaries).

      Can we empty the fourth row?

      What if every row below it was full? We would have:

      S = 5/16 + 6/32 + 7/64 + 8/128 + …
      S = (1/16 + 4/16) + (1/32 + 5/32) + (1/64 + 6/64) + (1/128 + 7/128) + …
      S = (1/16 + 1/32 + 1/64 + 1/128 + …) + (4/16) + (5/32 + 6/64 + 7/128 + …)
      S = (1/8)(1/2 + 1/4 + 1/8 + 1/16 + …) + (1/4) + S/2
      S = (1/8) + (1/4) + S/2
      S/2 = 3/8
      S = 3/4

      We can check this by calculating the sum the first few terms:

      >>> sum(fdiv(n + 1, 2**n) for n in irange(4, 100))
      0.75
      

      So there is not enough room to hide the entire amount using just rows 5 onwards, hence we can never empty row 4.

      Solution: (a) The first 3 rows were empty.

      The number of burglaries required to achieve any situation is one less than the number of occupied boxes.

      And as the fractions of the original collection decrease as we move down the rows we want to occupy the minimal number of boxes possible. If the first 3 rows are empty this minimal number is 4 on the 4th row, 5 on the 5th row, 6 on the 6th row (4/8 + 5/16 + 6/32 = 1), which required 4 + 5 + 6 − 1 = 14 burglaries.

      Solution: (b) There were 14 burglaries.

      Like

      • John Crabtree's avatar

        John Crabtree 9:50 pm on 13 November 2019 Permalink | Reply

        While experimenting with this, I found that one box in row 2 could be replaced by:
        i) a pyramid with one box full in row 4, two in row 5, three in row 6 etc
        or ii) one box full in row 4, and three in all lower rows
        Add the two together, and the grid would be full with two boxes full in row 4, and all all boxes in lower rows. It does not matter if this pattern can actually be reached. Row 4 cannot be empty. it is quite easy to check if the first three rows can be emptied.

        Like

  • Unknown's avatar

    Jim Randell 9:38 am on 17 October 2019 Permalink | Reply
    Tags:   

    Brainteaser 1734: Scratchcard 

    From The Sunday Times, 10th December 1995 [link]

    Every Saturday The Daily Broadsheet gives readers a free scratchcard comprising two crosses and a number of ticks concealed beneath silver foil squares. Readers have to scratch just three of the squares to reveal what is beneath. Anyone who scratches only ticks can use the card to obtain a 50p discount on The Sunday Broadsheet. The probability of revealing three ticks, expressed as a percentage, is a whole number larger than 50%.

    To increase circulation the paper intends to increase the number of silver squares, still with only two crosses. This will increase the chances of finding ticks when scratching three squares, and again the probability will be a whole number percentage.

    How many more silver squares do they intend to add?

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

    [teaser1734]

     
    • Jim Randell's avatar

      Jim Randell 9:39 am on 17 October 2019 Permalink | Reply

      If there are n ticks (where n ≥ 3), then the probability of scratching off 3 ticks is:

      (n / (n + 2)) × ((n − 1) / (n + 1)) × ((n − 2) / n)
      = (n − 1)(n − 2) / (n + 1)(n + 2)

      This Python program finds values of n where this probability is an integer percentage, greater than 50%.

      Run: [ @repl.it ]

      from enigma import irange, inf, first, printf
      
      # find n, p where probability p is an integer percentage > m
      def solve(m):
        for n in irange(3, inf):
          (p, r) = divmod(100 * (n - 1) * (n - 2), (n + 1) * (n + 2))
          if r == 0 and p > m: yield (n, p)
          if p == 99 and r > 0: break
      
      # find the first two (n, p) values with p > 50
      ((n1, p1), (n2, p2)) = first(solve(50), 2)
      d = n2 - n1
      printf("{d} more squares [{n1} ticks -> {p1}%; {n2} ticks -> {p2}%]")
      

      Solution: They intend to add 9 more squares.

      With 16 squares (14 ticks and 2 crosses) the probability is 65%.

      With 25 squares (23 ticks and 2 crosses) the probability is 77%.

      In fact there are only 4 values for n which give an integer percentage for p:

      n = 3, p = 10%
      n = 4, p = 20%
      n = 14, p = 65%
      n = 23, p = 77%

      Like

  • Unknown's avatar

    Jim Randell 8:10 am on 8 October 2019 Permalink | Reply
    Tags:   

    Brainteaser 1730: Not so usual 

    From The Sunday Times, 12th November 1995 [link]

    As usual Eric was early for his morning train. Two express trains of the same length passed through the station in opposite directions, each at its own constant speed. Out of interest Eric timed them and noted that the faster one took 6 seconds to pass him, while the slower one took 8 seconds.

    As soon as they had passed he saw his travelling companion, Len, 30 yards along the platform. In his conversation later, Eric told Len that he had been standing exactly in line with the point at which the fronts of the trains coincided.

    Len then commented that, by coincidence, he had been standing exactly in line with the point at which the ends of the trains coincided. Eric was delighted because he was now able to work out the speeds of the trains.

    How long were the trains?

    This puzzle is included in the book Brainteasers (2002), under the title “Commuter computer”. The puzzle text above is taken from the book.

    [teaser1730]

     
    • Jim Randell's avatar

      Jim Randell 8:11 am on 8 October 2019 Permalink | Reply

      At time 0s the fronts of the two trains (each of length x yards) are in line with Eric.

      At time 6s, the faster train’s end passes Eric. So it is travelling at (x/6) yards/s.

      At time 8s, the slower train’s end passes Eric. So it is travelling at (x/8) yards/s.

      The ends of the trains pass each other (and Len) at time t, when the faster train has travelled its entire length plus 30 yards, and the slower train has travelled its entire length less 30 yards.

      So:

      tx/6 = x + 30
      tx/8 = x − 30

      These equations are solved to give:

      x = 210, t = 48/7

      Solution: The trains were 210 yards long.

      So the faster train is travelling at 35 yards/s (about 72 mph) and the slower train is travelling at 26.25 yards/s (about 54 mph).

      And the ends of the trains passed each other (and Len) 6/7 seconds after the end of the fastest train passed Eric.

      Like

  • Unknown's avatar

    Jim Randell 3:51 pm on 3 October 2019 Permalink | Reply
    Tags:   

    Brainteaser 1724: Like a lamb 

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

    Mary had a little lamb,
    Its fleece was white as snow
    And everywhere that Mary went
    The lamb was sure to go.

    It followed her to school one day,
    But found the maths a bore,
    For lambs, you see, do all their counting
    On a scale of four.

    Thus when a lamb writes two times two
    Its answer looks like 10,
    While our 10 written by a lamb
    Is double-2 again!

    It much distressed them both to find
    They never could agree
    On how to write down any
    Of the numbers after three.

    At last they hit on some for which
    The lamb would only need
    Two figures placed in front of Mary’s,
    Then they were agreed.

    Which two figures?

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

    [teaser1724]

     
    • Jim Randell's avatar

      Jim Randell 3:52 pm on 3 October 2019 Permalink | Reply

      We are to infer from the puzzle text that the lamb counts using base 4, with the standard symbols 0, 1, 2, 3.

      So we need to find numbers in base 4 whose representation in base 10 is the same as the representation in base 4 but with the leftmost 2 digits removed.

      This Python program runs in 221ms.

      Run: [ @repl.it ]

      from enigma import irange, inf, nsplit, nconcat, printf
      
      # base 4 digits for number n
      base4 = lambda n: nsplit(n, base=4)
      
      # consider k-digit base 4 numbers, read as base 10 numbers, they need to be (k + 2) digits
      for k in irange(1, inf):
        # smallest k-digit base 4 number read as a base 10 number
        if len(base4(10 ** (k - 1))) > k + 2: break
        # largest k-digit base 4 number read as a base 10 number
        if len(base4((10 ** k - 1) // 3)) < k + 2: continue
      
        # consider possible (k + 2) digit base 4 numbers 
        for n in irange(4 ** (k + 1), 4 ** (k + 2) - 1):
          # represented in base 4
          ds = base4(n)
          # does removing the first two digits give the base 10 representation?
          if nconcat(ds[2:]) == n:
            printf("prefix = {p} [k={k}: {ds} (base 4) = {n} (base 10)]", p=nconcat(ds[:2]), ds=nconcat(ds))
      

      Solution: The 2-digit prefix is 30.

      The only viable numbers are:

      303320 (base 4) = 3320 (base 10)
      303321 (base 4) = 3321 (base 10)
      303322 (base 4) = 3322 (base 10)
      303323 (base 4) = 3323 (base 10)

      The program considers base 4 numbers that are 5-, 6- and 7-digits long, and looks for corresponding 3-, 4- and 5- digit base 10 numbers.

      Like

  • Unknown's avatar

    Jim Randell 5:04 pm on 29 September 2019 Permalink | Reply
    Tags:   

    Brainteaser 1721: Prime leaps 

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

    The history-cum-maths teacher asked the class to name some years which the knew from history lessons. Johnny named 1066, the Battle of Hastings, and 1939, the outbreak of the Second World War. The teacher then asked him to calculate the difference between the two and Johnny got the correct answer of 873.

    The teacher then asked Johnny if this difference was prime and Johnny answered correctly that it was not because, for example, it was divisible by three.

    The teacher then asked the class to find the longest list of years they could from 0, 1, 2, …, 1999, 2000 so that for any two numbers in the list their difference was not prime.

    Which numbers are in the longest such list?

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

    [teaser1721]

     
    • Jim Randell's avatar

      Jim Randell 5:05 pm on 29 September 2019 Permalink | Reply

      First, lets treat the list as a sequence of consecutive integers starting at 0. (We’re only interested in differences, so it doesn’t really matter where we start).

      If we consider the set of numbers (0, 4, 8, 12, 16, …) (i.e. the multiples of 4) then we see the difference between any two numbers is also a multiple of 4, and hence not prime. This allows us to select ⌈n / 4⌉ numbers from a sequence of n numbers.

      And this is the best we can manage. Every segment of size 4 has 1 number (and if there is a shorter segment left over from chopping the sequence into fours, then that also has one number).

      To do better we would have to have a segment of size 4 with (at least) two numbers in it.

      We can’t have … 0 _ 2 _ … or … 0 _ _ 3 … as these have a difference of 2 and 3 respectively, so the only possible segment is … 0 1 _ _ ….

      But any numbers that differ by 1 have to be followed by (or preceded by) 7 gaps:

      So the best we could manage is 2 numbers out of every 9, which is worse than 1 out of every 4.

      So presented with a sequence of 2001 numbers the best we can manage is ⌈2001 / 4⌉ = 501 numbers: (0, 4, 8, 12, 16, …, 1992, 1996, 2000).

      Solution: The longest list contains all exact multiples of 4.

      Considered as years (ignoring the fact that there was no year 0), most of the numbers represent leap years, hence the title of the puzzle.

      Like

    • Lise Andreasen's avatar

      Lise Andreasen 12:18 pm on 5 August 2024 Permalink | Reply

      Elegant.

      Like

  • Unknown's avatar

    Jim Randell 8:37 am on 15 September 2019 Permalink | Reply
    Tags:   

    Brainteaser 1712: Squambling 

    From The Sunday Times, 9th July 1995 [link]

    In “squambling” a number, the first (leftmost) digit is squared, the next is cubed, the next raised to the fourth power, and so on. The total of these answers provides a new number, ready for the squambling to begin again.

    For example:

    18 → 1² + 8³ = 513 → 5² + 1³ + 3⁴ = 107
    107 → 2402 → 100 → 1 → 1 → …

    If you take my age and squamble it you get a three-figure number. If you then squamble that answer you get my age next year.

    How old am I?

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

    [teaser1712]

     
    • Jim Randell's avatar

      Jim Randell 8:38 am on 15 September 2019 Permalink | Reply

      This Python program runs in 86ms.

      Run: [ @replit ]

      from enigma import (nsplit, irange, inf, printf)
      
      squamble = lambda n: sum(d ** n for (n, d) in enumerate(nsplit(n), start=2))
      
      assert squamble(18) == 513
      
      for n in irange(1, inf):
        sn = squamble(n)
        if not (99 < sn < 1000): continue
        s2n = squamble(sn)
        if not (s2n == n + 1): continue
        # output solution
        printf("{n} -> {sn} -> {s2n}")
        break
      

      Solution: You are 46.

      We have:

      46 → 232 → 47 → … → 43

      After 91 applications of the function we eventually reach a value of 43, which is stable (i.e. squamble(43) = 43).

      The next smallest value at which this occurs is 753:

      753 → 255 → 754

      Like

  • Unknown's avatar

    Jim Randell 9:19 am on 8 September 2019 Permalink | Reply
    Tags:   

    Brainteaser 1688: Sunday paper 

    From The Sunday Times, 22nd January 1995 [link]

    As usual, in this letters-for-digits puzzle different digits have consistently been replaced by different letters:

    Each letter in the second row is the sum of its two nearest letters in the row above.

    What is the value of READER?

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

    [teaser1688]

     
    • Jim Randell's avatar

      Jim Randell 9:20 am on 8 September 2019 Permalink | Reply

      I used the [[ SubstitutedExpression() ]] solver from the enigma.py library to solve the 5 sums.

      The following run files executes in 150ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "S + U = P"
      "U + N = A"
      "N + D = P"
      "D + A = E"
      "A + Y = R"
      
      --answer="READER"
      

      Solution: READER = 694596.

      Like

    • GeoffR's avatar

      GeoffR 8:25 pm on 8 September 2019 Permalink | Reply

      from itertools import permutations
      
      for x in permutations (range(1,10)):
          s, u, n, d, a, y, p, e, r = x
          if s + u == p:
            if u + n == a:
              if n + d == p:
                if d + a == e:
                  if a + y == r: 
                    reader = 100001*r + 10010*e + 1000*a + 100*d
                    print("READER = ", reader)
                    # READER =  694596
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:43 am on 5 September 2019 Permalink | Reply
    Tags: by: K E Mawardy   

    Brainteaser 1675: The A-to-Z of sport 

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

    The individual sport or sports of the 26 members of the Venerable Sports Club means that they are just enough to form:

    a football team (11); or
    a hockey team (11); or
    a rugby team (15).

    The number of members playing only two of these sports is the same as the number of members playing rugby only, which is less than one third of the total membership.

    How many members play all three sports?

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

    [teaser1675]

     
    • Jim Randell's avatar

      Jim Randell 9:46 am on 5 September 2019 Permalink | Reply

      We can label the 7 non-empty parts of the Venn diagram, F, H, R, FH, FR, HR, FHR, according to which sports each member plays.

      We can then express the constraints in terms of these 7 integer variables.

      I expressed the constraints as a MiniZinc model, and then used the minizinc.py wrapper to collect the solutions and count them using Python.

      This program runs in 263ms.

      from minizinc import MiniZinc, var
      from enigma import multiset, printf
      
      # decision variables, the 7 non-empty areas of the venn diagram
      vs = "F H R FH FR HR FHR"
      
      # the MiniZinc model
      model = f"""
      
        % declare the variables
        {var('0..26', vs.split())};
      
        % there are 26 members in total
        constraint F + H + R + FH + FR + HR + FHR = 26;
      
        % F's form a football team (11)
        constraint F + FH + FR + FHR = 11;
      
        % H's form a hockey team (11)
        constraint H + FH + HR + FHR = 11;
      
        % R's form a rugby team (15)
        constraint R + FR + HR + FHR = 15;
      
        % total number of two sports = number of only rugby
        constraint FH + FR + HR = R;
      
        % R is less than 1/3 of the total membership (= 26)
        constraint 3 * R < 26;
      
        solve satisfy;
      """
      
      # create the model
      m = MiniZinc(model)
      
      # record FHR values
      r = multiset()
      
      # solve it
      for (F, H, R, FH, FR, HR, FHR) in m.solve(result=vs):
        printf("[F={F} H={H} R={R} FH={FH} FR={FR} HR={HR} FHR={FHR}]")
        r.add(FHR)
      
      # output solutions
      for (k, v) in r.most_common():
        printf("FHR = {k} [{v} solutions]")
      

      Solution: 2 members play all three sports.

      There are 7 possible solutions:

      F=2 H=8 R=7 FH=1 FR=6 HR=0 FHR=2
      F=3 H=7 R=7 FH=1 FR=5 HR=1 FHR=2
      F=4 H=6 R=7 FH=1 FR=4 HR=2 FHR=2
      F=5 H=5 R=7 FH=1 FR=3 HR=3 FHR=2
      F=6 H=4 R=7 FH=1 FR=2 HR=4 FHR=2
      F=7 H=3 R=7 FH=1 FR=1 HR=5 FHR=2
      F=8 H=2 R=7 FH=1 FR=0 HR=6 FHR=2

      Like

      • Jim Randell's avatar

        Jim Randell 12:32 pm on 5 September 2019 Permalink | Reply

        And here’s a solution in Python. It runs in 93ms.

        Run: [ @repl.it ]

        from enigma import (irange, multiset, printf)
        
        # decompose t into k numbers (min value m)
        def decompose(t, k, m=0, s=[]):
          if k == 1:
            yield s + [t]
          else:
            for x in irange(m, t - k * m):
              yield from decompose(t - x, k - 1, m, s + [x])
        
        # record FHR values
        r = multiset()
        
        # can form a football team of 11
        for (F, FH, FR, FHR) in decompose(11, 4):
          # can form a hockey team of 11
          for (H, HR) in decompose(11 - FH - FHR, 2):
            # number of rugby only = total number of two sports
            R = FH + FR + HR
            # can form a rugby team of 15
            if not (R + FR + HR + FHR == 15): continue
            # R is less than 1/3 of the total membership (26)
            if not (3 * R < 26): continue
            # total membership is 26
            if not (F + H + R + FH + FR + HR + FHR == 26): continue
        
            printf("[F={F} H={H} R={R} FH={FH} FR={FR} HR={HR} FHR={FHR}]")
            r.add(FHR)
        
        # output solutions
        for (k, v) in r.most_common():
          printf("FHR = {k} [{v} solutions]")
        

        Like

        • John Crabtree's avatar

          John Crabtree 12:41 am on 6 September 2019 Permalink | Reply

          Let R members play rugby only and let A members play all three sports.
          Considering that there are 26 members and 37 players leads to 2A + R = 11,
          R ≤ 7 and so A ≥ 2.

          15 players play rugby, ie A + 2R ≥ 15, which leads to 3A ≤ 7.

          And so 2 members play all three sports.

          Liked by 1 person

  • Unknown's avatar

    Jim Randell 10:38 am on 27 August 2019 Permalink | Reply
    Tags:   

    Brainteaser 1661: Goals galore 

    From The Sunday Times, 10th July 1994 [link]

    Three football teams played each other once in a tournament. Athletic beat City, City beat Borough, and Borough beat Athletic.

    They could not decide which team should receive the trophy since:

    Athletic had scored the most goals;

    Borough had the best goal average (goals for ÷ goals against)

    City had the best goal difference (goals for − goals against)

    Fewer than 40 goals were scored in the tournament.

    What were the scores in the three games?

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

    [teaser1661]

     
    • Jim Randell's avatar

      Jim Randell 10:39 am on 27 August 2019 Permalink | Reply

      There are six bounded variables (the number of goals scored by each team in each of the three matches), and the conditions put further constraints on the variables.

      I used the MiniZinc system to solve the set of constraints, and the minizinc.py library to construct the constraints using Python.

      This Python 3 program runs in 228ms.

      from minizinc import MiniZinc, var
      from enigma import join
      
      # teams
      (A, B, C) = teams = list('ABC')
      
      # decision variables: XY = goals for X against Y in X vs Y match
      vs = "AB BA AC CA BC CB"
      
      # goals for / against team x
      gf = lambda x: "(" + join((x + t for t in teams if t != x), sep=' + ') + ")"
      ga = lambda x: "(" + join((t + x for t in teams if t != x), sep=' + ') + ")"
      
      model = f"""
      
        % declare the variables
        {var('0..39', vs.split())};
      
        % the match outcomes
        constraint BA > AB; % B beat A
        constraint AC > CA; % A beat C
        constraint CB > BC; % C beat B
      
        % A scored the most goals
        constraint {gf(A)} > {gf(B)};
        constraint {gf(A)} > {gf(C)};
      
        % B had the best goal average
        constraint {gf(B)} * {ga(A)} > {gf(A)} * {ga(B)};
        constraint {gf(B)} * {ga(C)} > {gf(C)} * {ga(B)};
      
        % C had the best goal difference
        constraint {gf(C)} - {ga(C)} > {gf(A)} - {ga(A)};
        constraint {gf(C)} - {ga(C)} > {gf(B)} - {ga(B)};
      
        % fewer than 40 goals were scored in the tournament
        constraint AB + BA + AC + CA + BC + CB < 40;
      
        solve satisfy;
      """
      
      # create the model
      m = MiniZinc(model)
      
      # solve the constraints
      for (AB, BA, AC, CA, BC, CB) in m.solve(result=vs):
        print(f"A vs B = {AB}-{BA}; A vs C = {AC}-{CA}; B vs C = {BC}-{CB}")
      

      Solution: The scores in the matches are: A vs B = 3-7; A vs C = 13-12; B vs C = 0-3.

      In total there were 38 goals scored in the tournament.

      The different measures are:

      goals scored: A = 16, C = 15, B = 7
      goal average: B = 1.17, C = 1.15, A = 0.84
      goal difference: C = +2, B = +1, A = −3

      Like

    • GeoffR's avatar

      GeoffR 3:37 pm on 28 August 2019 Permalink | Reply

      Minizinc found two solutions for me, the first solution looks incorrect as the averages for B and C were the same(7/6 and 14/12, which does not agree with my constraint for goal averages.

      The second solution is a correct solution and agrees with the published solution.

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Teams A, B and C goal scores
      % eg AB = goals for Team A playing Team B
      % and BA = goals against Team A playing Team B
      var 0..39:AB; var 0..39:BA;  
      var 0..39:AC; var 0..39:CA; 
      var 0..39:BC; var 0..39:CB; 
      
      % goals for and against Teams A, B and C
      var 0..39:gfA; var 0..39:gfB; var 0..39:gfC; 
      var 0..39:gaA; var 0..39:gaB; var 0..39:gaC; 
      
      % goal difference variables
      var -10..39: gdifA; 
      var -10..39: gdifB; 
      var -10..39: gdifC; 
      
      % goal average variables
      var 0.01..10.1: gavA; 
      var 0.01..10.1: gavB; 
      var 0.01..10.1: gavC;
      
      % total of all goals < 40
      constraint AB + BA + AC + CA + BC + CB < 40;
      
      % Team B beat Team A, Team A beat TeamC and Team C beat Team B
      constraint BA > AB; 
      constraint AC > CA; 
      constraint CB > BC; 
      
      % Goals for Teams A, B and C
      constraint gfA = AB + AC;
      constraint gfB = BA + BC;
      constraint gfC = CA + CB;
      
      % goals against Teams A, B and C
      constraint gaA = BA + CA;
      constraint gaB = AB + CB;
      constraint gaC = AC + BC;
      
      % A had the most goals of Teams A, B and C
      constraint (AC + AB) > (BA + BC) /\ (AC + AB) > (CA + CB);
      
      % C had the highest goal difference
      constraint gdifA = AB + AC - BA - CA;
      constraint gdifB = BA + BC - AB - CB;
      constraint gdifC = CA + CB - AC - BC;
      
      constraint gdifC > gdifA /\ gdifC > gdifB;
      
      % B had the best goal average
      constraint gavA = (AB + AC) / (BA + CA);
      constraint gavB = (BA + BC) / (AB + CB);
      constraint gavC = (CA + CB) / (AC + BC);
      
      constraint gavB > gavC /\ gavB > gavA;
      
      solve satisfy;
      
      % show team scores in the three games
      output [   "A v B = " ++ show(AB) ++ "-" ++ show(BA)
      ++ "\n" ++ "A v C = " ++ show(AC) ++ "-" ++ show(CA)
      ++ "\n" ++ "B v C = " ++ show(BC) ++ "-" ++ show(CB) ];
      
      % A v B = 3-7
      % A v C = 12-11
      % B v C = 0-3
      % ----------
      % A v B = 3-7
      % A v C = 13-12
      % B v C = 0-3
      % ----------
      % ==========
      % Finished in 433msec
      
      % Detailed outputs
      % AB = 3; BA = 7; AC = 12; CA = 11; BC = 0; CB = 3;
      % gfA = 15; gfB = 7; gfC = 14;0
      % gaA = 18; gaB = 6; gaC = 12;
      % gdifA = -3; gdifB = 1; gdifC = 2;
      % gavA = 0.833333333333333; gavB = 1.16666666666667; gavC = 1.16666666666667;
      % ----------
      % AB = 3; BA = 7; AC = 13; CA = 12; BC = 0; CB = 3;
      % gfA = 16; gfB = 7; gfC = 15;
      % gaA = 19; gaB = 6; gaC = 13;
      % gdifA = -3; gdifB = 1; gdifC = 2;
      % gavA = 0.842105263157895; gavB = 1.16666666666667; gavC = 1.15384615384615;
      %----------
      %==========
      
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 9:18 am on 29 August 2019 Permalink | Reply

        @GeoffR: I suspect the second solution comes about because the calculations for “goal average” will be done using floating point numbers. floats are only approximations to real numbers, so if we compare two floats that should be equal, but have been arrived at by different calculations, it is likely that one will compare as larger than the other.

        I would change the model to keep things in the integer domain, by replacing constraints of the form:

        constraint a / b > c / d;
        

        with the equivalent:

        constraint a * d > c * b;
        

        Like

    • GeoffR's avatar

      GeoffR 3:12 pm on 29 August 2019 Permalink | Reply

      @Jim: Yes, I think you are right as I got a similar second incorrect solution in Python, with a similar approach. For me, it was an unforeseen consequence of using floats instead of integers.
      Thanks for the explanation.

      Like

      • Jim Randell's avatar

        Jim Randell 3:17 pm on 29 August 2019 Permalink | Reply

        Although in Python you can avoid floats by using the [[ fractions ]] module to give an exact representation of rational numbers for the goal averages.

        Like

  • Unknown's avatar

    Jim Randell 9:47 am on 20 August 2019 Permalink | Reply
    Tags:   

    Brainteaser 1659: Prime cuts 

    From The Sunday Times, 26th June 1994 [link]

    My new season ticket number is a prime obtained by rearranging five consecutive digits. By cutting off successive single digits from alternate ends I can steadily reduce this to a four-figure prime, a three-figure prime, a two-figure prime and finally a one-figure prime.

    You too can use some short cuts to find my season ticket number.

    This puzzle is included in the book Brainteasers (2002).

    [teaser1659]

     
    • Jim Randell's avatar

      Jim Randell 9:48 am on 20 August 2019 Permalink | Reply

      If we denote the 5-digit number ABCDE, then we can see that all of (C, BCD, ABCDE) must be prime.

      And depending on whether we start truncating on the left or the right one of (CD, BCDE) or (BC, ABCD) must be prime.

      We can write these conditions as a set of alphametic constraints and use the general alphametic solver [[ SubstitutedExpression() ]] from the enigma.py library to solve them.

      This run file executes in 146ms.

      Run: [ @repl.it ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      --answer="ABCDE"
      
      # the digits are consecutive
      "max(A, B, C, D, E) - min(A, B, C, D, E) = 4"
      
      # odd length truncations
      "is_prime(ABCDE)"
      "is_prime(BCD)"
      "is_prime(C)"
      
      # even length truncations
      "(is_prime(BCDE) and is_prime(CD)) or (is_prime(ABCD) and is_prime(BC))"
      

      Solution: The ticket number is 68597.

      It is composed from the digits 5, 6, 7, 8, 9, and can be truncated to give the following primes: 68597, 8597, 859, 59, 5.

      Like

    • GeoffR's avatar

      GeoffR 12:47 pm on 20 August 2019 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn"; 
      
      % Pattern of Primes
      % C
      % CD
      % BCD
      % BCDE
      % ABCDE
      
      var 1..9:A; var 1..9:B; var 1..9:C; var 1..9:D; var 1..9:E;
      
      constraint all_different([A,B,C,D,E]);
      
      % the five digit prime number contain consecutive digits
      var set of int : s5 = {A,B,C,D,E};
      constraint max ([A,B,C,D,E]) - min([A,B,C,D,E]) == card(s5) - 1;
       
      predicate is_prime(var int: x) = 
      x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) 
      ((i < x) -> (x mod i > 0));
      
      var 10..99: CD = 10*C + D;
      var 100..999: BCD = 100*B + 10*C + D;
      var 1000..9999: BCDE = 1000*B + 100*C + 10*D + E;
      var 10000..99999: ABCDE = 10000*A + 1000*B + 100*C + 10*D + E;
      
      constraint is_prime(C) /\ is_prime(CD) /\ is_prime(BCD)
      /\ is_prime (BCDE) /\ is_prime(ABCDE);
      
      solve satisfy;
      
      output["Primes numbers are " ++ show(ABCDE) ++ ", " 
      ++ show(BCDE) ++ ", " ++ show(BCD) ++ ", " ++ show(CD) ++ 
      " and " ++ show(C) ];
      
      % Primes numbers are 68597, 8597, 859, 59 and 5
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:03 am on 15 August 2019 Permalink | Reply
    Tags: by: Ben Tarlow   

    Brainteaser 1657: Not a black and white case 

    From The Sunday Times, 12th June 1994 [link]

    Marvo has been up to his tricks again. He shuffled a normal pack of 52 cards and then looked at each one in turn without letting Alice see them. In each case she had to predict whether the card was red or black and Marvo gave no indication of whether Alice’s guesses were right or wrong. But after 51 guesses Alice had guessed “black” 26 times and “red” 25 times and Marvo announced that Alice had exactly 10 predictions wrong so far.

    (1) Which colour (if either) is more likely for Alice’s last card?

    Marvo repeated the game with Bob. After 51 guesses Bob too had guessed “black” 26 times and “red” 25 times and Marvo announced that Bob had exactly 11 predictions wrong so far.

    (2) Which colour (if either) is more likely for Bob’s last card?

    Marvo then repeated the game once more with Carol. After 51 guesses Carol too had guessed “black” 26 times and “red” 25 times and Marvo announced that Carol had got at least 10 predictions wrong so far.

    (3) Which colour (if either) is more likely for Carol’s last card?

    This puzzle is included in the book Brainteasers (2002). The puzzle text was, and the answers required were changed slightly, but the idea of the puzzle remains the same.

    [teaser1657]

     
    • Jim Randell's avatar

      Jim Randell 10:04 am on 15 August 2019 Permalink | Reply

      Alice has guessed “black” 26 times and “red” 25 times.

      If she has got n guesses wrong, then we can suppose b of the “black” guesses were wrong (i.e. they were actually red cards), and so (n − b) of the “red” guesses were wrong (i.e. they were actually black cards).

      So at this point the total number of actual black cards is:

      the (26 − b) cards that Alice correctly guessed as black; plus
      the (n − b) cards that were guessed as red, but were actually black.

      And similarly for red, giving:

      black = 26 + n − 2b
      red = 25 − n + 2b

      There can’t be more than 26 actual black cards, so:

      26 + n − 2b ≤ 26
      n ≤ 2b
      b ≥ n/2

      And there can’t be more than 26 actual red cards:

      25 − n + 2b ≤ 26
      2b ≤ n + 1
      b ≤ (n + 1)/2

      So:

      n/2 ≤ b ≤ (n + 1)/2

      Which fixes the value of b, depending on whether n is odd or even.

      In Alice’s case n = 10, so b = 5. Hence there have actually been 26 black cards and 25 red cards, so the remaining card has to be red.

      In Bob’s case n = 11, so b = 6. And there have actually been 25 black cards and 26 red cards, so the remaining card has to be black.

      And we see that if n is even then the remaining card must be red, and if it is odd the remaining card must be black.

      Solution: (1) Alice’s final card is certainly red. (2) Bob’s final card is certainly black.

      Carol has made at least 10 incorrect predictions. So she has made 10, 11, 12, …, 51, and depending on the exact number the remaining card is red, black, red, … black. There are 24 cases where the card must be red and 24 cases where the card must be black.

      We need to count the number of scenarios for each outcome.

      When n = 10 then b = 5 and there have been 26 black guesses (5 are guessed incorrectly) and 25 red guesses (5 are guessed incorrectly). So the number of ways of choosing the incorrect guesses are:

      C(26, 5) × C(25, 5)

      each one giving rise to the remaining card being red.

      When n = 11 and b = 6 we get:

      C(26, 6) × C(25, 5)

      each one giving rise to the remaining card being black.

      (I think if we are counting the total number of possible scenarios each of these numbers would be multiplied by an additional factor to account for the order of the cards and the guesses, but we don’t need to do that to compare the relative likelihood of red and black outcomes for the final card).

      This Python program counts up the number of possibilities. It runs in 85ms.

      Run: [ @repl.it ]

      from enigma import irange, C, fdiv, compare, printf
      
      r = b = 0
      for n in irange(10, 51):
        (k, p) = divmod(n, 2)
        if p == 0:
          r += C(26, k) * C(25, k)
        else:
          b += C(26, k + 1) * C(25, k)
      
      f = lambda x: fdiv(x, r + b)
      x = compare(r, b, ("black", "equal", "red"))
      printf("{x} [red = {r} ({fr:.4%}), black = {b} ({fb:.4%})]", fr=f(r), fb=f(b))
      

      Solution: (3) It is slightly more likely that Carol’s final card is red.

      The calculated figures give that Carol’s final card is red 50.0001% of the time and black 49.9999% of the time. So it is close.


      The puzzle as originally published in The Sunday Times asked for the outcome with the following numbers of wrong predictions: (a) exactly 10; (b) least 10; (c) at least 15.

      We have already covered (a) and (b), and for (c) the program can easily be modified to start counting from 15, rather than 10. The result is that it is slightly more likely that the card is black (50.02%) than red (49.98%).

      Liked by 1 person

  • Unknown's avatar

    Jim Randell 8:08 am on 11 August 2019 Permalink | Reply
    Tags:   

    Brainteaser 1649: Ages and ages! 

    From The Sunday Times, 17th April 1994 [link]

    Today is the birthday of both Mary and Nelly, so it is a good time to have a conundrum about their ages.

    When Mary was one third of the age she is today, Nelly was half the age that Mary was when Nelly was the age that Mary was when Nelly was the age that Mary was when Nelly was a quarter of the age she is today.

    If neither of them is yet eligible for an old-age pension, how old are they both today?

    This puzzle is included in the book Brainteasers (2002).

    [teaser1649]

     
    • Jim Randell's avatar

      Jim Randell 8:09 am on 11 August 2019 Permalink | Reply

      A lot of the puzzle text is concerned with the difference between the ages, so if we suppose N’s current age is n and M’s current age is (n + d), and then look at the expression:

      When Mary was one third of the age she is today, Nelly was half the age that Mary was when Nelly was the age that Mary was when Nelly was the age that Mary was when Nelly was a quarter of the age she is today

      We can successively refine it:

      → When Mary was (1/3)n + (1/3)d, Nelly was half the age that Mary was when Nelly was the age that Mary was when Nelly was the age that Mary was when Nelly was (1/4)n
      → When Mary was (1/3)n + (1/3)d, Nelly was half the age that Mary was when Nelly was the age that Mary was when Nelly was (1/4)n + d
      → When Mary was (1/3)n + (1/3)d, Nelly was half the age that Mary was when Nelly was (1/4)n + 2d
      → When Mary was (1/3)n + (1/3)d, Nelly was half (1/4)n + 3d
      → When Mary was (1/3)n + (1/3)d, Nelly was (1/8)n + (3/2)d
      → (1/3)n + (1/3)d = (1/8)n + (3/2)d + d
      → (5/24)n = (13/6)d
      → 5n = 52d

      So d must be a multiple of 5, and n must be a multiple of 52.

      For (n + d) < 65 we must have n = 52, d = 5.

      Solution: Nelly is 52. Mary is 57.

      Liked by 1 person

  • Unknown's avatar

    Jim Randell 8:36 am on 6 August 2019 Permalink | Reply
    Tags:   

    Brainteaser 1635: Double anagram 

    From The Sunday Times, 9th January 1994 [link]

    In the woodwork class the ABLEST students made STABLE TABLES.

    In the arithmetic class the cleverest students took those three six-letter words which were anagrams of each other, and they then assigned a different digit to each of the six letters involved. Substituting letters for digits then gave them three six-figure numbers.

    They found that one of the numbers was the sum of the other two. Furthermore, no matter what alternative substitution of digits they had used, they could never have achieved this coincidence with a lower sum.

    (a) Which word was the sum?
    (b) What was its numerical value?

    This puzzle is included in the book Brainteasers (2002). The text was changed slightly, but the puzzle remains the same.

    [teaser1635]

     
    • Jim Randell's avatar

      Jim Randell 8:37 am on 6 August 2019 Permalink | Reply

      This program uses the [[ SubstitutedExpression() ]] solver from the enigma.py library to solve the alphametic problem, and an [[ Accumulator() ]] object to find the smallest solution. It runs in 299ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, Accumulator, sprintf as f, join, printf)
      
      # the three words
      words = ("ABLEST", "STABLE", "TABLES")
      
      # the alphametic puzzle
      ws = join(words, sep=",", enc="()")
      p = SubstitutedExpression(f("sum({ws}) == 2 * max({ws})"), answer=ws)
      
      # look for a minimal solution
      r = Accumulator(fn=min)
      
      # solve the alphametic
      for ans in p.answers(verbose=0):
        # find the result of the sum
        s = max(ans)
        # and which word is the result
        i = ans.index(s)
        r.accumulate_data(s, i)
      
      # output the minimal solution
      printf("{word} = {r.value} [of {r.count} possible sums]", word=words[r.data])
      

      Solution: TABLES = 417582.

      The corresponding alphametic sum is: ABLEST + STABLE = TABLES.

      There are 18 possible solutions to this alphametic.

      Like

    • GeoffR's avatar

      GeoffR 1:29 pm on 6 August 2019 Permalink | Reply

      I tried the three possible addition sums, looking for the smallest total. Two of these sums proved unsatisfiable. The third addition sum gave two possible values for TABLES, the smallest of which agreed with Jim’s solution.

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9: A; var 0..9: B; var 0..9: L;
      var 0..9: E; var 1..9: S; var 1..9: T;
      
      constraint all_different([A, B, L, E, S, T]);
      
      var 100000..999999: ABLEST = 100000*A + 10000*B + 1000*L + 100*E + 10*S + T;
      var 100000..999999: STABLE = 100000*S + 10000*T + 1000*A + 100*B + 10*L + E;
      var 100000..999999: TABLES = 100000*T + 10000*A + 1000*B + 100*L + 10*E + S;
      
      %constraint ABLEST == STABLE + TABLES;
      %solve minimize(ABLEST);
      %output ["ABLEST = " ++ show(ABLEST) ];  % =====UNSATISFIABLE=====
      
      %constraint STABLE == ABLEST + TABLES;
      %solve minimize(STABLE);
      %output ["STABLE = " ++ show(STABLE) ];  % =====UNSATISFIABLE=====
      
      constraint TABLES == ABLEST + STABLE;
      solve minimize(TABLES);
      output ["TABLES = " ++ show(TABLES)  ++ " = " ++ show(ABLEST) ++ " + " ++ show(STABLE)];
      
      % TABLES = 428571 = 285714 + 142857
      % ----------
      % TABLES = 417582 = 175824 + 241758  << smallest answer for value of TABLES
      % ----------
      % ==========
      
      
      

      Like

      • John Crabtree's avatar

        John Crabtree 6:21 pm on 8 August 2019 Permalink | Reply

        With TABLES as the sum, ABLE = 999(T-S) – 100S – 10T. This enables the desired solution, as well as all of the possible 18 found by Jim, to be easily found.

        Like

    • Frits's avatar

      Frits 2:36 pm on 27 July 2020 Permalink | Reply

      Substituted expression which returns only one solution (minimized on first part of answer):

      # The following function has been manually added to enigma.py
      #
      # def substituted_expression_minimum(*args, **kw):
      #   if 'verbose' not in kw: kw['verbose'] = 0
      #   answs = []
      #   for r in SubstitutedExpression(*args, **kw).solve():
      #     answs.append(r)
      #   answs.sort(key=lambda x: x[1])  
      #   if len(answs) > 0:
      #       yield answs[0]
      
      
      from enigma import substituted_expression_minimum, printf, irange
      
      # the alphametic puzzle
      p = substituted_expression_minimum(\
          [\
          # if one element in (X,Y,Z) is sum of 2 others then sum(X,Y,Z) = 2*max(X,Y,Z)
          # in this way we don't have to specify it is Z=X+Y, Y=X+Z or X=Y+Z 
          "TABLES + ABLEST + STABLE == 2 * max(TABLES, ABLEST, STABLE)",\
          "max(TABLES, ABLEST, STABLE) = HIGNUM",\
          "not(is_duplicate(TABLES))",\
          "not(is_duplicate(ABLEST))",\
          "not(is_duplicate(STABLE))"],\
          distinct="",   # needed for HIGNUM \   
          answer="HIGNUM, ABLEST, STABLE, TABLES",\
          verbose=0)      
                                
      for (_, ans) in p: 
        if ans[1] == ans[0]: printf("ABLEST is the sum with value {ans[1]}")
        if ans[2] == ans[0]: printf("STABLE is the sum with value {ans[2]}")
        if ans[3] == ans[0]: printf("TABLES is the sum with value {ans[3]}")
      

      Like

      • Frits's avatar

        Frits 3:12 pm on 27 July 2020 Permalink | Reply

        @Jim:

        Any idea why I needed the HIGNUM field?

        p = substituted_expression_minimum(\
            [\
            "TABLES + ABLEST + STABLE == 2 * max(TABLES, ABLEST, STABLE)"],\
            answer="TABLES + ABLEST + STABLE, ABLEST, STABLE, TABLES",\
            verbose=0)   
        

        Using this setup I got no results (even with distinct=””).

        Like

        • Jim Randell's avatar

          Jim Randell 3:33 pm on 27 July 2020 Permalink | Reply

          @Frits: I think you get no answers because you are checking to see when one of the summands is equal to the sum total (which is never).

          Try using:

          answer="(max(TABLES, ABLEST, STABLE), ABLEST, STABLE, TABLES)"
          

          BTW: You don’t need to modify the enigma.py library, you can just define the [[ substituted_expression_minimum ]] function in your own program.

          Like

    • Frits's avatar

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

      @Jim: Thanks for the internal accumulate function

      from enigma import SubstitutedExpression, printf
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          # if one element in (X,Y,Z) is sum of 2 others then sum(X,Y,Z) = 2*max(X,Y,Z)
          # in this way we don't have to specify it is Z=X+Y, Y=X+Z or X=Y+Z 
          "TABLES + ABLEST + STABLE == 2 * max(TABLES, ABLEST, STABLE)",
          "not(is_duplicate(TABLES))",
          "not(is_duplicate(ABLEST))",
          "not(is_duplicate(STABLE))"
        ],
        answer="(TABLES + ABLEST + STABLE)/2, ABLEST, STABLE, TABLES",
        accumulate=min,
        verbose=0)      
          
      # solve the puzzle
      r = p.run()
      
      # Print answer
      lst = ["ABLEST", "STABLE", "TABLES"]
      cnt = 0
      for ans in r.accumulate:
          if cnt == 0: hghnum = ans
          else:  
            if ans == hghnum: printf("{lst[cnt-1]} is the sum with value {ans}")
          cnt += 1 
      

      Like

  • Unknown's avatar

    Jim Randell 12:40 pm on 28 July 2019 Permalink | Reply
    Tags:   

    Brainteaser 1633: New Year’s resolution 

    From The Sunday Times, 26th December 1993 [link]

    My New Year’s resolution is that this year I will remember my father’s birthday. To remind me I devised this formula: if I take the factorial of the number on the calendar representing the month in which he was born (1 for January to 12 for December) and multiply this by the square of the day of the month of his birth (1 to 31), the four digit result is the year when he was born.

    [Note, for example, that “6 factorial” is 6×5×4×3×2×1 = 720.]

    I then realised that this is no use for determining his birthday as even my own date of birth fits the formula.

    Can you tell me what is my father’s date of birth?

    This puzzle is included in the book Brainteasers (2002) under the title of “Birthday resolution”. The text was changed slightly, but the puzzle remains the same.

    [teaser1633]

     
    • Jim Randell's avatar

      Jim Randell 12:41 pm on 28 July 2019 Permalink | Reply

      This Python program finds day/month/year combinations that satisfy the formula, where the year is in the range [1850, 1993].

      It runs in 84ms.

      Run: [ @repl.it ]

      from enigma import irange, factorial, printf
      
      # number of possible days in each month
      days = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
      
      # consider day, month, formula values
      for (m, dm) in enumerate(days, start=1):
        f = factorial(m)
        for d in irange(1, dm):
          y = f * d * d
          if y < 1850: continue
          if y > 1993: break
          printf("d={d} m={m} -> y={y}")
      

      Solution: The father’s birthdate is 4th May 1920.

      There are 3 day/month combinations that give a reasonable value for the year.

      These are:

      day=18, month=3 → year=1944
      day=9, month=4 → year=1944
      day=4, month=5 → year=1920

      So the setters birthdate must be one of the 1944 dates (18th March 1944, 9th April 1944) and his fathers birthdate must be 4th May 1920.

      But it seems to me that the setter would probably be able to remember that his father was not born in 1944, so he can use the formula to determine his fathers birthday.

      The setter is obviously not a fan of Star Wars, otherwise he would find it easy to remember his father’s birthday.

      Like

  • Unknown's avatar

    Jim Randell 9:18 am on 23 July 2019 Permalink | Reply
    Tags:   

    Brainteaser 1631: Ali Baba’s boxes 

    From The Sunday Times, 12th December 1993 [link]

    The royal jewellers Fabulé make standard-sized golden cubic boxes which are diamond-encrusted on the outside. They make these from large sheets of gold which have been encrusted with diamonds on one side and they then cut out the required shapes to make the boxes.

    For example, the 3-by-4 cruciform shown on the left below provides a shape which folds up into a cubic box, but the strip on the right does not.

    Recently thieves broke into the Fabulé workshops and stole various cut-out pieces of the diamond-encrusted sheeting. They did not realise that in fact they had taken the mis-cuts: all the pieces that consisted of six squares but none would actually fold into a box. And their haul consisted of one piece of each possible faulty shape.

    How many pieces did they steal?

    This puzzle is included in the book Brainteasers (2002). The text was changed slightly, but the substance of the puzzle remains the same.

    [teaser1631]

     
    • Jim Randell's avatar

      Jim Randell 9:20 am on 23 July 2019 Permalink | Reply

      It seems like a bit of a wasteful manufacturing process, but it does mean that the shapes cannot be turned over. (Although card with different coloured sides would have sufficed).

      I enjoyed programming a constructive solution for this puzzle.

      This Python program generates possible shapes that consist of 6 connected squares, and determines how many different shapes there are when translation and rotation are allowed (but not reflection). It then takes those shapes and attempts to wrap each one around a cube to find out which of them represent the net of a cube.

      It runs in 982ms

      Run: [ @repl.it ]

      from enigma import Accumulator, ordered, uniq, update, first, join, printf
      
      # slide shape against x=0, y=0 axes
      def slide(shape):
        min_x = min(x for (x, y) in shape)
        min_y = min(y for (x, y) in shape)
        return tuple((x - min_x, y - min_y) for (x, y) in shape)
      
      # rotate shape through 90 degrees
      def rotate(shape):
        return tuple((y, -(x + 1)) for (x, y) in shape)
      
      # find a canonical representation of a shape
      def canonical(shape):
        r = Accumulator(fn=min)
        for i in (0, 1, 2, 3):
          # slide shape against the axes
          shape = slide(shape)
          r.accumulate(ordered(*shape))
          # rotate shape
          if i < 3: shape = rotate(shape)
        # return the minimim value
        return r.value
      
      # find squares adjacent to p
      def adj(p):
        (x, y) = p
        return ((x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1))
      
      # generate shapes with k additional squares
      def generate(k, shape):
        if k == 0:
          yield shape
        else:
          # choose a square to extend
          for p in shape:
            for q in adj(p):
              if q not in shape:
                yield from generate(k - 1, shape + [q])
      
      # roll a cube to the ajacent squares
      def roll(p, fs):
        ((x, y), (U, D, F, B, L, R)) = (p, fs)
        return (
           ((x - 1, y), (R, L, F, B, U, D)), # L
           ((x + 1, y), (L, R, F, B, D, U)), # R
           ((x, y - 1), (B, F, U, D, L, R)), # F
           ((x, y + 1), (F, B, D, U, L, R)), # B
        )
      
      # wrap a cube with shape
      # shape = the shape we are trying to wrap
      # ss = remaining unwrapped squares
      # m = map from square -> face
      # cube = current cube orientation (U, D, F, B, L, R)
      def wrap(shape, m):
        # are we done?
        n = len(shape)
        if len(m.keys()) == n:
          d = dict((k, v[1]) for (k, v) in m.items())
          if len(set(d.values())) == n:
            yield d
        else:
          # roll the cube
          for (p, fs) in m.items():
            for (q, gs) in roll(p, fs):
              if q in shape and q not in m:
                yield from wrap(shape, update(m, [(q, gs)]))
      
      # find the number of shapes made from 6 squares
      (t, n) = (0, 0)
      for s in uniq(canonical(s) for s in generate(5, [(0, 0)])):
        t += 1
        # can we form the shape into a cube?
        r = first(wrap(s, { s[0]: "UDFBLR" }))
        if r:
          n += 1
          printf("[{t}: {s} -> {r}]", r=join(r[0][p] for p in s))
        else:
          printf("[{t}: {s}]")
      
      printf("{n} of {t} shapes form cubes")
      

      Solution: There are 40 shapes that cannot be folded into a cube.

      We can make 60 different shapes out of 6 joined squares (where reflection is not allowed). (See OEIS A000988 [ @oeis ]).

      And there are 11 shapes that can be folded to make a cube (if reflection is allowed):

      The two of these on the left are achiral (i.e. their mirror image can be formed by rotating the piece), leaving 9 that are chiral (i.e. the mirror image cannot be formed by rotation of the piece). So we need to make mirror images of the 9 chiral shapes to give 20 shapes altogether that can be folded into a cube (where reflection is not allowed).

      The remaining 40 shapes cannot be folded into a cube.

      See: [ @wikipedia ], which tells us there are 60 one-sided hexonimoes and gives the 11 nets of the cube. Which is enough information to determine the answer.


      The program is constructive so can easily be augmented to plot the 60 shapes, and colour in those which can be folded into cubes. I used my simple plotting library [ @github ].

      Run: [ @repl.it ]

      The output looks like this:

      The colours show how the shape can be folded to give the colouring of a standard Rubik’s Cube.

      Like

  • Unknown's avatar

    Jim Randell 8:50 am on 16 July 2019 Permalink | Reply
    Tags: by: Alan Gebbie   

    Brainteaser 1629: Tangle o’ the aisles 

    From The Sunday Times, 28th November 1993 [link]

    It was a big day in Obness. Five girls, each from a different Scottish island, each marries the only sibling of one of the others. It was a big day for one of the licensed retailers too: ten bottles of good malt sold with all the brothers-in-law receiving one each from the respective grooms.

    Ann’s husband’s sister’s husband is married to the wife of the Harris girl’s brother. Bella’s husband’s sister’s husband’s sister’s husband’s sister’s husband’s sister is Emma. Celia and Delia are sisters-in-law. The Lewis girl’s brother’s wife is the Mull girl’s husband’s sister. Celia’s husband’s brothers-in-law are the Harris girl’s brother and the brother of the girl from Skye. Delia has a sister-in-law from Iona.

    For each girl find out:

    (a) who married her brother?
    (b) which island did she come from?

    This puzzle is included in the book Brainteasers (2002).

    Although the puzzle states that the girls are from different islands, Lewis and Harris are parts of the same island (although they are often referred to as if they were separate islands).

    [teaser1629]

     
    • Jim Randell's avatar

      Jim Randell 8:51 am on 16 July 2019 Permalink | Reply

      The brothers names aren’t mentioned, so I’m going to assume they share their initial with their sister and call them: Alan, Bruce, Callum, Dougal and Euan.

      We can then refer to the girls and the boys by their initial, and the mapping between siblings is the identity map.

      This Python program runs in 87ms.

      Run: [ @replit ]

      from enigma import (subsets, ordered, printf)
      
      # names (of girls and their brothers)
      names = "ABCDE"
      
      # islands
      islands = "HILMS"
      
      # marries: girl -> boy
      for s in subsets(names, size=len(names), select="P"):
        m = dict(zip(names, s))
        # no-one marries their own brother
        if any(g == b for (g, b) in m.items()): continue
      
        # "B's husband's sister's husband's sister's husband's sister's husband's sister is E"
        if not (m[m[m[m['B']]]] == 'E'): continue
      
        # "C and D are sisters-in-law"
        if not (m['C'] == 'D' or m['D'] == 'C'): continue
      
        # home: island -> girl (or boy)
        for s in subsets(names, size=len(names), select="P"):
          h = dict(zip(islands, s))
      
          # "A's husband's sister's husband is married to the wife of the H girl's brother"
          # X is married to the wife of X, so:
          # A's husband's sister's husband is the H girl's brother
          # A's husband's sister's husband is from H    
          if not (h['H'] == m[m['A']]): continue
      
          # "The L girl's brother's wife is the M girl's husband's sister"
          if not (h['L'] == m[m[h['M']]]): continue
      
          # "C's husband's brothers-in-law are the H girl's brother and the brother of the girl from S"
          if not (ordered('C', m[m['C']]) == ordered(h['H'], h['S'])): continue
      
          # "D has a sister-in-law from Iona"
          if not (h['I'] == m['D'] or m[h['I']] == 'D'): continue
          
          # output solution
          rev = lambda d: dict((v, k) for (k, v) in d.items())
          (rh, rm)  = (rev(h), rev(m))
          for g in names:
            printf("girl {g}: from {i}; her brother married {x}", i=rh[g], x=rm[g])
          printf()
      

      Solution: The answers are:

      Ann is from Iona. Her brother (Alan) married Bella.
      Bella is from Skye. Her brother (Bruce) married Emma.
      Celia is from Harris. Her brother (Callum) married Delia.
      Delia is from Mull. Her brother (Dougal) married Ann.
      Emma is from Lewis. Her brother (Euan) married Celia.

      So the weddings (using the names I made up) are:

      Ann (Iona) m. Dougal (Mull)
      Bella (Skye) m. Alan (Iona)
      Celia (Harris) m. Euan (Lewis)
      Delia (Mull) m. Callum (Harris)
      Emma (Lewis) m. Bruce (Skye)

      So Emma’s brother (Euan) and his wife, Celia, are actually from different parts of the same island – the island of Lewis and Harris [ link ].

      Like

    • Lise Andreasen's avatar

      Lise Andreasen 5:17 am on 3 June 2024 Permalink | Reply

      The pair Lewis and Skye are not neighbors. (A girl from one didn’t get married to the boy from the other.)
      The pair Harris and Skye are not neighbors.
      This can be done in 4 ways:

      L H M S I
      L S M H I
      M H L S I
      M S L H I

      In the 2nd and 4th row, A, C and D occupy the 1st, 2nd and 4th position. But then B and E can’t be sisters in law. Rejected.

      In the 1st row, it’s L girl, L boy, H girl, H boy etc. A is from I, D is from L, C is from H. But then A won’t get the right relationship to H. Rejected.

      Only the 3rd row is left. And it works.

      Like

  • Unknown's avatar

    Jim Randell 9:53 am on 11 July 2019 Permalink | Reply
    Tags:   

    Brainteaser 1625: Jolly sticks 

    From The Sunday Times, 31st October 1993 [link]

    In a qualifying round of the European Ladies Hockey Cup held recently, each of the four participating countries played each of the others once. There were no draws. The final results looked like this:

    If I gave you the score of the Holland vs. France game you could deduce the scores of all the other games. But if instead I gave you the score of any one of the other games you would not be able to do so.

    This puzzle is included in the book Brainteasers (2002), where it appears under the title “Qualifier“. The wording above is mostly taken from original puzzle, but I have slightly changed it using the book for clarity.

    [teaser1625]

     
    • Jim Randell's avatar

      Jim Randell 9:55 am on 11 July 2019 Permalink | Reply

      The following program uses the [[ Football() ]] helper class from the enigma.py library. It runs in 80ms.

      Run: [ @repl.it ]

      from enigma import (Football, filter_unique, printf)
      
      # scoring system (all games played)
      football = Football(games='wdl')
      
      # match outcomes are determined by the table
      HS = HF = HW = SF = SW = FW = 'w'
      
      # record possible scores
      ss = list()
      
      # find scores in F's matches
      for (sHF, sSF, sFW) in football.scores([HF, SF, FW], [1, 1, 0], 2, 4):
      
        # find scores in S's remaining matches
        for (sHS, sSW) in football.scores([HS, SW], [1, 0], 8, 3, [sSF], [0]):
      
          # and the remaining match (using H's goals)
          for (sHW,) in football.scores([HW], [0], 12, 1, [sHS, sHF], [0, 0]):
      
            # check W's goals
            if not (football.goals([sHW, sSW, sFW], [1, 1, 1]) == (1, 15)): continue
      
            printf("[HS={HS}:{sHS} HF={HF}:{sHF} HW={HW}:{sHW} SF={SF}:{sSF} SW={SW}:{sSW} FW={FW}:{sFW}]")
            ss.append((sHS, sHF, sHW, sSF, sSW, sFW))
      
      # determine unique results by each individual match
      vs = [None] * 6
      for (i, _) in enumerate(vs):
        (vs[i], _) = filter_unique(ss, (lambda r: r[i]))
      
      # solution is unique by HF (i=1) but not by any of the others
      rs = set(vs[1]).difference(*(vs[:1] + vs[2:]))
      
      # output solutions
      for (sHS, sHF, sHW, sSF, sSW, sFW) in rs:
        printf("HS={HS}:{sHS} HF={HF}:{sHF} HW={HW}:{sHW} SF={SF}:{sSF} SW={SW}:{sSW} FW={FW}:{sFW}")
      

      Solution: The scores were as follows:

      Holland vs Spain = 2-0
      Holland vs France = 2-1
      Holland vs Wales = 8-0
      Spain vs France = 2-0
      Spain vs Wales = 6-1
      France vs Wales = 1-0

      Like

  • Unknown's avatar

    Jim Randell 9:07 am on 30 June 2019 Permalink | Reply
    Tags:   

    Brainteaser 1611: Scrambled egg 

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

    When HUMPTY fell he broke into six pieces, each comprising one letter from his name, and they landed in a row to read as a rubbish six-letter word with none of the six piece in the correct position.

    The King’s horses tried to put HUMPTY together again by arranging the pieces in the reverse order, which meant that more than one piece was in the right place.

    The King’s men then split that arrangement into two threes and placed the last three pieces before the first. This gave a higher number of letters in the correct place.

    What was the arrangement of letters just after HUMPTY fell?

    This puzzle is included in the book Brainteasers (2002). The wording above is taken from the book. It is slightly changed from the original puzzle.

    [teaser1611]

     
    • Jim Randell's avatar

      Jim Randell 9:07 am on 30 June 2019 Permalink | Reply

      This Python program tries all possible derangements of the letters, and checks to see if the conditions are satisfied. It runs in 85ms.

      Run: [ @repl.it ]

      from enigma import (subsets, join, printf)
      
      # word with letters in their correct positions
      word = "HUMPTY"
      
      # count the number of correct letters
      def correct(w):
        return sum(a == b for (a, b) in zip(w, word))
      
      # find derangements of the word
      for w in subsets(word, size=len(word), select='P'):
        if correct(w) > 0: continue
      
        # reversing gives more than 1 piece in the correct position
        w1 = w[::-1]
        k1 = correct(w1)
        if not (k1 > 1): continue
      
        # putting the last 3 before the first 3 gives even more in the correct position
        w2 = w1[3:] + w1[:3]
        k2 = correct(w2)
        if not (k2 > k1): continue
      
        # output solution
        printf("{w} [{w1} -> {k1}, {w2} -> {k2}]", w=join(w), w1=join(w1), w2=join(w2))
      

      Solution: The arrangement after HUMPTY fell was MTHYUP.

      Like

  • Unknown's avatar

    Jim Randell 12:41 pm on 13 June 2019 Permalink | Reply
    Tags: by: Simon Porter   

    Brainteaser 1610: Word game 

    From The Sunday Times, 18th July 1993 [link]

    An unusual card game consisted of 12 cards each with a different well-known word of four letters on it. No letter appeared on more than three cards and no two cards had more than one letter in common. The winner was the first person to collect among his or her cards a set of three cards with the same letter on each.

    In one game against George I started and after two goes I had chosen:

    ZEST
    CHEW

    while George had picked up:

    DAWN
    RILE

    (the second to stop me getting a set of three with a common “E“).

    For my third I chose:

    QUAY

    and after George had selected his third card he told me that whatever card I now chose he would certainly win on this next turn.

    I picked up:

    SHIN

    for my fourth card and, as predicted, George chose:

    FLOW

    giving him a winning set.

    The unused cards were:

    CURT
    JUGS
    MOTH
    PARK

    what was George’s third card?

    This puzzle is included in the book Brainteasers (2002). The wording above is taken from the book. It is slightly changed from the original puzzle.

    [teaser1610]

     
    • Jim Randell's avatar

      Jim Randell 12:42 pm on 13 June 2019 Permalink | Reply

      This Python program extracts candidate 4-letters words from the supplied word list, selects those that are permissible according to the given rules, and then looks at each one as a possible third card for George.

      It runs in 391ms, although the run time will depend on the word list used. I used the [[ sowpods.txt ]] word list from Enigma 1195 and Enigma 288a.

      Run: [ @replit ]

      from enigma import (union, printf)
      
      # the known cards
      player1 = [ 'ZEST', 'CHEW', 'QUAY' ]
      player2 = [ 'DAWN', 'RILE' ] # and a mystery card
      remaining = [ 'SHIN', 'FLOW', 'CURT', 'JUGS', 'MOTH', 'PARK' ]
      cards = player1 + player2 + remaining
      
      # "no letter appears on more than three cards"
      # so any letter that appears on three of the known cards cannot appear
      # on the missing card
      inv = set(x for x in union(cards) if sum(x in c for c in cards) > 2)
      
      # look for 4 letter words in the word list
      path = "sowpods.txt"
      words = list()
      for w in open(path).readlines():
        w = w.strip().upper()
        if len(w) == 4:
          # discard words containing letters already on 3 cards
          if inv.intersection(w): continue
          # discard words with more than one letter in common with a known card
          s = set(w)
          if any(len(s.intersection(c)) > 1 for c in cards): continue
          words.append(w)
      printf("[{n} candidate words found]", n=len(words))
      
      # find winning cards from rs for hand hs
      def win(hs, rs):
        # pick up a card
        for w in rs:
          # have we won?
          # i.e. are there two cards in the hand containing a letter on the card?
          for x in set(w):
            if sum(x in h for h in hs) > 1:
              yield w
      
      # consider each candidate word as a possible mystery card
      for w in words:
        # George must be able to win in (at least) 2 ways, one of them using FLOW
        ss = list(win(player2 + [w], remaining))
        if len(ss) > 1 and 'FLOW' in ss:
          printf("{w} -> {ss}", ss=sorted(ss))
      

      Solution: George’s third card was LYNX.

      George is holding:

      DAWN
      RILE
      LYNX

      There are two cards with L and two cards with N.

      Picking FLOW will give a set of 3 cards with L, and picking SHIN will give a set of 3 cards with N.

      Like

  • Unknown's avatar

    Jim Randell 10:57 am on 2 June 2019 Permalink | Reply
    Tags:   

    Brainteaser 1590: Demonoes 

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

    Damien deleted a dot from one of the dominoes so that when I laid them out randomly to form a rectangle I ended up with the following arrangement:

    Fill in the outlines of the dominoes.

    This puzzle is included in the book Brainteasers (2002). The wording above is taken from the book. It is slightly changed from the original puzzle.

    [teaser1590]

     
    • Jim Randell's avatar

      Jim Randell 10:58 am on 2 June 2019 Permalink | Reply

      The puzzle uses a standard set of dominoes, so there should be 8 instances of each digit.

      In the supplied grid there 9 0’s and only 7 1’s, so one of the 0’s should be a 1.

      I adapted the code used in Enigma 179, Enigma 303 and Enigma 342 to give a generic solver for this type of problem, which also includes code to output solution grids.

      This Python code runs in 204ms.

      Run: [ @repl.it ]

      from enigma import update, printf
      from domino import DominoGrid
      
      grid = [
        6, 2, 4, 2, 4, 4, 5, 4,
        3, 0, 3, 5, 5, 3, 6, 6,
        4, 4, 3, 5, 5, 2, 2, 3,
        3, 6, 3, 0, 0, 1, 1, 6,
        1, 5, 6, 6, 0, 2, 1, 4,
        1, 4, 2, 5, 0, 2, 1, 3,
        1, 2, 0, 0, 0, 5, 0, 6,
      ]
      
      # look for 0's in the grid
      for (i, x) in enumerate(grid):
        if x != 0: continue
        # try to fit the dominoes with the 0 replaced by a 1
        p = DominoGrid(8, 7, update(grid, [(i, 1)]))
        for s in p.solve():
          printf("[@{i}: 0 -> 1]\n")
          p.output_solution(s)
      

      Solution: The dominoes are arranged thus:

      There are two 0-5 dominoes shown highlighted in yellow. One of them should be a 1-5 domino (although we don’t know which one).

      It is likely I will fold the [[ DominoGrid() ]] class into the enigma.py library in a future release.

      Like

      • John Crabtree's avatar

        John Crabtree 5:09 am on 13 August 2020 Permalink | Reply

        The 4-0 is unique, and then 3-0 is unique. With some perseverance, one gets to the grid shown by Jim.

        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