Updates from November, 2022 Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 4:36 pm on 11 November 2022 Permalink | Reply
    Tags:   

    Teaser 3138: Primus inter impares 

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

    Just nine Prime Ministers held office in the Kingdom of Primea during the 20th century. No two Prime Ministers held office at the same time, none had more than one period in office, and the gap between successive Prime Ministers’ terms was never more than a month. Each held office for a period of time in which the number of whole years was a different prime number (e.g. holding office from 1910 to 1915 could cover four or five whole years) and no Prime Minister served for more than 30 years. Appropriately, they all took office in prime years, but there was no change of Prime Minister in 1973.

    In which years during the 20th century did new Prime Ministers in Primea take up office?

    [teaser3138]

     
    • Jim Randell's avatar

      Jim Randell 5:12 pm on 11 November 2022 Permalink | Reply

      This Python program runs in 51ms. (Internal runtime is 554µs).

      Run: [ @replit ]

      from enigma import (primes, append, tuples, printf)
      
      # possible term lengths
      terms = set(primes.between(0, 30))
      
      # select <k> years from <years>
      # return (<start-years>, <term-lengths>)
      def solve(k, years, ys=[], ts=[]):
        # are we done?
        if k == 0:
          # no-one served more than 30 years
          if ys and ys[-1] > 1968:
            yield (ys, ts)
        elif not k > len(years):
          # choose the next year
          for (i, y) in enumerate(years):
            # consider possible term lengths
            g = y - ys[-1]
            for t in terms.intersection({g, g - 1}):
              if t in ts: continue
              # solve for the remaining years
              yield from solve(k - 1, years[i + 1:], append(ys, y), append(ts, t))
      
      # consider the start year of the incumbent at the start of the century
      for y0 in primes.between(1870, 1901):
        # fill out the 8 starting years in the 20th century
        years = list(primes.between(max(y0 + 1, 1901), 2000))
        years.remove(1973)  # but not 1973
        for (ys, ts) in solve(8, years, [y0]):
          # output solution
          for ((y1, y2), t) in zip(tuples(ys, 2), ts):
            printf("{y1} - {y2} = {t} years")
          printf("{y2} - .... = ? years")
          printf()
      

      Solution: The years are: 1901, 1907, 1931, 1949, 1979, 1993, 1997, 1999.

      The incumbent at the start of the 20th century began office in 1889, so we have:

      1. 1889 – 1901 = 11 years
      2. 1901 – 1907 = 5 years
      3. 1907 – 1931 = 23 years
      4. 1931 – 1949 = 17 years
      5. 1949 – 1979 = 29 years
      6. 1979 – 1993 = 13 years
      7. 1993 – 1997 = 3 years
      8. 1997 – 1999 = 2 years
      9. 1999 – …. = (7 or 19 years)

      There are two primes left that could correspond to the length of the term of the incumbent at the end of the 20th century. Note that it is not required that the term of the successor (if any) starts in a prime year.

      Like

      • Frits's avatar

        Frits 1:24 pm on 12 November 2022 Permalink | Reply

        @Jim, as I don’t have access to a PC I can’t publish/test a program myself.

        I think a check is needed to see if the last Prime Minister can serve a prime number of years and which ends after the 20th century (if high prime numbers already have been used this may not be possible anymore).

        Like

        • Hugh Casement's avatar

          Hugh Casement 7:17 am on 13 November 2022 Permalink | Reply

          Frits, the wording of the puzzle is vague on that score, but I think if we do allow the last term of office to extend beyond the end of the century then we get multiple solutions. Jim (line 25) would seem to agree with me that we need to start looking before the beginning of the century.

          Like

        • Jim Randell's avatar

          Jim Randell 8:18 am on 13 November 2022 Permalink | Reply

          @Frits: I did wonder about that. I check the final PM of the 20th century does not have a term that is longer than 30 years (line 12), and when I saw that there was only a single possible solution I didn’t worry about the actual length of the term, as it starts very close to the end of the century, and there are 2 unused primes available.

          Here is the code with an extra check to ensure the final term length is possible. (I also include code to specify the first and last years of the 20th century. I am using the “strict” definition 1901-2000, but you get the same answer with the “popular” usage of 1900-1999).

          Run: [ @replit ]

          from enigma import (primes, append, tuples, printf)
          
          # first/last years for 20th century
          (first, last) = (1901, 2000)
          
          # possible term lengths
          terms = set(primes.between(0, 30))
          
          # select <k> years from <years>
          # return (<start-years>, <term-lengths>)
          def solve(k, years, ys=[], ts=[]):
            # are we done?
            if k == 0:
              yield (ys, ts)
            elif not k > len(years):
              # choose the next year
              for (i, y) in enumerate(years):
                # consider possible term lengths
                g = y - ys[-1]
                for t in terms.intersection({g, g - 1}):
                  if t in ts: continue
                  # solve for the remaining years
                  yield from solve(k - 1, years[i + 1:], append(ys, y), append(ts, t))
          
          # consider the start year of the incumbent at the start of the century
          for y0 in primes.between(first - 31, first):
            # fill out the 8 starting years in the 20th century
            years = list(primes.between(max(y0 + 1, first), last))
            years.remove(1973)  # but not 1973
            for (ys, ts) in solve(8, years, [y0]):
              # check possible term lengths for the incumbent at the end of the century
              fs = sorted(t for t in terms.difference(ts) if ys[-1] + t > last)
              if not fs: continue
              # output solution
              for ((y1, y2), t) in zip(tuples(ys, 2), ts):
                printf("{y1} - {y2} = {t} years")
              printf("{y2} - .... = {fs} years")
              printf()
          

          But it seems that it may be possible that the final PM is still in office at the time the puzzle was set (having served less than 30 years so far), so we can’t say what the length of the term will be yet.

          Like

  • Unknown's avatar

    Jim Randell 8:40 am on 10 November 2022 Permalink | Reply
    Tags:   

    Teaser 2611: Simple arithmetic 

    From The Sunday Times, 7th October 2012 [link] [link]

    George and Martha are teaching their great-grandchildren some simple arithmetic. “If you add two thirties to four tens you get a hundred”, George was saying, and he wrote it like this:

    “Strangely”, added Martha, there are nine different letters there, and if you allow each letter to stand for a different digit, there is a unique sum which works”.

    Which digit would be missing?

    [teaser2611]

     
    • Jim Randell's avatar

      Jim Randell 8:40 am on 10 November 2022 Permalink | Reply

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

      The following run file executes in 58ms. (Internal runtime is 4.74ms).

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, irange, diff, join, printf)
      
      # construct the alphametic sum
      p = SubstitutedExpression.split_sum(
        ["THIRTY"] * 2 + ["TEN"] * 4, "HUNDRED",
        template="(2 * {THIRTY} + 4 * {TEN} = {HUNDRED})",
      )
      
      # solve the puzzle, and output unused digit(s)
      for s in p.solve():
        ds = diff(irange(0, 9), s.values())
        # output solution
        printf("-> unused digits = {ds}", ds=join(ds, sep=", "))
      

      Solution: The missing digit is 7.

      Like

    • GeoffR's avatar

      GeoffR 11:40 am on 10 November 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      %          T H I R T Y
      %          T H I R T Y
      %                T E N
      %                T E N
      %                T E N
      %                T E N
      %        -------------
      %        H U N D R E D
      %        -------------
      
      set of int: INT = 0..9;
      var INT: T; var INT: H; var INT: I;
      var INT: R; var INT: Y; var INT: E;
      var INT: N; var INT: U; var INT: D;
      
      constraint T > 0 /\ H > 0;
      constraint all_different( [T,H,I,R,Y,E,N,U,D]);
      
      % Carry digits from RH end
      var 0..6:c1; var 0..6:c2; var 0..6:c3;
      var 0..1:c4; var 0..1:c5; var 0..1:c6;
      
      % Adding digits in columns, with carry digits from RH end of sum
      constraint D == (4*N + 2*Y) mod 10 /\ c1 == (4*N + 2*Y) div 10;
      constraint E == (c1 + 4*E + 2*T) mod 10 /\ c2 == (c1 + 4*E + 2*T) div 10;
      constraint R == (c2 + 4*T + 2*R) mod 10 /\ c3 == (c2 + 4*T + 2*R) div 10;
      constraint D == (c3 + 2*I) mod 10 /\ c4 == (c3 + 2*I) div 10;
      constraint N == (c4 + 2*H) mod 10 /\ c5 == (c4 + 2*H) div 10;
      constraint U == (c5 + 2*T) mod 10 /\ c6 == (c5 + 2*T) div 10;
      constraint H == c6;
      
      solve satisfy;
      
      output ["[T,H,I,R,Y,E,N,U,D] = " ++ show([T,H,I,R,Y,E,N,U,D]) ];
      % [T, H, I, R, Y, E, N, U, D] = 
      % [9, 1, 5, 2, 6, 0, 3, 8, 4]
      % ----------
      % ==========
      % By inspection, missing digit = 7.
      % TEN = 903, THIRTY = 915296, HUNDRED = 1834204.
      
      

      Like

    • GeoffR's avatar

      GeoffR 10:25 pm on 10 November 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
       
      %          T H I R T Y
      %          T H I R T Y
      %                T E N
      %                T E N
      %                T E N
      %                T E N
      %        -------------
      %        H U N D R E D
      %        -------------
       
      set of int: INT = 0..9;
      var INT: T; var INT: H; var INT: I;
      var INT: R; var INT: Y; var INT: E;
      var INT: N; var INT: U; var INT: D;
       
      constraint T > 0 /\ H > 0;
      constraint all_different( [T,H,I,R,Y,E,N,U,D]);
       
      var 100..999:TEN = 100*T + 10*E + N;
      var 100000..999999:THIRTY = 100000*T + 10000*H + 1000*I + 100*R + 10*T + Y;
      var 1000000..9999999:HUNDRED = 1000000*H + 100000*U + 10000*N + 1000*D + 100*R + 10*E + D;
      
      constraint 2 * THIRTY + 4 * TEN == HUNDRED;
      
      solve satisfy;
      
      output [ "T,H,I,R,Y,E,N,U,D] = " ++ show( [T,H,I,R,Y,E,N,U,D] ) ];
      
      % [T, H, I, R, Y, E, N ,U, D] = 
      % [9, 1, 5, 2, 6, 0, 3, 8, 4]
      % time elapsed: 0.17 s
      % ----------
      % ==========
      % By inspection, the missing digit is 7.
      
      
      

      Like

  • Unknown's avatar

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

    Teaser 2695: Powers behind the thrones 

    From The Sunday Times, 18th May 2014 [link] [link]

    Gold sovereigns were minted in London for most years from the great recoinage in 1817 until Britain left the gold standard in 1917. I have a collection of eight sovereigns from different years during that period, the most recent being an Edward VII sovereign (he reigned from 1902 until 1910). I noticed that the year on one of the coins is a perfect square and this set me thinking about other powers. Surprisingly, it turns out that the product of the eight years on my coins is a perfect cube.

    What (in increasing order) are those eight years?

    [teaser2695]

     
    • Jim Randell's avatar

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

      The values were are interested in lie in the interval [1817, 1910].

      And the only square in that range is 1849 (= 43²), so that must be one of the 8 values.

      And the largest value is in the interval [1902, 1910].

      This Python program finds the prime factors for numbers in the required range, and then constructs sets of 8 values whose prime factors, when combined, all appear a multiple of 3 times, and so the product of the values is a cube.

      It runs in 164ms.

      Run: [ @replit ]

      from enigma import (irange, prime_factor, multiset, delete, append, printf)
      
      # find values whose product is a cube
      # k = remaining values for find (int)
      # vs = values so far (list)
      # fs = prime factors so far (multiset)
      # d = map of value -> prime factors (multiset)
      # ks = candidate keys in d (list)
      def solve(n, vs, fs, d, ks):
        if n == 0:
          # check all prime exponents are multiples of 3
          if all(x % 3 == 0 for x in fs.values()):
            yield vs
        elif not len(ks) < n:
          # add in a new candidate
          for (i, k) in enumerate(ks):
            # and solve for the remaining candidates
            yield from solve(n - 1, append(vs, k), fs.combine(d[k]), d, ks[i + 1:])
      
      # collect candidate numbers and their prime factors (as a multiset)
      d = dict((n, multiset.from_pairs(prime_factor(n))) for n in irange(1817, 1910))
      
      # find factors that appear fewer than 3 times in total
      while True:
        # count the factors (by combining values from each of the numbers)
        m = multiset.from_dict(*d.values())
        fs = set(k for (k, v) in m.items() if v < 3)
        if not fs: break
        # and remove any numbers which involve any of these factors
        d = delete(d, (k for (k, v) in d.items() if any(p in fs for p in v)))
      
      # 1849 is one of the values in the set
      (vs1, fs1) = ([1849], d[1849])
      
      # and the max value is between 1902 and 1910
      for v in irange(1902, 1910):
        if v not in d: continue
        vs2 = append(vs1, v)
        fs2 = fs1.union(d[v])
      
        # solve for the remaining 6 values
        ks = sorted(k for k in d.keys() if k < v and k not in vs2)
        for vs in solve(6, vs2, fs2, d, ks):
          # output solution
          printf("years = {vs}", vs=sorted(vs))
      

      Solution: The eight years are: 1820, 1836, 1849, 1859, 1870, 1890, 1892, 1904.

      So we have:

      numbers = (1820, 1836, 1849, 1859, 1870, 1890, 1892, 1904)
      factors = (2^12, 3^6, 5^3, 7^3, 11^3, 13^3, 17^3, 43^3)
      factors = (2^4, 3^2, 5, 7, 11, 13, 17, 43)^3

      And the product of the numbers is: (2^4, 3^2, 5, 7, 11, 13, 17, 43)^3 = 526846320^3.

      Like

    • Hugh Casement's avatar

      Hugh Casement 8:31 am on 10 November 2022 Permalink | Reply

      We can get part of the way by analysis.  We need another factor 43, so 1892 must be one of the years.
      The only integer in the range 1902 to 1910 with a small enough largest prime factor to allow us to find two more is 1904 = 2^4 × 7 × 17.
      After that I think trial and error (i.e. by computer) is needed.

      Like

      • Jim Randell's avatar

        Jim Randell 11:03 am on 10 November 2022 Permalink | Reply

        @Hugh: My manual solution proceeded as follows:

        Armed with the prime factorisations of the numbers between 1817 and 1910 (which I did not compute manually), we quickly find 1849, 1892, 1904 are on the list.

        Then we need more factors of 17, and there are only 2 candidates: 1836 and 1870. Both of which must be included. So we now have 5 of the 8 numbers on the list.

        We can eliminate numbers with a factor of 29 (1827, 1856, 1885).

        Considering numbers with factors of 13 (1820, 1859, 1872), if any of these are included it must be 1859 and just one of the others.

        Adding 1859 and 1820 to the list leaves factors of (2, 5, 7) to find, and the only candidate is 1890, and this does indeed give a viable list.

        And I didn’t look for further solutions.

        Like

  • Unknown's avatar

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

    Brain-Teaser 195: Common frontier 

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

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

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

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

    What is the Europhalian speed limit?

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

    [teaser195]

     
    • Jim Randell's avatar

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

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

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

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

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

      S = 8; E = 5

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

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

      And:

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

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

      Like

  • Unknown's avatar

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

    Teaser 3137: Common names 

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

    Eight friends met at a party; their ages in whole numbers of years were all different. They were Alan, Cary, James, Lucy, Nick, Ricky, Steve and Victor, with Lucy being the youngest. For each of them the square of their age was a three-figure number consisting of three different digits. Furthermore, for any two of them, the squares of their ages had at least one digit in common precisely when their names had at least one letter in common.

    In alphabetical order of their names, what are the eight ages?

    [teaser3137]

     
    • Jim Randell's avatar

      Jim Randell 4:49 pm on 4 November 2022 Permalink | Reply

      This Python program runs in 61ms. (Internal runtime is 9.2ms).

      Run: [ @replit ]

      from enigma import (irange, sq, nsplit, diff, intersect, append, delete, subsets, printf)
      
      # model a symmetric relation
      class Relation(set):
        # check if x is related to y
        def __call__(self, x, y):
          return (x, y) in self or (y, x) in self
      
      # names
      names = "ALAN CARY JAMES LUCY NICK RICKY STEVE VICTOR".split()
      
      # names are related when they share a letter
      N = Relation((x, y) for (x, y) in subsets(names, size=2) if intersect([x, y]))
      
      # find 3-digits squares with no repeated digits
      sqs = dict()
      for i in irange(10, 31):
        ds = set(nsplit(sq(i)))
        if len(ds) == 3:
          sqs[i] = ds
      
      # values are related when their squares share a digit
      V = Relation((x, y) for ((x, sx), (y, sy)) in subsets(sqs.items(), size=2) if intersect([sx, sy]))
      
      # assign values to remaining names
      # names = remaining names
      # ss = used (name, value) pairs
      # vs = remaining values
      def solve(names, ss, vs):
        if not names:
          yield ss
        elif not len(names) > len(vs):
          # choose a value for the next name
          n = names[0]
          for (i, v) in enumerate(vs):
            # check values have digits in common when names have letters in common
            if all(N(n, n_) == V(v, v_) for (n_, v_) in ss):
              # solve for the remaining names
              yield from solve(names[1:], append(ss, (n, v)), delete(vs, [i]))
      
      # choose an age for LUCY (the youngest)
      n0 = "LUCY"
      ks = sorted(sqs.keys())
      for (i, k) in enumerate(ks):
        # solve for the remaining names
        for ss in solve(diff(names, {n0}), [(n0, k)], ks[i + 1:]):
          # output solution
          for (n, v) in sorted(ss):
            printf("{n} -> {v} [{s}]", s=sq(v))
          printf()
      

      Solution: The ages are: 19, 31, 29, 16, 25, 23, 28, 27.

      With squares in square brackets:

      ALAN = 19 [361]
      CARY = 31 [961]
      JAMES = 29 [841]
      LUCY = 16 [256]
      NICK = 25 [625]
      RICKY = 23 [529]
      STEVE = 28 [784]
      VICTOR = 27 [729]

      Like

    • Paul Byrne's avatar

      Paul Byrne 9:54 pm on 14 November 2022 Permalink | Reply

      Hi Jim
      Thanks for all the good work on these Teasers.
      Love the simplicity of your website and your solutions.
      Re 3137 Teaser
      Is 24 instead of 31 also a correct answer? Keep up the good work!

      Like

      • Jim Randell's avatar

        Jim Randell 9:17 am on 15 November 2022 Permalink | Reply

        @Paul: Thanks for the feedback.

        We can’t swap CARY for 24 in the solution I give above, as 24² = 576, and CARY and JAMES share the letter A, so their squares need to share a digit. But 576 and 841 (= 29²) don’t have any digits in common.

        Like

    • Paul Byrne's avatar

      Paul Byrne 10:21 am on 16 November 2022 Permalink | Reply

      Hi Jim, thank you very much for your response.

      Forgive me I should’ve made myself clearer.
      If Cary becomes 576, and James 784, and Steve 841, can it then work as an alternative?

      Like

      • Jim Randell's avatar

        Jim Randell 12:14 pm on 16 November 2022 Permalink | Reply

        @Paul: My program performs an exhaustive search, so there is only one solution to the puzzle.

        If we had:

        CARY = 24 [576]
        JAMES = 28 [784]
        STEVE = 29 [841]

        Then {LUCY, NICK, RICKY} must correspond to {16 [256], 23 [529], 25 [625]} in some order.

        LUCY is the youngest, so we have:

        LUCY = 16 [256]

        But then ALAN has to have digits in common with CARY [576], LUCY [256], JAMES [784], but not STEVE [841]

        Which means for ALAN we need to find a square with a 7 and a 5 or a 6. The only candidate is 24 [576], but that is already used by CARY, so it is not possible to find a value for ALAN in this scenario.

        Like

    • Paul Byrne's avatar

      Paul Byrne 5:17 pm on 16 November 2022 Permalink | Reply

      Hi Jim
      Alas I’m thwarted!
      Thanks for this and all your efforts.
      They are all appreciated.
      Regards Paul

      Like

  • Unknown's avatar

    Jim Randell 7:47 am on 3 November 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 653: Hymn numbers 

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

    In Church last Sunday I studied the three numbers on the “service-board”. The first was of the appointed psalm, and the other two of the hymns. They included all the digits from 1 to 9 inclusive and were all prime numbers. (Our service-book contains 666 hymns and the normal 150 psalms).

    What were last Sunday’s numbers?

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

    [teaser653]

     
    • Jim Randell's avatar

      Jim Randell 7:48 am on 3 November 2022 Permalink | Reply

      This puzzle is very similar to Teaser 467 (and the answer is the same), although the conditions are slightly different.

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

      The following run file executes in 63ms. (Internal runtime of the generated program is 1.2ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      
      # the psalm
      "is_prime(ABC)"
      "ABC < 151"
      
      # the hymns
      "is_prime(DEF)"
      "is_prime(GHI)"
      "DEF < GHI < 667"
      
      --answer="(ABC, DEF, GHI)"
      

      Solution: Psalm 149. Hymn 263. Hymn 587.

      Like

    • GeoffR's avatar

      GeoffR 8:33 am on 3 November 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      set of int: INT = 1..9;
      var INT: A; var INT: B; var INT: C; % Psalm
      var INT: D; var INT: E; var INT: F; % Hymn 1
      var INT: G; var INT: H; var INT: I; % Hymn 2
      
      constraint all_different ([A, B, C, D, E, F, G, H, I]);
      constraint ABC < DEF /\ DEF < GHI;
      
      % Specified ranges of psalm and hymn numbers
      var 100..150:ABC = 100*A + 10*B + C;
      var 100..666:DEF = 100*D + 10*E + F;
      var 100..666:GHI = 100*G + 10*H + I;
      
      predicate is_prime(var int: x) = 
      x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) 
      ((i < x) -> (x mod i > 0));
      
      constraint sum([is_prime(ABC), is_prime(DEF), is_prime(GHI)]) == 3;
      
      solve satisfy;
      output["Sunday's numbers were " ++ show(ABC) ++ " , " ++ show(DEF)
      ++ " and " ++ show(GHI) ];
      
      % Sunday's numbers were 149 , 263 and 587
      % ----------
      % ==========
      
      

      There are many solutions without the restricted psalm and hymn number ranges.

      Like

    • GeoffR's avatar

      GeoffR 1:43 pm on 3 November 2022 Permalink | Reply

      
      from enigma import is_prime, nsplit, all_different
      
      # Find Psalm number
      for P1 in range(100,150):
          if not (is_prime(P1)): continue
          A,B,C = nsplit(P1)
          if 0 in (A,B,C):continue
          if not all_different(A,B,C):continue
          
          # Find 1st Hymn number
          for H1 in range(151,667):
              if not (is_prime(H1)):continue
              D,E,F = nsplit(H1)
              if 0 in (D,E,F): continue
              if not all_different(A,B,C,D,E,F):continue
              
              # Find 2nd Hymn number
              for H2 in range(H1+1, 667):
                  if not is_prime(H2):continue
                  G,H,I = nsplit(H2)
                  if 0 in (G,H,I):continue
                  if not all_different(A,B,C,D,E,F,G,H,I):continue
                  print(f"Sunday's numbers were {P1}, {H1}, {H2}")
      
      # Sundays numbers were 149, 263, 587
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:40 am on 1 November 2022 Permalink | Reply
    Tags:   

    Teaser 2679: Palprimes 

    From The Sunday Times, 26th January 2014 [link] [link]

    My two pals and I have been considering “palprimes” (i.e. palindromic numbers that are also prime). In particular each of us tried to find a five-figure palprime and I managed to come up with 39293. Then each of my two pals found a five-figure palprime. On comparing them we were surprised to find that overall our three palprimes used all the digits from 1 to 9.

    What were the other two five-figure palprimes?

    [teaser2679]

     
    • Jim Randell's avatar

      Jim Randell 8:41 am on 1 November 2022 Permalink | Reply

      A 5-digit palindrome is of the form XYZYX, so consists of 3 digits.

      In order for 3 of them to use all the digits from 1-9 then each digit can only appear in one prime, and X, Y, Z must be different digits.

      The setter has used 3, 9, 2, so we need to find 2 primes that use the digits 1, 4, 5, 6, 7, 8, between them.

      This Python program runs in 59ms. (Internal runtime is 370µs).

      Run: [ @replit ]

      from enigma import (
        irange, nconcat, diff, is_prime, subsets, partitions, cproduct,
        unpack, printf
      )
      
      # construct a 5-digit palindrome from digits X, Y, Z
      pal5 = unpack(lambda X, Y, Z: nconcat(X, Y, Z, Y, X))
      
      # construct palindromic primes from digits <ds>
      def primes(ds):
        for ss in subsets(ds, size=len, select="P"):
          if is_prime(pal5(ss)):
            yield ss
      
      # digits used in the first prime
      p0 = (3, 9, 2)
      assert is_prime(pal5(p0))
      
      # split the remaining digits for the other 2 primes
      for dss in partitions(diff(irange(1, 9), p0), 3):
        for (p1, p2) in cproduct(primes(ds) for ds in dss):
          printf("{p0} {p1} {p2}", p0=pal5(p0), p1=pal5(p1), p2=pal5(p2))
      

      Solution: The other palprimes are: 16561 and 78487.


      In fact as the palprimes must end (and begin) with one of 1, 3, 7, 9, we can see that the two numbers we have to find must be of the form 1???1 and 7???7. And we just need to work out how to arrange the digits 4, 5, 6, 8 in the middle of them.

      And we can make a slightly shorter program based on this observation:

      Run: [ @replit ]

      from enigma import (nconcat, is_prime, subsets, printf)
      
      # construct a 5-digit palindrome from digits X, Y, Z
      pal5 = lambda X, Y, Z: nconcat(X, Y, Z, Y, X)
      
      # digits used in the first prime
      p0 = pal5(3, 9, 2)
      assert is_prime(p0)
      
      # insert digits 4, 5, 6, 8 in 1???1, 7???7
      for (A, B, C, D) in subsets((4, 5, 6, 8), size=len, select="P"):
        (p1, p2) = (pal5(1, A, B), pal5(7, C, D))
        if is_prime(p1) and is_prime(p2):
          printf("{p0} {p1} {p2}")
      

      Like

    • GeoffR's avatar

      GeoffR 3:59 pm on 1 November 2022 Permalink | Reply

      
      from enigma import is_prime, nsplit
      
      mine = 39293  # given five digit palindromic prime
      mset = {3, 9, 2} # my digit set
      
      #  unused digits are 1, 4, 5, 6, 7, 8 for 2 palprimes
      # range of palindromic primes is from 14841 to 78687
      # find 1st palindromic prime
      for p1 in range(14841, 78687+1):
        if is_prime(p1):
          a, b, c, d, e = nsplit(p1)
          if 0 in (a, b, c, d, e):continue
          if a == e and b == d:
            s2 = set((a, b, c, d, e))
            if len(s2) == 3:  
              if len(mset.union(s2)) == 6:
                  
                # find 2nd palindromic prime 
                for p2 in range(p1+1, 78687+1):
                  if is_prime(p2):
                    f, g, h, i, j = nsplit(p2)
                    if 0 in (f, g, h, i, j):continue
                    if f == j and g == i:
                      s3 = set((f, g, h, i, j))
                      if len(s3) == 3:
                        all_set = mset.union(s2).union(s3)
                        if len(all_set) == 9:
                          print(f"Other two primes are {p1} and {p2}.")
      
      # Other two primes are 16561 and 78487.
      
      

      Like

  • Unknown's avatar

    Jim Randell 7:59 am on 30 October 2022 Permalink | Reply
    Tags: by: Comdr. A R Edwards   

    Brain-Teaser 797: Tuesday’s children 

    From The Sunday Times, 24th October 1976 [link]

    Old Andrew, our pensioner neighbour, was telling me about his equally well preserved brother, David.

    “We weren’t twins, but we were both born on a Tuesday and born on the same date in different months. In fact, David was born on the first Tuesday after my birth which occurred on the same monthly date. I am hardier, of course, born in the winter time”.

    “So, with these conditions, the interval between you couldn’t have been long”, I said.

    “Well”, replied Andrew, “it couldn’t have been any longer”.

    When was David born (day, month, year)?

    [teaser797]

     
    • Jim Randell's avatar

      Jim Randell 8:00 am on 30 October 2022 Permalink | Reply

      Assuming both brothers are aged somewhere between 65 and 122, we can start looking for birthdates sometime after 1854.

      This Python program looks for the longest gap between consecutive dates that both occur on Tuesday and the same day of the month (but different months). It runs in 60ms.

      Run: [ @replit ]

      from datetime import (date, timedelta)
      from enigma import (inc, repeat, group, tuples, Accumulator, printf)
      
      # generate possible birthdates
      def generate():
        # start from the first Tuesday in 1854
        for d in repeat(inc(timedelta(days=7)), date(1854, 1, 3)):
          # calculate age in 1976
          a = 1976 - d.year
          if a < 65: break
          yield d
      
      # group candidate birthdates by day of month
      g = group(generate(), by=(lambda d: d.day))
      
      # find maximal distance dates
      r = Accumulator(fn=max, collect=1)
      
      # consider possible days of month
      for (k, ds) in g.items():
        # and possible adjacent dates
        for (A, D) in tuples(ds, 2):
          t = D - A
          r.accumulate_data(t, (A, D))
      
      t = r.value
      printf("max gap = {t} days", t=t.days)
      for (A, D) in r.data:
        # A was born in the winter, say Dec, Jan, Feb
        if A.month in {12, 1, 2} and A.month != D.month:
          printf("-> A={A}; D={D}")
      

      Solution: David was born on Tuesday, 31st August 1880.

      So David is 96 years old at the time the puzzle was set.

      Andrew was born on Tuesday, 31st December 1878.

      So he is 97 years old at the time the puzzle was set.

      The longest possible gap between the birthdates is 609 days, which gives the following candidates:

      A=1855-07-31; D=1857-03-31; ages = 121 & 119
      A=1878-12-31; D=1880-08-31; ages = 97 & 96
      A=1883-07-31; D=1885-03-31; ages = 93 & 91

      But as A is born in the winter only the second of these candidates is viable.

      Other candidates outside the age range considered are:

      A=1850-12-31; D=1852-08-31; ages = 125 & 124
      A=1918-12-31; D=1920-08-31; ages = 57 & 56

      Like

  • Unknown's avatar

    Jim Randell 4:32 pm on 28 October 2022 Permalink | Reply
    Tags:   

    Teaser 3136: Fill cells mentally, my dear Watson 

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

    Moriarty’s papers were alight. Holmes memorised a 3×3 grid of different 4-digit values in ascending order as illustrated, from A (lowest) to I (highest), noticing that one numeral wasn’t used. Three cells, isolated from one another (no corner or edge contact), held squares of squares. Three others, similarly isolated, held cubes of non-squares. The other three held squares of non-squares. Holmes told Watson these features, specifying only the lowest value. “Not square”, remarked Watson.

    “True, and many grids match these facts. However, if I told you the positions of the squares of squares in the grid, you could deduce a majority of the other eight values (apart from the lowest one)”, replied Holmes.

    In ascending order, which values did Watson know certainly?

    [teaser3136]

     
    • Jim Randell's avatar

      Jim Randell 5:50 pm on 28 October 2022 Permalink | Reply

      I found 2 scenarios where Watson (when told the smallest number and the positions of the “squares of squares” in the grid) could work out 5 of the other numbers that must be in the grid.

      Since Watson says “Not square” I am assuming we want the scenario where the lowest number in the grid (A) is not a square (i.e. it is a cube of a non-square).

      The following Python program runs in 81ms. (Internal runtime is 29ms).

      Run: [ @replit ]

      from enigma import (
        defaultdict, irange, sq, cb, is_square, cproduct, union, diff,
        subsets, update, peek, intersect, join, printf
      )
      
      # we have 3 disjoint categories of 4-digit numbers:
      # cat1 = "square of a square" (i.e. a power of 4)
      cat1 = list(str(sq(sq(i))) for i in irange(6, 9))
      # cat2 = "cube of a non-square"
      cat2 = list(str(cb(i)) for i in irange(10, 21) if not is_square(i))
      # cat3 = "square of a non-square"
      cat3 = list(str(sq(i)) for i in irange(32, 99) if not is_square(i))
      
      # sets of isolated cells
      iso0 = [(0, 2, 6), (0, 2, 7), (0, 2, 8), (0, 5, 6), (0, 6, 8)] # with cell 0
      isox = [(1, 6, 8), (2, 3, 8), (2, 6, 8)] # without cell 0
      
      # collect candidate grids
      g = defaultdict(list)
      
      # fill out cells <cs> in grid <ns> with values from <cat>
      # making sure digit content (including <ds>) does not exceed 9
      def fill(ns, cs, cat, ds):
        # are we done?
        if not cs:
          yield (ns, ds)
        else:
          # fill out the next cell
          i = cs[0]
          lo = peek((ns[j] for j in irange(i - 1, 0, step=-1) if ns[j]), default='0000')
          hi = peek((ns[j] for j in irange(i + 1, 8) if ns[j]), default='AAAA')
          for n in cat:
            if not (n > lo): continue
            if not (n < hi): break
            ds_ = ds.union(n)
            if not (len(ds_) > 9):
              yield from fill(update(ns, [i], [n]), cs[1:], cat, ds_)
      
      # choose cat1, cat2 cells; cell 0 (A) is not a square
      for (cs1, cs2) in cproduct([isox, iso0]):
        # remaining cells are cat3
        cs = union([cs1, cs2])
        if len(cs) != 6: continue
        cs3 = diff(irange(0, 8), cs)
      
        # fill out the cells
        ns0 = [None] * 9
        # choose cat1 values
        for vs1 in subsets(cat1, size=3):
          ds1 = union(vs1)
          if len(ds1) > 9: continue
          ns1 = update(ns0, cs1, vs1)
      
          # fill out cat2 and cat3 values
          for (ns2, ds2) in fill(ns1, cs2, cat2, ds1):
            for (ns3, ds3) in fill(ns2, cs3, cat3, ds2):
              if len(ds3) == 9:
                # group grids by (<cell 0>, <cat1 positions>)
                g[(ns3[0], cs1)].append(ns3)
      
      # consider the groups
      for (k, vs) in g.items():
        # can we determine at least 5 additional values?
        xs = intersect(vs)
        if len(xs) < 6: continue
      
        # output solution
        (A, cs1) = (k[0], join("ABCDEFGHI"[x] for x in k[1]))
        printf("(A={A}, cat1={cs1}) -> {xs}", xs=join(sorted(xs), sep=", ", enc="()"))
      

      Solution: Watson knew for certain the grid contained: 1331, 2401, 4096, 4913, 5832, 6561.

      Watson was told that the lowest number was 1331, and the “squares of squares” were in positions: C, D, I.

      As 1331 = 11³, Watson can deduce that the cubes are in positions: A, F, G. And the squares of non-squares are in positions: B, E, H.

      And so he can deduce the cubes are: A=1331, F=4913, G=5832. And the powers of 4 are: C=2401, D=4096, I=6561.

      Together these 6 values use all the digits except 7.

      So the grid can be completed with any squares of non-squares that don’t use the digit 7, and fit in the appropriate position.

      The candidates are:

      B = 1369, 1444, 1521, 1600, 1681, 1849, 1936, 2025, 2116, 2209, 2304
      E = 4225, 4356, 4489, 4624, 4900
      H = 5929, 6084, 6241, 6400

      So there are 220 possible grids.

      Here is one possibility:

      red = squares of squares
      blue = cubes of non-squares
      yellow = squares of non-squares

      Like

  • Unknown's avatar

    Jim Randell 9:28 am on 27 October 2022 Permalink | Reply
    Tags:   

    Teaser 2295: Girl meets boy 

    From The Sunday Times, 17th September 2006 [link]

    In the two sums displayed, digits have consistently
    been replaced by letters, with different letters for
    different digits:

    GIRL + BOY = LOVE
    GIRLBOY = ???

    Unfortunately, I have missed out the second answer,
    but I can tell you that it is a three-letter word.

    Find my LOVER‘s number.

    [teaser2295]

     
    • Jim Randell's avatar

      Jim Randell 9:29 am on 27 October 2022 Permalink | Reply

      This Python program solves the first alphametic sum (using the [[ SubstitutedExpression.split_sum() ]] solve from the enigma.py library), and then produces potential answers for the remaining partial alphametic expression.

      It runs in 57ms. (Internal runtime is 5.8ms).

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, nsplit, substitute, join, map2str, printf)
      
      # create the alphametic puzzle
      p = SubstitutedExpression.split_sum("{GIRL} + {BOY} = {LOVE}", answer="{GIRL} - {BOY}")
      
      # solve the puzzle
      for (s, ans) in p.solve(verbose=0):
        # answer should be 3 digits
        ds = nsplit(ans)
        if len(ds) != 3: continue
        # map digits to symbols
        r = dict((v, k) for (k, v) in s.items() if k in p.symbols)
        # find the answer as a word
        w = join(r.get(d, '?') for d in ds)
        # output potential answers
        LOVER = substitute(s, "LOVER")
        printf("{ans}={w} -> LOVER={LOVER} / {r}", r=map2str(r, sep=" ", enc=""))
      

      From the output we find there are 4 candidate solutions:

      answer = 586 → IEY
      answer = 715 → YGI
      answer = 879 → BI?
      answer = 914 → VG?

      Only the third of these can form a normal word.

      By assigning an unused letter to 9, we can make 879 read as BID, BIN, BIT, BIZ. (None of which are particularly appropriate).

      Solution: LOVER = 26054.

      Like

    • GeoffR's avatar

      GeoffR 3:30 pm on 27 October 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 0..9: G; var 0..9: I; var 0..9: R; var 0..9: L; var 0..9: B;
      var 0..9: O; var 0..9: Y; var 0..9: V; var 0..9: E; 
      
      constraint G > 0 /\ B > 0 /\ L > 0;
      constraint all_different([G, I, R, L, B, O, Y, V, E]);
      
      var 1000..9999:GIRL = 1000*G +100*I + 10*R + L;
      var 100..999:BOY = 100*B + 10*O + Y;
      var 1000..9999: LOVE = 1000*L + 100*O + 10*V + E;
      var 10000..99999:LOVER = 10000*L + 1000*O + 100*V + 10*E + R;
      var 100..999: sub_res;  % result of 2nd subtraction sum
      
      % The two equations
      constraint GIRL + BOY == LOVE;
      constraint GIRL - BOY == sub_res;  % a 3-digit answer
      
      solve satisfy;
      
      output [ "LOVER = " ++ show(LOVER) ++ "  sub_res = " ++ 
      show(sub_res) ++ "\n" ++ "GIRL = " ++ show(GIRL) ++ " , " ++  
      " BOY = " ++ show(BOY) ++ ", LOVE = " ++ show(LOVE) 
      ++ "\n" ++ "[G,I,R,L,B,O,Y,V,E] = " ++ show([G,I,R,L,B,O,Y,V,E]) ];
      
      % LOVER = 23905  sub_res = 914 - which is VG?
      % GIRL = 1652 ,  BOY = 738, LOVE = 2390
      % [G,I,R,L,B,O,Y,V,E] = [1, 6, 5, 2, 7, 3, 8, 9, 0]
      % ---------- 
      % LOVER = 24096  sub_res = 715 - which is YGI
      % GIRL = 1562 ,  BOY = 847, LOVE = 2409
      % [G,I,R,L,B,O,Y,V,E] = [1, 5, 6, 2, 8, 4, 7, 0, 9]
      % ----------
      % LOVER = 24783  sub_res = 586 - which is IEY
      % GIRL = 1532 ,  BOY = 946, LOVE = 2478
      % [G,I,R,L,B,O,Y,V,E] = [1, 5, 3, 2, 9, 4, 6, 7, 8]
      % ----------
      % LOVER = 26054  sub_res = 879 - which is BI?
      % GIRL = 1742 ,  BOY = 863, LOVE = 2605
      % [G,I,R,L,B,O,Y,V,E] = [1, 7, 4, 2, 8, 6, 3, 0, 5]
      % ----------
      % ==========
      % ANS: LOVER = 26054 - for only viable 2nd equation
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 9:13 am on 25 October 2022 Permalink | Reply
    Tags:   

    Teaser 2698: Pond plants 

    From The Sunday Times, 8th June 2014 [link] [link]

    John bought some packs of pond plants consisting of oxygenating plants in packs of eight, floating plants in packs of four and lilies in packs of two, with each pack having the same price. He ended up with the same number of plants of each type. Then he sold some of these packs for twenty-five per cent more than they cost him. He was left with just fifty plants (with fewer lilies than any other) and he had recouped his outlay exactly.

    How many of these fifty plants were lilies?

    [teaser2698]

     
    • Jim Randell's avatar

      Jim Randell 9:14 am on 25 October 2022 Permalink | Reply

      If John ends up with L packs of lilies, F packs of floating plants and X packs of oxygenating plants, then we have:

      2L + 4F + 8X = 50

      And if he started out by buying a total of N packs, and selling some (at 25% markup) to cover the cost of the initial purchase, then we have:

      (5/4)(N − (X + F + L)) = N
      N = 5(X + F + L)

      And if he initially purchased n plants of each type we have:

      N = n/8 + n/4 + n/2 = (7/8)n

      This Python program runs in 56ms. (Internal runtime is 210µs).

      Run: [ @replit ]

      from enigma import (express, div, printf)
      
      # consider the make up of the 50 remaining plants
      # L = lily packs, F = floating packs; X = oxygenating packs
      # 2L + 4F + 8X = 50
      for (L, F, X) in express(50, (2, 4, 8)):
        # there are fewer lilies than any other type of plant
        if not (2 * L < min(4 * F, 8 * X)): continue
        # calculate the initial number of packs bought
        N = 5 * (L + F + X)
        # calculate the initial number of plants of each type bought
        n = div(8 * N, 7)
        if n is None: continue
        # output solution (final number of lily plants)
        printf("{x} lily plants [L={L} F={F} X={X} -> N={N} n={n}]", x=2 * L)
      

      Solution: 14 of the remaining 50 plants were lilies.

      Initially John bought 80 of each type of plant (240 plants in total) = 10 packs of oxygenators + 20 packs of floating plants + 40 packs of lilies.

      Of these 70 packs he sold 8 packs of oxygenators + 15 packs of floating plants + 33 packs of lilies. Leaving him with 14 packs.

      The sale of these 56 packs at a 25% markup exactly met the initial cost of the 70 packs.

      Leaving John with 2 packs of oxygenators (= 16 plants) + 5 packs of floating plants (= 20 plants) + 7 packs of lilies (= 14 plants). A total of 50 plants.

      Like

  • Unknown's avatar

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

    Brain-Teaser 177: The pay roll rules 

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

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

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

    Wages:

    1. Alf is to get more than Duggie.

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

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

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

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

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

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

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

    This puzzle is included in the book Sunday Times Brain Teasers (1974). The puzzle text above is taken from the book.

    [teaser177]

     
    • Jim Randell's avatar

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

      (See also: Teaser 811).

      I think the wording in this puzzle was slightly confusing.

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

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

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

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

      And you can then run it like this:

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

      Solution: The jobs and wages are as follows:

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

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

      Like

    • Hugh Casement's avatar

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

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

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

      Like

      • Jim Randell's avatar

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

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

        Like

  • Unknown's avatar

    Jim Randell 4:27 pm on 21 October 2022 Permalink | Reply
    Tags:   

    Teaser 3135: Rhythmic gymnastics 

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

    Skaredahora used three rhythmic patterns of quaver beats in a short, purely percussive, composition. His “Dotykam” rhythm has accents on beats 1, 4, 6, 7, 8, 10 & 11; “Kluc” has accents on beats 1, 8 and one particular beat in between; and “Omacka” has accents on beats 1, 2, 5, 6 & 10. Several percussion instruments are involved, each playing one of the three rhythms, but starting at different times. Overall the patterns overlap, with every beat in the composition being filled by an accent from exactly one of the instruments, and all the patterns finishing by the end of the composition.

    What is the other beat of Kluc, and what is the order of appearance of the rhythmic patterns (e.g. DOOKD)?

    [teaser3135]

     
    • Jim Randell's avatar

      Jim Randell 4:51 pm on 21 October 2022 Permalink | Reply

      This Python program considers possible additional accent positions for pattern K, and then fills out patterns with an accent on every beat, but no overlaps, until there is a continuous sequence of accents.

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

      Run: [ @replit ]

      from enigma import (irange, inf, update, peek, printf)
      
      # the rhythm patterns
      D = [1, 4, 6, 7, 8, 10, 11]
      K = [1, None, 8]  # a beat in the range [2, 7] is missing
      O = [1, 2, 5, 6, 10]
      
      # disjoint union of two sets (or None)
      def disjoint_union(ss, xs):
        ss = set(ss)
        for x in xs:
          if x in ss: return
          ss.add(x)
        return ss
      
      # fill out patterns from <ps> with an accent on every beat
      # starting from <s>
      def solve(ps, s=1, vs=[], bs=set()):
        # we are done if there are no missing beats
        if bs and len(bs) == max(bs):
          yield vs
        else:
          # find the first missing beat
          b = peek(k for k in irange(s, inf) if k not in bs)
          # and choose a pattern to start here
          for (k, pattern) in ps.items():
            bs_ = disjoint_union(bs, (x + b - 1 for x in pattern))
            if bs_ is not None:
              yield from solve(ps, s + 1, vs + [(k, b)], bs_)
      
      # consider the missing beat in pattern K
      for k in irange(2, 7):
        for vs in solve(dict(D=D, K=update(K, [1], [k]), O=O)):
          printf("k={k} vs={vs}")
      

      Solution: Kluc has an additional accent on beat 5. The order of the patterns as: OOKKDDK.

      The composition lasts for 33 beats (or a multiple of 33 beats). The patterns being:

      O @ 1: (1, 2, 5, 6, 10)
      O @ 3: (3, 4, 7, 8, 12)
      K @ 9: (9, 13, 16)
      K @ 11: (11, 15, 18)
      D @ 14: (14, 17, 19, 20, 21, 23, 24)
      D @ 22: (22, 25, 27, 28, 29, 31, 32)
      K @ 26: (26, 30, 33)

      Which covers all beats from 1 – 33.

      In order for the composition to end after 33 beats with each beat being accented by exactly one of the instruments, we need pattern K to end after the last accented beat. Pattern D can have 0 or 1 non-accented beats after the last accented beat. And pattern O could have up to 21 non-accented beats after the last accented beat.

      Like

    • NigelR's avatar

      NigelR 10:47 am on 25 October 2022 Permalink | Reply

      Less elegant than Jim’s solution, but gets the job done!

      rhy = [[1, 4, 6, 7, 8, 10, 11],[1, 2, 8],[1, 2, 5, 6,10]] 
      patts=('DO','KL','OM')
      ntotxt = lambda k : [[patts[j[0]],j[1]] for j in k]  #makes print out more intelligible using labels
      score = lambda k : sorted([y+x[1]-1 for x in k for y in rhy[x[0]]]) # creates list of beat numbers filled
      gap = lambda k : sorted([x for x in range(1,len(k)+1) if x not in k]) # identifies gaps in beat sequence
      for n in range(2,8): # iterate for missing value in KL
          rhy[1][1] = n
          seq=[[0,1]] #Initialise seq.  Entries in seq are in format:[pattern no.,starting beat]
          while seq:
              beats=score(seq)
              if dup:=len(beats)-len(set(beats)): #if overlap of beats found, go back in sequence
                  while seq and seq[-1][0]==2:
                      seq.pop()                
                  if not seq:break
                  seq[-1][0]+=1   #try next pattern in exisiting sequence
              else: #no overlaps in current sequence
                  if g:=gap(beats):  # start next pattern at first gap in sequence
                      seq.append([0,g[0]])                
                  else: # if no gap and no overlaps, solution has been found
                      print('solution found: ',ntotxt(seq), '   n=',n)
                      exit()                         
          print('options exhausted for n='         ,n)
      

      Like

  • Unknown's avatar

    Jim Randell 10:18 am on 20 October 2022 Permalink | Reply
    Tags:   

    Teaser 2746: Five finger exercise 

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

    George and Martha have a five-figure code for their burglar alarm. George commented that the three-figure number formed by the first three digits of the code equalled the sum of the cubes of those first three digits.

    Martha added that the three-figure number formed by the last three digits of the code equalled the sum of the factorials of those three digits. (She had to remind George what a factorial was — he remembered once she had pointed out that the factorial of 4 was 4×3×2×1 = 24).

    What is their code?

    This puzzle was not included in the book The Sunday Times Brain Teasers Book 1 (2019).

    This completes the archive of Teaser puzzles originally published in 2015. There is a complete archive of puzzles from 2015 – present available on S2T2, as wall as many older puzzles going back to 1949.

    [teaser2746]

     
    • Jim Randell's avatar

      Jim Randell 10:19 am on 20 October 2022 Permalink | Reply

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

      The following run file executes in 63ms. (The internal runtime of the generated program is 424µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --distinct=""
      --answer="ABCDE"
      
      "cb(A) + cb(B) + cb(C) = ABC"
      "factorial(C) + factorial(D) + factorial(E) = CDE"
      

      Solution: The code is: 37145.

      And we have:

      3³ + 7³ + 1³ = 371
      1! + 4! + 5! = 145

      Although not a condition of the puzzle, it turns out that the digits in the code are all different.

      Like

    • GeoffR's avatar

      GeoffR 10:47 am on 20 October 2022 Permalink | Reply

      from itertools import permutations
      from enigma import factorial as fact
      
      for p1 in permutations(range(1, 10), 5):
          A,B,C,D,E = p1
          if A**3 + B**3 + C**3 == 100*A + 10*B + C:
              if fact(C) + fact(D) + fact(E) == 100*C + 10*D + E:
                  code = 10000*A + 1000*B + 100*C + 10*D + E
                  print(f"Code is {code}.")
                             
      # Code is 37145.
      
      
      

      Like

  • Unknown's avatar

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

    Teaser 2538: Octahedra 

    From The Sunday Times, 15th May 2011 [link] [link]

    Fabulé’s latest jewellery creation consists of a set of identically sized regular octahedra made of solid silver. On each octahedron, Fabulé has gold-plated four of its eight equilateral triangle faces. No two octahedra are the same, but if Fabulé had to make another, then it would be necessary to repeat one of the existing designs.

    How many octahedra are there in the set?

    [teaser2538]

     
    • Jim Randell's avatar

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

      The octahedron is the dual of the cube, so colouring the faces of an octahedron is equivalent to colouring the vertices of a cube.

      I already have some code that deals with the rotations of the faces of the cube (see: Teaser 2835), so I used that to generate the rotations of an octahedron.

      We then consider all possible octahedra with 4 faces coloured gold and 4 coloured silver, and then remove those that are equivalent by rotation.

      The following Python program runs in 53ms. (Internal run time is 2.0ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, arg, printf)
      from cube import Cube
      
      # make a cube with faces labelled in powers of 2
      fs = list((1 << i) for i in irange(0, 5))
      cube = Cube(faces=fs)
      
      # extract the vertices from a cube
      def vertices(cube):
        (U, D, F, B, L, R) = cube.faces
        return (
          U + F + L, U + F + R, U + B + R, U + B + L,
          D + F + L, D + F + R, D + B + R, D + B + L,
        )
      
      # map vertex values to indices
      m = dict((v, k) for (k, v) in enumerate(vertices(cube)))
      
      # construct the rotations of the faces of an octahedron
      rot8s = list()
      for x in cube.rotations():
        rot8s.append(list(m[v] for v in vertices(x)))
      
      # now on with the puzzle ...
      
      # canonical form of a colouring of the faces of the octahedron
      def canonical(fs):
        return min(tuple(fs[r[i]] for (i, _) in enumerate(fs)) for r in rot8s)
      
      # colour k of the faces
      k = arg(4, 0, int)
      # initial colouring
      fs = list(int(i < k) for i in irange(0, 7))
      # collect all possible permutations of the faces
      rs = set(canonical(s) for s in subsets(fs, size=len, select="mP"))
      
      printf("{k} faces -> {n} different octahedra", n=len(rs))
      

      Solution: There are 7 different octahedra.

      Like

    • Hugh Casement's avatar

      Hugh Casement 12:50 pm on 18 October 2022 Permalink | Reply

      I was thinking of the octahedron as two pyramids stuck together base to base, but treating its dual may be easier to visualize.

      The seven variants are then, for example:
      1. All four upper vertices.
      2 to 5. Three upper vertices and each in turn of the lower ones.
      6. Two adjacent upper vertices and their diametric opposites.
      7. Two opposite upper vertices and their diametric opposites.
      Any further patterns turn out to be repeats, rotated or reflected in one way or another (a cube has many axes of symmetry).

      If we allow ourselves any number of gold faces, from 0 to 8, then I think there are 23 variants in all.

      Like

      • Jim Randell's avatar

        Jim Randell 11:17 am on 19 October 2022 Permalink | Reply

        Yes. I’ve updated my program to allow you to specify the number of faces to be coloured:

        0 (or 8) → 1 octahedron
        1 (or 7) → 1 octahedron
        2 (or 6) → 3 octahedra
        3 (or 5) → 3 octahedra
        4 → 7 octahedra

        Which gives the 23 essentially different colourings.

        Like

    • Jim Olson's avatar

      Jim Olson 6:05 pm on 18 October 2022 Permalink | Reply

      I looked at this teaser the same as Hugh and came up with 23.

      Like

  • Unknown's avatar

    Jim Randell 4:00 pm on 15 October 2022 Permalink | Reply
    Tags: ,   

    Teaser 3134: Stringing along [revised] 

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

    An artist hammered thin nails from a pack of 40 into a board to form the perimeter of a rectangle with a 1 cm gap between adjacent nails. He created a work of art by stringing a long piece of wire from one nail to another, such that no section of wire was parallel to an edge of the rectangle. The wire started and ended at two different nails, no nail was used more than once and the length of the wire was a whole number of cm. No longer wire was possible that satisfied the above conditions.

    What were the dimensions of the rectangle and the length of the wire chain (in cm)?

    This is a revised version of the puzzle posted as Teaser 3134. The underlined text has been changed from the previously published version on The Sunday Times website.

    This is the version of the puzzle that appeared in the printed newspaper.

    [teaser3134]

     
    • Jim Randell's avatar

      Jim Randell 4:01 pm on 15 October 2022 Permalink | Reply

      Having solved the (more general) previously published version of this puzzle [link], it is a simple case of adapting my program to account for the additional condition.

      For this version I am assuming we want to find the maximum achievable total length of wire between the nails that can be made using a rectangle constructed from up to 40 nails, with the wire strung between nails where no nail is used more than once and consecutive nails are on different edges of the rectangle (i.e. each linear section of wire must cross part of the interior of the rectangle, and be a whole number of centimetres long), and no linear section of wire is parallel to the edges of the rectangle.

      You can think of the wire being available in various stiff lengths with looped ends, but these individual links can only ever fit between nails that are a whole number of centimetres apart. We wish to make a chain of these wire links, where each link goes from the end of the previous link to a currently unoccupied nail. What is the longest possible chain that can be made, on a rectangle whose perimeter is constructed from no more than 40 nails spaced 1 cm apart, where each link must cross the interior of the rectangle not parallel to the edges of the rectangle, and the start and end of the complete chain are on different nails.

      This Python program runs in 104ms.

      Run: [ @replit ]

      from enigma import (irange, ihypot, chain, Accumulator, printf)
      
      # generate possible <x> by <y> rectangles, using (up to) <n> nails
      def generate(n):
        for y in irange(2, n // 4):
          for x in irange(2, (n - 2 * y) // 2):
            yield (x, y)
      
      # find paths by adding nails to <vs>
      def solve(x, y, nails, vs, d=0):
        # return the current path
        yield (d, vs)
        # choose the next nail
        (x1, y1) = vs[-1]
        for v in nails:
          (x2, y2) = v
          (dx, dy) = (x2 - x1, y2 - y1)
          if dx == 0 or dy == 0: continue
          dd = ihypot(dx, dy)
          if dd is not None:
            yield from solve(x, y, nails.difference([v]), vs + [v], d + dd)
      
      # collect maximal distance paths
      r = Accumulator(fn=max, collect=1)
      
      # consider possible rectangles
      for (x, y) in generate(40):
        # collect possible edge nails
        nails = set()
        for i in irange(0, x - 1):
          nails.update([(i, 0), (x - i, y)])
        for i in irange(0, y - 1):
          nails.update([(0, y - i), (x, i)])
        # start in the bottom/left quadrant
        botm = ((i, 0) for i in irange(0, x // 2))
        left = ((0, i) for i in irange(1, y // 2))
        for v in chain(botm, left):
          for (d, vs) in solve(x, y, nails.difference([v]), [v]):
            r.accumulate_data(d, (x, y, vs))
      
      # output solution(s)
      printf("max wire length = {r.value} cm")
      for (x, y, vs) in r.data:
        printf("-> {x} cm by {y} cm grid {vs}")
      

      Solution: The rectangle was 12 cm × 8 cm. The total length of wire used was 102 cm.


      Here is a diagram of the maximal length layout:

      Like

      • Jim Randell's avatar

        Jim Randell 11:28 am on 28 October 2022 Permalink | Reply

        In my program above, in order to get a total distance that is a whole number we require the individual segments between consecutive pins in the stringing to be a whole number.

        This is a reasonable assumption. As we can show that if we have a collection of positive integers, and at least one of them has a square root that is irrational, then the sum of the square roots of the entire collection is irrational. (See below [*]).

        However, this does not mean we cannot construct stringings that are very close to an integer value using segments that are not themselves integers. And this lets us find solutions that are much longer than the published solution.

        For example here is a stringing on the 12cm × 8 cm grid that has a total length of 440.999925 cm (i.e. 750 nanometres short of 441 cm). It would be practically impossible to tell this apart from a 441 cm length of wire.

        Note that in this construction every pin is visited.

        If we want more accuracy we can get within 6nm of 438 cm.


        [*] The core of the proof is:

        Suppose x, y are integers, such that at least one of √x and √y are irrational.

        Then (x − y) is also an integer, and:

        (x − y) = (√x + √y)(√x − √y)

        If (√x + √y) is rational (= p), then:

        (√x − √y) = (x − y)/(√x + √y) is also rational (= q)

        But then:

        √x = (p + q)/2 is rational; and
        √y = (p − q)/2 is rational

        Which is a contradiction, hence the sum (√x + √y) is irrational.

        Like

    • Hugh Casement's avatar

      Hugh Casement 10:16 am on 16 October 2022 Permalink | Reply

      The change in wording makes quite a difference to the total length, at least with the solution I worked out.

      The puzzle does not seem to be well thought out. That the nails are thin clearly means that we are to ignore their diameter (and the thickness of the wire). But with centimetres as the unit they are not nails at all but pins, and it’s fine fuse wire. Inches would have been somewhat less unrealistic.

      I also feel that if the “artist” had allowed himself more nails, the resulting pattern could have been more interesting. For example, 12 occurs in the Pythagorean triangles (9, 12, 15) and (12, 16, 20) as well as (5, 12, 13), which would presumably give more scope. But the length + width of the rectangle must not exceed 20, so we are much restricted.

      Like

      • Jim Randell's avatar

        Jim Randell 12:03 pm on 17 October 2022 Permalink | Reply

        @Hugh: Yes for the original wording I found a maximal solution with 24 links. For this revised version there are only 10 links. The rectangle was the same in both cases.

        Like

  • Unknown's avatar

    Jim Randell 4:35 pm on 14 October 2022 Permalink | Reply
    Tags:   

    Teaser 3134: Stringing along 

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

    An artist hammered thin nails from a pack of 40 into a board to form the perimeter of a rectangle with a 1 cm gap between adjacent nails. He created a work of art by stringing a long piece of wire from one nail to another, such that consecutive nails were on different edges of the rectangle. The wire started and ended at two different nails, no nail was used more than once and the length of the wire was a whole number of cm. No longer wire was possible that satisfied the above conditions.

    What were the dimensions of the rectangle and the length of the wire chain (in cm)?

    The wording of this puzzle was later revised. The underlined section was changed to a more restrictive condition.

    See: Teaser 3134: Stringing along [revised] for the revised puzzle.

    [teaser3134]

     
    • Jim Randell's avatar

      Jim Randell 5:07 pm on 14 October 2022 Permalink | Reply

      (Note: It seems that my initial interpretation of the puzzle may not be the intended one. See below for a more likely interpretation).

      It seems we would need to know the dimensions of the rectangle before we determined the maximum length of wire. I considered all rectangles that can be constructed using 40 nails, and found the maximum possible length of wire that touches each edge of the rectangle no more than once.

      This Python program tries all possible rectangles, and all possible stringings.

      It runs in 275ms.

      Run: [ @replit ]

      from enigma import (irange, inf, subsets, tuples, hypot, intr, Accumulator, printf)
      
      # generate possible <x> by <y> rectangles, using <n> nails
      def generate(n):
        for y in irange(3, inf):
          x = (n - 2 * y) // 2
          if x < 3: break
          yield (x, y)
      
      # calculate length of wire required between points <vs>
      def dist(vs):
        d = 0
        for ((x1, y1), (x2, y2)) in tuples(vs, 2):
          d += hypot(x2 - x1, y2 - y1)
        if abs(d - intr(d)) < 1e-6:
          return d
      
      # collect maximal stringings
      r = Accumulator(fn=max, collect=1)
      
      # consider possible rectangles
      for (x, y) in generate(40):
        # choose top, bottom nails
        for (x1, x2) in subsets(irange(1, x - 1), size=2, select="R"):
          (T, B) = ((x1, y), (x2, 0))
          # choose left, right nails
          for (y1, y2) in subsets(irange(1, y - 1), size=2, select="P"):
            (L, R) = ((0, y1), (x, y2))
            # calculate possible lengths
            for vs in [(T, R, B, L), (T, R, L, B), (T, B, R, L)]:
              d = dist(vs)
              if d is None: continue
              printf("[({x}, {y}) {d:g} = {vs}]")
              r.accumulate_data(d, (x, y, vs))
      
      printf("max wire length = {r.value:g} cm")
      for (x, y, vs) in r.data:
        printf("-> {x} cm by {y} cm grid {vs}")
      

      Solution: The rectangle was 6 cm × 14 cm, and the wire was 37 cm long.


      This program finds what I assume is the required answer (where each section of the wire is an integer length). But by changing the tolerance at line 15 we can find a longer wire that is sufficiently close to an exact whole number of centimetres that it would be physically impossible to determine that it wasn’t.

      So we can create a stringing on a 6 cm × 14 cm rectangle that uses 40.0007 cm of wire.

      And also a stringing on a 4 cm × 16 cm rectangle that uses 44.99 cm of wire.

      Like

      • Jim Randell's avatar

        Jim Randell 11:19 pm on 14 October 2022 Permalink | Reply

        Using a different (and more likely) interpretation of the problem (see below), where each edge of the rectangle may be visited many times (and allowing the corner nails to be used), but each linear section of wire is a whole number of centimetres, we can get a much longer wire.

        To be clear: I am assuming we want to find the maximum achievable total length of wire between the nails that can be made using a rectangle constructed from up to 40 nails, with the wire strung between nails where no nail is used more than once and consecutive nails are on different edges of the rectangle (i.e. each linear section of wire must cross part of the interior of the rectangle, and be a whole number of centimetres long).

        Here is a quick program to explore that (although there may be some duplication in the patterns found).

        It runs in 942ms.

        Run: [ @replit ]

        from enigma import (irange, ihypot, chain, Accumulator, printf)
        
        # generate possible <x> by <y> rectangles, using (up to) <n> nails
        def generate(n):
          for y in irange(2, n // 4):
            for x in irange(2, (n - 2 * y) // 2):
              yield (x, y)
        
        # find paths by adding nails to <vs>
        def solve(x, y, nails, vs, d=0):
          # return the current path
          yield (d, vs)
          # choose the next nail
          (x1, y1) = vs[-1]
          for v in nails:
            (x2, y2) = v
            (dx, dy) = (x2 - x1, y2 - y1)
            if dx == 0 and (x1 == 0 or x1 == x): continue
            if dy == 0 and (y1 == 0 or y1 == y): continue
            dd = ihypot(dx, dy)
            if dd is not None:
              yield from solve(x, y, nails.difference([v]), vs + [v], d + dd)
        
        # collect maximal distance paths
        r = Accumulator(fn=max, collect=1)
        
        # consider possible rectangles
        for (x, y) in generate(40):
          # collect possible edge nails
          nails = set()
          for i in irange(0, x - 1):
            nails.update([(i, 0), (x - i, y)])
          for i in irange(0, y - 1):
            nails.update([(0, y - i), (x, i)])
          # start in the bottom/left quadrant
          botm = ((i, 0) for i in irange(0, x // 2))
          left = ((0, i) for i in irange(1, y // 2))
          for v in chain(botm, left):
            for (d, vs) in solve(x, y, nails.difference([v]), [v]):
              r.accumulate_data(d, (x, y, vs))
        
        # output solution(s)
        printf("max wire length = {r.value} cm")
        for (x, y, vs) in r.data:
          printf("-> {x} cm by {y} cm grid {vs}")
        

        Solution: The rectangle was 12 cm × 8 cm. The total length of wire used was 258 cm.


        Here is a diagram of one maximal length arrangement (there are several):

        Like

    • Brian Gladman's avatar

      Brian Gladman 9:56 pm on 14 October 2022 Permalink | Reply

      The answer for this one depends on the assumptions being made. I found a different answer by assuming that the wire length was exact and that not all nails are used. I then looked for a number of Pythagorean triangle hypotenuses with a maximum sum (not necessarily alternating x, y edges).

      Like

      • Jim Randell's avatar

        Jim Randell 10:51 pm on 14 October 2022 Permalink | Reply

        @Brian: I was assuming that each edge of the rectangle was only used once (and the corners not at all). But the puzzle text only says consecutive nails are on different edges, so if we can visit edges more than once then we can use much more wire. (And get a more artful display into the bargain).

        In this case I assumed distance between each consecutive pair of nails was an exact whole number of cm, so that the total length of the wire is as well.

        (Although it still seems like we would need to know the size of the rectangle the artist had constructed before we could find the maximum length of wire he could use).

        Like

  • Unknown's avatar

    Jim Randell 8:04 am on 13 October 2022 Permalink | Reply
    Tags:   

    Teaser 2737: Pinpoint accuracy 

    From The Sunday Times, 8th March 2015 [link] [link]

    George and Martha’s bank PIN is an odd four-figure number. George did some calculations and wrote down three numbers: the first was the sum of the four digits in the PIN; the second was the average of the four digits; and the third was the product of the four digits.

    Then Martha multiplied these three numbers together and was surprised that the answer equalled the original PIN.

    What is their PIN?

    This puzzle was not included in the book The Sunday Times Brain Teasers Book 1 (2019).

    [teaser2737]

     
    • Jim Randell's avatar

      Jim Randell 8:05 am on 13 October 2022 Permalink | Reply

      None of the individual digits can be 0, as then the product of the digits would also be 0.

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

      The following run file executes in 65ms. (Internal runtime of the generated code is 1.1ms)

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --distinct=""
      --digits="1-9"
      --answer="ABCD"
      
      # The following multiplied together give the value of the PIN
      # (i) the sum of the digits;
      # (ii) the mean of the digits (= (i) / 4);
      # (iii) the product of the digits
      "sq(A + B + C + D) * (A * B * C * D) == 4 * ABCD"
      
      # the PIN is odd
      "D % 2 = 1"
      

      Solution: The PIN is: 1715.

      Like

    • GeoffR's avatar

      GeoffR 12:16 pm on 13 October 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;
      constraint D mod 2 == 1; % PIN number is odd
      
      var 1000..9999:PIN = 1000*A + 100*B + 10*C + D;
      
      % Num 1the sum of the four digits in the PIN
      var 4..36:Num1 = A + B + C + D;
      
      var 4..36: Num2; %  average = Num2 / 4
      constraint Num2 == A + B + C + D;
      
      % Num3 was the product of the four digits
      var 1..6561: Num3 == A * B * C * D ; % UB = 9^4
      
      % Martha multiplied these three numbers together
      constraint 4 * PIN == Num1 * Num2 * Num3;
      
      solve satisfy;
      
      output[ "PIN = " ++ show(PIN) ];
      
      % PIN = 1715
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:46 am on 11 October 2022 Permalink | Reply
    Tags:   

    Teaser 2676: New diary 

    From The Sunday Times, 5th January 2014 [link] [link]

    My diary has this design on the cover:

    In this 2-by-3 grid there are 12 junctions (including the corners), some pairs of which are joined by a straight line in the grid. In fact there are 30 such pairs.

    The diary publisher has been using such grids of various sizes for years and the 1998 diary was special because its grid had precisely 1998 pairs of junctions joined by lines. Within the next 10 years they will once again be able to produce a special diary where the number of joined pairs equals the year.

    (a) What was the grid size on the 1998 diary?
    (b) What is the year of this next special diary?

    [teaser2676]

     
    • Jim Randell's avatar

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

      In an x by y grid each horizontal line (of length x) has:

      1 pair that are distance x apart
      2 pairs that are distance (x − 1) apart
      3 pairs that are distance (x − 2) apart

      x pairs that are distance 1 apart

      Giving a total of tri(x) pairs on that line, and there are (y + 1) horizontal lines.

      So the total number of horizontal pairs is:

      h(x, y) = (y + 1) tri(x)

      Similarly the total number of vertical pairs is:

      v(x, y) = (x + 1) tri(y)

      And so the total number of pairs is:

      pairs(x, y) = h(x, y) + v(x, y)
      pairs(x, y) = (y + 1)x(x + 1)/2 + (x + 1)y(y + 1)/2
      pairs(x, y) = (x + 1)(y + 1)(x + y)/2

      This Python program runs in 54ms. (Internal runtime is 488µs).

      from enigma import (irange, inf, div, decompose, printf)
      
      # count pairs on an x by y grid
      pairs = lambda x, y: div((x + 1) * (y + 1) * (x + y), 2)
      
      # check the 3x2 grid gives 30 pairs
      assert pairs(3, 2) == 30
      
      # generate (x, y, pairs(x, y)) values
      def generate():
        # consider increasing x + y values
        for t in irange(2, inf):
          for (x, y) in decompose(t, 2, increasing=1, sep=0, min_v=1):
            yield (x, y, pairs(x, y))
      
      # find the answers
      a = b = 0
      for (x, y, n) in generate():
        # (a) look for grids with 1998 pairs
        if n == 1998:
          printf("(a) {x} x {y} -> {n}")
          a = 1
        # (b) looks for grid in the range [2015, 2024]
        if 2014 < n < 2025:
          printf("(b) {x} x {y} -> {n}")
          b = 1
        # are we done?
        if a and b: break
      

      Solution: (a) The 1998 diary had a 2 × 35 grid; (b) The next special diary is for 2016.

      We have:

      1998 = pairs(2, 35)
      2016 = pairs(5, 23) = pairs(11, 13)


      Alternatively we can start with the required number of pairs n and produce possible (x, y) grids, by considering the divisors of n.

      Which gives us a shorter program.

      This Python program runs in 56ms. (Internal runtime is 622µs).

      from enigma import (divisors_pairs, irange, printf)
      
      def solve(n):
        for (a, b) in divisors_pairs(2 * n):
          for (x, y) in divisors_pairs(b - a - 1):
            if x + y == a:
              yield (x, y)
      
      # years to investigate
      qs = {
        'a': [1998],
        'b': irange(2015, 2024),
      }
      
      # solve the puzzle
      for k in sorted(qs.keys()):
        for n in qs[k]:
          for (x, y) in solve(n):
            printf("({k}) {x} x {y} -> {n}")
      

      Like

    • Hugh Casement's avatar

      Hugh Casement 3:18 pm on 11 October 2022 Permalink | Reply

      Of course 2016 is in the past for us, and the next will be 2025:
      1 × 44 or 4 × 26 or 8 × 17.

      However, I maintain that those are the sizes of the arrays of square cells;
      the grids (of lines) are 2 × 45, 5 × 27, and 9 × 18.

      Like

  • Unknown's avatar

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

    Brain-Teaser 190: Towns and families 

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

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

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

    Where were the Carters married?

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

    [teaser190]

     
    • Jim Randell's avatar

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

      This Python program tries all possible arrangements.

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

      Run: [ @replit ]

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

      Solution: The Carters were married in Belfast.

      The full solution is:

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

      Like

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