Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 8:14 am on 25 February 2019 Permalink | Reply
    Tags:   

    Teaser 2945: Infernal indices 

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

     

    The above is the size of the evil multitude in the last “Infernal indices” novel: “A deficit of daemons”.

    Spoiler Alert: At the end, the forces of good engage in the fewest simultaneous battles that prevent this evil horde splitting, wholly, into equal-sized armies for that number of battles. The entire evil horde is split into one army per battle, all equally populated, bar one which has a deficit of daemons, leading to discord and a telling advantage for the forces of good.

    How many battles were there?

    [teaser2945]

     
    • Jim Randell's avatar

      Jim Randell 8:15 am on 25 February 2019 Permalink | Reply

      Although the given number (let’s call it N) is large (it is 36,306 decimal digits), it is not too large for Python to cope with.

      We need to find the smallest whole number n that does not divide N. This is a much smaller number.

      The following Python program runs in 107ms.

      Run: [ @replit ]

      from enigma import (irange, inf, printf)
      
      # construct the large number N
      x = 6**6
      N = (6**x) - (x**6)
      
      # find the smallest number that does not divide it
      for n in irange(2, inf):
        if N % n != 0:
          printf("n={n}")
          break
      

      Solution: There were 17 battles.

      (N mod 17) = 14, so the best the evil horde could manage would be 16 armies the same size and 1 army that had 3 fewer demons.

      Like

      • Jim Randell's avatar

        Jim Randell 4:46 pm on 2 March 2019 Permalink | Reply

        We can use the technique given in Enigma 1494 to compute the residues of large powers without the need to construct the huge numbers.

        The internal runtime for the following program is significantly less than the simple program given above (169µs vs. 961µs for the program given above). Although in practise both programs run instantaneously.

        from enigma import (irange, inf, printf)
        
        # compute a^b mod n
        def mpow(a, b, n):
          s = 1
          while b > 0:
            (b, r) = divmod(b, 2)
            if r > 0: s = (s * a) % n
            a = (a * a) % n
          return s
        
        # N = 6^x - x^6
        x = 6**6
        
        # find the smallest number that does not divide it
        for n in irange(2, inf):
          # find the residue N mod n
          if mpow(6, x, n) != mpow(x, 6, n):
            printf("n={n}")
            break
        

        Using the 3-argument version of the Python builtin [[ pow() ]] instead of [[ mpow() ]] gets the internal run time down to 40µs.


        For a manual solution we can use “the difference of two squares” method to express the number as:

        N = (6^36)(6^23310 − 1)(6^23310 + 1)

        From which it is easy to see that N will have a large number of factors of 2 and 3, and the fact that any power of 6 ends in a 6 means that the difference of two powers of 6 will end in a 0, so it will also be divisible by 5.

        So we can start by considering the primes: 7, 11, 13, 17, 19, 23 when looking for a suitable n.

        When evaluating mpow(6, x, n) the exponent can be reduced modulo (n − 1) (as n is prime) using Euler’s Theorem, and we can always evaluate the mpow(x, 6, n) in 3 iterations.

        In the end we don’t even need to consider all these primes in order to get the answer, so we can arrive at the solution with only a modest amount of calculation.

        Like

        • Jim Randell's avatar

          Jim Randell 12:14 pm on 3 March 2019 Permalink | Reply

          Here’s my complete manual solution:

          Writing:

          N = (6^36)(6^23310 − 1)(6^23310 + 1)

          We can start to examine divisors:

          For n = 2, it clearly divides the (6^36) term. Similarly for n = 3, 4, 6, 8, 9, 12, 16, ….

          For n = 5, it doesn’t divide the (6^36) term, so let’s consider the value of (6^23310 mod 5):

          5 is prime, so we can use Euler’s Theorem to reduce the exponent mod (p − 1):

          6^23310 mod 5 =
          6^(23310 mod (5 − 1)) mod 5 =
          6^2 mod 5 =
          36 mod 5 =
          1

          And if (6^23310 mod 5) = 1 then 5 divides the (6^23310 − 1) term.

          For n = 7, we consider the value of (6^23310 mod 7):

          6^23310 mod 7 =
          6^(23310 mod 6) mod 7 =
          6^0 mod 7 =
          1 mod 7 =
          1

          So 7 divides the (6^23310 − 1) term.

          For n = 10 we already know 2 divides the (6^36) term and 5 divides the (6^23310 − 1) term.

          For n = 11:

          6^23310 mod 11 =
          6^(23310 mod 10) mod 11 =
          6^0 mod 11 =
          1 mod 11 =
          1

          So 11 divides the (6^23310 − 1) term.

          For n = 13:

          6^23310 mod 13 =
          6^(23310 mod 12) mod 13 =
          6^6 mod 13 =
          12

          And 12 is equivalent to −1 mod 13, so 13 divides the (6^23310 + 1) term.

          For n = 14, we already have divisors of 2 and 7.

          For n = 15, we already have divisors of 3 and 5.

          for n = 17:

          6^23310 mod 17 =
          6^(23310 mod 16) mod 17 =
          6^14 mod 17 =
          (6^7 mod 17)^2 mod 17 =
          196 mod 17 =
          9

          9 is not equivalent to +1 or −1 mod 17, so 17 does not divide any of the terms, and gives our solution.

          Like

  • Unknown's avatar

    Jim Randell 7:39 am on 24 February 2019 Permalink | Reply
    Tags:   

    Teaser 2944: Happy families 

    From The Sunday Times, 24th February 2019 [link] [link]

    Teaser 2944

    Oliver lives with his parents, Mike and Nellie, at number 5. In each of numbers 1 to 4 lives a family, like his, with a mother, a father and one child. He tries listing the families in alphabetical order and produces the table above.

    However, apart from his own family, there is just one father, one mother and one child in the correct position. Neither Helen nor Beth lives at number 3 and neither Dave nor Ingrid lives at number 1. George’s house number is one less than Larry’s and Beth’s house number is one less than Carol’s. Apart from Oliver’s family, who are correctly positioned?

    [teaser2944]

     
    • Jim Randell's avatar

      Jim Randell 8:05 am on 24 February 2019 Permalink | Reply

      The wording of the puzzle is a little strange. I’m assuming the columns of the correct table are placed in order of house number (not “alphabetical” order), and the names of the fathers, mothers and children have been placed in the correct rows, just not necessarily in the correct columns. It seems like a reasonable assumption, and leads to a unique solution.

      I think I would have described the filling out of the original table like this:

      Oliver knows the names of the fathers, mothers and children, but is not sure who lives where, so he filled out each row in alphabetical order (from left to right), to give the table above. This correctly places his own family at house number 5. However …

      We can easily consider all the possibilities and eliminate those that do not satisfy the requirements.

      This Python program runs in 71ms.

      Run: [ @repl.it ]

      from itertools import permutations
      from enigma import printf
      
      # generate permutations that are only correct in 1 position
      def generate(vs, k=1):
        for s in permutations(vs):
          if sum(x == v for (x, v) in zip(s, vs)) == k:
            yield s
      
      # permute the Mums
      for mums in generate('BEHK'):
        (m1, m2, m3, m4) = mums
      
        # neither H or B live at number 3
        if m3 == 'H' or m3 == 'B': continue
      
        # permute the kids
        for kids in generate('CFIL'):
          (k1, k2, k3, k4) = kids
      
          # I does not live at number 1
          if k1 == 'I': continue
      
          # B's house number is 1 less than C's
          if not (mums.index('B') + 1 == kids.index('C')): continue
      
          # permute the dads
          for dads in generate('ADGJ'):
            (d1, d2, d3, d4) = dads
      
            # D does not live at 1
            if d1 == 'D': continue
      
            # G's house number is 1 less than L's
            if not (dads.index('G') + 1 == kids.index('L')): continue
      
            printf("1 = {d1}+{m1}+{k1}; 2 = {d2}+{m2}+{k2}; 3 = {d3}+{m3}+{k3}; 4 = {d4}+{m4}+{k4}")
      

      Solution: George, Kate and Larry were also correctly placed.

      Like

  • Unknown's avatar

    Jim Randell 10:31 pm on 21 February 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 454: Nine holes 

    From The Sunday Times, 25th January 1970 [link]

    The card of the course at Triangle Park Golf Club gives the total length of the first 9 holes as 3,360 yards. Par for the individual holes (numbers 1 to 9 respectively) being 5, 4, 5, 4, 3, 4, 4, 3, 5 (total 37).

    Closer inspection of the card would show that the lengths of the holes (each a whole number of yards) are such that holes 1, 2 and 3 could each form a different side of the same right-angled triangle (Triangle A) and that other right-angled triangles could similarly be formed from lengths equal to those of holes 4, 5 and 6 (Triangle B); 7, 8 and 9 (Triangle C); 1, 4 and 7 (Triangle X); 2, 5 and 8 (Triangle Y) and 3, 6 and 9 (Triangle Z).

    Moreover, the total length of the three holes forming Triangle A, the total length of the three holes forming Triangle B, and the total length of the three holes forming Triangle C could similarly form the three sides of another right-angled triangle (Triangle ABC) while yet another right-angled triangle could similarly be constructed from the total lengths of the holes forming triangles X, Y and Z (Triangle XYZ).

    In triangle ABC the sides would be in the ratio 5:3:4.

    The length of each of the par 3 holes is between 150 and 250 yards (one being 168 yards); the par 4 holes are between 250 and 450 yards; and the par 5 holes are between 475 and 600 yards (one being 476 yards).

    What are the lengths of holes 2, 6 and 7?

    This puzzle is included in the book Sunday Times Brain Teasers (1974).

    [teaser454]

     
    • Jim Randell's avatar

      Jim Randell 10:33 pm on 21 February 2019 Permalink | Reply

      This Python program uses the [[ pythagorean_triples() ]] function from the enigma.py library to find appropriate triangles. (Originally written for Teaser 2910).

      Run: [ @replit ]

      from enigma import (defaultdict, pythagorean_triples, sq, printf)
      
      # par for distance x
      def par(x):
        if 149 < x < 251: return 3
        if 249 < x < 451: return 4
        if 474 < x < 601: return 5
      
      # find triangles in the appropriate classes
      ts = defaultdict(list)
      for t in pythagorean_triples(600):
        k = tuple(par(x) for x in t)
        if None not in k:
          ts[k].append(t)
      
      # select tuples from d[k] ordered by indices in ss
      def select(d, k, ss):
        for x in d[k]:
          for s in ss:
            yield tuple(x[i] for i in s)
      
      # does (x, y, z) form a right-angled triangle
      def is_triangle(x, y, z):
        return sq(x) + sq(y) + sq(z) == 2 * sq(max(x, y, z))
      
      
      # choose triangle A (5, 4, 5)
      for (d1, d2, d3) in select(ts, (4, 5, 5), [(1, 0, 2), (2, 0, 1)]):
      
        # choose triangle B (4, 3, 4)
        for (d4, d5, d6) in select(ts, (3, 4, 4), [(1, 0, 2), (2, 0, 1)]):
      
          # choose triangle C (4, 3, 5)
          for (d7, d8, d9) in select(ts, (3, 4, 5), [(1, 0, 2)]):
      
            # check the total distance
            if not (sum((d1, d2, d3, d4, d5, d6, d7, d8, d9)) == 3360): continue      
      
            # check the other triangles
            (X, Y, Z) = ((d1, d4, d7), (d2, d5, d8), (d3, d6, d9))
            ABC = (d1 + d2 + d3, d4 + d5 + d6, d7 + d8 + d9)
            XYZ = (d1 + d4 + d7, d2 + d5 + d8, d3 + d6 + d9)
            if not all(is_triangle(*t) for t in (X, Y, Z, ABC, XYZ)): continue
      
            # check ABC is a 3, 4, 5 triangle
            if not (5 * min(ABC) == 3 * max(ABC)): continue
      
            # check the given pars
            if not (168 in (d5, d8) and 476 in (d1, d3, d9)): continue
      
            printf("d1={d1} d2={d2} d3={d3} / d4={d4} d5={d5} d6={d6} / d7={d7} d8={d8} d9={d9}")
      

      Solution: Hole 2 is 280 yards. Hole 6 is 357 yards. Hole 7 is 420 yards.

      There is only one solution:

      Hole 1 = 525 yards
      Hole 2 = 280 yards
      Hole 3 = 595 yards
      Hole 4 = 315 yards
      Hole 5 = 168 yards
      Hole 6 = 357 yards
      Hole 7 = 420 yards
      Hole 8 = 224 yards
      Hole 9 = 476 yards

      In fact if the triangle constraints for A, B, C, X, Y, Z, ABC, XYZ are satisfied, there is only a single solution even if the conditions for the total distance, ratios for ABC, and the specific distances for par 3 and 5 are ignored (lines 38, 47, 50).

      Like

      • Jim Randell's avatar

        Jim Randell 9:53 am on 23 February 2019 Permalink | Reply

        This puzzle is a good candidate for solving with a declarative set of constraints.

        Here is a MiniZinc model, that is run via the minizinc.py module. It solves the puzzle in 93ms.

        %#! python3 -m minizinc use_embed=1
        
        % hole distances
        {var("150..250", ["d5", "d8"])};  % par 3
        {var("250..450", ["d2", "d4", "d6", "d7"])};  % par 4
        {var("475..600", ["d1", "d3", "d9"])};  % par 5
        
        % total distance
        constraint d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 = 3360;
        
        % right angled triangle constraints
        predicate is_triangle(var int: a, var int: b, var int: c) =
          pow(a, 2) + pow(b, 2) + pow(c, 2) = pow(max([a, b, c]), 2) * 2;
        
        constraint is_triangle(d1, d2, d3); % triangle A
        constraint is_triangle(d4, d5, d6); % triangle B
        constraint is_triangle(d7, d8, d9); % triangle C
        
        constraint is_triangle(d1, d4, d7); % triangle X
        constraint is_triangle(d2, d5, d8); % triangle Y
        constraint is_triangle(d3, d6, d9); % triangle Z
        
        constraint is_triangle(d1 + d2 + d3, d4 + d5 + d6, d7 + d8 + d9); % triangle ABC
        constraint is_triangle(d1 + d4 + d7, d2 + d5 + d8, d3 + d6 + d9); % triangle XYZ
        
        % ABC is a 3, 4, 5 triangle
        constraint 5 * min([d1 + d2 + d3, d4 + d5 + d6, d7 + d8 + d9]) = 3 * max([d1 + d2 + d3, d4 + d5 + d6, d7 + d8 + d9]);
        
        % some lengths are given
        constraint d5 = 168 \/ d8 = 168;  % par 3
        constraint d1 = 476 \/ d3 = 476 \/ d9 = 476;  % par 5
        
        solve satisfy;
        

        Like

    • GeoffR's avatar

      GeoffR 11:07 am on 23 February 2019 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % The nine holes are H1..H9 with Par distances as below:
      var 475..600: H1; var 250..450: H2; var 475..600: H3; % Par 5, 4, 5
      
      var 250..450: H4; var 150..250: H5; var 250..450: H6; % Par 4, 3, 4
      
      var 250..450: H7; var 150..250: H8; var 475..600: H9; % Par 4, 3, 5
      
      % Given hole distances
      constraint H5 == 168 \/ H8 == 168;
      constraint H1 == 476 \/ H3 == 476 \/ H9 == 476;
      
      % Total distance for all holes
      constraint H1 + H2 + H3 + H4 + H5 + H6 + H7 + H8 + H9 == 3360;
      
      % Triangle A - H1 and H3 are both Par 5
      constraint (H2 * H2 + H3 * H3 == H1 * H1) \/(H2 * H2 + H1 * H1 == H3 * H3);
      
      % Triangle B - H4 and H6 are both Par 4
      constraint( H5 * H5 + H6 * H6 == H4 * H4) \/ (H5 * H5 + H4 * H4 == H6 * H6);
      
      % Triangle C - H9 is higher Par than H7 or H8
      constraint (H7 * H7 + H8 * H8 == H9 * H9);
      
      % % Triangle X - H1 is higher Par than H4 or H7
      constraint H4 * H4 + H7 * H7 == H1 * H1;
      
      % % Triangle Y - H2 is higher Par than H5 and H8
      constraint (H8 * H8 + H5 * H5 = H2 * H2); 
      
      % % Triangle Z - H3 and H9 are both Par 5
      constraint (H6 * H6 + H9 * H9 == H3 * H3)\/ (H3 * H3 + H6 * H6 == H9 * H9);
      
      % Triangle ABC sides
      var 500..2000: A1;
      var 500..2000: B1;
      var 500..2000: C1;
      
      constraint A1 = H1 + H2 + H3;
      constraint B1 = H4 + H5 + H6;
      constraint C1 = H7 + H8 + H9;
      
      % Sides are in ratio 5:4:3
      constraint A1 mod 5 == 0 \/ B1 mod  5 == 0 \/ C1 mod 5 == 0;
      constraint A1 mod 4 == 0 \/ B1 mod  4 == 0 \/ C1 mod 4 == 0;
      constraint A1 mod 3 == 0 \/ B1 mod  3 == 0 \/ C1 mod 3 == 0;
      
      constraint (A1 * A1 + B1 * B1 == C1 * C1)
      \/ (A1 * A1 + C1 * C1 == B1 * B1)
      \/ (B1 * B1 + C1 * C1 == A1 * A1);
      
      % Triangle XYZ sides
      var 500..4000: X1;
      var 500..4000: Y1;
      var 500..4000: Z1;
      
      constraint X1 = H1 + H4 + H7;
      constraint Y1 = H2 + H5 + H8;
      constraint Z1 = H3 + H6 + H9;
      
      constraint (X1 * X1 + Y1 * Y1 == Z1 * Z1)
      \/ (X1 * X1 + Z1 * Z1 == Y1 * Y1)
      \/ (Y1 * Y1 + Z1 * Z1 == X1 * X1);
      
      solve satisfy;
      
      % H1 = 525; H2 = 280; H3 = 595;  Holes H1..H9 lengths (yd)
      % H4 = 315; H5 = 168; H6 = 357;
      % H7 = 420; H8 = 224; H9 = 476;
      % Solution: H2 = 280, H6 = 357, H7 = 420
       
      % A1 = 1400; B1 = 840; C1 = 1120;  Triangle ABC sides
      % X1 = 1260; Y1 = 672; Z1 = 1428;  Triangle XYZ sides
      % ----------
      % ==========
      % Finished in 237msec
      
      

      Like

  • Unknown's avatar

    Jim Randell 3:06 pm on 21 February 2019 Permalink | Reply
    Tags:   

    Teaser 2910: Living on the coast 

    From The Sunday Times, 1st July 2018 [link] [link]

    George and Martha are living on Pythag Island. Not surprisingly, it is in the shape of a right-angled triangle, and each side is an integral number of miles (under one hundred) long. Martha noticed that the area of the island (expressed in square miles) was equal to an exact multiple of its perimeter (expressed in miles). George was trying to work out that multiple, but gave up: “Please write down the appropriate digit” he said. “Impossible!” said Martha.

    What are the lengths of the coasts?

    [teaser2910]

     
    • Jim Randell's avatar

      Jim Randell 3:07 pm on 21 February 2019 Permalink | Reply

      Suppose the triangle has sides x, y, z where 0 < x < y < z < 100.

      We have:

      x² + y² = z²

      area = A = xy/2
      perimeter = P = x + y + z

      ratio = k = A/P = xy/2(x + y + z)

      Consider:

      (x + y + z)(x + y − z) = (x² + y² − z²) + 2xy = 2xy
      ⇒ xy = (x + y + z)(x + y − z)/2

      So:

      k = (x + y + z)(x + y − z)/4(x + y + z) = (x + y − z)/4

      This Python program uses the [[ pythagorean_triples() ]] function from the enigma.py library to find appropriate triangles. The routine itself uses the technique described at [ @Wikipedia ]. It runs in 71ms.

      Run: [ @repl.it ]

      from enigma import (pythagorean_triples, div, printf)
      
      # consider all pythagorean triples with hypotenuse less than 100
      for (x, y, z) in pythagorean_triples(99):
        # find ratio of area to perimeter, an integer with more than 1 digit
        k = div(x + y - z, 4)
        if k is None or k < 10: continue
        # output solution
        printf("x={x} y={y} z={z}, k={k}")
      

      Solution: The lengths of the coasts (in miles) are 65, 72, 97.

      Like

    • GeoffR's avatar

      GeoffR 1:26 pm on 22 February 2019 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..99:X; var 1..99:Y; var 1..99:Z;  % 3 sides of triangle
      constraint X < Y /\ Y < Z;
      
      var 1..5000: area;
      var 3..297: perim;
      var 2..50:ratio;
      
      constraint X * X + Y * Y == Z * Z;
      constraint area = (X * Y) div 2;
      constraint perim = X + Y + Z;
      
      constraint ratio = area/perim /\ ratio > 9;  % Single digit ratio is impossible (Martha)
      
      solve satisfy;
      
      % X = 65;  Y = 72; Z = 97;
      % area = 2340; perim = 234; ratio = 10;
      % ----------
      % ==========
      % Finished in 267msec
      

      Like

  • Unknown's avatar

    Jim Randell 8:23 am on 19 February 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 453: [Square field] 

    From The Sunday Times, 18th January 1970 [link]

    Old Hassan has been at it again! He has thought of a new way of parcelling out his square field between his three sons (Rashid and Riad the twins, and Ali).

    Calling the three into his tent on the eve of Ramadhan, he addressed them thus:

    “My sons, as I am now 74 years of age, I desire that you shall come into part of your inheritance. I have therefore divided my field into four triangular plots such that we each have an area proportional to our age.”

    “To avoid any friction regarding upkeep of fences I have also arranged that none of you shall have a common boundary line with either of your brothers. Further, knowing Rashid and Riad’s objection to being too strongly identified with each other, I have arranged for their plots to be of a different shape.”

    The twins are 11 years older than Ali.

    How old is Ali?

    This puzzle was originally published with no title.

    [teaser453]

     
    • Jim Randell's avatar

      Jim Randell 8:40 am on 19 February 2019 Permalink | Reply

      Let’s suppose that the field is a unit square, and that Ali’s age is a (a whole number).

      Then the total of all the ages is d = a + 2(a + 11) + 74 = 3a + 96, so the corresponding areas of the triangular plots are:

      Hassan = 74 / d
      Rashid, Riad = (a + 11) / d
      Ali = a / d

      Splitting two sides of the square at x, y ∈ (0, 1) we have:

      For Rashid and Riad:

      (a + 11) / d = x / 2
      (a + 11) / d = (1 − x)(1 − y) / 2

      and for Ali:

      a / d = y / 2

      So, given a value for a we have:

      d = 3a + 96
      x = 2(a + 11) / d
      y = 2a / d
      (1 − x)(1 − y) = x

      So we can consider whole number values for a that give a solution to these equations:

      from enigma import (Rational, irange, printf)
      
      Q = Rational()
      
      # choose a value for a
      for a in irange(1, 50):
        # total age
        d = 3 * a + 96
        # calculate x and y
        (x, y) = (Q(2 * a + 22, d), Q(2 * a, d))
        # do they satisfy the third equation?
        if x == (1 - x) * (1 - y):
          printf("a={a} [x={x}, y={y}]")
      

      Solution: Ali is 24.

      A viable solution to the equations is:

      a = 24, x = 5/12, y = 2/7

      so the fields look like this:

      Hassan has 37/84 (≈ 44.1%) of the field. The twins have 5/24 (≈ 20.8%) each. And Ali has the remaining 1/7 (≈ 14.3%).

      There is a further solution to the equations at:

      a = −208/5, x = 17/8, y = 26/9

      but this doesn’t give a viable answer.

      Like

    • John Crabtree's avatar

      John Crabtree 4:24 pm on 16 April 2021 Permalink | Reply

      The four equations involving a, d, x and y can be reduced to:
      5a^2 + 88a -4992 = 0. Then a = -8.8 +/- 32.8

      Like

  • Unknown's avatar

    Jim Randell 8:25 am on 17 February 2019 Permalink | Reply
    Tags:   

    Teaser 2943: Keep your balance 

    From The Sunday Times, 17th February 2019 [link] [link]

    George and Martha have a set of a dozen balls, identical in appearance but each has been assigned a letter of the alphabet, A, B, …, K, L and each is made of a material of varying density so that their weights in pounds are 1, 2…. 11, 12 but in no particular order. They have a balance and the following weighings were conducted:

    (1) {A, C, I} vs {G, J, L}

    (2) {A, H, L} vs {G, I, K}

    (3) {B, I, J} vs {C, F, G}

    (4) {B, D, I} vs {E, G, H}

    (5) {D, F, L} vs {E, G, K}

    On all five occasions, there was perfect balance and the total of the threesome in each was a different prime number, that in (1) being the smallest and progressing to that in (5) which was the largest.

    In alphabetical order, what were the weights of each of the twelve balls?

    [teaser2943]

     
    • Jim Randell's avatar

      Jim Randell 8:38 am on 17 February 2019 Permalink | Reply

      We can solve this using the [[ SubstitutedExpression() ]] solver from the enigma.py library. Treating each symbol as standing for a different non-zero base 13 digit.

      This run-file executes in 332ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --base="13"
      --digits="1-12"
      
      --template=""
      
      # the weighings
      "A + C + I == G + J + L"
      "A + H + L == G + I + K"
      "B + I + J == C + F + G"
      "B + D + I == E + G + H"
      "D + F + L == E + G + K"
      
      # the total weights are prime
      "is_prime(A + C + I)"
      "is_prime(A + H + L)"
      "is_prime(B + I + J)"
      "is_prime(B + D + I)"
      "is_prime(D + F + L)"
      
      # the primes are in increasing order
      "A + C + I < A + H + L"
      "A + H + L < B + I + J"
      "B + I + J < B + D + I"
      "B + D + I < D + F + L"
      

      Solution: The weights of the balls are: A=4, B=8, C=5, D=9, E=12, F=11, G=1, H=6, I=2, J=7, K=10, L=3.

      Like

    • Jim Randell's avatar

      Jim Randell 10:19 am on 17 February 2019 Permalink | Reply

      There are six symbols in the first weighing, and together they must sum to twice the smallest prime, so the smallest possible value for the smallest prime is:

      divc(1 + 2 + 3 + 4 + 5 + 6, 2) = 11

      Similarly the largest possible value for the largest prime is:

      divf(7 + 8 + 9 + 10 + 11 + 12, 2) = 28

      So the list of possible primes is: 11, 13, 17, 19, 23.

      There are only 5 primes on the list, so the weighings give us 10 equations in 12 variables.

      Again using the [[ SubstitutedExpression() ]] solver, this run file executes in 131ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --base="13"
      --digits="1-12"
      --template=""
      
      # the weighings
      "A + C + I == 11"
      "G + J + L == 11"
      "A + H + L == 13"
      "G + I + K == 13"
      "B + I + J == 17"
      "C + F + G == 17"
      "B + D + I == 19"
      "E + G + H == 19"
      "D + F + L == 23"
      "E + G + K == 23"
      

      Like

      • Jim Randell's avatar

        Jim Randell 7:30 am on 5 March 2019 Permalink | Reply

        Here’s a manual solution:

        Given the 10 equations above and adding the equation:

        A + B + C + D + E + F + G + H + I + J + K + L = 78

        Gives us 11 equations in 12 variables.

        These can be solved to give the other values in terms of B:

        A = 44 − 5B
        C = 4B − 27
        D = 25 − 2B
        E = B + 4
        F = 27 − 2B
        G = 17 − 2B
        H = B − 2
        I = B − 6
        J = 23 − 2B
        K = B + 2
        L = 4B − 29

        And all the variables have to take on different values from 1 to 12.

        From I and E, we have:

        B ≥ 7
        B ≤ 8

        So B is 7 or 8.

        The formula for F gives:

        B = 7 → F = 13
        B = 8 → F = 11

        The first of these is not possible so B = 8, giving:

        A = 4; B = 8; C = 5; D = 9; E = 12; F = 11; G = 1; H = 6; I = 2; J = 7; K = 10; L = 3

        Like

        • Frits's avatar

          Frits 3:43 pm on 30 July 2020 Permalink | Reply

          @Jim

          “These can be solved to give the other values in terms of B:” –> easier said than done.

          Can your Enigma functions generate these 11 equations in terms of B?

          Like

          • Jim Randell's avatar

            Jim Randell 5:03 pm on 30 July 2020 Permalink | Reply

            You could use SymPy to reduce the set of equations down using a single parameter.

            But you can also choose a value for B and then you have 12 equations in 12 variables, which you can solve using the [[ Matrix.linear() ]] solver in enigma.py.

            Run: [ @replit ]

            from enigma import (Matrix, irange, join, printf)
            
            eqs = [
              # A  B  C  D  E  F  G  H  I  J  K  L
              ( 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), # B = ?
              ( 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 ), # (A, C, I) = 11
              ( 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 ), # (G, J, L) = 11
              ( 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 ), # (A, H, L) = 13
              ( 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0 ), # (G, I, K) = 13
              ( 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 ), # (B, I, J) = 17
              ( 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 ), # (C, F, G) = 17
              ( 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 ), # (B, D, I) = 19
              ( 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0 ), # (E, G, H) = 19
              ( 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1 ), # (D, F, L) = 23
              ( 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0 ), # (E, G, K) = 23
              ( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ), # (A ... L) = 78
            ]
            
            # the collection of weights
            weights = set(irange(1, 12))
            
            # choose a value for B
            for B in weights:
              # evaluate the equations
              s = Matrix.linear(eqs, [B, 11, 11, 13, 13, 17, 17, 19, 19, 23, 23, 78])
              # the answers should be the values in weights
              if not (set(s) == weights): continue
              # output solution
              printf("{s}", s=join((join((w, '=', v)) for (w, v) in zip("ABCDEFGHIJKL", s)), sep=" "))
            

            Like

            • Frits's avatar

              Frits 8:27 pm on 30 July 2020 Permalink | Reply

              Thanks, I ran the following code at [ https://live.sympy.org/ ]:

              from sympy import linsolve, symbols, linear_eq_to_matrix
              A,B,C,D,E,F,G,H,I,J,K,L = symbols("A,B,C,D,E,F,G,H,I,J,K,L", integer=True)
              variables = [A,C,D,E,F,G,H,I,J,K,L,B]
              
              def my_solver(known_vals):
              
                  eqns = [A + C + I - 11,
              G + J + L - 11,
              A + H + L - 13,
              G + I + K - 13,
              B + I + J - 17,
              C + F + G - 17,
              B + D + I - 19,
              E + G + H - 19,
              D + F + L - 23,
              E + G + K - 23,
              A + B + C + D + E + F + G + H + I + J + K + L - 78]
              
                  # add the known variables to equation list
                  for x in known_vals.keys():
                      eqns.append(x - (known_vals[x]))
              
                  X, b = linear_eq_to_matrix(eqns, variables)
                  solution = linsolve((X, b), variables)
              
                  return solution
              
              
              my_solver({})
              

              Output:

              {(44−5B, 4B−27, 25−2B, B+4, 27−2B, 17−2B, B−2, B−6, 23−2B, B+2, 4B−29, B)}

              Like

    • GeoffR's avatar

      GeoffR 3:38 pm on 19 February 2019 Permalink | Reply

      I assumed you did not want the answer shown?

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % The dozen balls weigh between 1 and 12 pounds
      var 1..12:A; var 1..12:B; var 1..12:C; var 1..12:D; var 1..12:E;
      var 1..12:F; var 1..12:G; var 1..12:H; var 1..12:I; var 1..12:J;
      var 1..12:K; var 1..12:L;
      
      % All the balls A..K are of different weight
      constraint all_different([A, B, C, D, E, F, G, H, I, J, K, L]);
      
      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 weighing equations
      constraint (A + C + I == G + J + L) 
      /\ (A + H + L == G + I + K)
      /\ (B + I + J == C + F + G) 
      /\ (B + D + I == E + G + H)
      /\ (D + F + L == E + G + K);
      
      % the total weights in the equations are all prime numbers
      constraint is_prime(A + C + I) /\ is_prime(A + H + L) /\ is_prime(B + I + J) 
      /\ is_prime(B + D + I) /\ is_prime(D + F + L);
      
      % The five primes are in increasing order
      constraint (A + C + I) < (A + H + L) /\  (A + H + L) < (B + I + J)
      /\ (B + I + J) < (B + D + I) /\  (B + D + I) < (D + F + L);
      
      solve satisfy;
      
      output [ " A = "++ show(A) ++ ", B = " ++ show(B) ++ ", C = " ++ show(C) ++ "\n"
      ++ " D = " ++ show(D) ++ ", E = " ++ show(E) ++ ", F = " ++ show(F) ++ "\n"
      ++ " G = " ++ show(G) ++ ", H = " ++ show(H) ++ ", I = " ++ show(I) ++ "\n"
      ++ " J = " ++ show(J) ++ ", K = " ++ show(K) ++ ", L = " ++ show(L) ];
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:53 pm on 16 February 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 452: [Coloured dice] 

    From The Sunday Times, 11th January 1970 [link]

    Three dice, numbered from 1 to 6, each had 2 blue, 2 red and 2 white faces. No number appeared in the same colour twice.

    When  Bill threw the dice they totalled 14, showing 2 white faces and 1 red. A second throw showed each die the same colour as before, but each had a different number totalling 15. On one die Bill noticed two similarly coloured sides added to 9.

    The telephone rang and Bill went to answer it, leaving little Tommy by himself, who then picked up one of the dice and painted a red face white and a white face red. He then threw the dice twice producing firstly 3 whites and then 3 reds, each throw showing one of the repainted sides.

    Tommy totalled the 3 whites to 8 and the 3 reds to 7. Unfortunately, sums were not his strong point and though he sometimes got them right, quite often he was 1 out, and sometimes even 2 out. However, he was certain that the 3 whites totalled more than the 3 reds.

    What numbers did Tommy repaint (1) red, and (2) white?

    This puzzle was originally published with no title.

    [teaser452]

     
    • Jim Randell's avatar

      Jim Randell 10:55 pm on 16 February 2019 Permalink | Reply

      This Python program runs in 370ms.

      Run: [ @repl.it ]

      from collections import namedtuple
      from itertools import permutations, combinations, product
      from enigma import irange, partitions, printf
      
      # represent a die
      Die = namedtuple("Die", "B R W")
      
      # collect possible dice
      dice = set(Die(*s) for ns in partitions(irange(1, 6), 2) for s in permutations(ns))
      
      # can we make the numbers in <ns> from the opposite faces of <fs>
      def make(ns, fs):
        ns = set(ns)
        (f1, f2, f3) = fs
        return any(ns.issubset([f1[i1] + f2[i2] + f3[i3], f1[1 - i1] + f2[1 - i2] + f3[1 - i3]]) for (i1, i2, i3) in product((0, 1), repeat=3))
      
      # change tuple <t> so index <i> is <v>
      def change(t, i, v):
        t = list(t)
        t[i] = v
        return tuple(t)
      
      # possible white, red values for Tommy (white > red)
      ts = set((w, r) for w in irange(6, 10) for r in irange(5, w - 1))
      
      # choose a set of three dice
      for (d1, d2, d3) in combinations(dice, 3):
      
        # "no number appeared in the same colour twice"
        if not all(len(set(x1 + x2 + x3)) == 6 for (x1, x2, x3) in zip(d1, d2, d3)): continue
      
        # "on one of the dice two similary coloured sides add up to 9"
        if not any(sum(x) == 9 for x in d1 + d2 + d3): continue
      
        # can we make a R, W, W combination that sums to 14 and 15
        if not any(make((14, 15), fs) for fs in [(d1.R, d2.W, d3.W), (d1.W, d2.R, d3.W), (d1.W, d2.W, d3.R)]): continue
      
        # Tommy picked one of the dice and swapped the colours of a red number and a white number, suppose he repaints t1
        for (t1, t2, t3) in [(d1, d2, d3), (d2, d1, d3), (d3, d1, d2)]:
          # choose red/white faces of t1 to repaint
          for (r, w) in product((0, 1), repeat=2):
            # the repainted die
            t = Die(B=t1.B, R=change(t1.R, r, t1.W[w]), W=change(t1.W, w, t1.R[r]))
      
            # record possible 3 white/red scores (including the repainted faces)
            ws = set(sum(s) for s in product([t.W[w]], t2.W, t3.W))
            rs = set(sum(s) for s in product([t.R[r]], t2.R, t3.R))
      
            # do these create feasible values?
            vs = ts.intersection(product(ws, rs))
            if vs:
              printf("dice: {t1} {t2} {t3}")
              printf("  red = {r}, white = {w} -> {vs}", r=t.R[r], w=t.W[w])
      

      Solution: (1) Tommy repainted 3 to red, (2) and 4 to white.

      There is only one possible set of dice:

      die 1: blue = 1,5; red = 2,4; white = 3,6
      die 2: blue = 2,6; red = 1,3; white = 4,5
      die 3: blue = 3,4; red = 5,6; white = 1,2

      The throws for Bill, showing 2 white faces and 1 red:

      throw 1: die 1 = white 3; die 2 = white 5; die 3 = red 6; total = 14
      throw 2: die 1 = white 6; die 2 = white 4; die 3 = red 5; total = 15

      Both die 1 and die 2 have white faces that sum to 9.

      Tommy chooses die 1 and repaints 3 from white to red, and 4 from red to white, giving (with the repainted faces indicated in braces):

      die 1: blue = 1,5; red = 2,{3}; white = {4},6
      die 2: blue = 2,6; red = 1,3; white = 4,5
      die 3: blue = 3,4; red = 5,6; white = 1,2

      Tommy then throws 3 whites (totalling 6 to 10), and then 3 reds (totalling 5 to 9), the white total being more than the red total.

      throw 1: die 1 = {white 4}; die 2 = white 4; die 3 = white 2; total = 10
      throw 2: die 1 = {red 3}; die 2 = red 1; die 3 = red 5; total = 9

      So Tommy’s calculation of 8 for the reds and 7 for the whites were both out by 2.

      Like

  • Unknown's avatar

    Jim Randell 7:48 am on 14 February 2019 Permalink | Reply
    Tags: by: Philip Spencer   

    Brain-Teaser 451: [Soccer tournament] 

    From The Sunday Times, 4th January 1970 [link]

    The final weeks of the University soccer tournament proved unusually exciting this year. With only four games left, Arts, Biology and Classics had drawn clear and were battling for the honours. The position then was that Biology were top with 32 points, Arts second also with 32 points, and Classics third with 30 points.

    All three teams had scored 70 goals, but Classics had conceded 35 goals giving them an inferior goal average (i.e. ratio of goals for and against) to both Arts and Biology.

    The remaining games required Classics to play both Biology and Arts, who each also had to play one game against another team lower in the division.

    It turned out that after the four games, Arts had failed to score and, although having scored five of the total of ten goals scored, Classics worsened their goal average.

    Consequently, all three teams finished with the same number of points, but Arts won the league from Classics on goal average.

    What were the final goals for and against for each team?

    A prize of £3 was offered for this puzzle.

    This puzzle was originally published with no title.

    [teaser451]

     
    • Jim Randell's avatar

      Jim Randell 9:25 am on 14 February 2019 Permalink | Reply

      Here’s a programmed solution in Python that uses the [[ Football() ]] helper class from the enigma.py library. It runs in 108ms.

      Run: [ @replit ]

      # there are four remaining matches: AC, BC, AX, BX
      
      from enigma import (Football, irange, cproduct, subsets, printf)
      
      # the scoring system
      football = Football(games='wdl', points=dict(w=2, d=1))
      
      # possible scores (no team scored more than 5)
      scores = dict()
      scores['w'] = set((x, y) for x in irange(1, 5) for y in irange(0, x - 1))
      scores['d'] = set((x, x) for x in irange(0, 5))
      scores['l'] = set((y, x) for (x, y) in scores['w'])
      
      # possible outcomes in the remaining 4 games
      for (AC, BC, AX, BX) in football.games(repeat=4):
      
        # table for A, B, C in these four games
        A = football.table([AC, AX], [0, 0])
        B = football.table([BC, BX], [0, 0])
        C = football.table([AC, BC], [1, 1])
      
        # A and B got the same number of additional points, and C got 2 more
        if not (A.points == B.points == C.points - 2): continue
      
        # choose scores for the 4 matches
        for (sAC, sBC, sAX, sBX) in cproduct(scores[x] for x in (AC, BC, AX, BX)):
      
          # in total 10 goals are scored in the 4 matches
          if not (sum(x + y for (x, y) in (sAC, sBC, sAX, sBX)) == 10): continue
      
          # goals for/against A, B, C
          (fA, aA) = football.goals([sAC, sAX], [0, 0])
          (fB, aB) = football.goals([sBC, sBX], [0, 0])
          (fC, aC) = football.goals([sAC, sBC], [1, 1])
      
          # A failed to score, C scored 5 goals
          if not (fA == 0 and fC == 5): continue
      
          # C worsened their goal average: (70 + fC) / (35 + aC) < 70 / 35
          if not (fC < 2 * aC): continue
      
          # A, B started off with 70 goals for and a, b goals against
          # their goal average is better than 70/35, so b < a < 35
          for (b, a) in subsets(irange(1, 34), size=2):
      
            # and we ended with A having the best goal average, then C, then B
            if not ((70 + fA) * (35 + aC) > (70 + fC) * (a + aA)): continue
            if not ((70 + fC) * (b + aB) > (70 + fB) * (35 + aC)): continue
      
            # output solution
            printf("AC={AC}:{sAC} BC={BC}:{sBC} AX={AX}:{sAX} BX={BX}:{sBX} / a={a} b={b}")
            printf("A={A} f={fA} a={aA}", fA=70 + fA, aA=a + aA)
            printf("B={B} f={fB} a={aB}", fB=70 + fB, aB=b + aB)
            printf("C={C} f={fC} a={aC}", fC=70 + fC, aC=35 + aC)
            printf()
      

      Solution: A: for = 70, against = 35; B: for = 73, against = 37; C: for = 75, against = 38.

      There are two scenarios. Assuming 2 points for a win and 1 for a draw, we start with:

      A: points = 32; goals for = 70; goals against = 33; avg = 70/33 = 2.121
      B: points = 32; goals for = 70; goals against = 32; avg = 70/32 = 2.188
      C: points = 30; goals for = 70; goals against = 35; avg = 70/35 = 2.000

      Then the remaining four matches are played. The outcomes are one of:

      (1) A vs C = 0-0; B vs C = 3-5; A vs X = 0-2; B vs X = 0-0
      (2) A vs C = 0-2; B vs C = 3-3; A vs X = 0-0; B vs X = 0-2

      Each of these scenarios has 10 goals scored, none of them by A and 5 of them by C.

      The final table then becomes:

      A: points = 33; goals for = 70; goals against = 35; avg = 70/35 = 2.000
      B: points = 33; goals for = 73; goals against = 37; avg = 73/37 = 1.973
      C: points = 33; goals for = 75; goals against = 38; avg = 75/38 = 1.974

      Like

  • Unknown's avatar

    Jim Randell 11:54 am on 12 February 2019 Permalink | Reply
    Tags:   

    Teaser 2935: A palindrome 

    From The Sunday Times, 23rd December 2018

    → See: [ Teaser 2935: A palindrome at Enigmatic Code ]

    [teaser2935]

     
  • Unknown's avatar

    Jim Randell 11:52 am on 12 February 2019 Permalink | Reply
    Tags:   

    Teaser 2907: Combinatorial cards 

    From The Sunday Times, 10th June 2018

    → See: [ Teaser 2907: Combinatorial cards at Enigmatic Code ]

    [teaser2907]

     
  • Unknown's avatar

    Jim Randell 11:49 am on 12 February 2019 Permalink | Reply
    Tags:   

    Teaser 2784: Three lives 

    From The Sunday Times, 31st January 2016

    → See: [ Teaser 2784: Three lives at Enigmatic Code ]

    [teaser2784]

     
  • Unknown's avatar

    Jim Randell 11:44 am on 12 February 2019 Permalink | Reply
    Tags:   

    Teaser 2779: New Year Party 

    From The Sunday Times, 27th December 2015

    → See: [ Teaser 2779: New Year Party at Enigmatic Code ]

    [teaser2779]

     
  • Unknown's avatar

    Jim Randell 11:41 am on 12 February 2019 Permalink | Reply
    Tags:   

    Teaser 2773: King Lear III 

    From The Sunday Times, 15th November 2015

    → See: [ Teaser 2773: King Lear III at Enigmatic Code ]

    [teaser2773]

     
  • Unknown's avatar

    Jim Randell 11:37 am on 12 February 2019 Permalink | Reply
    Tags:   

    Teaser 2759: King Lear II 

    From The Sunday Times, 9th August 2015

    → See: [ Teaser 2759: King Lear II at Enigmatic Code ]

    [teaser2759]

     
  • Unknown's avatar

    Jim Randell 10:33 am on 12 February 2019 Permalink | Reply
    Tags: ,   

    Teaser 2503: [Cube reflections] 

    From The Sunday Times, 12th September 2010

    → See: [ Teaser 2503 at Enigmatic Code ]

    This puzzle was originally published with no title.

    [teaser2503]

     
  • Unknown's avatar

    Jim Randell 3:27 pm on 11 February 2019 Permalink | Reply
    Tags:   

    Teaser 2565: Red and black 

    From The Sunday Times, 20th November 2011 [link] [link]

    I have taken 10 cards and written a different digit on each one. Some of the cards are red and the rest are black. I have placed some of the red cards in a row to form a long number. Then I have moved the last card to the front to give me a larger number. In fact this larger number divided by the original one equals a digit on one of the black cards.

    What was the original number?

    [teaser2565]

     
    • Jim Randell's avatar

      Jim Randell 3:53 pm on 11 February 2019 Permalink | Reply

      This puzzle is similar Enigma 1036.

      Here is a Python program that solves it using a similar analysis.

      Run: [ @replit ]

      from enigma import (irange, nsplit, int2base, printf)
       
      # consider multipliers
      for k in irange(2, 9):
        q = 10 * k - 1
       
        # consider n digit numbers
        for n in irange(1, 8):
          p = 10 ** n - k
       
          # consider possible mobile digit d (different from k)
          for d in irange(0, 9):
            if d == k: continue
       
            # the corresponding n digit number x, is...
            (x, r) = divmod(d * p, q)
            if r != 0: continue
            # x has to be n different digits
            s = set(nsplit(x, n))
            if len(s) != n: continue
            # also different from d and k
            if d in s or k in s: continue
       
            printf("{x}{d} x {k} = {d}{x}", x=int2base(x, width=n))
      

      Solution: The original number is 230769.

      So we have:

      230769 × 4 = 923076

      If we allow leading zeros there is a further solution:

      076923 × 4 = 307692

      Like

      • Frits's avatar

        Frits 11:42 am on 29 October 2020 Permalink | Reply

        @Jim,

        Is the phrase “some of the red cards” the reason why “n” cannot be 9?

        from enigma import SubstitutedExpression, seq_all_different, nsplit, div
        
        # the alphametic puzzle
        p = SubstitutedExpression(
          [# solve J * ABCDEFGHI = IABCDEFGH, J is one of the black cards
           
           # the derived formula
           "div(I * (10**N - J), (10 * J - 1)) = ABCDEFGH",
           # all different numbers
           "seq_all_different(nsplit(ABCDEFGH) + (I, J))",
           
           "len(str(ABCDEFGH)) = N"
          ],
          answer="10 * ABCDEFGH + I, J, I * 10**N + ABCDEFGH",
          verbose=0,
          d2i=dict([(k, "IJN") for k in {0,1}]),
          distinct="",   # allow variables with same values
          #reorder=0,
        )
        
        # Print answer
        for (_, ans) in p.solve():
          print(f"{ans[0]} x {ans[1]} = {ans[2]}")
        
        # 230769 x 4 = 923076
        

        Like

  • Unknown's avatar

    Jim Randell 8:27 pm on 7 February 2019 Permalink | Reply
    Tags:   

    Teaser 2942: What do points make? 

    From The Sunday Times, 10th February 2019 [link] [link]

    In the Premier League table a team’s points are usually roughly equal to their goals scored (Burnley were an interesting exception in 2017-18). That was exactly the case in our football league after the four teams had played each other once, with 3 points for a win and 1 for a draw.

    A ended up with the most points, followed by B, C and D in that order. Fifteen goals had been scored in total, and all the games had different scores. The best game finished 5-0, and the game BvD had fewer than three goals.

    What were the results of B’s three games (in the order BvA, BvC, BvD)?

    [teaser2942]

     
    • Jim Randell's avatar

      Jim Randell 11:38 am on 8 February 2019 Permalink | Reply

      I think this puzzle could have been worded more clearly.

      I took the first part to mean we are looking for situations where each team has exactly the same number of points as the number of goals scored by that team. And that the “best game finished 5-0” means that that game had the most goals scored altogether (i.e. the other games had no more than 4 goals scored in total). I took the fact that all matches had different scores to mean that you couldn’t have (for example) one game with a score of 2-0 and another with a score of 0-2.

      This program uses the [[ Football() ]] helper class from the enigma.py library. It looks for possible games for a team that can give the same number of points as the number of goals scored by that team, and then chooses outcomes for the four teams that satisfy the remaining conditions of the puzzle. It runs in 340ms.

      Run: [ @replit ]

      from itertools import (product, combinations)
      from enigma import (defaultdict, Football, ordered, printf)
      
      # scoring system
      football = Football(games='wdl', points=dict(w=3, d=1))
      
      # possible scores (a 5-0 and then no more than 4 goals in any match)
      scores = dict()
      scores['w'] = set([(1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (2, 1), (3, 1)])
      scores['d'] = set([(0, 0), (1, 1), (2, 2)])
      scores['l'] = set((x, y) for (y, x) in scores['w'])
      
      # record possible games by points (= goals for)
      ss = defaultdict(list)
      
      # consider outcomes for three matches for team T against X, Y, Z
      for (TX, TY, TZ) in football.games(repeat=3):
      
        # make the table for team T
        T = football.table([TX, TY, TZ], [0, 0, 0])
      
        # make possible score lines that give "goals for" = "points"
        for (sTX, sTY, sTZ) in product(scores[TX], scores[TY], scores[TZ]):
      
          (fT, aT) = football.goals([sTX, sTY, sTZ], [0, 0, 0])
      
          if not (fT == T.points): continue
      
          ss[fT].append((sTX, sTY, sTZ))
      
      # choose possible points (= goals for) for A, B, C, D
      for (pA, pB, pC, pD) in combinations(sorted(ss.keys(), reverse=1), 4):
      
        # but points = goals for, and the total number of goals is 15
        if not (pA + pB + pC + pD == 15): continue
      
        # choose matches for B
        for (BA, BC, BD) in ss[pB]:
      
          # B vs D has less than 3 goals scored
          if not (sum(BD) < 3): continue
      
          # choose matches for A
          for (AB, AC, AD) in ss[pA]:
            if not (AB == BA[::-1]): continue
      
            # choose matches for C
            for (CA, CB, CD) in ss[pC]:
              if not (CA == AC[::-1] and CB == BC[::-1]): continue
      
              # check matches for D
              if not ((AD[::-1], BD[::-1], CD[::-1]) in ss[pD]): continue
      
              # all games have different scores
              s = set(ordered(*x) for x in (AB, AC, AD, BC, BD, CD))
              if not (len(s) == 6): continue
      
              # there is a 5-0 game
              if not ((0, 5) in s): continue
      
              printf("AB={AB} AC={AC} AD={AD} BC={BC} BD={BD} CD={CD}, A={pA} B={pB} C={pC} D={pD}")
      

      Solution: The scores in B’s games are: B vs A = 1-2; B vs C = 2-2; B vs D = 1-0.

      It turns out that the fact that there is a 5-0 game means that there are only 10 goals to distribute between the remaining 5 matches, and there are no further solutions if games with more than 4 goals scored are considered, so it is enough to know that there is a 5-0 score.

      If we allow “mirror” scores (e.g. one game with a score of 2-0 and another with a score of 0-2), then there are no further solutions either. (Although if we allow repeated scores then there are additional solutions).

      If we relax the conditions that the number of points is exactly the same as the number of goals scored then there are multiple solutions, even if they must be within 1 of each other.

      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