Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • 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 2:48 pm on 12 November 2023 Permalink | Reply
    Tags:   

    Brainteaser 1500: Lying about his age 

    From The Sunday Times, 9th June 1991 [link]

    It’s Uncle Joe’s birthday today, but he’s reluctant to tell me how old he is. Here are some clues, although some of them aren’t true:

    1. Every number of a true statement is a factor of Joe’s age.

    2. Every number of a false statement is a factor of Joe’s age.

    3. This is neither the first true nor the last false statement.

    4. Of the two statements immediately before and after this one, just one is true and the other is false.

    5. Of the two statements immediately before and after this one, just one is true and the other is false.

    6. Each digit of Joe’s age is different and is the number of a statement which is false.

    7. This is either the last true or the last false statement.

    8. Every prime factor of Joe’s age is the number of a true statement.

    How old is Joe?

    [teaser1500]

     
    • Jim Randell's avatar

      Jim Randell 2:49 pm on 12 November 2023 Permalink | Reply

      The following Python program considers possible ages, and truth values for the statements.

      The [[ Delay() ]] class from the enigma.py library is used to calculate the digits and prime factors of the age only as necessary. (Although in the event this doesn’t save a great deal of processing).

      The program runs in 147ms. (Internal runtime is 73ms).

      Run: [ @replit ]

      from enigma import (Delay, irange, subsets, nsplit, prime_factor, map2str, printf)
      
      # the prime factors of <n>
      factors = lambda n: list(f for (f, _) in prime_factor(n))
      
      # choose an age
      for n in irange(1, 121):
        # delay the evaluation of digits and factors
        _ds = Delay(nsplit, n)
        _fs = Delay(factors, n)
      
        # choose truth values for the statements
        for vs in subsets((False, True), size=8, select="M"):
          if all(vs): continue
          ss = dict(enumerate(vs, start=1))
      
          # check the values:
          # 1. "every number of a true statement is a factor of the age"
          if ss[1] != all(n % k == 0 for (k, v) in ss.items() if v): continue
      
          # 2. "every number of a false statement is a factor of the age"
          if ss[2] != all(n % k == 0 for (k, v) in ss.items() if not v): continue
      
          # 3. "this is neither the first true nor the last false statement"
          if ss[3] != (not ((not any(vs[:2])) if ss[3] else all(vs[3:]))): continue
      
          # 4. "one of (3), (5) is true and one is false"
          if ss[4] != (ss[3] != ss[5]): continue
      
          # 5. "one of (4), (6) is true and one is false"
          if ss[5] != (ss[4] != ss[6]): continue
      
          # 6. "each digit of the age is different, and the number of a false statement"
          ds = _ds.value
          if ss[6] != (len(ds) == len(set(ds)) and all(not ss.get(d, 1) for d in ds)): continue
      
          # 7. "this is either the last true or the last false statement"
          if ss[7] != ((not ss[8]) if ss[7] else ss[8]): continue
      
          # 8. "every prime factor of the age is the number of a true statement"
          fs = _fs.value
          if ss[8] != all(ss.get(f, 0) for f in fs): continue
      
          # output solution
          printf("n={n} {ss}", ss=map2str(ss, enc="[]"))
      

      Solution: Joe is 72.

      And the values of the statements are:

      1. true [the true statements are 1, 3, 4, 6, and these are all divisors of 72]
      2. false [5 and 7 are false statements, but there are not divisors of 72]
      3. true [this is not the first true statement (1), nor the last false statement (8)]
      4. true [3 is true, 5 is false]
      5. false [both 4 and 6 are true]
      6. true [7 and 2 are different, and both number false statements]
      7. false [this is not the last true statement (6), nor the last false statement (8)]
      8. false [2 is a prime factor of 72, and statement 2 is false]

      Like

  • Unknown's avatar

    Jim Randell 4:37 pm on 10 November 2023 Permalink | Reply
    Tags:   

    Teaser 3190: Mods-In-Suits 

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

    In single-pack card game Mods-In-Suits, players are dealt two cards. Each card’s face value (Ace=1 to K=13) is modified by its suit, thus:

    “Spade” squares it;
    “Heart” halves it;
    “Club” changes its sign;
    “Diamond” divides it by the other card’s face value.

    Players score their modified values’ sum (or zero if there is a matching pair of face values). Players may exchange their second dealt card for a fresh card from the pack.

    Stuck in the jam in the Smoke at rush hour, John’s children were missing Andy’s party. Playing Mods-In-Suits for amusement led to one unusual game. Jo’s initial score was a positive whole number, Bo’s its negative. Four different suits and face values were dealt. Both exchanged their second card, but each score was unchanged. Four different suits were still showing.

    Give Jo’s final hand.

    [teaser3190]

     
    • Jim Randell's avatar

      Jim Randell 5:21 pm on 10 November 2023 Permalink | Reply

      This Python program runs in 80ms. (Internal runtime is 12ms).

      Run: [ @replit ]

      from enigma import (
        irange, cproduct, sq, rdiv, group, subsets, unpack,
        as_int, intersect, singleton, diff, join, printf
      )
      
      # a pack of cards (<suit>, <value>)
      pack = list(cproduct(['SHCD', irange(1, 13)]))
      
      # calculate modified value for card <X> paired with <Y>
      def value(X, Y):
        ((sx, vx), (sy, vy)) = (X, Y)
        if sx == 'S': return sq(vx)
        if sx == 'H': return rdiv(vx, 2)
        if sx == 'C': return -vx
        if sx == 'D': return rdiv(vx, vy)
      
      # score a pair of cards (we are only interested in non-zero integer
      # values, from pairs of cards with different suits)
      def score(X, Y):
        ((sx, vx), (sy, vy)) = (X, Y)
        if sx == sy or vx == vy: return None
        return as_int(value(X, Y) + value(Y, X), include='+-', default=None)
      
      # deal 3 cards from the pairs <ps>
      # return cards <x1> and <x2> which is replaced by <x3>
      def deal(ps):
        for (p1, p2) in subsets(ps, size=2, select='P'):
          x1 = singleton(intersect([p1, p2]))
          if x1 is None: continue
          x2 = singleton(diff(p1, p2))
          x3 = singleton(diff(p2, p1))
          yield (x1, x2, x3)
      
      # group pairs by integer value
      g = group(subsets(pack, size=2), by=unpack(score))
      del g[None]  # remove scores we are not interested in
      
      # consider possible scores for J
      for (k, psJ) in g.items():
        if k < 0 or len(psJ) < 2: continue
        # B's score is -k
        psB = g.get(-k)
        if psB is None or len(psB) < 2: continue
      
        # consider cards for J (J1, J2 -> J3)
        for (J1, J2, J3) in deal(psJ):
          # consider cards for B (B1, B2 -> B3)
          for (B1, B2, B3) in deal(psB):
            # check deals are possible
            if len({J1, J2, J3, B1, B2, B3}) < 6: continue
            # check 4 different suits and values initially
            if any(len({J1[i], J2[i], B1[i], B2[i]}) < 4 for i in [0, 1]): continue
            # check 4 different suits finally
            if len({J1[0], J3[0], B1[0], B3[0]}) < 4: continue
            # output solution
            (J1, J2, J3, B1, B2, B3) = map(join, (J1, J2, J3, B1, B2, B3))
            printf("J: {J1}, {J2} -> {J3} [+{k}]; B: {B1}, {B2} -> {B3} [-{k}]")
      

      Solution: Jo’s final hand is: Ace of Spades, 6 of Hearts.

      The initial cards dealt were:

      Jo: Ace of Spades, 3 of Diamonds; score = (+1, +3) = +4
      Bo: 6 of Clubs, 4 of Hearts; score = (−6, +2) = −4

      The suits are: S, D, C, H and the values: 1, 3, 4, 6.

      Jo then chooses to discard 3 of Diamonds, and is dealt 6 of Hearts:

      Jo: Ace of Spades, 6 of Hearts; score = (+1, +3) = +4

      Bo then chooses to discard 4 of Hearts, and is dealt Queen (12) of Diamonds:

      Bo: 6 of Clubs, 12 of Diamonds; score = (−6, +2) = −4

      The suits are: S, H, C, D and the values: 1, 6, 6, 12.


      If Jo’s discarded card is returned to the pack, and then it can be Bo’s exchange card (rather than a “fresh” card from the pack) and this leads to a further solution, where the final values are also all different (I initially was solving the puzzle with this extra condition). So presumably that does not happen.

      The initial cards dealt were:

      Jo: Ace of Spades, 4 of Hearts; score = (+1, +2) = 3
      Bo: 5 of Clubs, 10 of Diamonds; score = (−5, +2) = −3

      The suits are: S, H, C, D and the values 1, 4, 5, 10.

      Jo then chooses to discard the 4 of Hearts (which is returned to the pack), and is dealt 2 of Diamonds:

      Jo: Ace of Spades, 2 of Diamonds; score = (+1, +2) = 3

      Bo then chooses to discard the 10 of Diamonds, and is dealt 4 of Hearts (the card discarded by Jo):

      Bo: 5 of Clubs, 4 of Hearts; score = (−5, +2) = −3

      The suits are: S, D, C, H and the values: 1, 2, 4, 5.

      Like

  • Unknown's avatar

    Jim Randell 8:13 am on 5 November 2023 Permalink | Reply
    Tags:   

    Brain-Teaser 935: Ears, noses & throats 

    From The Sunday Times, 22nd June 1980 [link]

    Our local hospital is a busy place, with a large department, luckily, to keep records of what is wrong with whom. Those who come to the ENT specialist to complain about their ears, sometimes complain about their nose or their throat.

    Of his 60 patients, the number complaining of ears and nose only is three times as great as the number complaining of ears and throat only.

    Another group consists of those who say that their ears are the only body part of the three where they are in good health; and this group is three times as large as the group which declares that the throat alone is healthy.

    It’s all very confusing, I know; but I can tell you that there are 110 complaints in all — if you count a complaint about two ears as one complaint.

    Everyone complains about at least one thing.

    How many patients come with complaints about one part of their anatomy (ears or nose or throat) only?

    This puzzle is included in the book The Sunday Times Book of Brainteasers (1994).

    [teaser935]

     
    • Jim Randell's avatar

      Jim Randell 8:14 am on 5 November 2023 Permalink | Reply

      We can label the areas of the Venn diagram we are interested in as: E, N, T, EN, ET, NT, ENT (this accounts for all patients, as each has at least one complaint).

      We have:

      E + N + T + EN + ET + NT + ENT = 60
      EN = 3 ET
      NT = 3 EN
      E + N + T + 2(EN + ET + NT) + 3 ENT = 110

      And we want to determine the value of E + N + T.

      So:

      EN = 3 ET
      NT = 3 EN = 9 ET

      Hence:

      EN + ET + NT = 3 ET + ET + 9 ET = 13 ET

      Writing: X = E + N + T, we get:

      X + 13 ET + ENT = 60
      X + 26 ET + 3 ENT = 110

      X = ENT + 10

      And so:

      2 ENT = 50 − 13 ET

      There are 2 possibilities:

      ET = 0 ⇒ ENT = 25, E+N+T = 35
      ET = 2 ⇒ ENT = 12, E+N+T = 22

      If we suppose the phrase: “three times as great/large” in the puzzle text precludes zero values, then only a single solution remains.

      Solution: 22 of the patients have a single complaint.

      Like

  • Unknown's avatar

    Jim Randell 4:33 pm on 3 November 2023 Permalink | Reply
    Tags:   

    Teaser 3189: Telling tiles 

    From The Sunday Times, 5th November 2023 [link] [link]

    I have four tiles with a digit written on each of them: I shall refer to these as A, B, C and D. I have rearranged the tiles in various ways to make two 2-digit numbers and I have then multiplied those two numbers together (e.g., CB times AD). In this way I have found as many answers as possible with this particular set of tiles and I have discovered that:

    1. The number of different answers is AB.
    2. Of those answers B consist of the four digits A, B, C, D in some order.
    3. There are C other 4-digit answers.

    What are A, B, C and D respectively?

    [teaser3189]

     
    • Jim Randell's avatar

      Jim Randell 5:02 pm on 3 November 2023 Permalink | Reply

      It wasn’t clear to me if 0 digits were allowed or not, or if repeated digits were allowed, so I wrote code to solve the most permissive interpretation. But it turns out there is the same unique answer whatever variation is used.

      This Python program runs in 131ms. (Internal runtime is 52ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, multiset, nconcat, nsplit, singleton, printf)
      
      # choose the 4 digits
      for ds in subsets(irange(0, 9), size=4, select='R'):
        m = multiset.from_seq(ds)
      
        # find the products of 2 digit pairs (ignoring duplicates)
        ps = set(
          nconcat(a, b) * nconcat(c, d)
            for (a, b, c, d) in subsets(ds, size=4, select='mP')
        )
      
        # AB = number of different products
        n = len(ps)
        (A, B) = nsplit(n, 2)
        if not m.issuperset((A, B)): continue
      
        # count total 4-digit products = t
        # those that are rearrangements of <ds> = r
        t = r = 0
        for p in ps:
          ds_ = nsplit(p)
          if len(ds_) != 4: continue
          t += 1
          if multiset.from_seq(ds_) == m: r += 1
      
        # B = number of rearrangements
        if r != B: continue
      
        # C = number of remaining 4-digit products
        C = t - r
        # and D is the remaining digit
        D = singleton(m.difference((A, B, C)))
        if D is None: continue
      
        # output solution
        printf("(A, B, C, D) = {ds} [n={n} t={t} r={r}]", ds=(A, B, C, D))
      

      Solution: (A, B, C, D) = (1, 2, 7, 8).

      There are 12 different products:

      17 × 28 = 476
      18 × 27 = 486
      12 × 78 = 936
      12 × 87 = 1044
      18 × 72 = 1296
      17 × 82 = 1394
      21 × 78 = 1638
      21 × 87 = 1827 [*]
      28 × 71 = 1988
      27 × 81 = 2187 [*]
      71 × 82 = 5822
      72 × 81 = 5832

      2 of them [*] are rearrangements of the digits A, B, C, D, and there are 7 other 4-digit products.


      There are 55 sets of digits that satisfy condition 1, and the numbers of distinct products are: 1, 2, 4, 6, 10, 12.

      12 is the largest number of distinct products we can have, because there are 24 rearrangements of the 4 digits, but in this list each product will appear twice (as pq × rs and rs × pq).

      When we include condition 2 only 3 sets of digits survive:

      (1, 0, 4, 6) → 10 products {46, 64, 84, 244, 246, 460, 640, 840, 2440, 2460}, 0 rearrangements [2 others]
      (1, 0, 5, 9) → 10 products {59, 95, 135, 455, 459, 590, 950, 1350, 4550, 4590}, 0 rearrangements [3 others]
      (1, 2, 7, 8) → 12 products {476, 486, 936, 1044, 1296, 1394, 1638, 1827, 1988, 2187, 5822, 5832}, 2 rearrangements [7 others]

      And of these only the last set satisfies condition 3, and provides the required answer.

      Like

  • Unknown's avatar

    Jim Randell 11:20 am on 2 November 2023 Permalink | Reply
    Tags:   

    Brainteaser 1555: Riddle-me-ree 

    From The Sunday Times, 28th June 1992 [link]

    My first is a digit
    That is equal to “y”.
    My second’s not zero,
    But smaller than pi.

    My third, fourth and fifth
    You’ll find if you try,
    For my whole is the cube
    Of “x” squared plus “y”.

    “x” is an integer
    And so is “y”.
    I’ve only five digits,
    What number am I?

    [teaser1555]

     
    • Jim Randell's avatar

      Jim Randell 11:21 am on 2 November 2023 Permalink | Reply

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

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

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      # we are looking for: YABCD = (X^2 + Y)^3
      
      SubstitutedExpression
      
      --distinct=""
      --invalid="0,AY"
      --invalid="4|5|6|7|8|9,A"  # 2nd digit is 1, 2, 3
      
      # the 5-digit number is the cube of X^2 + Y
      "is_square(is_cube(YABCD) - Y)"
      
      --answer="YABCD"
      

      Or we can use a simple Python program:

      from enigma import (irange, nsplit, is_square, printf)
      
      # consider 5-digit cubes
      for i in irange(22, 46):
        n = i**3
        (y, a, _, _, _) = nsplit(n)
        if a == 0 or a > 3: continue
        x = is_square(i - y)
        if x is None: continue
        # output solution
        printf("{n} = ({x}^2 + {y})^3")
      

      Solution: The number is 91125.

      We have x = 6, y = 9, and:

      (6² + 9)³ = 91125

      Like

    • Ender Aktulga's avatar

      Ender Aktulga 11:12 am on 7 November 2023 Permalink | Reply

      Answer: 91125

      from math import sqrt
      
      i=0
      n=0
      while True:
          i+=1
          n=i**3
              
          if n=100000:
              break
          
          n2=int(str(n)[1])  #:[1]
          if n2==0 or n2>3:
              continue
      
          y=n//10000
          x=round(sqrt(i-y))
          if n!=(x**2+y)**3:
              continue
      
          print(f"{i=}, i^3={n=}, {x=}, i={x**2+y=}")
      
      """
      i=45, i^3=n=91125, x=6, i=x**2+y=45
      """
      

      Like

      • Jim Randell's avatar

        Jim Randell 8:21 am on 9 November 2023 Permalink | Reply

        Thanks for the comment, but if this is meant to be a Python program there seem to be some problems with it, because it doesn’t run.

        If you want to post a new comment with a revised version I can remove this version.

        When posting code it is best to wrap it in [code]...[/code] tags to make sure it appears correctly.

        Like

    • GeoffR's avatar

      GeoffR 9:53 am on 11 November 2023 Permalink | Reply

      cb5d = [x * x * x for x in range(22, 47)]
      
      for x in range(1, 10):
          for y in range(1, 10):
              n = (x * x + y) ** 3
              if n not in cb5d:continue
              # y is my first digit
              if str(n)[0] != str(y):continue
              # My second digit is not zero, but smaller than pi
              if str(n)[1] not in ('1', '2', '3'):continue
              print(f"My 5-digit number is {n}.")
      

      My 5-digit number is 91125.

      Like

    • GeoffR's avatar

      GeoffR 10:42 am on 12 November 2023 Permalink | Reply

      I updated the original 5-digit cube list to include a check on the 2nd digit of the 5-digit number. This reduced the original cube list from 25 potential candidates to 6 cubes to check.

      
      # list of 5-digit cubes with 2nd digit in range 1..3
      cb_5d = [x * x * x for x in range(22, 47)
              if str(x * x * x)[1] in ('1','2','3')]
      
      # find x and y values
      for x in range(1, 10):
          for y in range(1, 10):
              n = (x * x + y) ** 3
              # 5-digit number required
              if n < 10000:
                  continue
              if n in cb_5d:
                  # y is my first digit
                  if str(n)[0] == str(y):
                      print(f"My 5-digit number is {n}.")
      
      print(f"Candidate cube list was {cb_5d}")
      
      # My 5-digit number is 91125.
      # Candidate cube list was [12167, 13824, 21952, 32768, 42875, 91125]
      
      
      
      

      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

  • Unknown's avatar

    Jim Randell 3:06 pm on 27 October 2023 Permalink | Reply
    Tags:   

    Teaser 3188: Royal Mail slims down 

    From The Sunday Times, 29th October 2023 [link] [link]

    The Royal Mail, facing stiff competition, was looking for ways to streamline and simplify its operations. One dotty idea circulating in 2022 was to sell only two face values of postage stamps. Customers would then need to be able to make up 68p for a second-class letter and all values above 68p, to be ready for subsequent price rises. An obvious solution would be to sell only 1p and 68p stamps. But this would mean sticking 28 stamps on a first-class letter (costing 95p), leaving little room for the address!

    Which two stamp values would minimise the total number of stamps required to post two letters, one at 68p and one at 95p, and still allow any value above 68p to be made up?

    [teaser3188]

     
    • Jim Randell's avatar

      Jim Randell 3:23 pm on 27 October 2023 Permalink | Reply

      We have encountered similar puzzles before. (See: Enigma 1154, Enigma 1194, Enigma 228, Enigma 221, Enigma 122, Enigma 1635).

      For a pair of coprime denominations, the largest amount that cannot be expressed is known as the Frobenius number [@wikipedia], and can be calculated as:

      F(x, y) = x.y − (x + y)

      This Python program runs in 74ms. (Internal runtime is 10ms).

      Run: [ @replit ]

      from enigma import (Accumulator, coprime_pairs, express, printf)
      
      # calculate Frobenius number for coprime <x>, <y>
      F = lambda x, y: x * y - x - y
      
      # find minimal stamps to make total <t> with denominations <ds>
      stamps = lambda t, ds: min(express(t, ds), key=sum)
      
      # find minimal total
      r = Accumulator(fn=min, collect=1)
      
      # consider possible denominations
      for ds in coprime_pairs(68):
        # can we make 68p and all larger amounts?
        if F(*ds) > 67: continue
      
        # find minimal number of stamps used
        (q1, q2) = (stamps(68, ds), stamps(95, ds))
        r.accumulate_data(sum(q1) + sum(q2), ds)
      
      # output solution
      printf("denominations = {r.data} [{r.value} stamps]")
      

      Solution: The minimal number of stamps required is with denominations of 2p and 31p.

      With these denominations we can make the required amounts as:

      68p = 2× 31p + 3× 2p = 5 stamps
      95p = 3× 31p + 1× 2p = 4 stamps
      total = 9 stamps

      These denominations can make all values from 30p upwards.

      Like

  • Unknown's avatar

    Jim Randell 5:18 pm on 26 October 2023 Permalink | Reply
    Tags:   

    Teaser 2128: Goodbye, Norma Jean(e) 

    From The Sunday Times, 29th June 2003 [link]

     

    She was originally Norma Jean Baker, then she called herself Norma Jeane, before becoming the much-loved Marilyn Monroe. It is more than 40 years since she died and yet people still remember her fondly. Today we celebrate her life once again in the above addition sum, where each letter stands consistently for a different non-zero digit and O is prime.

    What is the numerical value of JAMBOREE?

    [teaser2128]

     
    • Jim Randell's avatar

      Jim Randell 5:18 pm on 26 October 2023 Permalink | Reply

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

      The following run file executes in 75ms. (Internal runtime of the generated code is 144µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression.split_sum
      
      # digits are non-zero
      --invalid="0,ABEJKMNOR"
      # O is a single-digit prime = {2, 3, 5, 7}
      --invalid="1|4|6|8|9,O"
      
      "NORMA + JEAN = BAKER"
      
      --answer="JAMBOREE"
      

      Solution: JAMBOREE = 94735611.

      And the unencoded sum is:

      25674 + 9142 = 34816

      Like

  • Unknown's avatar

    Jim Randell 9:04 am on 24 October 2023 Permalink | Reply
    Tags:   

    Teaser 2664: Time ‘n’ again 

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

    Time and again at school we would be given the exercise of changing a fraction into a decimal.

    This time the given fraction is in its simplest form and it equals a recurring decimal. In some places the digits have been consistently replaced by letters, with different letters used for different digits, but in four places the digits have merely been replaced by asterisks:

    TIME / **N** = .AGAINAGAIN

    Numerically, what is the TIME?

    [teaser2664]

     
    • Jim Randell's avatar

      Jim Randell 9:06 am on 24 October 2023 Permalink | Reply

      The fraction:

      TIME / **N**

      is in its lowest form, but may also be written:

      AGAIN / 99999

      So it follows that the numerator and denominator of the first fraction can be scaled up by an integer value (k) to give the numerator and denominator of second fraction.

      And k must be a 1-digit divisor of 99999.

      The only possible values are k=3 or k=9, meaning

      **N** = 33333
      **N** = 11111

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

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --invalid="0,NT"
      --invalid="2|4|5|6|7|8|9,N"  # N is 1 or 3
      
      # TIME / NNNNN is in lowest terms
      "gcd(TIME, NNNNN) = 1"
      
      # TIME scales up to give AGAIN
      "div(N * AGAIN, 9) = TIME"
      
      --answer="TIME"
      

      Solution: TIME = 5269.

      And we have:

      TIME / **N** = .(AGAIN)… → 5269 / 11111 = 0.(47421)…

      Like

      • Hugo's avatar

        Hugo 1:36 pm on 24 October 2023 Permalink | Reply

        99999 = 3² × 41 × 271. 11111 = 41 × 271.
        41 is the smallest value of n such that multiples and submultiples of 1/n recur with period five.

        5269 = 11 × 479, and AGAIN = 47421 is 9 times as much.

        Like

  • Unknown's avatar

    Jim Randell 4:17 pm on 20 October 2023 Permalink | Reply
    Tags: ,   

    Teaser 3187: Wired squares 

    From The Sunday Times, 22nd October 2023 [link] [link]

    Sitting at his study desk one morning, Ted noticed a paperclip poking out of his notebook, unbent to a straight thin wire. Opening it up he found that his granddaughter Jessica had drawn a square grid (less than 14 cm side width) on one of the pages, with vertical and horizontal grid lines at 1 cm intervals.

    Musing this, Ted numbered each cell consecutively from 1, working left to right along each row from top to bottom in turn. Moving the wire over the page, he rested the wire over (or touching the corner of) some cells containing a square number. Placing the wire carefully, he was able to connect as many square-numbered cells as possible in this way. No square grid of less than 14 cm side width could have allowed the connection of a larger number of squares.

    What squares did he connect?

    When originally posted on The Sunday Times website the upper limit was “less than 15 cm”, but this was revised down to “less than 14 cm” when the paper was published.

    [teaser3187]

     
    • Jim Randell's avatar

      Jim Randell 8:06 am on 21 October 2023 Permalink | Reply

      Initially I drew out the various grids and looked for straight lines that could connect cells. (I am assuming the wire forms a sufficiently long straight line).

      This program examines lines that pass through the corners of square-numbered cells (so it may not find all possible lines), but it does better than I did on my manual attempt.

      It runs in 664ms.

      Run: [ @replit ]

      from enigma import (fdiv, inf)
      
      # find the slope and intercept of a line defined by points p1, p2
      def line_slope_intercept(p1, p2, div=fdiv):
        ((x1, y1), (x2, y2)) = (p1, p2)
        if x1 == x2: return (inf, x1)
        m = div(y2 - y1, x2 - x1)
        c = y1 - m * x1
        return (m, c)
      
      
      from enigma import (
        Accumulator, irange, subsets, tuples, line_intersect, catch, uniq,
        unpack, rdiv, seq2str, printf
      )
      
      # return the corners of a square
      corners = lambda x, y: [(x, y), (x + 1, y), (x + 1, y + 1), (x, y + 1)]
      
      # solve for an n x n grid
      # return max hits
      def solve(n, verbose=1):
        # calculate the corners of each square cell
        sqs = dict()
        pts = set()
        for i in irange(1, n):
          k = i * i
          (y, x) = divmod(k - 1, n)
          sqs[k] = (x, y)
          pts.update(corners(x, y))
      
        # find maximal hits
        ss = Accumulator(fn=max, collect=1)
      
        # consider lines that pass through two of the points
        fn = unpack(lambda p1, p2: line_slope_intercept(p1, p2, div=rdiv))
        for (p1, p2) in uniq(subsets(pts, size=2), fn=fn):
          # which squares are hit
          hit = set()
          for (k, (x, y)) in sqs.items():
            for (c1, c2) in tuples(corners(x, y), 2, circular=1):
              r = catch(line_intersect, p1, p2, c1, c2, internal=2, div=rdiv)
              if r is None: continue
              hit.add(k)
              break
      
          ss.accumulate_data(len(hit), tuple(sorted(hit)))
      
        # output information
        if verbose:
          printf("[n={n}]")
          printf("max hits = {ss.value}")
          for hit in uniq(ss.data):
            printf("-> {hit}", hit=seq2str(hit, enc="[]"))
          printf()
      
        # return max hits
        return ss.value
      
      # consider grids up to size 13
      r = Accumulator(fn=max, collect=1)
      for n in irange(1, 13):
        h = solve(n)
        r.accumulate_data(h, n)
      printf("max hits = {r.value} on grids {r.data}")
      

      Solution: The connected cells are: 1, 4, 16, 36, 49, 64.

      My answer to the originally posted puzzle (with an upper limit of “less than 15 cm”) was the same, and is as follows:

      The program finds two grids (up to 14×14) that allow a maximal 6 square-numbered cells to be connected, shown below:

      (Although the program looks for “reasonable” solutions, it is possible there may be a line with more than 6 connections that is not found by the program).

      However the puzzle is looking for a single unique solution.

      The first (13×13) grid needs a wire of around 11.3 cm in length to link the cells, and the second (14×14) grid needs a wire of around 14 cm to link the cells.

      So if the length of wire is between these values, we can link the cells in the 13×13 grid, but not the 14×14 grid.

      I unbent a couple of paperclips, and found the smaller one used around 10 cm of wire, and the big one about 16 cm. So it is plausible that a paperclip could fall between the two required values, leaving the 13×13 grid as the unique answer.

      In the revised puzzle, only the 13×13 grid is considered, so this must provide the answer. (Which makes me wonder why the puzzle has all the stuff about straightening the paperclip at all).


      The [[ line_slope_intercept() ]] function is included in the latest version of enigma.py.

      Like

  • Unknown's avatar

    Jim Randell 9:21 am on 19 October 2023 Permalink | Reply
    Tags:   

    Teaser 2643: Pack points 

    From The Sunday Times, 19th May 2013 [link] [link]

    Five cubs were awarded points for effort. Enid’s son and Master Smith had 17 points between them, Masters Jones and Robinson had a total of 16, Ivan and Master Robinson together had 14, and the two youngest of the cubs had a total of 13. John and Master Brown had a total of 12 points, Brenda’s son and Mike had 11 points between them, Ken and his best friend had a total of 10, Ann’s son and Debbie’s son together had 9, Christine’s son and Nigel had a total of 8, and Master Perkins and Debbie’s son together had 6.

    In alphabetical order of their mothers’ Christian names, what were the names of the five cubs (Christian name and surname)?

    [teaser2643]

     
    • Jim Randell's avatar

      Jim Randell 9:21 am on 19 October 2023 Permalink | Reply

      If we use variables A, B, C, D, E to correspond to the points allocated (according to mothers name), then we can allocate the first names and surnames of the cubs to the same set of variables to give us a set of simultaneous linear equations, which we can attempt to solve to give the points values.

      I used the [[ Matrix.linear() ]] solver from the enigma.py library to find assignments that give non-negative points values. (Although there are no further solutions if negative values are permitted).

      I also ensure that the pairings mentioned in the text all consist of distinct cubs. (Although, again, there are no further solutions if a pairing can refer to the same cub twice).

      The following Python program runs in 375ms.

      Run: [ @replit ]

      from enigma import (Matrix, subsets, catch, printf)
      
      # suppose the points are: A, B, C, D, E (according to mothers name)
      # then we can map the first and surnames to these values
      
      # variables
      vs = "ABCDE"
      
      # a function to construct equations
      def eq(vs, k, fn=Matrix.equation(vs)):
        r = fn(vs, k)
        assert {0, 1}.issuperset(r[0])  # reject coefficients that aren't 0 or 1
        return r
      
      # validate point values
      def valid(x):
        assert not x < 0
        return x
      
      # for firstnames I, J, K, M, N
      for (I, J, K, M, N) in subsets(vs, size=len, select='P'):
      
        # for surnames P, R, S, T (= Brown), U = (Jones)
        for (P, R, S, T, U) in subsets(vs, size=len, select='P'):
      
          # try to solve the equations
          r = None
          try:
            # construct equations
            eqs = [
              eq(['E', S], 17),  # Enid + Smith = 17
              eq([U, R], 16),  # Jones + Robinson = 16
              eq([I, R], 14),  # Ivan + Robinson = 14
              eq([J, T], 12),  # John + Brown = 12
              eq(['B', M], 11),  # Brenda + Mike = 11
              eq(['A', 'D'], 9),  # Ann + Debbie = 9
              eq(['C', N], 8),  # Christine + Nigel = 8
              eq([P, 'D'], 6),  # Perkins + Debbie = 6
            ]
      
            # solve the equations
            r = Matrix.linear(eqs, valid=valid)
          except (ValueError, AssertionError):
            pass
          if r is None: continue
      
          # check there is a pair = 13 (two youngest), and a pair = 10 (Ken + friend)
          v = dict(zip(vs, r))
          if not any(v[x] + v[K] == 10 for x in vs if x != K): continue
          if not any(v[x] + v[y] == 13 for (x, y) in subsets(vs, size=2)): continue
      
          # output solution
          mn = { 'A': 'Anne', 'B': 'Brenda', 'C': 'Christine', 'D': 'Debbie', 'E': 'Enid' }
          fn = { I: 'Ivan', J: 'John', K: 'Ken', M: 'Mike', N: 'Nigel' }
          sn = { P: 'Perkins', R: 'Robinson', S: 'Smith', T: 'Brown', U: 'Jones' }
          for k in vs:
            printf("{fn} {sn} = {v} points [{mn}]", mn=mn[k], fn=fn[k], sn=sn[k], v=v[k])
          printf()
      

      Solution: The names of the cubs are:

      Mike Smith = 7 points [Anne]
      Ivan Perkins = 4 points [Brenda]
      Ken Jones = 6 points [Christine]
      Nigel Brown = 2 points [Debbie]
      John Robinson = 10 points [Enid]

      So the youngest two are Mike and Ken (= 13 points in total), and Ivan must be Ken’s best friend (= 10 points in total).

      Like

  • Unknown's avatar

    Jim Randell 7:46 am on 15 October 2023 Permalink | Reply
    Tags:   

    Teaser 2644: Route canal 

    From The Sunday Times, 26th May 2013 [link] [link]

    Stephen is planning a 70-mile canal trip from the lock at Aytown to the lock at Beeswick, stopping at a pub at a lock over half way along the route. He has listed the number of miles from each lock to the next, the largest being the stretch from the pub to the next lock. He has also noted the distances from Aytown to the pub and from the pub to Beeswick. All the numbers that he has written down are different primes.

    Stephen can use his figures to work out the number of miles from any lock to any other. He’s found that, whenever that number of miles between locks is odd, then it is also a prime.

    What (in order) are the numbers of miles between consecutive locks?

    [teaser2644]

     
    • Jim Randell's avatar

      Jim Randell 7:46 am on 15 October 2023 Permalink | Reply

      This Python program runs in 55ms. (Internal runtime is 806µs).

      Run: [ @replit ]

      from enigma import (irange, primes, express, diff, cproduct, subsets, flatten, printf)
      
      # decompose total <t> into different values from <ns>
      def decompose(t, ns):
        if not ns: return
        for qs in express(t, ns, qs=(0, 1)):
          yield list(n for (n, q) in zip(ns, qs) if q > 0)
      
      # check all odd distances are prime
      def check(ds):
        for (i, j) in subsets(irange(len(ds)), size=2):
          d = sum(ds[i:j + 1])
          if d % 2 == 1 and d not in primes: return False
        return True
      
      # consider possible distances from P to B (prime, < 35)
      for d2 in primes.between(2, 34):
        # distance from A to P (prime, > 35)
        d1 = 70 - d2
        if d1 not in primes: continue
      
        # decompose the distances into individual sections
        for d2s in decompose(d2, list(primes.between(2, d2 - 1))):
          max_d = d2s[-1]
          for d1s in decompose(d1, list(diff(primes.between(2, max_d - 1), d2s))):
      
            # construct possible ordered sequences
            for (s1, s2) in cproduct(subsets(s, size=len, select='P') for s in (d1s, d2s[:-1])):
              ds = flatten([s1, [max_d], s2])
              if check(ds):
                # output solution
                printf("{ds}")
      

      Solution: The distances between consecutive locks (from A to B) are (in miles): 17, 13, 11, 19, 7, 3.

      Like

  • Unknown's avatar

    Jim Randell 4:25 pm on 13 October 2023 Permalink | Reply
    Tags:   

    Teaser 3186: Contemporary classmates 

    From The Sunday Times, 15th October 2023 [link] [link]

    In our local school, the year begins on September 1 and ends on August 31, and class one contains all the children who reach the age of five during the school year. I was looking back at the school records and discovered that one year during the 1980s, it so happened that the birthdays of a group of children in class one fell on the same-numbered day of different months and also on the same day of the week. The size of this group was the largest possible for these circumstances, and by coincidence the number of classmates in the group was also the number of the day in the month for their birthday.

    The oldest member of that group, whose birthday was in January, was born in the 1980s.

    What was the full date of birth of the youngest member of that group?

    [teaser3186]

     
    • Jim Randell's avatar

      Jim Randell 4:54 pm on 13 October 2023 Permalink | Reply

      This Python program finds maximal groups of birthdays sharing day-of-month and day-of-week for academic years starting in the 1980s, and then looks for maximal groups satisfying the remaining conditions.

      It runs in 76ms. (Internal runtime is 4.6ms).

      Run: [ @replit ]

      from datetime import (date, timedelta)
      from enigma import (defaultdict, Accumulator, irange, repeat, inc, seq2str, printf)
      
      # collect all maximal groups
      gs = Accumulator(fn=max, collect=1)
      
      # consider school years that start in the 80s
      for y in irange(1980, 1989):
      
        # record (day-of-month, day-of-week) -> [date, ...]
        g = defaultdict(list)
        for d in repeat(inc(timedelta(days=1)), date(y, 9, 1)):
          if d.year > y and d.month > 8: break
          g[(d.day, d.isoweekday())].append(d)
      
        # look for the longest lists
        r = Accumulator(fn=max, collect=1)
        for (k, vs) in g.items():
          r.accumulate_data(len(vs), k)
      
        # record them
        for k in r.data:
          gs.accumulate_data(r.value, g[k])
      
      # maximal group size
      n = gs.value
      printf("max group = {n}")
      
      # look for maximal groups where day-of-month = n
      # and the oldest member of the group has a birthday in January
      for ds in gs.data:
        d = ds[0]
        if not (d.day == n and d.month == 1): continue
        # calculate the dates of birth
        bs = list(d.replace(year=d.year - 5) for d in ds)
        # check the eldest is born in the 80s
        if not bs[0].year // 10 == 198: continue
        # output solution
        printf("birthdates = {bs}", bs=seq2str(bs))
      

      Solution: The youngest member of the group was born on: 3rd July 1983.

      The largest group of dates in a year that share the same day of the week and day of the month is 3.

      The relevant school year starts on 1st September 1987, and the 3 birthdays are:

      Sunday, 3rd January 1988 (DOB: Monday, 3rd January 1983)
      Sunday, 3rd April 1988 (DOB: Sunday, 3rd April 1983)
      Sunday, 3rd July 1988 (DOB: Sunday, 3rd July 1983)

      Like

  • Unknown's avatar

    Jim Randell 12:31 pm on 12 October 2023 Permalink | Reply
    Tags:   

    Teaser 2656: Wrong adding 

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

    Here is an addition sum in which digits have consistently been replaced by letters, with different letters used for different digits. The six-figure total is even:

    Unfortunately, once again I have made a mistake. In just one place in the display I have written an incorrect letter.

    What is the correct numerical value of the six-figure total?

    [teaser2656]

     
    • Jim Randell's avatar

      Jim Randell 12:31 pm on 12 October 2023 Permalink | Reply

      I’ve added the [[ bungled_sum() ]] solver (from Puzzle 56 etc.) to the enigma.py library as a class method on [[ SubstitutedSum ]], and also allowed it to be called from the command line.

      Using this solver we find there are two possibilities for as single letter mistake in the translation.

      The following command line executes in 270ms.

      Run: [ @replit ]

      % python3 -m enigma -r SubstitutedSum.bungled_sum "AGAIN + WRONG = ADDING"
      
                  G
      AGAIN + WRONI = ADDING / @[1,4] G -> I
      14195 + 86759 = 100954 / A=1 D=0 G=4 I=9 N=5 O=7 R=6 W=8
      
                           G
      AGAIN + WRONG = ADDINA / @[2,5] G -> A
      16195 + 84756 = 100951 / A=1 D=0 G=6 I=9 N=5 O=7 R=4 W=8
      

      But we are told that in the original numerical sum the result is even, so we are looking at the first possibility.

      Solution: The result of the sum is 100954.

      The original sum was: 14195 + 86759 = 100954.

      And it should have been encoded as: AGAIN + WRONI = ADDING.

      Like

  • Unknown's avatar

    Jim Randell 1:24 pm on 10 October 2023 Permalink | Reply
    Tags:   

    Brainteaser 1702: And now not one is… 

    From The Sunday Times, 30th April 1995 [link]

    In what follows, digits have been replaced by letters, a different letter being used consistently for each different digit. There are no zeros.

    Each of the recurring decimals:

    .ANDANDAND
    .NOWNOWNOW
    .NOTNOTNOT
    .ONEONEONE
    .ISISISIS

    equals a fraction with a denominator less than 50. And now not one is easy to find.

    Please find what is DONE.

    [teaser1702]

     
    • Jim Randell's avatar

      Jim Randell 1:25 pm on 10 October 2023 Permalink | Reply

      We have:

      0.(ab)… = ab / 99
      0.(abc)… = abc / 999

      Here is a run file that uses the [[ SubstitutedExpression ]] solver from the enigma.py library to solve the puzzle.

      It runs in 86ms. (Internal runtime of the generated program is 14ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      
      # valid fractions have denominators less than 50
      --code="valid = lambda a, b: Fraction(a, b).den < 50"
      
      "valid(AND, 999)"
      "valid(NOW, 999)"
      "valid(NOT, 999)"
      "valid(ONE, 999)"
      "valid(IS, 99)"
      
      --answer="DONE"
      

      Solution: DONE = 4925.

      The fractions are:

      0.(AND)… = 324/999 = 12/37
      0.(NOW)… = 296/999 = 8/27 [*]
      0.(NOT)… = 297/999 = 11/37 [*]
      0.(ONE)… = 925/999 = 25/27
      0.(IS)… = 81/99 = 9/11 [*]
      0.(IS)… = 18/99 = 2/11 [*]

      [*] NOW and NOT can be interchanged, and the digits of IS can appear in either order, which accounts for the four solutions found by the program.

      Like

  • Unknown's avatar

    Jim Randell 12:28 pm on 8 October 2023 Permalink | Reply
    Tags:   

    Teaser 1866: Which Sunday paper? 

    From The Sunday Times, 21st June 1998 [link]

    Today you have to do a usual digits-for-letters substitution to make sense of the sum:

    SUN + DAYS = ?????

    I have not yet decided which Sunday paper the right-hand side is going to be. It will be one of GRAPH, MATCH, SCENE, SEEDY, SPORT, TIMES, TODAY, WORLD and WYNDY.

    But I must choose it so that the five-figure total is uniquely determined.

    Which word should be the right-hand side, and then what would the numerical total be?

    [teaser1866]

     
    • Jim Randell's avatar

      Jim Randell 12:29 pm on 8 October 2023 Permalink | Reply

      This Python program uses the [[ SubstitutedExpression.split_sum() ]] solver from the enigma.py library to try each of the candidate alphametic sums, and looks for ones that have a single solution.

      It runs in 82ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, singleton, sprintf, printf)
      
      # possible words
      words = "GRAPH MATCH SCENE SEEDY SPORT TIMES TODAY WORLD WYNDY".split()
      
      # choose a result
      for w in words:
        # construct a puzzle using the word
        expr = sprintf("SUN + DAYS = {w}")
        p = SubstitutedExpression.split_sum(expr).solver()
        # that has a single solution
        s = singleton(p.solve(verbose=0))
        if s is None: continue
        # output solution
        printf("{expr} / {s}", s=p.substitute(s, expr))
      

      Solution: The alphametic total is: WYNDY, and the numerical total is: 10390.

      So we have:

      SUN + DAYS = WYNDY → 783 + 9607 = 10390

      The remaining result words have no solutions, apart from WORLD, which has 6.

      Like

  • Unknown's avatar

    Jim Randell 4:42 pm on 6 October 2023 Permalink | Reply
    Tags:   

    Teaser 3185: Multiple squares 

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

    My grandson likes to compile 3×3 magic squares, where each of the three rows of numbers, each of the three columns of numbers and both of the straight diagonals, add up to the same total. This he did without repeating any of the nine numbers in the square.

    He has now progressed to compiling similar 3×3 squares, which instead of the eight rows, columns and diagonals of numbers adding to the same total, they instead multiply to produce the same product.

    In his first such square this product was 32,768. He was able to find every square of nine different whole numbers that gives this product, excluding identical rotational and mirrored squares.

    What, in ascending order, were the totals of the nine numbers in each of his different squares?

    [teaser3185]

     
    • Jim Randell's avatar

      Jim Randell 5:09 pm on 6 October 2023 Permalink | Reply

      32768 = 2^15, so each multiplicative square is filled with numbers that are powers of 2. And we can treat it as an additive magic square of the corresponding exponents.

      So we just need to find how many essentially different additive magic squares there are (modulo rotation and reflection) with a magic constant of 15. And we can then transform the exponents back to powers of 2 to give the required answers. (Note we allow an exponent of 0 to give a corresponding value of 2^0 = 1).

      This Python program uses the [[ SubstitutedExpression() ]] solver from the enigma.py library to generate all possible additive magic squares of exponents, and then collects those that are different, and we then transform the exponents back to powers of two and sum the numbers in the corresponding multiplicative magic square to give the required total.

      It runs in 77ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, irange, uniq, printf)
      
      # orientations of a square
      squares = [
        # rotations
        (0, 1, 2, 3, 4, 5, 6, 7, 8),
        (2, 5, 8, 1, 4, 7, 0, 3, 6),
        (8, 7, 6, 5, 4, 3, 2, 1, 0),
        (6, 3, 0, 7, 4, 1, 8, 5, 2),
        # reflections
        (2, 1, 0, 5, 4, 3, 8, 7, 6),
        (8, 5, 2, 7, 4, 1, 6, 3, 0),
        (6, 7, 8, 3, 4, 5, 0, 1, 2),
        (0, 3, 6, 1, 4, 7, 2, 5, 8),
      ]
      
      # find the canonical form of a square
      def canonical(sq):
        return min(tuple(sq[j] for j in js) for js in squares)
      
      # find possible squares
      p = SubstitutedExpression(
        [
          # rows
          "A + B + C == 15",
          "D + E + F == 15",
          "G + H + I == 15",
          # columns
          "A + D + G == 15",
          "B + E + H == 15",
          "C + F + I == 15",
          # diagonals
          "A + E + I == 15",
          "C + E + G == 15",
        ],
        base=11,
        answer="(A, B, C, D, E, F, G, H, I)"
      )
      
      # collect results
      ts = list()
      
      # find different squares
      for sq in uniq(canonical(sq) for sq in p.answers(verbose=0)):
        # transform the powers to numbers, and sum them
        t = sum(2**n for n in sq)
        printf("[{sq} -> {t}]")
        ts.append(t)
      
      # output solution
      printf("totals = {ts}", ts=sorted(ts))
      

      Solution: The totals of the numbers in the different squares are: 1022, 1533, 1911.

      And the corresponding squares are:

      Like

    • Hugo's avatar

      Hugo 9:26 am on 15 October 2023 Permalink | Reply

      If we halve each number in the first of those squares, we get one with magic product 4096.
      But that’s certainly not the smallest possible value, as Dudeney showed:
      see the solution and comments to Puzzle no. 203.

      Like

  • Unknown's avatar

    Jim Randell 8:59 am on 4 October 2023 Permalink | Reply
    Tags:   

    Teaser 2496: [Letter values] 

    From The Sunday Times, 25th July 2010 [link] [link]

    I have chosen 10 different letters of the alphabet before U to represent the digits 0 to 9. I can tell you that 0’s letter is in the word NOUGHT, 1’s letter is in the word ONE, 2’s letter is in TWO, and so on up to 9’s letter being in the word NINE.

    Furthermore, I can write down a particular five-figure number that is divisible by 8, and replacing the digits by their letters I get EIGHT. Similarly, a four-figure number divisible by 9 translates to NINE.

    What number is represented by TEN?

    This puzzle was originally published with no title.

    [teaser2496]

     
    • Jim Randell's avatar

      Jim Randell 9:00 am on 4 October 2023 Permalink | Reply

      We only need to consider the letters AT, and of these only ten of them appear in the words mentioned. So we can assign the digits 0-9 to the letters EFGHINORST (in some order).

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to do this, subject to the necessary conditions.

      The following run file executes in 126ms. (Internal runtime is 949µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "EIGHT % 8 = 0"
      "NINE % 9 = 0"
      
      "0 in {N, O, G, H, T}"
      "1 in {O, N, E}"
      "2 in {T, O}"
      "3 in {T, H, R, E}"
      "4 in {F, O, R}"
      "5 in {F, I, E}"
      "6 in {S, I}"
      "7 in {S, E, N}"
      "8 in {E, I, G, H, T}"
      "9 in {N, I, E}"
      
      --answer="TEN"
      --template=""
      

      Solution: TEN = 271.

      The assignment of digits to (bold) letters is:

      0 → (N) O (UGHT)
      1 → (O) N (E)
      2 → T (WO)
      3 → (T) H (REE)
      4 → (FOU) R
      5 → F (IVE)
      6 → S (IX)
      7 → (S) E (V) E (N)
      8 → (EI) G (HT)
      9 → (N) I (NE)

      And:

      EIGHT = 79832 = 8 × 9979
      NINE = 1917 = 9 × 213

      Like

  • Unknown's avatar

    Jim Randell 4:19 pm on 29 September 2023 Permalink | Reply
    Tags:   

    Teaser 3184: Four away 

    From The Sunday Times, 1st October 2023 [link] [link]

    The rectangular snooker table had four corner pockets and two centre pockets (one each in the middle of the left and right sides, which were 12ft long). A ball always left the cushions at the same angle as it struck them. The cue ball was by the bottom right corner pocket and Joe hit it off the left cushion; it bounced off another seven cushions without hitting a ball or entering a pocket, then entered the right centre pocket. Four away!

    Joe replayed the shot but the cue ball hit the left cushion further up the table than before, bounced another ten times without hitting a ball or entering a pocket, then entered the right centre pocket. Four away!

    How much further up the table did the second shot hit the left cushion?

    [teaser3184]

     
    • Jim Randell's avatar

      Jim Randell 5:02 pm on 29 September 2023 Permalink | Reply

      (See also: Teaser 1917 and Teaser 3129).

      Fairly straightforward with some graph paper to find paths between the bottom right and centre right pockets that hit exactly 8 and 11 cushions.

      These give the angle of the shot, from which we can calculate the distance between the respective first bounces, and this gives the answer to the puzzle.

      Run: [ @replit ]

      from enigma import (Rational, irange, is_coprime, printf)
      
      Q = Rational()
      
      # g: bounce -> [((x, y), angle), ...]
      g = { 8: [], 11: [] }
      
      # consider bounces between (0, 0) and (x, y)
      for y in irange(1, 24, step=2):
        for x in irange(2, 12, step=2):
          # check ball doesn't hit a pocket early
          if not is_coprime(x, y): continue
          # calculate number of bounces from (0, 0)
          b = (x - 1) + y // 2
          if b not in g: continue
          # calculate the angle
          t = Q(y, x)
          if t < 2:
            g[b].append(((x, y), t))
      
      # look for 8 bounces
      for ((x1, y1), t1) in g[8]:
        # look for 11 bounces, with a higher angle
        for ((x2, y2), t2) in g[11]:
          if not (t2 > t1): continue
          printf("(0, 0) -> ({x1}, {y1}) = 8 bounces [t = {t1}]")
          printf("-> (0, 0) -> ({x2}, {y2}) = 11 bounces [t = {t2}]")
          # calculate distance between first bounces
          d = 6 * (t2 - t1)
          printf("--> answer = {d:.2f} ft", d=float(d))
      

      Solution: The second shot hit the left cushion 4.5 feet further up the table than the first shot.

      In the following diagram the original table is in the bottom right corner, and the other rectangles are reflections of the table. We want to find paths between the red pocket in the extreme bottom right and a green pocket, that cross the boundaries of the table (the black lines) 8 and 11 times, but do not hit any intermediate pockets.

      The two paths are shown as dotted lines.

      We can denote the 8-bounce path is (8, 3) (i.e. 8 units horizontally, and 3 units vertically), and the 11-bounce path is (8, 9).

      There is a further 8-bounce path at (6, 7) and further 11-bounce path at (12, 1), but these are not involved in the answer to the puzzle.

      And here are the two paths (8-bounce = red, 11-bounce = green) plotted on the table:

      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