Tagged: by: Keith Austin Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 8:50 am on 22 July 2025 Permalink | Reply
    Tags: by: Keith Austin   

    Brainteaser 1263: I do apologise 

    From The Sunday Times, 16th November 1986 [link]

    The artist Pussicato has a new painting which is a 6×6 array of squares and each square is red or blue or green.

    (A) Label the horizontal rows of the painting U, V, W, X, Y, Z from top to bottom and the vertical columns 1, 2, 3, 4, 5, 6 from left to right. Then U1, U3, V5 are red; W2, Z2 are blue; and U2, V4, X2, X3, Y3, Y4 are green. Also V1 and W1 are the same colour, as are Y1 and Z1, and also X6 and Y6. Each row and each column contains two red squares, two blue and two green.

    (B) I do apologise. The information in (A) gives you a painting which is wrong in every square.

    (C) Use the information in (B), together with the following facts. Any two squares which are next to each other, horizontally or vertically, have different colours. There are more green squares than blue squares.

    (D) Look, I am so sorry. The information in (C) gives you a painting which is wrong in every square.

    (E) To be serious, the information in (B) and (D) is correct.

    Give (in order) the colours of the squares in the top row of Pussicato’s painting.

    [teaser1263]

     
    • Jim Randell's avatar

      Jim Randell 8:52 am on 22 July 2025 Permalink | Reply

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

      The program first uses the information in (A) to produce a grid (= “layout 1”). It then uses layout 1, and the information in (B) and (C) to produce another layout (= “layout 2”). It then uses layout 1, layout 2 and the information in (B) and (D) to produce a final layout, that provides the answer to the puzzle.

      Unfortunately, the program below fails with the standard CPython implementation (due to limitations in the CPython interpreter), but works fine with PyPy.

      (The code on codepad includes a workaround for CPython [*]).

      The following Python program runs in 132ms (under PyPy). (Internal runtime is 37.9ms).

      Run: [ @codepad ]

      from enigma import (enigma, SubstitutedExpression, first, sprintf as f, join, printf)
      
      # symbols for the 36 cells
      syms = first(enigma.str_upper + enigma.str_lower, 36)
      
      # map cells to symbols
      (rs, cs) = ("UVWXYZ", "123456")
      (v, vs) = (dict(), iter(syms))
      for r in rs:
        for c in cs:
          v[r + c] = next(vs)
      
      # rows and cols
      rows = list(list(r + c for c in cs) for r in rs)
      cols = list(list(r + c for r in rs) for c in cs)
      
      # colours
      (red, green, blue) = (0, 1, 2)
      
      
      # find layout 1
      def solve1():
      
        # construct expressions
        exprs = list()
      
        # U1, U3, V5 are red
        exprs.extend(f("{{{x}}} = {red}", x=v[x]) for x in ["U1", "U3", "V5"])
        # W2, Z2 are blue
        exprs.extend(f("{{{x}}} = {blue}", x=v[x]) for x in ["W2", "Z2"])
        # U2, V4, X2, X3, Y3, Y4 are green
        exprs.extend(f("{{{x}}} = {green}", x=v[x]) for x in ["U2", "V4", "X2", "X3", "Y3", "Y4"])
        # V1 = W1; Y1 = Z1; X6 = Y6
        exprs.extend(f("{{{x}}} = {{{y}}}", x=v[x], y=v[y]) for (x, y) in [("V1", "W1"), ("Y1", "Z1"), ("X6", "Y6")])
      
        # each row/column contains 2 red, 2 green, 2 blue
        check = lambda *vs: all(vs.count(x) == 2 for x in (red, green, blue))
        for r in rows:
          exprs.append(join((f("{{{x}}}", x=v[x]) for x in r), sep=", ", enc=["check(", ")"]))
        for c in cols:
          exprs.append(join((f("{{{x}}}", x=v[x]) for x in c), sep=", ", enc=["check(", ")"]))
      
        # make an alphametic puzzle to solve the grid
        p = SubstitutedExpression(exprs, base=3, distinct=0, env=dict(check=check))
        for s in p.solve(verbose=0):
          yield list(s[k] for k in syms)
      
      
      # find layout 2
      def solve2(g1):
      
        # no colour from layout 1 is correct
        d2i={ red: [], green: [], blue: [] }
        for (k, v) in zip(syms, g1):
          d2i[v].append(k)
      
        # no adjacent squares are the same colour
        distinct=list()
        for (i, k) in enumerate(syms):
          (r, c) = divmod(i, 6)
          if c < 5: distinct.append(k + syms[i + 1])
          if r < 5: distinct.append(k + syms[i + 6])
      
        # there are more green than blue squares
        check = lambda *vs: vs.count(green) > vs.count(blue)
        expr = join(syms, sep="}, {", enc=["check({", "})"])
      
        # make an alphametic puzzle to solve the grid
        p = SubstitutedExpression([expr], base=3, distinct=distinct, d2i=d2i, env=dict(check=check))
        for s in p.solve(verbose=0):
          yield list(s[k] for k in syms)
      
      
      # solve for layout 1
      for g1 in solve1():
        printf("g1={g1}")
        # solve for layout 2
        for g2 in solve2(g1):
          printf("g2={g2}")
          # generate the grid where each cell is different from g1 and g2
          g3 = list(3 - (c1 + c2) for (c1, c2) in zip(g1, g2))
          printf("g3={g3}")
      
          # output the grid
          d = { red: 'R', green: 'G', blue: 'B' }
          printf()
          for (i, v) in enumerate(g3):
            if i % 6 == 0: printf("[ \\")
            printf("{v} \\", v=d[v])
            if i % 6 == 5: printf("]")
      

      Solution: The squares in the top row are: blue, blue, green, red, blue, red.

      The requirements in (A) give a single layout (shown below on the left).

      The requirements (B) and (C) also give a single layout (shown below in the centre).

      And finally, the requirements (D) give a single layout (where each square is different colour to the corresponding positions the first two grids). (This is shown below on the right).


      [*] When producing layout 2 there is only a single expression, involving all 36 variables, so the “denesting” code cannot split the expressions into blocks that use fewer expressions. To fix this we add in the following expression, which mentions half the symbols (and is always true), and this allows the denesting code to do its job:

      true(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R)
      

      Like

  • Unknown's avatar

    Jim Randell 11:11 am on 28 May 2024 Permalink | Reply
    Tags: by: Keith Austin   

    Brainteaser 1351: Letters find the largest 

    From The Sunday Times, 24th July 1988 [link]

    Each letter stands for one of the two digits 1 and 2.

    TEN is larger than SIX, which is larger than TWO, which is larger than ONE.

    NINE is larger than FIVE, which is larger than FOUR, which is larger than ZERO.

    EIGHT is larger than SEVEN, which is larger than THREE.

    Which is the larger in each of the following pairs?

    (a) ELEVEN and NINETY;
    (b) TWENTY and EIGHTY;
    (c) FIFTY and SIXTY;
    (d) SEVENTY and HUNDRED;
    (e) EIGHTY and NINETY?

    [teaser1351]

     
    • Jim Randell's avatar

      Jim Randell 11:11 am on 28 May 2024 Permalink | Reply

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

      It runs in 80ms. (Internal runtime of the generated program is 122µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1,2"
      --distinct=""
      
      "TEN > SIX"
      "SIX > TWO"
      "TWO > ONE"
      
      "NINE > FIVE"
      "FIVE > FOUR"
      "FOUR > ZERO"
      
      "EIGHT > SEVEN"
      "SEVEN > THREE"
      
      --answer="(ELEVEN < NINETY, TWENTY < EIGHTY, FIFTY < SIXTY, SEVENTY < HUNDRED, EIGHTY < NINETY)"
      --template=""
      

      Solution: We have:

      NINETY > ELEVEN
      EIGHTY > TWENTY
      FIFTY > SIXTY
      SEVENTY > HUNDRED
      NINETY > EIGHTY

      The following letters have fixed values:

      E=2, F=2, G=2, H=1, I=2, N=2, O=1, S=2, T=2, V=1, W=1, X=1, Z=1

      And the letters D, L, R, U, Y can take on either value, and give rise to the 32 possible assignments.

      So:

      NINETY = 22222? > 2?2122 = ELEVEN
      EIGHTY = 22212? > 21222? = TWENTY
      FIFTY = 2222? > 2212? = SIXTY
      SEVENTY = 221222? > 1?2??2? = HUNDRED
      NINETY = 22222? > 22212? = EIGHTY

      Like

    • Ruud's avatar

      Ruud 3:51 pm on 28 May 2024 Permalink | Reply

      Brute force solution:

      from collections import defaultdict
      from istr import istr
      
      answers = defaultdict(set)
      for t, e, n, s, i, x, w, o, f, v, r, z, g, h, u, l, y, d in istr.product((1, 2), repeat=18):
          if t | e | n > s | i | x > t | w | o > o | n | e:
              if n | i | n | e > f | i | v | e > f | o | u | r > z | e | r | o:
                  if e | i | g | h | t > s | e | v | e | n > t | h | r | e | e:
                      answers["a"].add(("ninety", "eleven")[e | l | e | v | e | n > n | i | n | e | t | y])
                      answers["b"].add(("eighty", "twenty")[t | w | e | n | t | y > e | i | g | h | t | y])
                      answers["c"].add(("sixty", "fifty")[f | i | f | t | y > s | i | x | t | y])
                      answers["d"].add(("hundred", "seventy")[s | e | v | e | n | t | y > h | u | n | d | r | e | d])
                      answers["e"].add(("ninety", "eighty")[e | i | g | h | t | y > n | i | n | e | t | y])
      
      for letter, answer in answers.items():
          print(f"{letter}) {answer}")
      

      Like

      • Jim Randell's avatar

        Jim Randell 4:49 pm on 28 May 2024 Permalink | Reply

        I get:

        TypeError: tuple indices must be integers or slices, not istr
        

        Like

        • Ruud's avatar

          Ruud 5:05 pm on 28 May 2024 Permalink | Reply

          @Jim Randell
          Sorry, I forgot to tell that you need istr 1.0.7 to run this code.

          Like

    • Ruud's avatar

      Ruud 8:19 am on 29 May 2024 Permalink | Reply

      As some ‘Spielerei’, I did refactor my original istr-version.
      It uses n2w module to convert ints to ‘spoken’ strings. With that and a helper function, I can formulate all the comparison statements as ints, which make more compact code.

      A bit (?) overengineered, maybe …

      from collections import defaultdict
      from istr import istr
      import n2w  # this module can convert an integer to a 'spoken' string
      from functools import reduce, lru_cache
      
      
      @lru_cache
      def convert(number):
          """just as n2w.convert, but 100 is treated differenr]t as n2w.convert(100) is 'one hundred'"""
          return "hundred" if number == 100 else n2w.convert(number)
      
      
      def a(number):
          """translates an int into an istr, using the global variables that define the letter value"""
          return reduce(lambda x, y: x | globals()[y], convert(number), istr(""))
      
      
      def largest(number0, number1):
          return (convert(number1), convert(number0))[a(number0) > a(number1)]
      
      
      answers = defaultdict(set)
      for t, e, n, s, i, x, w, o, f, v, r, z, g, h, u, l, y, d in istr.product((1, 2), repeat=18):
          if a(10) > a(6) > a(2) > a(1):
              if a(9) > a(5) > a(4) > a(0):
                  if a(8) > a(7) > a(3):
                      answers["a"].add(largest(11, 90))
                      answers["b"].add(largest(80, 20))
                      answers["c"].add(largest(60, 50))
                      answers["d"].add(largest(100, 70))
                      answers["e"].add(largest(90, 80))
      
      for letter, answer in answers.items():
          print(f"{letter}) {answer}")
      

      Like

    • Jim Randell's avatar

      Jim Randell 10:09 am on 29 May 2024 Permalink | Reply

      All comparisons are between values of the same length, so we don’t need to convert the values into numbers, we can just use lexicographic ordering.

      This Python program uses integer keys to refer to the words, but considers the values of the words with all 2^18 possible assignments of symbols, so is much slower than my solution using the [[ SubstitutedExpression ]] solver.

      from enigma import (union, subsets, join, printf)
      
      # words we are interested in (use numbers as short keys)
      words = {
        10: 'TEN', 6: 'SIX', 2: 'TWO', 1: 'ONE',
        9: 'NINE', 5: 'FIVE', 4: 'FOUR', 0: 'ZERO',
        8: 'EIGHT', 7: 'SEVEN', 3: 'THREE', 50: 'FIFTY', 60: 'SIXTY',
        11: 'ELEVEN', 90: 'NINETY', 20: 'TWENTY', 80: 'EIGHTY',
        70: 'SEVENTY', 100: 'HUNDRED',
      }
      
      # answers we need to find (which is larger?)
      qs = [(11, 90), (20, 80), (50, 60), (70, 100), (80, 90)]
      
      # collect symbols used
      syms = sorted(union(words.values()))
      
      # collect answers
      ans = set()
      # assign the symbols to values
      for vs in subsets('12', size=len(syms), select='M'):
        m = dict(zip(syms, vs))
        # map the words to values
        w = dict((k, join(m[x] for x in v)) for (k, v) in words.items())
        # check the conditions
        if (w[10] > w[6] > w[2] > w[1] and w[9] > w[5] > w[4] > w[0] and w[8] > w[7] > w[3]):
          # accumulate answers
          ans.add(tuple(max(a, b, key=w.get) for (a, b) in qs))
      
      # output solution
      for ks in ans:
        for (q, k) in zip("abcde", ks):
          printf("({q}) {k}", k=words[k])
        printf()
      

      Like

  • Unknown's avatar

    Jim Randell 11:57 am on 23 April 2023 Permalink | Reply
    Tags: by: Keith Austin   

    Brain teaser 1005: Words and numbers 

    From The Sunday Times, 1st November 1981 [link]

    A familiar letters-for-digits problem is to take a correct addition sum such as:

    1 + 1 = 2

    and write it in words so:

    ONE + ONE = TWO

    The problem is to assign numbers from 0 to 9 to the letters so that we get a correct addition sum, e.g. E = 6, N = 3, O = 2, T = 4, W = 7 gives:

    236 + 236 = 472

    Two rules to be kept are that the left hand digit of any number is not zero, and different letters are assigned different numbers.

    Some addition sums clearly have no solution. e.g.:

    ONE + THREE = FOUR

    In the Kudali Language the words for 1, 2, 3, 4 are AA, BA, EB, DE, although not necessarily in that order. Now all the addition sums:

    1 + 1 = 2
    1 + 2 = 3
    1 + 3 = 4
    2 + 2 = 4
    1 + 4 = 2 + 3

    when written in Kudali, give letters-for-digits problems each of which has at least one solution.

    What are the Kudali words for 1, 2, 3, 4, in that order?

    This puzzle is included in the books Microteasers (1986) and The Sunday Times Book of Brainteasers (1994).

    [teaser1005]

     
    • Jim Randell's avatar

      Jim Randell 11:57 am on 23 April 2023 Permalink | Reply

      This Python program uses the [[ SubstitutedExpression() ]] solver from the enigma.py library to see if there are solutions to the alphametic expressions.

      It runs in 85ms.

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, subsets, sprintf, printf)
      
      # does an alphametic sum have answers?
      solve = lambda expr: any(SubstitutedExpression([expr]).solve(verbose=0))
      
      # expressions involving words for 1, 2, 3, 4
      exprs = [
        "{w1} + {w1} = {w2}",
        "{w1} + {w2} = {w3}",
        "{w1} + {w3} = {w4}",
        "{w2} + {w2} = {w4}",
        "{w1} + {w4} == {w2} + {w3}",
      ]
      
      # allocate the words
      for (w1, w2, w3, w4) in subsets(["AA", "BA", "EB", "DE"], size=4, select='P'):
        # are all the expressions solvable?
        if all(solve(sprintf(x)) for x in exprs):
          # output solution
          printf("1 = {w1}; 2 = {w2}; 3 = {w3}; 4 = {w4}")
      

      Solution: 1 = DE; 2 = BA; 3 = AA; 4 = EB.

      So we have:

      DE + DE = BA → 13 + 13 = 26, …
      DE + BA = AA → 10 + 23 = 33, …
      DE + AA = EB → 13 + 22 = 35, …
      BA + BA = EB → 21 + 21 = 42, …
      DE + EB = BA + AA → 14 + 42 = 23 + 33, …

      Like

    • Frits's avatar

      Frits 5:10 pm on 23 April 2023 Permalink | Reply

       
      from enigma import subsets
      
      '''
      0: ab + ab = ef       reject if: b in {a, f}, e = f,
      1: ab + cd = ef       reject if: b = f and d in {a, e}, d = f and b in {c, e}
                                       e = f and (b = c or a = d)
      2: ab + cd = ef + gh  reject if not: b = d or f = h 
      '''
      
      # reject words depending on the type of equation
      def rej(t, w, x, y, z=0):
        (a, b) = (w[0], w[1])
        (c, d) = (x[0], x[1])
        (e, f) = (y[0], y[1])
        if t == 0 and (e == f or b in {a, f}): return True
        if t == 1 and any([b == f and d in {a, e}, d == f and b in {c, e},
                           e == f and (b == c or a == d)]): return True
        if t == 2 and not (b == d or f == z[1]): return True                   
        
        return False
      
      # allocate the words
      for (w1, w2, w3, w4) in subsets(["AA", "BA", "EB", "DE"], size=4, select='P'):
        if rej(0, w1, w1, w2): continue
        if rej(1, w1, w2, w3): continue
        if rej(1, w1, w3, w4): continue
        if rej(0, w2, w2, w4): continue
        if rej(2, w1, w4, w2, w3): continue
        print(*zip(range(1, 5), (w1, w2, w3, w4)))
      

      Like

      • Frits's avatar

        Frits 5:23 pm on 23 April 2023 Permalink | Reply

        This is more of a program saying: “if there is a solution it must be one of these: ….”

        Like

  • Unknown's avatar

    Jim Randell 1:45 pm on 10 November 2019 Permalink | Reply
    Tags: by: Keith Austin,   

    Brainteaser 1742: Not-so-safe deposit 

    From The Sunday Times, 4th February 1996 [link]

    The safe deposit boxes at our bank are arranged in a huge pyramid, the top of which is illustrated:

    When the were first opened the top box had a quantity of gold coins placed in it and the rest were empty. One night a burglar broke into that box, emptied it, and (cleverly, he thought) instead of running off with the coins, he found the two empty boxes in the row below and put some of his haul in one and the rest in the other.

    Word spread through the underworld about the bank’s lax security, and a sequence of burglaries took place. Each burglar broke into one box containing some coins, emptied it, found two empty boxes in the row below, placed some of his haul in one of these boxes and the rest in the other.

    Ages later the manager of the bank opened up the top box and was horrified to find it empty. So he tried the boxes in the next row and found them empty too. He carried on like this until he found some coins in one particular row. In fact, no matter how many burglaries had taken place, there could not have been more empty rows at the top of the pyramid. And indeed if any fewer burglaries had taken place all those rows could not have been empty.

    (a) How many empty rows at the top were there?
    (b) How many burglaries were there?

    This puzzle is included in the book Brainteasers (2002). The puzzle text above is taken from the book.

    [teaser1742]

     
    • Jim Randell's avatar

      Jim Randell 1:45 pm on 10 November 2019 Permalink | Reply

      We can try to empty the uppermost rows as quickly as possible.

      Initially, in the top row there is 1 box, initially containing the complete amount:

      [0] 1/1 = 1

      The first burglar breaks in, and splits the amount between the two boxes in the second row. So each of the 2 boxes contains 1/2 of the complete amount:

      [1] 0/1 + 2/2 = 1

      The second burglar breaks in and splits the amount from one of the boxes in the second row between 2 (of the 3) boxes in the third row:

      [2] 0/1 + 1/2 + 2/4 = 1

      The third burglar can’t burgle the remaining box in the second row, as there aren’t enough empty boxes in the third row to hide the spoils. So he splits the amount from one of the boxes in the third row into 2 (of the 4) boxes in the fourth row:

      [3] 0/1 + 1/2 + 1/4 + 2/8 = 1

      The fourth burglar can burgle the box in the second row, as there are now 2 empty boxes in the third row:

      [4] 0/1 + 0/2 + 3/4 + 2/8 = 1

      And so the process continues:

      [5] 0/1 + 0/2 + 2/4 + 4/8 = 1
      [6] 0/1 + 0/2 + 2/4 + 3/8 + 2/16 = 1
      [7] 0/1 + 0/2 + 2/4 + 2/8 + 4/16 = 1
      [8] 0/1 + 0/2 + 1/4 + 4/8 + 4/16 = 1
      [9] 0/1 + 0/2 + 1/4 + 4/8 + 3/16 + 2/32 = 1
      [10] 0/1 + 0/2 + 1/4 + 3/8 + 5/16 + 2/32 = 1
      [11] 0/1 + 0/2 + 1/4 + 3/8 + 4/16 + 4/32 = 1
      [12] 0/1 + 0/2 + 1/4 + 3/8 + 3/16 + 6/32 = 1
      [13] 0/1 + 0/2 + 1/4 + 2/8 + 5/16 + 6/32 = 1
      [14] 0/1 + 0/2 + 0/4 + 4/8 + 5/16 + 6/32 = 1

      So we’ve managed to empty the first 3 rows (in 14 burglaries).

      Can we empty the fourth row?

      What if every row below it was full? We would have:

      S = 5/16 + 6/32 + 7/64 + 8/128 + …
      S = (1/16 + 4/16) + (1/32 + 5/32) + (1/64 + 6/64) + (1/128 + 7/128) + …
      S = (1/16 + 1/32 + 1/64 + 1/128 + …) + (4/16) + (5/32 + 6/64 + 7/128 + …)
      S = (1/8)(1/2 + 1/4 + 1/8 + 1/16 + …) + (1/4) + S/2
      S = (1/8) + (1/4) + S/2
      S/2 = 3/8
      S = 3/4

      We can check this by calculating the sum the first few terms:

      >>> sum(fdiv(n + 1, 2**n) for n in irange(4, 100))
      0.75
      

      So there is not enough room to hide the entire amount using just rows 5 onwards, hence we can never empty row 4.

      Solution: (a) The first 3 rows were empty.

      The number of burglaries required to achieve any situation is one less than the number of occupied boxes.

      And as the fractions of the original collection decrease as we move down the rows we want to occupy the minimal number of boxes possible. If the first 3 rows are empty this minimal number is 4 on the 4th row, 5 on the 5th row, 6 on the 6th row (4/8 + 5/16 + 6/32 = 1), which required 4 + 5 + 6 − 1 = 14 burglaries.

      Solution: (b) There were 14 burglaries.

      Like

      • John Crabtree's avatar

        John Crabtree 9:50 pm on 13 November 2019 Permalink | Reply

        While experimenting with this, I found that one box in row 2 could be replaced by:
        i) a pyramid with one box full in row 4, two in row 5, three in row 6 etc
        or ii) one box full in row 4, and three in all lower rows
        Add the two together, and the grid would be full with two boxes full in row 4, and all all boxes in lower rows. It does not matter if this pattern can actually be reached. Row 4 cannot be empty. it is quite easy to check if the first three rows can be emptied.

        Like

  • Unknown's avatar

    Jim Randell 11:01 am on 7 April 2019 Permalink | Reply
    Tags: by: Keith Austin,   

    Brainteaser 1542: Precisely what do I mean? 

    From The Sunday Times, 29th March 1992 [link]

    There is a row of 10 boxes and each box contains one object, which is a knife, a fork or a spoon. Each of the three utensils is in at least one box. Here are five true statements to help you answer the questions below:

    1. A knife is in more boxes than a spoon is in;
    2. A spoon is in more boxes than a fork is in;
    3. A knife is not in precisely five boxes;
    4. A spoon is not in precisely three boxes;
    5. A fork is not in precisely half the number of boxes a spoon is in.

    How many boxes contain a knife?

    How many boxes contain a spoon?

    This puzzle is included in the book Brainteasers (2002), in which it appears in the following (slightly different) form:

    There is a row of ten boxes and each box contains one object, which is a knife, a fork or a spoon. There is at least one of each utensil. Here are five true statements to help you work out how many of each there are:

    • A knife is in more boxes than a spoon is in;
    • A spoon is in more boxes than a fork is in;
    • A knife is not in precisely five boxes;
    • A spoon is not in precisely three boxes;
    • A fork is not in precisely half the number of boxes a spoon is in.

    How many of each utensil are there?

    However I think both these formulations are flawed, in that under any reasonable interpretation there are no solutions. In the comments I present a variation that can be solved.

    [teaser1542]

     
    • Jim Randell's avatar

      Jim Randell 11:05 am on 7 April 2019 Permalink | Reply

      Having read the solution given in the book, I think the spirit of the puzzle can be maintained by phrasing it slightly differently:

      The National Museum of Cutlery in the small country of Ambiguity has an exhibit consisting of a row of ten boxes.

      Each box contains one object, which is either a knife, a fork or a spoon. There is at least one of each utensil.

      On a recent visit I heard five natives make the following true statements:

      1. A knife is in more boxes than a spoon is in.
      2. A spoon is in more boxes than a fork is in.
      3. A knife is not in precisely five boxes.
      4. A spoon is not in precisely three boxes.
      5. A fork is not in precisely half the number of boxes a spoon is in.

      How many of each utensil are there?

      And here is my solution:

      Suppose there are K knives, F forks and S spoons, each number has a value from 1 to 9.

      Looking at the statements:

      “A knife is in more boxes than a spoon is in”, seems to be an oddly worded way of saying: “there are more knives than spoons”:

      K > S

      Similarly: “A spoon is in more boxes than a fork is in”, seems to be saying:

      S > F

      We then have three statements of the form: “an X is not in precisely N boxes”

      I think there are two reasonable interpretations of this:

      (a) “The number of boxes containing an X, is not exactly N”

      i.e.:

      X ≠ N

      or:

      (b) “There are exactly N boxes, containing an object that is not an X”

      i.e.:

      10 − X = N

      The following Python 3 program considers these possible interpretations for the last three statements. It runs in 80ms.

      Run: [ @repl.it ]

      from enigma import (irange, join, printf)
      
      # decompose <t> into <k> increasing values
      def decompose(t, k, m=1, s=[]):
        if k == 1:
          if not (t < m):
            yield s + [t]
        else:
          for x in irange(m, t):
            yield from decompose(t - x, k - 1, x + 1, s + [x])
      
      # consider interpretations for:
      # "the number of boxes containing an <x> is <n>/<d>"
      def interpretations(x, n, d=1):
        # (a) "the number of boxes containing an <x> is exactly <n>/<d>"
        if d * x != n: yield 'a'
        # (b) "there are exactly <n>/<d> boxes containing an object that is not an <x>"
        if d * (10 - x) == n: yield 'b'
      
      # find possible values for F < S < K
      for (F, S, K) in decompose(10, 3):
        # consider possible interpretations for the last three statements
        ss = list(join(interpretations(*args)) for args in [(K, 5), (S, 3), (F, S, 2)])
        # each must have some interpretation
        if not all(ss): continue
        # output solution
        printf("F={F} S={S} K={K} {ss}")
      

      Solution: There is 1 fork, 4 spoons, 5 knives.

      The constraint (F < S < K) narrows the solution down to 4 possibilities:

      F=1, S=2, K=7
      F=1, S=3, K=6
      F=1, S=4, K=5
      F=2, S=3, K=5

      Only in the case (F=1, S=4, K=5) do the last three statements all have viable interpretations. These are:

      3. “A knife is not in precisely 5 boxes” = “There are exactly 5 boxes containing an object that is not a knife”
      4. “A spoon is not in precisely 3 boxes” = “The exact number of boxes containing a spoon is not 3”
      5. “A fork is not in precisely half the number of boxes a spoon is in” = “The exact number of boxes containing a fork is not half the exact number of boxes containing a spoon”

      or:

      3. “10 − K = 5”
      4. “S ≠ 3”
      5. “F ≠ S / 2”

      We can see each of these is a true statement, but they do not all use the same interpretation of the statement form. (Statement 3 uses the (b) interpretation. Statements 4, 5 use the (a) interpretation). This is why I changed the puzzle wording, so that the statements are made by different people. To have them made by the same person implies a consistent interpretation and gives no viable solutions.

      Like

  • Unknown's avatar

    Jim Randell 7:10 am on 30 March 2019 Permalink | Reply
    Tags: by: Keith Austin   

    Brainteaser 1510: Sum puzzle 

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

    In this addition sum nine digits are each represented by a different letter and the remaining one digit it represented in various places by nine letters.

    When the 47-figure number:

    INTHISADDITIONSUMNINEDIGITSAREEACHREPRESENTEDBY

    is added to the 46-digit number:

    ONELETTERANDONEDIGITISREPRESENTEDBYNINELETTERS

    the answer is the 47-figure number:

    TDODRRITECIPADAABSDLOTRSORANOHMIAGSAOTUDAYDOEOE

    What is the SOLUTION?

    The text of this puzzle is taken from the book Brainteasers (2002), so may differ from the originally published puzzle.

    [teaser1510]

     
    • Jim Randell's avatar

      Jim Randell 7:11 am on 30 March 2019 Permalink | Reply

      Potentially we could just feed the entire sum as a single expression to the [[ SubstitutedExpression() ]] solver from the enigma.py library, but generating and considering all the possibilities for the 15 digits in the terms of the sum is going to take some time.

      So to help it along I provided some additional expressions, which are the sums formed taking some of the columns (in pairs, to reduce the amount of typing), linked together by carries. After 9 pairs we have mentioned each letter at least once, and the solver finds the solution in less than 200ms.

      This run-file executes in 183ms.

      Run: [ @replit ]

      #! python -m enigma -rr
      
      SubstitutedExpression
      
      --distinct=""
      --answer="SOLUTION"
      
      # use lowercase symbols for carries
      --symbols="ABCDEGHILMNOPRSTUYabcdefghi"
      --invalid="0,IOT"
      --invalid="2-9,abcdefghi"
      
      # neaten up the output
      --template=""
      --solution="ABCDEGHILMNOPRSTUY"
      
      # the entire sum
      "INTHISADDITIONSUMNINEDIGITSAREEACHREPRESENTEDBY + ONELETTERANDONEDIGITISREPRESENTEDBYNINELETTERS = TDODRRITECIPADAABSDLOTRSORANOHMIAGSAOTUDAYDOEOE"
      
      # verify the digit count in the solution
      --code="v = lambda ds: sorted(ds.count(d) for d in irange(0, 9))"
      
      "v([A, B, C, D, E, G, H, I, L, M, N, O, P, R, S, T, U, Y]) == [1, 1, 1, 1, 1, 1, 1, 1, 1, 9]"
      
      # to speed things up we break down the sum in a sequence of partial
      # sums starting from the rightmost column (in groups of 2)
      "BY + RS + 0 = aOE"
      "ED + TE + a = bOE"
      "NT + ET + b = cYD"
      "SE + EL + c = dDA"
      "RE + IN + d = eTU"
      "EP + YN + e = fAO"
      "HR + DB + f = gGS"
      "AC + TE + g = hIA"
      "EE + EN + h = iHM"
      # by this stage we have used all the letters at least once
      

      Solution: SOLUTION = 78665482

      The assignment of letters to digits is:

      A=9 D=0 E=3 I=4 N=2 O=8 R=1 S=7 T=5; {BCGHLMPUY}=6

      so the sum is:

        42564790045482766242304645791339661361373253066
      +  8236355319208230464547136137325306624236355317
        -----------------------------------------------
      = 50801145364690996706851781928664967985609608383
        ===============================================
      

      This is the only solution, even if the 18 symbols are grouped together differently (i.e. not 9 symbols standing for 1 digit each, and 9 all standing for the same remaining digit).

      Like

      • Jim Randell's avatar

        Jim Randell 3:50 pm on 27 April 2023 Permalink | Reply

        Instead of splitting the sum manually we can use the [[ SubstitutedExpression.split_sum ]] solver from the enigma.py library to do it for us.

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

        Run: [ @replit ]

        #! python3 -m enigma -rr
        
        SubstitutedExpression.split_sum
        
        --distinct=""
        --invalid="0,IOT"
        --answer="SOLUTION"
        --split=4
        
        # the entire sum
        "INTHISADDITIONSUMNINEDIGITSAREEACHREPRESENTEDBY + ONELETTERANDONEDIGITISREPRESENTEDBYNINELETTERS = TDODRRITECIPADAABSDLOTRSORANOHMIAGSAOTUDAYDOEOE"
        
        # verify the digit count in the solution (9 digits have 1 symbol, 1 digit has 9 symbols)
        --code="check = lambda ds: multiset.from_seq(ds.count(d) for d in irange(0, 9)) == {1: 9, 9: 1}"
        --extra="check([A, B, C, D, E, G, H, I, L, M, N, O, P, R, S, T, U, Y])"
        
        # neaten up the output
        --template=""
        

        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