Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 8:55 am on 17 February 2022 Permalink | Reply
    Tags:   

    Teaser 2851: Knowing the lingo 

    From The Sunday Times, 14th May 2017 [link] [link]

    A group of 64 students can speak, between them, French, German and Russian. There are three times as many French speakers and two times as many German speakers as there are Russian speakers. The number who speak all three languages is one fifth of the number who speak French and at least one other of these languages. The number who speak all three languages is also two ninths of the number who speak German and at least one other of these languages. Furthermore, the number who speak all three languages is also two fifths of the number who speak Russian and at least one other of these languages.

    How many of the 64 students speak only French?

    [teaser2851]

     
    • Jim Randell's avatar

      Jim Randell 8:57 am on 17 February 2022 Permalink | Reply

      We assume that each of the students speaks at least one language (otherwise we can find many solutions), then we can represent the 7 internal areas of the Venn diagram: F, G, R, FG, FR, GR, FGR.

      So we have:

      F + G + R + FG + FR + GR + FGR = 64

      And the additional conditions give 5 more equations.

      But together these only give 6 equations in the 7 variables, so we need to choose a value for one of the regions to make a complete set of equations. The value for FGR region needs to be divisible by 2, so we can use that.

      The following Python program runs in 54ms.

      Run: [ @replit ]

      from enigma import (Matrix, as_int, irange, printf)
      
      # the given equations (incomplete)
      eqs = [
        #  F   G   R  FG  FR  GR FGR = k
        (( 1,  1,  1,  1,  1,  1,  1), 64), # 64 students in total
        ((-1,  0,  3, -1,  2,  3,  2),  0), # total F = 3(total R)
        (( 0, -1,  2, -1,  2,  1,  1),  0), # total G = 2(total R)
        (( 0,  0,  0, -1, -1,  0,  4),  0), # FGR = (1/5)(FG + FR + FGR)
        (( 0,  0,  0, -2,  0, -2,  7),  0), # FGR = (2/9)(FG + GR + FGR)
        (( 0,  0,  0,  0, -2, -2,  3),  0), # FGR = (2/5)(FR + GR + FGR)
      ]
      
      # choose a value for FGR
      for FGR in irange(0, 64, step=2):
        # make an additional equation for FGR
        #      F  G  R FG FR GR FGR = k
        eq = ((0, 0, 0, 0, 0, 0, 1), FGR)
      
        # solve the equations for non-negative integers
        try:
          vs = tuple(as_int(x, "0+") for x in Matrix.linear(eqs + [eq]))
        except ValueError:
          continue
      
        (F, G, R, FG, FR, GR, FGR) = vs
      
        # there must be at least 1 speaker of each language
        if R + FR + GR + FGR == 0: continue
      
        # output solution
        printf("F={F} G={G} R={R} FG={FG} FR={FR} GR={GR} FGR={FGR}")
      

      Solution: 25 of the students speak only French.

      There is only one possible arrangement (subject to the assumption above):

      F=25
      G=12
      R=5
      FG=12
      FR=4
      GR=2
      FGR=4


      We can manually simplify the equations:

      FGR = (1/5)(FG + FR + FGR) ⇒ 2 FG + 2 FR = 8 FGR
      FGR = (2/5)(FR + GR + FGR) ⇒ 2 FR + 2 GR = 3 FGR
      FGR = (2/9)(FG + GR + FGR) ⇒ 2 FG + 2 GR = 7 FGR
      ⇒ GR = FGR / 2
      ⇒ FR = FGR
      ⇒ FG = 3 FGR

      F + G + R + FG + FR + GR + FGR = N ⇒ F + G + R + (11/2) FGR = N
      F + FG + FR + FGR = 3(R + FR + GR + FGR) ⇒ F = 3 R + (5/2) FGR
      G + FG + GR + FGR = 2(R + FR + GR + FGR) ⇒ G = 2 R + (1/2) FGR
      ⇒ R = (N – (17/2) FGR) / 6
      ⇒ F = 3 R + (5/2) FGR
      ⇒ G = 2 R + (1/2) FGR

      So given an even value for FGR = 2k, we can calculate the values for the other regions:

      Run: [ @replit ]

      from enigma import (irange, div, printf)
      
      # total number of students
      N = 64
      
      # choose a value for FGR = 2k
      for k in irange(0, N // 2):
        n = N - 17 * k
        if n < 0: break
        R = div(n, 6)
        if R is None: continue
      
        # determine the other values
        FR = FGR = 2 * k
        GR = k
        FG = 3 * FGR
        F = 3 * R + 5 * k
        G = 2 * R + k
      
        # output solution
        printf("F={F} G={G} R={R} FG={FG} FR={FR} GR={GR} FGR={FGR}")
      

      If we do the steps manually we find we only need to check k = 0, 1, 2, 3, and only k = 2 gives viable values.

      Like

    • GeoffR's avatar

      GeoffR 11:45 am on 17 February 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
       
      var 1..30:F;  var 1..30:G;  var 1..30:R; 
      var 1..60:FG; var 1..60:FR; var 1..60:GR; 
      var 1..90:FGR; 
      
      % A group of 64 students can speak, between them, French, German and Russian
      constraint F + G + R + FG + FR + GR + FGR == 64;
      
      % There are 3X as many French speakers as there are Russian speakers
      constraint F + FG + FR + FGR == 3 * (R + FR + GR + FGR);
      
      % There are 2X as many German speakers as there are Russian speakers
      constraint G + FG + GR + FGR == 2 * (R + FR + GR + FGR);
      
      % Number who speak all three languages is one fifth of the number who speak
      % .. French and at least one other of these languages
      constraint 5 * FGR == FG + FR + FGR;
      
      % Number who speak all three languages is also two ninths of the number 
      % ..who speak German and at least one other of these languages
      constraint 9 * FGR == 2 *(FG + GR + FGR);
      
      % Number who speak all three languages is also two fifths of the number 
      % ..who speak Russian and at least one other of these languages
      constraint 5 * FGR == 2 *(FR + GR + FGR);
      
      solve satisfy;
      
      % F = 25;
      % G = 12;
      % R = 5;
      % FG = 12;
      % FR = 4;
      % GR = 2;
      % FGR = 4;
      % ----------
      % ==========
      
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 4:24 pm on 17 February 2022 Permalink | Reply

        Although we are not told that each of the 7 subsets is non-empty (but we do know that there must be at least one student that speaks each of the languages).

        Like

    • GeoffR's avatar

      GeoffR 3:56 pm on 18 February 2022 Permalink | Reply

      Although the Solve Function is often used in Mathematica for solving sets of simultaneous equations, it did not work in this instance. It turns out that the FindInstance Function (ref Brian) was suitable. and can sometimes be used as an alternative solver in Mathematica.

      FindInstance[{ F + G + R + FG + FR + GR + FGR == 64,
        F + FG + FR + FGR == 3 (R + FR + GR + FGR),
        G + FG + GR + FGR == 2 (R + FR + GR + FGR),
        5 FGR == FG + FR + FGR,
        9 FGR == 2 (FG + GR + FGR),
        5 FGR == 2 (FR + GR + FGR),
        F + FG + FR + FGR > 0, G + FG + GR + FGR > 0,
        R + FR + GR + FGR > 0},
        {F, R, G, FG, FR, GR, FGR}, Integers ]
      
      {{F -> 25, R -> 5, G -> 12, FG -> 12, FR -> 4, GR -> 2, FGR -> 4}}
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:24 am on 15 February 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 878: Four twos make …? 

    From The Sunday Times, 4th June 1978 [link]

    Two maths pupils had been indulging in the traditional “Four Fours” recreation, (i.e. seeing how many consecutive integers from 1 up they could express by means of a string of just four 4s with the signs for addition, subtraction, multiplication or division (to obviate numerator/denominator) interspersed as required, along with brackets where needed for clarity, but (according to their rules) featuring no power indices nor other aids except that each expression may use one decimal point. Examples illustrating all this are:

    18 = (4 / .4) + 4 + 4
    28 = 4 × (4 + 4) – 4

    As a variant they then tried working with 2s instead of 4s but soon found that there is a certain integer, lower than both of the above, which defeats every effort to express it with only four 2s. But when they used the minimum number of 2s to express it, they found, under corresponding conditions, that they could express it in a number of different ways. Indeed they found two ways which used neither addition nor multiplication.

    What were those two expressions?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981). The puzzle text above is taken from the book.

    [teaser878]

     
    • Jim Randell's avatar

      Jim Randell 10:24 am on 15 February 2022 Permalink | Reply

      See also: Teaser 1949-12-25.

      We can take the code for Teaser 1949-12-25, and change it to using four 2’s, and only the 4 basic mathematical operators.

      We find that we can generate all the numbers from 1 to 16:

      1 = 22 / 22
      2 = (2 / 2) + (2 / 2)
      3 = 2 + 2 − (2 / 2)
      4 = 2 + 2 + 2 − 2
      5 = 2 + 2 + (2 / 2)
      6 = (2 + 2) × 2 − 2
      7 = 2 + 2 / (2 × .2)
      8 = 2 + 2 + 2 + 2
      9 = (22 / 2) − 2
      10 = 22 / 2.2
      11 = (2 / .2) + (2 / 2)
      12 = (22 + 2) / 2
      13 = (22 / 2) + 2
      14 = (2 / .2) + 2 + 2
      15 = (2 + (2 / 2)) / .2
      16 = (2 + 2) × (2 + 2)
      17 = ???

      So we can’t express 17 using four 2’s. But as we can express 15 using four 2’s that means we can certainly express 17 using five 2’s:

      17 = ((2 + (2 / 2)) / .2) + 2

      But the question asks us to express it without using addition or multiplication:

      17 = 22 − ((2 / 2) / .2)
      17 = 22 − ((2 / .2) / 2)

      Like

  • Unknown's avatar

    Jim Randell 10:05 pm on 13 February 2022 Permalink | Reply
    Tags:   

    Teaser 2739: Funny dice 

    From The Sunday Times, 22nd March 2015 [link] [link]

    I have two cube-shaped dice, one red and one blue, with a positive whole number on each face. When I throw the dice and add up the two numbers, the most likely total is 7. The next most likely totals are 6 and 8, the next are 5 and 9, the next are 4 and 10, the next are 3 and 11, and the least likely are 2 and 12. However, my dice are not standard: indeed, the total of the six numbers on the red dice is higher than the total of those on the blue dice.

    What are the six numbers on the red dice?

    [teaser2739]

     
    • Jim Randell's avatar

      Jim Randell 10:05 pm on 13 February 2022 Permalink | Reply

      See also: Teaser 3098, Enigma 382, Enigma 646b.

      We assume that the 6-sided dice are “fair” (i.e. when one is thrown each face has a 1/6 chance of showing), and also that each of the mentioned values (2-12) is an achievable throw with the pair.

      The probability of each combination must be expressible as n/36 and the sum of the probabilities must be 1.

      If we consider the lowest possible probabilities:

      2, 12 = 1/36
      3, 11 = 2/36
      4, 10 = 3/36
      5, 9 = 4/36
      6, 8 = 5/36
      7 = 6/36

      Then we find this accounts for 36/36, so must be the required distribution, and is the same distribution as a pair of standard dice.

      So the dice we are looking for are a non-standard pair with the same throw distribution as a standard pair.

      There is only one such value for 6-sided dice, known as the Sicherman dice.

      We can use the [[ sicherman() ]] function I wrote for Teaser 3098 to solve this problem.

      This Python program runs in 46ms (internal run time is 841µs).

      Run: [ @replit ]

      from enigma import printf
      from sicherman import sicherman
      
      for (d1, d2) in sicherman(6):
        if sum(d1) != sum(d2):
          printf("{d1} + {d2}")
      

      Solution: The numbers on the red die are: 1, 3, 4, 5, 6, 8.

      And the numbers on the blue die are: 1, 2, 2, 3, 3, 4.

      Like

    • Hugh+Casement's avatar

      Hugh+Casement 7:12 am on 14 February 2022 Permalink | Reply

      We are told ” the least likely are 2 and 12″ but there is no stipulation that they have to occur at all.
      I found eight sets where only sums from 3 to 11 can occur, with frequencies in the order given.
      So for me the puzzle is ill-defined — that’s apart from the misuse of the plural ‘dice’.

      Like

  • Unknown's avatar

    Jim Randell 3:07 pm on 11 February 2022 Permalink | Reply
    Tags:   

    Teaser 3099: Recurring theme 

    From The Sunday Times, 13th February 2022 [link] [link]

    Liam is learning about fractions and decimals. He has been shown that some fractions give rise to decimals with a recurring pattern eg 4/11 = 0.3636… . Each pupil in his class has been given four numbers. The largest (two-figure) number is to be used as the denominator and the others as numerators, to create three fractions that are to be expressed as decimals. Liam found that each decimal was of the form 0.abcabc…, with the same digits but in different orders.

    Liam’s largest number contained the digit 7 and if I told you how many of his four numbers were divisible by ten you should be able to work out the numbers.

    What, in increasing order, are the four numbers?

    [teaser3099]

     
    • Jim Randell's avatar

      Jim Randell 3:42 pm on 11 February 2022 Permalink | Reply

      We can use some handy routines from the enigma.py library to help in solving this puzzle. [[ recurring() ]] finds the recurring part of the decimal representation of a fraction, and [[ filter_unique() ]] can be used to find sets that are unique by the count of multiples of 10 involved.

      The following Python program runs in 91ms.

      Run: [ @replit ]

      from enigma import (defaultdict, irange, union, recurring, join, subsets, filter_unique, printf)
      
      # generate sets of possible numbers
      def generate():
        # choose the denominator (2-digits, one of them a 7)
        for d in union([irange(17, 97, step=10), irange(70, 79)]):
      
          # consider the smaller numbers, n/d = 0.(xyz)...
          # and record them by the digits that make up the repetend
          r = defaultdict(list)
          for n in irange(1, d - 1):
            (_, nr, rr) = recurring(n, d)
            if (not nr) and len(rr) == 3:
              r[join(sorted(rr))].append(n)
      
          # find sets of 3 numerators
          for ns in r.values():
            for ss in subsets(ns, size=3, fn=list):
              yield tuple(ss + [d])
      
      # find sets unique by the count of multiples of 10 involved
      fn = lambda ns: sum(n % 10 == 0 for n in ns)
      for ns in filter_unique(generate(), fn).unique:
        printf("{ns}")
      

      Solution: Liam’s numbers are: 4, 30, 40, 74.

      So we have:

      4/74 = 0.(054)…
      30/74 = 0.(405)…
      40/74 = 0.(540)…

      And this the only set of 4 numbers where 2 of them are divisible by 10.

      In all there are 30 possible sets of 4 numbers. 12 sets use a denominator of 37, and the numbers in these sets can be multiplied by 2 to give 12 further sets (that give the same fractions) using a denominator of 74. The remaining 6 sets have a denominator of 27.

      Of these there are 19 sets with none of the 4 numbers divisible by 10, and 10 sets with one of the 4 numbers divisible by 10. The remaining set has two of the numbers divisible by 10, and so gives the answer.

      Like

    • GeoffR's avatar

      GeoffR 3:09 pm on 15 February 2022 Permalink | Reply

      Not very elegant code, but it finds the answer, and runs in less than 100ms.
      I used two Python dictionaries in this solution.

      from enigma import recurring, join
      from collections import defaultdict
      
      # for potential number lists
      Nums = defaultdict(list)
      
      # for counting values in Nums divisible by 10
      D10 = defaultdict(list) 
      
      # using list of possible denominators including digit 7
      # .. to search for repeating decimal patterns
      for n in (17,27,37,47,57,67,70,71,72,73,74,75,76,77,78,79,87,97):
        (_, nr, rr) = recurring(1, n)
        if not(nr) and len(rr) == 3:
          print(f"Number = {n}, reciprocal = {1/n}")
      
      # only 27 and 37 were found as denominators with repeating patterns
      # ... add multiples 54 and 74 as extra 2-digit numbers
      # ...in search for all 3-digit repeating patterns
      for n in range(1, 80):
        for d in (27, 37, 54, 74):
          if n < d:
            (_, nr, rr) = recurring(n, d)
            if not(nr) and len(rr) == 3:
              Nums[join(sorted(rr)) + " " + str(d)].append(n)
      
      # add denominator (from the key) to values            
      for k, v in Nums.items():  
        v = v + [int(k[4:6])]
      
      # form a new dictionary D10, based on the Nums dict values
      # ... which are divisible by 10
      for k, v in Nums.items():
        tot10 = 0
        # count numbers divisible by 10
        for x in v:
          q, r = divmod(int(x), 10)
          if q and r == 0:
            tot10 += 1
          # add denominator to D10 values
          D10[tot10].append(v + [int(k[4:6])] )
      
      # look for a unique set of four numbers
      # ... based on the number of values divisible by 10
      for k, v in D10.items():
        if len(v) == 1:
          print(f"Four numbers are {v}")
      
      
      

      Like

    • A.T.Surherland's avatar

      A.T.Surherland 9:40 am on 16 February 2022 Permalink | Reply

      I have obtained an interesting answer :-
      The sum of the 3 lowest numbers = the greatest number.
      ie the sum of the fractions = 1.
      Is there a reason for this condition?.

      Like

      • Jim Randell's avatar

        Jim Randell 12:01 pm on 16 February 2022 Permalink | Reply

        There are 12 sets of numbers that work with a denominator of 37. Of these 6 have the fractions sum to 1, and 6 have the fractions sum to 2. And we can obviously multiply all the numbers up by a factor of 2 to get another 12 sets that behave the same.

        The remaining 6 sets of numbers (with denominator 27) give fractions that don’t sum to a whole number (but are all multiples of 1/9).

        This is presumably due to the 3 different digits in the repetends lining up, so that the repetend of the sum is a single digit, and in the case of .(9)… this gives a whole number.

        Like

      • Brian Gladman's avatar

        Brian Gladman 12:29 pm on 16 February 2022 Permalink | Reply

        If we add 0.abc… + 0.bca… + 0.cab… we will get 0.111… x (a + b + c) = (a + b + c) / 9, so sum(numerators) / denominator = (a + b + c) / 9. So this happens when a + b + c == 9, which it does in our case. The sum can be 2 when a + b + c equals 18 but it is not necessarily an integer.

        Like

    • GeoffR's avatar

      GeoffR 11:07 am on 16 February 2022 Permalink | Reply

      Yes, This is my understanding of the reason.

      A repeating fraction of the format 0.abcabcabc.. can be expressed exactly as a rational fraction abc/999 – see number theory elsewhere.

      Each of the three numerators in the answer divided by the denominator, gives a 3-digit repeating pattern.

      Without displaying the answer publicly, as this is a current Sunday Times Teaser,
      the three repeating fractions for this teaser can be expressed as:

      0.abcabcabc   0.cabcabcab   0.bcabcabca – since the three fractions use the same digits as a stated teaser condition.

      The rational fractions for this teaser are then abc/999, cab/999 and bca/999.

      Also, abc + cab + bca = 999.

      It can be seen that abc/999 + cab/999 + bca/999 = 1.

      Like

  • Unknown's avatar

    Jim Randell 9:10 am on 10 February 2022 Permalink | Reply
    Tags:   

    Teaser 2872: Appropriate arithmetic 

    From The Sunday Times, 8th October 2017 [link] [link]

    I wrote down three two-figure numbers, one of which was double one of the others. Overall the three numbers used six consecutive digits between them. I then added up the three numbers to give a three-figure sum, and I also multiplied together the three numbers to give a five-figure product. Replacing digits consistently by letters my two answers, appropriately, were ADD and TIMES.

    What were the three original numbers?

    [teaser2872]

     
    • Jim Randell's avatar

      Jim Randell 9:11 am on 10 February 2022 Permalink | Reply

      If we suppose the three numbers are ab, cd, ef, and that 2 × ab = cd then we have:

      ab + cd + ef = ADD
      ab × cd × ef = TIMES

      And we can use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve the alphametic expressions.

      The following run file executes in 59ms (the generated program has an internal runtime of 575µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --symbols="ADEIMSTabcdef"
      --distinct="ADEIMST,abcdef"
      --answer="(ab, cd, ef)"
      
      # cd is twice ab
      "2 * ab = cd"
      
      # sum is ADD, product is TIMES
      "ab + cd + ef = ADD"
      "ab * cd * ef = TIMES"
      
      # a, b, c, d, e, f are 6 consecutive digits
      --code="is_consecutive = lambda *vs: all(y == x + 1 for (x, y) in tuples(ordered(*vs), 2))"
      "is_consecutive({a}, {b}, {c}, {d}, {e}, {f})"
      
      # [optional]
      --template="(ab + cd + ef = ADD) (ab * cd * ef = TIMES)"
      --solution=""
      

      Solution: The three original numbers are: 23, 46, 75.

      Like

      • Frits's avatar

        Frits 1:17 pm on 10 February 2022 Permalink | Reply

        Instead of the all(…) condition you can also use:

        "(s := sorted([{a}, {b}, {c}, {d}, {e}, {f}]))[-1] - s[0] == 5"
        

        or

        "max(s := [{a}, {b}, {c}, {d}, {e}, {f}]) - min(s) == 5"
        

        Like

    • GeoffR's avatar

      GeoffR 3:07 pm on 10 February 2022 Permalink | Reply

      Using Frits second suggestion proved useful in a MiniZinc solution.

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Digits forming three 2-digit numbers ab, cd, ef
      var 1..9:a; var 1..9:b; var 1..9:c; 
      var 1..9:d; var 1..9:e; var 1..9:f;
      
      constraint all_different([a, b, c, d, e, f]);
      
      % The three 2-digit numbers used six consecutive digits between them
      constraint max([a,b,c,d,e,f]) - min([a,b,c,d,e,f]) == 5;
      
      var 10..99:ab == 10*a + b;
      var 10..99:cd == 10*c + d;
      var 10..99:ef == 10*e + f;
      
      constraint cd == 2 * ab; 
      
      % Upper case letters
      var 1..9:A; var 0..9:D; var 1..9:T; var 1..9:I;
      var 0..9:M; var 0..9:E; var 0..9:S;
      constraint all_different([A, D, T, I, M, E, S]);
      
      % Upper case sum and multiplication values
      var 100..999:ADD = 100*A + 11*D;
      var 10000..99999:TIMES = 10000*T + 1000*I + 100*M + 10*E + S;
      
      constraint ab + cd + ef == ADD;
      constraint ab * cd * ef == TIMES;
      
      solve satisfy;
      output ["The three original numbers were " ++ show([ab, cd, ef]) ];
      
      % The three original numbers were [23, 46, 75]
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 11:42 am on 8 February 2022 Permalink | Reply
    Tags: by: Margery Elliott   

    Brain-Teaser 902: Square telephones 

    From The Sunday Times, 19th November 1978 [link]

    “I have discovered”, said Ian, “that if I square my five-figure telephone number, the square ends in the same four figures, in the same order, as the number itself, which, incidentally, does not begin with a zero”.

    “Funnily enough”, said James, “the same things apply to my five-figure telephone number, which is different from Ian’s”.

    “There are quite a few numbers which fulfil that condition”, I said. “Give me some more information”.

    “If I were to tell you”, said Ian, “how many of the digits 1 to 9 do not appear in the square of my number, you could deduce the number”.

    “And you could deduce mine, if were to tell you how many of the digits 1 to 9 do not occur in the square of my number”, added James.

    “Then I already know enough to deduce your numbers”, I said, but I am not able to say whose is which”.

    My number is divisible by 29″, said James.

    This last statement enabled me to deduce Ian’s and James’s telephone numbers.

    What are they?

    [teaser902]

     
    • Jim Randell's avatar

      Jim Randell 11:47 am on 8 February 2022 Permalink | Reply

      This Python program runs in 60ms.

      Run: [ @replit ]

      from enigma import irange, sq, nsplit, filter_unique, printf
      
      # find possible 5-digit phone numbers (no leading 0)
      # whose square ends with the last 4 digits of the original number
      ps = list(n for n in irange(10000, 99999) if sq(n) % 10000 == n % 10000)
      
      # look for numbers which can be deduced from the number of unused
      # digits 1-9 in the square of the number
      digits = set(irange(1, 9))
      fn = lambda n: len(digits.difference(nsplit(sq(n))))
      ns = filter_unique(ps, fn).unique
      
      # from the candidates: divisible by 29 = James, else Ian
      for n in ns:
        x = ("James" if n % 29 == 0 else "Ian")
        printf("{x} = {n}")
      

      Solution: Ian’s number is 69376. James’ number is 60001.


      69376² = 4813029376, which doesn’t use 5 (i.e. 1 of the digits 1-9).

      60001² = 3600120001, which doesn’t use 4, 5, 7, 8, 9 (i.e. 5 of the digits 1-9).

      Like

    • GeoffR's avatar

      GeoffR 4:26 pm on 9 February 2022 Permalink | Reply

      @Jim:
      It looks as though James(not Ian’s) number should be 60001?

      “My number is divisible by 29″, said James.

      >>> divmod(69376,29) = (2392, 8) – Ian’s number must be 69376

      >>> divmod(60001,29) = (2069, 0) – James’ number must be 60001

      Like

    • GeoffR's avatar

      GeoffR 11:01 am on 11 February 2022 Permalink | Reply

      
      from enigma import irange, sq, nsplit
      from collections import defaultdict
      
      Nums = defaultdict(list)
      
      # re-using Jim's code list of  phone numbers 
      # find possible 5-digit phone numbers (no leading 0)
      # whose square ends with the last 4 digits of the original number
      ps = list(n for n in irange(10000, 99999) if sq(n) % 10000 == n % 10000)
       
      # digits 1-9 in the square of the number
      digits = set(irange(1, 9))
      
      # form a dictionary, indexing phone numbers by unused digits
      for p in ps:
        ud = len(digits.difference(nsplit(sq(p))))
        Nums[ud].append(p)
      
      # counter for single phone numbers in the dictionary
      cnt = 0
      for k, v in Nums.items():
        if len(v) == 1:
          cnt += 1
      
      # find the two phone numbers for James and Ian
      if cnt == 2:
        for k, v in Nums.items():
          if len(v) == 1:
            if v[0] % 29 != 0:
              print(f"Ian's phone number = {v[0]}")
            elif v[0] % 29 == 0:
              print(f"James' phone number = {v[0]}")
            
      # James' phone number = 60001
      # Ian's phone number = 69376
      
      

      Like

  • Unknown's avatar

    Jim Randell 4:15 pm on 4 February 2022 Permalink | Reply
    Tags:   

    Teaser 3098: Wonky dice 

    From The Sunday Times, 6th February 2022 [link] [link]

    I have two octahedral (eight-faced) dice. There are positive whole numbers (not necessarily all different) on each of their faces. I throw the two dice and add the two numbers that are showing on the top faces. The probability of a total of 2 is 1/64, the probability of a total of 3 is 2/64 and so on. The probabilities of the totals are the same as for a pair of standard octahedral dice (each numbered 1 to 8). Interestingly, however, the highest number on the first die is 11.

    What are the eight numbers (in ascending order) on the second die?

    [teaser3098]

     
    • Jim Randell's avatar

      Jim Randell 4:32 pm on 4 February 2022 Permalink | Reply

      (See also: Enigma 382, Enigma 646b).

      Using the routines I wrote for Enigma 382 we can find pairs of non-standard octahedral dice that score the same as a standard pair. (The octahedral equivalent of the Sicherman dice [@wikipedia]).

      It’s not quick though. The following Python program runs in 30s.

      from enigma import printf
      from dice import (throws, dice)
      
      # a standard pair of octahedral dice
      d = (1, 2, 3, 4, 5, 6, 7, 8)
      standard = throws(d, d)
      
      # find equivalent non-standard pairs 
      for (d1, d2) in dice(8, set(standard), s=1, k=0):
        if not (d1[-1] == 11 or d2[-1] == 11): continue
        if throws(d1, d2) == standard:
          printf("{d1} + {d2}")
      

      Solution: The numbers on the second die are: 1, 2, 2, 3, 3, 4, 4, 5.

      Without the stipulation that one of the dice has a score of 11 on it there are 3 non-standard pairs that give the same distribution of scores as a standard pair:

      (1, 2, 2, 3, 5, 6, 6, 7) + (1, 3, 3, 5, 5, 7, 7, 9)
      (1, 2, 3, 3, 4, 4, 5, 6) + (1, 2, 5, 5, 6, 6, 9, 10)
      (1, 2, 2, 3, 3, 4, 4, 5) + (1, 3, 5, 5, 7, 7, 9, 11)

      The solution is given by the final pair of dice.

      Like

      • Jim Randell's avatar

        Jim Randell 7:34 pm on 4 February 2022 Permalink | Reply

        With a bit of analysis we can get a much faster program.

        The only way to throw 2 is 1 + 1, and for the distribution to be the same as a standard dice, there can only be one 1 on each die.

        One die has an 11 on it, which means the maximum possible for the other die is 5. (And there can only be one each of these).

        So the dice are:

        (1, (2-10)×6, 11) + (1, (2-4)×6, 5)

        Limiting the dice considered to just these possibilities gives a faster program.

        The following Python program runs in 98ms.

        Run: [ @replit ]

        from enigma import (irange, multiset, printf)
        
        # generate possible throws from a pair of dice
        def throws(d1, d2, fn=multiset, op=lambda a, b: a + b):
          return fn(op(a, b) for a in d1 for b in d2)
        
        # a standard pair of octahedral dice
        d = (1, 2, 3, 4, 5, 6, 7, 8)
        standard = throws(d, d)
        
        # add faces to dice d1 and d2
        # n = number of faces on each die
        # d1 = dice 1 (so far)
        # d1_min, d1_max = min/max values for d1
        # d2 = dice 2 (so far)
        # d2_min, d2_max = min/max values for d2
        # ts = target throws
        def solve(n, d1, d1_min, d1_max, d2, d2_min, d2_max, ts):
          if d1.size() == 8:
            if d2.size() == 8:
              # check throws
              if throws(d2, d1) == ts:
                yield (d2, d1)
            else:
              # swap dice over
              yield from solve(n, d2, d2_min, d2_max, d1, d1_min, d1_max, ts)
          else:
            # add face to d1
            for x in irange(d1_min, d1_max):
              d1_ = d1.copy().add(x)
              if throws(d1_, d2).issubset(ts):
                yield from solve(n, d1_, x, d1_max, d2, d2_min, d2_max, ts)
            
        # find equivalent non-standard pairs
        d1 = multiset.from_seq([1, 5])
        d2 = multiset.from_seq([1, 11])
        for (d1, d2) in solve(8, d1, 2, 4, d2, 2, 10, standard):
          printf("{d1} + {d2}", d1=sorted(d1), d2=sorted(d2))
        

        Like

        • Frits's avatar

          Frits 5:43 pm on 10 July 2023 Permalink | Reply

          @Jim,

          Unfortunately your variable d1_min is fixed. If you could set d1_min to the second highest value (with a minimum of 2) the program would be more efficient. A similar change to a recursive program on PuzzlingInPython [https://brg.me.uk/?p=7660#comment-3853] resulted in a 9 times faster program.

          Like

      • Jim Randell's avatar

        Jim Randell 10:47 am on 5 February 2022 Permalink | Reply

        The Mathematical justification section on the Wikipedia page for Sicherman dice [link], shows how to construct the factorisation of the generating polynomial for an n-sided standard die using cyclotomic polynomials [link].

        We can then look at non-standard ways to combine these factors into pairs of polynomials, such that each polynomial represents a valid 8-sided die, and their product represents the throws of a standard pair of dice. And we are interested in the case where the largest number on one of those dice is 11.

        Here is a routine that generates the nth cyclotomic polynomial, using the [[ Polynomial ]] class from the enigma.py library. (Although for this puzzle it is sufficient to find the cyclotomic polynomials for n = 2, 4, 8, which are all powers of a prime, and so easily determined).

        from enigma import (Polynomial, prime_factor, multiples, irange)
        
        # return the nth cyclotomic polynomial (can be @cached)
        def cyclotomic(n):
          if n < 1: return None
          if n == 1: return Polynomial([-1, 1]) # x - 1
          fs = list(prime_factor(n))
          if len(fs) == 1:
            (p, e) = fs[0]
            if e == 1: return Polynomial([1] * n) # prime
            # power of a prime
            q = n // p
            return Polynomial.from_pairs((k * q, 1) for k in irange(0, p - 1))
          else:
            p = Polynomial.from_pairs([(0, -1), (n, 1)]) # x^n - 1
            for d in multiples(fs):
              if d < n:
                (p, r) = p.divmod(cyclotomic(d))
            return p
        

        We can then use this to write a routine that generates pairs of dice that have the same distribution of throws as a pair of standard dice. (For 6-sided dice, the non-standard pair is known as the Sicherman dice):

        from enigma import (Polynomial, multiset, divisors, subsets, printf)
        
        # find pairs of n-sided dice with the same distribution as a standard pair
        def sicherman(n):
        
          # polynomial p(x) = x must be present on all dice
          x = Polynomial([0, 1])
        
          # make a die from a collection of polynomial (factor, multiplicity) pairs
          def die(ps):
            fs = multiset.from_pairs(ps)
            p = x * Polynomial.multiply(fs)
            if sum(p) == n and all(c >= 0 for c in p):
              return tuple(sorted(multiset.from_pairs(p.to_pairs())))
        
          # find the remaining factors of the generating polynomial of a standard die
          fs = list(cyclotomic(d) for d in divisors(n) if d > 1)
        
          # choose 0, 1, 2 copies of each factor
          for ms in subsets((0, 1, 2), size=len(fs), select="M"):
            # make the first die
            d1 = die(zip(fs, ms))
            if d1:
              # make the second die
              d2 = die((f, 2 - m) for (f, m) in zip(fs, ms))
              if d2 and not (d1 > d2):
                # return the pair
                yield (d1, d2)
        

        And finally we can solve the puzzle, by looking for a pair of 8-sided dice, with the same throw distribution as a pair of standard dice, but where one of the dice has a largest value of 11.

        This Python program runs in 47ms (internal run time is just 883µs).

        Run: [ @replit ]

        # solve the puzzle
        for (d1, d2) in sicherman(8):
          if d1[-1] == 11 or d2[-1] == 11:
            printf("{d1} + {d2}")
        

        Like

      • Frits's avatar

        Frits 11:53 am on 10 July 2023 Permalink | Reply

        @Jim,

        Where can I find your dice package (except as separate functions in Enigma 382) ?

        Like

    • A.T.Surherland's avatar

      A.T.Surherland 9:34 am on 16 February 2022 Permalink | Reply

      Refer to a paper by Duane M Bourne,Auburn University,Auburn on the above subject.
      This paper lists the octahedral answer plus other configurations..

      Like

    • Frits's avatar

      Frits 12:45 pm on 13 July 2023 Permalink | Reply

      Jim mentioned 3 non-standard pairs that give the same distribution of scores as a standard pair. These have symmetrical properties. The (1, 8) ,(2,7), (3,6) and (4,5) positional numbers of dice 1 and 2 all add up to 18.

      The following program tries to limit unnecessary work by controlling the range of each number on a die.

       
      from itertools import product
      
      # check die (1, (2-10)×6, 11) + (1, (2-4)×6, 5) 
      
      # chances            2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
      # for standard dice  1, 2, 3, 4, 5, 6, 7, 8,  7,  6 , 5,  4,  3,  2 , 1
      N, E1 = 8, 11
      E2 = 2 * N - E1
      
      # chance dictionary
      d = {i: N - abs(i - N - 1) for i in range(2, 2 * N + 1)}
      
      # check for standard chance distribution
      def check(s1, s2):
        cd = [0] * (2 * N + 1)
        for m in s1:
          for n in s2:
            if cd[(mn := m + n)] >= d[mn]:
              return False
            cd[mn] += 1
        return True     
      
      # minima range ([2, 2, 3, 3, 3, 4])  
      mn1 = sum([[i] * i for i in range(2, 5)], [])[:N - 2]
      mn2 = sum([[i] * i for i in range(2, 5)], [])[:N - 2]
      # maxima range ([8, 9, 9, 9, 10, 10] and [2, 3, 3, 3, 4, 4])
      mx1 = sum([[i] * (E1 - i + 1) for i in range(E1 - 3, E1)], [])[2 - N:]
      mx2 = sum([[i] * (E2 - i + 1) for i in range(E2 - 3, E2)], [])[2 - N:]
      
      # try to further lower the maxima for 1st die
      # total sum of numbers on both dice is 2 * tri(N)
      # minimal sum of 2nd die is 1 + E2 + sum(mn2) = 23
      # maximal value of 2nd digit is (N * (N + 1) - 23 - 1 - E1) / 6 = 6.17
      # maximal value of 3rd digit is (N * (N + 1) - 23 - 12 - 2) / 5 = 7
      # maximal value of 4th digit is (N * (N + 1) - 23 - 12 - 2 - 2) / 4 = 8.x
      # maximal value of 5th digit is (N * (N + 1) - 23 - 12 - 2 - 2 - 3) / 3 = 10
      mx = N * (N + 1) - (sum(mn2) + 1 + E2 + 1 + E1) 
      mx1 = [min((mx - sum(mn2[:i])) // (N - 2 - i), mx1[i]) for i in range(6)]
      
      # die 1: [A, B, C, D, E, F, H]
      A, H = 1, E1
      # product can be used instead of 6 separate loops as 2nd die ranges are compact
      # (and don't allow for decreasing variables)
      for n6 in product(*[range(mn2[i], mx2[i] + 1) for i in range(6)]):
        die2 = (1, ) + n6 + (E2, )
        sum2 = sum(die2)
        # skip if B gets too high (total numbers on dice will exceed N * (N + 1))
        m = (rB := N * (N + 1) - sum([A, H]) - sum2) // 6
        for B in range(mn1[0], min(m, mx1[0]) + 1):
          if not check([A, B, H], die2): continue
          for C in range(B, min((rC := rB - B) // 5, mx1[1]) + 1):
            if not check([A, B, C, H], die2): continue
            for D in range(max(mn1[2], C), min((rD := rC - C) // 4, mx1[2]) + 1):
              if not check([A, B, C, D, H], die2): continue
              for E in range(max(mn1[3], D), min((rD - D) // 3, mx1[3]) + 1):
                if not check([A, B, C, D, E, H], die2): continue
                for F in range(E, mx1[4] + 1):
                  G = N * (N + 1) - sum([A, B, C, D, E, F, H]) - sum2
                  if not F <= G < H: continue
                  if not check([A, B, C, D, E, F, G, H], die2): continue
                  print("answer:", sorted(die2))  
      

      Like

  • Unknown's avatar

    Jim Randell 10:45 am on 3 February 2022 Permalink | Reply
    Tags:   

    Teaser 2861: Fond memories 

    From The Sunday Times, 23rd July 2017 [link] [link]

    One of my memories of my grandparents’ house is of an ornament consisting of three monkeys with the caption “See no evil, hear no evil, speak no evil”.

    I have written down four odd numbers, one of them being the sum of the other three. Then I have consistently replaced digits with letters, using different letters for different digits. In this way the four numbers have become:

    SPEAK
    HEAR
    SEE
    EVIL

    What number is represented by SPEAK?

    [teaser2861]

     
    • Jim Randell's avatar

      Jim Randell 10:47 am on 3 February 2022 Permalink | Reply

      E, K, L, R must be odd digits, and SPEAK must be the result of the sum.

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve the puzzle.

      The following run file executes in 128ms.

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # the alphametic sum
      "SEE + HEAR + EVIL = SPEAK"
      
      # the numbers are all odd, so E, K, L, R are odd digits
      "E % 2 = 1"
      "R % 2 = 1"
      "L % 2 = 1"
      "K % 2 = 1"
      
      --answer="SPEAK"
      

      Solution: SPEAK = 12507.

      There are 2 ways to arrive at the solution:

      155 + 6503 + 5849 = 12507
      155 + 6509 + 5843 = 12507

      The values of L and R can be swapped. They are 3 and 9 (in some order).

      Like

    • GeoffR's avatar

      GeoffR 8:35 pm on 3 February 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:S; var 0..9:P; var 1..9:E;  var 0..9:A;  var 0..9:K;
      var 1..9:H; var 0..9:R; var 0..9:V;  var 0..9:I;  var 0..9:L;
      
      constraint all_different ([S,P,E,A,K,H,R,V,I,L]);
      
      var 10000..99999: SPEAK == 10000*S + 1000*P + 100*E + 10*A + K;
      var 1000..9999: HEAR == 1000*H + 100*E + 10*A + R;
      var 1000..9999: EVIL == 1000*E + 100*V + 10*I + L;
      var 100..999: SEE == 100*S + 11*E;
      
      constraint SPEAK == HEAR + SEE + EVIL;
      
      % Confirm four numbers are odd
      constraint SPEAK mod 2 == 1 /\ HEAR mod 2 == 1
      /\ SEE mod 2 == 1 /\ EVIL mod 2 == 1;
      
      solve satisfy;
      
      output ["HEAR = " ++ show(HEAR) ++ ", SEE = " ++ show(SEE) ++ 
      ", EVIL = " ++ show(EVIL) ++ ", SPEAK = " ++ show(SPEAK)];
      
      % HEAR = 6509, SEE = 155, EVIL = 5843, SPEAK = 12507
      % ----------
      % HEAR = 6503, SEE = 155, EVIL = 5849, SPEAK = 12507
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:54 am on 1 February 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 652: Stumped! 

    From The Sunday Times, 6th January 1974 [link] [link]

    The scoreboard at our local cricket ground shows the total number of runs scored, the number of wickets fallen and the scores of the two batsmen in at the time.

    At the match last Sunday we declared at 500 for 8 wickets, when our first man in had just reached 100 runs (I was the other opener and was already out for 20). The seventh batsman had also scored a century, and there were no ‘extras’ at all in the match (i.e. all the runs were scored by the batsmen).

    Some time after my dismissal I noticed that the scoreboard showed eight similar digits (e.g. 999 for 9, both men in having scored 99 each) and later the board showed eight consecutive digits in ascending order, though with 0 following 9 (e.g. 678 for 9; batsmen with 01 and 23).

    What were the two scores I had seen?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981). The puzzle text above is taken from the book.

    [teaser652]

     
    • Jim Randell's avatar

      Jim Randell 8:56 am on 1 February 2022 Permalink | Reply

      The innings was declared 500 for 8, so the options are:

      For scores greater between 20 and 500 we have:

      444 for 4, (44 and 44)
      333 for 3, (33 and 33)
      222 for 2, (22 and 22)
      111 for 1, (11 and 11)

      The last of these is not possible. If only one batsman has been dismissed, it is the setter for 20, so the total score would be 20 + 11 + 11 = 42, not 111.

      The second time the scoreboard could be showing (assuming the “in” scores can have a leading zero):

      456 for 7, (89 and 01)
      345 for 6, (78 and 90)
      234 for 5, (67 and 89)
      123 for 4, (56 and 78)

      Again the last of these is not possible, as the sum of the scores of the “in” batsmen is more than the total score.

      So we have the following potential observations:

      444 → 456
      333 → 345, 456
      222 → 234, 345, 456

      But some of these transitions are not possible.

      222 → 234 – only 12 more runs have been scored but the “in” scores have increased by 45 and 67 = 112
      222 → 345 – only 123 more runs have been scored, but the “in” scores have increased by 56 and 68 = 124
      333 → 345 – only 12 more runs have been scored, but the “in” scores have increased by 45 and 57 = 102
      444 → 456 – only 12 more runs have been scored, but one of the “in” scores has increased by 45

      Which leaves only the following transitions:

      333 → 456
      222 → 456

      Consider the “333 for 3” → “456 for 7” transition:

      Initially the score is “333 for 3”, and the “in” batsmen have both scored 33. So the 3 dismissed batsmen must have scored 267 between them. We know one of these is the setter, who scored 20. Which leaves 247 to be scored between the remaining 2 dismissed batsmen. We’ll distribute the runs as 123 and 124 run to batsmen 3 and 4:

      1: 33 (not out)
      2: 20 (out, setter)
      3: 123 (out)
      4: 124 (out)
      5: 33 (not out)

      By the time we get to “456 for 7”, the first batsman must have increased his score to 89, and the remaining “in” batsman has a score of 1 (let’s say this is batsman 9):

      1: 89 (not out)
      2: 20 (out, setter)
      3: 123 (out)
      4: 124 (out)
      5: ≥ 33 (out)
      6: ? (out)
      7: ≥100 (out)
      8: ? (out)
      9: 1 (not out)

      But these already sum to 490, which is more than 456, so this situation is not possible.

      Consider the “222 for 2” → “456 for 7” transition:

      Initially the score is “222 for 2”, and the “in” batsmen have both scored 22. So the 2 dismissed batsmen must have scored 178 between them, and we know one of them is the setter, who scored 20. So the other must have scored 158 (let’s say that was the 3rd batsman):

      1: 22 (not out)
      2: 20 (out, setter)
      3: 158 (out)
      4: 22 (not out)

      By the time we get to “456 for 7” the 1st batsman must have increased his score to 89, and the remaining “in” batsman has a score of 1 (let’s say this is batsman 9):

      1: 89 (not out)
      2: 20 (out, setter)
      3: 158 (out)
      4: ≥22 (out)
      5: ? (out)
      6: ? (out)
      7: ≥100 (out)
      8: ? (out)
      9: 1 (not out)

      This leaves 66 runs to distribute between 4, 5, 6, 7, 8. Which is possible.

      So this is the only possible situation.

      Solution: The two scores are: “222 for 2, (22 and 22)” and: “456 for 7, (89 and 01)”.

      Like

  • Unknown's avatar

    Jim Randell 9:57 am on 30 January 2022 Permalink | Reply
    Tags: by: Mr. Alwyn Hazel   

    Brain Teaser: Four fours 

    From The Sunday Times, 25th December 1949 [link]

    Many readers will recall the problem of constructing the integers each by means of four fours, with the usual mathematical signs. For example:

    17 = (4×4) + 4/4
    71 = (4! + 4.4)/.4

    Competitors are invited to submit a continuous series of integers so constructed, beginning with 1 and proceeding, without gaps, as far as possible.

    A follow-up was posted on 1st January 1950, to elaborate on the allowed constructions (although it is not as explicit as it could have been): [link]

    The problem set was that of constructing a continuous series of integers each by means of four fours, “with the usual mathematical signs”. Several readers have asked for a closer definition of the phrase quoted.

    It covers all signs (not themselves letters or numbers other than 4) which “every schoolboy knows”. It therefore includes: addition, subtraction, multiplication and division signs, square roots (and fourth roots), fourth powers, factorial signs, decimal points and recurring points, and combinations of these. It does not include gamma functions, logs or anti-logs, permutations or combinations, or trigonometrical functions. Nor does it include (as some hopeful readers have permitted themselves to believe) the use of square brackets to indicate “the integral part of”.

    This one of the occasional Holiday Brain Teasers published in The Sunday Times prior to the start of numbered Teasers in 1961. A prize of 5 guineas was offered for the longest list.

    [teaser-1949-12-25] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 9:59 am on 30 January 2022 Permalink | Reply

      This is a puzzle that has appeared with many variations, see [ @wikipedia ].

      I limited my program to dealing with rational calculations where both the numerator and denominator are less than 1,000,000,000,000.

      Using the five 2-argument operators +, −, ×, ÷, ^, along with 1-argument functions sqrt(x) and fact(n), I found it was possible to construct the integers from 0 to 112.

      But then we reach an impasse at 113 (and: 157, 158, 161, 163, 166, 167, 171, 173, 185, 187, 191, 193, 197, 199, …).

      So it would be reasonable to suppose that a list of constructions from 1 to 112 would provide the required solution.

      However, in the solution published in The Sunday Times [ link ] a possible construction for 113 is given:

      113\; =\; \frac{4!}{.\dot{\left( \sqrt{4} \right)}}\; +\; \frac{\sqrt{4}}{.4}

      In which the literal construction .(123)… is turned into an operator.

      The first denominator is interpreted as:

      .(√4)… = .(2)… = 2/9

      giving:

      (4! / .(√4)…) + (√4 / .4)
      = 24/(2/9) + 2/(4/10)
      = 108 + 5
      = 113

      And 157 is apparently constructed as:

      157 = \frac{\sqrt{.\dot{4}}\; + \left( .4\; -\;  .\left( .\sqrt{4} \right) \right)}{.\left( .\sqrt{.\dot{4}}\right)}

      which I don’t understand. (Although it is possible I have mis-transcribed it from the image in The Sunday Times digital archive).

      A prize was awarded for construction of numbers in this fashion up to 1041.

      But if we can co-opt the construction of recurring decimals, why not co-opt the literal concatenation operation (on integers) so:

      12 : 3 = 123

      In that way 113 can be constructed as follows:

      ((√4 : 4!) + √4) / √4
      = ((2 : 24) + 2) / 2
      = (224 + 2) / 2
      = 226 / 2
      = 113

      But it doesn’t help with 157.

      Alternatively if sq(x) is allowed as the square of a number (the inverse of sqrt(x)) we can have:

      sq(4) + sq(4) + sq(4 / .(4)…)
      = 16 + 16 + sq(4/(4/9))
      = 32 + sq(9)
      = 32 + 81
      = 113

      (sq(fact(4)) + sq(44)) / sq(4)
      = (sq(24) + 1936) / 16
      = (576 + 1936) / 16
      = 2512 / 16
      = 157

      But allowing sq(x) only gets us to 306.

      Another alternative (which is sanctioned by the published solution, even though it was not explicitly mentioned in the clarification) is to allow the tri(n) function which gives the nth triangular number (as an analogy to fact(n) giving the n factorial):

      fact(n) = 1 × 2 × … × n
      tri(n) = 1 + 2 + … + n

      This allows us to construct 113 and 157:

      tri(4) × tri(4) + tri(4) + tri(√4)
      = 10 × 10 + 10 + 3
      = 113

      tri(4) × tri(4) + tri(tri(4)) + √4
      = 10 × 10 + tri(10) + 2
      = 100 + 55 + 2
      = 157

      And using tri(n) lets us get all the way to 3526. (I did not find constructions for 3527, 3707, 3947, 3989, 5164).

      A prize was also awarded to a list (using tri()) up to 2338.

      I used the following operations:

      + (addition)
      − (subtraction)
      × (multiplication)
      ÷ (division)
      ^ (general powers, not just fourth powers)
      sqrt(x) (square root (rational))
      fact(x) (factorial (integer))
      tri(x) (triangular numbers (integer))

      I didn’t implement fourth roots (which presumably use up one of the 4 digits), or general roots, as they didn’t seem to be useful when I experimented with them, and I didn’t allow the co-opting of literal construction methods.

      Using these operators I got:

      0 = 44 − 44
      1 = 44 / 44

      1041 = tri(44) + tri(tri(4)) − 4
      1042 = tri(44) + tri(tri(4)) − tri(√4)

      2338 = tri(4) + tri(4 × fact(4)) / √4
      2339 = tri(4) + tri(tri(4 × 4)) / 4

      3526 = tri(tri(4)) × (4 ^ tri(√4)) + fact(tri(√4))
      3527 = ???

      And it turns out we can manage all of these without needing to use the recurring literals at all (if we are allowing tri()).


      The following program produces constructions (without using tri()) for values up to 112. Although it displays the first construction found for each number, which is not necessarily the most aesthetically pleasing construction.

      It runs in 2.4s, and can be adapted to include tri() (and to remove recurring literals) to give constructions for numbers up to 3526 (in a few minutes).

      Run: [ @replit ]

      from enigma import (Rational, irange, product, is_square, factorial, tri, sprintf as f, number, arg, printf)
      
      Q = Rational()
      
      # possible literals using 1 - 4 digits
      # <number of 4's used> -> { (<value> -> <repr>, ... }
      lits = {
        1: {
          Q(4, 1): "4",
          Q(4, 10): ".4",
          Q(4, 9): ".(4)...",
        },
        2: {
          Q(44, 1): "44",
          Q(44, 10): "4.4",
          Q(44, 100): ".44",
          Q(40, 9): "4.(4)...",
          Q(4, 9): ".4(4)...",
        },
        3: {
          Q(444, 1): "444",
          Q(444, 10): "44.4",
          Q(444, 100): "4.44",
          Q(444, 1000): ".444",
          Q(400, 9): "44.(4)...",
          Q(40, 9): "4.4(4)...",
          Q(4, 9): ".44(4)...",
        },
        4: {
          Q(4444, 1): "4444",
          Q(4444, 10): "444.4",
          Q(4444, 100): "44.4",
          Q(4444, 1000): "4.444",
          Q(4444, 10000): ".4444",
          Q(4000, 9): "444.(4)...",
          Q(400, 9): "44.4(4)...",
          Q(40, 9): "4.44(4)...",
          Q(4, 9): ".444(4)...",
        }
      }
      
      M = number("1_000_000_000_000")
      def check(v): return (v if v.denominator < M and -M < v.numerator < M else None)
      
      def add(a, b): return (check(a[0] + b[0]), f("({a[1]}) + ({b[1]})"))
      def sub(a, b): return (check(a[0] - b[0]), f("({a[1]}) - ({b[1]})"))
      def mul(a, b): return (check(a[0] * b[0]), f("({a[1]}) * ({b[1]})"))
      def div(a, b): return (check(a[0] / b[0]), f("({a[1]}) / ({b[1]})"))
      
      def sqrt_(a):
        (v, r) = a
        p = is_square(v.numerator)
        if p is not None:
          q = is_square(v.denominator)
          if q is not None:
            return (Q(p, q), f("sqrt({r})"))
        return (None, None)
      
      def fact_(a):
        (v, r) = a
        if v.denominator == 1 and 0 <= v.numerator < 100:
          x = factorial(v.numerator)
          if x < M:
            return (Q(x, 1), f("fact({r})"))
        return (None, None)
      
      def pow_(a, b):
        if b[0].denominator == 1 and 0 <= b[0].numerator <= 10:
          x = a[0] ** b[0].numerator
          if x < M:
            return (Q(x, 1), f("({a[1]}) ^ ({b[1]})"))
        return (None, None)
      
      def tri_(a):
        (v, r) = a
        if v.denominator == 1 and v.numerator >= 0:
          x = tri(v.numerator)
          if x < M:
            return (Q(x, 1), f("tri({r})"))
        return (None, None)
      
      # operators
      ops = {
        # unary
        1: {
          'sqrt': sqrt_,
          'fact': fact_,
          #'tri': tri_,
        },
        # binary
        2: {
          'add': add,
          'sub': sub,
          'mul': mul,
          'div': div,
          'pow': pow_,
        },
      }
      
      # values to skip
      skip = set()
      
      
      # values to skip
      skip = set()
      skip.update({113, 157, 158, 161, 163, 166, 167, 171, 173, 185, 187, 191, 193, 197, 199}) # up to 200 without tri()
      #skip.update({3527, 3707, 3947, 3989, 5164}) # with tri
      
      # target values to print
      tgt = arg(None, 0, Q)
      
      # number of 4's to use in constructions (<= 4)
      N = 4
      
      # find values using 4 digits
      # <m> is first missing number
      def solve(m=0):
        n = 0  # number of operations
      
        # start with the literals
        (V, upd) = (lits, None)
      
        while True:
          printf("[{n} iterations]")
          n += 1
          while True:
            if m in skip:
              v = "<skip>"
            else:
              v = V[N].get(m) 
              if v is None: break
            if tgt is None: printf("{m} = {v}")
            m += 1
          printf("[first missing = {m}]\n")
          if n > 1 and not upd: return
      
          # apply the operations to values in V
          upd = dict()
      
          # consider unary operations
          for (_, fn) in ops[1].items():
            for k in irange(1, N):
              for v in V[k].items():
                try:
                  (v_, r_) = fn(v)
                  if v_ is None: continue
                except ArithmeticError:
                  continue
                # record new values
                if k == 4 and v_ == tgt: printf("{tgt} = {r_}")
                if v_ not in V[k] and (k, v_) not in upd: upd[(k, v_)] = r_
      
          # consider binary operations
          for (_, fn) in ops[2].items():
            # combinations of up to 4 digits
            for (a, b) in [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (3, 1)]:
              k = a + b
              if k > N: continue
              for (va, vb) in product(V[a].items(), V[b].items()):
                try:
                  (v_, r_) = fn(va, vb)
                  if v_ is None: continue
                except ArithmeticError:
                  continue
                if k == 4 and v_ == tgt: printf("{tgt} = {r_}")
                # record new values
                if v_ not in V[k] and (k, v_) not in upd: upd[(k, v_)] = r_
      
          # apply the updates
          for ((k, v), r) in upd.items(): V[k][v] = r
      
      
      solve()
      

      Like

      • Jim Randell's avatar

        Jim Randell 1:53 pm on 30 January 2022 Permalink | Reply

        Although the clarification specifically excludes an “integer part of” function, if we do include it (as int()) we can get up to 23898.

        113 = int(((4 ^ 4) / fact(4)) ^ sqrt(4))

        157 = int(fact(4 + 4) / (4 ^ 4))

        3527 = int(tri((tri(4) + tri(4)) ^ 4) / fact(tri(4)))

        3707 = int((tri(tri(fact(4))) × sqrt(4) / fact(4)) − tri(tri(4)))

        3947 = int(tri(tri(tri(tri(4)))) / tri(fact(4)) − 4 − 4)

        3989 = int(sqrt(4) + (tri(tri(4)) + tri(tri(tri(4)))) / .4)

        5164 = int(tri(tri(tri(fact(4)))) / fact(tri(sqrt(4)) × tri(4 ^ 4)))

        23898 = tri(fact(tri(sqrt(4)))) + tri(tri(fact(4) + tri(sqrt(4)))) / tri(sqrt(4))
        23899 = ???

        Like

  • Unknown's avatar

    Jim Randell 4:25 pm on 28 January 2022 Permalink | Reply
    Tags:   

    Teaser 3097: Crazy golf 

    From The Sunday Times, 30th January 2022 [link] [link]

    Ian was trying to entertain John and Ken, his 2 young nephews, in the local park, which had a 9-hole crazy golf course. He decided to play a round with each of them separately, and gave them money for each hole he lost on a rising scale. He would pay £1 if he lost the first hole (or the first hole after winning one), then £2 if he lost the next consecutive hole, and £3 for the third, and so on.

    In the event, Ian won only 5 holes in total between the two rounds, including the first hole against John and the last hole against Ken. There were no ties. At the reckoning after both rounds, both boys received equal amounts of money.

    How much did it cost Uncle Ian?

    [teaser3097]

     
    • Jim Randell's avatar

      Jim Randell 4:47 pm on 28 January 2022 Permalink | Reply

      Each boy loses a hole at one end of the course, so we can just consider course with 8 holes, as we are only interested in groups of consecutive wins.

      This Python program runs in 47ms.

      Run: [ @replit ]

      from enigma import (irange, subsets, intersect, printf)
      
      # consider a course with 8 holes
      holes = irange(1, 8)
      
      # calculate the winnings for a nephew who wins holes <ws>
      def winnings(ws):
        (t, h) = (0, 0)
        for n in holes:
          if n in ws:
            h += 1
            t += h
          else:
            h = 0
        return t
      
      # choose 5 to 8 extra holes for J and K
      X = dict()
      for n in irange(5, 8):
        X[n] = set(winnings(ws) for ws in subsets(holes, size=n))
      
      # consider number of holes won by J
      for j in irange(5, 8):
        k = 13 - j
        # look for equal amounts
        for w in intersect([X[j], X[k]]):
          printf("I pays {t} [J wins {j}; K wins {k}]", t=w + w)
      

      Solution: Ian paid £32 to the boys.

      Ian lost to one of the boys on 5 consecutive holes and 1 other, paying (1 + 2 + 3 + 4 + 5) + 1 = £16.

      And to the other boy on 4 consecutive holes and another 3 consecutive holes, playing (1 + 2 + 3 + 4) + (1 + 2 + 3) = £16.

      So, in total Ian pays £32 for the 13 holes he lost.

      From 8 holes there are 6 ways that runs of 5 and 1 can be made:

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

      And there are 2 ways that runs of 4 and 3 can be made:

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

      So if John wins 5+1 and Ken wins 4+3 there are 12 possibilities.

      And if John wins 4+3 and Ken wins 5+1 there are also 12 possibilities.

      Giving 24 possible ways to achieve the required games.


      Manually, we see that the boys won 13 holes between them, so the possible splits are (8, 5) or (7, 6).

      Winning 8 holes would give a prize of T(8) which must be more than the winnings from 5 holes. So the splits are (7, 6).

      Possible consecutive runs for 7 (and corresponding prizes) are:

      (7) → T(7) = 28
      (1, 6) → T(1) + T(6) = 22
      (2, 5) → T(2) + T(5) = 18
      (3, 4) → T(3) + T(4) = 16

      Possible runs for 6 (and corresponding prizes) are:

      (6) → T(6) = 21
      (1, 5) → T(1) + T(5) = 16
      (2, 4) → T(2) + T(4) = 13
      (3, 3) → T(3) + T(3) = 12
      (1, 1, 4) → T(1) + T(1) + T(4) = 12
      (1, 2, 3) → T(1) + T(2) + T(3) = 10
      (2, 2, 2) → T(2) + T(2) + T(2) = 9

      The only overlap is for a prize of £16, with consecutive runs of (3, 4) and (1, 5).

      Like

      • Jim Randell's avatar

        Jim Randell 11:04 am on 29 January 2022 Permalink | Reply

        Here’s an alternative approach, which instead of generating the numbers of the holes won, finds possible runs of wins among the 8 holes.

        It is slightly faster than my original solution (above).

        Run: [ @replit ]

        from enigma import (irange, decompose, tri, intersect, printf)
        
        # generate possible runs of <n> wins for <k> holes
        def generate(n, k):
          # split n into j parts
          for j in irange(1, k + 1 - n):
            yield from decompose(n, j, sep=0)
        
        # consider winning runs totalling 5-8 holes on an 8 hole course
        X = dict()
        for n in irange(5, 8):
          X[n] = set(sum(tri(x) for x in ws) for ws in generate(n, 8))
        
        # consider number of holes won by J
        for j in irange(5, 8):
          k = 13 - j
          # look for equal winnings
          for w in intersect([X[j], X[k]]):
            printf("I pays {t} [J wins {j}; K wins {k}]", t=w + w)
        

        Like

  • Unknown's avatar

    Jim Randell 9:31 am on 27 January 2022 Permalink | Reply
    Tags: ,   

    Teaser 2879: Blood line 

    From The Sunday Times, 26th November 2017 [link] [link]

    Patients numbered 1 to 99 were waiting for a blood test. George’s and Martha’s numbers were consecutive. Patients were called in [a] random order to one of the available cubicles numbered 1 to 9. The nurse in each cubicle took a fixed whole number of minutes to do the test, but that time varied from cubicle to cubicle. All cubicles started at 10am and they all finished together before 5pm on the same day. Amazingly, it turned out that each patient’s cubicle number was the highest digit that divided into their patient number. George noted that, when he was called, the session had been running for a number of minutes whose digits [when] added to[gether gave] his cubicle number. Martha noted that the same was true for her.

    What were their patient numbers?

    Unfortunately this puzzle has multiple solutions (apparently introduced by the editing process for puzzles at the paper).

    This puzzle was not included in the published collection of puzzles The Sunday Times Brainteasers Book 1 (2019).

    [teaser2879]

     
    • Jim Randell's avatar

      Jim Randell 9:31 am on 27 January 2022 Permalink | Reply

      The following program finds 5 possible pairs of patient numbers that satisfy the conditions.

      We know which cubicle each patient is to visit, so we can form them into queues for that cubicle.

      The session lasts for less than 7 hours = 420 minutes, and the cubicles finish at the same time. So the total length of the session must be a multiple of the length of each queue. (And it turns out there is only one possible duration).

      We can now calculate the time taken for each cubicle, and look for cubicles that have elapsed call times with a digit sum that is the same as the cubicle number.

      Then we can look for consecutive patient numbers that are called to these cubicles, and these give us possible patient numbers for Martha and George.

      The following Python program runs in 50ms.

      Run: [ @replit ]

      from enigma import (irange, peek, group, call, mlcm, dsum, tuples, union, printf)
      
      # map patients to queue number
      ns = irange(1, 99)
      queue = lambda n: peek(d for d in irange(9, 1, step=-1) if n % d == 0)
      n2q = dict((n, queue(n)) for n in ns)
      
      # form the queues
      qs = group(ns, by=(lambda n: n2q[n]))
      
      # consider the total length of the session < 7h = 420m
      m = call(mlcm, map(len, qs.values()))
      for t in irange(m, 419, step=m):
        printf("duration = {t} mins")
      
        # find cubicles with elapsed call times that sum to their digit
        ks = set()
        for (k, vs) in qs.items():
          x = t // len(vs)
          for s in irange(x, t - x, step=x):
            if dsum(s) == k:
              printf("queue {k}; {x} mins/test; call at {s} mins")
              ks.add(k)
      
        # find consecutive numbers for patients in queues in ks
        for (a, b) in tuples(sorted(union(qs[k] for k in ks)), 2):
          if b == a + 1:
            printf("({a}, {b}) -> ({qa}, {qb})", qa=n2q[a], qb=n2q[b])
      

      Solution: The are 5 possible pairs of patient numbers for Martha and George: (26, 27), (44, 45), (45, 46), (62, 63), (81, 82).


      After constructing the queues for each of the digits 1-9, we see that the only possible duration of the session is 264 minutes, and there are 6 possible times when a patient could be called such that the digit sum of the elapsed number of minutes is the same as the cubicle number.

      called at 72 minutes to cubicle 9
      called at 110 minutes to cubicle 2
      called at 132 minutes to cubicle 6
      called at 144 minutes to cubicle 9
      called at 216 minutes to cubicle 9
      called at 220 minutes to cubicle 4

      This means that we can have the following consecutive numbers for George and Martha:

      26 & 27 → called to cubicle 2 (after 110 minutes) and cubicle 9 (after 72, 144, or 216 minutes)
      44 & 45 → called to cubicle 4 (after 220 minutes) and cubicle 9 (after 72, 144, or 216 minutes)
      45 & 46 → called to cubicle 9 (after 72, 144, or 216 minutes) and cubicle 2 (after 110 minutes)
      62 & 63 → called to cubicle 2 (after 110 minutes) and cubicle 9 (after 72, 144, or 216 minutes)
      81 & 82 → called to cubicle 9 (after 72, 144, or 216 minutes) and cubicle 2 (after 110 minutes)

      The additional constraint that “George and Martha were called less than half an hour apart” would narrow these down to a single solution:

      44 & 45 → called to cubicle 4 (after 220 minutes) and cubicle 9 (after 216 minutes)

      Like

  • Unknown's avatar

    Jim Randell 9:47 am on 25 January 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 904: The six ages 

    From The Sunday Times, 18th November 1979 [link]

    Problems concerning ages have always proved fruitful and entertaining exercises to both mathematicians and non-mathematicians. Trial and error methods, calculators and normal or esoteric mathematical techniques can all be deployed to find the correct solution. The most elegant or the most economical method is naturally the most commendable, but the correct solution, however obtained, is the desideratum.

    Our problem concerns six men whose ages are within the range 21 to 89 and any two of them differ by at least 9. If we take the two digits comprising each of the ages of three of the men, and reverse them, we obtain the ages of the other three men.

    What is more, if we take the sum of the ages of the first group, we find that it equals the sum of the ages of the second group of three.

    Also the sum of the squares of the three ages of the first group equals the sum of the squares of the ages of the second group of three.

    Finally, one of the ages in each group is exactly twice an age in the other group.

    What are the ages of the six men (in increasing order)?

    This puzzle appeared in the first issue of The Sunday Times to be published in nearly a year (after it was hit by industrial action). The previous issue having been published on 26th November 1978 (almost one year earlier).

    [teaser904]

     
    • Jim Randell's avatar

      Jim Randell 9:48 am on 25 January 2022 Permalink | Reply

      I used the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      The following run file executes in 66ms. (The internal run time of the generated program is 2.17ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      # suppose the ages are: AB CD EF; BA DC FE
      
      SubstitutedExpression
      
      --digits="2-8"
      
      # suppose AB CD EF are in order, and AB is the smallest of the 6 ages
      "AB < CD" "CD < EF"
      "AB < BA" "AB < DC" "AB < FE"
      
      # differences are all at least 9
      "all(abs(x - y) > 8 for (x, y) in subsets([AB, CD, EF, BA, DC, FE], size=2))"
      
      # sums of the 2 groups are equal
      "AB + CD + EF == BA + DC + FE"
      
      # sums of the squares of the 2 groups are equal
      "sumsq([AB, CD, EF]) == sumsq([BA, DC, FE])"
      
      # in each group one of the ages is twice one of the ages in the other group
      "any(2 * x in {BA, DC, FE} for x in {AB, CD, EF})"
      "any(2 * x in {AB, CD, EF} for x in {BA, DC, FE})"
      
      # answer is the ages in order
      --answer="ordered(AB, CD, EF, BA, DC, FE)"
      
      # [optional]
      --template="(AB CD EF) (BA DC FE)"
      --solution=""
      

      Solution: The ages are: 23, 32, 46, 64, 78, 87.

      The two sets being: (23, 64, 78) and (32, 46, 87).

      Like

      • Jim Randell's avatar

        Jim Randell 12:23 pm on 29 January 2022 Permalink | Reply

        Some observations allow us to write a program that runs even faster.

        Suppose the six ages are (AB, CD, EF) and (BA, DC, FE).

        Each of them is between 21 and 89. So A, C, E are digits from 2-8 (as they are tens digits in the first set), and so are B, D, F (as they are tens digits in the second set).

        And the digits must all be different as the numbers are all at least 9 apart (so we can’t have two numbers with the same tens digit).

        The sums of the two sets are equal:

        AB + CD + EF = BA + DC + FE
        A + C + E = B + D + F

        And the sums of the squares are equal:

        AB² + CD² + EF² = BA² + DC² + FE²
        A² + C² + E² = B² + D² + F²

        So we can start by looking for two disjoint sets of digits from 2-8 with the same “sum of squares” value and also the same sum. (It turns out there is only one such set).

        We can then pair up the digits into two sets of ages, where each set contains an age that is twice one of the ages in the other set.

        This Python program runs in 50ms (with an internal runtime of just 246µs ).

        from enigma import (irange, subsets, group, sumsq, nconcat, tuples, printf)
        
        # group sets of 3 digits by the sum of their squares
        d = group(subsets(irange(2, 8), size=3), by=sumsq)
        
        # look for two disjoint sets with the same sum
        for vs in d.values():
          for (s1, s2) in subsets(vs, size=2):
            if not (sum(s1) == sum(s2) and len(set(s1 + s2)) == 6): continue
        
            # form the numbers
            (A, C, E) = s1
            for (B, D, F) in subsets(s2, size=3, select="P"):
              ns1 = set(map(nconcat, [(A, B), (C, D), (E, F)]))
              ns2 = set(map(nconcat, [(B, A), (D, C), (F, E)]))
        
              # check each set contains an element that is double one of the others
              if not any(2 * x in ns2 for x in ns1): continue
              if not any(2 * x in ns1 for x in ns2): continue
        
              # construct the answer
              ns = sorted(ns1.union(ns2))
              # check no numbers are within 9 of each other
              if any(b - a < 9 for (a, b) in tuples(ns, 2)): continue
        
              printf("{ns} [{ns1} + {ns2}]", ns1=sorted(ns1), ns2=sorted(ns2))
        

        Like

    • GeoffR's avatar

      GeoffR 3:35 pm on 25 January 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:A; var 1..9:B; var 1..9:C; 
      var 1..9:D; var 1..9:E; var 1..9:F;
      
      % Digits for two groups of ages
      constraint all_different([A, B, C, D, E, F]);
      
      % First group
      var 21..89: AB = 10*A + B;
      var 21..89: CD = 10*C + D;
      var 21..89: EF = 10*E + F;
      
      % Second group - ages reverse of 1st group
      var 21..89: BA = 10*B + A;
      var 21..89: DC = 10*D + C;
      var 21..89: FE = 10*F + E;
      
      % Order ages
      constraint BA > AB /\ AB < CD /\ CD < EF
      /\ BA < DC /\ DC < FE; 
      
      % Any two of the ages differ by at least 9
      constraint 
         abs(AB-CD) >=9 /\ abs(AB-EF) >=9 /\ abs(AB-BA) >=9
      /\ abs(AB-DC) >=9 /\ abs(AB-FE) >=9 /\ abs(CD-EF) >=9 
      /\ abs(CD-BA) >=9 /\ abs(CD-DC) >=9 /\ abs(CD-FE) >=9
      /\ abs(EF-BA) >=9 /\ abs(EF-DC) >=9 /\ abs(EF-FE) >=9
      /\ abs(BA-DC) >=9 /\ abs(BA-FE) >=9 /\ abs(DC-FE) >=9;
      
      % Sum of ages of the first group = sum of ages of the second group
      constraint AB + CD + EF == BA + DC + FE;
      
      % Sum of the squares of the three ages of the first group 
      % equals sum of the squares of the three ages of the second group
      constraint AB*AB + CD*CD + EF*EF == BA*BA + DC*DC + FE*FE;
      
      % One of the ages in each group is exactly twice an age in the other group
      %
      % *** This teaser condition was not needed to give the single answer ***
      % Output shows 64 = 2 * 32 and 46 = 2 * 23, which satisfies this condition.
      
      solve satisfy;
      
      output ["1st ages group = " ++ show(AB) ++ ", " ++ show(CD)
       ++  ", " ++ show(EF) ++ "\n"
      ++ "2nd ages group = " ++ show(BA) ++ ", " ++ show(DC) 
      ++ ", " ++ show(FE) ];
      
      % 1st ages group = 23, 64, 78
      % 2nd ages group = 32, 46, 87
      % ----------
      % ==========
      % Sorted ages:  23, 32, 46, 64, 78, 87
      
      

      Like

      • Jim Randell's avatar

        Jim Randell 4:43 pm on 25 January 2022 Permalink | Reply

        @GeoffR:

        Without the “in each group one of the ages is twice one of the ages in the other group” condition there is a further solution:

        (28, 64, 73) and (82, 46, 37)

        So it is needed.

        Like

    • GeoffR's avatar

      GeoffR 5:41 pm on 25 January 2022 Permalink | Reply

      @Jim: OK. I see you got a 2nd solution, justifying the constraint.

      For me, MinZinc only came up with the single solution, even though I had set multiple output configuration to 5 solutions.

      On the Configuration Menu, I tried both unchecking and checking the check-box with the heading “Stop after this many solutions(uncheck for all). I am using the MiniZinc IDE under Windows 10.

      Like

      • Jim Randell's avatar

        Jim Randell 9:46 pm on 25 January 2022 Permalink | Reply

        @GeoffR:

        The problem is your ordering constraint is over specified.

        Try:

        constraint AB < CD /\ CD < EF /\ AB < BA /\ AB < DC /\ AB < FE; 
        

        Like

    • GeoffR's avatar

      GeoffR 10:48 am on 26 January 2022 Permalink | Reply

      @Jim: Thanks, that solved my ordering constraint issue.

      I used your suggested ordering constraint in my code and got the two solutions. It then needs the “in each group one of the ages is twice one of the ages in the other group” constraint to get the single answer.

      Like

    • GeoffR's avatar

      GeoffR 11:22 am on 26 January 2022 Permalink | Reply

      % A Solution in MiniZinc - revised programme
      include "globals.mzn";
      
      var 1..9:A; var 1..9:B; var 1..9:C; 
      var 1..9:D; var 1..9:E; var 1..9:F;
      
      % Digits for two groups of ages are different
      constraint all_different([A, B, C, D, E, F]);
      
      % First group
      var 21..89: AB = 10*A + B;
      var 21..89: CD = 10*C + D;
      var 21..89: EF = 10*E + F;
      
      % Second group - ages reverse of 1st group
      var 21..89: BA = 10*B + A;
      var 21..89: DC = 10*D + C;
      var 21..89: FE = 10*F + E;
      
      % Revised ordering constraint (Jim)
      constraint AB < CD /\ CD < EF /\ AB < BA /\ AB < DC /\ AB < FE; 
      
      % Any two of the ages differ by at least 9
      constraint 
         abs(AB-CD) >=9 /\ abs(AB-EF) >=9 /\ abs(AB-BA) >=9
      /\ abs(AB-DC) >=9 /\ abs(AB-FE) >=9 /\ abs(CD-EF) >=9 
      /\ abs(CD-BA) >=9 /\ abs(CD-DC) >=9 /\ abs(CD-FE) >=9
      /\ abs(EF-BA) >=9 /\ abs(EF-DC) >=9 /\ abs(EF-FE) >=9
      /\ abs(BA-DC) >=9 /\ abs(BA-FE) >=9 /\ abs(DC-EF) >=9;
      
      % Sum of ages of the first group = sum of ages of the second group
      constraint AB + CD + EF == BA + DC + FE;
      
      % Sum of the squares of the three ages of the first group 
      % equals sum of the squares of the three ages of the second group
      constraint AB*AB + CD*CD + EF*EF == BA*BA + DC*DC + FE*FE;
      
      % One of the ages in each group is exactly twice an age in the other group
      % One of 1st ages group is double one of 2nd group
      constraint 
         (AB == 2*BA \/ AB == 2*DC \/ AB == 2*FE)
      \/ (CD == 2*BA \/ CD == 2*DC \/ CD == 2*FE)
      \/ (EF == 2*BA \/ EF == 2*DC \/ EF == 2*FE);
      
      % One of 2nd ages group is double one of 1st group
      constraint 
         (BA == 2*AB \/ BA == 2*CD \/ BA == 2*EF)
      \/ (DC == 2*AB \/ DC == 2*CD \/ DC == 2*EF)
      \/ (FE == 2*AB \/ FE == 2*CD \/ FE == 2*EF);
      
      solve satisfy;
      
      output ["1st ages group = " ++ show(AB) ++ ", " ++ show(CD)
       ++  ", " ++ show(EF) ++ "\n"
      ++ "2nd ages group = " ++ show(BA) ++ ", " ++ show(DC) 
      ++ ", " ++ show(FE) ];
      
      % 1st ages group = 23, 64, 78
      % 2nd ages group = 32, 46, 87
      % ----------
      % ==========
      
      
      
      

      Like

    • Hugh+Casement's avatar

      Hugh+Casement 7:17 am on 30 January 2022 Permalink | Reply

      Without the upper and lower age limits
      there would be another solution: 14, 82, 69 and 41, 28, 96,
      again with group sum 165.

      Like

  • Unknown's avatar

    Jim Randell 10:31 am on 23 January 2022 Permalink | Reply
    Tags:   

    A Brain Teaser: [Grains of rice] 

    From The Sunday Times, 21st December 1952 [link]

    An Eastern potentate, at the end of a game of chess with his professional chess player, broached the delicate question of a reward for his many years of service.

    The old man pointed to the board and said: “Suppose we number the squares from 1 to 64, thus. Your Majesty will observe that your King is on a lower-numbered square than mine. Pay me, I humbly beg, in grains of rice, for each square as many as the number of that square raised to the power of the number of the square occupied by your King, finishing at and including the square occupied by my King”.

    “But that will be ruinous”, protested the monarch. “Not so, for the sum total contains but nine figures”.

    “Then be it so. But will you take payment to the nearest thousand?”. “Certainly”, replied the sage, since if your men count accurately, I shall thereby forfeit only eight grains of rice”.

    What squares were occupied by the two Kings, and how many grains were the old man’s reward?

    This one of the occasional Holiday Brain Teasers published in The Sunday Times prior to the start of numbered Teasers in 1961. Prizes of 3 guineas and 2 guineas were offered.

    This puzzle was originally published with no title.

    [teaser-1952-12-21] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 10:32 am on 23 January 2022 Permalink | Reply

      If the king is on square k, and the old man is on square j then the total number of grains t is given by:

      t = sum (i = 1 .. j) (i^k)

      This Python program runs in 52ms.

      Run: [ @replit ]

      from enigma import (irange, ndigits, printf)
      
      # consider the king's square
      for k in irange(1, 63):
        # t = total number of grains
        # j = old man's square
        t = j = 0
        while j < 64:
          j += 1
          t += j**k
          n = ndigits(t)
          if n > 9: break
          # check conditions
          if n == 9 and k < j and t % 1000 == 8:
            printf("k={k} j={j} t={t}")
      

      Solution: The kings are on squares 5 and 32. The reward consisted of 196,171,000 grains of rice.

      The actual total being:

      >>> sum(powers(1, 32, 5))
      196171008
      

      A grain of rice weighs approximately 1/64 gram, so the total prize would be just over 3065 kg of rice.

      Like

  • Unknown's avatar

    Jim Randell 4:35 pm on 21 January 2022 Permalink | Reply
    Tags:   

    Teaser 3096: That strikes a chord 

    From The Sunday Times, 23rd January 2022 [link] [link]

    Skaredahora used a scale with seven notes, labelled J to P, for an idiosyncratic composition. It had a sequence of chords, each comprising exactly three different notes (the order being immaterial). The sequence started and ended with JNP. Each change from one chord to the next involved two notes increasing by one step in the repeating scale, and the other decreasing by two steps (e.g. JNP changing to KLJ).

    Every chord (eventually) reachable from JNP was used at least once; every allowable change from one of these chords to another was used exactly once. The number of chord changes between the first pair of JNPs was more than that between the last such pair, but, given that, was the smallest it could be.

    With notes in alphabetical order, what was the seventh chord in the sequence?

    [teaser3096]

     
    • Jim Randell's avatar

      Jim Randell 7:20 pm on 21 January 2022 Permalink | Reply

      I am assuming notes that are an octave apart are considered identical. So going up three notes from M to P gives the same note as going down 4 notes from M to P. Also that chords are identical if they contain the same three notes, and a chord change requires at least one of the notes to change.

      This Python program constructs the graph of transitions between chords, and then finds paths from JNP back to JNP that traverse all the edges (Eulerian circuits). We then apply the remaining conditions, and find the 7th element of the paths.

      It runs in 51ms.

      Run: [ @replit ]

      from enigma import (mod, all_different, ordered, tuples, Accumulator, join, printf)
      
      # there are 7 notes in the scale
      fn = mod(7)
      
      # format a sequence, 0..6 represent J..P
      fmt = lambda s: join("JKLMNOP"[i] for i in s)
      fmts = lambda ss: join((fmt(s) for s in ss), sep=" ", enc="()")
      
      # start node = JNP
      start = (0, 4, 6)
      assert fmt(start) == 'JNP'
      
      # generate changes for chord <s>
      def changes(s):
        (x1, y1, z1) = (fn(n + 1) for n in s)
        (x2, y2, z2) = (fn(n - 2) for n in s)
        for t in ((x2, y1, z1), (x1, y2, z1), (x1, y1, z2)):
          t = ordered(*t)
          if t != s and all_different(*t):
            yield t
      
      # generate possible transitions, starting with <start>
      adj = dict()
      trans = set()
      ns = {start}
      while ns:
        ns_ = set()
        for s in ns:
          ts = set(changes(s))
          adj[s] = ts
          trans.update((s, t) for t in ts)
          ns_.update(ts)
        ns = ns_.difference(adj.keys())
      
      # find sequences that use every transition in <ts>, starting from <ss>
      def seqs(ts, ss):
        # are we done?
        if not ts:
          yield ss
        else:
          # make a transition
          s = ss[-1]
          for x in adj[s]:
            t = (s, x)
            if t in ts:
              yield from seqs(ts.difference([t]), ss + [x])
            
      # find viable sequences
      def generate(trans, start):
        for ss in seqs(trans, [start]):
          # sequence must be a circuit
          if ss[0] != ss[-1]: continue
          # find the indices of start
          js = list(j for (j, x) in enumerate(ss) if x == start)
          # and distances between them
          ds = list(j - i for (i, j) in tuples(js, 2))
          # first distance is greater than last
          if not (ds[0] > ds[-1]): continue
          # return (distances, sequence)
          yield (ds, ss)
      
      # find the smallest first distance
      r = Accumulator(fn=min, collect=1)
      for (ds, ss) in generate(trans, start):
        r.accumulate_data(ds[0], ss)
      
      # output solutions
      for ss in r.data:
        printf("{x} {ss}", x=fmt(ss[6]), ss=fmts(ss))
      

      Solution: The seventh chord in the sequence is: KNO.

      The complete sequence is:

      1. JNP
      2. JKL
      3. KMP
      4. JKL
      5. LMO
      6. JNP
      7. KNO
      8. LMO
      9. KMP
      10. JNP

      [Follow the [@replit] link, click on “Show Files”, and select “Teaser 3096.mp3” to hear the chords played with JP transposed to notes AG in a single octave].

      There are 4 chords between the first two JNPs, and 3 chords between the last two JNPs.

      And this is the only progression of the form (JNP [4] JNP [3] JNP).

      There are 4 other viable progressions of the form (JNP [5] JNP [2] JNP), but we are only interested in progressions with the shortest possible distance between the first two occurrences of JNP.

      Like

  • Unknown's avatar

    Jim Randell 9:46 am on 20 January 2022 Permalink | Reply
    Tags:   

    Teaser 2878: Magic slides 

    From The Sunday Times, 19th November 2017 [link] [link]

    I have a toy consisting of eight tiles that can move by sliding them around a three-by-three frame:

    At any stage a tile adjacent to the space can slide into that space. I gave the toy to my grandson in the state illustrated and after some moves he presented it back to me with the space once again in the bottom right-hand corner but with the “2” (amongst others) not in its original position. Furthermore, his arrangement had some “magical” properties: each row, each column, and the diagonal from bottom left to top right all added up to the same total.

    What was his arrangement?

    [teaser2878]

     
    • Jim Randell's avatar

      Jim Randell 9:47 am on 20 January 2022 Permalink | Reply

      We’ve looked at sliding puzzles before. See: Enigma 1444, Enigma 106, Enigma 1210, Enigma 1160, Enigma 588.

      In order for a position to be reachable the number of “inversions” (pairs of tiles that are out of order) must be even.

      This Python program runs in 111ms.

      Run: [ @replit ]

      from enigma import (subsets, irange, all_same, printf)
      
      # fill out the grid
      for (A, B, C, D, E, F, G, H) in subsets(irange(1, 8), size=len, select="P"):
        # B cannot be 2
        if B == 2: continue
        # check the magic lines
        if not all_same(
          # rows
          A + B + C, D + E + F, G + H,
          # columns
          A + D + G, B + E + H, C + F,
          # diagonal
          C + E + G,
        ): continue
      
        # the number of inversions must be even
        n = sum(i > j for (i, j) in subsets((A, B, C, D, E, F, G, H), size=2))
        if n % 2 != 0: continue
      
        # output solution
        printf("{A} {B} {C} / {D} {E} {F} / {G} {H}")
      

      Solution: The arrangement was:

      And we can use the sliding puzzle program I wrote for Enigma 1444 [link] to verify that there is a sequence of moves that produces the required arrangement:

      % python sliding-puzzle.py 3 3 2 3 7 6 1 5 4 8
      [SOLVED] Moves: 44, Elapsed Time: 0m08s
      

      Like

  • Unknown's avatar

    Jim Randell 9:43 am on 18 January 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 901: Otherwise encaged 

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

    Our local pet shop caters especially for people who wish to keep mice in pairs. The shop sells three designs of half-cages and each customer buys two half-cages and joins them together to make a cage for two mice.

    Each of the three designs of half-cages, the Astoria, the Dorchester and the Hilton, is based on the plan above and has 3 walls ga, ab, bj together with walls at some of cd, de, ef, gh, hi, ij, dh and ei.

    Each customer buys two half-cages of different designs and joins them together such that point g of each coincides with point j of the other. A mouse is then placed in area abfc of each and allowed to run freely except where it is prevented from going by the walls. There may be some parts of the overall cage which cannot be reached by either mouse.

    The half-cages have been designed so that in no case can two mice reach each other and such that the following situation occurs: when an Astoria is joined to a Dorchester, the mouse from the Astoria has a larger area to move in than the other mouse; when a Dorchester is joined to a Hilton, the mouse from the Dorchester has a larger area; and when a Hilton is joined to an Astoria, the mouse from the Hilton has a larger area.

    When I was last in the shop I noticed that the Astoria was the only cage with a wall at dh, and also that was the only cage with a wall at ef.

    Draw a plan of the Dorchester and of the Hilton.

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    All puzzles from the The Sunday Times Book of Brain-Teasers: Book 1 (1980) book are now available on the site. I will shortly move on to puzzles from the The Sunday Times Book of Brain-Teasers: Book 2 (1981).

    [teaser901]

     
    • Jim Randell's avatar

      Jim Randell 9:44 am on 18 January 2022 Permalink | Reply

      I used the [[ grid_adjacency() ]] function from the enigma.py library to provide the transitions between the areas of a cage, and then removed those that are impeded by the inclusion of relevant walls.

      The following Python program runs in 179ms.

      Run: [ @replit ]

      from enigma import (subsets, grid_adjacency, cproduct, join, printf)
      
      # walls and the passage they impede
      walls = dict(
        cd=(0, 3), de=(1, 4), ef=(2, 5), dh=(3, 4),
        ei=(4, 5), gh=(3, 6), hi=(4, 7), ij=(5, 8),
      )
      
      # walls that exist in A, and only in A
      A_walls = {'ef', 'dh'}
      
      # walls that may exist in any of A, D, H
      ADH_walls = set(walls.keys()).difference(A_walls)
      
      # possible walls for A (must include 'ef', 'dh')
      As = list(A_walls.union(s) for s in subsets(ADH_walls))
      
      # possible walls for DH (must not include 'ef', 'dh')
      DHs = list(subsets(ADH_walls, fn=set))
      
      # adjacency matrix with no walls
      adj = grid_adjacency(3, 4)
      
      # construct a cage from two half-cages
      def construct(X, Y):
        # copy the default matrix
        m = list(set(x) for x in adj)
        # remove any adjacencies from the top walls
        for k in X:
          (x, y) = walls[k]
          m[x].discard(y)
          m[y].discard(x)
        # remove any adjacencies from the bottom walls
        for k in Y:
          (x, y) = (11 - j for j in walls[k])
          m[x].discard(y)
          m[y].discard(x)
        # return the new adjacency matrix
        return m
      
      # find the area of the maze reachable from ns
      def area(m, ns):
        xs = set()
        while ns:
          n = ns.pop()
          xs.add(n)
          ns.update(m[n].difference(xs))
        return xs
      
      # check an X+Y construction
      def check(X, Y):
        m = construct(X, Y)
        # area reachable from X should be greater than from Y
        return len(area(m, {0})) > len(area(m, {11}))
      
      # choose a set of walls for A and D
      for (A, D) in cproduct([As, DHs]):
        # check an A+D construction
        if not check(A, D): continue
      
        # choose a (dfferent) set of walls for H
        for H in DHs:
          if H == D: continue
          # check a D+H and H+A constructions
          if not (check(D, H) and check(H, A)): continue
      
          # output solution
          fmt = lambda s: join(sorted(s), sep=" ", enc="()")
          printf("D={D} H={H}; A={A}", D=fmt(D), H=fmt(H), A=fmt(A))
      

      Solution: The plans of the Dorchester and Hilton are shown below:

      The Astoria can be either of the following 2 diagrams:

      (The “walled-orf” area in the second is always inaccessible when A is combined with D or H, so the inclusion of the ij wall doesn’t make any difference).

      When combined they produce the following cages:

      Like

  • Unknown's avatar

    Jim Randell 9:33 am on 16 January 2022 Permalink | Reply
    Tags:   

    A Brain Teaser: Trial and Error 

    From The Sunday Times, 13th April 1952 [link]

    A bank cashier has a pile of 364 similar coins of which one Is known to be of abnormal weight, the remainder being all of equal weight. For testing, he has only a pair of scales, in which he can balance one pile of coins against another.

    What is the smallest number of such “trials” in which he can be sure of finding the odd coin, and what is the greatest number of coins among which he can likewise be sure of finding a single coin of odd weight in nine “trials”?

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

    [teaser-1952-04-13] [teaser-unnumbered]

     
    • Jim Randell's avatar

      Jim Randell 9:38 am on 16 January 2022 Permalink | Reply

      We have tackled similar problems before, see: Enigma 1589, Teaser 2500.

      For Enigma 1589 I wrote a constructive solution that searches for a minimal decision tree.

      For Teaser 2500 I gave a program that will construct minimal decision trees for a set of related problems.

      For a “type 1” problem (where we need to find the counterfeit coin, and determine if it is heaver or lighter), with n weighings we can deal with a set of (3^n − 3) / 2 coins.

      However, each decision tree for the “type 1” problem has a leaf that is impossible to reach if we are guaranteed that there is exactly one counterfeit coin. But if there are no counterfeit coins then every weighing balances and we reach the impossible leaf.

      So, for this type of puzzle (where we need to determine which coin is counterfeit, but not if it is heavier or lighter), we can put one of the coins aside and then proceed with the weighings for a “type 1” puzzle, if we reach the impossible leaf we know that it must be the unweighed coin that is counterfeit, and we don’t need to determine whether it is lighter or heavier, so we can stop. This means we can manage 1 extra coin compared to the “type 1” puzzle.

      So in n weighings we can deal with a set of (3^n − 1) / 2 coins. (The same as a “type 2” problem, where we have to determine the counterfeit coin, and whether it is lighter or heavier, but we have a reference coin available).

      Hence:

      With 6 weighings we can deal with a set of (3^6 − 1) / 2 = 364 coins.

      And with 9 weighings we can determine the counterfeit coin from (3^9 − 1) / 2 = 9841 coins.

      Solution: 364 coins require 6 weighings. With 9 weighings we can deal with up to 9841 coins.


      Note that the formula applies to n ≥ 2 weighings.

      A set consisting of 1 coin requires 0 weighings (as the set must contain a counterfeit coin), and with a set consisting of 2 coins it is not possible to determine which coin is counterfeit (unless we have a reference coin, or are told that the counterfeit is light (or heavy)).

      With 4 coins (1, 2, 3, 4) we can weigh 1 against 2. If they balance (and so are both good), we weigh one of these good coins against 3. If the second weighing balances, the counterfeit is 4. If it doesn’t balance, it is 3. If the initial weighing does not balance, then the counterfeit coin is 1 or 2, and 3 and 4 are good. So weigh 1 against 3. If they balance, the counterfeit is 2. If not the counterfeit is 1. So we can solve 4 coins with 2 weighings.


      The published solution (27th April 1952) is as follows: [link]

      Readers were posed the problem before a cashier having a pile of coins of which he knows one to be abnormal in weight, and a pair of scales but no other Instrument. They were asked (a) the least number of trial weighings he must make to be sure of identifying the abnormal coin in a pile of 364, and (b) the largest number of coins among which he may be sure of Identifying the abnormal one In nine “trials.” This was a highly successful brain-teaser in respect of the number of clever brains which it evidently teased. There were nearly 1,500 entries, but barely one in ten of them gave both answers correctly. Answers to (a) ranged from 6 to 16, and to (b) from 22 to 19,683.

      On the other hand, correct entrants had little success in expressing the method of trial succinctly. The clue is to start by weighing 121 against 121. If they balance, the remaining 122 include the abnormal coin; if not, the 122 are known to be all “good” coins. Combinations from the good and doubtful groups then reduce the scale of alternative third trials to 27 or 40, fourth to 9 or 13, fifth to 3 or 4, and sixth to one coin in each pan.

      The correct answers are: (a) 6, (b) 9,841. The latter follows the formula (3^n − 1) / 2 where n = the maximum number of trials; many entrants offered 3^n (giving the answer 19,683), which is demonstrably incorrect, e.g., for n=2.

      Like

  • Unknown's avatar

    Jim Randell 4:05 pm on 14 January 2022 Permalink | Reply
    Tags:   

    Teaser 3095: Diddums! 

    From The Sunday Times, 16th January 2022 [link] [link]

    “Diddums” was Didi’s dada’s name for numbers where the number of positive DIvisors (including 1 and the number), the number of Digits, and the Digit sUM are equal.

    Didi enjoyed mental arithmetic, doing it speedily and accurately. For numbers of digits from 1 to 9 she listed in ascending order all possible numbers whose digit sum equalled the number of digits. Working through her list in order, she quickly found the first two diddums (1 and 11) and the next three diddums. After many further calculations, Didi confirmed that the sixth diddum (which is even) was listed.

    “Now I’m bored”, she moaned.

    “Diddums!”, said Didi’s dada.

    What is the sixth diddum’s digit sum?

    [teaser3095]

     
    • Jim Randell's avatar

      Jim Randell 4:49 pm on 14 January 2022 Permalink | Reply

      This Python program generates the numbers in order.

      To find the first 6 numbers takes 74ms. (Internal runtime is 13ms).

      Run: [ @replit ]

      from enigma import (tau, irange, inf, arg, printf)
      
      # generate numbers with a digit sum of t and k digits
      def generate(t, k, n=0):
        if k == 1:
          if t < 10:
            yield 10 * n + t
        else:
          for d in irange((0 if n else 1), min(9, t)):
            yield from generate(t - d, k - 1, 10 * n + d)
      
      # generate "diddums" numbers in order
      def solve():
        for k in irange(1, inf):
          for n in generate(k, k):
            if tau(n) == k:
              yield (k, n)
      
      N = arg(6, 0, int)
      
      # find the first N numbers
      for (i, (k, n)) in enumerate(solve(), start=1):
        printf("[{i}] {k} -> {n}")
        if i == N: break
      

      Solution: The sixth number on the list has a digit sum of 8.

      The list of numbers looks like this:

      1 → 1
      2 → 11
      4 → 1003, 1111, 2101
      8 → 10000034, …, 70000001 (630 numbers)
      10 → 1000003024, …, 5100000112 (70 numbers)
      12 → 100000001028, …, 900000101001 (6320 numbers)
      14 → 10000000260032, …, 70001000100032 (2149 numbers)
      16 → 1000000000000186, …

      For the larger numbers it is more efficient to use Pollard’s Rho method [[ prime_factor_rho() ]] to find prime factors, which we can do by calling [[ tau() ]] like this:

      tau(n, prime_factor_rho)
      

      Like

    • GeoffR's avatar

      GeoffR 8:41 pm on 14 January 2022 Permalink | Reply

      A brute force solution ran in about 20 sec. After the first sixth number, there were many similar solutions to the sixth number.

      from enigma import nsplit, tau, divisors
      
      cnt = 0  #counter for number of diddum numbers.
      
      for k in range(1, 100000000):
        n = nsplit(k) #split number into digits
        sn = sum(n)   #find sum of digits
        nd = len(n)   #find number of digits
        # check sum of digits = number of digits
        if sn == nd:
          tk = tau(k)
          # check sum of digits = no. of divisors
          if sn == tk:
            print(f"{cnt+1}: num={k}, no. divisors={tau(k)}")
            print(f"Divisors are {divisors(k)}")
            print()
            cnt += 1
            # stop after the 6th number
            if cnt == 6:
              break
      
      
      

      Like

    • NigelR's avatar

      NigelR 9:55 am on 15 January 2022 Permalink | Reply

      I used a very similar approach to GeoffR (a sledgehammer once again!) but without the enigma functions. I also added a fudge to exploit the ‘(which is even)’ advice for the 6th number by using an increment of 2 once the 5th number had been found. This reduced the run time by a couple of days.

      import math
      #function to return list of digits in n
      def digs(n):    
          cont = []
          while n:
              n,a = divmod(n,10)
              cont.append(a)
          return cont        
      #function to return list of divisors of n
      def divisg(n):
          divs = [1]
          for i in range(2,int(math.sqrt(n))+1):
              if n%i == 0:
                  divs.extend([i,int(n/i)])
          divs.extend([n])
          return list(set(divs))
      #main
      i = 0
      inc = 1 
      count = 0
      while count < 6:
          i += inc
          num = digs(i)      
          if sum(num) != len(num): continue
          div = list(divisg(i))
          if len(div) != len(num):continue
          print(i,num,div)
          count += 1
          if count==5:
              if i%2 != 0: i+=1
              inc=2
      

      Like

    • Hugh+Casement's avatar

      Hugh+Casement 8:52 am on 16 January 2022 Permalink | Reply

      That was a giant sledgehammer, Nigel! There are faster ways. For example, the square of a prime has three integer divisors: none fits here. The cube of a prime has four divisors: again none found. The product of two different primes also has four divisors: that provides the next three (mentioned in the puzzle). The product of one prime and the square of another has six divisors; one of those primes obviously has to be 2 to give an even product.

      I’ll spare you my program in Basic, but after rejecting a few numbers of factors & digits it quickly found the sixth diddums and a further five.

      Like

    • GeoffR's avatar

      GeoffR 3:42 pm on 23 January 2022 Permalink | Reply

      This programme considers ‘Diddums’ numbers for separate three, four, five, six, seven and eight digit groups. This method produced the six-number solution with a much faster run-time than my previous posting. It ran in 28 msec.

      
      from enigma import Primes, tau, divisors, nsplit
      
      from enigma import Timer
      timer = Timer('ST3095', timer='E')
      timer.start()
      
      # primes for 4-digit 'Diddums' numbers
      primes = list(Primes(200))
      
      # Check 3,4,5,6,7,8 digit 'Diddums' numbers
      DD_nums = []
      
      DD_nums.append(1) # two given 'Diddums' numbers
      DD_nums.append(11)
      
      # 3-digit 'Diddums' numbers
      D3 = [x * x for x in range(10, 20) if x * x in range(100, 301)]
      for n in D3:
        if tau(n) == 3 and sum(nsplit(n)) == 3:
          DD_nums.append(n)
      
      # 4-digit 'Diddums' numbers - use a  and b as primes,
      # where four divisors are 1, a, b, a * b
      for a in primes:
        for b in primes:
          if a == b:
            continue
          if a * b in range(1000, 4001):
            if a * b in DD_nums:
              continue
            if tau(a * b) == 4 and sum(nsplit(a * b)) == 4:
              DD_nums.append(a * b)
      
      # 5-digit 'Diddums' numbers for powers p ^ 4,
      # five divisors are 1, p, p^2, p^3, p^4
      for p in (11, 13, 17):
        num = p ** 4
        if num in range(10000, 50001):
          if tau(num) == 5 and sum(nsplit(num)) == 5:
            DD_nums.append(num)
      
      # 6-digit 'Diddums' numbers 
      # product of 2 primes in a^2 * b format gives six divisors
      for a in primes:
        for b in primes:
          if a == b:
            continue
          if a * a * b in range(100000, 600001):
            if a * a * b in DD_nums:
              continue
            if tau(a * a * b) == 6 and sum(nsplit((a * a * b))) == 6:
              DD_nums.append(a * a * b)
      
      # 7-digit 'Diddums' numbers for powers p^6,
      # seven divisors are 1, p, p^2, p^3, p^4, p^5, p^6
      for p in (11, 13):   
        num = p ** 6
        if num in range(1000000, 7000001):
          if tau(num) == 7 and sum(nsplit(num)) == 7:
            DD_nums.append(num)
      
      # check first 10,000 8-digit  numbers (initially) for
      # potential 8-digit numbers summing to eight
      D8 = []
      
      for n in range(10000000, 10010000):
        ns = nsplit(n)
        if sum(ns) == 8:
          D8.append(n)
      
      for n in D8:
        if tau(n) == 8:
          DD_nums.append(n)
          break
      
      print('Diddums Numbers are ', sorted(DD_nums))
      timer.stop()      
      timer.report()
      
      # Numbers are  [1, 11, 1003, 1111, 2101, 10000034]
      # [ST3095] total time: 0.0276404s (27.64ms)
      
      

      Like

    • Frits's avatar

      Frits 8:37 am on 29 June 2023 Permalink | Reply

      Only fast for this puzzle (for general purposes too slow).
      Using tau(n, prime_factor_rho) wasn’t necessary.

         
      from enigma import tau
      from itertools import compress
      
      sumd = lambda n: sum(int(x) for x in str(n))
      
      def primesbelow(n):
        # rwh_primes1v2(n):
        """ Returns a list of primes < n for n > 2 """
        sieve = bytearray([True]) * (n // 2 + 1)
        for i in range(1, int(n ** 0.5) // 2 + 1):
          if sieve[i]:
            sieve[2 * i * (i + 1)::2 * i + 1] = \
            bytearray((n // 2 - 2 * i * (i + 1)) // (2 * i + 1) + 1)
        return [2, *compress(range(3, n, 2), sieve[1:])]
      
      # return next valid diddum
      def next_diddum(m, n, maxd):
        while m <= maxd:  
          m += 9
          if sumd(m) == n and tau(m) == n:
            return m
        return maxd + 1   # too high
      
      ds = [1, 11]   # two given diddum numbers
      
      # if n = 3, 6 or 9 then a diddum can't be of the form p^(n-1) as it must be 
      # 3 must be a divisor (3^2 = 9, 3^5 = 243 and 3^8 = 6561)
      
      # if n = 6 then a diddum can't be of the form p1 * p2**2 as p1 and p2 must 
      # be chosen from 2 and 3 
      
      for n in [4, 5, 7, 8, 9]:
        maxd = n * 10**(n - 1)              # maximum diddum number
        if n in {5, 7}:                     # only option p^(n-1) for prime numbers
          maxp = int(maxd**(1 / (n - 1)))   # maximum prime number
          # test prime power p^(n-1), divisors are 1, p, p^2, ..., p^(n-2), p^(n-1)
          P = primesbelow(maxp + 1)
          # prime number 3 may not be used if sum digits is not divisible by 3
          P = [p for p in P if not n % 3 or p != 3]   
          
          ds.extend(pwr for p in [x for x in P if 10 < x <= maxp]
                    if sumd(pwr := p**(n - 1)) == n)
        else:
          found5 = (len(ds) == 5)
         
          # initial valid diddum (start just too low)
          m = next_diddum(10**(n - 1) + n - 10, n, maxd)
          while m <= maxd:
            ds.append(m)
            if found5: 
              print("sixth diddum's digit sum:", m)
              exit()
              
            m = next_diddum(m, n, maxd)
      

      Like

  • Unknown's avatar

    Jim Randell 9:17 am on 13 January 2022 Permalink | Reply
    Tags:   

    Teaser 2850: On course 

    From The Sunday Times, 7th May 2017 [link] [link]

    Hackers-on-Sea golf course is designed by the great Jack Arnold. It is a par-seventy course and its eighteen holes are all par three, four or five. The numbers of all the par-five holes are primes, and the numbers of the par-four holes are odd.

    Recently I only had time to play half the course and ideally my nine consecutive holes should be par thirty-five. However, there is no such collection of holes.

    What are the numbers of the par-four holes?

    [teaser2850]

     
    • Jim Randell's avatar

      Jim Randell 9:17 am on 13 January 2022 Permalink | Reply

      If n3, n4, n5 are the numbers of par 3, 4, 5 holes respectively we have:

      n3 + n4 + n5 = 18
      3 × n3 + 4 × n4 + 5 × n5 = 70

      We can treat the problem as a “money changing” puzzle, i.e. we want to make an amount of 70 using 18 coins of denominations 3, 4, 5.

      The following Python program uses the [[ express() ]] function from the enigma.py library. It runs in 50ms.

      Run: [ @replit ]

      from enigma import (express, primes, irange, subsets, tuples, printf)
      
      # primes up to 18
      primes = set(primes.between(1, 18))
      
      # odd numbers up to 18
      odds = set(irange(1, 18, step=2))
      
      for (n3, n4, n5) in express(70, [3, 4, 5], min_q=1):
        if not (n3 + n4 + n5 == 18): continue
      
        # choose par 5 holes (prime)
        for p5s in subsets(primes, size=n5):
      
          # choose par 4 holes (odd)
          for p4s in subsets(odds.difference(p5s), size=n4):
      
            # the rest are par 3
            par = [3] * 18
            for k in p5s: par[k - 1] = 5
            for k in p4s: par[k - 1] = 4
      
            # no consecutive sequence of 9 holes sums to 35
            if any(sum(t) == 35 for t in tuples(par, 9)): continue
      
            printf("p4s={p4s} p5s={p5s}; {par}")
      

      Solution: The par 4 holes are: 1, 9.

      The par 5 holes are: 2, 3, 5, 7, 11, 13, 17.


      We can use a more sophisticated “money changing” algorithm by using:

      from enigma import Denominations
      ...
      
      for (n3, n4, n5) in Denominations(3, 4, 5).express(70, min_q=1):
        ...
      

      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