Tagged: by: Victor Bryant Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 8:35 am on 8 May 2026 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2441: [Letter grid] 

    From The Sunday Times, 5th July 2009 [link]

    Your task today is to place nine different letters of the alphabet in a 3-by-3 grid.

    The letters must include S, A, M, E. You must do this in such a way that, when the letters are given their numerical value (A = 1, B = 2, C = 3, etc.) then each row, column and diagonal of the grid has the same total.

    You will find that the other five letters used can swiftly be rearranged into a common word.

    What is that word?

    This puzzle was originally published with no title.

    [teaser2441]

     
    • Jim Randell's avatar

      Jim Randell 8:36 am on 8 May 2026 Permalink | Reply

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

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

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # assign values from 1 - 26 to the grid:
      #
      #   A B C
      #   D E F
      #   G H I
      #
      # to make a magic square with sum 3E
      --base="27"
      --digits="1-26"
      
      # rows sum to 3E
      "A + B + C == 3 * E"
      "D + F == 2 * E"
      "G + H + I == 3 * E"
      
      # cols sum to 3E
      "A + D + G == 3 * E"
      "B + H == 2 * E"
      "C + F + I == 3 * E"
      
      # diags sum to 3E
      "A + I == 2 * E"
      "C + G == 2 * E"
      
      # required values (S, A, M, E)
      "{1, 5, 13, 19}.issubset({A, B, C, D, E, F, G, H, I})"
      
      # remove symmetric arrangements
      "A < C" "C < G" "A < I"
      
      --template=""
      --answer="ordered(A, B, C, D, E, F, G, H, I)"
      

      The completed grid is:

      [ C  (3) | U (21) | I  (9) ]      [ S (19) | A  (1) | M (13) ]
      [ Q (17) | K (11) | E  (5) ]  ->  [ E  (5) | K (11) | Q (17) ]
      [ M (13) | A  (1) | S (19) ]      [ I  (9) | U (21) | C  (3) ]

      Each row, column and diagonal has a sum of 33.

      And the corresponding letters are: SAME (= 19, 1, 13, 5) + KQIUC (= 11, 17, 9, 21, 3).

      Solution: The word is: QUICK.

      Like

  • Unknown's avatar

    Jim Randell 7:57 am on 3 May 2026 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 3319: Watch out! 

    From The Sunday Times, 3rd May 2026 [link] [link]

    One January a few years ago I was given a new watch that also displayed the date. It did this by turning two wheels, one with 0-3 on it and the other with 0-9. So, if left running, it displayed in turn 00, 01, 02, … 29, 30, 31, … 39, 00, 01, … . Therefore it needed resetting each month, but I never bothered. The date was correctly displayed when I received the watch but then was incorrect for every other month of that year. However, on my birthday that year the display showed an odd number that was in fact the correct date but with the digits reversed! I had to console myself with the fact that, before the watch was 30 years old, on one occasion my birthday would be correctly displayed!

    When is my birthday (day and month)?

    [teaser3319]

     
    • Jim Randell's avatar

      Jim Randell 8:13 am on 3 May 2026 Permalink | Reply

      We only need to consider 4 candidate start years (a leap year, +1, +2, +3).

      This Python program runs in 63ms. (Internal runtime is 755µs).

      from datetime import (date, timedelta)
      from enigma import (irange, repeat, inc, nrev, printf)
      
      # start in year y
      def solve(y):
        # look for possible birthdays in the first year
        bds = set()
        # consider odd displays
        n = 1
        for t in repeat(inc(timedelta(days=2)), date(y, 1, 1)):
          if t.year > y: break
          # display must be incorrect for months other than January
          if n == t.day:
            if t.month > 1: return
          elif n == nrev(t.day, 2):
            # display is the reverse of the day
            bds.add((t, n))
          # advance the display
          n = (n + 2) % 40
      
        # consider birthdays up to 29 years later
        for (t, n) in bds:
          for i in irange(1, 29):
            t1 = t.replace(year=y + i)
            n1 = (n + (t1 - t).days) % 40
            if n1 == t1.day:
              printf("{t} ({n:02d}) -> {t1} ({n1:02d}) [+{i} years]")
      
      # consider 4 candidate years ("a few years ago")
      for y in [2020, 2021, 2022, 2023]:
        solve(y)
      

      Solution: Your birthday is 30th August.

      The setter was given the watch in a leap year (2020 for example), when it showed the correct date for January (but not in any other months).

      On 2020-08-30 the watch displays “03” (instead of “30”). And 28 years later on 2048-08-30 the watch will display “30”.

      There are other birthdays in the first year when the watch will display a date that is incorrect, but the actual date reversed:

      10th February (displays “01” in any year; displays “10” after 55 years)
      10th June (displays “01” in a non-leap year)
      31st July (displays “13” in a leap year; displays “31” after 34 years)

      Like

    • Ruud's avatar

      Ruud 12:57 pm on 3 May 2026 Permalink | Reply

      import datetime
      import istr
      import calendar
      
      istr.int_format("02")
      
      
      def display_time(start_date, year, month, day):
          diff = (datetime.datetime(year, month, day) - start_date).days
          return istr((diff + 1) % 40)
      
      
      for year in (2021, 2022, 2023, 2024):
          start_date = datetime.datetime(year, 1, 1)
          for month in range(2, 13):
              if display_time(start_date, year, month, 1) == 1:
                  break
          else:
              for month in range(1, 13):
                  for day in range(1, calendar.monthrange(year, month)[1]):
                      displayed = display_time(start_date, year, month, day)
                      if displayed.is_odd() and day == displayed.reversed() and displayed.reversed() != displayed:
                          correct_years = [year for year in range(year, year + 30) if display_time(start_date, year, month, day) == day]
                          if len(correct_years) == 1:
                              print(year, correct_years[0], month, day)
      

      Like

      • Jim Randell's avatar

        Jim Randell 4:38 pm on 3 May 2026 Permalink | Reply

        @Ruud: Python’s [[ range(a, b) ]] builtin excludes the final value (b in this case). So your code at line 20 will miss checking the final day of the month.

        The enigma.py library has the [[ irange(a, b) ]] function, if you want to include both endpoints.

        Like

        • Ruud's avatar

          Ruud 4:47 pm on 3 May 2026 Permalink | Reply

          Well spotted. It should read

          import datetime
          import istr
          import calendar
          
          istr.int_format("02")
          
          
          def display_time(start_date, year, month, day):
              diff = (datetime.datetime(year, month, day) - start_date).days
              return istr((diff + 1) % 40)
          
          
          for year in (2021, 2022, 2023, 2024):
              start_date = datetime.datetime(year, 1, 1)
              for month in range(2, 13):
                  if display_time(start_date, year, month, 1) == 1:
                      break
              else:
                  for month in range(1, 13):
                      for day in range(1, calendar.monthrange(year, month)[1] + 1):
                          displayed = display_time(start_date, year, month, day)
                          if displayed.is_odd() and day == displayed.reversed() and displayed.reversed() != displayed:
                              correct_years = [year for year in range(year, year + 30) if display_time(start_date, year, month, day) == day]
                              if len(correct_years) == 1:
                                  print(year, correct_years[0], month, day)
          

          Like

  • Unknown's avatar

    Jim Randell 9:21 am on 1 May 2026 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2445: [Divisible by 9] 

    From The Sunday Times, 2nd August 2009 [link]

    I started with a list of six numbers (with no leading zeros, of course). Five of the six numbers were divisible by 9.

    Then I coded the numbers by consistently using letters for digits, with different letters representing different digits.

    In this way, the list became:

    SETTER
    SENT
    PRETTY
    EASY
    SUNDAY
    TEASER

    Which of those words represented the number that was not divisible by 9?

    This puzzle was originally published with no title.

    [teaser2445]

     
    • Jim Randell's avatar

      Jim Randell 9:22 am on 1 May 2026 Permalink | Reply

      Here is a straightforward solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      It runs in 106ms. (Internal runtime of the generated code is 21ms).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --distinct="ADENPRSTUY"
      --invalid="0,EPST"
      
      # exactly five of the six numbers are divisible by 9
      # so just 1 of them isn't, identify it as X = 1..6
      --invalid="0|7-9,X"
      
      "(X == 1) == (SETTER % 9 != 0)"
      "(X == 2) == (SENT % 9 != 0)"
      "(X == 3) == (PRETTY % 9 != 0)"
      "(X == 4) == (EASY % 9 != 0)"
      "(X == 5) == (SUNDAY % 9 != 0)"
      "(X == 6) == (TEASER % 9 != 0)"
      
      --answer="X"
      
      --template="(SETTER) (SENT) (PRETTY) (EASY) (SUNDAY) (TEASER) (X)"
      

      Solution: TEASER is not divisible by 9.

      There are 36 ways to assign the digits to the letters, but in all cases TEASER is the odd number out.


      If the puzzle had been set so that just one of the words is not divisibly by 29, then there is a single way to assign digits to the letters.

      Like

      • Ruud's avatar

        Ruud 11:07 am on 1 May 2026 Permalink | Reply

        Brute force reveals that there are 36 different solutions, all resulting in TEASER to be the one that’s not divisible by 9.

        import peek
        import istr
        
        count = 0
        for s, e, t, r, n, p, y, a, u, d in istr.permutations(range(10)):
            if s * p * e * t: # may not start with 0
                not_divisible_by_9 = ""
                for name in "setter sent pretty easy sunday teaser".split():
                    if not istr(f":={name}").is_divisible_by(9):
                        if not_divisible_by_9:
                            break
                        not_divisible_by_9 = name
                else:
                    if not_divisible_by_9:
                        count += 1
                        peek(count, not_divisible_by_9, setter, sent, pretty, easy, sunday, teaser)
        

        Like

  • Unknown's avatar

    Jim Randell 6:14 am on 22 March 2026 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 3313: Rod’s rods 

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

    Rodney has a set of small identical rods, which he uses to display numbers in “calculator” style. The “0” requires six rods, the “1” requires two rods, the “2”, “3” and “5” require five each, the “4” requires four, the “6” and “9” require six, the “7” requires three and the “8” requires seven.

    He recently showed me three numbers, each made by using the whole set. The lowest was a three-figure perfect square, the next was a palindromic prime, and the highest was a four-figure prime with its four different digits increasing from left to right.

    What were those three numbers?

    [teaser3313]

     
    • Jim Randell's avatar

      Jim Randell 6:30 am on 22 March 2026 Permalink | Reply

      This Python program collects possible candidate numbers in the three categories, grouping them by the total number of segments used. It then looks for a total common to each of the categories, and finds suitable numbers from each of the categories.

      It runs in 75ms. (Internal runtime is 5.9ms).

      from enigma import (
        enigma, defaultdict, primes, nsplit, powers,
        intersect, cproduct, true, is_palindrome, printf
      )
      
      # number of segments per digit
      #      0  1  2  3  4  5  6  7  8  9
      seg = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6]
      
      # collect numbers from <ns> by the total count of segments used
      # where the digits of the number satisfy function <fn>
      # returns dict: <segment-count> -> <numbers>
      def collect(ns, fn=true):
        r = defaultdict(list)
        for n in ns:
          ds = nsplit(n)
          if fn(ds):
            k = sum(seg[d] for d in ds)
            r[k].append(n)
        return r
      
      # look for strictly increasing sequences
      is_increasing = lambda ns: enigma.is_increasing(ns, strict=1)
      
      # collect number categories by total segment count
      nss = [
        # first number (3-digit perfect squares)
        collect(powers(10, 31, 2)),
        # second number (3- and 4-digit palindromic primes)
        collect(primes.between(100, 9999), is_palindrome),
        # third number (4-digit primes, with digits in increasing order)
        collect(primes.between(1000, 9999), is_increasing),
      ]
      
      # look for common total segments
      for k in intersect(ns.keys() for ns in nss):
        for ss in cproduct(ns[k] for ns in nss):
          if is_increasing(ss):
            # output solution
            printf("{k} segments -> {ss}")
      

      Solution: The three numbers are: 225, 353, 1237.

      The only total number of segments that can make numbers in each category is 15. And we have the following possibilities with 15 segments:

      first number = 225, 484, 676
      second number = 353
      third number = 1237

      Since the numbers must be in increasing order, there is only one possible arrangement.

      Like

      • Jim Randell's avatar

        Jim Randell 4:03 pm on 24 March 2026 Permalink | Reply

        By noting that all 4-digit palindromes are multiples of 11:

        XYYX = 1001.X + 110.Y = 11(91.X + 10.Y)

        We can see that the second number must be a 3-digit prime, allowing us to test fewer candidates, and this provides a modest improvement in the runtime of my program above (down to 3.9ms). (I’m not sure if other programs posted that only check 3-digit palindromes use this fact, or just missed the possibility that the second number could have 4 digits).

        Also, as noted in Geoff’s program below it is faster to generate 3-digit palindromes and 4-digit numbers with increasing digits and then test them for primality, instead of starting with primes.

        The following (more compact) variation on my original program has an internal runtime of 625µs.

        from enigma import (
          irange, subsets, is_prime, nsplitter, nconcat, group,
          powers, npalindromes, intersect, cproduct, is_increasing, printf
        )
        
        # number of segments per digit
        #      0  1  2  3  4  5  6  7  8  9
        seg = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6]
        
        # count the total number of segments used in a number
        segs = lambda n: sum(seg[d] for d in nsplitter(n))
        
        # collect number categories by total segment count
        collect = lambda ns: group(ns, by=segs)
        nss = [
          # first number (3-digit perfect squares)
          collect(powers(10, 31, 2)),
          # second number (3-digit palindromic primes) [there are no 4-digit palindromic primes]
          collect(n for n in npalindromes(3) if is_prime(n)),
          # third number (4-digit primes, with digits in increasing order)
          collect(n for n in subsets(irange(1, 9), size=4, fn=nconcat) if is_prime(n)),
        ]
        
        # look for common total segments
        for k in intersect(ns.keys() for ns in nss):
          for ss in cproduct(ns[k] for ns in nss):
            if is_increasing(ss, strict=1):
              # output solution
              printf("{k} segments -> {ss}")
        

        Like

    • Ruud's avatar

      Ruud 7:19 am on 22 March 2026 Permalink | Reply

      import peek
      import istr
      
      
      rods = [6, 2, 5, 5, 4, 5, 6, 3, 8, 6]
      
      for n0 in istr.squares(100, 1000):
          for n1 in istr.primes(n0, 10000):
              if n1.is_palindrome():
                  for n2 in istr.primes(max(n1, 1000), 10000):
                      if n2[0] < n2[1] < n2[2] < n2[3]:
                          if len(set(sum(rods[int(i)] for i in n) for n in (n0, n1, n2))) == 1:
                              peek(n0, n1, n2)
      

      Like

    • ruudvanderham's avatar

      ruudvanderham 10:23 am on 22 March 2026 Permalink | Reply

      As a one liner:

      import istr
      
      print(
          *(
              (n0, n1, n2)
              for n0 in istr.squares(100, 1000)
              for n2 in filter(istr.is_increasing, istr.primes(1000, 10000))
              for n1 in filter(istr.is_palindrome, istr.primes(n0 + 1, n2))
              if len({sum([6, 2, 5, 5, 4, 5, 6, 3, 8, 6][i] for i in map(int, n)) for n in (n0, n1, n2)}) == 1
          )
      )
      

      Like

    • GeoffR's avatar

      GeoffR 5:12 pm on 22 March 2026 Permalink | Reply

      I used Claude AI to find to refine its original solution, and to produce shorter, faster code. This solution ran in 37 msec on my I7 laptop.

      # Teaser 3313 -  Rod’s rods - Code by Claude AI
      
      from itertools import combinations
      from time import perf_counter
      
      t0 = perf_counter()
      
      # --- Sieve of Eratosthenes ---
      def sieve(limit):
          is_prime = bytearray([1]) * (limit + 1)
          is_prime[0] = is_prime[1] = 0
          for i in range(2, int(limit**0.5) + 1):
              if is_prime[i]:
                  is_prime[i*i::i] = bytearray(len(is_prime[i*i::i]))
          return is_prime
      
      RODS = [6,2,5,5,4,5,6,3,7,6]
      is_prime = sieve(9999)
      
      # Rod counts via integer arithmetic (no str() conversion)
      def rc3(n): return RODS[n%10] + RODS[(n//10)%10] + RODS[n//100]
      def rc4(n): return RODS[n%10] + RODS[(n//10)%10] + RODS[(n//100)%10] + RODS[n//1000]
      
      # 3-digit perfect squares (10²..31²)
      squares = {}
      for r in range(10, 32):
          n = r * r
          squares.setdefault(rc3(n), []).append(n)
      
      # 3-digit palindromic primes (form aba)
      palprimes = {}
      for a in range(1, 10):
          for b in range(0, 10):
              n = 100*a + 10*b + a
              if is_prime[n]:
                  palprimes.setdefault(rc3(n), []).append(n)
      
      # 4-digit primes with strictly increasing digits (210 combos)
      inc4 = {}
      for digits in combinations(range(10), 4):
          if digits[0] == 0: continue
          n = digits[0]*1000 + digits[1]*100 + digits[2]*10 + digits[3]
          if is_prime[n]:
              inc4.setdefault(rc4(n), []).append(n)
      
      # Find solutions: same rod count, correct ordering
      for t in set(squares) & set(palprimes) & set(inc4):
          for sq in squares[t]:
              for pp in palprimes[t]:
                  for p4 in inc4[t]:
                      if sq < pp < p4:
                          print(f"Rods={t}: square={sq}, palindromic prime={pp}, 4-digit prime={p4}")
      
      print(f"Runtime: {(perf_counter() - t0)*1000:.3f} ms")
      
      

      Like

      • Frits's avatar

        Frits 10:19 am on 24 March 2026 Permalink | Reply

        Claude AI generated fast code but it doesn’t cover the full solution space unless you proof/explain that a 4-digit palindromic number never can be prime (as it is divisible by 11).

        Like

  • Unknown's avatar

    Jim Randell 7:40 am on 17 March 2026 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2451: [Just one out] 

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

    I have just typed a product, but my machine gives a display that is “just one out”:

    3 × 6 = 19

    Whenever I type a digit, it displays a digit one more or less than the one I intended. So you should realise that I was trying to type:

    4 × 7 = 28

    I have tried again with another product, and again the display shows one digit times another higher digit equalling a two-figure prime answer that appears to be just one out.

    This time if I showed you the display it would be impossible for you to work out the product I was trying to type.

    What is the display this time?

    This puzzle was originally published with no title.

    [teaser2451]

     
    • Jim Randell's avatar

      Jim Randell 7:41 am on 17 March 2026 Permalink | Reply

      I have assumed that digits do not “wrap around”, and so if “0” is typed the machine always replaces it with “1”. And if “9” is typed the machine always replaces it with “8”.

      This Python program runs in 75ms. (Internal runtime is 744µs).

      from enigma import (defaultdict, irange, subsets, cproduct, is_prime, sprintf as f, join, printf)
      
      # possible incorrect digits
      ds = {
        0: [1], 1: [0, 2], 2: [1, 3], 3: [2, 4], 4: [3, 5],
        5: [4, 6], 6: [5, 7], 7: [6, 8], 8: [7, 9], 9: [8],
      }
      
      # collect candidate inputs by possible output
      rs = defaultdict(list)
      
      # choose an input sum: a * b = xy
      for (a, b) in subsets(irange(1, 9), size=2):
        xy = a * b
        (x, y) = divmod(xy, 10)
        if x == 0: continue
      
        # can we adjust all the digits by 1 to give a result that is off by one?
        for (a_, b_, x_, y_) in cproduct(ds[k] for k in (a, b, x, y)):
          if x_ == 0: continue
          xy_ = 10*x_ + y_
          if abs(a_ * b_ - xy_) == 1 and is_prime(xy_):
            (expr, expr_) = (f("{a} * {b} = {xy}"), f("{a_} * {b_} = {xy_}"))
            rs[expr_].append(expr)
            printf("[{expr} -> {expr_}]")
      
      # look for ambiguous outputs
      for (k, vs) in rs.items():
        if len(vs) > 1:
          printf("{k} <- {vs}", vs=join(vs, sep="; "))
      

      Solution: The display shows: “5 × 6 = 31”.

      And there are two inputs that may lead to this display:

      “4 × 5 = 20” → “5 × 6 = 31” (++++)
      “6 × 7 = 42” → “5 × 6 = 31” (−−−−)

      Note that in both these cases the digits are all changed in the same direction.


      However, if we allow the digits to “wrap around” so that “0” could be replaced by “1” or “9”, and “9” could be replaced by “0” or “8”, then there are further possible displays that have ambiguous origins:

      “3 × 6 = 18” → “4 × 7 = 29” (++++)
      “5 × 6 = 30” → “4 × 7 = 29” (−+−−)

      “4 × 5 = 20” → “3 × 6 = 19” (−+−−)
      “4 × 7 = 28” → “3 × 6 = 19” (−−−+)

      And these require that some digits in the expression are moved up and some moved down.

      Like

  • Unknown's avatar

    Jim Randell 9:43 am on 10 February 2026 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2333: [Continuous ledger] 

    From The Sunday Times, 10th June 2007 [link]

    The firm I work for has kept a ledger since its foundation. One fresh page is used each day, and these pages have been numbered consecutively since the firm was founded. During a weekend last year I noticed that the page number was a perfect square. Then, during a weekend earlier this year, I noticed that the page number was a perfect cube. On another day earlier that month, I had noticed that the page number was a perfect fourth power.

    When will the number next be palindromic?

    This puzzle was originally published with no title.

    There are now 1300 Teaser puzzles available on the site — which is 25 years worth of weekly puzzles. (And between S2T2 and Enigmatic Code there are 3866 puzzles available in total).

    [teaser2333]

     
    • Jim Randell's avatar

      Jim Randell 9:44 am on 10 February 2026 Permalink | Reply

      You need to take into account the date the puzzle was originally published when solving this puzzle.

      This Python program runs in 72ms. (Internal runtime is 1.12ms).

      from datetime import (date, timedelta)
      from enigma import (powers, inf, first, le, repeat, inc, peek, irange, is_npalindrome, printf)
      
      # date of puzzle (all dates must be earlier than this)
      pub = date(2007, 6, 10)
      
      # consider numbers up to 200_000
      (sqs, cbs, p4s) = (first(powers(1, inf, k), count=le(200000)) for k in [2, 3, 4])
      
      # is a date a weekend (= Saturday (6) or Sunday (7))
      is_weekend = lambda d: d.isoweekday() in {6, 7}
      
      # consider powers of 4
      for p4 in p4s:
      
        # look for a higher cube, potentially within the same month
        for cb in reversed(cbs):
          if not (cb > p4): break
          if cb - p4 > 30: continue
      
          # consider possible dates for the cube in the current year (before publication)
          for dcb in repeat(inc(timedelta(days=-1)), pub):
            if dcb.year < pub.year: break
            # must be on a weekend
            if not is_weekend(dcb): continue
            # calculate date for the power of 4
            dp4 = dcb - timedelta(days=cb - p4)
            # must be in the same month
            if not (dp4.month == dcb.month): continue
      
            # look for earlier perfect squares
            for sq in sqs:
              if not (sq < p4): break
              # calculate date for the square
              dsq = dp4 - timedelta(days=p4 - sq)
              y = dp4.year - dsq.year
              if not (0 < y < 2): continue
              # must be on a weekend
              if not is_weekend(dsq): continue
      
              # when is the next palindromic date (after cb)
              pl = peek(n for n in irange(cb + 1, inf) if is_npalindrome(n))
              dpl = dp4 + timedelta(days=pl - p4)
      
              # calculate founding date
              df = dp4 - timedelta(days=p4 - 1)
      
              # output solution
              printf("p4 = D{p4} = {dp4}; cb = D{cb} = {dcb}; sq = D{sq} = {dsq}; pl = D{pl} = {dpl}; D1 = {df}")
      

      Solution: The next palindromic number will occur on 20th June 2007.

      The square number is day 50176 (= 224^2) on 2006-01-07 (Saturday).

      The perfect cube is day 50653 (= 37^3) on 2007-04-29 (Sunday).

      The power of 4 is day 50625 (= 15^4) on 2007-04-01 (Sunday).

      And the next palindromic number after 50653 is 50705 on 2007-06-20 (Wednesday), i.e. 10 days after the puzzle was published.

      If the ledger has been in continual operation from the day of founding, then day 1 was 1868-08-23 (Sunday).

      Like

  • Unknown's avatar

    Jim Randell 6:53 am on 1 February 2026 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 3306: Pin-up 

    From The Sunday Times, 1st February 2026 [link] [link]

    Over the years I have used seven different PINs. The first consisted of a four-digit number (the first digit not being zero), and thereafter each new PIN was obtained by swapping over two of the digits of the previous PIN; each time this increased the PIN.

    The first PIN (not surprisingly!) was divisible by 1, the second was divisible by 2, the third by 3, and so on, with the seventh divisible by 7 (in fact the seventh PIN was the only one divisible by 7).

    What was the third PIN?

    [teaser3306]

     
    • Jim Randell's avatar

      Jim Randell 7:06 am on 1 February 2026 Permalink | Reply

      It is quicker (and neater) to build the list in reverse order. We can start with a final PIN that is a multiple of 7 (and 3), and then work backwards, undoing the swaps, to get back to the initial PIN.

      This Python program runs in 76ms. (Internal runtime is 6.8ms).

      from enigma import (irange, subsets, nsplit, nconcat, swap, printf)
      
      # generate possible swaps by index (on a 4-digit sequence)
      swap_inds = list(subsets(irange(4), size=2))
      
      # solve the puzzle for a sequence of (<number>, <digit>) pairs
      # at each stage finding the _previous_ PIN
      def _solve(ss):
        k = len(ss)
        # are we done?
        if k == 7:
          yield ss
        else:
          (n, ds) = ss[0]
          # swap 2 digits of the following PIN
          for (i, j) in swap_inds:
            ds1 = swap(ds, i, j)
            # no leading zeros
            if ds1[0] == 0: continue
            n1 = nconcat(ds1)
            # previous PINs must be: smaller; not be divisible by 7; divisible by their position (= 7 - k)
            if n1 < n and n1 % 7 > 0 and n1 % (7 - k) == 0:
              yield from _solve([(n1, ds1)] + ss)
      
      # solve the puzzle for final PIN <n>
      def solve(n):
        # final PIN is divisible by 7
        if n % 7 > 0: return
        # find previous PINs
        for ss in _solve([(n, nsplit(n))]):
          # return the list of numbers
          yield tuple(n for (n, ds) in ss)
      
      # consider the final PIN (divisible by 7 (and 3))
      for n in irange.round(1000, 9999, step=21, rnd='I'):
        for ns in solve(n):
          printf("{ns}")
      

      Solution: The third PIN is 5286.

      There are 2 possible sequences of PINs that differ at the second PIN:

      2568 → {2586, 5268} → 5286 → 8256 → 8265 → 8562 → 8652

      Like

      • Ruud's avatar

        Ruud 11:19 am on 1 February 2026 Permalink | Reply

        How do you know that the final PIN does not start with 0 ?

        Like

        • Jim Randell's avatar

          Jim Randell 11:47 am on 1 February 2026 Permalink | Reply

          @Ruud: The first PIN has no leading zero, so is greater than 999. And each subsequent PIN is larger than the one preceding it, so all the PINs are greater than 999, hence none of them have a leading zero.

          Like

      • ruudvanderham's avatar

        ruudvanderham 11:27 am on 1 February 2026 Permalink | Reply

        Ah, I see: because it has to be greater than the first PIN, which is >=1000 .
        But, how do you know that the final PIN is divisible by 3?

        Like

        • Jim Randell's avatar

          Jim Randell 11:48 am on 1 February 2026 Permalink | Reply

          @Ruud: If a number is divisible by 3 than any number formed from a rearrangement of the digits is also divisible by 3. Hence all the PINs must be divisible by 3.

          So if the final PIN is divisible by both 7 and 3, then it must be divisible by 21 (= LCM(7, 3)).

          Like

          • Jim Randell's avatar

            Jim Randell 12:25 pm on 2 February 2026 Permalink | Reply

            @Ruud: Did you try this? It produces no output for me.

            If you want to go in steps of 21 you need to start with the final PIN and work backwards, because the final PIN is the only one that is divisible by 7.

            Like

            • Ruud's avatar

              Ruud 1:10 pm on 2 February 2026 Permalink | Reply

              I thought I did, but apparently I did not. Can you remove this?

              Like

    • Ruud's avatar

      Ruud 9:34 am on 1 February 2026 Permalink | Reply

      import istr
      
      
      def solve(pins):
          for i0, i1 in istr.combinations(range(4), r=2):
              n = len(pins) + 1
              pin = istr.join(pins[-1][int(i0)] if i == i1 else pins[-1][int(i1)] if i == i0 else pins[-1][i] for i in range(4))
              if pin > pins[-1] and pin.is_divisible_by(n):
                  if n == 7:
                      yield pins + [pin]
                  elif not pin.is_divisible_by(7):
                      yield from solve(pins + [pin])
      
      
      for pin in istr(range(1000,10000)):
          for pins in solve([pin]):
              print(pins)

      Like

    • Frits's avatar

      Frits 1:32 pm on 1 February 2026 Permalink | Reply

      from itertools import combinations
      
      # generate and check the k-th PIN (decreasing k)
      def check(s, k):
        if not k:
          yield int(''.join(s[4]))      # third PIN
        else:
          # swap 2 digits
          for i, j in combinations(range(4), 2):
            # swap should result in a lower number > 999
            if s[-1][i] <= s[-1][j] or (i == 0 and s[-1][j] == "0"): continue
            new = [s[-1][j] if y == i else s[-1][i] if y == j else s[-1][y]
                 for y in range(4)]  
            # the seventh PIN was the only one divisible by 7
            if (n := int(''.join(new))) % k == 0 and n % 7: 
              yield from check(s + [new], k - 1)       
      
      sols = set()
      # first possible seventh PIN divisible by 21 (div 7, div 3)
      f = next(i for i in range(2001, 2024, 3) if i % 7 == 0)
      
      for p7 in range(f, 10000, 21):
        # div 5 case
        if "5" in (s := str(p7)):
          # we need an even digit for the div 4 case
          if set("02468").isdisjoint(s): continue
        elif "0" in s:
          # we need at least 2 even digits for the div 4 case 
          if sum(int(x) % 2 for x in s) > 2: continue
        else: 
          continue  
          
        # we need at least 7 different permutations  
        if len(set(s)) < 3: continue
        
        # generate all PINs and store the third PIN
        for p3 in check([s], 6):
          sols.add(p3)
          
      print(f"answer: {' or '.join(str(s) for s in sols)}")    
      

      Like

    • Ruud's avatar

      Ruud 5:43 am on 2 February 2026 Permalink | Reply

      ChatGPT solved it in 47 seconds:

      from itertools import permutations, combinations
      
      def swaps(a, b):
          """Return True if b can be obtained from a by swapping exactly two digits."""
          diffs = [i for i in range(4) if a[i] != b[i]]
          return len(diffs) == 2 and a[diffs[0]] == b[diffs[1]] and a[diffs[1]] == b[diffs[0]]
      
      def valid_chain(chain):
          """Check divisibility, increasing order, and swap condition."""
          for i in range(7):
              if chain[i] % (i+1) != 0:
                  return False
              if i > 0:
                  if chain[i] <= chain[i-1]:
                      return False
                  if not swaps(str(chain[i-1]), str(chain[i])):
                      return False
          # only the 7th is divisible by 7
          return all(chain[i] % 7 != 0 for i in range(6))
      
      solutions = []
      
      # choose 4 distinct digits, first not zero
      digits = [str(d) for d in range(10)]
      
      for digs in combinations(digits, 4):
          for perm in permutations(digs):
              if perm[0] == "0":
                  continue
              nums = sorted(
                  int("".join(p)) for p in permutations(perm) if p[0] != "0"
              )
              for chain in permutations(nums, 7):
                  if valid_chain(chain):
                      solutions.append(chain)
      
      # print solutions and the third PIN
      for sol in solutions:
          print(sol, "→ third PIN:", sol[2])
      

      Scary …

      Like

      • Ruud's avatar

        Ruud 6:10 am on 2 February 2026 Permalink | Reply

        Maybe less scary than I thought.
        Although, ChatGPT gave the proper answer, it can’t have been done with this pure brute force program. It takes for ever, even for one PIN.

        Like

      • Jim Randell's avatar

        Jim Randell 3:48 pm on 3 February 2026 Permalink | Reply

        This code looks like it might solve the puzzle, although it has decided to generate all possible sequences of 7 numbers and the check to see if it can find any that work (which itself quite a slow approach). But the code it has written for doing this is terrible.

        First it selects 4 digits from 10 (= C(10, 4) = 210 arrangements, and also means it is assuming that all the digits are different), then for each of these it considers all possible permutations of the digits (= P(4, 4) = 24 arrangements), discarding those that begin with a zero. It then makes a sorted list of all arrangements of these digits that do not have a leading zero (up to P(4, 4) = 24 arrangements), and then considers all possible 7 digit permutations of this list (not combinations so they are already correctly sorted), which accounts for up to 1,744,364,160 checks for each of the possible 210 × 24 = 5,040 sequences of digits. So in total it is trying about 8.7 × 10^12 possible sequences, including many repeated checks.

        It is no wonder it does not run in a reasonable time. For quite a straightforward problem ChatGPT has made a real mess of it.

        As Samuel Johnson might say, “[it] is like a dog walking on his hind legs. It is not done well; but you are surprised to find it done at all”.

        In my experience ChatGPT is good at producing something that looks plausible. So you need to know enough about what you are asking it, so that you can determine if the answer is actually reasonable. And this case, it is a massive failure.

        The really scary thing is that some people treat its responses as though it knows what it is talking about.

        Liked by 1 person

    • Ruud's avatar

      Ruud 7:06 am on 2 February 2026 Permalink | Reply

      I asked ChatGPT again (mentioning that it didn’t finish in reosonable time). And now it comes with a solution. Amazing …

      from collections import defaultdict
      
      SWAP_PAIRS = [(0,1), (0,2), (0,3), (1,2), (1,3), (2,3)]
      
      def swap_digits(s, i, j):
          if i == j:
              return s
          lst = list(s)
          lst[i], lst[j] = lst[j], lst[i]
          return "".join(lst)
      
      def build_neighbors():
          """
          neighbors[n] = sorted list of all valid 4-digit numbers obtainable by swapping two digits of n
                         (leading digit cannot become 0).
          """
          neighbors = [[] for _ in range(10000)]
          for n in range(1000, 10000):
              s = f"{n:04d}"
              seen = set()
              for i, j in SWAP_PAIRS:
                  t = swap_digits(s, i, j)
                  if t[0] == "0":
                      continue
                  m = int(t)
                  if m != n and m not in seen:
                      seen.add(m)
              neighbors[n] = sorted(seen)
          return neighbors
      
      def find_chains():
          neighbors = build_neighbors()
      
          # Precompute which numbers satisfy divisibility by k (k=1..7)
          div_ok = {k: [False]*10000 for k in range(1, 8)}
          for n in range(1000, 10000):
              for k in range(1, 8):
                  div_ok[k][n] = (n % k == 0)
      
          solutions = []
      
          def dfs(chain):
              step = len(chain) + 1  # next step index (1..7)
              if step == 8:
                  solutions.append(chain[:])
                  return
      
              prev = chain[-1]
      
              for nxt in neighbors[prev]:
                  if nxt <= prev:
                      continue
                  if not div_ok[step][nxt]:
                      continue
                  # only the 7th is divisible by 7
                  if step < 7 and nxt % 7 == 0:
                      continue
                  dfs(chain + [nxt])
      
          # Step 1: any 4-digit number works for divisibility by 1,
          # but it must allow an increasing chain of swaps.
          # Still, searching all 9000 starts is fine with pruning.
          for start in range(1000, 10000):
              # (Optional) tiny prune: if start has no larger neighbors, skip
              if not neighbors[start] or neighbors[start][-1] <= start:
                  continue
              dfs([start])
      
          return solutions
      
      if __name__ == "__main__":
          sols = find_chains()
          print(f"Found {len(sols)} solution chain(s).")
          for ch in sols:
              print(" -> ".join(map(str, ch)), " | third PIN:", ch[2])
      

      Like

      • Ruud's avatar

        Ruud 4:26 pm on 3 February 2026 Permalink | Reply

        I fully agree with you. But when told ChatGPT that this code does not run in reasonable time, it immediately came up with a way better solution (see my reply). And I find that amazing.

        Like

    • GeoffR's avatar

      GeoffR 10:09 am on 4 February 2026 Permalink | Reply

      Interesting reply from ChatGPT, after I put to it that it was not very efficient at coding teasers.

      It looks as though the problem is recognised and things may improve in the future. Quite a lot of detailed comments – things can only get better!

      https://chatgpt.com/share/698313ad-02fc-8000-ab4c-2a3873afb266

      Like

  • Unknown's avatar

    Jim Randell 9:48 am on 23 January 2026 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2462: [What NOW?] 

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

    Puzzlers are often confused about whether the number 1 is prime or square, so perhaps this Teaser will clarify the issue!

    • ONE is not prime, but it is an odd perfect square;
    TWO is divisible by 2 but not 4;
    • The number of odd primes less than SIX is TWO.

    In those statements digits have consistently been replaced by capital letters, with different letters being used for different digits.

    What is the number NOW?

    This puzzle was originally published with no title.

    [teaser2462]

     
    • Jim Randell's avatar

      Jim Randell 9:49 am on 23 January 2026 Permalink | Reply

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      The following run-file executes in 98ms. (Internal runtime of the generated code is 14.4ms).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # no leading zeros (ONE, TWO, SIX, NOW)
      --invalid="0,OTSN"
      
      # collect primes < 1000
      --code="primes.expand(999)"
      
      # ONE is not prime, but it is an odd perfect square
      "ONE not in primes"
      "E % 2 = 1"
      "is_square(ONE)"
      
      # TWO is divisible by 2, but not 4
      "O % 2 = 0"
      "WO % 4 != 0"
      
      # the number of odd primes less than SIX is TWO
      "icount(primes.between(3, SIX - 1)) = TWO"
      
      --template="(ONE) (TWO) (SIX)"
      --answer="NOW"
      

      Solution: NOW = 820.

      There are two possible assignments of digits to letters:

      ONE = 289, TWO = 102, SIX = 564
      ONE = 289, TWO = 102, SIX = 567

      The next prime after 563 is 569, so we count 102 odd primes if X is 4 or 7.

      Like

      • Frits's avatar

        Frits 9:09 am on 24 January 2026 Permalink | Reply

        @Jim, your code allows N to be zero. Is this what you wanted?

        Like

    • ruudvanderham's avatar

      ruudvanderham 11:24 am on 23 January 2026 Permalink | Reply

      import peek
      import istr
      
      for o, n, e, t, w, s, i, x in istr.permutations(range(10), 8):
          if o * t * s != 0:
              if not (one := istr("=one")).is_prime() and one.is_odd() and one.is_square():
                  if (two := istr("=two")).is_divisible_by(2) and not two.is_divisible_by(4):
                      if len(istr.primes(3, (six := istr("=six")))) == two:
                          peek(one, two, six)
      

      Like

  • Unknown's avatar

    Jim Randell 7:54 am on 14 December 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 3299: Who wins mostly? 

    From The Sunday Times, 14th December 2025 [link] [link]

    The teams in our local football league play each other once during the season, getting three points for a win and one for a draw. On the last day of the season each team played a game and then I worked out the league table with the teams in alphabetical order. Here is part of three rows of that league table in which digits have consistently been replaced by letters, with different letters used for different digits.

    How many teams are there in the league and what number is LOST?

    [teaser3299]

     
    • Jim Randell's avatar

      Jim Randell 7:56 am on 14 December 2025 Permalink | Reply

      It is the end of the season, and so each team has played each of the other teams once. So, for each team, the sum of the “won”, “drawn” and “lost” values must be one less than the total number of teams in the league (and if each team plays a match on the last day, then the number of teams must be even).

      Also in order to win a game, the winning team must score at least one goal. So the value in “goals for” must be greater than or equal to “won”. And the value in “goals against” must be greater than or equal to “lost”.

      This is sufficient analysis to provide a single candidate solution to the puzzle.

      Here is a programmed solution using the [[ SubstitutedExpression ]] solver from the enigma.py library. (Which took me less than 2 minutes to write).

      It executes in 81ms, and the internal runtime of the generated code is 439µs.

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # end of the season
      "W + H + O == W + I + N == M + O + S"
      
      # 3 points for win, 1 point for a draw
      "3 * W + I = S"
      "3 * M + O = T"
      
      # "goals for" >= "won"; "goals against" >= "lost"
      "L >= M"
      "Y >= S"
      
      --answer="(W + H + O + 1, LOST)"
      --template=""
      

      Solution: There are 12 teams in the league. LOST = 3467.

      Like

    • Frits's avatar

      Frits 5:51 pm on 14 December 2025 Permalink | Reply

      # n = number of teams - 1 = number of games per team
      # 8 <= n = 3W + M + I + O   with 0 < W < 3 and I <= 5 (3W + I < Y)
      # n != 17 otherwise 
      #   if W = 1 no correct values exist for  {H, O, I, N} 
      #   if W = 2 then {H, O, I, N} = {6, 7, 8, 9} but 3W + I < 10 --> invalid
      
      dgts = set(range(10))
      # consider odd number of games per team
      for n in range(9, 17, 2):
        for W in range(1, 3):       # W != 3 as Y > (S = 3.W + I)
          for I in range(6):        # I != 6 as S < 9
            if len(n6 := dgts - {W, I, (N := n - W - I), (S := 3 * W + I)}) != 6:
              continue
            for M in range(1, 4):
              H, O, T = M + 2 * W + I, n - M - S, n + 2 * M - S
              if len(LY := list(n6 - {M, H, O, T})) != 2: continue
              # assert L > M, Y > S
              for L, Y in [LY, LY[::-1]]:
                if L <= M or Y <= S: continue
                LOST = 1000 * L + 100 * O + 10 * S + T  
                print(f"{n + 1} teams, {LOST = }")
      

      Like

  • Unknown's avatar

    Jim Randell 8:14 am on 21 November 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2498: [Party trick] 

    From The Sunday Times, 8th August 2010 [link]

    Keith and I used to do a party trick. We had a set of cards labelled A, B, C, D, E, F, G and H. We would ask someone to choose three cards without Keith seeing. I would pass Keith one of the chosen cards, followed by a second. Amazingly, he would announce what the third chosen card was. Keith and I have phenomenal memories, so I then thought of a way to improve the trick. On each of the two occasions, I pass a card with either my left or right hand. Using this extra piece of trickery, we increased the number of cards in the set, ending with a letter way beyond H.

    What is the letter on the last card?

    This puzzle was originally published with no title.

    [teaser2498]

     
    • Jim Randell's avatar

      Jim Randell 8:15 am on 21 November 2025 Permalink | Reply

      See also: Enigma 1214 (which was set by Keith Austin).

      There are 3 cards chosen, so the setter has P(3, 2) = 6 ways they can choose to present two of the cards to Keith. If each of these ways is used to identify the remaining card there can be up to 6 + 2 = 8 cards in the pack. And this is the initial scenario.

      In the second part of the puzzle each of the two passes can be made with the left (L) or right (R) hand, so each of the 6 ways of presenting the cards can have 4 variations (LL, LR, RL, RR), so there are now a total of 6 × 4 = 24 different ways of identifying the missing card. So there can be up to 26 cards in the pack. Hence:

      Solution: The last card has the letter Z on it.

      Like

  • Unknown's avatar

    Jim Randell 10:02 am on 11 November 2025 Permalink | Reply
    Tags: by: Victor Bryant,   

    Teaser 2481: [Coach trip] 

    From The Sunday Times, 11th April 2010 [link]

    My wife and I took a coast-to-coast coach tour of the USA, starting at New York and travelling steadily westwards to San Francisco. The coach had rows of seats, each row consisting of a double seat on the left and a double seat on the right. In New Jersey we were in the left-hand seat of the middle row. For each new state we moved two seats clockwise. Just as we were about to move to the best seat (front right) Clive – the courier – told us thereafter to move three seats clockwise for each state. But we did get the best seat later in the holiday.

    In which state were we sitting in the best seat?

    This puzzle was originally published with no title.

    [teaser2481]

     
    • Jim Randell's avatar

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

      I’m not sure how this puzzle is meant to work, as it surely depends on the route taken.

      I tried using Google and Apple Maps to give routes from New York to San Francisco, and the 4 suggested routes visited the following states:

      NY → NJ → PA → OH → IN → IL → IA → NE → WY → UT → NV → CA (I80)
      NY → NJ → PA → WV → OH → IN → IL → MO → NE → WY → UT → NV → CA (I70/I80)
      NY → NJ → PA → WV → OH → IN → IL → MO → OK → TX → NM → AZ → CA (I40/I5)
      NY → NJ → PA → MD → WV → VA → TN → AR → OK → TX → NM → AZ → CA (I40/I5)

      And these are just the “direct” routes. On a coach tour it is quite possible that it takes a less direct route, and visits more states.

      This Python program considers possible numbers of seats on the coach and looks for configurations where the front right seat is missed the first time (when the increment changes from +2 to +3) but hit subsequently. It then checks what that means for the four routes given above.

      from enigma import (irange, seq_get, seq2str, printf)
      
      # possible routes from New York to San Francisco
      routes = list(x.split() for x in [
        "NY NJ PA OH IN IL IA NE WY UT NV CA", # I80
        "NY NJ PA WV OH IN IL MO NE WY UT NV CA", # I70/I80
        "NY NJ PA WV OH IN IL MO OK TX NM AZ CA", # I40/I5
        "NY NJ PA MD WV VA TN AR OK TX NM AZ CA", # I40/I5
      ])
      
      # suppose the coach has n rows in front of the middle row and n rows
      # behind it, so (2n + 1) rows in total, and (4n + 2) seats, which we
      # will number 0 .. 4n + 1, going clockwise starting from the left hand
      # seat, middle row
      
      def solve(n):
        # layout of the coach
        rows = 2 * n + 1
        seats = 2 * rows
        best = n + 1
        # we start in seat 0, and move 2 each state
        k = 0
        i = 2
        # consider visiting state j (starting at 1 = NJ)
        for j in irange(2, 50):
          # move to the next seat
          k = (k + i) % seats
          # are we going to get the best seat?
          if k == best:
            if i == 2:
              # we start to move 3 seats instead
              i += 1
              k += 1
            else:
              # we got the best seats in state j
              printf("{rows} rows; {seats} seats; best @ {j}")
              # check against the routes
              for route in routes:
                r = seq_get(route, j)
                if r is not None:
                  printf("-> {route} @ {r}", route=seq2str(route))
      
      # consider possible coach configurations
      for n in irange(1, 20):
        solve(n)
      

      There appear to be two possible scenarios:

      (1) In a coach with 7 rows of seats (= 28 individual seats).

      The seats are laid out as follows (viewed from above):

      [01] - [02] = front
      [03] - [04]
      [05] - [06]
      [07] - [08]
      [09] - [10]
      [11] - [12]
      [13] - [14] = back

      In NJ (state 1) the setter is in seat 07 and proceeds as follows:

      07 (+2) 03 (+3) 04 (+3) 10 (+3) 13 (+3) 07 (+3) 01 (+3) 06 (+3) 12 (+3) 11 (+3) 05 (+3) 02 [best]

      Which means being in the best seat in state 12.

      The shortest route given above only has 10 states after NJ, so is not possible, and for the remaining three routes this is the final state, California.

      (2) In a coach with 11 rows of seats (= 44 individual seats).

      The seats are laid out as:

      [01] - [02] = front
      [03] - [04]
      [05] - [06]
      [07] - [08]
      [09] - [10]
      [11] - [12]
      [13] - [14]
      [15] - [16]
      [17] - [18]
      [19] - [20]
      [21] - [22] = back

      In NJ (state 1) the setter is in seat 11 and proceeds as follows:

      11 (+2) 07 (+2) 03 (+3) 04 (+3) 10 (+3) 16 (+3) 22 (+3) 17 (+3) 11 (+3) 05 (+3) 02 [best]

      Which means being in the best seat in state 11.

      For the routes given this could be California, Nevada, or Arizona.

      And if the route were to visit more states there are further solutions.

      The next is in a coach with 23 rows of seats (= 92 individual seats), and happens in state 22 (which would require a less direct route, possibly revisiting states).


      If the list of states visited had been given as:

      NY → NJ → PA → OH → IN → IL → IA → NE → WY → UT → NV → CA (I80)

      (i.e. the most direct of the suggested routes).

      Then the only possible solution is a coach with 11 rows of seats, and the setter gets the best seat in state 11 = California.

      And the published solution is “California”, so perhaps this is what the setter had in mind.

      Like

  • Unknown's avatar

    Jim Randell 7:02 am on 2 November 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 3293: Three sisters 

    From The Sunday Times, 2nd November 2025 [link] [link]

    I have three granddaughters Jay, Kay and Elle. I set Jay and Kay a test and asked each of them to produce a list of positive numbers that used just nine digits [in total] with no digit occurring more than once. I wanted the majority of the numbers in each list to be odd and the majority of the numbers in each list to be perfect squares.

    Jay’s list (which contained more numbers than Kay’s) added up to give the year of Elle’s birth, whereas Kay’s list added up to give the year in which Elle will be 25.

    In one of the lists the highest number was a perfect square.

    What (in increasing order) were the numbers in that list?

    [teaser3293]

     
    • Jim Randell's avatar

      Jim Randell 8:36 am on 2 November 2025 Permalink | Reply

      Assuming the puzzle is set at the current time (i.e. during 2025). Then the latest Elle can be born is 2025, and if she has not reached her 25th birthday yet, the earliest she can be born is 2000. So Jay’s numbers must sum to between 2000 and 2025. (And Kay’s numbers sum to between 2025 and 2050).

      I also assumed that Jay and Kay completed their test successfully, and the lists of numbers they produced satisfied the given requirements.

      My first program was a bit slow. This one is a bit more involved, but both find the same answer.

      The following Python program runs in 191ms. (Internal runtime is 121ms).

      from enigma import (
        defaultdict, irange, powers, nsplit, seq_is_distinct, nconcat,
        divc, subsets, disjoint_union, intersect, cproduct, printf
      )
      
      # map squares to digit contents
      sq = dict()
      for s in powers(1, 45, 2):
        ds = nsplit(s)
        if seq_is_distinct(ds):
          sq[s] = ds
      
      # available digits
      digits = set(irange(0, 9))
      
      # allocate remaining digits <rs> for units digits <us>
      def allocate(us, rs, ns=list()):
        k = len(rs)
        if k == 1:
          # there should be 1 digit remaining unused
          yield us + ns
        elif k > 1 and us:
          # choose some digits to add to the next unit digit
          u = us[0]
          for ds in subsets(rs, max_size=3, select='P', fn=list):
            if ds and ds[0] == 0: continue
            n = nconcat(ds + [u])
            if 0 < n <= 2050:
              yield from allocate(us[1:], rs.difference(ds), ns + [n])
      
      # record candidate sequences for J and K
      (Js, Ks) = (defaultdict(set), defaultdict(set))
      
      # consider the length of the list
      for k in irange(3, 6):
        # majority size
        m = divc(k + 1, 2)
      
        # choose some squares
        for sqs in subsets(sq.keys(), size=m, fn=list):
          # digits used
          ds = disjoint_union(sq[s] for s in sqs)
          if not ds: continue
      
          # count odd numbers so far, and remaining digits
          odd = sum(s % 2 for s in sqs)
          rs = digits.difference(ds)
      
          # choose units digits for the remaining numbers
          for us in subsets(rs, size=k - m, fn=list):
            if odd + sum(u % 2 for u in us) < m: continue
            # allocate the remaining digits
            for ns in allocate(us, rs.difference(us)):
              ss = tuple(sorted(sqs + ns))
              t = sum(ss)
              # record candidates by birth year
              if 2000 <= t <= 2025: Js[t].add(ss)
              if 2025 <= t <= 2050: Ks[t - 25].add(ss)
      
      # find solutions
      for j in intersect([Js.keys(), Ks.keys()]):
        for (J, K) in cproduct([Js[j], Ks[j]]):
          if len(J) > len(K) and (max(J) in sq or max(K) in sq):
            printf("J={J} [{j}]; K={K} [{k}]", k=j + 25)
      

      Solution: The numbers are: 305, 784, 961.

      Jay’s list is:

      [4, 9, 625, 1387]; sum = 2025
      (4 numbers; 3 squares; 3 odd)

      So Elle was born this year (2025), and will be 25 in the year 2050.

      Kay’s list is:

      [305, 784, 961]; sum = 2050
      (3 numbers; 2 squares; 2 odd)

      In Kay’s list the highest number (961) is a square (= sq(31)), and so this list is the required answer.

      Like

    • Jim Olson's avatar

      Jim Olson 4:11 pm on 10 November 2025 Permalink | Reply

      I am perhaps missing something but I can’t see how the solution meets the requirements of the puzzle. Each list is supposed to be a list of positive numbers. Zero is not a positive number.

      Like

      • Jim Randell's avatar

        Jim Randell 4:33 pm on 10 November 2025 Permalink | Reply

        @Jim: The numbers in the lists are all positive ((4, 9, 625, 1387) and (305, 784, 961)), but that doesn’t mean the individual digits have to be non-zero. Many positive numbers are written using a 0 digit.

        Like

  • Unknown's avatar

    Jim Randell 6:11 am on 24 August 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 3283: Die hard? 

    From The Sunday Times, 24th August 2025 [link] [link]

    I have three identical standard dice (1 to 6 spots on each face, with 1 opposite 6, 2 opposite 5 and 3 opposite 4). I have placed them together in a row on a table and looked at the row from various sides. Regarding each face of a die as a digit (so that, for example, five spots would be regarded as a 5), I can read three 3-digit primes; one along the front of the row, one (the largest of the three) along the top of the row, and one along the back viewed from behind. Furthermore, the total number of  spots on the 11 visible faces is also a prime.

    What, in increasing order, are the three 3-digit primes?

    [teaser3283]

     
    • Jim Randell's avatar

      Jim Randell 6:26 am on 24 August 2025 Permalink | Reply

      See also: Teaser 2598.

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      It assumes the dice are “right-handed”, but you get the same answer if you use “left-handed” dice.

      The following run-file executes in 93ms. (Internal runtime of the generated code is 498µs).

      Run: [ @codepad ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # layout (viewed from above):
      #
      #       I H G
      #     +-------+
      #   X | A B C | Y
      #     +-------+
      #       D E F
      
      --digits="1-6"
      --distinct="ADIX,BEH,CFGY"
      
      # check the top
      "is_prime(ABC)"
      "ABC > max(DEF, GHI)"
      
      # the front and back
      "D + I = 7" "E + H = 7" "F + G = 7"
      "is_prime(DEF)"
      "is_prime(GHI)"
      
      # total number of exposed spots is also prime
      "is_prime(sum([A, B, C, D, E, F, G, H, I, X, Y]))"
      
      # generate die corners (clockwise around a corner)
      --code="""
      def die_corners(x, y, z, k=7):
        for (y1, z1) in tuples([y, z, k - y, k - z, y], 2):
          for t in [(x, y1, z1), (k - x, k - z1, k - y1)]:
            yield from repeat(rotate, t, 2)
      """
      --code="corners = set(die_corners(3, 2, 1))"  # (3, 2, 1) = RH; (1, 2, 3) ]] = LH
      
      # check corner configurations
      "(A, D, X) in corners"
      "(A, X, I) in corners"
      "(C, Y, F) in corners"
      "(C, G, Y) in corners"
      
      --answer="ordered(ABC, DEF, GHI)"
      --template="(A B C) (D E F) (G H I) (X Y)"
      --solution=""
      

      Solution: The 3-digit primes are: 433, 443, 661.

      Here is a layout using right-handed dice:

      The sum of the spots on the exposed faces is 41.

      The die in the middle can be rotated through 180° to give another layout with 433 on the front and 443 on the back, but this doesn’t change the answer to the puzzle.

      With left-handed dice the layout is the same, but with the 5 on the left end and the 2 on the right end.

      Like

      • Jim Randell's avatar

        Jim Randell 9:25 am on 24 August 2025 Permalink | Reply

        Here is a Python program that finds the same layout(s).

        It has an internal runtime of 499µs.

        from enigma import (primes, nsplit, tuples, repeat, rotate, find, seq_items, chain, nconcat, printf)
        
        # generate die corners (clockwise around a corner) from example (x, y, z)
        def die_corners(x, y, z, k=7):
          for (y1, z1) in tuples([y, z, k - y, k - z, y], 2):
            for t in [(x, y1, z1), (k - x, k - z1, k - y1)]:
              yield from repeat(rotate, t, 2)
        
        # make map (x, y) -> z for (x, y, z) clockwise values round a corner
        # [use: (3, 2, 1) for RH dice; (1, 2, 3) for LH dice]
        corner = dict(((x, y), z) for (x, y, z) in die_corners(3, 2, 1))
        
        # look for 3-digit primes that can be formed using the digits 1-6
        digits = { 1, 2, 3, 4, 5, 6 }
        prs = list(ds for ds in map(nsplit, primes.between(111, 666)) if digits.issuperset(ds))
        
        # choose a prime for the front
        for (i, front) in enumerate(prs):
          # determine the number on the back
          back = tuple(7 - x for x in reversed(front))
          j = find(prs, back)
          if j == -1: continue
          # choose a larger number for the top
          for (_, top) in seq_items(prs, max(i, j) + 1):
        
            # check for valid dice
            xs = tuple(corner.get((t, f)) for (t, f) in zip(top, front))
            if None in xs: continue
        
            # left and right exposed faces
            (x, y) = (xs[0], corner.get((front[-1], top[-1])))
            # sum of exposed faces is a prime
            if not (sum(chain(top, front, back, (x, y))) in primes): continue
        
            # output solution
            ns = sorted(map(nconcat, (top, front, back)))
            printf("{ns} [top={top} front={front} back={back}; left={x} right={y}]")
        

        Like

    • Frits's avatar

      Frits 9:37 am on 24 August 2025 Permalink | Reply

      #       I H G
      #     +-------+
      #   X | A B C | Y
      #     +-------+
      #       D E F
       
      # given two dice faces anti-clockwise at a vertex, find the third
      # face at this vertex (using a western die if 'same' is True)
      def third_face(fi, se, same=True):
        f, s = int(fi), int(se)
        if s in {f, 7 - f}:
          raise ValueError
        oth = [i for i in range(1, 7) if i not in {f, s, 7 - f, 7 - s}]
        return oth[((f < s) == ((s - f) % 2)) == (same == (s + f > 7))]
       
      dgts = "123456"
      # opposite side dictionary
      opp = {str(i): str(7 - i) for i in range(1, 7)}  
          
      # determine valid 3-digit primes
      prms1 = {3, 5, 7}
      prms1 |= {x for x in range(11, 52, 2) if all(x % p for p in prms1)}
      prms = {str(i) for i in range(101, 667, 2) if all(i % p for p in prms1) and
              all(x in dgts for x in str(i))}
       
      # front and back: DEF and IHG
      fb = [(DEF, GHI[::-1]) for DEF in prms if DEF[0] not in "16" and
            (GHI := (''.join(opp[x] for x in DEF))[::-1]) in prms]
      
      # combine front DEF and back IHG with top ABC
      ftb = [(f, t, b) for t in prms for f, b in fb if t > f and t > b[::-1]
             and all(y not in {x, z} for x, y, z in zip(f, t, b))]       
      
      # determine side faces X and Y and check for prime total of 11 spots
      ftbxy = [(f, t, b, x, y) for (f, t, b) in ftb if 
               (x := third_face(f[0], t[0])) + (y := third_face(t[2], f[2])) +
               sum(sum(int(b) for b in a) for a in (f, b, t)) in prms1]
      
      sols = set(tuple(sorted([f, t, b[::-1]])) for f, t, b, _, _ in ftbxy)
      print("answer:", ' or '.join(str(s) for s in sols))
      

      Like

  • Unknown's avatar

    Jim Randell 10:46 am on 15 August 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2492: [Paper cut] 

    From The Sunday Times, 27th June 2010 [link] [link]

    I started with a rectangular piece of paper and made a [straight] cut across it, dividing it into a triangle and a pentagon. Then I discarded the triangle and measured the lengths of the sides of the pentagon, all of which were whole numbers of centimetres.

    I remember that the lengths of the shortest two sides [of the pentagon] were 17 cm and 19 cm, and that the length of the longest side was 33 cm.

    What were the lengths of the other two sides?

    This puzzle was originally published with no title.

    [teaser2492]

     
    • Jim Randell's avatar

      Jim Randell 10:46 am on 15 August 2025 Permalink | Reply

      To make a rectangle into a pentagon with a single cut, we cut one of the corners off, which means we discard a right-angled triangle. And all sides of the pentagon have integer lengths, which means so do the sides of the triangle, so its sides are a Pythagorean triple.

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

      from enigma import (irange, cproduct, pythagorean_triples, ordered, printf)
      
      # the hypotenuse of the removed triangle cannot be more than 33
      for (x, y, z) in pythagorean_triples(33):
        if z < 17: continue  # or less than 17
        # consider possible rectangles (a, b)
        for (a, b) in cproduct([irange(x + 17, 33), irange(y + 17, 33)]):
          # the sides of the pentagon
          P = ordered(a, b, a - x, b - y, z)
          if not (P[0] == 17 and P[1] == 19 and P[-1] == 33): continue
          # output solution
          printf("rectangle={R} triangle={T} -> pentagon={P}", R=(a, b), T=(x, y, z))
      

      Solution: The remaining sides of the pentagon have lengths of 20 cm and 31 cm.

      The scenario is this:

      Like

    • Frits's avatar

      Frits 2:54 pm on 15 August 2025 Permalink | Reply

      from enigma import pythagorean_triples
      
      m1, m2, M = 17, 19, 33
       
      # the hypotenuse of the removed triangle cannot be more than M
      for (x, y, z) in pythagorean_triples(33):
        if z < m1: continue
        # consider possible rectangles (w, h)
        for w in range(m1 + x, M + 1):
          lo, mi, hi = sorted([w, w - x, z])
          if mi < m2: continue
          if {m1, m2, M}.isdisjoint({lo, mi, hi}): continue 
          if hi != M: 
            h_rng = [M] 
          elif lo != m1: 
            h_rng = [m1 + y] if m1 + y <= M else []
          else:
            h_rng = range(m1 + y, M + 1)
          
          for h in h_rng:
            # five sides of the pentagon
            s1, s2, s3, s4, s5 = sorted([lo, mi, hi, h, h - y])
            if not (s1 == m1 and s2 == m2 and s5 == M): continue
            print("answer:", [s3, s4])
      

      Like

  • Unknown's avatar

    Jim Randell 9:06 am on 5 August 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Brainteaser 1153: Teasing square 

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

    The diagram shows part of a magic square (a 3×3 array of positive whole numbers each of whose rows, columns and long diagonals adds up to the same total). But in this case only five of the numbers are shown. And each digit has been replaced by a letter, different letters representing different digits.

    To add to the squareness of this magic I can tell you also that the sum of each row is a perfect square.

    What is the full (numerical) version of the magic square?

    [teaser1153]

     
    • Jim Randell's avatar

      Jim Randell 9:07 am on 5 August 2025 Permalink | Reply

      Here is a solution using the SubstitutedExpression solver from the enigma.py library.

      It runs in 77ms. (Internal runtime of the generated code is 382µs).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # [ @a | @b | @c ]
      # [ BR | AI |  N ]
      # [ TE |  A | @d ]
      
      # magic sum is a square number
      --macro="@sum = (3 * AI)"
      "is_square(@sum)"
      
      # missing values are positive integers
      --macro="@a = (@sum - BR - TE)"  # @a + BR + TE == @sum  [col 1]
      --macro="@b = (2 * AI - A)"      # @b + AI + A  == @sum  [col 2]
      --macro="@c = (2 * AI - TE)"     # @c + AI + TE == @sum  [diag 2]
      --macro="@d = (@sum - TE - A)"   # TE +  A + @d == @sum  [row 3]
      "@a > 0" "@b > 0" "@c > 0" "@d > 0"
      
      # remaining rows / cols / diags give the magic sum
      "@a + @b + @c == @sum"  # [row 1]
      "BR + AI + N == @sum"   # [row 2]
      "@c + N + @d == @sum"   # [col 3]
      "@a + AI + @d == @sum"  # [diag 1]
      
      --answer="((@a, @b, @c), (BR, AI, N), (TE, A, @d))"
      --output="lambda p, s, ans: print(ans)"
      

      Solution: The completed square is:

      And so the magic constant is 81.

      Which would correspond to: [X, XA, AB], [BR, AI, N], [TE, A, BY] for some unused symbols X and Y.

      Like

    • Frits's avatar

      Frits 2:07 pm on 5 August 2025 Permalink | Reply

      Minimising the number of iterations.

      # magic constant M = 3 * AI or AI = 3 * k^2
      for AI in [3 * k * k for k in range(2, 6)]:
        A, I = divmod(AI, 10)
        # A can never be equal to I as AI is not a multiple of 11
        M = 3 * AI
        sum2 = M - AI
        # BR + N = sum2 
        if sum2 > 105: continue
        # BR = A + 2.(c33 - AI) so R and A have the same parity
        # (sum2 - N) has same parity as A so N must have the same parity as A
        for N in set(range(A % 2, 10, 2)) - {A, I}: 
          B, R = divmod(sum2 - N, 10)
          if not (0 < B < 10) or not {A, I, N}.isdisjoint({B, R}): continue
          c33 = ((BR := 10 * B + R) - A + sum2) // 2
          T, E = divmod(M - c33 - A, 10)
          if not (0 < T < 10) or not {A, I, N, B, R}.isdisjoint({T, E}): continue
          c11 = sum2 - c33
          c12 = sum2 - A
          c13 = sum2 - (TE := 10 * T + E)
          
          mat = [(c11, c12, c13), (BR, AI, N), (TE, A, c33)]
          # double check matrix
          if not all(sum(r) == M for r in mat): continue
          if len({sum2, c11 + c33, TE + c13}) > 1: continue
          if not all(sum(r) == M for r in zip(*mat)): continue
          for c1, c2, c3 in mat:
            print(f"|{c1:>2} | {c2:>2} | {c3:>2} |")
      

      Like

    • GeoffR's avatar

      GeoffR 12:22 pm on 6 August 2025 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      %   w   x   y
      %   BR  AI  N
      %   TE  A   z
      
      predicate is_sq(var int: y) =
      exists(z in 1..ceil(sqrt(int2float(ub(y))))) (z*z = y );
      
      var 1..9:A; var 1..9:B; var 1..9:T; var 1..9:N; 
      var 0..9:R; var 0..9:I; var 0..9:E;
      var 10..99:BR; var 10..99:AI; var 10..99:TE;
      var 1..99:w; var 1..99:x; var 1..99:y; var 1..99:z;
      var 20..210:m_const;
      
      constraint all_different ([B, R, A, I, N, T, E]);
      constraint BR = 10*B + R /\ AI = 10*A + I /\ TE = 10*T + E;
      
      % All rows are squares
      constraint is_sq(BR + AI + N) /\ is_sq(TE + A + z) /\ is_sq(w + x + y);
      
      constraint m_const  == BR + AI + N;
      
      % Check conditions for a magic square
      constraint m_const == w + x + y /\ m_const == TE + A + z
      /\ m_const == w + BR + TE /\ m_const == x + AI + A /\ m_const == y + N + z
      /\ m_const == w + AI + z /\ m_const == y + AI + TE;
      
      solve satisfy;
      
      output[ show([w, x, y]) ++ "\n" ++ show([BR, AI, N]) ++ "\n" ++ show([TE, A, z]) ];
      
      % [5, 52, 24]
      % [46, 27, 8]
      % [30, 2, 49]
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:19 am on 18 July 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2518: [Empty boxes] 

    From The Sunday Times, 26th December 2010 [link] [link]

    For an appropriate Boxing Day exercise, write a digit in each of the empty boxes so that (counting all the digits from here to the final request) the following statements are true:

    Number of occurrences of 0 = [ _ ]
    Number of occurrences of 1 = [ _ ]
    Total occurrences of 2s and 3s = [ _ ]
    Total occurrences of 4s and 5s = [ _ ]
    Total occurrences of 6s and 7s = [ _ ]
    Total occurrences of 8s and 9s = [ _ ]
    A digit you have written in another box = [ _ ]
    The average of all your written digits = [ _ ]

    What, in order, are the digits in your boxes?

    This puzzle was originally published with no title.

    [teaser2518]

     
    • Jim Randell's avatar

      Jim Randell 8:20 am on 18 July 2025 Permalink | Reply

      There are 8 boxes to be filled out with a digit (from 0 to 9), and each of the digits 0 to 9 already occurs once before the boxes are filled out.

      The first six boxes count the 10 given digits, plus those in the 8 boxes, so must sum to 18.

      The first 2 boxes must contain 1 or more, and the next 4 boxes must contain 2 or more.

      And the box with the mean must also be 2 or more.

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

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

      Run: [ @codepad ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # let the 8 empty boxes be: A B C D E F G H
      --distinct=""
      --digits="1-9"
      --invalid="1,CDEFH"
      --macro="@boxes = [A, B, C, D, E, F, G, H]"
      
      # first 2 boxes
      "@boxes.count(0) + 1 = A"
      "@boxes.count(1) + 1 = B"
      
      # next 4 boxes
      "@boxes.count(2) + @boxes.count(3) + 2 = C"
      "@boxes.count(4) + @boxes.count(5) + 2 = D"
      "@boxes.count(6) + @boxes.count(7) + 2 = E"
      "@boxes.count(8) + @boxes.count(9) + 2 = F"
      
      # final 2 boxes
      "G in [A, B, C, D, E, F, H]"
      "iavg(@boxes) = H"
      
      --template="@boxes"
      --solution=""
      
      # [optional] additional constraints to improve run time
      # the sum of the first 6 boxes must be 18
      "A + B + C + D + E + F = 18"
      # we can't write 0 in any of the boxes, so there is only 1 zero
      --assign="A,1"
      

      Solution: The digits are: 1, 2, 8, 2, 2, 3, 3, 3.

      The statements then become:

      Number of occurrences of 0 = [ 1 ]
      Number of occurrences of 1 = [ 2 ]
      Total occurrences of 2s and 3s = [ 8 ]
      Total occurrences of 4s and 5s = [ 2 ]
      Total occurrences of 6s and 7s = [ 2 ]
      Total occurrences of 8s and 9s = [ 3 ]
      A digit you have written in another box = [ 3 ]
      The average of all your written digits = [ 3 ]

      Like

      • Frits's avatar

        Frits 2:04 pm on 18 July 2025 Permalink | Reply

        @Jim, you can also add B in line 8 (ao because of line 32).

        Like

  • Unknown's avatar

    Jim Randell 8:21 am on 15 July 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2489: [Digit circle] 

    From The Sunday Times, 6th June 2010 [link] [link]

    Your task today is to put more than two digits evenly spaced around a circle so that:

    (a) Any three adjacent digits clockwise form a prime number.

    (b) Any three adjacent digits anti-clockwise form a prime number.

    (c) If a digit occurs in the circle, then it occurs a prime number of times.

    (d) The total of all the digits around the circle is a prime.

    What is the total of all the digits?

    This puzzle was originally published with no title.

    [teaser2489]

     
    • Jim Randell's avatar

      Jim Randell 8:21 am on 15 July 2025 Permalink | Reply

      Each digit is the last digit of a 3-digit prime, so the only possible digits are 1, 3, 9, 7.

      This Python program looks for solutions using increasing numbers of digits, and stops after the smallest possible sequences are found.

      It runs in 60ms. (Internal runtime is 841µs).

      Run: [ @codepad ]

      from enigma import (irange, inf, primes, multiset, subsets, tuples, rev, nconcat, wrap, uniq, printf)
      
      # up to 3-digit primes
      primes.expand(999)
      
      # possible digits
      digits = [1, 3, 7, 9]
      
      # generate numbers from <k> adjacent digits of <ns> (in both directions)
      @wrap(uniq)  # skip duplicate numbers
      def adj(ns, k=3):
        for ds in tuples(ns, k, circular=1):
          yield nconcat(ds)
          yield nconcat(rev(ds))
      
      # look for solutions using <n> digits
      def solve(n):
        # choose the n digits
        for ds in subsets(digits, size=n, select='R'):
          # the sum of the digits is prime
          if not (sum(ds) in primes): continue
          # each digit occurs a prime number of times
          m = multiset.from_seq(ds)
          if not all(v in primes for v in m.values()): continue
      
          # start with the smallest number
          n0 = m.min()
          # and choose an arrangement of the rest
          for ns in subsets(m.difference([n0]), size=len, select='mP', fn=list):
            if rev(ns) < ns: continue  # skip duplicate solutions
            ns.insert(0, n0)
            # check each 3 adjacent digits form a prime (in both directions)
            if not all(x in primes for x in adj(ns)): continue
            yield ns
      
      # find the smallest possible sequences
      for n in irange(3, inf):
        s = 0
        for ns in solve(n):
          printf("[n={n}] {ns} -> {t}", t=sum(ns))
          s += 1
        if s: break
      

      Solution: The total of the digits is 29.

      The digits in the circle are: (1, 9, 1, 9, 9). Consisting of 2 occurrences of the digit 1, and 3 occurrences of the digit 9.

      The 3-digit numbers formed are: 191, 199, 919, 991. Which are all prime.

      Like

    • Ruud's avatar

      Ruud 8:03 am on 16 July 2025 Permalink | Reply

      import istr
      import collections
      import itertools
      
      primes = [i for i in istr.range(2, 1000) if i.is_prime()]
      candidates = {str(prime) for prime in primes if len(prime) == 3 and all(c in "1379" for c in prime)}
      for n in itertools.count(2):
          for x in itertools.product("1379", repeat=int(n)):
              s = "".join(x)
              if all(count in primes for count in collections.Counter(s).values()):
                  s2 = str(s + s[0:2])
                  if (
                      all(s2[i : i + 3] in candidates for i in range(len(s)))
                      and all(s2[i : i + 3][::-1] in candidates for i in range(len(s)))
                      and (sumx:=sum(map(int, x))) in primes
                  ):
                      print(s,sumx)
                      assert False
      

      Like

  • Unknown's avatar

    Jim Randell 10:55 am on 4 July 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2478: [Palindromic primes] 

    From The Sunday Times, 21st March 2010 [link]

    An article in The Times of January 2 commented that 2010 was the sum of four primes. As a reader pointed out in a letter on January 5, this was hardly worthy of comment: by Goldbach’s Conjecture, any even number greater than two is the sum of just two primes.

    What is worthy of note is that 2010 is the sum of four palindromic primes. I found one such set of primes and each of my two grandsons, Oliver and Sam found different sets. My set had two primes in common with Oliver’s set and two in common with Sam’s set.

    What are my four palindromic primes?

    This puzzle was originally published with no title.

    [teaser2478]

     
    • Jim Randell's avatar

      Jim Randell 10:55 am on 4 July 2025 Permalink | Reply

      This Python program runs in 69ms. (Internal runtime is 902µs).

      from enigma import (primes, is_npalindrome, subsets, intersect, printf)
      
      # target value
      T = 2010
      
      # collect palindromic primes
      ps = list(p for p in primes.between(2, T) if is_npalindrome(p))
      
      # find 4-subsets with the required sum
      ts = list(ss for ss in subsets(ps, size=4) if sum(ss) == T)
      
      # choose a set for the setter
      for V in ts:
        # find other sets with 2 values in common with V
        gs = list(ss for ss in ts if ss != V and len(intersect([ss, V])) == 2)
        if len(gs) == 2:
          # output solution
          printf("V = {V} [O,S = {gs}]")
      

      Solution: Victor’s set was: (11, 151, 919, 929).

      And the grandsons sets were:

      (11, 313, 757, 929) [shares: 11, 929]
      (11, 353, 727, 919) [shares: 11, 919]

      And these are the only sets of 4 palindromic primes that sum to 2010.

      Like

    • Ruud's avatar

      Ruud 4:32 pm on 4 July 2025 Permalink | Reply

      import istr
      
      solutions = [set(p) for p in istr.combinations((i for i in istr.range(2010) if i.is_prime() and i == i[::-1]), 4) if sum(p) == 2010]
      print([solution for solution in solutions if sum(len(solution & solution1) == 2 for solution1 in solutions) == 2])
      

      Like

  • Unknown's avatar

    Jim Randell 8:26 am on 24 June 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2467: [Some primes] 

    From The Sunday Times, 3rd January 2010 [link]

    To celebrate the start of a new decade, I have written down a list of prime numbers whose sum is equal to one of the years of this decade (in other words, it is one of the numbers from 2010 to 2019, inclusive). Overall, the numbers in this list contain just eight digits, namely four different even digits and four different odd digits, each used once.

    With simple logic, rather than complicated calculations, you should be able to work out the list of numbers.

    What are they?

    This puzzle was originally published with no title.

    [teaser2467]

     
    • Jim Randell's avatar

      Jim Randell 8:26 am on 24 June 2025 Permalink | Reply

      This Python 3 program looks for sets of primes with the required property.

      It runs in 120ms. (Internal run time is 53ms).

      from enigma import (primes, nsplit, seq_items, join, printf)
      
      # find possible primes, and record digits
      ps = list()
      for p in primes.between(2, 2018):
        ds = nsplit(p)
        ss = set(ds)
        if len(ss) == len(ds):
          ps.append((p, ss))
      
      # odd and even digits
      (evens, odds) = ({0, 2, 4, 6, 8}, {1, 3, 5, 7, 9})
      
      # solve for primes in <ps>
      # i = start index in <ps>
      # t = total to far
      # ds = digits used so far
      # ns = primes used so far
      def solve(ps, i=0, t=0, ds=set(), ns=[]):
        if t > 2009:
          if len(ds) == 8:
            yield ns
        if len(ds) < 8:
          # add in another prime
          for (j, (p, ss)) in seq_items(ps, i):
            t_ = t + p
            if t_ > 2019: break
            if not ss.isdisjoint(ds): continue
            ds_ = ds.union(ss)
            if evens.issubset(ds_) or odds.issubset(ds_): continue
            yield from solve(ps, j + 1, t_, ds_, ns + [p])
      
      # solve the puzzle
      for ns in solve(ps):
        # output solution
        printf("{ns} = {t}", ns=join(ns, sep=" + "), t=sum(ns))
      

      Solution: The numbers are: 2, 947, 1063.

      And:

      2 + 947 + 1063 = 2012

      The unused digits are 5 (odd) and 8 (even).

      Like

    • Frits's avatar

      Frits 11:01 am on 24 June 2025 Permalink | Reply

      from itertools import compress
      
      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:])]
      
      # decompose <t> into increasing numbers from <ns> so that <k> different
      # digits have been used and the sum of the numbers is in range (t-9) ... t
      def decompose(t, ns, k=8, ds=set(), s=[]):
        if k <= 0 or t < 10:
          if k == 0 and t < 10:
            # not using one odd and one even digit
            if sum(int(x) for x in set("0123456789") - ds) % 2:
              yield s 
        else:
          for i, (n, dgts) in enumerate(ns):
            if n > t: break 
            if ds.isdisjoint(dgts):
              yield from decompose(t - n, ns[i + 1:], k - len(dgts), 
                                   ds | dgts, s + [n])
      
      m = 2000
      P = [(p, s) for p in primesbelow(m) if len(s := set(str(p))) == len(str(p))]
      
      # look for prime numbers that add up to 2010 ... 2019
      for ps in decompose(2019, P):
        print(f"answer: {' + '.join(str(x) for x in ps)} = {sum(ps)}")
      

      Like

  • Unknown's avatar

    Jim Randell 7:51 am on 17 June 2025 Permalink | Reply
    Tags: by: Victor Bryant   

    Teaser 2524: [Whispered birthday] 

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

    My birthday is on the first of a month. My three logical friends Lettice, Daisy and Bertha wanted to know which month. So I whispered to Lettice the number of letters in the spelling of the month, I whispered to Daisy the number of days in the month, and I whispered to Bertha the day of the week of my birthday this year. I explained to all three what I had done and then I asked them in turn whether they were able to work out the month yet. Their replies were:

    Lettice: no
    Daisy: no
    Bertha: no
    Lettice: no
    Daisy: no
    Bertha: yes

    What is my birthday month?

    This puzzle was originally published with no title.

    [teaser2524]

     
    • Jim Randell's avatar

      Jim Randell 7:53 am on 17 June 2025 Permalink | Reply

      This Python program uses the [[ filter_unique() ]] function from the enigma.py library to eliminate candidate solutions at each step.

      It runs in 70ms. (Internal runtime is 263µs).

      from enigma import (filter_unique, printf)
      
      # month names
      months = "January February March April May June July August September October November December".split()
      
      # maps month names to: L = number of letters; D = number of days; B = day of week of 1st
      L = dict((k, len(k)) for k in months)
      D = dict(zip(months, [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]))  # for 2011
      B = dict(zip(months, "Sa Tu Tu Fr Su We Fr Mo Th Sa Tu Th".split()))  # for 2011
      
      # start with all possible months
      ss = months
      
      # answers: 1 = "no" (non_unique), 0 = "yes" (unique)
      for (d, i) in [(L, 1), (D, 1), (B, 1), (L, 1), (D, 1), (B, 0)]:
        ss = filter_unique(ss, d.get)[i]
      
      # output solution(s)
      for m in ss:
        printf("month = {m}")
      

      Solution: The birthday month is March.

      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