Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 8:29 am on 13 September 2019 Permalink | Reply
    Tags: ,   

    Teaser 2783: Old boys’ banquet 

    From The Sunday Times, 24th January 2016 [link] [link]

    George and Martha have arranged the seating plan for the annual Old Boys banquet; it involves a number of tables, each seating 50 diners. More than half the Old Boys are bringing one female guest each and the rest are coming alone. Martha wrote down three numbers, namely the number of Old Boys bringing a guest, the number of Old Boys coming alone, and the total number of Old Boys coming. George noted that the three numbers between them used each of the digits 0 to 9 exactly once.

    How many Old Boys are bringing a guest, and how many are coming alone?

    [teaser2783]

     
    • Jim Randell's avatar

      Jim Randell 8:30 am on 13 September 2019 Permalink | Reply

      If s is the number of old boys without guests and g is the number with guests, then we have a pandigital sum of the form:

      s + g = b

      where s < g.

      Altogether there are 10 digits used so, alphametically, the sum is one of:

      AB + CDEF = GHIJ
      ABC + DEF = GHIJ

      This Python program uses the [[ SubstitutedSum() ]] solver from the enigma.py library to examine solutions to both sums. It runs in 277ms.

      Run: [ @repl.it ]

      from enigma import SubstitutedSum, div, printf
      
      # solve the pandigital sum
      digits = 'ABCDEFGHIJ'
      for k in (2, 3):
        (x, y, z) = (digits[0:k], digits[k:6], digits[6:])
        p = SubstitutedSum([x, y], z)
        for s in p.solve():
          # turn the solution into numbers
          (s, g, b) = (int(p.substitute(s, w)) for w in (x, y, z))
          # more than half the old boys have guests
          if not (g > s): continue
          # so the total number of people is...
          t = b + g
          # and the number of tables is...
          n = div(t, 50)
          if n is None: continue
        
          printf("s={s} g={g} boys={b}, people={t} tables={n}")
      

      Solution: There are four possible solutions:

      26 without guests, 4987 with guests; 5013 total boys, 10000 total people, 200 tables
      74 without guests, 5938 with guests; 6012 total boys, 11950 total people, 239 tables
      86 without guests, 1957 with guests; 2043 total boys, 4000 total people, 80 tables
      346 without guests, 752 with guests; 1098 total boys, 1850 total people, 37 tables

      I suspect the setter intended the final solution to be the correct answer, which is the only solution where the pandigital sum is of the form: ABC + DEF = GHIJ.

      A suitable upper limit on the total number of old boys, total number of people, or number of tables would eliminate the other solutions.

      However if we change the condition in the puzzle text to:

      More than half the Old Boys are coming alone and the rest are bringing one female guest each.

      (i.e. we require s > g).

      Then there is only a single solution to the puzzle:

      746 without guests, 352 with guests; 1098 total boys, 1450 total people, 29 tables

      Reversing the inequality at line 12 of the program will give this solution.

      Like

    • GeoffR's avatar

      GeoffR 1:59 pm on 14 September 2019 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn"; 
      
      var 0..9: a; var 0..9: b; var 0..9: c; var 0..9: d; var 0..9: e;
      var 0..9: f; var 0..9: g; var 0..9: h; var 0..9: i; var 0..9: j;
      
      % Assume boys/boys with guest/total boys are in ratio 3:3:4
      var 100..999: abc;     % boys with guest
      var 100..999: def;     % boys alone
      var 1000..2000: ghij;  % total boys
      var 0..250: tables;    % total tables
      
      % All ten digits are used exactly once
      constraint alldifferent([a,b,c,d,e,f,g,h,i,j]) 
                 /\ a > 0 /\ d > 0 /\ g > 0;
      
      constraint  abc = 100*a + 10*b + c
               /\ def = 100*d + 10*e + f
               /\ ghij = 1000*g + 100*h + 10*i + j ;
      
      % More than half the boys brought a female guest
      constraint abc > ghij div 2;
      
      constraint abc + def == ghij;
      
      constraint tables * 50 == abc * 2 + def;
      
      solve satisfy;
      
      output [" Boys with a guest: " ++ show(abc) ++ "\n" ++
              " Boys on their own: " ++ show(def) ++ "\n" ++
              " Total boys: " ++ show(ghij) ++ "\n" ++
              " Tables: " ++ show(tables)];  
      
      % Boys with a guest: 752
      % Boys on their own: 346
      % Total boys: 1098
      % Tables: 37
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:03 am on 10 September 2019 Permalink | Reply
    Tags: by: David W Poyner   

    Brain-Teaser 499: [League table] 

    From The Sunday Times, 20th December 1970 [link]

    Paddy was in a reminiscent mood. “Sure it was a good year for Ireland, for didn’t we beat England by 4 goals to 1 and win the championship?” he said, proudly showing us the league table of the championship in which each country had played against each of the other three.

    “Of course”, he added slyly, “you can now work out the score in one of the other matches”.

    Which match? And what was the score?

    This puzzle was originally published with no title.

    [teaser499]

     
    • Jim Randell's avatar

      Jim Randell 9:04 am on 10 September 2019 Permalink | Reply

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

      from enigma import (Football, digit_map, Accumulator, printf)
      
      # scoring system
      football = Football(games='wdl')
      
      # labels for the teams (in table order)
      order = (I, W, E, S) = (0, 1, 2, 3)
      teams = "IWES"
      
      # values stand for themselves
      d = digit_map(0, 9)
      
      # columns of the table
      (table, gf, ga) = (dict(w="3110", d="0101", l="0122"), "9565", "3589")
      
      # known matches
      (ms0, ss0) = ({ (I, E): 'w' }, { (I, E): (4, 1) })
      
      # find scores common to each scenario
      r = Accumulator(fn=lambda s, v: s.intersection(v))
      
      # find possible match outcomes
      for (ms, _) in football.substituted_table(table, d=d, matches=ms0):
      
        # find possible scores
        for ss in football.substituted_table_goals(gf, ga, ms, d=d, scores=ss0):
      
          # output scenario
          football.output_matches(ms, ss, teams=teams)
      
          # record scores
          r.accumulate(set(ss.items()))
      
      # output fixed scores (other than for I vs E)
      printf("SOLUTION:")
      for ((x, y), (sx, sy)) in r.value:
        if (x, y) == (I, E): continue
        printf("-> {x} vs {y} = {sx}-{sy}", x=teams[x], y=teams[y])
      

      Solution: The score in the Wales vs Scotland match must be 2-2.

      There are 5 different scenarios for the scores, but in each of them W vs S is 2-2.

      Like

      • John Crabtree's avatar

        John Crabtree 3:39 pm on 10 September 2019 Permalink | Reply

        As league table puzzles go, this is an easier one.

        If the score in IvW is x – y, and the score in WvS is d – d, the other scores are:
        IvS (5 – x) – (2 – y)
        WvE (5 – d – y) – (5 – d – x)
        EvS (d + x) – (d + y – 1)

        Looking at the goals scored by Scotland, 5 = d + (2 – y) + (d + y – 1) = 2d + 1, ie d = 2

        And so the score in Wales v. Scotland is 2 – 2

        By inspection, the possible values for (x,y) are (1, 0), (2, 1), (2,0), (3, 1), and (3, 2)

        Like

  • Unknown's avatar

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

    Teaser 2802: That’s the ticket! 

    From The Sunday Times, 5th June 2016 [link] [link]

    Alan, Betty and Charlie each chose a different set of six numbers (from 1 to 49) for their lottery ticket. In each case the product of the six numbers was a perfect square and also each set of six numbers used each of the digits 0 to 9 exactly once.

    Alan won the lottery by getting all six numbers correct. Betty and Charlie also got prizes because they each had at least three numbers correct.

    What were Alan’s six numbers?

    [teaser2802]

     
    • Jim Randell's avatar

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

      We have six numbers (between 1 and 49) that between them use 10 digits (each of the digits 0-9 exactly once). So four of the numbers must have 2 digits, and their tens digits are all different, so the set of six numbers is of the form (alphametically):

      a, b, 1c, 2d, 3e, 4f

      where a, b, c, d, e, f are the digits 0, 5, 6, 7, 8, 9 (in some order).

      It turns out there are only three possible sets of six numbers that use the digits 0-9 exactly once and multiply together to give a square. Fortunately one of them shares 3 or more numbers with the other two, so this gives us A’s numbers.

      This Python program runs in 36ms.

      Run: [ @repl.it ]

      from enigma import subsets, multiply, is_square, printf
      
      # find possible sets of numbers
      ss = list()
      for (a, b, c, d, e, f)  in subsets((0, 5, 6, 7, 8, 9), size=len, select="P"):
        if not (0 < a < b): continue
        s = (a, b, 10 + c, 20 + d, 30 + e, 40 + f)
        # we only want sets whose product is a perfect square
        if not is_square(multiply(s)): continue
        ss.append(set(s))
        printf("{s}")
      
      # now choose a set for A
      for A in ss:
        # and find other sets that share at least 3 numbers
        BC = list(s for s in ss if s != A and len(A.intersection(s)) > 2)
        if len(BC) > 1:
          printf("A={A}, BC={BC}", A=sorted(A), BC=list(map(sorted, BC)))
      

      Solution: Alan’s numbers were: 8, 9, 16, 27, 30, 45.

      There are only two options for Betty and Charlie:

      5, 8, 16, 27, 30, 49 = 4 matched numbers
      8, 9, 15, 27, 36, 40 = 3 matched numbers

      Like

    • GeoffR's avatar

      GeoffR 9:47 am on 19 September 2019 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Let digits used be (a, b, cd, ef, gh, ij) for the six numbers
      
      predicate is_sq(var int: y) =
        let {
           var 1..ceil(sqrt(int2float(ub(y)))): z
        } in
         z*z = y;
      
      var 1..9:a; var 1..9:b; var 1..9:c; var 0..9:d;
      var 1..9:e; var 0..9:f; var 1..9:g; var 0..9:h;
      var 1..9:i; var 0..9:j;
      
      var 10..49: cd = 10*c + d;
      var 10..49: ef = 10*e + f;
      var 10..49: gh = 10*g + h;
      var 10..49: ij = 10*i + j;
      
      % six numbers used each of the digits 0 to 9 exactly once
      var set of int: all_dig = {a, b, c, d, e, f, g, h, i, j};
      constraint all_dig = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
      
      % product of six numbers is a square
      constraint is_sq(a * b * cd * ef * gh * ij);
      
      % put six numbers in order
      constraint increasing ([a, b, cd, ef, gh, ij]);
      
      solve satisfy;
      
      output [" Six Numbers are " ++ show([a, b, cd, ef, gh, ij]) ];
      
      % First two solutions are for Betty and Charlie
      % Six Numbers are [5, 8, 16, 27, 30, 49] - 4 nos same as Alan(8,16,27,30)
      %  ----------
      % Six Numbers are [8, 9, 15, 27, 36, 40] - 3 nos same as Alan (8,9,27)
      % ----------
      % Six Numbers are [8, 9, 16, 27, 30, 45] -  Alan's six numbers
      % ----------
      % ========== 
      

      Like

  • Unknown's avatar

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

    Brainteaser 1688: Sunday paper 

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

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

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

    What is the value of READER?

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

    [teaser1688]

     
    • Jim Randell's avatar

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

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

      The following run files executes in 150ms.

      Run: [ @replit ]

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

      Solution: READER = 694596.

      Like

    • GeoffR's avatar

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

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

      Like

  • Unknown's avatar

    Jim Randell 5:56 pm on 6 September 2019 Permalink | Reply
    Tags:   

    Teaser 2972: A relaxing day 

    From The Sunday Times, 8th September 2019 [link] [link]

    George and Martha have a digital clock, which displays time with six digits on the 24-hour system (i.e. HH:MM:SS).

    One afternoon, George looked at the clock and saw a six-digit display involving six different positive digits. He dozed off immediately, and when he awoke in the evening he saw another display of six digits, again all positive and different. He dozed off immediately and later on (before midnight) he awoke, having slept for exactly 23 minutes longer than the previous time. At that time, he saw a third display, yet again six different positive digits. He thus had seen eighteen digits and the nine positive digits had each appeared exactly twice.

    At what time did George wake up after his first sleep?

    [teaser2972]

     
    • Jim Randell's avatar

      Jim Randell 6:30 pm on 6 September 2019 Permalink | Reply

      This Python program looks for possible 6-digit times composed of different digits, it separates them into afternoon (before 6pm) and evening (after 6pm) and the looks for two times in the evening that have a difference that is 23 minutes longer than the difference between the first time and one of the afternoon times. We then check the three times between them use each of the digits exactly twice. It runs in 460ms.

      Run: [ @repl.it ]

      from enigma import (irange, nsplit, cproduct, subsets, multiset, sprintf, printf)
      
      # format a number of seconds as HH:MM:SS
      def hms(t):
        (h, m, s) = nsplit(t, 3, base=60)
        return sprintf("{h:02d}:{m:02d}:{s:02d}")
      
      # consider 2-digit numbers with different digits that make valid times
      (hs, mss) = (list(), list())
      for n in irange(12, 59):
        (a, b) = nsplit(n, 2)
        if a == b or b == 0: continue
        if n < 24: hs.append((a, b))
        mss.append((a, b))
      
      # collect times composed of 6 different digits
      (aft, eve) = (dict(), dict())
      for ((h1, h0), (m1, m0), (s1, s0)) in cproduct([hs, mss, mss]):
        ds = (h1, h0, m1, m0, s1, s0)
        if len(set(ds)) != 6: continue
        # time as a number of seconds
        t = ((h1 * 10 + h0) * 60 + (m1 * 10 + m0)) * 60 + (s1 * 10 + s0)
        (aft if t < 64800 else eve)[t] = ds
      
      # find times t2, t3, both in the evening
      for (t2, t3) in subsets(sorted(eve.keys()), size=2):
        d = t3 - t2 - 1380
        if not (d > 0): continue
        # and t1 in the afternoon
        t1 = t2 - d
        if t1 not in aft: continue
      
        # check each digit is used exactly twice
        m = multiset(aft[t1], eve[t2], eve[t3])
        if not all(v == 2 for v in m.values()): continue
      
        # output solution
        printf("{t1} -> {t2} -> {t3}", t1=hms(t1), t2=hms(t2), t3=hms(t3))
      

      Solution: After the first nap George awoke when the clock read 19:56:48.

      There are two possible start times for the first nap (although they are within a minute of each other), but in both cases the wake time from the first nap is the same, and so there is a unique answer to the puzzle.

      The two possible sequences are:

      16:27:39 → (3h29m09s) → 19:56:48 → (3h52m09s) → 23:48:57
      16:28:37 → (3h28m11s) → 19:56:48 → (3h51m11s) → 23:47:59

      And these are the only two sequences even if we don’t restrict the first time to be “afternoon” and the final two times to be “evening”.

      Like

    • GeoffR's avatar

      GeoffR 3:33 pm on 10 September 2019 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % hours, min and sec for afternoon time
      var 12..17: ah;
      var 12..59: am;
      var 12..59: as;
      
      % No zeroes in six positive digits
      constraint am != 20 /\ am != 30 /\ am != 40 /\ am != 50;
      constraint as != 20 /\ as != 30 /\ as != 40 /\ as != 50;
      
      constraint  all_different([ah div 10, ah mod 10,
      am div 10, am mod 10, as div 10, as mod 10]);
      
      % afternoon time in sec (range from midday to midnight)
      var 43000..90000: a_sec = ah*3600 + am*60 + as;
      
      % hours, min and sec for 1st evening time 
      var 18..23: eh1;
      var 12..59: em1;
      var 12..59: es1;
      
      constraint eh1 != 20;
      
      % No zeroes in six positive digits of time
      constraint em1 != 20 /\ em1 != 30 /\ em1 != 40 /\ em1 != 50;
      constraint es1 != 20 /\ es1 != 30 /\ es1 != 40 /\ es1 != 50;
      
      constraint all_different ([eh1 div 10, eh1 mod 10,
      em1 div 10, em1 mod 10, es1 div 10, es1 mod 10]);
      
      % 1st evening time in sec
      var 43000..90000: e1_sec = eh1*3600 + em1*60 + es1;
      
      % 2nd evening time 
      % hours, min and sec for 2nd evening time
      var 18..23: eh2;
      var 12..59: em2;
      var 12..59: es2;
      
      constraint all_different ([eh2 div 10, eh2 mod 10,
      em2 div 10, em2 mod 10, es2 div 10, es2 mod 10]);
      
      constraint eh2 != 20;
      
      % No zeroes in six positive digits
      constraint em2 != 20 /\ em2 != 30 /\ em2 != 40 /\ em2 != 50;
      constraint es2 != 20 /\ es2 != 30 /\ es2 != 40 /\ es2 != 50;
      
      % 2nd evening time in sec
      var 43000..90000: e2_sec = eh2*3600 + em2*60 + es2;
      
      % all digits 1..9 in the three 6-digit times occur exactly twice
      constraint forall(i in 1..9) 
      (count ([ 
      ah div 10, ah mod 10,am div 10, am mod 10, as div 10, as mod 10,
      eh1 div 10, eh1 mod 10,em1 div 10, em1 mod 10, es1 div 10, es1 mod 10,
      eh2 div 10, eh2 mod 10, em2 div 10, em2 mod 10, es2 div 10, es2 mod 10
              ], i, 2) );
      
      % 2nd time span is 23 minutes longer than the 1st time span
      constraint (e1_sec - a_sec) ==  (e2_sec - e1_sec) - 23 * 60;
      
      solve satisfy;
      
      output  [ "Time are in format [hh mm ss] " ++ "\n" ++
      show ([ah, am, as])  ++ " " ++ show(a_sec) ++ " total sec"
      ++"\n" ++ show([eh1, em1, es1]) ++ " " ++ show(e1_sec) ++ " total sec"
      ++"\n" ++ show([eh2, em2, es2]) ++ " " ++ show(e2_sec) ++ " total sec"
      ++"\n" ++ "George woke up after his first sleep at " 
      ++ show(eh1) ++ ":" ++ show(em1) ++ ":" ++ show(es1)  ];
      
      % Time are in format [hh mm ss] 
      % [16, 27, 39] 59259 total sec
      % [19, 56, 48] 71808 total sec
      % [23, 48, 57] 85737 total sec
      % George woke up after his first sleep at 19:56:48
      % ----------
      % Time are in format [hh mm ss] 
      % [16, 28, 37] 59317 total sec
      % [19, 56, 48] 71808 total sec
      % [23, 47, 59] 85679 total sec
      % George woke up after his first sleep at 19:56:48
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:30 am on 6 September 2019 Permalink | Reply
    Tags:   

    Teaser 2804: Spots before the eyes 

    From The Sunday Times, 19th June 2016 [link] [link]

    At the opticians I was shown a sequence of six screens. On each there was a different number of spots ranging from 0 to 5 and I had to say how many I thought I could see. This was done with the left eye and then repeated with the right. The optician always used the same sequence and my answers were:

    (5 2 3 4 4 3)
    (4 1 4 5 2 3)

    In each case I got two correct. When I asked if this was the worst he’d seen, the optician showed me these three earlier sets of answers in which just one was correct in each:

    (2 2 1 2 1 4)
    (3 3 2 3 5 1)
    (0 4 5 1 3 2)

    What was the correct sequence?

    [teaser2804]

     
    • Jim Randell's avatar

      Jim Randell 10:33 am on 6 September 2019 Permalink | Reply

      Programatically we can consider all possible orderings of the numbers from 0 to 5, and look for a sequence that gives the appropriate number of matches in the 5 cases.

      This Python program runs in 39ms.

      Run: [ @repl.it ]

      from enigma import subsets, irange, printf
      
      # sequences, values to check
      ss = (
        ((5, 2, 3, 4, 4, 3), 2),
        ((4, 1, 4, 5, 2, 3), 2),
        ((2, 2, 1, 2, 1, 4), 1),
        ((3, 3, 2, 3, 5, 1), 1),
        ((0, 4, 5, 1, 3, 2), 1),
      )
      
      # count matches
      def check(s1, s2):
        return sum(a == b for (a, b) in zip(s1, s2))
      
      # consider possible sequences
      for s in subsets(irange(0, 5), size=len, select="P"):
        if all(check(s, t) == n for (t, n) in ss):
          printf("{s}")
      

      Solution: The correct sequence is (4 2 0 1 5 3).

      Like

    • GeoffR's avatar

      GeoffR 11:00 am on 6 October 2020 Permalink | Reply

      
      % A Solution in MiniZinc 
      include "globals.mzn";
      
      % Correct sequence is A,B,C,D,E,F
      var 0..5: A; var 0..5: B; var 0..5: C; var 0..5: D;
      var 0..5: E; var 0..5: F;
      
      constraint all_different ([A,B,C,D,E,F]);
      
      % Two sequences with two correct answers
      % Sequence 1 - 5 2 3 4 4 3
      constraint sum ([A==5,B==2,C==3,D==4,E==4,F==3]) == 2;
      
      % Sequence 2 - 4 1 4 5 2 3
      constraint sum ([A==4,B==1,C==4,D==5,E==2,F==3]) == 2;
      
      % Three sequences with one correct answers
      % Sequence 3 - 2 2 1 2 1 4)
      constraint sum ([A==2,B==2,C==1,D==2,E==1,F==4]) == 1;
      
      % Sequence 4 - (3 3 2 3 5 1)
      constraint sum ([A==3,B==3,C==2,D==3,E==5,F==1]) == 1;
      
      % Sequence 5 - (0 4 5 1 3 2)
      constraint sum ([A==0,B==4,C==5,D==1,E==3,F==2]) == 1;
      
      solve satisfy;
      
      output ["Correct sequence = " ++ show([A,B,C,D,E,F]) ];
      
      % Correct sequence = [4, 2, 0, 1, 5, 3]
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

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

    Brainteaser 1675: The A-to-Z of sport 

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

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

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

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

    How many members play all three sports?

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

    [teaser1675]

     
    • Jim Randell's avatar

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

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

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

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

      This program runs in 263ms.

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

      Solution: 2 members play all three sports.

      There are 7 possible solutions:

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

      Like

      • Jim Randell's avatar

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

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

        Run: [ @repl.it ]

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

        Like

        • John Crabtree's avatar

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

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

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

          And so 2 members play all three sports.

          Liked by 1 person

  • Unknown's avatar

    Jim Randell 9:50 am on 3 September 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 497: [Alphabets Cricket Club] 

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

    All 26 members of the Alphabets Cricket Club (all surnames starting with a different letter of the alphabet) turned up for the annual general meeting at the Ugly Duck. Afterwards they divided into 5 teams of 5 and played a quick game of skittles, the lowest-scoring team to buy beer for the rest. (P. L. Zeigler sportingly stood down).

    Each player had just one ball at the 9 skittles, scoring, of course, a point for each skittle knocked down. The team captains varied in performance: Thomson got 9, Knight 5, Oldwhistle 3, C. F. Arrowroot 2, and the luckless Fisher 0. (Fisher’s team moved to have all captains’ scores ignored, but the plea was rejected; it was pointed out that anyhow Fisher’s weren’t due to buy the beer).

    The team totals were all different, as indeed they would have been had captains’ scores been ignored. No other player made the same score as any Captain, and no two team members of the same team made the same score. Oldwhistle’s team won.

    What was the order of finishing, and what were the individual scores of the team buying the beer?

    This puzzle was originally published with no title.

    [teaser497]

     
    • Jim Randell's avatar

      Jim Randell 9:50 am on 3 September 2019 Permalink | Reply

      This Python program considers the set of scores for the winning team, and then looks for possible scores for the remaining teams that gives the teams different scores (both including and excluding the captains scores).

      from enigma import (subsets, irange, printf)
      
      # we can identify the teams by the captains scores (winning captain first)
      caps = [ 3, 9, 5, 2, 0 ]
      
      # team names (by captains score)
      team = dict(zip(caps, "OTKAF"))
      
      # possible remaining scores
      scores = set(irange(0, 9)).difference(caps)
      
      # solve for captains score in caps
      # t1 = winning teams total
      # t0 = winning teams total without captains score
      # ss = scores so far
      # t1s = team totals (with captains score)
      # t0s = team totals (without captains score)
      def solve(caps, t1, t0, ss=[], t1s=[], t0s=[]):
        # are we done?
        if not caps:
          yield ss
        else:
          # consider scores for the next team
          x = caps[0]
          for s in subsets(scores, size=4):
            s0 = sum(s)
            s1 = s0 + x
            # check scores are less than the winner
            if not (s1 < t1 and s0 < t0): continue
            # and also not repeated
            if s1 in t1s or s0 in t0s: continue
            # solve for the remaining captains
            yield from solve(caps[1:], t1, t0, ss + [s], t1s + [s1], t0s + [s0])
            
      
      # find scores for the winning team
      for s in subsets(scores, size=4):
        t = sum(s)
        # solve for the remaining teams
        for ss in solve(caps[1:], caps[0] + t, t):
      
          # record (0=team, 1=captain score, 2=other scores, 3=total score (with cap), 4=total score (without cap))
          rs = [ ('O', caps[0], s, caps[0] + t, t) ]
          for (c, x) in zip(caps[1:], ss):
            rs.append((team[c], c, x, c + sum(x), sum(x)))
          # sort according to total score (with cap)
          rs.sort(key=lambda t: t[3], reverse=1)
      
          # team F did not come last
          if rs[-1][0] == 'F': continue
      
          # output the table
          for (x0, x1, x2, x3, x4) in rs:
            printf("{x0}: {x1} + {x2} = {x3} ({x4})")
          printf()
      

      Solution: The finishing order was: 1st = Oldwhistle, 2nd = Thomson, 3rd = Knight, 4th = Fisher, 5th = Arrowroot. The individual scores of the team buying the beer were: 1, 2 (Arrowroot), 4, 6, 8.

      The complete set of results is:

      O: 3 + (4, 6, 7, 8) = 28 (25) = 1st (1st)
      T: 9 + (1, 4, 6, 7) = 27 (18) = 2nd (5th)
      K: 5 + (1, 4, 7, 8) = 25 (20) = 3rd (3rd)
      F: 0 + (1, 6, 7, 8) = 22 (22) = 4th (2nd)
      A: 2 + (1, 4, 6, 8) = 21 (19) = 5th (4th)

      There is also a “near miss” solution:

      O: 3 + (4, 6, 7, 8) = 28 (25) = 1st (1st)
      T: 9 + (1, 4, 6, 7) = 27 (18) = 2nd (5th)
      K: 5 + (1, 4, 7, 8) = 25 (20) = 3rd (3rd)
      A: 2 + (1, 6, 7, 8) = 24 (22) = 4th (2nd)
      F: 0 + (1, 4, 6, 8) = 19 (19) = 5th (4th)

      But this is disallowed as team F is last (and wouldn’t be last if the captains scores are ignored).

      Like

  • Unknown's avatar

    Jim Randell 10:41 am on 2 September 2019 Permalink | Reply
    Tags:   

    Teaser 2810: Almost a year apart 

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

    Tomorrow’s date is 1st August 2016, which in the usual six-digit format is 01 08 16. This can be regarded as a “square” date since 10816 is a perfect square. No square date is followed by another square date exactly one year later, although some pairs of square dates are very nearly one year apart.

    Which two square dates (in their six-digit format) come closest to being a year apart?

    [teaser2810]

     
    • Jim Randell's avatar

      Jim Randell 10:43 am on 2 September 2019 Permalink | Reply

      Richard England likes puzzles involving square dates. See also: Enigma 1547, Enigma 1574, Enigma 1668, Enigma 1771.

      This Python program looks at possible squares and checked to see if they represent a viable date. It then looks for the two of these dates that are closest to the specified number of days apart.

      Run: [ @repl.it ]

      import datetime
      from enigma import irange, sqrt, intc, intf, nsplit, Accumulator, subsets, printf
      
      # accumulate results closest to N days apart
      N = 365
      
      # find square dates from years (20)00 - (20)99
      dates = list()
      for k in irange(intc(sqrt(10100)), intf(sqrt(311299))):
        (dd, mm, yy) = nsplit(k * k, base=100)
        try:
          d = datetime.date(2000 + yy, mm, dd)
        except ValueError:
          continue
        dates.append((d.toordinal(), dd, mm, yy))
      dates.sort()
      
      # find the two dates closest to N days apart
      r = Accumulator(fn=min)
      for p in subsets(dates, size=2):
        d = p[1][0] - p[0][0]
        r.accumulate_data(abs(N - d), p)
      
      # output the solution
      ((i1, dd1, mm1, yy1), (i2, dd2, mm2, yy2)) = r.data
      printf("{dd1:02d}.{mm1:02d}.{yy1:02} - {dd2:02d}.{mm2:02d}.{yy2:02d} = {d} days", d=i2 - i1)
      

      Solution: The two square dates closest to a year apart are 11.02.24 and 7.02.25.

      These dates are 362 days apart.

      The next closest dates are:

      26.01.00 – 01.02.01 = 372 days
      24.01.00 – 01.02.01 = 374 days

      Like

  • Unknown's avatar

    Jim Randell 7:29 am on 30 August 2019 Permalink | Reply
    Tags:   

    Teaser 2971: Six sisters on the ski lift 

    From The Sunday Times, 1st September 2019 [link] [link]

    The sum of the ages of six sisters known to me is 92. Though there is no single whole number greater than 1 that simultaneously divides the ages of any three of them, I did notice this morning, while they lined up for the ski lift, that the ages of any two of them standing one behind the other, had a common divisor greater than 1.

    In increasing order, how old are the six sisters?

    [teaser2971]

     
    • Jim Randell's avatar

      Jim Randell 8:06 am on 30 August 2019 Permalink | Reply

      This program builds up a list of the ages in the order they appear in the queue, such that each consecutive pair share a common factor (greater than 1), but no 3 of them have a common factor (greater than 1).

      It runs in 287ms.

      Run: [ @replit ]

      from enigma import (irange, mgcd, cached, subsets, printf)
      
      # use a caching version of gcd
      gcd = cached(mgcd)
      
      # look for k numbers that sum to t
      # each has a common factor with the previous number
      # but no 3-set has a common factor > 1
      def solve(t, k, m=1, ns=[]):
        if k == 0:
          yield ns
        else:
          # choose a value from the remaining total
          for n in (irange(m, t - (k - 1) * m) if k > 1 else [t]):
            # any new number must have a gcd > 1 with the previous number
            if len(ns) > 0 and gcd(n, ns[-1]) == 1: continue
            # but must have a gcd of 1 combined with all other pairs
            if any(gcd(n, a, b) > 1 for (a, b) in subsets(ns, size=2)): continue
            # solve for the remaining numbers
            yield from solve(t - n, k - 1, m, ns + [n])
      
      # solve the puzzle
      for ns in solve(92, 6, 2):
        printf("{s} -> {ns}", s=sorted(ns))
      

      Solution: The ages of the sisters are 7, 10, 13, 15, 21, 26.

      They are queuing in the order (7, 21, 15, 10, 26, 13), or the reverse of this.

      Which we can factorise as: (7, 7×3, 3×5, 5×2, 2×13, 13). So the common factors are 7, 3, 5, 2, 13.

      And, in fact, picking a sequence of different prime numbers and then multiplying them together in pairs will give us a sequence where consecutive pairs share a factor, but no three numbers share a common factor.

      However this procedure will not necessary find all possible solutions. For example, if the required total had been 96, there is a possible queue of (7, 7×3, 3×5, 5×2×2, 2×11, 11) that cannot be constructed in this way (note the 5×2×2 term), but it is found by the program given above.

      Like

      • Jim Randell's avatar

        Jim Randell 2:17 pm on 31 August 2019 Permalink | Reply

        Here is an alternative approach that uses a bit of analysis.

        Each consecutive pair of numbers in the queue must have some prime as a common factor. So we can start with a prime and then look at pairs that are multiples of that prime. We can then choose a different prime (if a prime is repeated we will have 3 numbers that are divisible by it) that divides the second number, and choose the third number that is a multiple of the new prime, and so we continue until we have all six numbers in the queue.

        This recursive Python 3 program is a little longer that my previous approach, but it runs quicker, and can be used to explore solutions for different totals and number of elements in the queue. (It also removes the solutions that that are reverse of another solution).

        For the parameters of the puzzle it runs in 80ms. (Internal runtime is 23ms).

        Run: [ @replit ]

        from enigma import (primes, irange, mgcd as gcd, subsets, arg, printf)
        
        # T = total sum of numbers, K = number of numbers to consider
        T = arg(92, 0, int)
        K = arg(6, 1, int)
        printf("[T={T} K={K}]")
        
        # possible common prime factors
        primes = set(primes.between(1, T // 3))
        
        # return viable multiples of p
        multiples = lambda p, t, k: irange(p, t - (k - 1) * 2, step=p)
        
        # find k more numbers, that sum to t
        # the next number should be a multiple of prime p
        # ns = numbers found so far
        # ps = primes used so far
        def solve(k, t, p, ns=[], ps=set()):
          # are we done?
          if k == 1:
            if t % p == 0:
              yield ns + [t]
          else:
            # the next age n is some multiple of p and also some other prime q
            for q in primes.difference(ps):
              for n in multiples(p * q, t, k):
                yield from solve(k - 1, t - n, q, ns + [n], ps.union([q]))
        
        # choose the first prime
        for p in primes:
          # the first age is some multiple of p
          for n in multiples(p, T, K):
            # solve for the remaining 5 numbers
            for ns in solve(K - 1, T - n, p, [n], set([p])):
              if ns[0] > ns[-1]: continue # [optional] only consider queues one way round
              # check no 3-set has a gcd > 1
              if any(gcd(*s) > 1 for s in subsets(ns, size=3)): continue
              # output solution
              printf("{s} -> {ns}", s=sorted(ns))
        

        Like

    • Robert Brown's avatar

      Robert Brown 5:18 pm on 30 August 2019 Permalink | Reply

      That’s odd. My QuickBasic program (using a different algorithm!) runs in < 10ms.

      Like

  • Unknown's avatar

    Jim Randell 2:28 pm on 29 August 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 496: [Operatics] 

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

    The soprano, the tenor, and the manager are all well aware that the five operas in the repertoire will be sung on the first five evenings of next week.

    The tenor is expecting Ernani to be two nights after Don Giovanni and one night before Carmen, and he is expecting Bohème later in the week than Aida. The soprano is expecting Aida to be two nights before Ernani.

    The manager, who knows all their expectations and knows the true state of affairs, tells me that for every night next week the soprano and tenor had different operas on their lists — and neither is right; he adds that the true list shows Don Giovanni two nights before Bohème.

    I asked him what the tenor expected to sing on the evening booked for Aida; he told me, but I could still not be sure about next week’s true programme till he also told me what the soprano expected to sing on the evening booked for Aida. Then I knew the true programme.

    What do the soprano and tenor expect on the evening of Aida?

    This puzzle was originally published with no title.

    [teaser496]

     
    • Jim Randell's avatar

      Jim Randell 2:29 pm on 29 August 2019 Permalink | Reply

      This Python program selects possible sets of orderings for the manager, soprano and tenor, and then uses the [[ filter_unique() ]] function from the enigma.py library to reduce these to sets where knowledge of the tenor’s selection for the actual night of A is ambiguous, but the addition of the soprano’s selection gives a unique answer.

      This Python program runs in 90ms.

      from itertools import product
      from enigma import (subsets, irange, filter_unique, unpack, join, printf)
      
      # the five operas
      operas = list("ABCDE")
      
      # record possibilities for the manager, soprano, tenor
      (ms, ss, ts) = (list(), list(), list())
      
      # consider possible orders
      for s in subsets(irange(1, 5), size=len, select="P"):
        (A, B, C, D, E) = s
      
        # tenor is expecting...
        # E then C then D, A before B
        if D + 2 == E and E + 1 == C and A < B:
          ts.append(s)
      
        # soprano is expecting...
        # A two nights before E
        if A + 2 == E:
          ss.append(s)
      
        # manager knows...
        # D two nights before B
        if D + 2 == B:
          ms.append(s)
      
      # collect viable (m, s, t) orders
      rs = list()
      
      # choose a pair of orders for the soprano and tenor
      for (s, t) in product(ss, ts):
        # they must differ in every position
        if any(a == b for (a, b) in zip(s, t)): continue
      
        # choose an order for the manager
        for m in ms:
          # it must differ from s and t in every position
          if any(a == b or a == c for (a, b, c) in zip(m, s, t)): continue
      
          rs.append((m, s, t))
      
      # indices into the orders for A, B, C, D, E
      (A, B, C, D, E) = (0, 1, 2, 3, 4)
      
      # knowing what the tenor expects on the day of A is not enough to determine the correct order
      (_, rs) = filter_unique(rs, unpack(lambda m, s, t: t.index(m[A])), unpack(lambda m, s, t: m))
      
      # ... but knowing what the soprano expects as well is enough
      (rs, _) = filter_unique(rs, unpack(lambda m, s, t: (t.index(m[A]), s.index(m[A]))), unpack(lambda m, s, t: m))
      
      # output solutions
      fmt = lambda x: join((n for (i, n) in sorted(zip(x, "ABCDE"))), sep=" ")
      for (m, s, t) in rs:
        printf("m = {m}; s = {s}; t = {t}", m=fmt(m), s=fmt(s), t=fmt(t))
      

      Solution: On the night of Aida, the soprano is expecting Bohème, and the tenor is expecting Ernani.

      The complete orders are:

      C E D A B = manager
      D C A B E = soprano
      A D B E C = tenor

      Before the setter has asked any questions there is enough information that he can narrow the correct order down to one of four options.

      The setter asks the manager what the tenor expects to sing on the night booked for A. This is the 4th night, and so will get the answer E.

      This allows the setter to narrow the correct order down to one of two options.

      On also being told that the soprano expects to sing B on that night, only one of those options remains, giving the solution.

      Like

    • Pete Good's avatar

      Pete Good 9:43 am on 13 August 2023 Permalink | Reply

      Jim,

      I think that this teaser has two solutions. I found 10 sets of three schedules that appear to meet the constraints and not just 4 sets:-

      C E D A B , A D B E C, B C A D E
      E A D C B, A D B E C, C B A D E
      C E D A B, A D B E C, D B A C E
      C E D A B, A D B E C, D C A B E
      E D C B A, D A E C B, B C A D E
      E A D C B, A D B E C, B C A D E
      E C D A B, A D B E C, C B A D E
      E C D A B, A D B E C, D B A C E
      E A D C B, A D B E C, D C A B E
      E D C B A, D A E C B, C B A D E

      On the evening of Aida, the tenor and soprano are expecting ED, DB, EC, EB, BE, DC, ED, EC, DC or BE. There are two sets of schedules containing ED, EC, BE and DC so these can be discounted but DB and EB are both unique so it appears that there are solutions.

      Have I overlooked something?

      Like

      • Jim Randell's avatar

        Jim Randell 11:31 am on 13 August 2023 Permalink | Reply

        @Pete: You have found the right list of 10 possibilities.

        We are told that if we knew what the tenor was expecting on the day that A is actually to be performed, then that would not be enough to determine the correct order.

        Looking at the list, the tenor is expecting one of B, D, E to be performed on the day of A.

        Looking at the corresponding correct orders:

        B → (E D C B A)
        D → (E A D C B)
        E → (C E D A B) (E C D A B)

        The first 2 lead to a unique correct ordering, so we eliminate those cases and we are left with the 5 possibilities (where the tenor expects E to be performed on the day of A).

        From those 5 possibilities we look at what the soprano is expecting on the day of A.

        It is one of: B, C, D.

        Looking at the corresponding correct orders:

        B → (C E D A B)
        C → (C E D A B) (E C D A B)
        D → (C E D A B) (E C D A B)

        So only B will allow us to determine the correct ordering.

        Hence the soprano expects B, and the tenor expects E. And the correct ordering is (C E D A B).

        Like

    • Pete Good's avatar

      Pete Good 11:55 am on 13 August 2023 Permalink | Reply

      Thank you for explaining this.

      Like

  • Unknown's avatar

    Jim Randell 3:06 pm on 28 August 2019 Permalink | Reply
    Tags:   

    Teaser 2814: Today’s Teaser 

    From The Sunday Times, 28th August 2016 [link] [link]

    Today’s Teaser concerns writing a date as a string of six digits. For example, today’s date can be written as 280816. Our puzzle concerns two numbers that add up to a date. I have written down two six-figure numbers and then consistently replaced digits by letters. In this way the two numbers have become:

    TODAYS
    TEASER

    The sum of these two numbers equals the six-digit date of a Sunday in July, August or September this year.

    What is the value of my TEASER?

    [teaser2814]

     
    • Jim Randell's avatar

      Jim Randell 3:07 pm on 28 August 2019 Permalink | Reply

      There are four potential dates in each of the given months, but removing those with a leading zero leaves 10 candidates.

      We can check if the alphametic sum corresponds to one of those dates in a single expression using the [[ SubstitutedExpression() ]] solver from the enigma.py library, but we can speed things up considerably by considering the year part separately, which must give 16 in the final two columns, and the month part which must give 07, 08, 09 in the next two columns.

      This run file executes in 168ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --invalid="0,T"
      --answer="TEASER"
      
      # day/month must be:
      # 10/07, 17/07, 24/07, 31/07 (Jul)
      # 07/08, 14/08, 21/08, 28/08 (Aug)
      # 04/09, 11/09, 18/09, 25/09 (Sep)
      --code="dates = set([100716, 170716, 240716, 310716, 140816, 210816, 280816, 110916, 180916, 250916])"
      
      # the result of the alphametic sum is one of the dates
      "(TODAYS + TEASER) in dates"
      
      # [optional] year
      --code="yy = set(d % 100 for d in dates)"
      "(YS + ER) % 100 in yy"
      
      # [optional] month + year
      --code="mmyy = set(d % 10000 for d in dates)"
      "(DAYS + ASER) % 10000 in mmyy"
      

      Solution: TEASER = 127026.

      And so the sum is:

      153790 + 127026 = 280816

      So the date is 28th August 2016, the publication date of the puzzle.

      Like

  • Unknown's avatar

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

    Brainteaser 1661: Goals galore 

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

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

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

    Athletic had scored the most goals;

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

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

    Fewer than 40 goals were scored in the tournament.

    What were the scores in the three games?

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

    [teaser1661]

     
    • Jim Randell's avatar

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

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

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

      This Python 3 program runs in 228ms.

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

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

      In total there were 38 goals scored in the tournament.

      The different measures are:

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

      Like

    • GeoffR's avatar

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

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

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

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

      Like

      • Jim Randell's avatar

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

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

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

        constraint a / b > c / d;
        

        with the equivalent:

        constraint a * d > c * b;
        

        Like

    • GeoffR's avatar

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

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

      Like

      • Jim Randell's avatar

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

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

        Like

  • Unknown's avatar

    Jim Randell 12:01 pm on 26 August 2019 Permalink | Reply
    Tags:   

    Teaser 2818: Accountability 

    From The Sunday Times, 25th September 2016 [link] [link]

    George and Martha’s bank account number is an 8-digit number consisting of the digits 1 to 8 in some order.

    Martha commented that, if you split the number into two 4-figure numbers, then the number formed by the last four digits is a multiple of the number formed by the first four digits.

    Then George listed all the different prime numbers that divided exactly into the account number. When he added together all those primes the total was less than a hundred.

    What is their account number?

    [teaser2818]

     
    • Jim Randell's avatar

      Jim Randell 12:01 pm on 26 August 2019 Permalink | Reply

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

      The following run file executes in 218ms.

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      # solver paraemters
      --digits="1-8"
      --answer="ABCDEFGH"
      
      # expressions to solve
      "EFGH % ABCD = 0"
      "sum(p for (p, e) in prime_factor(ABCDEFGH)) < 100"
      

      Solution: The account number is 12876435.

      We have:

      6435 = 5 × 1287

      The number factorises as:

      12876435 = 3³ . 5 . 11 . 13 . 23 . 29

      So the sum of the prime divisors is 84.

      Like

  • Unknown's avatar

    Jim Randell 7:17 am on 25 August 2019 Permalink | Reply
    Tags: by: V W Lewis   

    Brain-Teaser 495: [Goal scorers] 

    From The Sunday Times, 22nd November 1970 [link]

    All five forwards (numbers 7-11) scored in their team’s eight-nil win. Their names, alphabetically, were Alan, Bob, Craig, Dave and Eric.

    Five goals were kicked (number 10 scoring the last of just three consecutive ones). Dave headed his second goal. He and two other forwards scored two goals each — heading one and kicking the other. Successive goals were scored by the other two forwards. No player scored two consecutive goals.

    The scores, in scoring order, were:

    1st goal: Number 11
    2nd goal: Alan
    3rd goal: Eric
    4th goal: Number 8
    5th goal: Bob
    6th goal: Number 7
    7th goal: Dave
    8th goal: Number 9

    What was each forward’s number?

    This puzzle was originally published with no title.

    [teaser495]

     
    • Jim Randell's avatar

      Jim Randell 7:18 am on 25 August 2019 Permalink | Reply

      This Python program runs in 95ms.

      from enigma import subsets, irange, tuples, diff, update, unpack, join, printf
      
      names = "ABCDE"
      
      # m: maps number (7-11) to name A-E
      for s in subsets(irange(7, 11), size=len, select="P"):
        m = dict(zip(s, names))
      
        # use the map to fill out the list of goal scorers
        ns = [ m[11], 'A', 'E', m[8], 'B', m[7], 'D', m[9] ]
      
        # no-one scored consecutive goals
        if any(a == b for (a, b) in tuples(ns, 2)): continue
      
        # count the number of goals scored by each
        n = dict((x, ns.count(x)) for x in names)
      
        # D and 2 others scored 2 goals
        if n['D'] != 2: continue
        g2s = list(x for x in names if n[x] == 2)
        if len(g2s) != 3: continue
      
        # the 1 goal scorers goals were consecutive
        g1s = diff(names, g2s)
        if abs(ns.index(g1s[0]) - ns.index(g1s[1])) != 1: continue
      
        # assign the kicked goals we know (1 = kick, 0 = head)
        k = [ None ] * 8
        # D's goals are kick then head
        (j0, j1) = list(i for (i, x) in enumerate(ns) if x == 'D')
        (k[j0], k[j1]) = (1, 0)
      
        # number 10 scored the final goal of just three consecutive kicks
        for (i, x) in enumerate(ns):
          if x != m[10]: continue
          if i < 2: continue
          if k[i - 1] == 0 or k[i - 2] == 0: continue
          k[i] = k[i - 1] = k[i - 2] = 1
          if i > 2:
            if k[i - 3] == 1: continue
            k[i - 3] = 0
          if i < 7:
            if k[i + 1] == 1: continue
            k[i + 1] = 0
      
          # fill out any remaining 5 kicked goals
          j = k.count(1)
          if j > 5: continue
          ms = list(i for (i, x) in enumerate(k) if x is None)
          for js in subsets(ms, size=5 - j, select="P"):
            k2 = update(k, ms, list((1 if x in js else 0) for x in ms))
      
            # check the other scorers of 2 goals are a kick and a head
            if any(len(set(k2[i] for (i, x) in enumerate(ns) if x == y)) == 1 for y in diff(g2s, 'D')): continue
      
            # output solution
            for (k, v) in sorted(m.items(), key=unpack(lambda k, v: v)):
              printf("{v} = #{k}")
            printf("[goals = {gs}]", gs=join((n + "hk"[i] for (n, i) in zip(ns, k2)), sep=","))
      

      Solution: Alan is number 9. Bob is number 10. Craig is number 8. Dave is number 11. Eric is number 7.

      The goals scored are:

      1. Dave (#11), kicked
      2. Alan (#9), headed
      3. Eric (#7), kicked
      4. Craig (#8), kicked
      5. Bob (#10), kicked
      6. Eric (#7), headed
      7. Dave (#11), headed
      8. Alan (#9), kicked

      Like

  • Unknown's avatar

    Jim Randell 1:56 am on 24 August 2019 Permalink | Reply
    Tags:   

    Teaser 2970: Puzzling powers 

    From The Sunday Times, 25th August 2019 [link] [link]

    I came across a most remarkable number recently and wrote it down. All its digits were different and it was divisible by 11. In itself, that wasn’t particularly interesting, but I wrote down the number of digits in the number and then wrote down the sum of the digits in the number.

    I therefore had three numbers written down. What surprised me was that of the three numbers, one was a square and two were cubes.

    What is the remarkable number?

    [teaser2970]

     
    • Jim Randell's avatar

      Jim Randell 7:42 am on 24 August 2019 Permalink | Reply

      I started without making the assumption that the numbers are separately a square, a cube and a cube (in some order), i.e. one of them could be both a square and a cube.

      The smallest numbers that are both squares and cubes are 0, 1, 64, 729, … (i.e. the powers of 6).

      So we can write down 0 (a multiple of 11 using all different digits), which has 1 digit and a digit sum of 0. And we can say one of them is a square and two of them are cubes (as they are all both squares and cubes). If however, we require exactly 1 of them to be a square and exactly 2 of them to be cubes we can eliminate this solution.

      The number of digits is going to be between 1 and 10, and the digits sum is going to be between 0 and 45, so neither of those can be both a square and a cube, which means that the multiple of 11 must be either a square or a cube (possibly both), so can be written as either (11.m)² or (11.m)³.

      This Python program considers numbers of this form, and finds only one solution. It runs in 157ms.

      Run: [ @repl.it ]

      from enigma import (irange, iroot, nsplit, icount_exactly as count, is_square, is_cube, printf)
      
      # consider multiples of 11 both squared and cubed
      for p in (2, 3):
        for m in irange(11, iroot(9876543210, p), step=11):
          n = m**p
          # digits
          ds = nsplit(n)
          # number of digits
          k = len(ds)
          # all digits different
          if len(set(ds)) != k: continue
          # digit sum
          s = sum(ds)
      
          # one is a square, two are cubes
          ns = (n, k, s)
          if count(ns, is_square, 1) and count(ns, is_cube, 2):
            printf("n={n} k={k} s={s}")
      

      Solution: The number is 26198073.

      Changing [[ count() ]] to use [[ icount_at_least() ]] does not yield any further solutions.

      The number is (27×11)³.

      The digit length is 8 (= 2³).

      The digit sum is 36 (= 6²).

      If we look at the 6th power of multiples of 11 we find that the powers of 11, 22, 33, 44 all have repeated digits, and the power of 55 is longer than 10 digits. So none of the numbers are going to be both a square and a cube. We can use this analysis to reduce the run time of the program, for example we only have to check numbers with 1, 4, 8, 9 digits. So we can reduce the maximum possible number (at line 5) to 987654321.

      For k = 1 the only possible value is n = 0, and we have already eliminated this as a solution.

      For k = 4 (a square), the only multiple of 11 cubed with 4 digits is n = 11³ = 1331, which has repeated digits.

      For k = 8 (a cube) and k = 9 (a square) the only possible digit sum is s = 36 (a square), so we see the solution must be a multiple of 11 cubed, with 8 different digits and a digit sum of 36. So we only have to check values n = (11.m)³ for m = 20 … 42 to find a value with 8 different digits. There are 2 possible values, and only one of these has a digit sum of 36.

      Like

  • Unknown's avatar

    Jim Randell 9:54 am on 21 August 2019 Permalink | Reply
    Tags:   

    Teaser 2824: New extension 

    From The Sunday Times, 6th November 2016 [link] [link]

    George has just moved departments and he has a new four-figure telephone extension number. He shows it to Martha and proudly points out that, if you delete any two of the digits of the extension number, then the remaining two-figure number is prime. Martha then lists the resulting six different primes and comments delightedly that more than one of them divides exactly into the extension number.

    What is George’s new extension number?

    [teaser2824]

     
    • Jim Randell's avatar

      Jim Randell 9:55 am on 21 August 2019 Permalink | Reply

      We can express the puzzle as a set of alphametic constraints and use the [[ SubstitutedExpression() ]] solver from the enigma.py library to solve it.

      The following run file executes in 81ms.

      Run: [ @repl.it ]

      #! python -m enigma -rr
      
      # solver to use
      SubstitutedExpression
      
      # solver parameters
      --distinct=""
      --invalid=""
      --answer="ABCD"
      
      # 2-digit subsequences are prime
      "is_prime(AB)"
      "is_prime(AC)"
      "is_prime(AD)"
      "is_prime(BC)"
      "is_prime(BD)"
      "is_prime(CD)"
      
      # and all different
      "all_different(AB, AC, AD, BC, BD, CD)"
      
      # and more than 1 of them divides ABCD
      "sum(ABCD % x == 0 for x in (AB, AC, AD, BC, BD, CD)) > 1"
      

      Solution: George’s new extension number is 4371.

      4371 is divisible by 47 and by 31.

      Like

    • GeoffR's avatar

      GeoffR 8:43 am on 22 August 2019 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:A; var 1..9:B; var 1..9:C;  var 1..9:D;
      
      predicate is_prime(var int: x) = 
      x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) 
      ((i < x) -> (x mod i > 0));
      
      % the four-figure telephone extension number
      var 1000..9999: ABCD = 1000*A + 100*B + 10*C + D;
      
      % six different two-digit numbers
      constraint all_different([AB, AC, AD, BC, BD, CD]);
      
      var 10..99: AB = 10*A + B;
      var 10..99: AC = 10*A + C; 
      var 10..99: AD = 10*A + D;
      var 10..99: BC = 10*B + C;
      var 10..99: BD = 10*B + D;
      var 10..99: CD = 10*C + D;
      
      constraint is_prime(AB) /\ is_prime(AC) /\ is_prime(AD)
      /\ is_prime(BC) /\ is_prime(BD) /\ is_prime(CD);
      
      % more than one two-digit number divides exactly into the extension number
      constraint sum ([ABCD mod AB == 0, ABCD mod AC == 0,
      ABCD mod AD == 0, ABCD mod BC == 0, ABCD mod BD == 0,
      ABCD mod CD == 0]) > 1;
      
      solve satisfy;
      output ["George's Ext No. = " ++ show(ABCD)];
      
      % George's Ext No. = 4371
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

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

    Brainteaser 1659: Prime cuts 

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

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

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

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

    [teaser1659]

     
    • Jim Randell's avatar

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

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

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

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

      This run file executes in 146ms.

      Run: [ @repl.it ]

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

      Solution: The ticket number is 68597.

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

      Like

    • GeoffR's avatar

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

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

      Like

  • Unknown's avatar

    Jim Randell 4:31 pm on 19 August 2019 Permalink | Reply
    Tags:   

    Teaser 2884: Farewell 

    From The Sunday Times, 31st December 2017 [link] [link]

    Today I am retiring after editing this column for 40 very pleasurable years. My heartfelt thanks go out to all the setters and solvers of puzzles with whom I have corresponded.

    To celebrate the 40 years I threw a party for the puzzle-setters. At the party we assigned a different whole number from 1 to 26 to each letter of the alphabet; for example, we had A=13 and Z=3. We did this in such a way that, for each person present (including me), the values of the letters of their Christian name added to 40.

    Bob, Graham, Hugh, Nick and Richard were there, as were two of Andrew, Angela, Danny, Des, Ian, John, Mike, Peter, Robin, Steve and Tom.

    Which two?

    Victor Bryant also contributed many Enigma puzzles for New Scientist under the pseudonym Susan Denham.

    [teaser2884]

     
    • Jim Randell's avatar

      Jim Randell 4:32 pm on 19 August 2019 Permalink | Reply

      I treated this puzzle as a pair of linked alphametic problems in base 27 (so the symbols have values from 1 to 26), using the [[ SubstitutedExpression() ]] solver from the enigma.py library. It’s not especially quick, but it is moderately succinct. It runs in 2.60s.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, irange, join, subsets, printf)
      
      # initial words
      ws0 = [ "VICTOR", "BOB", "GRAHAM", "HUGH", "NICK", "RICHARD" ]
      
      # additional words
      ws1 = [ "ANDREW", "ANGELA", "DANNY", "DES", "IAN", "JOHN", "MIKE", "PETER", "ROBIN", "STEVE", "TOM" ]
      
      # make a puzzle where words in <ws> add to <n>
      def puzzle(ws, n, **args):
        kw = dict(base=27, digits=irange(1, 26), verbose=0) # default args
        kw.update(args)
        return SubstitutedExpression(list((join(w, sep=' + '), n) for w in ws), **kw)
      
      # format a puzzle solution <s>
      fmt = lambda s: join((join((k, "=", s[k])) for k in sorted(s.keys())), sep=" ")
      
      # make a puzzle with where words in <ws0> add up to 40, with initial conditions
      p0 = puzzle(ws0, 40, s2d=dict(A=13, Z=3))
      
      # find solutions to the first puzzle
      for s0 in p0.solve():
      
        # choose 2 words from <ws1>
        for ws2 in subsets(ws1, size=2):
      
          # make a puzzle where words in <ws2> add to 40, using the letters to numbers assignment in <s0>
          p2 = puzzle(ws2, 40, s2d=s0)
      
          # find a solution to the second puzzle
          for s2 in p2.solve(first=1):
            # output the solution <s2>
            printf("+ {ws2} [{s2}]", ws2=join(ws2, sep=", "), s2=fmt(s2))
      

      Solution: DES and MIKE also add up to 40.

      There are four ways the letters can be assigned to make the required set of names add up to 40.

      The following letters are fixed:

      A=13 Z=3
      B=16 D=10 E=18 G=7 H=4 M=2 O=8 R=1 S=12 U=25

      Then C, I, K, N, T, V have one of the following assignments:

      C=5 I=6 K=14 N=15 T=9 V=11
      C=5 I=6 K=14 N=15 T=11 V=9
      C=6 I=5 K=15 N=14 T=9 V=11
      C=6 I=5 K=15 N=14 T=11 V=9

      The remaining letters: J, L, P, W, Y (and the unused letters: F, Q, X) take on the remaining values: 17, 19, 20, 21, 22, 23, 24, 26 (in any order).

      Like

    • GeoffR's avatar

      GeoffR 7:38 pm on 19 August 2019 Permalink | Reply

      I had to change to the Chuffed Solver after the Geocode solver froze – this has happened previously.

      I found multiple solutions, all with MIKE and DES adding up to 40.
      I noticed the names which seemed to vary most in value were ANGELA, JOHN and PETER.

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..26:A;   var 1..26:B;   var 1..26:C;   var 1..26:D;
      var 1..26:E;   var 1..26:F;   var 1..26:G;   var 1..26:H;   
      var 1..26:I;   var 1..26:J;   var 1..26:K;   var 1..26:L;
      var 1..26:M;   var 1..26:N;   var 1..26: O;  var 1..26:P;   
      var 1..26:Q;   var 1..26:R;   var 1..26:S;   var 1..26:T;   
      var 1..26:U;   var 1..26:V;   var 1..26:W;   var 1..26:X;   
      var 1..26:Y;   var 1..26:Z;
      
      constraint A == 13 /\ Z == 3;
      
      constraint all_different ([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,
      P,Q,R,S,T,U,V,W,X,Y,Z]);
      
      % Bob, Graham, Hugh, Nick and Richard were there, plus Victor
      % - all have letters adding up to 40
      constraint B+O+B == 40 /\ G+R+A+H+A+M == 40 /\ H+U+G+H == 40
      /\ N+I+C+K == 40 /\ R+I+C+H+A+R+D == 40 /\ V+I+C+T+O+R == 40;
      
      % Two of Andrew, Angela, Danny, Des, Ian, John, Mike, 
      % Peter, Robin, Steve and Tom have letters summing to 40
      constraint sum( [A+N+D+R+E+W == 40, A+N+G+E+L+A == 40, 
      D+A+N+N+Y == 40, D+E+S == 40, I+A+N == 40, J+O+H+N == 40, 
      M+I+K+E == 40, P+E+T+E+R == 40, R+O+B+I+N == 40, 
      S+T+E+V+E == 40, T+O+M == 40]) == 2;  
      
      solve satisfy;
      
      output [ 
          "ANDREW = " ++ show (A+N+D+R+E+W) 
      ++ " ANGELA = " ++ show(A+N+G+E+L+A)
      ++ " DANNY = " ++ show(D+A+N+N+Y) 
      ++ "\nDES = " ++ show(D+E+S)
      ++ " IAN = " ++ show(I+A+N)
      ++ " JOHN = " ++ show(J+O+H+N)
      ++ "\nMIKE = " ++ show(M+I+K+E)
      ++ " PETER = " ++ show(P+E+T+E+R)
      ++ " ROBIN = " ++ show(R+O+B+I+N)
      ++ "\nSTEVE = " ++ show(S+T+E+V+E)
      ++ " TOM = " ++ show(T+O+M) ];
      
      % Multiple solutions found
      % DES = 40 and MIKE = 40 (20 solutions checked)
      
      % Typical solution
      % ANDREW = 81 ANGELA = 87 DANNY = 79
      % DES = 40 IAN = 34 JOHN = 46
      % MIKE = 40 PETER = 68 ROBIN = 46
      % STEVE = 68 TOM = 19
      % ----------
      % Finished in 396msec
      

      Like

  • Unknown's avatar

    Jim Randell 3:23 pm on 18 August 2019 Permalink | Reply
    Tags: by: Len Wood   

    Brain-Teaser 494: [Squares from rectangles] 

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

    Tina and Cindy each started with a rectangular sheet of cardboard. The two rectangles had equal dimensions.

    Each girl cut her sheet into 5 squares using 4 straight cuts; the first 3 cuts producing one square each and the fourth cut producing two squares. The first square obtained by Tina had twice the area of the first square obtained by Cindy.

    If Tina’s fifth square had an area of one square inch, what was the area of the rectangles?

    This puzzle was originally published with no title.

    [teaser494]

     
    • Jim Randell's avatar

      Jim Randell 3:24 pm on 18 August 2019 Permalink | Reply

      Instead of considering cutting up the rectangle we consider adding squares to an existing rectangle.

      Tina’s 4th and 5th squares are 1 inch squares, so come from a 2 × 1 rectangle. A square can be added to any rectangle by extending it horizontally or vertically, and we need to add three more squares, so there are 8 possible starting rectangles.

      Cindy’s rectangle is also one of these 8 appropriately scaled to make the rectangles have the same area. If Cindy’s rectangle is scaled by f along each side then the area is scaled by .

      Also the area of Tina’s first square is twice the area of Cindy’s first square.

      This Python 3 program finds the 8 possible rectangles and their divisions and finds a pair that give an appropriate scale factor.

      It runs in 87ms.

      from enigma import (subsets, printf)
      
      # extend the rectangle (x, y) with k more squares
      def solve(x, y, k, rs=[]):
        if k == 0:
          yield (x, y, rs)
        else:
          # grow in the x direction by adding a y square
          yield from solve(x + y, y, k - 1, rs + [y])
          # grow in the y direction by adding an x square
          yield from solve(x, x + y, k - 1, rs + [x])
      
      # choose rectangles for T and C
      for ((Tx, Ty, Tss), (Cx, Cy, Css)) in subsets(solve(1, 2, 3, [1, 1]), size=2, select="P"):
        (A, f2A) = (Tx * Ty, Cx * Cy)
        # check scale factors
        if f2A * Tss[-1] ** 2 == 2 * A * Css[-1] ** 2:
          # output solution
          printf("area = {A} [T: {Tx}x{Ty} = {Tss}, fC: {Cx}x{Cy} = {Css}]", A=Tx * Ty)
      

      Solution: The area of the rectangles was 28 square inches.

      Tina’s rectangle was 4×7, and her squares had area: 16, 9, 1, 1, 1.

      Cindy’s rectangle was (7√2)×(2√2), and her squares had area: 8, 8, 8, 2, 2.

      Here is a diagram of the two rectangles:

      Like

c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel
Design a site like this with WordPress.com
Get started