Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 5:20 pm on 24 July 2019 Permalink | Reply
    Tags:   

    Teaser 2896: Light angles 

    From The Sunday Times, 25th March 2018 [link] [link]

    The hotel’s night clerk was always glancing at the novelty 12-hour clock facing her. Its hands’ rotation axis was exactly midway across and between three-quarters and four-fifths of the way up the four-metre high rectangular wall. Light rays directed out along the continuous-motion minute and hour hands made pencil beams across the wall. Twice during her 12-hour shift she saw the beams hit the top corners of the wall simultaneously, and twice she saw the beams hit the bottom corners simultaneously.

    How wide was the wall to the nearest metre?

    [teaser2896]

     
    • Jim Randell's avatar

      Jim Randell 5:21 pm on 24 July 2019 Permalink | Reply

      I thought this was a neat puzzle, but there are only a few values to consider.

      For any given hour, there will be a (fractional) minute when the hour “hand” and the minute “hand” are the at the same angle from vertical, but on opposite sides, and so mirror each other about the vertical axis.

      If we consider the times between 12 o’clock and 6 o’clock, at some time before 3 o’clock the two “hands” will illuminate the top corners of the wall, and as sometime after 3 o’clock the two “hands” will illuminate the bottom corners of the wall. By choosing values for the “top” hours and “bottom” hours, we can determine the corresponding minutes, angles of the “hands” and hence the height of the origin of the hands up the wall. If it’s between 3.0m and 3.2m we have a solution.

      This Python program runs in 68ms.

      Run: [ @repl.it ]

      from math import tan
      from itertools import product
      from enigma import (irange, fdiv, two_pi, printf)
      
      # given an hour, compute the corresponding minute and tangent
      def hmt(h):
        m = fdiv((12 - h) * 60, 13) # minute
        a = fdiv(h, 12) + fdiv(m, 720) # angle (fraction of a circle)
        t = tan(two_pi * a) # tangent of a
        return (h, m, t)
      
      # consider the times when the hour and minute beams mirror each other
      # about the vertical axis
      
      # calculate (h, m, t) for hours from 0 to 5
      hmts = list(hmt(h) for h in irange(0, 5))
      
      # the hour for the top right corner is before 3
      # the hour for the bottom right corner is after 3
      for ((h1, m1, t1), (h2, m2, t2)) in product(hmts[:3], hmts[3:]):
        # calculate the height of the hands above the floor
        y = fdiv(4 * t1, t1 - t2)
        # check it is in range
        if 3.0 <= y <= 3.2:
          # calculate the width of the wall
          x = y * -t2
          w = 2 * x
          # output solution
          printf("width={w:.03f}, x={x:.03f} y={y:.03f} [TR @ {h1}:{m1:05.2f}, BR @ {h2}:{m2:05.2f}]")
      

      Solution: The wall is approximately 16 metres wide.

      The actual width of the wall (to the nearest millimetre) is 15.979m.

      The height of the origin of the hands is 3.030m.

      Here is a diagram that shows the wall, ruled with lines at heights of 1m, 2m, 3m and 3.2m.

      The upper lines would be illuminated at 2:46 and 9:14.

      The lower lines would be illuminated at 3:42 and 8:18.

      So it is possible to observe the situation between 2:46 and 9:14, which is only 6 hours 28 minutes.

      Like

      • Jim Randell's avatar

        Jim Randell 9:29 am on 25 July 2019 Permalink | Reply

        Here’s a chart with the choices for the times (upward times across the top, downward times along the side), and the calculated width of the wall and height of the hands.

        We find the solution (highlighted) when h is between 3.0m and 3.2m.

        Like

  • Unknown's avatar

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

    Brainteaser 1631: Ali Baba’s boxes 

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

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

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

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

    How many pieces did they steal?

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

    [teaser1631]

     
    • Jim Randell's avatar

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

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

      I enjoyed programming a constructive solution for this puzzle.

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

      It runs in 982ms

      Run: [ @repl.it ]

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

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

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

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

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

      The remaining 40 shapes cannot be folded into a cube.

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


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

      Run: [ @repl.it ]

      The output looks like this:

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

      Like

  • Unknown's avatar

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

    Teaser 2897: Easter egg 

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

    “Granddad, I’ve got a great big Easter egg in a square box!” My grandson Liam inspired this Teaser in which I have used non-zero digits then consistently replaced them with letters, using different letters for different digits.

    In this way, I have:

    EASTER
    (EGG)²
    BOX

    Two of the numbers add to make the third, and if I told you which number was largest you should be able to solve this Teaser.

    Find the value of BOX.

    [teaser2897]

     
    • Jim Randell's avatar

      Jim Randell 8:52 am on 22 July 2019 Permalink | Reply

      EASTER has 6 digits, (EGG)² has 5 or 6 digits, but whichever is larger BOX is the difference between them.

      The alphametic expression:

      abs(EASTER − (EGG)²) = BOX

      only has one solution (when we don’t use the digit 0):

      % python -m enigma SubstitutedExpression --digits="1-9" "abs(EASTER - sq(EGG)) = BOX"
      (abs(EASTER - sq(EGG)) = BOX)
      (abs(954891 - sq(977)) = 362) / A=5 B=3 E=9 G=7 O=6 R=1 S=4 T=8 X=2
      [1 solution]
      

      So this gives us our solution (BOX=362), even without needing to know which of EASTER or (EGG)² is larger. (In fact EASTER > (EGG)²).

      But here is a full solution, that solves the two possible sums as alphametic problems, and looks for situations where there is a single solution for BOX. It runs in 122ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, irange, printf)
      
      # look for solutions to the alphametic sum that give a unique value for ans
      def check(expr, answer):
        p = SubstitutedExpression(expr, digits=irange(1, 9), answer=answer, verbose=0)
        r = set(x for (_, x) in p.solve())
        if len(r) == 1:
          x = r.pop()
          printf("{answer}={x} [{expr}]")
      
      # there are two possible largest values
      check("sq(EGG) + BOX = EASTER", "BOX") # if EASTER is largest
      check("sq(EGG) - BOX = EASTER", "BOX") # if EGG^2 is largest
      

      Solution: BOX = 362.

      In fact this program runs faster than the single alphametic expression above, as each alphametic expression only requires considering the possibilities for 5 digits, rather than 6 with the single sum.

      If (non-leading) 0 digits are allowed, then there are three further solutions to the (EGG)² + BOX = EASTER sum (but still none for (EGG)² − BOX = EASTER).

      Like

    • GeoffR's avatar

      GeoffR 8:52 am on 30 July 2019 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:E;   var 1..9:A;   var 1..9:S;   var 1..9:T;
      var 1..9:R;   var 1..9:G;   var 1..9:B;   var 1..9:O;
      var 1..9:X;
      
      constraint all_different ([E, A, S, T, R, G, B, O, X]);
      
      var 100..999:EGG = 100*E + 11*G;
      var 100..999:BOX = 100*B + 10*O + X;
      var 100000..999999: EASTER = 100000*E + 10000*A + 1000*S + 100*T + 10*E + R;
      
      % Two of the numbers add to make the third
      constraint (EASTER - EGG*EGG == BOX) \/ (EGG*EGG - EASTER == BOX);
      
      solve satisfy;
      
      output [ "EASTER = " ++ show(EASTER) 
      ++ ", EGG = " ++ show(EGG)  ++ ", BOX = " ++ show(BOX) ];
      
      % EASTER = 954891, EGG = 977, BOX = 362
      % ----------
      % Finished in 232msec
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:38 am on 21 July 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 488: [Truth and lies] 

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

    The villagers of Titterton always tell the truth; those from Lilling always lie; those from Trulham alternate between truth and lie, starting with a truth; those from Litford also alternate, but start with a lie.

    I recently met four men, one from each village:

    Alf: “I’m from Trulham.”
    Bert: “Alf’s from Lilling.”
    Charlie: “Bert’s from Lilling.”
    Dan: “Charlie’s from Lilling.”
    Alf: “Dan’s from Titterton.”

    As I was sorting this out, in came three more men, each from one of other of the villages:

    Edgar: “I’m from Dan’s village.”
    Frank: “There are more chaps here from my village than from any other.”
    George: “Frank’s lying. I’m from Bert’s village.”

    Name the men from Titterton, Trulham and Litford. (I should perhaps add that I live in Totworth and tell the truth invariably).

    This puzzle was originally published with no title.

    [teaser488]

     
    • Jim Randell's avatar

      Jim Randell 8:40 am on 21 July 2019 Permalink | Reply

      I assumed the puzzle text itself was a completely true account, and that the “alternators” were at the beginning of their cycle with their first quoted statements.

      This Python program runs in 85ms.

      Run: [ @repl.it ]

      from enigma import (subsets, printf)
      
      # bT = always true (= Titterton)
      def bT(ss):
        return all(ss)
      
      # bF = always false (= Lilling)
      def bF(ss):
        return not any(ss)
      
      # bTF = alternate, T then F (= Trulham)
      def bTF(ss):
        return all(ss[0::2]) and not any(ss[1::2])
      
      # bFT = alternate, F then T (= Litford)
      def bFT(ss):
        return all(ss[1::2]) and not any(ss[0::2])
      
      # possible behaviours
      fns = (bT, bF, bTF, bFT)
      
      # solve the puzzle
      def solve():
        # choose behaviours for A, B, C, D
        for (A, B, C, D) in subsets(fns, size=len, select='P'):
      
          # A: "I am from Trulham", "D is from Titterton"
          if not A([A is bTF, D is bT]): continue
      
          # B: "A is from Lilling"
          if not B([A is bF]): continue
      
          # C: "B is from Lilling"
          if not C([B is bF]): continue
      
          # D: "C is from Lilling"
          if not D([C is bF]): continue
      
          # choose behaviours for E, F, G
          for (E, F, G) in subsets(fns, size=3, select='M'):
      
            # E: "I am from D's village"
            if not E([E == D]): continue
      
            # F: "There are more from my village than any other"
            s = (A, B, C, D, E, F, G)
            n = dict((k, s.count(k)) for k in s)
            f = all(n[F] > n[x] for x in s if x != F)
            if not F([f]): continue
      
            # G: "F is lying" "I am from B's village"
            if not G([(not f), G == B]): continue
      
            yield s
      
      for s in solve():
        (A, B, C, D, E, F, G) = (b.__name__ for b in s)
        printf("A={A} B={B} C={C} D={D} E={E} F={F} G={G}")
      

      Solution: The affiliations are:

      Titterton: Charlie
      Lilling: Bert, Edgar
      Trulham: Alf, George
      Litford: Dan, Frank

      Like

  • Unknown's avatar

    Jim Randell 6:21 pm on 19 July 2019 Permalink | Reply
    Tags:   

    Teaser 2965: Knock, knock! 

    From The Sunday Times, 21st July 2019 [link] [link]

    Last year a two-figure number of teams entered our football competition. With that number, it was impossible to have a straightforward knockout competition (where half the teams are knocked out in each round), so we divided the teams into equal-sized groups, each pair of teams within a group played each other once, and the overall winner from each group went forward to the second stage consisting of a knockout competition. Unfortunately our local team was knocked out in the quarter-finals of that stage.

    This year a higher number of teams entered (but not twice as many). Again a straightforward knockout competition was impossible so we copied last year’s model but with smaller groups and more of them. By coincidence the total number of games played this year equalled the number played last year.

    How many teams entered last year and how many this year?

    [teaser2965]

     
    • Jim Randell's avatar

      Jim Randell 7:01 pm on 19 July 2019 Permalink | Reply

      In order to have a knockout tournament (without using byes) the number of teams needs to be a power of 2 (half the teams are knocked out at each stage).

      So the numbers of teams we are dealing with cannot be powers of two. But we can divide the numbers into groups where the winners of the groups can have a knockout tournament, so the number of groups must be a power of 2. Hence the total number of teams has to have a divisor that is a power of 2.

      Within a group of size m, the number of matches where each team plays each other team is T(m − 1).

      This Python program looks for 2-digit values for the number of teams last year, works out the total number of matches played in the tournament, and then finds a scenario for this year which has the same total number of matches. It runs in 91ms.

      Run: [ @replit ]

      from enigma import (irange, div, tri, bit_permutations as powers2, printf)
      
      # check a positive integer is a power of 2
      is_power2 = lambda n: not (n & (n - 1))
      
      # number of teams last year (a 2-digit number)
      for n in irange(24, 99, step=8):
        # skip numbers that are powers of 2
        if is_power2(n): continue
      
        # divide n into k groups of m (k is a power of 2)
        # a team was knocked out in the quarter finals, so k > 7
        for k in powers2(8):
          m = div(n, k)
          if m is None: break
          # calculate the total number of games played
          t = k * tri(m - 1) + (k - 1)
      
          # now look for possible values with the same total, but with smaller groups
          for m2 in irange(3, m - 1):
            # and m2 must not be a power of 2
            if is_power2(m2): continue
            # calculate the number of groups
            k2 = div(t + 1, tri(m2 - 1) + 1)
            if k2 is None: continue
            # there must be more groups
            if not (k2 > k): continue
            # k2 must be a power of 2
            if not is_power2(k2): continue
            # the total number of teams this year
            n2 = k2 * m2
            # and n2 greater than n, but not twice n
            if not (n2 > n and n2 != n * 2): continue
      
            printf("t={t}: last year = {n} teams ({k} groups of {m}), this year = {n2} teams ({k2} groups of {m2})")
      

      Solution: 56 teams entered last year. 80 teams entered this year.

      Last year the 56 teams were formed into 8 groups of 7. Each group would play 21 matches, and then the 8 winners of the groups would play 4 quarter-finals, 2 semi-finals and 1 final to give 175 matches in total.

      This year the 80 teams were formed into 16 groups of 5. Each group would play 10 matches, and then the 16 winners of the groups would play 8 eighth-finals, 4 quarter-finals, 2 semi-finals and 1 final to also give 175 matches in total.


      As a team was knocked out in the quarter-finals last year, the number of groups must have been at least 8, and we can consider multiples of 8 (that are not exact powers of 2). So there are only 8 candidate numbers for the number of teams last year: 24, 40, 48, 56, 72, 80, 88, 96.

      There are two “near miss” scenarios:

      48 teams last year (8 groups of 6), and 96 teams this year (32 groups of 3). Both have 127 matches.
      96 teams last year (16 groups of 6), and 192 teams this year (64 groups of 3). Both have 255 matches.

      But both are eliminated by the requirement that the number of teams this year is not twice the number of teams last year.

      When I initially read the puzzle I took “not twice as many” to mean “fewer than twice as many”, but in my program I relaxed this condition to “not exactly twice as many”, and the single solution remains.

      There is also the question of whether, in the knockout tournament, there is a match to decide 3rd and 4th places. It doesn’t matter if there is or not, as long as both tournaments use the same format. If there is a match for the semi-final losers then the total numbers of matches will be increased by 1.

      Like

    • Jim Randell's avatar

      Jim Randell 12:52 pm on 22 July 2019 Permalink | Reply

      Here is a MiniZinc model for the puzzle. It executes in 66ms.

      %#! minizinc -a
      
      % powers of 2
      set of int: pow2 = { pow(2, i) | i in 1..9 };
      
      
      % last year: n teams, k groups of m
      var 10..99: n; % n is a 2 digit number
      var 3..33: k;
      var 5..99: m;
      
      % n can be divided into k groups of m;
      % n is not a power of 2
      % k is a power of 2
      % m is not a power of 2
      constraint k * m = n;
      constraint not(n in pow2);
      constraint k in pow2;
      constraint not(m in pow2);
      
      % k is at least 8
      constraint not(k < 8);
      
      
      % this year: n1 teams, k1 groups of m1
      var 11..1000: n1;
      var 4..334: k1;
      var 3..98: m1;
      
      constraint n1 = k1 * m1;
      constraint not(n1 in pow2);
      constraint k1 in pow2;
      constraint not(m1 in pow2);
      
      % more teams, but not twice as many
      constraint n1 > n;
      constraint n1 != 2 * n;
      
      % smaller groups, but more of them
      constraint m1 < m;
      constraint k1 > k;
      
      % total number of matches is the same
      constraint k * (m * (m - 1) + 2) = k1 * (m1 * (m1 - 1) + 2);
      
      solve satisfy;
      

      I wasn’t sure how use the neat trick to test for powers of 2 in MiniZinc, so I assumed an upper bound of 1,000 teams, and just made a set of powers of 2 less than this.

      Like

    • Lise Andreasen's avatar

      Lise Andreasen 2:50 pm on 6 June 2024 Permalink | Reply

      I think it’s possible to figure this out on paper, but there are a lot of restrictions to deal with.
      9 < x < 100
      x < y < 2x
      x = m * 2^k
      y = n * 2^l
      (that’s an L)
      l > k
      2 < n < m
      2^k or 2^l groups
      m or n members of each group
      Neither m nor n can be a power of 2
      There are this number of matches the first year:
      2^k * m(m-1)/2 + 2^k – 1
      And the second year:
      2^l * n(n-1)/2 + 2^l – 1
      These 2 numbers are the same
      A little calculation gives:
      m(m-1) + 2 = 2^(l-k) * [n(n-1) + 2]
      (I hope I got that one right!)
      Now it's possible to systematically go through all the possibilities.
      Like, k = 3, l = 4, n = 3, is m integer? And so on.
      Neat puzzle!

      Like

    • Frits's avatar

      Frits 7:38 pm on 6 June 2024 Permalink | Reply

      Comparing triangular numbers.

      tri = lambda n: (n * (n + 1)) // 2
      
      # powers of 2 (upper bound is not important as breaks are used)
      ps2 = [1 << i for i in range(1, 10)]
      
      # maximum number of teams last year is 96 (a multiple of 8)
      # t = number of teams per group
      # ngroups * t < 192 with ngroups >= 8 (quarter finals) so nteams < 24
      
      # nmatches = ngroups * (tri(t - 1) + 1) - 1  
      d = {tri(t - 1) + 1: t for t in range(2, 24) if t & (t - 1)} 
      mx = max(d)
      
      # last: matches = g * d[k2] - 1
      # this: matches = q * g * d[k1] - 1  where q = k2 / k1 and q is a power of 2
      for k1 in d.keys():
        for q in ps2:
          # k2 must be a multiple of k1
          if (k2 := q * k1) > mx: break
          if k2 not in d: continue
        
          # try different number of groups
          for g in ps2:
            if g < 8: continue
            if (nlast := g * d[k2]) > 99: break
            if (nthis := q * g * d[k1]) >= 2 * nlast: break
           
            print("answer:", nlast, "and", nthis)     
      

      Like

  • Unknown's avatar

    Jim Randell 10:05 am on 19 July 2019 Permalink | Reply
    Tags:   

    Teaser 2898: My valentine 

    From The Sunday Times, 8th April 2018 [link] [link]

    Using only positive digits, MILLY wrote down a two-digit number and a nine-digit number, both numbers being divisible by 14.

    Splitting her nine-digit number into three three-digit numbers, she noticed that all three numbers were also divisible by 14.

    Systematically replacing different digits by different letters, her two-digit number and nine-digit number ended up as MY VALENTINE.

    What number is represented by MILLY?

    [teaser2898]

     
    • Jim Randell's avatar

      Jim Randell 10:06 am on 19 July 2019 Permalink | Reply

      This puzzle can be solved directly using the [[ SubstitutedExpression() ]] solver from the enigma.py library.

      The following run file executes in 131ms.

      Run: [ @repl.it ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      --answer="MILLY"
      
      # the 2 digit number is MY
      "MY % 14 = 0"
      
      # the 9 digit number is VALENTINE
      "VALENTINE % 14 = 0"
      "VAL % 14 = 0"
      "ENT % 14 = 0"
      "INE % 14 = 0"
      

      Solution: MILLY = 17224.

      MY = 14
      VALENTINE = 392658756 or 532896798

      Like

    • GeoffR's avatar

      GeoffR 10:46 am on 20 July 2019 Permalink | Reply

      Gave up using Geocode Solver after three minutes but Chuffed Solver produced a solution in a reasonable time.
      @Jim: Any idea why solvers run-times can vary so much?

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9: M; var 1..9: Y; var 1..9: V;
      var 1..9: A; var 1..9: L; var 1..9: E;
      var 1..9: N; var 1..9: T; var 1..9: I;
      
      constraint all_different ([M,Y,V,A,L,E,N,T,I]);
      
      var 100..999: VAL = 100*V + 10*A + L;
      var 100..999: ENT = 100*E + 10*N + T;
      var 100..999: INE = 100*I + 10*N + E;
      
      var 10..99: MY = 10*M + Y;
      var 10000..99999: MILLY = 10000*M + 1000*I + 110*L + Y;
      
      var 100000000..999999999: VALENTINE = V*pow(10,8) + A*pow(10,7)
      + L*pow(10,6) + E*pow(10,5) + N*pow(10,4) + T*pow(10,3)
      + I*100 + N*10 + E;
      
      constraint MY mod 14 == 0 /\ VALENTINE mod 14 == 0;
      constraint VAL mod 14 == 0 /\ ENT mod 14 == 0 /\ INE mod 14 == 0;
      
      solve satisfy;
      
      output [ "MILLY = " ++ show(MILLY) ]
      ++ ["\nMY = " ++ show(MY)]
      ++ ["\nVALENTINE = " ++ show(VALENTINE)  ];
      
      % MILLY = 17224
      % MY = 14
      % VALENTINE = 392658756
      % % time elapsed: 0.02 s
      % ----------
      % MILLY = 17224
      % MY = 14
      % VALENTINE = 532896798
      % % time elapsed: 0.02 s
      % ----------
      % ==========
      % Finished in 387msec
      

      Like

      • Jim Randell's avatar

        Jim Randell 11:10 am on 23 July 2019 Permalink | Reply

        @GeoffR: Using integer literals for the powers of 10 instead of [[ pow() ]] seems to make the model acceptable to the [[ gecode ]] solver.

        Like

    • GeoffR's avatar

      GeoffR 12:20 pm on 23 July 2019 Permalink | Reply

      @Jim: Thanks for the reply, yes that suggestion now works OK for the Geoode solver. Perhaps the Chuffed solver works better for powers as it looks as though it is based on C++

      Like

      • Jim Randell's avatar

        Jim Randell 1:16 pm on 23 July 2019 Permalink | Reply

        I suspect that one of the solvers notices that they are constant terms and calculates them once, but the other one doesn’t and keeps re-evaluating them.

        Like

    • Frits's avatar

      Frits 11:15 am on 18 September 2020 Permalink | Reply

       
      # concatenate list of integers
      to_num = lambda *args: int("".join(map(str, args)))
      
      # make sure loop variable value is not equal to previous ones
      def domain(v):
        # find already used loop values  ...
        vals = set()
        # ... by accessing previously set loop variable names
        for s in lvd[v]:
          vals.add(globals()[s])
        
        return [x for x in range(1,10) if x not in vals]
      
      # set up dictionary of for-loop variables
      lv = ['M', 'Y', 'E', 'N', 'T', 'I', 'V', 'A', 'L']
      lvd = {v: lv[:i] for i, v in enumerate(lv)}
      
      sol = set()
      
      # Solve puzzle by testing all posssible values
      for M in range(1,10):
        for Y in domain('Y'):
          if (to_num(M, Y) % 14 != 0): continue
          for E in domain('E'):
            for N in domain('N'):
              for T in domain('T'):
                if (to_num(E, N, T) % 14 != 0): continue
                for I in domain('I'):
                  if (to_num(I, N, E) % 14 != 0): continue
                  for V in domain('V'):
                    for A in domain('A'):
                      for L in domain('L'):
                        if (to_num(V, A, L) % 14 != 0): continue
                        sol.add(to_num(M, I, L, L, Y))
                          
      for s in sol:
        print(s)  
        
      # Output:
      #
      # 17224
      

      Like

      • Frits's avatar

        Frits 11:35 am on 18 September 2020 Permalink | Reply

        As VAL000000 is divisible by 14, ENT000 is divisible by 14 and INE is divisible by 14 we don’t need to verify that VALENTINE is divisible by 14.

        Like

    • Frits's avatar

      Frits 2:31 pm on 30 November 2021 Permalink | Reply

      Using choose().

        
      from enigma import choose
      
      # concatenate list of integers
      to_num = lambda *args: int("".join(map(str, args)))
      
      S = list(choose(range(1, 10), 
          [None, 
          (lambda M, Y: Y not in {M} and to_num(M, Y) % 14 == 0), 
          (lambda M, Y, E: E not in {M, Y}),
          (lambda M, Y, E, N: N not in {M, Y, E}),
          (lambda M, Y, E, N, T: T not in {M, Y, E, N} and 
                  to_num(E, N, T) % 14 == 0),
          (lambda M, Y, E, N, T, I: I not in {M, Y, E, N, T} and 
                  to_num(I, N, E) % 14 == 0),
          (lambda M, Y, E, N, T, I, V: V not in {M, Y, E, N, T, I}),
          (lambda M, Y, E, N, T, I, V, A: A not in {M, Y, E, N, T, I, V}),
          (lambda M, Y, E, N, T, I, V, A, L: L not in {M, Y, E, N, T, I, V, A} and 
                  to_num(V, A, L) % 14 == 0 and 
                  to_num(V, A, L, E, N, T, I, N, E) % 14 == 0),
          ]))
      
      print("MILLY:", *set("".join(str(x) for x in [s[0], s[5], s[8], s[8], s[1]]) 
                 for s in S))
      

      Like

    • Frits's avatar

      Frits 4:28 pm on 30 November 2021 Permalink | Reply

      With list comprehensions.

        
      # even digits are reserved for Y, T, E and L
      digits = set("123456789")
      odds = set("13579")
      evens = digits.difference(odds)
      
      # even digits are reserved for Y, T, E and L
      # so M must be odd
      MY = set(s for x in range(10, 100) if x % 14 == 0 and 
           len(set((s := str(x)))) == 2 and 
           s[0] in odds and '0' not in s)
      
      # list of 3 digit numbers divisible by 14    
      # middle digit (A or N) must be odd
      dgts3 = [s for x in range(100, 1000) if x % 14 == 0 and 
               len(set((s := str(x)))) == 3 and 
               s[1] in odds and '0' not in s]
      
      ooe = [x for x in dgts3 if x[0] in odds] # odd, odd, even
      
      # ENT and INE are both divisible by 14
      # N and I must be odd
      ENT_INE = [(x, y) for x in dgts3 for y in ooe 
                 if y[2] + y[1] == x[:2] and y[0] != x[2]]
      
      ENT_INE_VAL = [(x, y, z) for x, y in ENT_INE for z in ooe 
                     if "".join(sorted(digits.difference(x + y + z))) in MY]
                    
      sol = set()             
      for ENT, INE, VAL in ENT_INE_VAL:
        t = ENT + INE + VAL
        M = odds.difference([x for x in t if x in odds])
        Y = evens.difference([x for x in t if x in evens])
        sol.add(M.pop() + t[3] + t[8] + t[8] + Y.pop())
      
      for s in sol:  
        print("MILLY:", s)
      

      Like

    • GeoffR's avatar

      GeoffR 11:39 am on 1 December 2021 Permalink | Reply

      from itertools import permutations
      
      for p1 in permutations('123456789', 2):
        M, Y = p1
        MY = int(M + Y)
        if MY % 14 != 0: continue
        q1 = set('123456789').difference([M, Y])
      
        # 1st part(VAL) of VALENTINE
        for p2 in permutations(q1, 3):
          V, A, L = p2
          VAL = int(V + A + L)
          if VAL % 14 != 0:continue
          q2 = q1.difference([V, A, L])
      
          # 2nd part(ENT) of VALENTINE
          for p3 in permutations(q2, 4):
            E, N, T, I = p3
            ENT = int(E + N + T)
            if ENT % 14 != 0:continue
      
            # 3rd part(INE) of VALENTINE
            INE = int(I + N + E)
            if INE % 14 != 0: continue
            VALENTINE = int(V + A + L + E + N + T + I + N + E)
            # Stated condition, but not necessary
            #if VALENTINE % 14 != 0:continue
            MILLY = int(M + I + L + L + Y)
            print(f"MY={MY}, VALENTINE={VALENTINE}, MILLY={MILLY}") 
      
      # MY=14, VALENTINE=392658756, MILLY=17224
      # MY=14, VALENTINE=532896798, MILLY=17224
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 3:13 pm on 18 July 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 487: [Seating arrangements] 

    From The Sunday Times, 27th September 1970 [link]

    Five friends, Archer, Brown, Carter, Dale and Eade, and their wives, Anne, Betty, Clara, Diana and Edna, meet on the first Wednesday of every month to dine together (at a round table) in a local hotel. They have a rule that no wife may sit next to, or immediately opposite, her husband. Archer, as M.C., sits always with his back to the fireplace.

    We are concerned with the seating arrangements in January, April, July and October. On these four occasions no man, with the exception of the M.C., sat at any place more than once. In January the men sat in clockwise alphabetical order (men and women always sit alternately). Also, on these four evenings, the ladies sitting opposite Archer were successively: Betty, Clara, Diana and Edna.

    In April, Diana sat at the same place she occupied in January, and in July, Anne sat at the same place that she occupied in April.

    What was the seating arrangement in October? (Clockwise, A to A, using capital initials for the men and small initials for the women).

    This puzzle was originally published with no title.

    [teaser487]

     
    • Jim Randell's avatar

      Jim Randell 3:14 pm on 18 July 2019 Permalink | Reply

      This Python program runs in 88ms.

      Run: [ @repl.it ]

      from enigma import subsets, update as _update, join, printf
      
      # possible names
      names = "ABCDE"
      
      # update a string with new items
      def update(s, js, vs):
        return join(_update(list(s), js, vs))
      
      # generate possible seating plans
      def generate(ss):
        # find unseated people, and empty places
        (xs, js) = (set(names), list())
        for (j, x) in enumerate(ss):
          if x == '?':
            js.append(j)
          else:
            xs.remove(x)
      
        # complete the seating plan
        for s in subsets(xs, size=len, select='P'):
          yield update(ss, js, s)
      
      # groups of adjacent and opposite seats: w -> m
      group = {
        0: [0, 1, 3],
        1: [1, 2, 4],
        2: [2, 3, 0],
        3: [3, 4, 1],
        4: [4, 0, 2],
      }
      
      # check no men reuse seats, apart from seat 0
      def m_check(ms, ss):
        for (i, x) in enumerate(ms):
          if i == 0: continue
          if any(s[i] == x for (s, _) in ss):
            return False
        return True
      
      # check no woman is seated opposite or next to her husband
      def w_check(ms, ws):
        for (i, w) in enumerate(ws):
          if any(ms[j] == w for j in group[i]):
            return False
        return True
      
      # find seating plans, given previous plans
      def solve(ms, ws, ss=[]):
      
        # complete the seating plan for the men
        for ms1 in generate(ms):
          if not m_check(ms1, ss): continue
      
          # complete the seating plan for the women
          for ws1 in generate(ws):
            if not w_check(ms1, ws1): continue
            yield (ms1, ws1)
      
      # find the January seating plan
      for Jan in solve('ABCDE', '??B??'):
      
        # find the April seating plan
        # Mrs D sits in the same place as in Jan
        ws = update('??C??', [Jan[1].index('D')], ['D'])
        for Apr in solve('A????', ws, [Jan]):
      
          # find the July seating plan
          # Mrs A sits in the same place as in April
          ws = update('??D??', [Apr[1].index('A')], ['A'])
          for Jul in solve('A????', ws, [Jan, Apr]):
      
            # find the October seating plan
            for Oct in solve('A????', '??E??', [Jan, Apr, Jul]):
      
              # output the seating plans
              printf("Jan={Jan[0]}+{Jan[1]} Apr={Apr[0]}+{Apr[1]} Jul={Jul[0]}+{Jul[1]} Oct={Oct[0]}+{Oct[1]}")
      

      Solution: The seating arrangement in October was:

      Mr A; Mrs B; Mr E; Mrs A; Mr D; Mrs E; Mr C; Mrs D; Mr B; Mrs C

      The other seating plans are:

      Jan = Mr A; Mrs E; Mr B; Mrs A; Mr C; Mrs B; Mr D; Mrs C; Mr E; Mrs D
      Apr = Mr A; Mrs B/E; Mr D; Mrs E/B; Mr B/E; Mrs C; Mr E/B; Mrs A; Mr C; Mrs D
      Jul = Mr A; Mrs E/B; Mr C; Mrs B/E; Mr E/B; Mrs D; Mr B/E; Mrs A; Mr D; Mrs C
      Oct = Mr A; Mrs B; Mr E; Mrs A; Mr D; Mrs E; Mr C; Mrs D; Mr B; Mrs C

      In places where there are options either the first or second option is used consistently throughout.

      Like

  • Unknown's avatar

    Jim Randell 8:35 am on 17 July 2019 Permalink | Reply
    Tags:   

    Teaser 2899: Base jumping 

    From The Sunday Times, 15th April 2018 [link] [link]

    In written multiplication tests Jay always calculated correct answers, but, quirkily, wrote the digits of any two-figure answers in reverse order. A completed test sheet (pre-printed with ordered triplets of single-figure positive integers to multiply, e.g., 1×2×2=? and 3×5×9=?) included a question and his two-figure written answer that would be right when thought of in a number base other than “base 10” (any numerals keeping their usual values). Jay told Kay about this, but didn’t give the question or answer. Kay asked whether one of that question’s numbers to multiply was a particular value and from the reply knew both the question and the answer Jay had written.

    What was Jay’s written answer to that question?

    [teaser2899]

     
    • Jim Randell's avatar

      Jim Randell 8:35 am on 17 July 2019 Permalink | Reply

      This Python program collects the possible multiplications where a reversed answer would be correct in a non-decimal base. It then uses the [[ filter_unique() ]] function (from the enigma.py library) to find situations where the answer for a specific digit indicate a unique multiplication question.

      This Python program runs in 60ms. (Internal runtime is 673µs).

      Run: [ @replit ]

      from enigma import (subsets, irange, multiply, div, unpack, filter_unique, printf)
      
      # collect possible questions
      ss = list()
      
      # consider triples composed of single digits (i, j, k)
      for t in subsets(irange(1, 9), size=3, select='R'):
        n = multiply(t)
        if not (9 < n < 100): continue
      
        # n = 10x + y = by + x
        (x, y) = divmod(n, 10)
        if x == y or y == 0: continue
        b = div(n - x, y)
        if b is None: continue
        if not (x < b and y < b and b != 10): continue
      
        # record tuples (<triple>, <product>, <tens digit>, <units digit>, <base>)
        printf("[{n} (base 10) = {y}{x} (base {b}), {n} -> {t}]")
        ss.append((t, n, x, y, b))
      
      # K asks if one of the numbers in the triple is a particular value k
      for k in irange(1, 9):
        # K asks if the digit k is (at least) one of the digits in the question
        # and from the answer can determine both the question and J's answer
        f = unpack(lambda t, n, x, y, b: k in t)
        g = unpack(lambda t, n, x, y, b: (t, n))
        rs = filter_unique(ss, f, g).unique
        # output unique solutions
        for r in rs:
          (t, n, x, y, b) = r
          printf("[k={k} -> {z}] question: {t[0]}x{t[1]}x{t[2]} (= {n}), jay's answer: {y}{x} (in base {b} = {n})", z=f(r))
      

      Solution: Jay’s answer was 48.

      It is odd that the setter asks for Jay’s written answer, which can refer to multiple possible written questions, rather than the three digits of the written question, which are unique.

      There are 9 possible multiplications where a reversed answer would be correct in a non-decimal base:

      1×3×7 = 21 [base 10] = 12 [base 19]
      1×6×7 = 2×3×7 = 42 [base 10] = 24 [base 19]
      1×7×9 = 3×3×7 = 63 [base 10] = 36 [base 19]
      2×6×7 = 3×4×7 = 84 [base 10] = 48 [base 19]
      1×9×9 = 3×3×9 = 81 [base 10] = 18 [base 73]

      Kay asks if the question includes the digit 4, and receives the answer “Yes”, this narrows the question down to a single possibility:

      3×4×7 = 84 [base 10] = 48 [base 19]

      This is the only digit/answer combination which gives a unique answer for the question.

      However, if Kay had asked about the digit 7, and received a “No” answer, there would have been two possible questions, but he would still be able to work out Jay’s answer:

      1×9×9 = 3×3×9 = 81 [base 10] = 18 [base 73]

      Like

    • GeoffR's avatar

      GeoffR 8:18 pm on 23 July 2019 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % triplets of single value positive integers
      var 1..9: i; var 1..9: j; var 1..9: k;
      
      % the correct two digit result in base 10
      var 9..99: nbr;    
      
      % tens and units digits of the result                    
      var 1..9: t; var 1..9: u;  
      
      % the number base for reversed digits
      var 11..99: b;              
      
      % order triplet digits
      constraint i <= j /\ j <= k;
      
      constraint nbr = i * j * k;
      
      % find tens and units digits of 2-digit number
      constraint t = nbr div 10 /\ u == nbr mod 10;
      
      % number is correct in base b if digits are reversed
      constraint nbr == 10*t + u /\ nbr == b*u + t;
      
      solve satisfy;
      
      output [show(i) ++ " * " ++ show(j) ++ " * " ++ show(k) ++ " == " 
      ++show(nbr) ++ " (base 10) == " ++ show(u) ++ show(t) ++ 
      " (base " ++ show(b) ++ ")"]
                  
      % 1 * 3 * 7 == 21 (base 10) == 12 (base 19)
      % 3 * 3 * 9 == 81 (base 10) == 18 (base 73)
      % 1 * 9 * 9 == 81 (base 10) == 18 (base 73)
      % 1 * 6 * 7 == 42 (base 10) == 24 (base 19)
      % 2 * 3 * 7 == 42 (base 10) == 24 (base 19)
      % 3 * 3 * 7 == 63 (base 10) == 36 (base 19)
      % 1 * 7 * 9 == 63 (base 10) == 36 (base 19)
      
      % Jay's answer - digit 4 gives a unique result  
      % 3 * 4 * 7 == 84 (base 10) == 48 (base 19)  <<<
      
      % 2 * 6 * 7 == 84 (base 10) == 48 (base 19)
      % ----------
      % ==========         
      

      Like

  • Unknown's avatar

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

    Brainteaser 1629: Tangle o’ the aisles 

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

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

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

    For each girl find out:

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

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

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

    [teaser1629]

     
    • Jim Randell's avatar

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

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

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

      This Python program runs in 87ms.

      Run: [ @replit ]

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

      Solution: The answers are:

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

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

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

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

      Like

    • Lise Andreasen's avatar

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

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

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

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

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

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

      Like

  • Unknown's avatar

    Jim Randell 9:42 am on 15 July 2019 Permalink | Reply
    Tags:   

    Teaser 2796: George and the Dragon 

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

    Yesterday was St George’s day and to celebrate George getting even with the dragon I wrote down an addition sum. Then I replaced digits consistently by letters, using different letters for different digits, to give:

    SAINT + GEORGE = DRAGON

    Given that GEORGE is even, what number is DRAGON?

    [teaser2796]

     
    • Jim Randell's avatar

      Jim Randell 9:43 am on 15 July 2019 Permalink | Reply

      This puzzle can be solved using the [[ SubstitutedSum() ]] solver from the enigma.py library.

      The following run file executes in 153ms.

      Run: [ @replit ]

      #! python -m enigma -rr
      
      SubstitutedSum
      
      # GEORGE is even, so E cannot be an odd digit
      --invalid="1|3|5|7|9,E"
      
      # the alphametic sum
      "SAINT + GEORGE = DRAGON"
      

      Solution: DRAGON = 932801.

      The full sum is: 72415 + 860386 = 932801. In this sum E = 6.

      There are two further solutions to the alphametic sum where E is odd:

      64123 + 790579 = 854702 → E = 9
      92138 + 650465 = 742603 → E = 5

      Like

    • GeoffR's avatar

      GeoffR 1:02 pm on 15 July 2019 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 0..9:S; var 0..9:A; var 0..9:I; var 0..9:N; var 0..9:T;
      var 0..9:G; var 0..9:E; var 0..9:O; var 0..9:R; var 0..9:D;
      
      constraint all_different ([S, A, I, N, T, G, E, O, R, D]);
      constraint S > 0 /\ G > 0 /\ D > 0;
      
      var 10000..99999: SAINT = 10000*S + 1000*A + 100*I + 10*N + T;
      var 100000..999999: GEORGE = 100010*G + 10001*E + 1000*O + 100*R;
      var 100000..999999: DRAGON = 100000*D + 10000*R + 1000*A 
      + 100*G + 10*O + N;
      
      constraint GEORGE mod 2 == 0;
      constraint SAINT + GEORGE == DRAGON;
      
      solve satisfy;
      
      output ["Equation is " ++ show(SAINT) ++ " + " ++ show(GEORGE)
      ++ " = " ++ show(DRAGON) ];
      
      % Equation is 72415 + 860386 = 932801
      % ----------
      % ==========
      

      Like

  • Unknown's avatar

    Jim Randell 9:00 am on 14 July 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 486: [Counting cards] 

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

    So as to encourage my four children to practise their arithmetic I have invented a game played with a standard pack of playing-cards. For scoring purposes an Ace counts as 1; Jack, Queen and King as 11, 12 and 13 respectively; and each other card by its numerical value.

    To make matters a little complicated, I decided that Hearts should count at face value, Clubs at double value, Diamonds at triple value, and Spades at quadruple value. Before each deal, one card is extract from the pack, and a Joker, valued at 50 points, is substituted.

    The cards are then dealt in the normal manner, and each child then counts up the points value of his or her hand. There is a small reward for the child who most rapidly correctly counts up his points, but the winner is the child with the highest total.

    At the conclusion of one such game, each of the totals was an exact multiple of 7, and the winner, Bert, had scored 42 more than Amy, who had beaten Don by 35. Poor Clara came last with 14 points fewer than Don.

    Which card had been replaced by the Joker?

    This puzzle was originally published with no title.

    [teaser486]

     
    • Jim Randell's avatar

      Jim Randell 9:01 am on 14 July 2019 Permalink | Reply

      The scores in order from lowest to highest are:

      C
      D = C + 14
      A = C + 49
      B = C + 91

      So the total number of points is:

      A + B + C + D = 4C + 154

      and C must be divisible by 7.

      This program creates a set of cards, and then looks at replacing one of the cards with a Joker (value 50), to find possible values for C (and hence A, B, D).

      It runs in 79ms.

      Run: [ @repl.it ]

      from enigma import irange, update, div, printf
      
      # multipliers
      mul = dict(H=1, C=2, D=3, S=4)
      
      # map each card to the corresponding score
      score = dict()
      for v in irange(1, 13):
        score.update(((v, k), m * v) for (k, m) in mul.items())
      
      # one card is replaced with a 50
      for k in score.keys():
        s = update(score, [(k, 50)])
        # total number of points
        t = sum(s.values())
      
        # score for C (= 7x)
        x = div(t - 154, 28)
        if x is None: continue
      
        # calculate the scores
        C = 7 * x
        (B, A, D) = (C + 91, C + 49, C + 14)
        printf("k={k} t={t}, A={A} B={B} C={C} D={D}")
      

      Solution: The card replaced by the Joker is the Jack of Clubs.

      This is the only card which gives a viable value for C.

      The scores are: B = 287; A = 245; D = 210; C = 196 (being 41×; 35×; 30×; 28× 7).

      We can get the program to additionally look for a way of dealing the cards that achieves the required scores. Fortunately there are many ways of dealing the cards. Here is one of them:

      A = 5♠ + 6♥ + 6♣ + 6♦ + 7♥ + 7♣ + 7♦ + 7♠ + 8♥ + 8♣ + 8♠ + X♦ + J♦
        = 20 +  6 + 12 + 18 +  7 + 14 + 21 + 28 +  8 + 16 + 32 + 30 + 33 = 245
      
      B = 8♦ + 9♥ + 9♣ + X♥ + X♣ + X♠ + J♥ + J♠ + Q♥ + Q♣ + Q♦ + K♥ + K♣
        = 24 +  9 + 18 + 10 + 20 + 40 + 11 + 44 + 12 + 24 + 36 + 13 + 26 = 287
      
      C = A♥ + A♣ + A♦ + A♠ + 2♥ + 2♣ + 2♦ + 2♠ + 3♥ + 6♠ + Q♠ + K♦ + K♠
        =  1 +  2 +  3 +  4 +  2 +  4 +  6 +  8 +  3 + 24 + 48 + 39 + 52 = 196
      
      D = 3♣ + 3♦ + 3♠ + 4♥ + 4♣ + 4♦ + 4♠ + 5♥ + 5♣ + 5♦ + 9♦ + 9♠ + JK
           6 +  9 + 12 +  4 +  8 + 12 + 16 +  5 + 10 + 15 + 27 + 36 + 50 = 210

      Like

  • Unknown's avatar

    Jim Randell 6:18 pm on 12 July 2019 Permalink | Reply
    Tags:   

    Teaser 2964: “Bingo a go-go” lingo a no-go 

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

    My rest home’s Bingo set uses numbers 1 to 99. To win, nine numbers on your game card must be called. Our caller, not knowing “bingo-lingo”, says “Number 1, total factors 1”, “Number 11, total factors 2” and “Number 30, total factors 8”, etc.

    Yesterday, in one game, my hearing aid howled whenever a call started. I missed each number, but heard each “total factors” value. Fortunately, after just nine calls I shouted “HOUSE!” certain that I’d won.

    I told my daughter how many different “factor” values I’d heard, but didn’t say what any of the values were. Knowing that I had won after nine calls, she could then be sure about some (fewer than nine) of my winning numbers.

    Find, in ascending order, the numbers that she could be sure about.

    [teaser2964]

     
    • Jim Randell's avatar

      Jim Randell 6:43 pm on 12 July 2019 Permalink | Reply

      (See also: Enigma 1004 for another puzzle that involves counting divisors).

      This Python program groups the numbers from 1 to 99 into collections that have the same number of divisors, and then looks for groups of those collections that give a set of exactly 9 numbers, and these groups are recorded by the size of the group.

      We can then find groups of the same size that have a non-empty set of fewer than 9 numbers in common, and this gives our solution.

      It runs in 78ms.

      Run: [ @repl.it ]

      from enigma import (defaultdict, irange, tau, subsets, flatten, intersect, printf)
      
      # consider the numbers, and group them by number of divisors
      g = defaultdict(list)
      for n in irange(1, 99):
        t = tau(n)
        g[t].append(n)
      
      # [optional] we can discard groups with > 9 elements
      g = dict((k, v) for (k, v) in g.items() if not (len(v) > 9))
      
      # collect possible keys that give a set of 9 numbers
      # grouped by the total number of keys
      rs = defaultdict(list)
      for ks in subsets(g.keys()):
        ns = flatten(g[k] for k in ks)
        if not (len(ns) == 9): continue
        
        n = len(ks)
        printf("[{n}: {ks} -> {ns}]")
        rs[n].append(ns)
      
      # find groups with more than 0, but less than 9 elements in common
      for (n, ns) in rs.items():
        s = intersect(ns)
        if not (0 < len(s) < 9): continue
      
        # output solution
        printf("{s} [n={n}]", s=sorted(s))
      

      Solution: The 7 numbers she could be sure about are: 1, 4, 9, 25, 36, 49, 64.

      The daughter was told that 5 different “factor” values were heard, so she knows the divisors are either:

      (1, 3, 5, 7, 9), which identify the numbers (1, 4, 9, 16, 25, 36, 49, 64, 81); or:
      (1, 3, 7, 9, 10), which identify the numbers (1, 4, 9, 25, 36, 48, 49, 64, 80)

      So the solution is given by the subset of the numbers that have 1, 3, 7, or 9 divisors.

      1 is the only number with 1 divisor.
      4, 9, 25, 49 (the squares of primes) have 3 divisors.
      64 (a prime to the power of 6) has 7 divisors
      36 (the square of the product of two different primes) has 9 divisors.

      Like

  • Unknown's avatar

    Jim Randell 9:33 am on 12 July 2019 Permalink | Reply
    Tags:   

    Teaser 2803: Easy as ABC 

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

    George and Martha have replaced the digits 0 to 9 by the letters A to J in some order. George then noted a neat product, namely:

    AB × CDE = FGHIJ

    Then Martha noted a neat sum, namely:

    AB + CD + EF + GH + IJ = CCC

    What, in order, are the values of the letters A to J?

    [teaser2803]

     
    • Jim Randell's avatar

      Jim Randell 9:34 am on 12 July 2019 Permalink | Reply

      When this puzzle was published (June 2016) I wrote a couple of programs to solve it using the [[ SubstitutedSum() ]] solver from the enigma.py (which itself was originally written to solve Enigma 63 and similar puzzles).

      However, shortly afterwards I started work on a solver for general alphametic expressions in Python, and this puzzle was one of the examples I used to test it. I wrote up my thoughts as Solving Alphametics with Python and Solving Alphametics with Python, Part 2. And although there have been many incremental improvements to the code over the last 3 years, the ideas from Part 2 still form the basis of the [[ SubstitutedExpression() ]] solver in the enigma.py library, which I have used it to solve a wide variety of alphametic puzzles in that time.

      For this puzzle we can use the following command line (which executes in 88ms):

      Run: [ @replit ]

      % python3 -m enigma SubstitutedExpression --answer="ABCDEFGHIJ" "AB * CDE = FGHIJ" "AB + CD + EF + GH + IJ = CCC"
      (AB * CDE = FGHIJ) (AB + CD + EF + GH + IJ = CCC) (ABCDEFGHIJ)
      (52 * 367 = 19084) (52 + 36 + 71 + 90 + 84 = 333) (5236719084) / A=5 B=2 C=3 D=6 E=7 F=1 G=9 H=0 I=8 J=4
      ABCDEFGHIJ = 5236719084 [1 solution]
      

      Solution: The values of the letters are: A=5, B=2, C=3, D=6, E=7, F=1, G=9, H=0, I=8, J=4.

      Like

    • GeoffR's avatar

      GeoffR 11:18 am on 12 July 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;
      
      constraint A > 0 /\ C > 0 /\ E > 0 /\ F > 0
      /\ G > 0 /\ I > 0;
      
      constraint all_different ([A, B, C, D, E, F, G, H, I]);
      
      var 10..99: AB = 10*A + B;
      var 10..99: CD = 10*C + D;
      var 10..99: EF = 10*E + F;
      var 10..99: GH = 10*G + H;
      var 10..99: IJ = 10*I + J;
      var 100..999: CCC = 111*C;
      var 100..999: CDE = 100*C + 10*D + E;
      var 10000..99999: FGHIJ = 10000* F + 1000*G + 100*H + 10*I + J;
      
      % two alphametic equations to solve
      constraint AB * CDE == FGHIJ;
      constraint AB + CD + EF + GH + IJ == CCC;
      
      solve satisfy;
      
      output ["A,B,C,D,E,F,G,H,I,J = " ++ show([A,B,C,D,E,F,G,H,I,J])]
      ++ ["\n" ++ show(AB) ++ " * " ++ show(CDE) ++ " = " ++ show(FGHIJ)]
      ++ ["\n" ++ show (AB) ++ " + " ++  show(CD) ++ " + " ++ show(EF) ++ " + "
      ++ show(GH) ++ "  + " ++ show(IJ) ++ " = "  ++ show(CCC) ] ;
      
      % A,B,C,D,E,F,G,H,I,J = [5, 2, 3, 6, 7, 1, 9, 0, 8, 4]
      % 52 * 367 = 19084
      % 52 + 36 + 71 + 90  + 84 = 333
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

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

    Brainteaser 1625: Jolly sticks 

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

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

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

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

    [teaser1625]

     
    • Jim Randell's avatar

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

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

      Run: [ @repl.it ]

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

      Solution: The scores were as follows:

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

      Like

  • Unknown's avatar

    Jim Randell 10:22 am on 10 July 2019 Permalink | Reply
    Tags:   

    Teaser 2900: An ancestral equation 

    From The Sunday Times, 22nd April 2018 [link] [link]

    I recently discovered a 16th-century ancestor called David, which happens to be my maiden name. (I never really liked Bricket, even when pronounced in the French way). I have always been partial to word arithmetic (or cryptarithms) and the other day I found a solution to this one:

    MY × NAME = DAVID

    When eight distinct digits are substituted for the eight letters and no number starts with zero, the equation holds. Amazingly the solution gave me more information about my ancestor. MY turned out to be his age when he died and NAME was the year he was born.

    What was that year?

    [teaser2900]

     
    • Jim Randell's avatar

      Jim Randell 10:24 am on 10 July 2019 Permalink | Reply

      This is a simple alphametic puzzle that can be solved using the [[ SubstitutedExpression() ]] solver from the enigma.py library.

      The multiplication expression is enough to find a unique solution, but we can also check that the date is in the required range.

      This run file executes in 166ms.

      Run: [ @replit ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      # this is enough to solve the puzzle
      "MY * NAME = DAVID"
      
      # [optional] check they were alive in the 16th century (1501 - 1600)
      "1500 - MY < NAME < 1601"
      

      Solution: He was born in 1543.

      And died aged 49 around 1592.

      Like

    • GeoffR's avatar

      GeoffR 12:13 pm on 11 July 2019 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 0..9:M; var 0..9:Y; var 0..9:N; var 0..9:A; 
      var 0..9:E; var 0..9:D; var 0..9:V; var 0..9:I; 
      
      % 16th-century means N = 1 and A = 5, as year born = NAME
      constraint M > 0 /\ D > 0 /\ N == 1 /\ A == 5;
      constraint all_different([M, Y, N, A, E, D, V, I]);
      
      var 10..99: MY = 10*M + Y;
      var 1000..9999: NAME = 1000*N + 100*A + 10*M + E;
      var 10000..99999: DAVID = 10001*D + 1000*A + 100*V + 10*I;
      
      constraint MY * NAME == DAVID;
      
      solve satisfy;
      output[ "Equation: "  ++ show(MY) ++ " * " ++ show(NAME) ++ " = " ++ show(DAVID)]
      ++ [ "\nYear born = " ++ show(NAME) ];
      
      % Equation: 49 * 1543 = 75607
      % Year born = 1543
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:26 am on 9 July 2019 Permalink | Reply
    Tags:   

    Brain-Teaser 485: Delphic oracle 

    From The Sunday Times, 13th September 1970 [link]

    Delphi has three computers, in order to confuse the faithful. The 1969 one always gives a true answer; the 1960 one always gives an untrue answer; the 1965 is quite unpredictable.

    Professor Binary, who is visiting the shrine, fed to each computer in turn the question: “What is the date of the largest computer?”

    The first computer replied “1969”; the second replied “Not 1960”; and Binary, who could see the size, though not the date, of the computers as they replied, had no need to study the third computer’s reply, since he now knew the answer to his question.

    What is the date of the largest computer?

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

    [teaser485]

     
    • Jim Randell's avatar

      Jim Randell 8:28 am on 9 July 2019 Permalink | Reply

      This Python program runs in 81ms.

      Run: [ @replit ]

      from enigma import (subsets, printf)
      
      # the behaviours of the computers
      def F(s): return (not s)
      def U(s): return True
      def T(s): return bool(s)
      
      # the professor asks two of the computers (identified by size)
      for (a, b) in subsets((0, 1, 2), size=2):
      
        # record largest computers in scenarios that work
        rs = set()
      
        # consider all possible size orders (L, M, S)
        for size in subsets((F, U, T), size=3, select='P'):
          (L, A, B) = (size[0], size[a], size[b])
      
          # first computer states "1969 (T) is largest"
          if not A(L == T): continue
      
          # second computer states "1960 (F) is not largest"
          if not B(L != F): continue
      
          rs.add(L)
      
        # look for unique solutions
        if len(rs) == 1:
          L = rs.pop()
          printf("L={L.__name__} [a={a} b={b}]")
      

      Solution: The largest computer is from 1965.

      So the largest computer, from 1965, is unreliable.

      The medium sized computer is from 1960, and always makes false statements.

      The smallest computer is from 1969, and always makes true statements.

      The Professor asks his first question to the medium sized computer, and his second question to the smallest computer.


      We can verify manually that this does indeed give a solution.

      First we refer to the computers by their behaviour, instead of their year. So:

      1960, always makes false statements = F
      1965, makes unreliable statements = U
      1969, always makes true statements = T

      The Professor asks the firsts question (“which computer is largest”) to M (the medium sized computer), and gets an answer of “T”, and he then asks the same question to S (the smallest computer), and gets an answer of “not F”.

      There are six possible orders of computer (Largest, Medium, Smallest):

      L=F, M=U, S=T: S would not say “not F” (as S=T and L=F). Impossible.
      L=F, M=T, S=U: M would not say “T” (as M=T and L=F). Impossible.
      L=U, M=F, S=T: M would say “T” (as M=F and L=U), and S would say “not F” (as S=T and L=U). Possible.
      L=U, M=T, S=F: M would not say “T” (as M=T and L=U). Impossible.
      L=T, M=F, S=U: M would not say “T” (as M=F and L=T). Impossible.
      L=T, M=U, S=F: S would not say “not F” (as S=F and L=T). Impossible.

      So there is only one possible situation, and so the Professor can determine that: L=U=1965 (and also: M=F=1960, S=T=1969).

      Asking the question to M and S is the only situation where a single largest computer is identified, so this is the only solution.

      Like

  • Unknown's avatar

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

    Teaser 2812: Sum card trick 

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

    Joe has nine cards, white on one side and grey on the other, with a single digit written on each side of each card. He gave them to Penny to arrange white-side-up to form three 3-figure numbers with the sum of the first two equalling the third. This was her attempt:

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

    Then Joe turned the cards over one at a time to reveal

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

    where the total of the first two did not equal the third. So he challenged Penny to arrange the cards so that both white-side-up and grey-side-up the third number was the sum of the first two, which she did.

    What was her third grey number?

    [teaser2812]

     
    • Jim Randell's avatar

      Jim Randell 8:17 am on 8 July 2019 Permalink | Reply

      The following run file uses the [[ --code ]] parameter of the [[ SubstitutedExpression() ]] solver (from the enigma.py library) to provide a function that translates from white numbers to grey numbers. This can then be used in the alphametic expressions.

      The run file executes in 236ms.

      Run: [ @replit ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      # map from white to corresponding grey digit
      --code="w2g = dict(zip(nsplit(219654873), nsplit(987142365)))"
      
      # return the grey number for a corresponding white number <n>
      --code="grey = lambda n: nconcat(w2g[x] for x in nsplit(n))"
      
      # solve the sum so the white side and the grey side are both valid
      --digits="1-9"
      --answer="grey(GHI)"
      
      "ABC + DEF = GHI"
      "grey(ABC) + grey(DEF) == grey(GHI)"
      

      Solution: The third grey number is 738.

      Like

    • GeoffR's avatar

      GeoffR 8:41 am on 16 July 2019 Permalink | Reply

      # Equations are ABC + DEF = GHI for white cards
      # and abc + def1 = ghi for grey cards
      
      from itertools import permutations
      
      # dictionary for white to grey card values
      wtg = {2:9, 1:8, 9:7, 6:1, 5:4, 4:2, 8:3, 7:6, 3:5}
      
      # permute white card digits
      for p in permutations( (2, 1, 9, 6, 5, 4, 8, 7, 3) ): 
          A, B, C, D, E, F, G, H, I = p
          ABC = 100*A + 10*B + C
          DEF = 100*D +10*E + F
          GHI = 100*G + 10*H + I
          if ABC + DEF != GHI: continue
          if ABC < DEF: continue
      
          # Look up grey card values from white cards
          a, b, c = wtg[A], wtg[B], wtg[C]
          abc = 100*a + 10*b + c
          
          d, e, f1 = wtg[D], wtg[E], wtg[F]
          def1 = 100*d + 10*e + f1
      
          g, h, i = wtg[G], wtg[H], wtg[I]
          ghi = 100*g + 10*h + i
          if abc + def1 == ghi:
              print(f"White cards are ({ABC}), ({DEF}), ({GHI})" )
              print(f"Grey Cards are  ({abc}), ({def1}), ({ghi})")
              print()
      
      # White cards are (624), (357), (981)
      # Grey Cards are  (192), (546), (738)
      
      # White cards are (627), (354), (981)
      # Grey Cards are  (196), (542), (738)
      
      # White cards are (654), (327), (981)
      # Grey Cards are  (142), (596), (738)
      
      # White cards are (657), (324), (981)
      # Grey Cards are  (146), (592), (738)
      
      # Third grey number was 738
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 5:26 pm on 5 July 2019 Permalink | Reply
    Tags:   

    Teaser 2963: A result 

    From The Sunday Times, 7th July 2019 [link] [link]

    For any number, I square the digits and then add the resulting numbers. If necessary, I keep repeating the process until I end up with a single digit, called the result. For example: 142 gives 1 + 16 + 4 = 21 which then gives 4 + 1 = 5, the result.

    I have written down a two-digit number. If I tell you one of the digits [the key digit], you should be able to work out the result.

    I then use a 3rd digit to get a three-digit number. The result of that number happens to be the key digit.

    In increasing order, what are the three digits?

    [teaser2963]

     
    • Jim Randell's avatar

      Jim Randell 5:59 pm on 5 July 2019 Permalink | Reply

      The following Python program runs in 90ms.

      Run: [ @repl.it ]

      from enigma import (nsplit, subsets, irange, unpack, filter_unique, printf)
      
      # process a sequence of digits
      def process(ds):
        while True:
          n = sum(d * d for d in ds)
          if n < 10: return n
          ds = nsplit(n)
      
      # collect the results for 2 digit numbers
      ss = list((ds, process(ds)) for ds in subsets(irange(0, 9), size=2, select='R') if ds != (0, 0))
      
      # if I tell you "one of the digits is <k>" you should be able to work out the result
      for k in irange(0, 9):
        (rs, _) = filter_unique(ss, unpack(lambda ds, r: k in ds), unpack(lambda ds, r: r))
      
        # consider possible 2-digit sequences
        for (ds, _) in rs:
          # and add an extra digit
          for x in irange(0, 9):
            # the result should be the digit <k>
            if process(ds + (x,)) == k:
              printf("k={k}: {ds} + {x}")
      

      Solution: The three digits are: 5, 6, 9.

      The order of the digits in a number does not matter. 24 will have the same result as 42 (both are 2² + 4² = 20 → 2² + 0² = 4).

      The key digit is 5. Any 2-digit sequence that contains a 5 has a result of 4.

      The initial number is one of: 56, 59, 65, 95. An extra digit is added so the three digit sequence consists of the digits 5, 6, 9 (in some order). And this sequence has a result of 5.


      Most of the 2-digit numbers give a result of 4, and if the process is allowed to continue summing the squares of the digits we see the following cycle emerge:

      4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4

      Numbers that eventually result in this cycle are known as “unhappy numbers” (see [ link ]).

      The other 2-digit numbers give results of 1, 2, 5, 8, 9, we can also continue the process with these values:

      1 → 1
      2 → 4
      5 → 25 → 29 → 85 → 89 → … → 4
      8 → 64 → 52 → 29 → … → 4
      9 → 81 → 65 → 61 → 37 → … → 4

      We see that any numbers with a result of 2, 5, 8, 9 are also “unhappy”, and only those numbers that have a result of 1 are “happy”. (See OEIS A124095 [ link ]).

      Like

      • Jim Randell's avatar

        Jim Randell 3:54 pm on 7 July 2019 Permalink | Reply

        Here’s a simpler program to solve the puzzle.

        Run: [ @repl.it ]

        from enigma import (nsplit, seq_all_same, irange, subsets, printf)
        
        # process a sequence of digits
        def process(*ds):
          while True:
            n = sum(d * d for d in ds)
            if n < 10: return n
            ds = nsplit(n)
        
        # look for possible key digits
        for k in irange(0, 9):
          # must give the same result when combined with all other digits
          if seq_all_same(process(k, d) for d in irange(0, 9) if k + d > 0):
        
            # look for two additional digits to give a result of k
            for (a, b) in subsets(irange(0, 9), size=2, select='R'):
              if process(k, a, b) == k and k + a + b > 0:
                printf("k={k}: {s}", s=sorted([k, a, b]))
        

        Like

  • Unknown's avatar

    Jim Randell 8:26 am on 5 July 2019 Permalink | Reply
    Tags:   

    Teaser 2901: Square deal 

    From The Sunday Times, 29th April 2018 [link] [link]

    George and Martha have five daughters who have periodically appeared in my teasers over the years. They are all working for the same company and have perfect-square four-digit telephone extensions, in all five cases. The letters ABCDEFGHIJ stand for the digits 0-9 but in no particular order. The extensions are as follows:

    Andrea = IBHC
    Bertha = DJJC
    Caroline = BAFH
    Dorothy = GFID
    Elizabeth = GAEE

    What are the five extensions?

    [teaser2901]

     
    • Jim Randell's avatar

      Jim Randell 8:27 am on 5 July 2019 Permalink | Reply

      This is a straightforward alphametic problem which can be handled directly by the [[ SubstitutedExpression() ]] solver in the enigma.py library.

      The following run file executes in 127ms.

      Run: [ @repl.it ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      --answer="(IBHC, DJJC, BAFH, GFID, GAEE)"
      
      "is_square(IBHC)"
      "is_square(DJJC)"
      "is_square(BAFH)"
      "is_square(GFID)"
      "is_square(GAEE)"
      

      Solution: The five extension numbers are: 2916, 5776, 9801, 3025, 3844.

      Like

    • GeoffR's avatar

      GeoffR 12:13 pm on 5 July 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;
      
      constraint all_different ([A,B,C,D,E,F,G,H,I,J]);
      constraint I > 0 /\ D > 0 /\ B > 0 /\ G > 0;
      
      set of int: sq4 = {n*n | n in 32..99};
      
      % The five extensions
      var 1000..9999: IBHC = 1000*I + 100*B + 10*H + C;
      var 1000..9999: DJJC = 1000*D + 100*J + 10*J + C;
      var 1000..9999: BAFH = 1000*B + 100*A + 10*F + H;
      var 1000..9999: GFID = 1000*G + 100*F + 10*I + D;
      var 1000..9999: GAEE = 1000*G + 100*A + 10*E + E;
      
      constraint IBHC in sq4 /\ DJJC in sq4 /\ BAFH in sq4
      /\ GFID in sq4 /\ GAEE in sq4;
      
      solve satisfy;
      
      output [ "The five extensions are " ++ show(IBHC) ++ "," ++ show(DJJC) 
      ++ "," ++ show(BAFH) ++ "," ++ show(GFID) ++ " and " ++ show(GAEE) ];
      
      % The five extensions are 2916,5776,9801,3025 and 3844
      % % time elapsed: 0.02 s
      % ----------
      % ==========
      % Finished in 437msec
      

      Like

  • Unknown's avatar

    Jim Randell 9:52 am on 4 July 2019 Permalink | Reply
    Tags: ,   

    Brain-Teaser 484: [Family ages] 

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

    The total ages of my children, all of single birth, exactly equal my own age. On the same day next year they will exactly equal my husbands age.

    At present my husband’s age is divisible by the age of only one child, but in one year’s time it will be divisible by the separate ages of three children and also by the number of children in the family, while my own age will be divisible by the age of one child only.

    During the year ahead, on just one day in May, my husband’s age will be divisible by that of my eldest child.

    What are the children’s ages?

    Note: This puzzle is flawed, as there is not a single solution. A note was published with Brain-Teaser 485 saying that as there are three solutions no prize can be awarded.

    This puzzle was originally published with no title.

    [teaser484]

     
    • Jim Randell's avatar

      Jim Randell 9:54 am on 4 July 2019 Permalink | Reply

      I found many solutions to this puzzle, not just the 3 mentioned when Brain-Teaser 485 was published.

      The simplest solution was with 4 children with ages: 1, 2, 11, 18. This gives:

      The wife’s age is 32, the husband’s age is 35.

      Only 1 divides into 35, but next year (when the children are 2, 3, 12, 19 and the husband is 36) three of the children ages (2, 3, 12) will divide in 36.

      And the wife will be 33, and 3 divides into this.

      During May the eldest child’s age (18) does not divide into the husband’s age (35), until the husband’s birthday, when he is 36. The next day is the eldest child’s birthday, who become 19, which not divide the husband’s age.

      The problem with this solution is that at the time of birth of the eldest child the wife would be 14 and the husband 17. While not impossible, it’s probably not what the setter had in mind, so we’ll stick with more conventional scenarios.

      To make a version of the puzzle with a single solution I added a couple of extra conditions.

      Firstly I strengthened the “all of single birth” condition to the children all having different ages (in years), and restricted the current ages of the children to be between 2 and 16.

      This gets me down to 5 solutions (one with 5 children, and four with 7 children). Adding a further reasonable conditions such as “the husband and wife are both aged less than 50” or “the wife was in her twenties at the time of the birth of the eldest child” can be used to narrow the possibilities down to a single solution.

      So I settled on the following additional conditions to give a single solution:

      • The children are aged between 2 and 16, and their ages (expressed in years) are all different.
      • The husband and wife are currently aged less than 50.

      This Python program solves the revised puzzle. It runs in 159ms.

      Run: [ @repl.it ]

      from enigma import (subsets, irange, div, printf)
      
      # x is divisible by y
      divides = lambda x, y: div(x, y) is not None
      
      # consider sets of at least 3 children ([extra] with no repeated ages)
      for ages in subsets(irange(2, 16), min_size=3, select='C'):
        k = len(ages)
      
        # wife's current age is the sum
        w = sum(ages)
        # husbands age + 1 is w + k
        h = w + k - 1
      
        # [extra] husband and wife are less than 40
        if not (h < 50): continue
      
        # husbands current age is divisible by the age of only one child
        if not (sum(divides(h, x) for x in ages) == 1): continue
      
        # in 1 years time it is divisible by the ages of three children...
        if not (sum(divides(h + 1, x + 1) for x in ages) == 3): continue
        # ... and also the number of children ...
        if not divides(h + 1, k): continue
      
        # ... while the wifes age is divisible by only one child
        if not (sum(divides(w + 1, x + 1) for x in ages) == 1): continue
      
        # on one day the husbands age will divide the age of the eldest child
        # so their birthdays must be on consecutive days
        a = ages[-1]
        # so they don't divide in general either now or both incremented
        if divides(h, a) or divides(h + 1, a + 1): continue
        # but they do divide when one of them is incremented
        if not (divides(h + 1, a) or (divides(h, a + 1))): continue
      
        # output solution (w0 and h0 are ages at the birth of the first child)
        printf("k={k}, ages={ages}, w={w}, h={h} [{w0}, {h0}]", w0=w - a, h0=h - a)
      

      Solution: The children are aged: 3, 6, 7, 9, 10.

      The wife’s current age is 35 (the sum of the children’s ages). The husband’s current age is 39. (So they would be 25 and 29 at the birth of eldest child).

      Of the children’s current ages, only 3 divides into 39. But when the ages are all increased by one to 4, 7, 8, 10, 11 then 4, 8 and 10 divide into 40. And the wife will be 36, which is divisible by 4.

      During May the eldest child’s age (10) does not divide into the husband’s age (39), until the husband’s birthday, when he is 40. The next day is the eldest child’s birthday (11) and non-divisibility is resumed.

      The intended solution was apparently 1, 6, 7, 9, 12.

      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