Design a site like this with WordPress.com
Get started

Updates from December, 2022 Toggle Comment Threads | Keyboard Shortcuts

  • Jim Randell 8:51 am on 4 December 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 252: Blocks 

    From The Sunday Times, 27th February 1966 [link]

    “See these eleven blocks?”, says a so-called friend. “Four of them of 8 inch thickness, two of them of 4 inch thickness, three of 3 inch and two of 1 inch thickness”.

    “Pile them in a column 51 inches high with a 3 inch block at the bottom so that, remaining in position, individual blocks or combinations of adjacent blocks can be used to measure every thickness in exact inches from 1 inch to 48 inches”.

    In what order do they stand?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser252]

     
    • Jim Randell 8:54 am on 4 December 2022 Permalink | Reply

      (See also: Teaser 119, Teaser 560).

      This Python program performs an exhaustive search of all possible stacks of blocks.

      It runs in 268ms.

      Run: [ @replit ]

      from enigma import (multiset, subsets, csum, irange, printf)
      
      # the blocks
      blocks = multiset.from_dict({8: 4, 4: 2, 3: 3, 1: 2})
      assert blocks.sum() == 51
      
      # extract a 3 block
      blocks.remove(3)
      
      # and consider possible arrangements of the remaining blocks
      for bs in subsets(blocks, size=len, select="mP", fn=list):
        bs.insert(0, 3)
        # find what thicknesses can be measured
        ts = set(b - a for (a, b) in subsets(csum(bs, empty=1), size=2))
        # can we measure all values from 1 to 48
        if all(x in ts for x in irange(1, 48)):
          printf("{bs}")
      

      Solution: The pile of blocks (bottom to top) is one of the following two sequences:

      (3, 4, 3, 8, 8, 8, 8, 4, 1, 1, 3)
      (3, 1, 1, 4, 8, 8, 8, 8, 3, 4, 3)

      One being the reverse of the other.

      Like

  • Jim Randell 9:47 am on 27 November 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 222: British triangles 

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

    British Triangles, naturally, stand on horizontal bases with their points upwards. All their sides, never more than a  sensible 60 inches, and their heights measure an exact number of inches. No B.T. is isosceles or right angled.

    You can often put more than one B.T. on a common base. On a base of 28 inches 8 B.T.s are erected.

    What are their heights?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser222]

     
    • Jim Randell 9:47 am on 27 November 2022 Permalink | Reply

      If the 3 sides of a triangle are a, b, c, then the area A is given by Heron’s formula:

      s = (a + b + c) / 2
      A = √(s(s − a)(s − b)(s − c))

      And if the height (from side a) is h, then we also have:

      A = ha / 2

      Hence the height h can be determined from the sides a, b, c:

      h = 2A / a
      h = (2/a)√(s(s − a)(s − b)(s − c))

      This Python program considers possible values for the 2nd and 3rd sides of the triangle, and then looks for values where the height is an integer.

      It runs in 53ms. (Internal runtime is 1.8ms).

      Run: [ @replit ]

      from enigma import (irange, div, is_square, subsets, sq, printf)
      
      # check for a viable triangle
      def is_triangle(a, b, c):
        # check triangle inequalities
        if not (a + b > c and a + c > b and b + c > a): return
        # no triangle is isosceles
        if len({a, b, c}) < 3: return
        # no triangle is right angled
        sqs = (sq(a), sq(b), sq(c))
        if sum(sqs) == 2 * max(sqs): return
        # looks OK
        return (a, b, c)
      
      # calculate integer height from side a
      def height(a, b, c):
        p = a + b + c
        x = div(p * (p - 2 * a) * (p - 2 * b) * (p - 2 * c), 4)
        if not x: return
        x = is_square(x)
        if not x: return
        h = div(x, a)
        return h
      
      # base
      a = 28
      # consider possible integer length for the remaining sides, b, c
      for (b, c) in subsets(irange(1, 60), size=2, select="R"):
        if not is_triangle(a, b, c): continue
        # calculate the height of the triangle
        h = height(a, b, c)
        if not h: continue
        # output viable triangles
        printf("a={a} b={b} c={c} -> h={h}")
      

      Solution: The heights of the triangles are 9″ (2 triangles), 15″ (4 triangles), 24″ (2 triangles).

      The four triangles found by the program are shown below. And they can be mirrored to produce the remaining four triangles.

      Like

    • Hugh Casement 7:41 am on 28 November 2022 Permalink | Reply

      Alternatively we can think of it as two right-angled triangles joined together.
      If their bases are d and e, then d² = b² – h² and e² = c² – h².
      d + e, or |d – e| in the case of the obtuse-angled triangles, must equal a = 28.
      b must not equal c, for then the overall triangle would be isosceles
      (that is the case when h = 48, b = c = 50, and d = e = 14).

      Like

      • Hugh Casement 9:09 am on 28 November 2022 Permalink | Reply

        Joined together along the line of their common height h, of course.
        A base a = 21 also allows eight resultant triangles, including reflections.

        Like

  • Jim Randell 11:32 am on 20 November 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 204: Logical will 

    From The Sunday Times, 21st March 1965 [link]

    Uncle George occasionally wasted time on Brain-teasers, and it was expected that his will would take an awkward form. But his five nephews were surprised to learn, after his death, that he had dictated his will to a solicitor who had no use for punctuation. The will ran as follows:

    maurice is not to sing at my funeral service if stephen receives the envelope containing three thousand pounds alec is to have five thousand pounds if thomas receives less than maurice alec is to have exactly twice what nigel has if thomas does not receive the envelope containing a thousand pounds stephen is to have exactly four thousand pounds

    The nephews were confident that Uncle George always uttered complete sentences, none of which contained more than one conditional clause. They also felt sure that what he had said to the solicitor allowed one way, and one way only, of distributing the five envelopes (containing £5000, £4000, £3000, £2000, and £1000), which Uncle George had left for them.

    Who receives each envelope? And may Maurice sing at the funeral service?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser204]

     
    • Jim Randell 11:33 am on 20 November 2022 Permalink | Reply

      There are four ways we can parse the will into statements, each consisting of 4 statements, 3 of which are conditional:

      [1]
      (maurice is not to sing at my funeral service)
      (stephen receives £3000) → (alec is to have £5000)
      (thomas receives less than maurice) → (alec is to have exactly twice what nigel has)
      (thomas does not receive £1000) → (stephen is to have exactly £4000)

      [2]
      (maurice is not to sing at my funeral service) ← (stephen receives £3000)
      (alec is to have £5000)
      (thomas receives less than maurice) → (alec is to have exactly twice what nigel has)
      (thomas does not receive £1000) → (stephen is to have exactly £4000)

      [3]
      (maurice is not to sing at my funeral service) ← (stephen receives £3000)
      (alec is to have £5000) ← (thomas receives less than maurice)
      (alec is to have exactly twice what nigel has)
      (thomas does not receive £1000) → (stephen is to have exactly £4000)

      [4]
      (maurice is not to sing at my funeral service) ← (stephen receives £3000)
      (alec is to have £5000) ← (thomas receives less than maurice)
      (alec is to have exactly twice what nigel has) ← (thomas does not receive £1000)
      (stephen is to have exactly £4000)

      This Python program investigates each interpretation, rejecting those that do not have a single way of distributing the money.

      It runs in 54ms. (Internal runtime is 866µs).

      Run: [ @replit ]

      from enigma import (defaultdict, cproduct, subsets, implies, singleton, join, printf)
      
      # evaluate the clauses under interpretation <i>
      def check(i, cs):
        (a, b, c, d, e, f, g) = cs
        # [1] (a, b -> c, d -> e, f -> g)
        if i == 1: return all([a, implies(b, c), implies(d, e), implies(f, g)])
        # [2] (a <- b, c, d -> e, f -> g)
        if i == 2: return all([implies(b, a), c, implies(d, e), implies(f, g)])
        # [3] (a <- b, c <- d, e, f -> g)
        if i == 3: return all([implies(b, a), implies(d, c), e, implies(f, g)])
        # [4] (a <- b, c <- d, e <- f, g)
        if i == 4: return all([implies(b, a), implies(d, c), implies(f, e), g])
      
      # the amounts in the envelopes
      money = [5000, 4000, 3000, 2000, 1000]
      
      # choose an interpretation of the will (1-4)
      for i in (1, 2, 3, 4):
      
        # record outcomes: (A, M, N, S, T) -> sing
        r = defaultdict(list)
      
        # allocate the envelopes, and whether Maurice can sing
        for (k, sing) in cproduct([subsets(money, size=len, select="P"), [0, 1]]):
          (A, M, N, S, T) = k
      
          # the clauses:
          cs = [
            # (a) "M is not to sing ..."
            (not sing),
            # (b) "S gets 3000"
            (S == 3000),
            # (c) "A gets 5000"
            (A == 5000),
            # (d) "T gets less than M"
            (T < M),
            # (e) "A gets twice N"
            (A == 2 * N),
            # (f) "T does not get 1000"
            (T != 1000),
            # (g) "S gets 4000"
            (S == 4000),
          ]
      
          # is this is a valid allocation?
          if check(i, cs):
            r[k].append(sing)
            # do we need to proceed further?
            if len(r.keys()) > 1: break
      
        # there is only one way to distribute the money?
        k = singleton(r.keys())
        if k:
          # output solution
          (A, M, N, S, T) = k
          printf("[{i}] A={A} M={M} N={N} S={S} T={T}; sing={sing}", sing=join(sorted(r[k]), sep=", ", enc="{}"))
      

      Solution: The money is distributed as follows:

      Alec: £2000
      Maurice: £3000
      Nigel: £1000
      Stephen: £4000
      Thomas: £5000

      The solution is provided by interpretation [3] of the will.

      And as Stephen does not get £3000, Maurice is not prohibited from singing at the funeral.

      Like

  • Jim Randell 9:01 am on 6 November 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 195: Common frontier 

    From The Sunday Times, 17th January 1965 [link]

    The adjoining countries of Europhalia and Sopiculia have different standard units of weight and length, but both use the normal units of time. Although both countries use Arabic numerals, neither uses the denary (tens) method of counting, but each has a different integer less than ten as its counting base.

    In reply to my request for more information a Europhalian friend wrote: “Our unit of weight is the Elbo, and there are 42 Elbos to 24 of their Solbos. The length of our common frontier is 21 Emils”. My Sopiculian correspondent replied: “16 Solbos weigh the same as 26 Elbos; the common frontier is 21 Somils long”.

    I later discovered that in both countries there is a speed limit equivalent to our 50 mph. In Sopiculia this is defined by law as 104 Somils per hour.

    What is the Europhalian speed limit?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser195]

     
    • Jim Randell 9:02 am on 6 November 2022 Permalink | Reply

      If the bases are E and S (both less than 10).

      Then the ratio of the weights W = (Elbo/Solbo) is (according to to the two correspondents):

      W = (2E + 4) / (4E + 2)
      W = (S + 6) / (2S + 6)
      ⇒ 4ES + 12E + 8S + 24 = 4ES + 24E + 2S + 12
      ⇒ E = S/2 + 1

      And we see from the digits used, E > 4 and S > 6, and S must be even, so:

      S = 8; E = 5

      We can then translate the correspondents statements into decimal to get:

      Eu: “There are 22 Elbos to 14 of their Solbos [W = 7/11]. The common frontier is 11 Emils”.
      So: “14 Solbos weigh the same as 22 Elbos [W = 7/11]. The common frontier is 17 Somils”.

      And:

      “104 Somils/hour” [base 8]
      = 68 Somils/hour [base 10]
      = 4× (17 Somils)/hour [base 10]
      = 4× (11 Emils)/hour [base 10]
      = 44 Emils/hour [base 10]
      = “134 Emils/hour” [base 5]

      Solution: The Europhalian speed-limit would be expressed as: “134 Emils per hour”.

      Like

  • Jim Randell 9:55 am on 23 October 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 177: The pay roll rules 

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

    I am the Managing Director of a factory and I have under me five employees. Their names are: Alf, Bert, Charlie, Duggie and Ernie. And their jobs are, not necessarily respectively: Doorkeeper, Doorknob Polisher, Bottle Washer, Welfare Officer and Worker.

    There has been some dissatisfaction recently about wages which, in the past, I am bound to admit, have sometimes been rather haphazard. It is clearly very difficult to arrange things in such a way that merit is appropriately rewarded, but it seemed to me important that everybody’s position should at least be clear. After much thought, therefore, I put up the following notice:

    Wages:

    1. Alf is to get more than Duggie.

    2. Ernie is to get 12 per cent more than the Bottle Washer will when he receives the 10 percent rise that he will be getting next month.

    3. The Doorknob Polisher is to get 30 per cent more than he used to.

    4. Charlie is to get £12 a year less than 20 per cent more than the Welfare Officer.

    5. No one is to get less than £200 or more than £600 a year.

    6. The Doorkeeper is to get 5 per cent more than he would if he got 10 per cent less than Bert.

    Everyone always has received in my factory, receives now, and as long as I am in charge will always receive an exact number of £s per year.

    What are the various jobs of my employees, and what yearly wage is each of them to get?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill). The puzzle text above is taken from the book.

    [teaser177]

     
    • Jim Randell 9:56 am on 23 October 2022 Permalink | Reply

      (See also: Teaser 811).

      I think the wording in this puzzle was slightly confusing.

      I am assuming that an unqualified wage referred to on the notice are the new wage that each employee is to receive immediately.

      However there is also a future wage referred to (the Bottle Washer will receive a 10% rise next month), and a previous wage (the Doorknob polisher receives 30% more than he used to).

      This time I used constraint satisfaction solver, so here is a MiniZinc model to solve the puzzle. It uses my minizinc.py library to provide a shortcut for allowing multiple variables with the same domain to be declared in one statement.

      %#! python3 -m minizinc use_embed=1
      
      include "globals.mzn";
      
      % wages by name: A, B, C, D, E
      {var("200..600", "ABCDE")};
      
      % wages by job: DK, DP, BW, WO, WK
      {var("200..600", ["DK", "DP", "BW", "WO", "WK"])};
      
      % the sets of values are the same
      constraint sort([A, B, C, D, E]) = sort([DK, DP, BW, WO, WK]);
      
      % 1. A gets more than D
      constraint A > D;
      
      % 2. In the future BW is to get a 10% increase ...
      constraint (BW * 110) mod 100 = 0;
      % ... and E (now) gets 12% more than this future amount
      constraint (BW * 112 * 110) mod (100 * 100) = 0;
      constraint E = (BW * 112 * 110) div (100 * 100);
      
      % 3. DP gets a 30% increase
      constraint (DP * 100) mod 130 = 0;
      
      % 4. C is to get 12 less than 20% more than WO
      constraint (WO * 120) mod 100 = 0;
      constraint C = (WO * 120) div 100 - 12;
      
      % 6. DK is to get 5% more than 10% less than B
      constraint (B * 90 * 105) mod (100 * 100) = 0;
      constraint DK = (B * 90 * 105) div (100 * 100);
      
      solve satisfy;
      

      And you can then run it like this:

      % python3 minizinc.py use_embed=1 teaser177.mzn
      A=378 B=400 C=468 D=250 E=308 DK=378 DP=468 BW=250 WO=400 WK=308
      

      Solution: The jobs and wages are as follows:

      Alf: Doorkeeper; £378
      Bert: Welfare Officer; £400
      Charlie: Doorknob Polisher; £468 (was £360)
      Duggie: Bottle Washer; £250 (future £275)
      Ernie: Worker; £308

      The originally published puzzle was pre-decimalisation, and gave the salaries in shillings per week, rather than pounds per year, but used the same values.

      Like

    • Hugh Casement 11:08 am on 23 October 2022 Permalink | Reply

      I have yet to come across a good puzzle set by Eric Emmet.

      I think we have to assume that what the doorknob polisher “used to get” is what he currently gets, and that all the new wages come into effect next month.

      Like

      • Jim Randell 12:52 pm on 23 October 2022 Permalink | Reply

        @Hugh: I thought originally that everyone had a “before” and “after” value. But unless you have a “future” value for D (that comes after “after”), then you don’t get the published solution.

        Like

  • Jim Randell 8:18 am on 9 October 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 190: Towns and families 

    From The Sunday Times, 13th December 1964 [link]

    Cardiff and London share a line of latitude; Cardiff and Edinburgh share a line of longitude.

    The Archers, the Brewers, the Carters and the Drews are four married couples born and married in London, Cardiff, Edinburgh and Belfast. One of each sex was born in each city; one marriage took place in each city. No one was married in the city of his or her birth. Mrs Archer was the only woman married east of where she was born; Mr Archer was the only man married south of where he was born; Mr Brewer was the only man to marry a woman born north of him. Mr Carter and Mrs Drew were twins.

    Where were the Carters married?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser190]

     
    • Jim Randell 8:18 am on 9 October 2022 Permalink | Reply

      This Python program tries all possible arrangements.

      It runs in 55ms. (Internal runtime is 790µs).

      Run: [ @replit ]

      from enigma import (subsets, printf)
      
      # Belfast, Cardiff, Edinburgh, London
      cities = "BCEL"
      
      # assign North and East values (approximate latitude and longitude)
      N = dict(C=51.5, L=51.5, B=54.6, E=56.0)
      E = dict(B=-5.9, C=-3.2, E=-3.2, L=-0.1)
      
      # choose cities of birth for the men
      for (mA, mB, mC, mD) in subsets(cities, size=len, select="P"):
      
        # choose cities of birth for the women
        for (fA, fB, fC, fD) in subsets(cities, size=len, select="P"):
      
          # "Mr C and Mrs D were twins" (presumably born in the same place)
          if not (mC == fD): continue
      
          # "Mr B was the only man to marry a woman born N of him"
          if not (N[fB] > N[mB]): continue
          if any(N[f] > N[m] for (m, f) in zip((mA, mC, mD), (fA, fC, fD))): continue
      
          # choose cities for marriages
          for (wA, wB, wC, wD) in subsets(cities, size=len, select="P"):
            # no-one was married in the city of their birth
            if any(w == m or w == f for (m, f, w) in zip((mA, mB, mC, mD), (fA, fB, fC, fD), (wA, wB, wC, wD))): continue
      
            # "Mrs A is the only woman married east of her birth"
            if not (E[wA] > E[fA]): continue
            if any(E[w] > E[f] for (f, w) in zip((fB, fC, fD), (wB, wC, wD))): continue
      
            # "Mr A is the only man married south of his birth"
            if not (N[mA] > N[wA]): continue
            if any(N[m] > N[w] for (m, w) in zip((mB, mC, mD), (wB, wC, wD))): continue
      
            printf("          A B C D")
            printf("husband = {mA} {mB} {mC} {mD}")
            printf("wife    = {fA} {fB} {fC} {fD}")
            printf("wedding = {wA} {wB} {wC} {wD}")
            printf()
      

      Solution: The Carters were married in Belfast.

      The full solution is:

      Archer: husband = Edinburgh; wife = Belfast; married = London
      Brewer: husband = London; wife = Edinburgh; married = Cardiff
      Carter: husband = Cardiff; wife = London; married = Belfast
      Drew: husband = Belfast; wife = Cardiff; married = Edinburgh

      Like

  • Jim Randell 8:36 am on 2 October 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 188: Family birthdays 

    From The Sunday Times, 29th November 1964 [link]

    I wrote to an American friend on the 3rd February 1964, and told him of the coincidence of our family birthdays. My wife and I, our three sons, and our four grandsons all have our birthdays on the same day of the week every year, though all our birthdays are different. When I wrote, I used the usual English form of the date — 3/2/64 — and I gave all our birthdays in that form also.

    My third son received a cable from my friend on his birthday in 1964, but later I was surprised to get a cable from him myself on my eldest son’s birthday. Next my eldest grandson received a cable on his right birthday, and I realised that we were all going to receive cables, but that my friend was, quite reasonably, reading the dates in the American form, i.e. he assumed that the letter had been sent on 2nd March 1964.

    However, I did not write to put him right, and my wife was the next person to get a cable; this was not on her birthday.

    What was the day and date of her birthday in 1964?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser188]

     
    • Jim Randell 8:37 am on 2 October 2022 Permalink | Reply

      The birthdays are on the same day of the week each year, so they cannot straddle 29th February.

      But in any case the letter was sent on 3/2, which was misinterpreted as 2nd March, so the birthdays must be after that date.

      This Python program runs in 57ms. (Internal run time is 434µs).

      Run: [ @replit ]

      from datetime import (date, timedelta)
      from enigma import (defaultdict, repeat, catch, subsets, printf)
      
      # reverse the day/month of a date
      rev = lambda d: catch(date, d.year, d.day, d.month)
      
      # is a date reversible?
      is_rev = lambda d: rev(d) == d
      
      # group days by day of the week
      g = defaultdict(list)
      # look for dates in 1964 that can be misinterpreted as US style dates
      inc = lambda x, i=timedelta(days=1): x + i
      for d in repeat(inc, date(1964, 1, 1)):
        if d.year > 1964: break
        # try US format
        d_ = rev(d)
        if not d_: continue
        # must be the same day of the week as the original date
        w = d.isoweekday()
        if d_.isoweekday() == w:
          g[w].append(d)
      
      # consider the day of the week
      for k in g.keys():
        # we need 9 dates the come after 2nd March
        for ds in subsets((d for d in g[k] if d > date(1964, 3, 2)), size=9):
          # the first birthday is the correct date (third son)
          if not is_rev(ds[0]): continue
          # the second birthday is not the correct date (eldest son, sent to setter)
          if is_rev(ds[1]): continue
          # the third birthday is the correct date (eldest grandson)
          if not is_rev(ds[2]): continue
          # the fourth birthday is not the correct date (sent to wife)
          if is_rev(ds[3]): continue
          # so the wife's birthday is ...
          d = rev(ds[3])
          # output solution
          printf("{d}", d=d.strftime("%A, %d %B %Y"))
      

      Solution: Her birthday is on: Saturday, 7th November 1964.

      There is only one set of dates that works:

      4/4 = 4th April, third son
      9/5 = 9th May, eldest son; misinterpreted as 5th September (the setter’s birthday)
      6/6 = 6th June, eldest grandson
      11/7 = 11th July; misinterpreted at 7th November (the setter’s wife’s birthday)
      8/8 = 8th August
      5/9 = 5th September, setter; misinterpreted at 9th May
      10/10 = 10th October
      7/11 = 7th November, setter’s wife; misinterpreted as 11th July
      12/12 = 12th December

      Like

  • Jim Randell 10:53 am on 25 September 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 418: Bell’s weights 

    From The Sunday Times, 11th May 1969 [link]

    “Now”, says Bell at the pub, “look intelligent and imaginative even if you don’t look beautiful by any means”. We swallow the insult and look solemn. Bell likes his jokes and we like his puzzles.

    “Imagine you have some scales, but only three weights. However, you have a heap of Grade A sand, and a couple of bags; so you make two extra weights, one as heavy as all the first three put together, and the other twice as heavy as all the first three. Whereupon all the remaining sand is removed to a great distance”.

    “With these five weights you must be able to weigh 1 ounce, 2 ounces, 3, 4, and so on, as far as possible. No gaps in that lot. It’s how far you can to the first gap I’m after. Usual prize — one pint for the best score before closing time”.

    What should Bell’s five weights be to give the highest possible score?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser418]

     
    • Jim Randell 10:54 am on 25 September 2022 Permalink | Reply

      This Python program considers increasing values for the total of the first 3 weights, from 3 to 40.

      It runs in 351ms.

      Run: [ @replit ]

      from enigma import (irange, inf, decompose, subsets, peek, Accumulator, printf)
      
      def unreachable(ws):
        # collect possible weights
        vs = set()
        # choose multipliers for each weight
        for ms in subsets((1, 0, -1), size=len(ws), select="M"):
          v = sum(m * w for (m, w) in zip(ms, ws))
          if v > 0: vs.add(v)
        # find the first unreachable value
        return peek(v for v in irange(1, inf) if v not in vs)
      
      # record maximum unreachable weight
      r = Accumulator(fn=max, collect=1)
      
      # consider t = a + b + c
      for t in irange(3, 40):
        for (a, b, c) in decompose(t, 3, increasing=1, sep=0, min_v=1):
          # calculate the first unreachable value
          ws = (a, b, c, t, 2 * t)
          v = unreachable(ws)
          r.accumulate_data(v, ws)
          if v == r.value: printf("[t={t}: {ws} -> unreachble = {v}]")
      
      printf("values = 1 .. {x}", x=r.value - 1)
      for ws in r.data:
        printf("-> {ws}")
      

      Solution: The 5 weights are: 4, 6, 9, 19, 38.

      And this set of weights can be used to weigh all values from 1 to 64.


      Using a set of balancing scales each weight can go in the left pan, right pan, or neither.

      For for n weights there are 3^n possibilities.

      But these include, not using any weights (all in neither pan), and each combination has a mirror image.

      So the total maximum possible number of different weights would be:

      W(n) = (3^n − 1) / 2

      For 5 weights we have: W(5) = 121, and using a set consisting of increasing powers of 3 we can achieve this maximum and weigh all values between 1 and 121:

      1, 3, 9, 27, 81

      But for a set of the form:

      (a, b, c, a + b + c, 2(a + b + c))

      There are a 70 different arrangements. So the maximum number of different values we could achieve would be no more than this. And we can use the program to perform an exhaustive check up to this total, but there are no better solutions.

      Like

  • Jim Randell 10:09 am on 18 September 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 560: Ribbon counter 

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

    “Puzzle here”, says Bell at the pub. “Chap has a ribbon shop, sells the stuff by the inch, no commercial sense. He’s barmy anyway; look how he measures it. His counter is exactly 100 inches long and he’s marked it off into 16 bits; 6 of 11 inches, 2 of 6 inches, 3 of 5 inches, 1 of 3 inches and 4 of 1 inch, and he can measure any number of inches up to a hundred, that is, by picking the right pair of marks.

    “You have to sort the spaces out; but I’ll tell you, all the 11 inches are together round about the middle — well, a bit to the right, but not as much as 4 inches off centre. You get the idea? For most measurements he’s using a kind of feet and inches with eleven inches to the foot”.

    “Young Green is nearly right: he can’t measure 99 inches unless there’s a 1-inch space at one end, but he doesn’t need a 1-inch at the other end for 98 inches; he does it with two 1-inch spaces at the same end; but there might be a 1-inch at the other end, all the same, and there might not”.

    “In answer to two foolish questions, the ribbon must be measured single thickness, no folding; and it’s a straight counter, it’s not circular”.

    “Usual prize, one pint”.

    How were the spaces arranged from left to right?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser560]

     
    • Jim Randell 10:10 am on 18 September 2022 Permalink | Reply

      The puzzle is describing a sparse ruler of length 100 with 17 marks. (See: Teaser 119).

      However, in this case we are told that one end of the ruler has two 1-segments (lets put them at the start), and the six 11-segments all occur together in the middle-ish (displaced slightly to the right, but not by more than 3 inches), so we can look for arrangements of the remaining segments that give a viable ruler.

      This Python program runs in 104ms. (Internal runtime is 49ms).

      Run: [ @replit ]

      from enigma import (multiset, subsets, csum, reverse, printf)
      
      # segment groups we are given
      ss1 = (1, 1)  # start with (1, 1)
      ss2 = (11,) * 6  # 11s are in the middle
      
      # remaining segments
      segs = multiset.from_dict({ 6: 2, 5: 3, 3: 1, 1: 2 })
      n = segs.size()
      
      # consider the remaining segments
      for rs in subsets(segs, size=n, select="mP"):
        # accumulate sufficient segments to place the 11s close to the middle
        (t, i) = (rs[0] + 35, 1)
        while True:
          t += rs[i]
          i += 1
          if t < 47 or t == 50: continue
          if t > 53: break
          # construct the sequence of segments
          ss = ss1 + rs[:i] + ss2 + rs[i:]
          # construct the sequence of marks
          ms = list(csum(ss, empty=1))
          # check all 100 differences are achievable
          if len(set(b - a for (a, b) in subsets(ms, size=2))) == 100:
            # do we want the mirror image?
            if t < 50: (ss, ms) = (reverse(ss), reverse(ms))
            # output segments and marks
            printf("{ss} -> {ms}")
      

      Solution: The segments are as follows:

      (1, 1, 3, 5, 5, 5, 11, 11, 11, 11, 11, 11, 6, 6, 1, 1)

      The centre of the 11’s is 53 inches from the left edge.


      Using the code I wrote for Teaser 119 we find there are only 2 sparse rulers of length 100 with 17 marks (and it is not possible to construct a length 100 ruler with fewer marks):

      (0, 1, 2, 5, 10, 15, 20, 31, 42, 53, 64, 75, 86, 92, 98, 99, 100)
      (0, 1, 2, 8, 14, 25, 36, 47, 58, 69, 80, 85, 90, 95, 98, 99, 100)

      {+1^2, +3, +5^3, +11^6, +6^2, +1^2}
      {+1^2, +6^2, +11^6, +5^3, +3, +1^2}

      The second being the mirror image of the first (which is clear from the representation in difference format).

      Like

      • Frits 11:38 am on 19 September 2022 Permalink | Reply

        @Jim, you still have to put the latest version of enigma.py to the magwag site.

        Like

        • Jim Randell 3:57 pm on 19 September 2022 Permalink | Reply

          I’ve uploaded enigma.py version 2022-09-17, which has all my recent changes in it.

          The most interesting change is that SubstitutedExpression.split_sum() can now take multiple simultaneous sums to split. In general it is now faster to use split_sum() than it is to use the specialised SubstitutedSum solver.

          Like

    • Frits 3:15 pm on 19 September 2022 Permalink | Reply

      Similar reusing parts of Jim’s code.

         
      from itertools import permutations
      
      # put bits (1, 1) up front 
      
      # list of numbers to use besides (1, 1) and (11, 11, 11, 11, 11, 11)
      bits = (1, 1, 3, 5, 5, 5, 6, 6)
      
      # segment groups we are given
      ss1 = (1, 1)     # start with (1, 1)
      ss2 = (11,) * 6  # 11s are in the middle
      
      ps = set()
      target = list(range(1, 101))
      
      for p in permutations(bits):
        if p in ps: continue
        # determine where to put the six 11s
        t = 2    # first 2 bits
        for i, b in enumerate(p):
          t += b
          # 1 to 3 inches off centre (to the right) meaning 51 - 53
          if t < 18: continue
          if t > 20: break
          
          # construct the sequence of segments
          ss = ss1 + p[:i+1] + ss2 + p[i+1:]
          
          # collect lenghts of all possible consecutive bits
          ls = set(sum(ss[i:j]) for i in range(len(ss) - 1) 
                   for j in range(i + 1, len(ss) + 1))
          
          if sorted(ls) == target:
            print(ss)
        ps.add(p)
      

      Like

      • Frits 3:50 pm on 19 September 2022 Permalink | Reply

        This only handles the situation if the two 1-segments appear at the start (at the left).

        Like

  • Jim Randell 11:56 am on 31 July 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 161: 100-yard race 

    From The Sunday Times, 10th May 1964 [link]

    Harry, Kenneth, Lionel, Martin, Nicholas and Oliver were, the competitors in the 100-yard race on Sports Day. They wore cards numbered 1, 2, 3, 4, 5, 6 but not in that order. In no case was the position of any of the competitors the same as his card number but two of the competitors had positions equal to each other’s card number.

    Nicholas was 5th and his card number was the same as Kenneth’s position. Harry’s card number was the same as Oliver’s position which was 4th. Martin’s card number was 1.

    It was found that the sum of the products of each competitor’s position and card number was 61.

    Place the competitors in the order in which they finished the race and give their card numbers.

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser161]

     
    • Jim Randell 11:57 am on 31 July 2022 Permalink | Reply

      This Python program runs in 56ms. (Internal run time is 1.3ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, reverse, diff, singleton, printf)
      
      pos = list(irange(1, 6))
      
      # consider the cards in each position (1st, ..., 6th)
      for ps in subsets(pos, size=6, select="D"):
        # card: map pos -> card
        card = dict(enumerate(ps, start=1))
        # the sum of the position/card products is 61
        if sum(i * p for (i, p) in card.items()) != 61: continue
        # there is (at least) one 2-cycle
        if not any(card[p] == i for (i, p) in card.items()): continue
      
        # name: map pos -> name
        # "N was 5th"
        # "O was 4th"
        # "K's position is N's card number (N is 5th)"
        # "H's card number is O's position (O is 4th)"
        # "M's card number is 1"
        # so we have enough information to fill out the map
        rcard = reverse(card)
        ks = [5, 4, card[5], rcard[4], rcard[1]]
        ks.append(singleton(diff(pos, ks)))
        if ks[-1] is None: continue
        name = dict(zip(ks, "NOKHML"))
        # check disallowed order
        if all(name[i] == x for (i, x) in zip(pos, "HKLMNO")): continue
      
        # output solution
        for i in pos:
          printf("{i}: {n} = #{c}", c=card[i], n=name[i])
        printf()
      

      Solution: The finishing positions are:

      1st: Lionel (#6)
      2nd: Harry (#4)
      3rd: Kenneth (#2)
      4th: Oliver (#5)
      5th: Nicholas (#3)
      6th: Martin (#1)

      Like

    • Frits 11:53 am on 3 August 2022 Permalink | Reply

       
      from enigma import SubstitutedExpression
      
      # get position from card number
      pos = lambda n, lst: [i for i, x in enumerate(lst, start=1) if x == n][0]
      
      l1 = "[A, B, C, D, E, F]"          # card numbers
      l2 = "[1, 2, 3, 4, 5, 6], " + l1   # positions and card numbers
      z = "zip(" + l2 + ")"
      
      exprs = []
      # position unequal to card number for all competitors
      exprs.append("all(x != y for x, y in " + z + ")")
      # sum of the products of each competitor's position and card number was 61
      exprs.append("sum(x * y for x, y in " + z + ") == 61")
      # two of the competitors had positions equal to each other's card number
      exprs.append("sum((y, x) in " + z + " for x, y in " + z + ") == 2")
      # M ( , 1)
      # N (5, x)
      # O (4,  )
      # H ( , 4)
      # K (x,  )
      # L ( ,  )
      # check on five different positions
      exprs.append("len({pos(1, " + l1 + "), pos(4, " + l1 + "), E, 4, 5}) == 5")
                    
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        exprs,
        answer="((1, A), (2, B), (3, C), (4, D), (5, E), (6, F))",
        digits=range(1, 7),
        distinct=("ABCDEF"),
        env=dict(pos=pos),
        verbose=0,    # use 256 to see the generated code
      )
      
      names = ["", "", "", "Oliver", "Nicholas", ""]
      for (_, ans) in p.solve():
        # dictionary, card --> postion
        d = {y: x for x, y in ans}
        
        names[d[1] - 1] = "Martin"
        names[d[4] - 1] = "Harry"
        names[ans[4][1] - 1] = "Kenneth"
        
        for i in range(6):
          if names[i] == "":
            names[i] = "Lionel"
         
        # integrity check 
        if len(set(names)) != 6: continue
        
        for i in range(6):
          print(f"{ans[i][0]}: {names[i]} with card number {ans[i][1]}")   
      

      Like

  • Jim Randell 2:04 pm on 24 July 2022 Permalink | Reply
    Tags: ,   

    Brain-Teaser 159: Fallen leaves 

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

    Three leaves fall from a book. The total of the page numbers remaining in the second half of the book is now three times the total of the page numbers remaining in the first half.

    The total of the page numbers on the fallen leaves lies between 1050 and 1070, and is the highest which could have produced this effect.

    How many numbered pages did the book contain originally?

    I found many solutions, which improve on the published answer. So I have marked the puzzle as flawed.

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser159]

     
    • Jim Randell 2:06 pm on 24 July 2022 Permalink | Reply

      I think this puzzle is flawed, as I found many solutions that seem to satisfy the puzzle text, and are different from the published solution.

      If we suppose the book consists of n leaves (= 2n pages), and that the leaves are 1|2, 3|4, …, (2n − 1)|(2n).

      And the two halves of the book are pages 1 – n and (n + 1)(2n).

      Then the initial sum of page numbers in each half of the book are:

      first half = sum(1 .. n) = n(n + 1)/2
      second half = sum(n+1 .. 2n) = n(3n + 1)/2

      Now, if the three leaves removed are (2a − 1)|(2a), (2b − 1)|(2b), (2c − 1)|(2c).

      Then the total of the page numbers removed is t = 4(a + b + c) − 3, and this is in the range 1050 – 1070.

      So (a + b + c) is in the range 264 – 268, and we want this to take on the highest possible value (i.e. 268 if possible, which corresponds to t = 1069).

      If the sum of the removed pages in the first and second halves is t1, t2 (t1 + t2 = t), then we have:

      n(3n + 1)/2 − t2 = 3(n(n + 1)/2 − t1)
      n = 3.t1 − t2

      This Python program starts looking for values of t from 268 down to 264, and stops when it finds a value that gives viable books. It runs in 74ms. And it finds many possible solutions.

      Run: [ @replit ]

      from enigma import (irange, decompose, printf)
      
      # solve with leaves removed (even page numbers given)
      def solve(*vs):
        # calculate the removed page numbers
        (x, y, z) = (2 * v for v in vs)
        ps = (x - 1, x, y - 1, y, z - 1, z)
        # split the pages at index i
        for i in irange(2, 4):
          t1 = sum(ps[:i])
          t2 = sum(ps[i:])
          # calculate the number of leaves (so there are 2n pages)
          n = 3 * t1 - t2
          if n < 3 or ps[-1] > 2 * n: continue
          # and check the split occurs between the halves
          if i > 0:
            if ps[i - 1] > n: continue
          if i < 6:
            if not (ps[i] > n): continue
          yield (n, ps, t1, t2)
      
      # record solutions (number of pages = twice the number of leaves)
      ss = set()
      
      # t = a + b + c
      for t in irange(268, 264, step=-1):
        # calculate possible a, b, c values
        for (a, b, c) in decompose(t, 3):
          for (n, ps, t1, t2) in solve(a, b, c):
            printf("[t={t}: a={a} b={b} c={c} -> {ps}; t1={t1} t2={t2}; n={n}]")
            ss.add(2 * n)
        # are we done?
        if ss:
          printf("pages = {ss}", ss=sorted(ss))
          break
      

      Solution: The book could have: 310, 318, 342, 350, 374, 406, 438, 470, 502, 534, 566, 598, 630, 662, 694, 6414 pages.


      Here is one of the solutions found:

      If the book has 203 leaves = 406 pages, numbered 1|2, 3|4, …, 203|204, …, 405|406.

      Then the sum of the page numbers in the first half of the book is: sum(1..203) = 20706.

      And the sum of the page numbers in the second half of the book is: sum(204..406) = 61915.

      (Note that if leaf 203|204 is removed, that is one page from each half of the book).

      Now, if pages 1|2, 157|158, 375|376 are removed (total = 1069, the largest possible value).

      Then the pages removed from the first half are: 1, 2, 157, 158; sum = 318, so the sum of the remaining pages in the first half is 20706 − 318 = 20388.

      And the pages removed from the second half are: 375, 376; sum = 751, so the sum of the remaining pages in the second half is 61915 − 751 = 61164.

      And: 61164 = 3 × 20388, as required.


      The published solution (and that given in the 1974 book) is that the book initially had 302 pages (i.e. 151 leaves), and the removed pages are 75|76, 151|152, 301|302. Which gives a total of 1057.

      But I have shown above that this is not the largest total possible.

      It was also noted: “Only 4 correct answers in a very small entry”. So it seems I wasn’t the only one that had issues with this puzzle.

      The solution in the book only considers 3 ways that leaves can be removed: 1 from the first half + 2 from the second half; 2 from the first half + 1 from the second half; 1.5 from each half. In terms of pages these are 2+4, 3+3, 4+2, but my program also considers 0+6, 1+5, 5+1, 6+0. However the example I give is a 4+2 combination, which should be accounted for by the published solution. But it has arrived at the conclusion that the maximum viable total of the numbers on the removed pages for a book with x leaves is 7x, so 1057 is the maximum possible (with 151 leaves), but I think this is faulty reasoning.

      (The only thing I can think of is that the two halves of the book are considered after the leaves have been removed. (Although the solution given in the book doesn’t seem to support this). But even with this interpretation there are multiple solutions with a total of 1069 – any that removes 3 pages from the first half and three from the second half will leave the boundary unchanged).

      Like

  • Jim Randell 12:49 pm on 3 July 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 120: Rugby results 

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

    At a certain stage in our all-against-all Rugby football competition the table of results read as follows. There had been no matches in which neither side had scored at all:

    What was the result of the match between A and B?

    Note: This problem was set when a try was worth 3 points, a penalty goal 3 points and a converted try 5 points. Match points are on the usual basis of 2 for a win and 1 for a draw.

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser120]

     
    • Jim Randell 12:50 pm on 3 July 2022 Permalink | Reply

      There are certain scores which are not possible to make from combinations of 3 and 5 points.

      This Python program uses the [[ express() ]] function from the enigma.py library to determine what they are. We then use the [[ Football() ]] helper class to determine possible match outcomes and scorelines.

      It runs in 89ms.

      Run: [ @replit ]

      from enigma import (express, irange, peek, Football, digit_map, cache)
      
      # find scores that cannot be made from multiples of 3 and 5
      invalid = set(n for n in irange(0, 18) if not peek(express(n, [3, 5]), default=None))
      
      # scoring system (2 points for a win, 1 for a draw)
      football = Football(points=dict(w=2, d=1))
      
      # digits used in the table (10=A, 13=D, 15=F, 16=G, 18=I)
      d = digit_map(0, 18)
      
      # the table, goals for/against
      (table, gf, ga) = (dict(played="34432", points="55420"), "DGF53", "8AI88")
      
      # check for a valid score (and cache as we go along)
      @cache
      def valid(s):
        # unplayed?
        if s is None: return True
        # otherwise:
        # (0, 0) is not allowed
        if s == (0, 0): return False
        # reject invalid scores
        if invalid.intersection(s): return False
        # looks OK
        return True
      
      # find possible match outcomes
      for (ms, _) in football.substituted_table(table, d=d):
        # find possible scorelines
        for ss in football.substituted_table_goals(gf, ga, ms, d=d, valid=valid):
          # output solution
          football.output_matches(ms, ss, teams="ABCDE")
      

      Solution: The result of the A vs B match was a 5-3 win for A.

      The full results are:

      A vs B = 5-3
      A vs C = 5-5
      A vs D = 3-0
      A vs E = (unplayed)
      B vs C = 5-5
      B vs D = 5-0
      B vs E = 3-0
      C vs D = 0-5
      C vs E = 5-3
      D vs E = (unplayed)

      Like

  • Jim Randell 11:55 am on 26 June 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 101: Midsummer birthday madness 

    From The Sunday Times, 3rd March 1963 [link]

    Midsummer Day, 1962 (June 24) was my youngest grandson John’s first birthday, and I was then able to claim that my nine grandchildren were aged 0, 1, 2, 3, 4, 5, 6, 7, and 8 years old (neglecting, of course, the odd days). They were all born in June, and if they are arranged in birthday order through the month the following facts are true:

    John is the middle grandchild;

    The sum of the dates of the last four is an exact multiple of the sum of the dates of the first four;

    The sum of the ages of the last four is two-thirds of the sum of the ages of the first four;

    The sum of the years of birth of the first three is equal to the sum of the years of birth of the last three;

    The intervals between birthdays are 0, 1, 2, 3, 4, 5, 6, and 7 days, but not in that order;

    Also:

    My eldest son’s two daughters are exactly two years apart;

    The twins were born four hours apart;

    Two children are as old as their dates of birth.

    What was the date of birth of the grandchild born in 1954?

    This puzzle was included in the book Sunday Times Brain  Teasers (1974, edited by Ronald Postill).

    There are now 700 Teaser puzzles available on the site!

    [teaser101]

     
    • Jim Randell 11:56 am on 26 June 2022 Permalink | Reply

      The ages are all different, so the twins must be born either side of midnight on the 24th, so John is one of the twins, and the other must have a birthday of the 25th. John is the youngest grandson, so his (younger) twin must be a girl.

      This Python program starts with the 24th in the middle of the order, and then chooses an ordering for the intervals to give the remaining dates. It then assigns the ages, and checks all the required conditions apply.

      It runs in 161ms.

      Run: [ @replit ]

      from enigma import (subsets, div, printf)
      
      # the middle birthday is on the 24th, and the separation between
      # birthdays is (0, 1, 2, 3, 4, 5, 6, 7), in some order
      for ss in subsets((0, 1, 2, 3, 4, 5, 6, 7), size=len, select="P"):
        # construct the dates
        d5 = 24
        (d4, d6) = (d5 - ss[3], d5 + ss[4])
        (d3, d7) = (d4 - ss[2], d6 + ss[5])
        (d2, d8) = (d3 - ss[1], d7 + ss[6])
        (d1, d9) = (d2 - ss[0], d8 + ss[7])
        if d1 < 1 or d9 > 30: continue
        ds = [ d1, d2, d3, d4, d5, d6, d7, d8, d9]
      
        # John's twin must be born on the 25th
        if 25 not in ds: continue
      
        # sum of the dates of the last 4 is an exact multiple of the sum of
        # the dates of the first four
        x = div(sum(ds[-4:]), sum(ds[:4]))
        if x is None or x < 2: continue
      
        # choose values for the ages (John is 1)
        for ns in subsets((0, 2, 3, 4, 5, 6, 7, 8), size=len, select="P", fn=list):
          # insert 1
          ns.insert(4, 1)
      
          # John's twin (age 0) was born on the 25th
          if ds[ns.index(0)] != 25: continue
      
          # the sum of the ages of the last four is 2/3 the sum of the ages
          # of the first four
          if 3 * sum(ns[-4:]) != 2 * sum(ns[:4]): continue
      
          # [exactly] 2 ages are the same as the date
          if sum(x == y for (x, y) in zip(ds, ns)) != 2: continue
      
          # the sum of the years of birth of the first three is the same as
          # the sum of the years of birth of the last three
          ys = list(1962 - x for x in ns[:5]) + list(1961 - x for x in ns[5:])
          if sum(ys[:3]) != sum(ys[-3:]): continue
      
          # find the 2 grandchildren that share a birthday
          i = ss.index(0)
          # neither can be John
          if i == 3 or i == 4: continue
          # the ages must differ by 2
          if abs(ns[i] - ns[i + 1]) != 2: continue
      
          # output solution
          for (d, n, y) in zip(ds, ns, ys):
            printf("{d:02d} June {y} -> age = {n}{s}", s=(' [*** SOLUTION ***]' if y == 1954 else ''))
          printf()
      

      Solution: The birthday of the grandchild born in 1954 is 11th June.

      The dates of birth and ages are:

      02 June 1960 → age = 2 (age 2 on the 2nd)
      07 June 1955 → age = 7 (age 7 on the 7th)
      11 June 1954 → age = 8 (answer)
      17 June 1958 → age = 4
      24 June 1961 → age = 1 (today, John’s birthday)
      25 June 1961 → age = 0 (John’s twin sister)
      28 June 1958 → ages = 3 & 5 (two granddaughters, exactly 2 years apart)
      30 June 1955 → age = 6

      The program prints two solutions because the granddaughters born on 28th June could appear in either order.

      Like

    • Frits 3:09 pm on 29 June 2022 Permalink | Reply

         
      # dates: A,  B,  C,  D,  E,  F,  G,  H,  I  in order
      # ages : R,  S,  T,  U,  V,  W,  X,  Y,  Z  not necessarily in order
      
      '''
      Notation: XYZ stands for X + Y + Z
      
      The ages are all different, so the twins must be born either side of 
      midnight on the 24th, so John is one of the twins, and the other must
      have a birthday of the 25th. John is the youngest grandson, 
      so his (younger) twin must be a girl.
      
      E = 24, F = 25, V = 1, zero age is either W or X 
      
      After F (25) we can only have intervals 0, 2 and 3 otherwise I exceeds 30
      so I has to be 30 
      A has to be equal to E (24) - intervals 4, 5, 6 and 7, so A = 2
      
      Two children are as old as their dates of birth
      so R = A and S = B
      
      The sum of the ages of the last four is two-thirds of the sum of the
      ages of the first four, sum of all ages is 36, John in the middle is 1
      so we have to split the remaining 35 into a 21 and 14. 
      
      The sum of the years of birth of the first three is equal to the sum of 
      the years of birth of the last three, RST must be equal to XYZ + 3.
      
      2 * RSTU = 3 * WXYZ ==> 2 * RST + 2 * U = 3 * W + 3 * RST - 9
      ==> ST = 2 * U - 3 * W + 7  (using R = 2)
      
      RSTUVWXYZ = 36 
      
      R    [     ST      ]       V       [    XYZ      ]   
      2 +  2 * U - 3 * W + 7 + U + 1 + W + 2 * U - 3 * W + 6 = 36
      5 * (U - W) = 20 ==> U = W + 4
      (U, W) is (4, 0), (7, 3) or (8, 4)
      
      If W is not zero:
        X must be zero leaving (U, W) to be (7, 3) or (8, 4).
        Two daughters with same birthday are exactly two years apart.
        These must be Y and Z (not W as V is male and Y can't be 2)
        W + Y + Z = 14. if W is odd than Y and Z can't be two years apart.
        This leaves W = 4 and Y + Z = 10, only two years apart option for Y and Z
        is 4 and 6. This is impossible as W already is 4. So W = 0 and U = 4.
      '''
      
      # dates: 2,  B,  C,  D, 24, 25,  G,  H, 30  in order
      # ages : 2,  S,  T,  4,  1,  0,  X,  Y,  Z  not necessarily in order
      
      '''
      In order to have after F the intervals 2 and 3 either G or H must be 27 or 28
      so 109 <= FGHI <= 115.
      In order to have intervals 4, 5, 6, 7 we have 36 <= ABCD <= 46
      (using 2, 6, 11, 17 resp. 2, 9, 15, 20 for A, B, C, D). 
      
      So FGHI = 3 * ABCD (using multiplier three) leaving only values 37 and 38
      for ABCD (with FGHI resp. 111 and 114) .
      Using (2, 8, C, D) for (A, B, C, D) we get at least a sum of 39
      then (A, B, C, D) is either (2, 6, C, D) or (2, 7, C, D).
      
      As U = 4 RST must be 17, ST = 15 so R and S must use 7 and 8 
      leaving 3, 5 and 6 for X, Y and Z.
      
      As S is 7 or 8 and B is either 6 or 7 we can deduce R = 7, B = 7, S = 8.
      
      Using (2, 7, 13, 17) for (A, B, C, D) we get at a sum of 39 so 
      (A, B, C, D) is (2, 7, 11, 17). 
      '''
      
      # dates: 2,  7, 11, 17, 24, 25,  G,  H, 30  in order
      # ages : 2,  7,  8,  4,  1,  0,  X,  Y,  Z  not necessarily in order
      
      '''
      As a grandchild born in 1954 can only occur with age 8 and date before 25
      or age 7 and date after 24 we see the answer is 11th June 1954.
      '''
      

      Like

  • Jim Randell 3:11 pm on 19 June 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 77: Ali’s counter move 

    From The Sunday Times, 16th September 1962 [link]

    Ali and Baba are playing the Chay game, Ali has a bag containing 12 numbered tickets, 3 each of numbers 1, 2, 3, 4 (all numbers represented by strokes). Baba has 6 double-sided counters containing the same 12 numbers, and a board of 6 squares.

    Ali draws 6 tickets from his bag one at a time, calling out the number as he does so. At each call Baba selects a counter with that number on one of its sides and places it face up in a square. If in 6 calls he fills 6 squares he wins. Once a counter is laid it stays. The counter-couplings are so arranged that 5 squares could always be filled if the numbers were known beforehand.

    But, unnoticed by Baba, Ali has slyly added 1 stroke each to 2 of his opponent’s counters. As a result, Baba frequently places no more than 3 or 4 counters, and at last comes a deal when, after Ali has called “Two”, “One”, he calls a third number and Baba cannot fill it.

    It is the last straw.

    Baba, having lost many pice and much temper, angrily examines the four remaining counters. Three of them are identical couplings!

    “Ah! wicked one”, he cries, “you have forged my counters!”. And, throwing them down, he clears for action.

    What couplings are on these 4 counters?

    This puzzle was included in the book Sunday Times Brain  Teasers (1974, edited by Ronald Postill).

    [teaser77]

     
    • Jim Randell 3:14 pm on 19 June 2022 Permalink | Reply

      This Python program considers possible sets of counters that meet the specified conditions, and looks at ways to fake two of the counters so that there are 3 counters the same. We then match two of the fake set to the calls “1” and “2” and look to see if there is a third call that cannot be matched from the remaining counters. (It is a good opportunity to do some work with multisets).

      This Python program runs in 295ms.

      Run: [ @replit ]

      from enigma import (multiset, irange, subsets, product, union, printf)
      
      # update multiset <m>, by removing values in <rem>, and adding values in <add>
      def update(m, rem=None, add=None):
        if rem:
          m = m.difference(multiset.from_seq(rem))
        else:
          m = m.copy()
        if add:
          m.update_from_seq(add)
        return m
      
      # partition the multiset into <k>-tuples
      # return a multiset of <k>-tuples
      def mpartitions(m, k, ss=[]):
        # are we done?
        if not m:
          yield multiset.from_seq(ss)
        else:
          # choose the next <k>-tuple
          for s in subsets(m, size=k, select="mC"):
            t = tuple(sorted(s))
            if (not ss) or not (t < ss[-1]):
              yield from mpartitions(update(m, rem=s), k, ss + [t])
      
      # the tickets
      tickets = multiset.from_seq([1, 2, 3, 4], count=3)
      
      # possible sequences of tickets
      tss = list(tickets.subsets(size=6))
      
      # possible counter displays
      def display(cs, ss=multiset()):
        if not cs:
          yield ss
        else:
          # choose the next key
          (a, b) = k = cs.peek()
          n = cs[k]
          # and choose the split (how many showing a, the rest show b)
          for x in irange(0, n):
            # remaining counters
            cs_ = cs.copy().remove(k, n)
            ss_ = ss.copy().update_from_pairs([(a, x), (b, n - x)])
            yield from display(cs_, ss_)
      
      # check this set of counters
      def check(cs, ts):
        # consider each possible display
        for x in display(cs):
          # remove ticket sequences with at least 5 matches
          ts = list(t for t in ts if len(x.intersection(t)) < 5)
          if not ts: return True
        return False
      
      # fake a counter by incrementing one side
      def fake1(x, y):
        yield (x, y + 1)
        if x < y: yield (x + 1, y)
      
      # fake a set of counters
      def fake(cs):
        # choose 2 counters
        for (a, b) in subsets(cs, size=2, select="mC"):
          # fake them
          for (fa, fb) in product(fake1(*a), fake1(*b)):
            # replace the originals with the fakes
            yield update(cs, rem=(a, b), add=(fa, fb))
      
      # consider possible counters
      for cs in mpartitions(tickets, 2):
        # check these counters can match at least 5 of all tickets sequences
        if not check(cs, tss): continue
        printf("counters = {cs}", cs=sorted(cs))
      
        # make the fake set of counters
        for fcs in fake(cs):
          # there should be 3 counters the same
          if all(v < 3 for v in fcs.values()): continue
          printf("-> faked = {fcs}", fcs=sorted(fcs))
      
          # choose 2 counters to match the calls "1" and "2"
          for (x, y) in subsets(fcs, size=2, select="mP"):
            if not (1 in x and 2 in y): continue
            # remaining counters
            rs = update(fcs, rem=(x, y))
            # is there a call that cannot be matched?
            if len(union(rs.keys())) == 4: continue
      
            # output solution (rs)
            printf("-> remaining = {rs}; [1 -> {x}, 2 -> {y}]", rs=sorted(rs))
            printf()
      

      Solution: The four remaining counters are: 2|4, 2|4, 2|4, 3|4.


      There is only one set of counters that meets the specified conditions:

      1|2, 1|3, 1|4, 2|3, 2|4, 3|4

      These are then faked by incrementing the 3rd and 4th counters to give:

      1|2, 1|3, 2|4, 2|4, 2|4, 3|4

      Which has 3 copies of the 2|4 counter.

      For the call of “1” the 1|3 counter is chosen. For the call of “2” the 1|2 counter is chosen.

      This leaves: 2|4, 2|4, 2|4, 3|4, which cannot match a call of “1”.

      Like

    • Frits 12:10 pm on 21 June 2022 Permalink | Reply

      A lot easier to solve without using Python.

         
      # Suppose numbers in Ali's bag are a, b, c and d
      
      # ------ Check Baba's original set of counters -----------------
      # Can one counter have the same numbers on it?
      
      # Then fi we have counters a/a and a/x where x is b, c or d.
      # If Ali calls 3 a's and 3 x's then both his third a call and third x call 
      # can't be filled.   ===> each counter has 2 different numbers on it
      
      # Can 2 counters be the same?
      # Then fi we have counters a/b and a/b.
      # If Ali calls 3 a's followed by 3 b's then the last two b calls can't be
      # filled.   ===> no counter combination occurs more than once.
      
      # So Baba has to have 1/2, 1/3 and 1/4. He also needs to have 2/3 and 2/4
      # and finally 3/4.
      
      # so we have 1|2, 1|3, 1|4, 2|3, 2|4 and 3|4
      # --------------------------------------------------------------
      
      
      # In two counters a number is incremented by one, resulting in at least 
      # 3 counters with the same numbers x and y. This means the x|y counter must 
      # have been present in Baba's original set of counters.
      # x|y cannot be a counter like 1|z as there is only one option 1|(z-1) to 
      # produce an additional 1|z counter.   ===> 1|2, 1|3 and 1|4 are not x|y
      
      # x|y cannot be a counter like z|(z + 1) as there is only one option 
      # (z-1)|(z+1) to produce an additional z|(z + 1) counter.
      # ==> 2|3 and 3|4 are not x|y
      
      # so x|y is 2|4 and Baba's faked set being 1|2, 1|3, 2|4, 2|4, 2|4 and 3|4.
      # As Ali has called “Two” and “One” resp. 1|2 and 1|3 must have been placed
      # in a square.   ===> the four remaining counters are 2|4, 2|4, 2|4 and 3|4
      

      Like

  • Jim Randell 9:53 am on 29 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 110: Rectangular blocks 

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

    Two rectangular blocks have all their dimensions an exact number of inches, all different.

    The sum of the edges of one block is equal to the sum of the edges of the other.

    The sum of the areas of the faces of one block is equal to the sum of the areas of the faces of the other.

    The volumes of the two blocks, however, differ by 140 cubic inches.

    If the smallest of the 6 dimensions of the two blocks is 5 inches, what are the dimensions of the blocks?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser110]

     
    • Jim Randell 9:54 am on 29 May 2022 Permalink | Reply

      For blocks with dimensions (x, y, z) this Python program consider blocks that be made with the same (x + y + z) value, and then looks for two with the same surface area, but with areas that differ by 140.

      It runs in 62ms. (Internal runtime is 4.7ms).

      Run: [ @replit ]

      from enigma import (irange, inf, decompose, subsets, multiply, printf)
      
      # for comparing surface areas
      area = lambda ds: 2 * sum(x * y for (x, y) in subsets(ds, size=2))
      
      # for calculating volumes
      vol = multiply
      
      # find the first solution
      def main():
        # consider increasing x + y + z values
        for t in irange(18, inf):
          # consider possible dimensions for the blocks
          for (b1, b2) in subsets(decompose(t, 3, increasing=1, sep=1, min_v=5), size=2):
            ds = set(b1 + b2)
            if not(len(ds) == 6 and 5 in ds): continue
            if not(area(b1) == area(b2)): continue
            if not(abs(vol(b1) - vol(b2)) == 140): continue
            printf("t={t}: {b1} {b2}")
            return  # we only need the first value
      
      main()
      

      Solution: The dimensions of the blocks are: (5 × 14 × 17) inches, (7 × 10 × 19) inches.

      Like

    • Jim Randell 11:38 am on 29 May 2022 Permalink | Reply

      Or, using some analysis:

      If the blocks have dimensions (a, b, c) and (x, y, z), then we have (assuming (a, b, c) has the larger volume):

      a + b + c = x + y + z
      ab + ac + bc = xy + xz + yz
      abc − xyz = 140

      Now, if we consider the expression:

      (a − 5)(b − 5)(c − 5) − (x − 5)(y − 5)(z − 5)
      = (abc − 5(ab + ac + bc) + 25(a + b + c) − 125) − (xyz − 5(xy + xz + yz) + 25(x + y + z) − 125)
      = 140

      We note that one side of the subtraction must equal zero, so the other is ±140.

      And as all the numbers multiplied together in each side of the subtraction are non-negative, it must be the right-hand term that is zero. Hence x = 5.

      So we can look at divisors of 140 to determine the dimensions (a, b, c), and then choose (x, y, z) to give the same sum, and check the surface area constraint.

      Here is a Python program that fully explores the solution space. It runs in 57ms. (Internal run time is 236µs).

      Run: [ @replit ]

      from enigma import (subsets, divisors_tuples, is_pairwise_distinct, div, printf)
      
      # surface area
      area = lambda *ds: 2 * sum(x * y for (x, y) in subsets(ds, size=2))
      
      # start with x = 5
      x = 5
      # calculate dimensions for (a, b, c)
      for (a, b, c) in divisors_tuples(140, 3):
        (a, b, c) = (a + x, b + x, c + x)
        if not is_pairwise_distinct(a, b, c, x): continue
        # find possible y, z values, given xyz = abc - 140
        n = div(a * b * c - 140, x)
        if n is None: continue
        for (y, z) in divisors_tuples(n, 2):
          if not(x < y < z and x + y + z == a + b + c): continue
          # all dimensions are distinct
          if not is_pairwise_distinct(x, y, z, a, b, c): continue
          # check the areas
          if area(a, b, c) != area(x, y, z): continue
          # output solution
          printf("{b1} {b2}", b1=(x, y, z), b2=(a, b, c))
      

      Like

      • Mark Valentine 12:42 pm on 30 May 2022 Permalink | Reply

        Nice trick to factorise with x-5 to obtain the zero. I was factorising with x+1 not getting anywhere.

        Like

      • Frits 1:44 pm on 31 May 2022 Permalink | Reply

        @Jim,

        I you disable the area check in the first program you get answer (5, 6, 14) (7, 8, 10) and if you disable in the second program you get anwer (5, 14, 17) (7, 10, 19).

        Like

      • Frits 2:16 pm on 31 May 2022 Permalink | Reply

        @GeoffR, the problem with MiniZinc most of the times that you have to set boundaries.
        You have chosen 25 as upper limit.

        Like

        • GeoffR 6:59 pm on 31 May 2022 Permalink | Reply

          @Frits:
          The teaser description gives us a means to fix the LB.

          With no prior knowledge of the solution and no logical means to set the UB, I guess the UB could be set fairly high to try and find a solution in MiniZinc. It could then reduced by trial and error if an improvement in total run-time was needed.

          I did an experiment for this teaser, varying the upper bound, to see the variation in run-time to find a solution and the total run-time:

          Upper bound = 25 (Soln = 170ms, Total time = 170ms)
          Upper bound = 50 (Soln = 170ms, Total time = 180ms)
          Upper bound = 100 (Soln = 170ms, Total time = 230ms)
          Upper bound = 200 (Soln = 170ms, Total time = 530ms)

          Absolute boundaries do not seem that important practically in this case.

          Like

      • Frits 8:41 pm on 31 May 2022 Permalink | Reply

           
        from enigma import is_square_p
        
        # dimensions are (a + 5), (b + 5), (c + 5) and 5, y, z
        
        # as (4, 5, 7) is the 140-factorization with the highest lowest factor
        # a is less equal to 4 (and not divisble by 3)
        for a in [1, 2, 4]:
          # as (1, 10, 14) is the 140-factorization with the highest middle factor
          # b is less equal to 10 (and not divisble by 3 or 8)
          for b in [2, 4, 5, 7, 10]:
            if b <= a: continue
            
            # a * b * c = 140
            (c, r) = divmod(140, a * b)
            if r or c <= b: continue
            
            # sum of edges is the same
            # a + b + c + 15 - 5 - y = z
            # volume difference is equal to 140
            # ((a + 5) * (b + 5) * (c + 5) - 140) / 5y = z
            
            # (5 * y) * (a + b + c + 10 - y) = ((a + 5) * (b + 5) * (c + 5) - 140) 
            # 5y^2 - 5 * (a + b + c + 10) * y + (a + 5) * (b + 5) * (c + 5) - 140 = 0
            
            # calculate discriminant
            D = (5 * (a + b + c + 10))**2 - 20 * ((a + 5) * (b + 5) * (c + 5) - 140)
            if D < 0 or not is_square_p(D): continue
            
            # choose lowest solution as y < z
            y = (5 * (a + b + c + 10) - D**.5) // 10 
            z = a + b + c + 15 - 5 - y
            print((a + 5, b + 5, c + 5), (5, y, z))
        

        Like

    • GeoffR 12:45 pm on 29 May 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 5..25:a; var 5..25:b; var 5..25:c; % 1st block
      
      var 6..25:x; var 6..25:y; var 6..25:z; % 2nd block (bigger)
      
      constraint all_different([a, b, c, x, y, z]);
      
      constraint a == 5;
      constraint a < b /\ b < c;
      constraint x < y /\ y < z;
      
      % The sum of the edges of one block is equal
      % to the sum of the edges of the other.
      constraint 4 * (a + b + c) == 4 * (x + y + z);
      
      % The sum of the areas of the faces of one block is equal 
      % to the sum of the areas of the faces of the other.
      constraint 2 * (a * b + a * c + b * c) == 2 *(x * y + y * z + x * z);
      
      % The volumes of the two blocks differ by 140 cubic inches.
      constraint x * y * z == a * b * c + 140;
      
      solve satisfy;
      
      output[ "First block dimensions(inches) : " ++ show(a) ++ 
      " by " ++ show(b) ++ " by " ++ show(c) 
      ++ "\n" ++ "Second block dimensions(inches) : " ++ show(x) 
      ++ " by " ++ show(y) ++ " by " ++ show(z)];
      
      % First block dimensions(inches) : 5 by 14 by 17
      % Second block dimensions(inches) : 7 by 10 by 19
      % ----------
      % ==========
      
      
      

      Like

  • Jim Randell 10:28 am on 22 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 87: Inter-schools trophy 

    From The Sunday Times, 25th November 1962 [link]

    Annually in the Easter and summer terms from 1957 Ardington and Bradling competed in five matches at different sports. Points were given for win (6 each for cricket and football, 4 each for hockey, swimming and athletics), the points being shared equally in the case of a draw or tie. The total points score decides the annual winner of the Topp Cup.

    From 1957 to 1961 inclusive Ardington, won the cup three times and Bradling won it twice, but their grand totals of points were then equal. The winning margin decreased each year, from 12 points in 1957 to 2 points in 1961.

    In each of the sports there was [exactly] one draw or tie; the hockey being drawn in 1959. Bradling last won the cricket in 1958, a year in which they won the cup. Ardington have never won the swimming but have the better record at athletics (which they won in 1957).

    What were the results of all matches in the series?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser87]

     
    • Jim Randell 10:29 am on 22 May 2022 Permalink | Reply

      I made a MiniZinc model to solve this puzzle:

      %#! minizinc -a --solver chuffed --output-mode json
      
      % results for A
      % x[<year>, <event>] = 0 (loss) | 1 (draw) | 2 (win)
      % <year> = 1 (1957) .. 5 (1961)
      % <event> = 1 .. 5 (C, F, H, S, A)
      array [1..5, 1..5] of var 0..2: x;
      
      % points for each year
      function var int: ptsA(var int: i) = 3 * (x[i, 1] + x[i, 2]) + 2 * (x[i, 3] + x[i, 4] + x[i, 5]);
      function var int: ptsB(var int: i) = 24 - ptsA(i);
      
      % cumulative points
      function var int: cumA(var int: y) = sum (i in 1..y) (ptsA(i));
      function var int: cumB(var int: y) = sum (i in 1..y) (ptsB(i));
      
      % A won the cup 3 times, B won the cup twice
      constraint sum (i in 1..5) (ptsA(i) > ptsB(i)) = 3;
      constraint sum (i in 1..5) (ptsB(i) > ptsA(i)) = 2;
      
      % final cumulative total is equal
      constraint cumA(5) = cumB(5);
      
      % winning margin decreased each year ...
      constraint forall (i in 1..4) (abs(ptsA(i) - ptsB(i)) > abs(ptsA(i + 1) - ptsB(i + 1)));
      % ... from 12 points in 1957 ...
      constraint abs(ptsA(1) - ptsB(1)) = 12;
      % ... to 2 points in 1961
      constraint abs(ptsA(5) - ptsB(5)) = 2;
      
      % each sport has exactly one tie
      constraint forall (j in 1..5) (sum (i in 1..5) (x[i, j] = 1) = 1);
      
      % H was drawn in 1959
      constraint x[3, 3] = 1;
      
      % B last won C in 1958
      constraint x[2, 1] = 0;
      constraint forall (i in 3..5) (x[i, 1] > 0);
      
      % B won the cup in 1958
      constraint ptsA(2) < ptsB(2);
      
      % A have never won the swimming
      constraint forall (i in 1..5) (x[i, 4] < 2);
      
      % team A have a better record at event A
      constraint sum (i in 1..5) (x[i, 5]) > 5;
      
      % team A won event A in 1957
      constraint x[1, 5] = 2;
      
      solve satisfy;
      

      And a Python wrapper to format the output (using the minizinc.py library):

      from enigma import join, printf
      from minizinc import MiniZinc
      
      for [x] in MiniZinc("teaser87.mzn").solve(result='x', use_shebang=1):
        printf("      C F H S A | pA pB | cA cB")
        cA = cB = 0
        for (y, vs) in enumerate(x, start=1957):
          # points for A, B
          pA = sum(x * y for (x, y) in zip(vs, [3, 3, 2, 2, 2]))
          pB = 24 - pA
          # cumulative totals
          cA += pA
          cB += pB
          # output the table row
          r = join(("BdA"[x] for x in vs), sep=" ")
          printf("{y}: {r} | {pA:2d} {pB:2d} | {cA:2d} {cB:2d}")
        printf()
      

      Solution: The full results are (d = drawn):

      1957: cricket = A, football = A, hockey = B, swimming = d, athletics = A; cup = A (18 – 6)
      1958: cricket = B, football = d, hockey = B, swimming = B, athletics = A; cup = B (17 – 7)
      1959: cricket = A, football = B, hockey = d, swimming = B, athletics = B; cup = B (16 – 8)
      1960: cricket = A, football = A, hockey = B, swimming = B, athletics = d; cup = A (14 – 10)
      1961: cricket = d, football = A, hockey = B, swimming = B, athletics = A; cup = A (13 – 11)

      At the end of the 5 years each team has a cumulative total of 60 points each.

      The winning margin for each year is: 12, 10, 8, 4, 2.

      Like

  • Jim Randell 10:47 am on 26 September 2021 Permalink | Reply
    Tags:   

    Brain-Teaser: Silver collection 

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

    Seven South Sea Island brothers found on the beach a broken box and mixed silver coins scattered. These were old British coins (six-pence, shilling, florin, half-crown, and crown). 135 of them bore a man’s head and 54 a woman’s. The two younger brothers claimed the latter and their elders shared the former.

    Being ignorant of the value of the coins they agreed to take twenty-seven each. First they laid the coins in heaps of each size. The senior brother then took seven coins from each of two heaps and smaller numbers from the other three heaps to make up twenty-seven coins. Each other brother then took the same numbers but each from a different order of heaps.

    Unknown to themselves, the five elders had equal money value. The boys also had equal value, but greater than their elders.

    What were the values?

    [Note: Respective values: 6d, 12d, 24d, 30d, 60d]

    This is one of the occasional Holiday Brain-Teasers published in The Sunday Times prior to the start of numbered Teasers in 1961. A prize of 3 guineas was offered.

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill). The above text is taken from the book.

    [teaser-1960-07-31] [teaser-unnumbered]

     
    • Jim Randell 10:49 am on 26 September 2021 Permalink | Reply

      This Python program runs in 51ms.

      Run: [ @replit ]

      from enigma import decompose, group, subsets, printf
      
      # denominations of coins
      denominations = [6, 12, 24, 30, 60]
      
      # total for quantities of coins in qs (wrt denominations)
      total = lambda qs: sum(x * y for (x, y) in zip(qs, denominations))
      
      # decompose 27 - 14 = 13 into 3 numbers between 1 and 6
      for qs in decompose(13, 3, min_v=1, max_v=6, sep=0):
        qs += (7, 7)
      
        # collect permutations by total amount
        d = group(subsets(qs, size=len, select="mP"), by=total)
      
        # look for 5 permutations for the elders
        for (k1, vs1) in d.items():
          if not(len(vs1) > 4): continue
          # and 2 permutations, with a greater sum for the youngers
          for (k2, vs2) in d.items():
            if not(k2 > k1 and len(vs2) > 1): continue 
            printf("elders = {k1} [from {vs1}]")
            printf("youngers = {k2} [from {vs2}]")
            printf()
      

      Solution: The elders had selected coins with a value of 798d (= 66s 6d). The youngers had selected coins with a value of 834d (= 69s 6d).

      Like

    • Hugh Casement 3:13 pm on 26 September 2021 Permalink | Reply

      I confess I cheated by working backward from Jim’s solution, but was able to deduce that there were 48 crowns, 44 half crowns, 38 florins, 32 shillings, and 27 sixpences.
      Total 189 coins with a value £23 11s 6d = 5658 d.

      If the five piles were arranged in order of decreasing value, the seniors (in some order) took
      7, 7, 4, 3, 6; 7, 7, 3, 6, 4; 7, 6, 4, 7, 3; 7, 4, 7, 6, 3; 6, 7, 7, 3, 4 coins from the respective piles.
      The youngsters took 7, 7, 6, 3, 4 and 7, 6, 7, 4, 3.

      There would be other ways of making up the values 798 and 834 with 27 coins, but not with as many as five of one and two of the other, using the same numbers in different orders.

      Like

  • Jim Randell 9:13 am on 16 September 2021 Permalink | Reply
    Tags: by: Adrian Winder   

    Brain-Teaser 186 

    From The Sunday Times, 15th November 1964 [link]

    A is 73 feet from a straight river, and B is on the same side of the river but not so far from it. M and N are the (distinct) points on the river nearest to A and B respectively. The lengths of AB, MN, and BN are whole numbers of feet.

    A man walks from A to B via the river, taking the shortest possible route, and this is also whole number of feet.

    How far does the man walk, and what is the direct distance from A to B?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    The puzzle is included as a postscript to the puzzles in the book, and the following text appears in the foreword:

    Perhaps the most outstanding example of teasing is Brain Teaser 186, of 15th November 1964, which was based on a deceptively simple diagram by an undergraduate, Adrian Winder, who died in a road accident just before his puzzle was published. Few correct answers were submitted; within the year there were 250 requests for the full solution; and still, from time to time, yet another reader concedes defeat and begs to be relieved of his misery.

    Mr Winder’s problem, with his solution, is published as a fitting postscript to this collection.

    — Anthony French

    This brings the total number of puzzles available on S2T2 to 550, but this is less than 20% of the Brain Teaser puzzles published in The Sunday Times.

    [teaser186]

     
    • Jim Randell 9:14 am on 16 September 2021 Permalink | Reply

      The direct route (h = AB) and the indirect route (z = AB′ = z1 + z2) are the hypotenuses of right-angled triangles (AXB and AXB′ respectively):

      h² = x² + d²
      z² = (146 − x)² + d²

      For a given value of x we can determine values for h, d and z by looking at the divisors of x²:

      h² − d² = x²
      (h − d)(h + d) = x² = a⋅b
      h = (a + b) / 2
      d = (b − a) / 2
      z = √((146 − x)² + d²)

      This Python program runs in 51ms. (Internal run time is 1.3ms).

      Run: [ @replit ]

      from enigma import (irange, divisors_pairs, div, is_square, printf)
      
      for x in irange(1, 72):
        for (a, b) in divisors_pairs(x * x):
          h = div(a + b, 2)
          d = div(b - a, 2)
          if not (h and d): continue
          t = 146 - x
          z = is_square(t * t + d * d)
          if not z: continue
          printf("x={x}; h={h} d={d} z={z}")
      

      Solution: The man walks 169 ft between A and B. The direct distance is 123 ft.

      B is 46 ft from the river (BN = 46). And M and N are 120 ft apart (MN = 120).

      Like

    • Frits 11:32 am on 16 September 2021 Permalink | Reply

      This Python program runs in 75ms (with PyPy).

        
      from enigma import SubstitutedExpression, is_square
      
      # h^2 = x^2 + d^2                      d < 2592 ((72^2 - 1) / 2) 
      # z^2 = (146 – x)^2 + d^2  
      
      # the alphametic puzzle
      p = SubstitutedExpression([
          "DE < 26",
          "0 < XY < 73", 
          "is_square(XY**2 + DEFG**2) = HIJK",
          "is_square((146 - XY)**2 + DEFG**2) = ZABC",
          "DEFG > 0",
        ],
        answer="(ZABC, HIJK)",
        distinct="",        # allow symbols with same values
        d2i=dict([(k, "D") for k in range(3, 10)]),
        verbose=256,
      )
       
      # print answers
      for (_, ans) in p.solve(verbose=0):
        print(f"The man walks {ans[0]} ft between A and B. "
              f"The direct distance is {ans[1]} ft.")
      

      Like

    • Frits 12:34 pm on 16 September 2021 Permalink | Reply

      This Python program runs in 4ms.

        
      from enigma import pythagorean_triples, is_square
      
      # h^2 = x^2 + d^2                      h < 2593 ((72^2 + 1) / 2) 
      # z^2 = (146 – x)^2 + d^2  
      # x < 73
      
      for (p, q, h) in pythagorean_triples(2592):
        if p > 72: continue
        if q > 72:
          # (x, d) must be (p, q)
          z = is_square((146 - p)**2 + q**2)
        else:
          # (x, d) can be (p, q) or (q, p)
          z = is_square((146 - p)**2 + q**2)
          if not z:
            z = is_square((146 - q)**2 + p**2)
          
        if z:
          print(f"The man walks {z} ft between A and B. "
                f"The direct distance is {h} ft.")
      

      Like

      • Jim Randell 1:58 pm on 16 September 2021 Permalink | Reply

        Here’s a version that only uses the list of Pythagorean triples (and doesn’t use [[ is_square() ]]).

        from enigma import (pythagorean_triples, ordered, printf)
        
        # we can show:
        #
        #   d <= (x^2 - 1) / 2
        #
        # and max x = 72, so:
        #
        #   d < 2592
        #
        # hence:
        #
        #   z^2 <= 145^2 + 2592^2
        #   z < 2597
        
        # find pythagorean triples, map: (a, b) -> c
        triples = dict(((a, b), c) for (a, b, c) in pythagorean_triples(2597))
        
        # consider triples
        for ((a, b), c) in triples.items():
          if a > 72: continue
          # consider x=a, d=b, h=c
          z = triples.get(ordered(b, 146 - a))
          if z:
            printf("x={a}; h={c} d={b} z={z}")
          if b < 73:
            # consider x=b, d=a, h=c
            z = triples.get(ordered(a, 146 - b))
            if z:
              printf("x={b}; h={c} d={a} z={z}")
        

        Although it is not quite as fast as my original program.

        Like

    • John Crabtree 2:52 pm on 22 September 2021 Permalink | Reply

      In theory there should be a way to solve brain teasers manually. Today I take this to mean without the use of computers or programable calculators. When this teaser was published in 1964, handheld calculators were some way off.

      Putting h = d + m and z = d + n, and then
      d = (x^2 – m^2)/(2m) = ((146 – x)^2 – n^2)/(2n) with n > m
      where if x is odd, m and n are odd, and if x ix even, m and n are even.
      Effectively, this is a way of finding Pythagorean triples with a common shorter side.

      Then for each value of x, one can look for values of m and n which give the same value of d. I did this with the aid of a calculator. I could have done it by hand with a table of squares, but it would have been tedious. The ratio of n/m is approximately (146 – x)^2/x^2. One can use this to reduce the number of calculations. The results took up two and half sides of paper. I found x = 27, m = 3, n = 49, which gave d = 120. The solution = d + n = 169.

      In summary, this is very challenging but accessible teaser. If Brian Gladman’s table of Pythagorean triples sorted by shorter sides went to 140, it would be very straightforward.

      Like

      • John Crabtree 5:02 pm on 22 September 2021 Permalink | Reply

        The man walks d + n = 169 feet. The direct distance is d + m = 123 feet.

        Like

  • Jim Randell 10:28 am on 27 June 2021 Permalink | Reply
    Tags:   

    Brain-Teaser 37: Which page? 

    From The Sunday Times, 3rd December 1961 [link]

    A student desiring to find a reference in a book, could not remember the actual page number, but only the three figures comprising it.

    Before beginning his search, he wrote down in ascending order the six possible variants of the three figures. In writing the second variant his pencil broke on
    a figure which thereafter was illegible. With a fresh pencil he completed the list of variants and then found his reference, the page number being the second variant.

    Some time later he found in his pocket the scrap of paper bearing the six numbers. Idly adding them he obtained a total of 4,796, having unwittingly misread the illegible figure.

    On what page was the reference?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser37]

     
    • Jim Randell 10:29 am on 27 June 2021 Permalink | Reply

      If he had written out each of the 6 numbers correctly (the digits being a, b, c), then each digit would appear twice in each position so the total sum of the six numbers would be:

      T = 222×(a + b + c)

      If the misinterpreted digit is off by x then the total sum is off by 100x if the first digit of the number is misinterpreted, 10x if its the second digit, and x if its the final digit.

      This Python program runs in 49ms.

      Run: [ @replit ]

      from enigma import irange, subsets, div, nconcat, printf
      
      M = 4796  # mistaken total
      
      # check to see if "abc" can be misread by d
      def check(a, b, c, d):
        # error in hundreds position? (a)
        x = div(abs(d), 100)
        if x:
          return 0 < (a + x if d > 0 else a - x) < 10
        # error in tens position? (b)
        x = div(abs(d), 10)
        if x:
          return 0 < (b + x if d > 0 else b - x) < 10
        # error in units position? (c)
        return 0 < (c + d) < 10
      
      # consider possible digits in the number
      for (a, b, c) in subsets(irange(1, 9), size=3):
        # the total sum should be...
        T = 222 * (a + b + c)
        # and the difference from the calculated sum
        d = M - T
        # digits of the 2nd number in the list are a, c, b
        if d != 0 and check(a, c, b, d):
          n = nconcat(a, c, b)
          printf("reference = {n} [read as {m}]", m=n + d)
      

      Solution: The reference was on page 198.

      So the sum of the numbers should have been 3396, but the second number was read as 998, adding 800 to the sum.

      Like

  • Jim Randell 10:47 am on 3 May 2020 Permalink | Reply
    Tags:   

    Brain-Teaser 519: Badberg clock 

    From The Sunday Times, 23rd May 1971 [link]

    At the village of Badberg, hidden away in the Alps, there is a town clock of a certain antiquity. The maker was a wealthy but inexpert amateur. After the ornate instrument had been installed it was found that the great hands stood still during the equal intervals between each stroke of the hour on the massive bell. Naturally, the clock was always slow.

    The placid villagers became accustomed to the erratic behaviour of their timepiece; only after the death of its donor did his nephew dare to tackle the problem. Finding it impossible to alter the striking mechanism, he ingeniously modified the movement to run at a higher constant speed so that the hands showed the correct time at least when the clock struck certain hours.

    Unfortunately, the hands still stop for the same period between successive strokes of the bell, but the villages can now see and hear the correct time every six hours.

    At what hours does the clock make its first stroke correctly?

    This puzzle was included in the book Sunday Times Brain Teasers (1974, edited by Ronald Postill).

    [teaser519]

     
    • Jim Randell 10:47 am on 3 May 2020 Permalink | Reply

      The clock only pauses between the strokes, so at 1 o’clock it doesn’t pause. At 2 o’clock it pauses for 1 interval. At 3 o’clock, 2 intervals. And so on until 12 o’clock when it pauses for 11 intervals. So the total time lost in a 12 hour period is 66 intervals.

      And the speed of the clock is adjusted so that the time lost in these 66 intervals is made up during the 12 hours. So we need to find adjacent 6 hour periods each containing 33 intervals. And there are only 6 pairs of periods to consider.

      Run: [ @repl.it ]

      from enigma import irange, tuples, printf
      
      # "clock" arithmetic
      clock = lambda n, m=12: (n % m) or m
      
      # interval count for each hour
      ps = list(irange(0, 11))
      
      # find a consecutive sequence of 6 intervals that is half the total
      t = sum(ps)
      for (i, s) in enumerate(tuples(ps, 6)):
        if sum(s) * 2 == t:
          (h1, h2) = (i + 1, i + 7)
          printf("{h1}, {h2}", h1=clock(h1), h2=clock(h2))
      

      Solution: The clock is correct at 4 o’clock and 10 o’clock.

      In striking 4, 5, 6, 7, 8, 9 o’clock the clock pauses for 3 + 4 + 5 + 6 + 7 + 8 = 33 intervals.

      In striking 10, 11, 12, 1, 2, 3 o’clock the clock pauses for 9 + 10 + 11 + 0 + 1 + 2 = 33 intervals.

      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
%d bloggers like this: