Updates from February, 2024 Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 9:03 am on 25 February 2024 Permalink | Reply
    Tags: ,   

    Teaser 2572: Abominable 

    From The Sunday Times, 8th January 2012 [link] [link]

    In the art class Pat used five circles for his new design, Snowman. One circle made the head and another touching circle (with radius three times as big) made the body. Two equal circles (of radius one centimetre less than that of the head) made the arms (one on each side) and they touched both the body and the head. The fifth circle enclosed the other four, touching each of them. The radius of the head’s circle was a whole number of centimetres.

    How many centimetres?

    [teaser2572]

     
    • Jim Randell's avatar

      Jim Randell 9:04 am on 25 February 2024 Permalink | Reply

      See also: Teaser 2926.

      The arrangement looks like this:

      If we consider the head, body, one arm and the enclosing circle we have four mutually tangent circles, so we can use Descartes’ Theorem [ @wikipedia ] to express a relationship between their curvatures.

      If the curvatures are represented by k (where: k = 1 / r, i.e. the curvature is the reciprocal of the corresponding radius), then we have:

      (Σk)² = 2Σ(k²)

      In this instance the circles have radii of r, 3r, r − 1, 4r and so the curvatures are:

      k1 = 1/r
      k2 = 1/3r
      k3 = 1/(r − 1)
      k4 = −1/4r

      The curvature of the fourth circle is negative as it circumscribes the other three circles.

      We can solve the puzzle numerically by simply considering possible r values.

      The following program runs in 65ms. (Internal runtime is 102µs).

      Run: [ @replit ]

      from enigma import (Rational, irange, inf, sq, printf)
      
      Q = Rational()
      
      # consider possible values for r
      for r in irange(2, inf):
      
        # calculate the 4 curvatures
        ks = (Q(1, r), Q(1, 3 * r), Q(1, r - 1), Q(-1, 4 * r))
      
        # check Descartes' Theorem
        if sq(sum(ks)) == 2 * sum(map(sq, ks)):
          printf("r = {r}")
          break
      

      Solution: The head has a radius of 13 cm.

      Or we can solve it by finding the roots of a polynomial.

      This Python program runs in 66ms. (Internal runtime is 3.2ms).

      Run: [ @replit ]

      from enigma import (Polynomial, sq, printf)
      
      # consider the numerators of the curvatures, where the denominator is 12r(r-1)
      ks = [
        12 * Polynomial([-1, 1]),  # k1 = 12(r-1) / 12r(r-1) = 1/r
         4 * Polynomial([-1, 1]),  # k2 = 4(r-1) / 12r(r-1) = 1/3r
        12 * Polynomial([ 0, 1]),  # k3 = 12r / 12r(r-1) = 1/(r-1)
        -3 * Polynomial([-1, 1]),  # k4 = -3(r-1) / 12r(r-1) = -1/4r
      ]
      
      # Descartes' Theorem: sq(sum(ks)) = 2 * sum(map(sq, ks))
      p = sq(sum(ks)) - 2 * sum(map(sq, ks))
      
      # solve the polynomial for positive integer solutions
      for r in p.rational_roots(domain='Z', include='+'):
        printf("r = {r}")
      

      Analytically, we find the polynomial we are solving is:

      r² − 26r + 169 = 0

      (r − 13)² = 0
      r = 13

      Like

  • Unknown's avatar

    Jim Randell 5:16 pm on 19 February 2024 Permalink | Reply
    Tags:   

    Teaser 2671: On the face of it 

    From The Sunday Times, 1st December 2013 [link] [link]

    At the beginning of last year my wife and I were each given a clock. When we first looked at them the hands on both clocks showed the correct time but thereafter (although the two clocks coincided at least once more during the year) they generally showed different times.

    The problem was that my clock gained a certain whole number of minutes each day, and the same was true of my wife’s clock, but hers was even faster than mine. Actually my clock showed the correct time at least once in every month last year but that was not the case for my wife’s clock.

    How many minutes did each clock gain in a day?

    [teaser2671]

     
    • Jim Randell's avatar

      Jim Randell 5:17 pm on 19 February 2024 Permalink | Reply

      In this puzzle I am assuming that the clocks are standard 12-hour analogue clocks, that are not adjusted during the year (even for daylight savings time), and the correct time is also not adjusted for DST, and that the puzzle takes place in the year 2012 (it was originally published in 2013).

      Clock 1 showed the correct time at least once each month in 2012, but Clock 2 did not.

      Some time in January both clocks are correct, and some other time during the year (2012 had 366 days), both clocks are showing the same time. And the first time this happens Clock 2 is exactly 12 hours ahead of Clock 1. So during the course of up to 366 days, it has gained 720 minutes over Clock 1, this means the Clock 2 is gaining at least 2 minutes per day more than Clock 1.

      If it gains only 2 minutes more then it will take 360 days to get 720 minutes ahead of Clock 1, and so this will only work if the clocks are first looked at during the first 6 days of January. If it gains 3 or more minutes, any time in January will suffice.

      But if Clock 2 gains 720 minutes (or more) in any 29 day period, then it cannot avoid showing the right time in every month. So Clock 2 must gain less than 25 minutes per day.

      For each possible set of gains per day, this Python program looks for the earliest time in January when the clocks could give a correct time.

      It runs in 56ms. (Internal runtime is 302µs).

      Run: [ @replit ]

      from datetime import (datetime, timedelta)
      from enigma import (irange, irangef, fdiv, repeat, inc, divf, arg, printf)
      
      # year
      Y = arg(2012, 0, int)
      
      # calculate max gain for clock 2 (depends on number of days in Feb)
      M = divf(720, (datetime(Y, 3, 1) - timedelta(days=1)).day)
      
      # return day offsets when a clock gaining <x> minutes per day
      # has gained multiples of 720 minutes
      offsets = lambda x: list(irangef(0, 366, step=fdiv(720, x)))
      
      # look for earliest time when clocks are correct
      # with clock 1 gaining x min/day
      for x in irange(1, M - 2):
        # day offsets for clock 1
        ds1 = offsets(x)
        # clock 1 is correct at least once in each month
        if len(ds1) < 12: continue
      
        # clock 2 gains at least 2 min/day more than clock 1
        for y in irange(x + 2, M):
      
          # choose a time in Jan when the clocks are correct
          for t in repeat(inc(timedelta(minutes=1)), datetime(Y, 1, 1, 0, 0, 0)):
            if t.month > 1: break
      
            # find dates when clock 1 shows the correct time
            ts1 = list(t + timedelta(days=x) for x in ds1)
            # look for these occurring in 12 different months
            if not (len({t.month for t in ts1}) == 12): continue
      
            # find the dates when clock 2 is correct
            ts2 = list(t + timedelta(days=x) for x in offsets(y))
            # look for these occurring in fewer than 12 different months
            if not (len({t.month for t in ts2}) < 12): continue
            # check when the clocks are showing the same time
            s = t + timedelta(days=fdiv(720, y - x))
            if s.year > Y: continue
      
            # earliest solution found
            printf("clock 1 gains {x} mins/day")
            for z in ts1: printf("-> clock 1 correct @ {z}")
            printf("clock 2 gains {y} mins/day")
            for z in ts2: printf("-> clock 2 correct @ {z}")
            printf("clocks read same time @ {s}")
            printf()
            break
      

      Solution: The clocks gain 22 minutes and 24 minutes per day.

      If the clocks both show the correct time at midnight on 1st January 2012 then clock 1 shows the correct time at:

      2012-01-01 @ 00:00
      2012-02-02 @ 17:27
      2012-03-06 @ 10:54
      2012-04-08 @ 04:21
      2012-05-10 @ 21:49
      2012-06-12 @ 15:16
      2012-07-15 @ 08:43
      2012-08-17 @ 02:10
      2012-09-18 @ 19:38
      2012-10-12 @ 13:05
      2012-11-23 @ 06:32
      2012-12-26 @ 00:00

      We see that each of the 12 correct times occurs in a different month.

      And clock 2 shows the correct time at:

      2012-01-01 @ 00:00
      2012-01-31 @ 00:00
      2012-03-01 @ 00:00
      2012-03-31 @ 00:00
      2012-04-30 @ 00:00
      2012-05-30 @ 00:00
      2012-06-29 @ 00:00
      2012-07-29 @ 00:00
      2012-08-28 @ 00:00
      2012-09-27 @ 00:00
      2012-10-27 @ 00:00
      2012-11-26 @ 00:00
      2012-12-26 @ 00:00

      Although this clock shows 13 correct times in the same interval that the other clock shows 12 correct times, it has 2 correct times in January, 2 correct times in March, and no correct times in February.

      After 360 days, on 2012-12-26 @ 00:00, clock 1 has gained 7920 min = 5.5 days, and clock 2 has gained 8640 min = 6 days, so both clocks are showing the same (correct) time of 12:00.


      However, if we were to run the same puzzle in 2013 (not a leap year), we still get a solution with 22 min/day and 24 min/day (although dates after Feb are a day earlier, so clock 2 has 2 correct times in May not March), but we also find there is a second solution, where clock 1 gains 23 min/day and clock 2 gains 25 min/day.

      For example if the clocks both clocks show the correct time at 9:36am on 2nd January 2013, then clock 1 shows the correct time at:

      2013-01-02 @ 09:36
      2013-02-02 @ 16:54
      2013-03-06 @ 00:12
      2013-04-06 @ 07:30
      2013-05-07 @ 14:49
      2013-06-07 @ 22:07
      2013-07-09 @ 05:25
      2013-08-09 @ 12:43
      2013-09-09 @ 20:02
      2013-10-11 @ 03:20
      2013-11-11 @ 10:38
      2013-12-12 @ 17:56

      And clock 2 shows the correct time at:

      2013-01-02 @ 09:36
      2013-01-31 @ 04:48
      2013-03-01 @ 00:00
      2013-03-29 @ 19:12
      2013-04-27 @ 14:24
      2013-05-26 @ 09:36
      2013-06-24 @ 04:48
      2013-07-23 @ 00:00
      2013-08-20 @ 19:12
      2013-09-18 @ 14:24
      2013-10-17 @ 09:36
      2013-11-15 @ 04:48
      2013-12-14 @ 00:00

      After 360 days, on 2012-12-28 @ 09:36, clock 1 has gained 8280 min = 5.75 days, and clock 2 has gained 9000 min = 6.25 days, so both clocks are showing the same (incorrect) time of 3:36.

      And a third solution with the clocks gaining 22 min/day and 25 min/day, where the clocks start on 2013-01-02 @ 09:36 and show the same time after 240 days at 2013-08-30 @ 09:36.

      Like

    • Frits's avatar

      Frits 9:51 am on 23 February 2024 Permalink | Reply

         
      from datetime import date, timedelta
      from math import ceil
      
      DAYS = 366
      
      # dictionary month number per day (day 31 becomes month 2 (2012-02-01 00:00))
      d = {i: (date(2012, 1, 1) + timedelta(days=i)).month 
              for i in range(1, DAYS + 1)}
      
      # "I" need to have a correct time in at least another 11 months 
      mn = ceil(11 * 720 / DAYS)
      # maximum gain for not to have a guaranteed correct time at least once a month
      mx = ceil(720 / 29) - 1
      
      for my in range(mn, mx - 1):
        mths = set(d[ceil((m * 720) / my)]
                   for m in range(1, (my * DAYS) // 720 + 1)) 
        # at least twelve different months?
        if len({1} | mths) == 12:
          mytimes = set(ceil((m * 720) / my) 
                        for m in range(1, (my * DAYS) // 720 + 1))
        
          # my wife's clock is gaining at least 2 minutes per day more than my clock
          for wife in range(my + 2, mx + 1):
            mths = set(d[ceil((m * 720) / wife)]
                       for m in range(1, wife * DAYS // 720 + 1)) 
            if len({1} | mths) < 12:
              wifetimes = {ceil((m * 720) / wife) 
                           for m in range(1, wife * DAYS // 720 + 1)}
              # the two clocks coincided at least once more during the year     
              if mytimes & wifetimes:
                print(f"answer: {my} and {wife}")  
      

      Like

  • Unknown's avatar

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

    Teaser 2574: Higher powers 

    From The Sunday Times, 22nd January 2012 [link] [link]

    I have chosen two different two-digit numbers. They use the same two digits as each other, but in different orders. If I start with either number and raise it to a power equal to either of the two numbers, then I find that the answer is a number whose last two digits form the number with which I started.

    What are my two numbers?

    [teaser2574]

     
    • Jim Randell's avatar

      Jim Randell 4:04 pm on 15 February 2024 Permalink | Reply

      The following run file executes in 68ms. (Internal runtime of the generated program is 128µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "pow(AB, AB, 100) = AB"
      "pow(AB, BA, 100) = AB"
      
      "pow(BA, AB, 100) = BA"
      "pow(BA, BA, 100) = BA"
      
      "A < B"  # remove duplicate solution
      

      Solution: The numbers are 16 and 61.

      The powers can be calculated. We have:

      16^16 = 18 446744 073709 551616
      16^61 = 28 269553 036454 149273 332760 011886 696253 239742 350009 903329 945699 220681 916416

      Both of which end in 16, and:

      61^16 = 36751 693856 637464 631913 392961
      61^61 = 8 037480 562545 943774 063961 638435 258139 453693 382991 023311 670379 647429 452389 091570 630196 571368 048020 948560 431661

      both of which end in 61.

      Like

      • Hugo's avatar

        Hugo 4:26 pm on 15 February 2024 Permalink | Reply

        Successive powers of 16 end in 16, 56, 96, 36, 76, a repeating cycle.
        Successive powers of 61 end in 61, 21, 81, 41, 01, a repeating cycle.
        16 mod 5 = 1 = 61 mod 5. So for powers modulo 100 we take the first in each cycle
        (at least, I think that’s a logical conclusion!).

        Like

    • GeoffR's avatar

      GeoffR 7:01 pm on 15 February 2024 Permalink | Reply

       
      for A in range(1, 10):
          for B in range(A+1, 10):
              AB = 10*A + B
              BA = 10*B + A
              #  1st number properties
              if AB ** AB % 100 != AB:
                  continue
              if AB ** BA % 100 != AB:
                  continue
              # 2nd number properties
              if BA ** AB % 100 != BA:
                  continue
              if BA ** BA % 100 == BA:
                  print (f"Two numbers are {AB} and {BA}.")
      
      # Two numbers are 16 and 61.
      

      Like

  • Unknown's avatar

    Jim Randell 8:21 am on 13 February 2024 Permalink | Reply
    Tags:   

    Teaser 2661: Three clocks 

    From The Sunday Times, 22nd September 2013 [link] [link]

    I have three digital clocks, each showing the time as a four-digit display “hhmm” on the 24-hour scale. One keeps perfect time, one runs fast at a constant rate (less than a minute per day) and the third runs slow at exactly the same rate. Every six months I simultaneously reset the faulty clocks to the correct time. Recently I noticed that each clock displayed the same collection of digits but in different orders. In fact, on the fast clock no digit was in the correct place.

    What time did the correct clock show at that moment?

    [teaser2661]

     
    • Jim Randell's avatar

      Jim Randell 8:22 am on 13 February 2024 Permalink | Reply

      If the clocks are reset every 6 months then that is at most 184 days apart. So the inaccurate clocks are less than 184 minutes adrift from the accurate clock.

      This Python program collects together times that use the same digits but rearranged, and then looks for a collection of at least 3 different times, chooses a correct time, and looks for a fast time that is a derangement of the correct time. From this we calculate the difference window for the inaccurate clocks, and we can look for a corresponding slow time that appears in the same collection.

      It runs in 67ms. (Internal runtime is 9.8ms).

      Run: [ @replit ]

      from enigma import (defaultdict, irange, cproduct, ordered, dot, printf)
      
      # collect possible times by digit content
      d = defaultdict(list)
      for (hh, mm) in cproduct([irange(0, 23), irange(0, 59)]):
        ds = divmod(hh, 10) + divmod(mm, 10)
        d[ordered(*ds)].append(ds)
      
      # digit position values (in minutes, left to right)
      pos = (600, 60, 10, 1)
      
      # look for sets of digits corresponding to at least 3 times
      for (k, vs) in d.items():
        if len(vs) < 3: continue
      
        # choose the correct time
        for corr in vs:
          mc = dot(corr, pos)
          # choose a fast time
          for fast in vs:
            # which must be a derangement of corr
            if fast == corr or any(x == y for (x, y) in zip(fast, corr)): continue
            # calculate the window of difference (in minutes)
            md = (dot(fast, pos) - mc) % 1440
            if md > 183: continue
      
            # look for possible slow times (difference may be +/- 1)
            for w in [-1, 0, +1]:
              ms = (mc - md + w) % 1440
              (hh, mm) = divmod(ms, 60)
              slow = divmod(hh, 10) + divmod(mm, 10)
              if slow not in vs: continue
      
              # output solution
              printf("correct = {corr}; fast = {fast}; slow={slow}")
      

      Solution: The correct clock was showing: 2301.

      And the inaccurate clocks are adrift from the accurate clock by 150 – 151 minutes.

      For example, using fractional minutes we can suppose the accurate clock is showing:

      23:01.75 → 2301

      If the time difference with the inaccurate clocks is 150.5 minutes (= 2h30.5m) then the fast clock is showing:

      01:32.25 → 0132

      and the slow clock is showing:

      20:31.25 → 2031

      Each display uses the digits: 0, 1, 2, 3.

      Like

    • Frits's avatar

      Frits 4:39 am on 23 February 2024 Permalink | Reply

      I have a different version that matches Jim’s speed (but with more code).

          
      from enigma import SubstitutedExpression
      
      # invalid digit / symbol assignments
      d2i = dict()
      for d in range(0, 10):
        vs = set()
        if d > 1: vs.update('X')
        if d > 2: vs.update('AFSW')
        if d > 5: vs.update('CHU')
      
        d2i[d] = vs 
      
      # return time difference in minutes between h1:m1 and an earlier h2:m2  
      def diff_minutes(h1, m1, h2, m2):
        d = 60 * (h1 - h2) + m1 - m2 
        return d if d > 0 else 1440 + d
      
      # remove elements from list <s2> from list <s1>
      # eg diff_lists([1,3,1,2], [2,0,1]) results in [3,1]
      def diff_lists(s1, s2):
        for x in s2:
          if x in s1:
            s1.remove(x)
        return s1
      
      # correct : AB:CD
      # fast    : FG:HI
      # slow    : ST:UV
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          "AB < 24", 
          # ----- logic for fast clock FG:HI ------
          # speeding things up: check for <I>
          "(D + Z) % 10 in {A, B, C, D}",
      
          # add XYZ minutes for fast clock
          "0 < XYZ < 184",
          "(60 * AB + CD + XYZ) % 1440 = OPQR",
          "OPQR // 60 = FG",
          "OPQR % 60 = HI",
      
          # the same collection of digits 
          "sorted([A, B, C, D]) == sorted([F, G, H, I])",
        
          # ----- logic for slow clock ST:UV ------
          # 2 * correct = fast + slow + 1 - W with 0 <= W <= 2
          "(2 * (60 * AB + CD) - (60 * FG + HI) - 1 + W) % 1440 = KLMN",
          "KLMN // 60 = ST",
          "KLMN % 60 = UV",
          
          # the same collection of digits 
          "sorted([A, B, C, D]) == sorted([S, T, U, V])",
        ],
        answer="ABCD",
        d2i=d2i,
        # on the fast clock no digit was in the correct place
        distinct=("AF", "BG", "CH", "DI"),
        env=dict(diff_minutes=diff_minutes, diff_lists=diff_lists),
        reorder=0,
        verbose=0,    # use 256 to see the generated code
      )
      
      # print answers
      for ans in sorted(p.answers()):
        print(f"answer: {ans}")
      

      Like

  • Unknown's avatar

    Jim Randell 11:30 am on 8 February 2024 Permalink | Reply
    Tags:   

    Teaser 2659: Two by two 

    From The Sunday Times, 8th September 2013 [link] [link]

    I have given each letter of the alphabet a different value from 0 to 25, so some letters represent a single digit and some represent two digits.

    Therefore, for example, a three-letter word could represent a number of three, four, five or six digits. With my values it turns out that:

    TWO × TWO = FOUR

    Another “obvious” fact that I can tell you is that every digit occurring in TWO is a prime!

    What is the number FOUR?

    Interestingly, one of the earliest published alphametic puzzles was:

    TWO × TWO = THREE

    (using more usual alphametic rules), which appeared almost 100 years ago in the July 1924 issue of Strand Magazine.

    [teaser2659]

     
    • Jim Randell's avatar

      Jim Randell 11:31 am on 8 February 2024 Permalink | Reply

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library (although the method of constructing alphametic words is different from the usual alphametic rules).

      It runs in 194ms. (Internal runtime of the generated program is 78ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --base=26
      
      --code="nconc = lambda *ss: int(concat(ss))"
      --macro="@TWO = nconc(T, W, O)"
      --macro="@FOUR = nconc(F, O, U, R)"
      
      "sq(@TWO) == @FOUR"
      
      --answer="@FOUR"
      
      # T, W, O are chosen from: 2, 3, 5, 7, 22, 23, 25
      --invalid="0|1|4|6|8-21|24,TWO"
      
      # [optional] speeds things up a bit
      # FOUR is a square ...
      "is_square(@FOUR)"
      # ... so R must be the final digits of a square
      --invalid="2-3|7-8|10-15|17-20|22-23,R"
      

      Solution: FOUR = 105625.

      We have:

      TWO = 3:2:5 = 325
      FOUR = 10:5:6:25 = 105625 = 325^2


      And the solution to the TWO × TWO = THREE puzzle is:

      % python3 enigma.py SubstitutedExpression "TWO * TWO = THREE"
      (TWO * TWO = THREE)
      (138 * 138 = 19044) / E=4 H=9 O=8 R=0 T=1 W=3
      [1 solution]
      

      Like

    • GeoffR's avatar

      GeoffR 4:04 pm on 8 February 2024 Permalink | Reply

      from enigma import all_different
      from math import isqrt
      
      def is_sq(x):
         return isqrt(x) ** 2 == x
      
      tup_TWO = (2, 3, 5, 7, 22, 23, 25)
       
      for F in range(1, 26):
        for O in tup_TWO:
          if O == F:continue
          for U in range(26):
            if U in (F, O):continue
            for R in (4, 9, 25): # max value of R = 25
              if R in (U, O, F):continue
              FOUR = int(str(F) + str(O) + str(U) + str(R))
              if not is_sq(FOUR):continue
              for T in tup_TWO:
                for W in tup_TWO:
                  if not all_different(T, W, O, F, U, R):continue
                  TWO = int(str(T) + str(W) + str(O))
                  if TWO * TWO == FOUR:
                    print(f"TWO = {TWO}, FOUR = {FOUR}.")
                    
      # TWO = 325, FOUR = 105625.
      
      

      Like

    • GeoffR's avatar

      GeoffR 8:55 pm on 8 February 2024 Permalink | Reply

      TWO × TWO = THREE is much easier than TWO × TWO = FOUR, as the former has only digits 0..9 to consider.

      from itertools import permutations
      
      for p1 in permutations('1234567890', 3):
         T, W, O = p1
         if T == '0':continue
         TWO = int(T + W + O)
         q1 = set('01234560789').difference([T, W, O])
         for p2 in permutations(q1, 3):
            R, H, E = p2
            THREE = int(T + H + R + E + E)
            if TWO * TWO == THREE:
               print(f"Sum: {TWO} * {TWO} = {THREE}.")
      
      # Sum: 138 * 138 = 19044.
      

      Like

  • Unknown's avatar

    Jim Randell 11:14 pm on 4 February 2024 Permalink | Reply
    Tags:   

    Teaser 2660: Oblonging 

    From The Sunday Times, 15th September 2013 [link] [link]

    I have made a set of “oblongs”, which is what I call rectangles where the longer sides are one centimetre longer than the shorter sides. I made this set from some A4 sheets of card (without any pasting), cutting out one oblong of each of the sizes 1 cm × 2 cm, 2 cm × 3 cm, 3 cm × 4 cm, and so on up to a particular size. I needed more than one A4 sheet to do this. I have given the set to my family and challenged them to use all the card in jig-saw fashion to make another oblong. After considerable effort they have managed to do this.

    How many oblongs are in the set?

    There are now 1000 Teaser puzzles available on the site.

    [teaser2660]

     
    • Jim Randell's avatar

      Jim Randell 11:15 pm on 4 February 2024 Permalink | Reply

      See also: Enigma 1491.

      An A4 sheet is 21.0 cm × 29.7 cm, so the largest oblong that can be made is 21 × 22.

      And the total area is more than 21 × 29 = 609 sq cm, then we will need multiple A4 sheets to make the oblongs.

      This is sufficient to find a single candidate solution.

      Run: [ @replit ]

      from enigma import (irange, quadratic, printf)
      
      # consider possible oblongs a x (a + 1) and accumulate total area
      t = 0
      for a in irange(1, 21):
        t += a * (a + 1)
        # total area > A4
        if not (t > 609): continue
        # look for an oblong with same area: x(x + 1) = t
        for x in quadratic(1, 1, -t, domain='Z', include='+'):
          printf("1x2 .. {a}x{a1} -> t = {t} -> {x}x{x1}", a1=a + 1, x1=x + 1)
      

      Solution: There are 20 oblongs in the set.

      The oblongs 1×2, 2×3, …, 20×21 have total area of 3080, which is the same as a 55×56 oblong.

      This is the only possible candidate, but it is perhaps more difficult to actually fit the tiles together to make the large oblong.

      However, here is one possible packing:

      Like

  • Unknown's avatar

    Jim Randell 9:16 am on 1 February 2024 Permalink | Reply
    Tags:   

    Teaser 2619: First and last 

    From The Sunday Times, 2nd December 2012 [link] [link]

    A “First and last” number (FLN) is any number that is divisible by its first and last digits. Using each non-zero digit just once, I wrote down a 3-digit FLN followed by three 2-digit FLNs. Putting these in one long line in that order gave me a 9-digit FLN. When the first and last digits were removed from this 9-digit number, I was left with a 7-digit FLN.

    What was the 9-digit number?

    [teaser2619]

     
    • Jim Randell's avatar

      Jim Randell 9:16 am on 1 February 2024 Permalink | Reply

      This run file executes in 83ms. (Internal runtime of the generated program is 202µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      
      # the following are FLNs:
      #
      #   ABC, DE, FG, HI
      #   ABCDEFGHI
      #   BCDEFGH
      
      # 3-digit FLN
      "ABC % A = 0" "ABC % C = 0"
      
      # 3x 2-digit FLNs
      "DE % D = 0" "DE % E = 0"
      "FG % F = 0" "FG % G = 0"
      "HI % H = 0" "HI % I = 0"
      
      # 9-digit FLN
      "ABCDEFGHI % A = 0" "ABCDEFGHI % I = 0"
      
      # 7-digit FLN
      "BCDEFGH % B = 0" "BCDEFGH % H = 0"
      
      --answer="ABCDEFGHI"
      --template="(ABC DE FG HI)"
      --solution=""
      

      Solution: The 9-digit FLN is: 972364815.

      So we have the following FLNs:

      3-digit: 972
      2-digit: 36, 48, 15
      9-digit: 972364815
      7-digit: 7236481

      Like

    • GeoffR's avatar

      GeoffR 9:49 pm on 1 February 2024 Permalink | Reply

      
      from itertools import permutations
      from functools import reduce
      
      nfd = lambda ns: reduce(lambda x, y: 10 * x + y, ns)
      
      # 3-digit FLN number
      for a, b, c  in permutations(range(1, 10), 3):
          abc = nfd([a, b, c])
          if not(abc % c == 0 and abc % a == 0):continue
         
          # find remaining three 2-digit FLN numbers
          q1 = set(range(1, 10)).difference({a,b,c})
          for p2 in permutations(q1):
              d, e, f, g, h, i = p2
              de, fg, hi = 10*d + e, 10*f + g, 10*h + i
              if not (de % d == 0 and de % e == 0):continue
              if not (fg % f == 0 and fg % g == 0):continue
              if not (hi % h == 0 and hi % i == 0):continue
              
              # check 9-digit and 7-digit FLN numbers
              dig9 = nfd([a, b, c, d, e, f, g, h, i])
              if not (dig9 % a == 0 and dig9 % i == 0):continue
              # remove first and last digits from 9-digit number
              dig7 = nfd([b, c, d, e, f, g, h])
              if dig7 % b == 0 and dig7 % h == 0:
                  print(f"Nine digit number = {dig9}.")
      
      # Nine digit number = 972364815.
      

      Like

  • Unknown's avatar

    Jim Randell 11:11 am on 25 January 2024 Permalink | Reply
    Tags:   

    Teaser 2620: Stating the obvious 

    From The Sunday Times, 9th December 2012 [link] [link]

    I have replaced the letters of the alphabet by the numbers 0 to 25 in some order, using different numbers for different letters. So, for example, a three-letter word could represent a number of three, four, five or six digits. With my particular values:

    ONE + ONE = TWO, and
    ONE is a perfect square.

    What number does TWO represent?

    [teaser2620]

     
    • Jim Randell's avatar

      Jim Randell 11:13 am on 25 January 2024 Permalink | Reply

      The value of a word is formed by concatenating the values (in base 10) of the digits, and then reading the result as a number.

      So, for example, if O = 10, N = 6, E = 25, then the value of ONE is 10625 (= 10:6:25).

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

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --base=26
      --code="word = lambda *vs: int(concat(vs))"
      --macro="@ONE = word(O, N, E)"
      --macro="@TWO = word(T, W, O)"
      --invalid="0,OT"
      
      # ONE is a perfect square
      "is_square(@ONE)"
      
      # ONE + ONE = TWO
      "2 * @ONE == @TWO"
      
      --answer="@TWO"
      

      Solution: TWO = 4232.

      There are two assignments that achieve this:

      E=6 N=11 O=2 T=4 W=23 → ONE=2116 TWO=4232
      E=16 N=1 O=2 T=4 W=23 → ONE=2116 TWO=4232

      And if we allow leading zeros (i.e. O or T is 0), we can also have:

      E=25 N=2 O=0 T=4 W=5 → ONE=225 TWO=450
      E=25 N=6 O=0 T=12 W=5 → ONE=625 TWO=1250
      E=25 N=12 O=0 T=24 W=5 → ONE=1225 TWO=2450

      So perhaps the puzzle would have been better if the values used were 1 to 26.

      Like

    • GeoffR's avatar

      GeoffR 3:00 pm on 25 January 2024 Permalink | Reply

      from math import isqrt
      def is_sq(x):
         return isqrt(x) ** 2 == x
      
      for O in range(1, 27):
          for N in range(26):
              if N == O:
                  continue
              for E in range(26):
                  if E in (O, N):
                      continue
                  #1. ONE is a perfect square.
                  ONE = int(str(O) + str(N) + str(E))
                  if is_sq(ONE):
                      for T in range(1, 27):
                          if T in (O, N, E):
                              continue
                          for W in range(26):
                              if W in (T, O, N, E):
                                  continue      
                              #2. ONE + ONE = TWO
                              TWO = int(str(T) + str(W) + str(O))
                              if ONE + ONE == TWO:
                                  print(f"ONE = {ONE} and TWO = {TWO}")
                                  print(f"O={O}, N={N}, E={E}, T={T}, W={W}")
                                  print()
                                                    
      # ONE = 2116 and TWO = 4232
      # O=2, N=1, E=16, T=4, W=23
      
      # ONE = 2116 and TWO = 4232
      # O=2, N=11, E=6, T=4, W=23
      
      

      Like

    • Frits's avatar

      Frits 4:50 am on 26 January 2024 Permalink | Reply

          
      from itertools import permutations
      from enigma import is_square
      
      sols = set()
      # O has to be even as 2 * ONE = TWO  
      for O in range(2, 26, 2):
        lnO = 1 + (O > 9)
        for N, E in permutations([n for n in range(26) if n != O], 2):
          if not is_square(ONE := int(str(O) + str(N) + str(E))): continue
          
          # parse TWO into TW and O_
          (sTW, sO_) = ((sTWO := str(ONE + ONE))[:-lnO], sTWO[-lnO:])
          if sO_ != str(O): continue
          
          # parse TW into T and W
          for t in range(1 + (len(sTW) == 4), 2 + (len(sTW) > 2)):
            (T, W) = (int(sTW[:t]), int(sTW[t:]))
            # different numbers
            if T == W or any(n in {O, N, E} or n > 25 for n in [T, W]): continue
            sols.add(sTWO)
      
      print(f"answer: {' or '.join(sols)}")      
      

      Like

  • Unknown's avatar

    Jim Randell 1:33 pm on 23 January 2024 Permalink | Reply
    Tags:   

    Teaser 2669: Extending the garden 

    From The Sunday Times, 17th November 2013 [link] [link]

    George and Martha used to have a square garden with sides of length 10 metres, but they have extended it by adding an acute-angled triangle on each side of the square. The result is that the new garden has eight straight sides, each of which is fenced. The fences are different whole number of metres long, with the shortest being one metre and the longest 13 metres. George remarked that the average length of the fences was also a whole number of metres.

    In increasing order, what were the lengths of the eight fences?

    [teaser2669]

     
    • Jim Randell's avatar

      Jim Randell 1:34 pm on 23 January 2024 Permalink | Reply

      If we consider the side of the 10 metre square to be the base of a triangle, then the vertex of the triangle formed by the new fences must lie above the interior of the base, and outside a semicircle whose diameter is the base.

      This Python program finds possible side configurations (for a shorter and longer site, chosen from 1..13), and then combines 4 of these configurations to find viable sets of triangles.

      It runs in 59ms. (Internal runtime is 3.5ms).

      Run: [ @replit ]

      from enigma import (irange, fdiv, sq, disjoint_union, printf)
      
      # generate possible (a, b) extensions
      def sides():
        # consider possible sides (from 1 - 13) to make an acute angled triangle
        # the shorter side
        for a in irange(1, 12):
          # the longer side
          for b in irange(max(a + 1, 11 - a), 13):
            # determine vertex points
            x = fdiv(sq(b) - sq(a), 20)
            # vertex must be above base
            if not (x < 5): continue
            x2 = sq(x)
            y2 = sq(b) - sq(x + 5)
            # vertex must lie outside a radius 5 semicircle
            if not (x2 + y2 > 25): continue
            # this is a viable configuration
            yield (a, b)
      
      # generate solutions
      def solve(sides, k, ts=list(), ss=set()):
        # are we done?
        if k == 0:
          # mean is an integer
          if sum(ss) % len(ss) != 0: return
          # smallest side is 1, largest is 13
          ss = sorted(ss)
          if not (ss[0] == 1 and ss[-1] == 13): return
          # viable solution
          yield (ts, ss)
        else:
          # add in another triangle
          for (i, t) in enumerate(sides):
            # all sides are different
            ss_ = disjoint_union([ss, t])
            if ss_:
              yield from solve(sides[i:], k - 1, ts + [t], ss_)
      
      # find solutions
      rs = set()
      for (ts, ss) in solve(list(sides()), 4):
        printf("{ts} -> {ss}")
        rs.add(tuple(ss))
      
      # output solutions
      for ss in rs:
        printf("sides = {ss}")
      

      Solution: The lengths of the fencing are (in metres): 1, 5, 7, 8, 9, 10, 11, 13.

      There are 2 configurations that use this set of fences:

      (1, 10) (5, 9) (7, 8) (11, 13)
      (1, 10) (5, 11) (7, 8) (9, 13)

      Here are examples of these configurations:

      I did consider adding code to ensure the fences could be arranged such that the two fences meeting at the corner were not co-linear, but from the example diagrams it is clear that this can be done in both cases.

      Like

  • Unknown's avatar

    Jim Randell 3:09 pm on 15 January 2024 Permalink | Reply
    Tags:   

    Teaser 2666: Multiple calls 

    From The Sunday Times, 27th October 2013 [link] [link]

    In my office each of the 100 employees has a different two-digit phone number, from 00 to 99. Recently my phone was rewired so that each number button generated an incorrect digit. Trying to phone four of my colleagues resulted in me calling double the intended number, and, for more than ten of my colleagues, trying to phone their number resulted in me calling triple the intended number. Also if I tried to phone any colleague and then asked for their phone number, then phoned that number and asked that person for their number, then phoned that number … and so on, it always took ten calls to contact the person I intended.

    If I tried to phone the numbers 01, 23, 45, 67 and 89 respectively, which numbers would I actually get?

    [teaser2666]

     
    • Jim Randell's avatar

      Jim Randell 3:09 pm on 15 January 2024 Permalink | Reply

      Numbers that map to their double must be in the range 01 – 49, and numbers that map to their triple in the range 01 – 33, and we need more than 10 of them.

      If start with any number we always get a chain of 10 numbers before we reach the intended number. So the reassignment of digits must form a complete (length 10) cycle.

      We could generate all possible complete cycles and look for any that meet the remaining conditions, but this is a slow approach (although quite short to program).

      This Python program considers possible mappings that give 11 or more numbers that map to their triple, and then attempts to fill out the remaining values such that a complete cycle is formed.

      It runs in 73ms. (Internal runtime is 9.9ms).

      from enigma import (irange, diff, subsets, peek, map2str, printf)
      
      digits = list(irange(0, 9))
      
      # check a mapping is a complete cycle
      def is_cycle(m):
        if not m: return
        k = peek(m.keys())
        (v, n) = (m[k], 1)
        while v != k:
          v = m[v]
          n += 1
        return n == len(m.keys())
      
      # update a mapping
      def update(m, ks, vs):
        m = m.copy()
        for (k, v) in zip(ks, vs):
          # must be a derangement
          if k == v: return
          # existing key
          if k in m:
            if m[k] != v: return
          else:
            # new key
            if v in m.values(): return
            # make the update
            m[k] = v
        return m
      
      # construct mappings with more than 10 triples
      def solve(k=0, m=dict(), ts=set()):
        # are we done?
        if k > 33:
          if len(ts) > 10:
            yield m
        else:
          # consider mapping k -> 3k
          m_ = update(m, divmod(k, 10), divmod(3 * k, 10))
          if m_:
            yield from solve(k + 1, m_, ts.union({k}))
          # or not
          if m_ != m:
            yield from solve(k + 1, m, ts)
      
      # look for potential mappings
      for r in solve():
        # map remaining keys to remaining values
        ks = diff(digits, r.keys())
        vs = diff(digits, r.values())
        for ss in subsets(vs, size=len, select='P'):
          m = update(r, ks, ss)
          # the map must form a complete cycle
          if not is_cycle(m): continue
          # find doubles and triples
          (ds, ts) = (list(), list())
          for k in irange(0, 49):
            (a, b) = divmod(k, 10)
            v = 10 * m[a] + m[b]
            if v == 2 * k: ds.append(k)
            if v == 3 * k: ts.append(k)
          # we need exactly 4 doubles and more than 10 triples
          if not (len(ds) == 4 and len(ts) > 10): continue
          # output solution
          printf("{m}", m=map2str(m, arr="->", enc=""))
          printf("doubles = {ds}")
          printf("triples = {ts}")
          printf()
      

      Solution: 01 → 13; 23 → 69; 45 → 20; 67 → 84; 89 → 57.

      So the 4 numbers that map to their doubles are:

      05 → 10
      07 → 14
      15 → 30
      17 → 34

      and the 11 numbers that map to their triples are:

      04 → 12
      06 → 18
      11 → 33
      12 → 36
      13 → 39
      21 → 63
      22 → 66
      23 → 69
      31 → 93
      32 → 96
      33 → 99

      Like

    • Frits's avatar

      Frits 2:34 pm on 19 January 2024 Permalink | Reply

      Nice solution.

      I also tried the same setup starting with mappings of exactly 4 doubles but that was a lot slower.
      I can’t find the “Run” button anymore when using the replit link.

      Like

      • Jim Randell's avatar

        Jim Randell 5:38 pm on 19 January 2024 Permalink | Reply

        It looks like replit have changed their interface so you can no longer directly run someone else’s code. Now you need to fork it to your own account before you can run it.

        It seems like this means you can no longer use replit to share code as easily as before, which is a bit annoying.

        Like

  • Unknown's avatar

    Jim Randell 12:36 pm on 7 January 2024 Permalink | Reply
    Tags:   

    Teaser 2668: Small cubes 

    From The Sunday Times, 10th November 2013 [link] [link]

    Oliver and Megan each had a different- sized cuboid whose sides were all whole numbers of centimetres in length. Their cuboids were painted all over. They each cut their cuboid into one-centimetre cubes, some of which were unpainted, the rest being painted on one or more face. Oliver observed that for both cuboids, the number of cubes with no painted faces was the same as the number with two painted faces. Then Megan added: “I have more cubes than you, and the difference between our totals is equal to the number of your unpainted cubes”.

    How many of Megan’s cubes had just one painted face?

    [teaser2668]

     
    • Jim Randell's avatar

      Jim Randell 12:37 pm on 7 January 2024 Permalink | Reply

      Consider a cubiod with dimensions a × b × c. Then we can calculate the number of cubelets that are painted on the required number of faces:

      3 faces = 8
      2 faces = 4(a + b + c − 6)
      1 face = 2((a − 2)(b − 2) + (a − 2)(c − 2) + (b − 2)(c − 2))
      0 faces = (a − 2)(b − 2)(c − 2)

      providing the smallest dimension is greater than 1.

      And as some of the cubelets are unpainted the cuboids we are interested in must have smallest dimension of at least 3.

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

      Run: [ @replit ]

      from enigma import (defaultdict, irange, inf, decompose, map2str, printf)
      
      # total number of faces is indicated by key 7
      total = 7
      
      # for a cuboid of dimensions a x b x c
      # return number of cubelets by faces painted, and the total number of cubelets
      def cubelets(a, b, c):
        assert min(a, b, c) > 1
        r = dict()
        r[3] = 8
        r[2] = 4 * (a + b + c - 6)
        r[0] = (a - 2) * (b - 2) * (c - 2)
        t = a * b * c
        r[1] = t - sum(r.values())
        r[total] = t
        return r
      
      def solve():
        # collect cubes by total
        g = defaultdict(list)
        # consider increasing a + b + c dimensions
        for s in irange(3, inf):
          # consider cuboids for M
          for (a, b, c) in decompose(s, 3, increasing=-1, sep=0, min_v=3):
            # calculate number of cubelets
            r = cubelets(a, b, c)
            # we are interested in those cubes where r[0] = r[2]
            if not (r[2] == r[0] > 0): continue
      
            # consider smaller totals (for O)
            for (k, vs) in g.items():
              # difference between totals
              d = r[total] - k
              if d < 1: continue
              for (r_, (a_, b_, c_)) in vs:
                if r_[0] == d:
                  # return (M, O) as (cubelets, dimensions)
                  yield ((r, (a, b, c)), (r_, (a_, b_, c_)))
            # record this cuboid
            g[r[total]].append((r, (a, b, c)))
      
      # find possible (M, O) values
      for ((r, (a, b, c)), (r_, (a_, b_, c_))) in solve():
        fmt = lambda r: map2str(r, arr='->', enc="")
        printf("M = {a} x {b} x {c} [{r}]; O = {a_} x {b_} x {c_} [{r_}]", r=fmt(r), r_=fmt(r_))
        break  # only need the first answer
      

      Solution: Megan has 112 cubelets painted on just one face.

      Megan has a cuboid with dimensions: 12 × 5 × 4.

      12 × 5 × 4 = 240 cubelets
      8 painted on 3 faces
      60 painted on 2 faces
      112 painted on 1 face
      60 painted on 0 faces

      Oliver has a cuboid with dimensions: 8 × 6 × 4.

      8 × 6 × 4 = 192 cubelets
      8 painted on 3 faces
      48 painted on 2 faces
      88 painted on 1 face
      48 painted on 0 faces

      And:

      240 − 192 = 48


      If cuboids with a dimensions less than 3 were allowed, then we can find further solutions using cuboids that give 0 unpainted cubelets, such as:

      Oliver = 8 × 6 × 4 (= 192 cubelets, 48 unpainted, 48 painted on 2 faces)
      Megan = 120 × 2 × 1 (= 240 cubelets, 0 unpainted, 0 painted on 2 faces)

      Like

  • Unknown's avatar

    Jim Randell 7:51 am on 2 January 2024 Permalink | Reply
    Tags:   

    Teaser 2667: Prime time 

    From The Sunday Times, 3rd November 2013 [link] [link]

    On a picture of a clock face I have written A next to one of the numbers. Then I counted a certain number of “hours” clockwise and wrote B. Then I continued in this pattern, always counting the same number of places clockwise and writing the next letter of the alphabet. In this way each letter corresponds to a number between 1 and 12. I noticed that the two numbers with three letters by them were prime. I also noticed that if I wrote the numbers corresponding to the letters of PRIME in order and read them as one long number, then I got a 6-digit prime.

    Which number corresponds to A and which to B?

    [teaser2667]

     
    • Jim Randell's avatar

      Jim Randell 7:52 am on 2 January 2024 Permalink | Reply

      This Python program runs in 60ms. (Internal runtime is 2.1ms).

      Run: [ @replit ]

      from enigma import (
        str_upper, clock, cproduct, irange, inf, multiset, concat, is_prime,
        map2str, printf
      )
      
      # the letters to distribute
      letters = str_upper
      
      # calculate clock values (1-12)
      fn = clock(12)
      
      # choose a starting number and a step
      for (A, n) in cproduct([irange(1, 12), irange(1, 11)]):
        # assign values to the letters
        d = dict((k, fn(v)) for (k, v) in zip(letters, irange(A, inf, step=n)))
      
        # find the values that correspond to 3 letters
        m = multiset.from_seq(d.values())
        k3s = list(k for (k, v) in m.items() if v == 3)
        # there are exactly 2 values, and each is prime
        if not (len(k3s) == 2 and all(is_prime(k) for k in k3s)): continue
      
        # collect the values corresponding to PRIME
        vs = list(d[k] for k in 'PRIME')
        # does it form a 6-digit prime number
        p = concat(vs)
        if not (len(p) == 6 and is_prime(int(p))): continue
      
        # output solution
        printf("A={A} n={n} p={p} [{d}]", d=map2str(d, sep=" ", enc=""))
      

      Solution: A = 7. B = 2.

      So we start at 7 = A and advance 7 hours for each letter.

      The assignment of letters to values is:

      1 = GS
      2 = BNZ
      3 = IU
      4 = DP
      5 = KW
      6 = FR
      7 = AMY
      8 = HT
      9 = OC
      10 = JV
      11 = EQ
      12 = LX

      The values corresponding to three letters being 2 and 7 (both prime).

      And we have:

      PRIME = 4:6:3:7:11

      Which corresponds to the 6-digit prime 463711.

      Like

  • Unknown's avatar

    Jim Randell 8:38 am on 14 December 2023 Permalink | Reply
    Tags: ,   

    Teaser 2665: Milky ways 

    From The Sunday Times, 20th October 2013 [link] [link]

    Each evening Pat drives his herd of cows into the milking parlour. The cows are ear-tagged 1, 2, 3, … and the parlour is divided into stalls also numbered 1, 2, 3, …, with one stall for each cow. The cows file in and choose empty stalls at random until all the stalls are full. Pat has noticed that very often it happens that no cow occupies a stall with the same number as her tag. Pat worked out the number of different ways this could happen, and also the number of ways that at least one cow could be in a stall with the same number as herself. The two answers were less than a million and Pat noticed that the sum of the digits in each was the same.

    How many cows are in the herd?

    [teaser2665]

     
    • Jim Randell's avatar

      Jim Randell 8:39 am on 14 December 2023 Permalink | Reply

      (See also: Teaser 2995, Teaser 2779).

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

      Run: [ @replit ]

      from enigma import (irange, inf, factorial, subfactorial, dsum, printf)
      
      # consider 3 or more cows
      for k in irange(3, inf):
        # calculate the number of possible arrangements
        n = factorial(k)
        # calculate the number of derangements
        d = subfactorial(k)
        # and the number of arrangements that are not derangements
        r = n - d
        if not (max(d, r) < 1000000): break
        # calculate digit sum
        if dsum(d) == dsum(r):
          # output solution
          printf("k={k}: n={n} d={d} r={r}")
      

      Solution: There were 7 cows in the herd.

      The cows can be arranged in factorial(7) = 5040 different ways.

      Of these subfactorial(7) = 1854 are derangements.

      And the remaining 3186 are not derangements, so at least one cow must have the same number as the stall it is in.

      And dsum(1854) = dsum(3186) = 18.


      If we allow numbers over 1 million, then there are further solutions at k = 46, 52, 94, 115, 124, 274, 313, 346, …

      Like

  • Unknown's avatar

    Jim Randell 3:47 pm on 12 December 2023 Permalink | Reply
    Tags:   

    Teaser 2658: Different views 

    From The Sunday Times, 1st September 2013 [link] [link]

    Oliver arranged six [identical standard] dice in a neat pile with three in the bottom row, two in the middle row and one at the top. The faces of these dice were digits rather than the corresponding number of dots. Looking down on them, Beth saw that the five partially-visible tops of the dice contained different digits. In the three rows at the front she saw 1-digit, 2-digit and 3-digit primes, whereas from the back she saw three perfect squares. On the left, working down the three sides, she saw a 3-digit square whereas on the right, again working down, she saw a 3-digit prime.

    What was this 3-digit prime?

    [teaser2658]

     
    • Jim Randell's avatar

      Jim Randell 3:47 pm on 12 December 2023 Permalink | Reply

      We need to make some additional assumptions about the dice in order to arrive at a unique solution.

      I assumed that the dice are “standard”, i.e. each has the digits 1-6 on, and they are arranged such that opposite faces sum to 7, and the numbers 1, 2, 3 are arranged anti-clockwise around one of the vertices.

      I used the [[ Cube() ]] class (originally written for Teaser 2835) to generate all possible rotations of a standard die, and then used the [[ SubstitutedExpression ]] solver from the enigma.py library to solve the puzzle.

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

      #! python3 -m enigma -rr
      
      #            C
      #         +-----+
      #      B S| K/R |V D
      #      +--+--+--+--+
      #   A T| I/Q | J/P |W E
      #   +--+--+--+--+--+--+
      #  U| F/N | G/M | H/L |X
      #   +-----+-----+-----+
      #
      #  top   = ABCDE
      #  front = FGH, IJ, K
      #  back  = LMN, PQ, R
      #  left  = STU
      #  right = VWX [= answer]
      
      SubstitutedExpression
      
      --digits="1-6"
      
      # visible top faces are all different
      # faces of each dice are different
      --distinct="ABCDE,AFNU,BIQT,CKRSV,DJPW,EHLX,GM"
      
      # visible front faces form primes
      "is_prime(FGH)"
      "is_prime(IJ)"
      "is_prime(K)"
      
      # visible back faces form squares
      "is_square(LMN)"
      "is_square(PQ)"
      "is_square(R)"
      
      # left (top to bottom) forms a 3-digit square
      "is_square(STU)"
      
      # right (top to bottom) forms a 3-digit prime
      "is_prime(VWX)"
      
      # answer is the 3 digit prime on the right
      --answer="VWX"
      
      # additional checks for standard dice
      --code="from cube import Cube"
      --code="dice = set()"
      --code="dice.update(r.faces for r in Cube(faces=(1, 6, 2, 5, 3, 4)).rotations())" # standard die
      --code="is_die = lambda *fs: any(all(x == 0 or x == y for (x, y) in zip(fs, die)) for die in dice)"
      
      # perform check on the six (partial) dice
      "is_die(A, 0, F, N, U, 0)"
      "is_die(B, 0, I, Q, T, 0)"
      "is_die(C, 0, K, R, S, V)"
      "is_die(D, 0, J, P, 0, W)"
      "is_die(E, 0, H, L, 0, X)"
      "is_die(0, 0, G, M, 0, 0)"
      
      --template="(A, 0, F, N, U, 0) (B, 0, I, Q, T, 0) (C, 0, K, R, S, V) (D, 0, J, P, 0, W), (E, 0, H, L, 0, X) (0, 0, G, M, 0, 0)"
      --solution=""
      --verbose="THA"
      --denest
      

      Solution: The prime when the pile is viewed from the right is: 523.

      From the top the faces form: 31642 (all digits different).

      From the front the faces form: 3, 31, 251 (prime numbers).

      From the back the faces form: 4, 64, 625 (square numbers).

      From the left (top-to-bottom) the faces form: 256 (a square number).

      From the right (top-to-bottom) the faces form: 523 (a prime number).


      The problem can also be solved with six identical dice where the numbers 1, 2, 3 are arranged clockwise around one of the vertices (i.e. “mirror image” dice), and we get the same result.

      But if we are allowed to mix these two types of dice then we can get an additional answer of 653.

      And without the additional constraints (i.e. allowing dice where the digits 1-6 appear in any pattern) we can find many possible solutions.

      Like

    • Frits's avatar

      Frits 2:03 pm on 13 December 2023 Permalink | Reply

       
      from itertools import product
      
      # get rid of numbers with invalid digits 0,7,8 and 9 or 
      # with digits occuring more than once
      cleanup = lambda s: {x for x in s if len(set(str(x))) == len(str(x)) and
                           not any(d in str(x) for d in '7890')}
      oppsides = lambda n: [n, str(7 - int(n))]      
      
      # given two dice faces anti-clockwise at a vertex, find the third
      # face anti-clockwise at this vertex (western die if same is true)
      def die_third_face(first, second, same=False):
        # credit: B. Gladman
        if second in (first, 7 - first):
          raise ValueError
        t, f = min(first, 7 - first), min(second, 7 - second)
        c1 = ((f - t) % 3 == (1 if same else 2))
        c2 = (first < 4) == (second < 4)
        return 6 - t - f if c1 == c2 else t + f + 1
      
      # determine valid primes up to 1000
      P = {3, 5, 7}
      P |= {x for x in range(11, 100, 2) if all(x % p for p in P)}
      P |= {2} | {x for x in range(101, 1000, 2) if all(x % p for p in P)}
      P = cleanup(P)
      
      # determine valid squares up to 1000
      sq = cleanup(x * x for x in range(1, 32))
      sq3 = [str(x) for x in sq if x > 99]
      pr3 = [str(x) for x in P if x > 99]
      
      # valid prime/square combinations
      cands = {ln: [(p, s) for s in sq  
               if len(st := str(s)) == ln and 
               (p := (7 * int('111'[:ln]) - int(st[::-1]))) in P and
               all(len(set(str(x))) == len(st) for x in (s, p))] for ln in (1, 2, 3)}         
      
      # check all possible combinations
      for (pt, st), (pm, sm), (pb, sb) in product(*cands.values()):
        # filter 3-digit squares to have different digits from front and back faces
        for lft in [s for s in sq3 if all(s[i] not in 
                   oppsides(str((pt, pm, pb)[i])[0]) for i in range(3))]:
          # filter 3-digit primes to have correct hundreds digit
          rghts = [x for x in pr3 if x[0] == str(7 - int(lft[0]))]
          # filter 3-digit primes to have different digits from front and back faces
          for rght in [r for r in rghts if all(r[i] not in 
                       oppsides(str((st, sm, sb)[i])[0]) for i in range(3))]:
            # all visible top faces (left to right) could be seen to be different
            tp1 = die_third_face(int(lft) % 10, pb // 100)
            tp2 = die_third_face((int(lft) % 100) // 10, pm // 10)
            tp3 = die_third_face(pt % 10, int(rght) // 100)
            tp4 = die_third_face(pm % 10, (int(rght) % 100) // 10)
            tp5 = die_third_face(pb % 10, int(rght) % 10)
            if len({tp1, tp2, tp3, tp4, tp5}) == 5:
              print("answer:", rght)  
      

      Like

  • Unknown's avatar

    Jim Randell 10:58 am on 5 December 2023 Permalink | Reply
    Tags:   

    Teaser 2657: Puzzling book 

    From The Sunday Times, 25th August 2013 [link] [link]

    George and Martha have a book of puzzles numbered from 1 to 30. The solutions are also numbered from 1 to 30, but a solution number is not necessarily the same as the number of the puzzle. George and Martha have solved some of the puzzles. If you look at the number of the puzzle and the number of the solution, then the sum of the two is a perfect power of the difference of the two. George has added up the numbers of the solved puzzles and got a three-figure total. Martha has added up the numbers of the solutions of the solved puzzles and got a higher three-figure total. In fact her total used the same nonzero digits as George’s, but in a different order.

    What (in increasing order) are the numbers of the solved puzzles?

    [teaser2657]

     
    • Jim Randell's avatar

      Jim Randell 10:59 am on 5 December 2023 Permalink | Reply

      This Python program generates possible (puzzle-number, solution-number) pairs, and then searches for a collection of these pairs that satisfies the requirements.

      It runs in 66ms. (Internal runtime is 11ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, is_power_of, unpack, nsplit, printf)
      
      # find puzzles <ps>, solutions <ss> from <pairs>, sum(<ps>) = <tp>, sum(<ss>) = <ts>
      def solve(pairs, ps=[], ss=[], tp=0, ts=0):
        # is this a valid set of pairs?
        if 99 < tp < ts < 1000:
          (dp, ds) = (nsplit(tp), nsplit(ts))
          if not (0 in dp or 0 in ds or sorted(dp) != sorted(ds)):
            # output solution
            printf("G={tp} M={ts} -> ps={ps} ss={ss}")
        if pairs:
          # try adding in the next pair
          (p, s) = pairs[0]
          (tp_, ts_) = (tp + p, ts + s)
          if tp_ < 999 and ts_ < 1000 and p not in ps and s not in ss:
            solve(pairs[1:], ps + [p], ss + [s], tp_, ts_)
          # without the next pair
          solve(pairs[1:], ps, ss, tp, ts)
      
      # consider possible puzzle/solution numbers
      fn = unpack(lambda p, s: is_power_of(p + s, abs(p - s)) is not None)
      pairs = list(filter(fn, subsets(irange(1, 30), size=2, select='M')))
      solve(pairs)
      

      Solution: The solved puzzles are: 1, 3, 6, 7, 9, 10, 12, 15, 21, 28.

      The (puzzle-number, solution-number) pairs are:

      (1, 3) → 1 + 3 = 4 = (3 – 1)^2
      (3, 5) → 3 + 5 = 8 = (5 – 3)^3
      (6, 10) → 6 + 10 = 16 = (10 – 6)^2
      (7, 9) → 7 + 9 = 16 = (9 – 7)^4
      (9, 7) → 9 + 7 = 16 = (9 – 7)^4
      (10, 6) → 10 + 6 = 16 = (10 – 6)^2
      (12, 15) → 12 + 15 = 27 = (15 – 12)^3
      (15, 17) → 15 + 17 = 32 = (17 – 15)^5
      (21, 28) → 21 + 28 = 49 = (28 – 21)^2
      (28, 21) → 28 + 21 = 49 = (28 – 21)^2

      The sum of the puzzle numbers is 112 (George’s total).

      And the sum of the solution numbers is 121 (Martha’s total).

      Like

  • Unknown's avatar

    Jim Randell 9:27 am on 28 November 2023 Permalink | Reply
    Tags:   

    Teaser 2648: Painted cubes 

    From The Sunday Times, 23rd June 2013 [link] [link]

    Oliver found some painted cubes in the loft. These cubes had edges whose lengths were whole numbers of centimetres. After choosing some cubes whose edge lengths were consecutive, Oliver proceeded to cut them into “mini-cubes” of side one centimetre. Of course, some of these mini-cubes were partially painted and some were not painted at all.

    On counting up the mini-cubes, Oliver noticed that exactly half of them were not painted at all.

    How many mini-cubes were not painted at all?

    [teaser2648]

     
    • Jim Randell's avatar

      Jim Randell 9:28 am on 28 November 2023 Permalink | Reply

      I assume the large cubes are painted on all faces.

      When a cube with side n cm is cut into 1 cm cubelets, there will be (n − 2)³ cubelets (or 0 for n ≤ 2) that were hidden in the interior, and so have all faces unpainted. And the remaining cubelets will have at least one face painted.

      n = 1 → 1 painted, 0 unpainted
      n = 2 → 8 painted, 0 unpainted
      n = 3 → 26 painted, 1 unpainted
      n = 4 → 56 painted, 8 unpainted

      This Python program assembles a list of painted/unpainted cubelets for increasing sizes of cubes (up to 50 cm) and looks for a collection of consecutively sized cubes that has the same number of painted and unpainted cubes.

      It runs in 59ms. (Internal runtime is 3.8ms).

      Run: [ @replit ]

      from enigma import (irange, cb, unzip, printf)
      
      # record painted/unpainted cubelets
      cs = dict()
      
      # consider the size of the largest cube
      for n in irange(1, 50):
        # calculate painted/unpainted cubelets
        u = (0 if n < 3 else cb(n - 2))
        cs[n] = (cb(n) - u, u)
      
        # choose the number of large cubes selected k..n
        for k in irange(1, n - 1):
          # calculate total painted/unpainted cubelets
          (tp, tu) = map(sum, unzip(cs[i] for i in irange(k, n)))
          if tp == tu:
            # output solution
            printf("cubes {k}..{n} -> {tp} painted + {tu} unpainted")
      

      Solution: There 3024 unpainted cubelets.

      The cubes are sizes 4 to 12.

      So the total number of cubelets is:

      4³ + … + 12³ = 6048

      And the total number of unpainted cubelets is:

      2³ + … + 10³ = 3024

      So exactly half the cubelets are unpainted.


      With some analysis we can construct a more efficient program:

      Assuming the cubes are sizes k .. n, then the total number of cubelets is:

      T = k³ + … + n³

      And the number of unpainted cubelets is:

      U = (k − 2)³ + … + (n − 2)³

      And:

      T = 2U

      The sum of the first n cube numbers is given by [@wikipedia]:

      ST[n] = T[n]² = (n(n + 1)/2)²

      So we have:

      ST[n] − ST[k − 1] = 2 ST[n − 2] − 2 ST[k − 3]

      or:

      ST[n] − 2 ST[n − 2] = ST[k − 1] − 2 ST[k − 3]

      which is of the form:

      f[n − 2] = f[k − 3]
      where:
      f[x] = ST[x + 2] − 2 ST[x]

      So we can calculate values for f[x] until we find two values that have the same result.

      Say:

      f[x] = f[y]
      where x > y

      Then we have:

      n = x + 2
      k = y + 3

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

      Run: [ @replit ]

      from enigma import (irange, sq, tri, printf)
      
      # square triangular numbers
      ST = lambda x: sq(tri(x))
      
      # calculate the value of f for increasing x
      d = dict()
      for x in irange(1, 48):
        # calculate f(x)
        v = ST(x + 2) - 2 * ST(x)
        printf("[{x} -> {v}]")
        # have we seen this value before
        y = d.get(v)
        if y:
          # corresponding n, k values
          (n, k) = (x + 2, y + 3)
          # output solution
          printf("cubes {k} .. {n} -> {u} unpainted", u=ST(x) - ST(y))
          break
        # record the value
        d[v] = x
      

      Like

  • Unknown's avatar

    Jim Randell 9:36 am on 23 November 2023 Permalink | Reply
    Tags:   

    Teaser 2654: Square cut 

    From The Sunday Times, 4th August 2013 [link] [link]

    Given a rectangular piece of paper that is not actually square it is possible with one straight cut to divide it into a square and a rectangle (which might or might not itself be square). I call this process a “square cut”. Recently I started with a rectangle of paper one metre long with the width being more than half a metre. I performed a square cut and put the square to one side. On the remaining rectangle I performed a square cut and put the square to one side. I kept repeating this process until the remaining rectangle was itself a square. The result was that I had cut the original piece of paper into six squares all of whose sides were whole numbers of centimetres.

    How many centimetres wide was the original piece of paper?

    [teaser2654]

     
    • Jim Randell's avatar

      Jim Randell 9:37 am on 23 November 2023 Permalink | Reply

      This Python program considers possible widths between 51 and 99 cm, and looks for those which cut the rectangle into 6 squares.

      It runs in 69ms. (Internal runtime is 452µs).

      Run: [ @replit ]

      from enigma import (irange, ordered, printf)
      
      # perform cuts on an a x b rectangle, where a <= b
      def cut(a, b):
        assert not (a > b)
        ss = list()
        while True:
          ss.append(a)
          if a == b: break
          (a, b) = ordered(a, b - a)
        return ss
      
      # consider the width of the rectangle
      for n in irange(51, 99):
        # cut into squares
        ss = cut(n, 100)
        if len(ss) == 6:
          # output solution
          printf("n={n}: {ss}")
      

      Solution: The original piece of paper was 70 cm wide.

      The sizes of the 6 squares made are: 70, 30, 30, 10, 10, 10.

      Like

  • Unknown's avatar

    Jim Randell 4:23 pm on 20 November 2023 Permalink | Reply
    Tags:   

    Teaser 2670: Answers on a postcard 

    From The Sunday Times, 24th November 2013 [link] [link]

    On a postcard I have written four two-digit numbers, none of which is divisible by three. In three of the numbers the two digits used are consecutive (in some order) and in fact overall the four numbers use eight consecutive digits. I have calculated the sum and product of the four numbers. Then I have consistently replaced each of the digits 0 to 9 by a different letter of the alphabet. It turns out that the sum of my four numbers is SUM and their product is PRODUCT.

    What number is represented by POSTCARD?

    [teaser2670]

     
    • Jim Randell's avatar

      Jim Randell 4:24 pm on 20 November 2023 Permalink | Reply

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

      It runs in 73ms. (Internal runtime of the generated code is 2.3ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --distinct="abcdefgh,ACDMOPRSTU"
      
      # check integers <ns> are consecutive (when considered in order)
      --code="check = lambda ns: ns[-1] + 1 == ns[0] + len(ns)"
      --code="is_consecutive = lambda *ns: check(sorted(ns))"
      
      # sum is SUM, product is PRODUCT
      "{ab} + {cd} + {ef} + {gh} = {SUM}"
      "{ab} * {cd} * {ef} * {gh} = {PRODUCT}"
      
      # suppose {ab}, {cd}, {ef} (in order) are the 3 with consecutive digits
      "is_consecutive({a}, {b})"
      "is_consecutive({c}, {d})"
      "is_consecutive({e}, {f})"
      "{a} < {c} < {e}"
      # [optional] the other one does not consist of consecutive digits
      "not is_consecutive({g}, {h})"
      
      # 8 consecutive digits are used overall
      "is_consecutive({a}, {b}, {c}, {d}, {e}, {f}, {g}, {h})"
      
      --answer="POSTCARD"
      --template="({ab} {cd} {ef} {gh}) ({SUM}) ({PRODUCT}) ({POSTCARD})"
      --solution=""
      

      Solution: POSTCARD = 94267830.

      The numbers are:

      (23, 56, 74, 98)
      SUM = 251
      PRODUCT = 9340576

      Like

    • GeoffR's avatar

      GeoffR 9:43 am on 21 November 2023 Permalink | Reply

      from enigma import all_different,nsplit,nconcat
      
      def consec_dig(L):
          return sorted(L) == list(range(min(L), max(L)+1))
      
      # Find three 2-digit numbers (ab, cd, ef) with consecutive digits
      for ab in range(12, 99):
        if ab % 3 == 0:continue
        for cd in range(ab+1, 99):
          if cd % 3 == 0:continue
          for ef in range(cd+1, 99):
            if ef % 3 == 0:continue
            a, b, c, d = ab // 10, ab % 10, cd // 10, cd % 10
            e, f = ef // 10, ef % 10
            if not a < c < e:continue
            L_6d = [a, b, c, d, e, f]
            if consec_dig(L_6d):
                
              # last 2-digit number does not have consecutive digits
              for gh in range(ef+1, 99):
                if gh % 3 == 0:continue
                g, h = gh // 10, gh % 10
                if g < h:continue
                
                # check overall the four numbers use eight consecutive digits 
                L_8d = [a, b, c, d, e, f, g, h]
                if consec_dig(L_8d):
                    
                  # Find SUM and PRODUCT
                  SUM = ab + cd + ef + gh
                  S, U, M = SUM //100, SUM // 10 % 10, SUM % 10
                  if not all_different(S, U, M): continue
                  
                  PRODUCT = ab * cd * ef * gh
                  # Check PRODUCT has 7 digits
                  if len(str(PRODUCT)) != 7:continue
                  P, R, O, D, U, C, T = nsplit(PRODUCT)
                  if not all_different (P,R,O,D,U,C,T ):continue
                  
                  # Check 'U' is same value in SUM and PRODUCT
                  if SUM // 10 % 10 == PRODUCT // 100 % 10:
                    # Find missing letter A for POSTCARD value
                    digits = set(range(10))
                    A = digits.difference({S,U,M,P,R,O,D,C,T}).pop()
                    POSTCARD = nconcat(P,O,S,T,C,A,R,D)
                    print(f"SUM = {SUM},PRODUCT = {PRODUCT}")
                    print(f"POSTCARD = {POSTCARD}")
                    print(f"Four Numbers are {ab},{cd},{ef} and {gh}.")
      
      # SUM = 251,PRODUCT = 9340576
      # POSTCARD = 94267830
      # Four Numbers are 23,56,74,98.
         
      
      

      Like

  • Unknown's avatar

    Jim Randell 7:55 am on 14 November 2023 Permalink | Reply
    Tags:   

    Teaser 2652: Square meals 

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

    A company produces five different types of muesli. The ingredients are bought from a wholesaler who numbers his items from 1 to 10. In each type of muesli there is a mixture of a square number of different ingredients and the weight in grams of each ingredient is the square of its item number: also the total weight of its ingredients is a perfect square number of grams.

    Last month one of the ingredients was unavailable and so only the “basic” and “fruity” varieties could be produced. This week a different ingredient is unavailable and so only the “luxury” variety can be made.

    What are the item numbers of the ingredients in the luxury muesli?

    [teaser2652]

     
    • Jim Randell's avatar

      Jim Randell 7:55 am on 14 November 2023 Permalink | Reply

      I assumed each mixture has multiple ingredients, and as there are only 10 possible ingredients this means each must have 4 or 9.

      This Python program looks for sets of 4 or 9 numbers from 1 to 10 whose squares sum to a square number. We then choose 5 of these sets to form the different types of muesli made, and look for the 2 (different) unavailable ingredients. The first unavailable ingredient only allows 2 of the types to be made (these are “basic” and “fruity”) and the second only allows 1 to be made (and this is “luxury”).

      It runs in 84ms. (Internal runtime is 879µs).

      Run: [ @replit ]

      from enigma import (irange, subsets, sq, is_square, union, intersect, printf)
      
      # collect possible collections of ingredients
      ms = list()
      
      # choose 4 or 9 ingredients
      for k in [4, 9]:
        for ns in subsets(irange(1, 10), size=k):
          # calculate the total weight
          t = sum(sq(n) for n in ns)
          if not is_square(t): continue
          printf("[k={k}: ns={ns} t={t}]")
          ms.append(ns)
      
      # choose 5 varieties
      for vs in subsets(ms, size=5):
        ns = union(vs)
        # choose the first unavailable ingredient
        for u1 in ns:
          # find how many can be made without u1
          vs1 = list(v for v in vs if u1 not in v)
          # there are 2 that cannot be made ("basic" and "fruity")
          if len(vs1) != 2: continue
      
          # choose the second unavailable ingredient
          for u2 in ns:
            if u2 == u1: continue
            # find how many can be made without u2
            vs2 = list(v for v in vs if u2 not in v)
            # there is only 1 that cannot be made ("luxury")
            if len(vs2) != 1: continue
            if intersect([vs1, vs2]): continue
      
            # output solution
            printf("u1={u1} -> basic/fruity={vs1}; u2={u2} -> luxury={vs2[0]}")
      

      Solution: Luxury muesli uses ingredients: 2, 4, 5, 6.

      There are only 5 possible sets of ingredients, so these correspond to each of the 5 types of museli:

      (2, 4, 5, 6) → 81
      (1, 2, 4, 10) → 121
      (1, 2, 8, 10) → 169
      (2, 4, 7, 10) → 169
      (5, 6, 8, 10) → 225

      The unavailable ingredient last month was 4, meaning only: (1, 2, 8, 10) and (5, 6, 8, 10) could be made. These are (in some order) “basic” and “fruity”.

      The unavailable ingredient this week is 10, meaning only (2, 4, 5, 6) can be made, and this is “luxury”.

      Like

  • Unknown's avatar

    Jim Randell 7:39 am on 31 October 2023 Permalink | Reply
    Tags:   

    Teaser 2647: Happy medium 

    From The Sunday Times, 16th June 2013 [link] [link]

    At a recent séance Adam, Alan, Andy, Eden, Eric, Fred, Gary, Glen, John, Mike, Pete and Tony sat around a circular table of radius one metre. Around its edge there were 12 equally-spaced points, each representing a different letter of the alphabet.

    A light started at the centre, moved straight to one of the points, moved straight to another, then straight to another, and so on, before returning directly to the centre. In this way it spelt out the name of one of the people present. It then started again and in a similar fashion spelt out a day of the week. Then it started again and spelt out a month. Every straight line path that it took was a whole number of centimetres long.

    Which three words did it spell out?

    [teaser2647]

     
    • Jim Randell's avatar

      Jim Randell 7:41 am on 31 October 2023 Permalink | Reply

      See also: Enigma 595a, Enigma 146, Enigma 1369.

      The 12 points fall into two regular hexagonal orbits, and once the initial letter is selected we must stay in that particular orbit, making transitions of ±1 or 3 to move between letters. Therefore any word spelled out must not have more than 6 different letters (which eliminates 4 of the days of the week, and 3 months of the year).

      Also, if we label the letters in any particular orbit alternately as “odd” and “even”, then we can only transition from an “odd” position to an “even” position and vice versa. (So we can eliminate 3 more months which have a letter with both parities).

      So each arrangement consists of two distinct orbits, and each orbit consists of two parities, each of which consists of 3 letters. And there are 12 different letters used in an arrangement.

      This Python program runs in 61ms. (Internal runtime is 703µs).

      Run: [ @replit ]

      from enigma import (update, union, disjoint_union, join, unpack, printf)
      
      # an orbit is an (<even>, <odd>) pair:
      empty = (set(), set())  # the empty orbit
      
      # possible index orders for a pair
      def orders(vs):
        yield (0, 1)
        if vs[0] != vs[1]: yield (1, 0)
      
      # add a word into an orbit
      def add(word, orbit):
        # collect the letters by even/odd parity
        ps = (set(word[0::2]), set(word[1::2]))
        # consider possible orderings to add the letters
        for (i0, i1) in orders(orbit):
          # construct new orbit
          orb = (union([orbit[0], ps[i0]]), union([orbit[1], ps[i1]]))
          # check orbit is valid
          if len(orb[0]) < 4 and len(orb[1]) < 4 and disjoint_union(orb):
            # return updated orbit
            yield orb
      
      # add a word into a pair of orbits
      def add_word(word, pair):
        # choose an orbit to add the word to
        for (i0, i1) in orders(pair):
          # attempt to add the word to the orbit
          for orb in add(word, pair[i0]):
            # check orbits are disjoint
            if disjoint_union(orb + pair[i1]):
              yield update(pair, [(i0, orb)])
      
      # find arrangements that spell out a word from each of the word lists <wss>
      def solve(wss, ws=[], orbs=(empty, empty)):
        # are we done?
        if not wss:
          yield (ws, orbs)
        else:
          # choose the next word
          for w in wss[0]:
            for orbs_ in add_word(w, orbs):
              yield from solve(wss[1:], ws + [w], orbs_)
      
      # word sets (with impossible words removed)
      words1 = "ADAM ALAN ANDY EDEN ERIC FRED GARY GLEN JOHN MIKE PETE TONY".split()
      words2 = "MONDAY FRIDAY SUNDAY".split()
      words3 = "MARCH APRIL MAY JUNE JULY AUGUST".split()
      
      # format an orbit (x, y)
      fmt = unpack(lambda x, y: join(sorted(x)) + "+" + join(sorted(y)))
      
      # solve the puzzle
      for (ws, orbs) in solve([words1, words2, words3]):
        # output solution
        printf("{ws} <- {orbs}", ws=join(ws, sep=" "), orbs=join(map(fmt, orbs), sep=", "))
      

      Solution: The words are: GLEN, FRIDAY, JUNE.

      The two orbits are:

      EGU + JLN → GLEN, JUNE
      AFI + DRY → FRIDAY

      From these we can construct many viable arrangements of letters. For example: E A J D G F L R U I N Y.

      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