Design a site like this with WordPress.com
Get started

Updates from November, 2022 Toggle Comment Threads | Keyboard Shortcuts

  • Jim Randell 8:38 am on 24 November 2022 Permalink | Reply
    Tags: by: Dr J C Brown   

    Brain-Teaser 665: … Steady … 

    From The Sunday Times, 7th April 1974 [link]

    There were six starters for the special handicap event for the youngest class at the school sports. All started at the gun, and none fell by the wayside or was disqualified.

    Dave started quicker than Ed and finished before Fred. Colin finished two seconds after he would have finished if he had finished two seconds before Ed. Bob and Dave started quicker than Colin, and Bob was faster than Ed.

    Alf, who won third prize, finished two seconds after he would have finished if he had finished two seconds after Colin.

    Who won second prize?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser665]

     
    • Jim Randell 8:38 am on 24 November 2022 Permalink | Reply

      If the total times are A, B, C, D, E, F. Then we derive:

      D < F
      C = (E − 2) + 2 ⇒ C = E
      B < E
      A = (C + 2) + 2 ⇒ A = C + 4 (and A was 3rd)

      From which we get 2 chains (shortest to longest time):

      B < C,E < A
      D < F

      However we are told that A was in 3rd place, but we see there are at least 3 competitors (B, C, E) who finished in a shorter time, which would imply the best A could have placed was 4th.

      So, assuming positions are ranked according to time, either the puzzle is flawed, or the goal of the race is to achieve the longest time, not the shortest.

      In this latter case the finishing positions (longest to shortest time) are:

      1st: Fred
      2nd: Dave
      3rd: Alf
      4th: Colin, Ed (joint)
      6th: Bob

      Solution: Dave won second prize.

      Like

  • Jim Randell 9:37 am on 15 November 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 670: A shared legacy 

    From The Sunday Times, 12th May 1974 [link]

    When my neighbour Giles found he could no longer look after his prize herds of cattle and sheep, he planned to divide the lot among his four sons in equal shares.

    But when he started by counting up the cattle he soon found that their total was not exactly divisible by four, so he decided he would juggle with the numbers of beasts and achieve the four shares of equal value by making to the respective recipients quite arbitrary allocations of both cattle and sheep, taking into account that the value per head of the former was four times that of the latter.

    The outcome was that:

    (i) one son received 80% more beasts than another;
    (ii) two sons each received a total of beasts which equalled the aggregate total of two of the other three sons;
    (iii) one son received twice as many of one type of beast as of the other;
    (iv) only one son received over 100 beasts in all.

    How many cattle were included in this last total of over 100 beasts?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser670]

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

      If the total number of animals received by each brother is: A, B, C, D (in descending order), then (from (iv)) A is the only one to receive more than 100.

      And from (ii) two of the brothers totals are equal to the sum of the totals of two of the remaining three brothers. These two must be A and B, and so B = C + D, and A = C + 2D or 2C + D.

      This Python program starts by finding possible A, B, C, D values, and then tries to split these totals into numbers of cattle and sheep such that the remaining conditions hold.

      It runs in 62ms. (Internal runtime is 11.2ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, ediv, as_int, printf)
      
      # split total <t> into (<cattle>, <sheep>) with value <v>
      def split(t, v):
        c = ediv(v - t, 3)
        return (as_int(c, "0+"), as_int(t - c, "0+"))
      
      # solve puzzle for given totals: A, B, C, D
      def solve(A, B, C, D):
        # consider number of cattle for A
        for Ac in irange(0, A):
          As = A - Ac
          # total value for A (cattle = 4, sheep = 1)
          v = 4 * Ac + As
          # find splits for B, C, D
          try:
            (Bc, Bs) = split(B, v)
            (Cc, Cs) = split(C, v)
            (Dc, Ds) = split(D, v)
          except ValueError:
            continue
      
          # total number of cattle is not divisible by 4
          if (Ac + Bc + Cc + Dc) % 4 == 0: continue
          # someone has twice as many of one type of animal as the other
          if all(2 * x != y and x != 2 * y for (x, y) in [(Ac, As), (Bc, Bs), (Cc, Cs), (Dc, Ds)]): continue
      
          # output scenario
          printf("[v={v}] A: {Ac}c+{As}s = {A}; B: {Bc}c+{Bs}s = {B}; C: {Cc}c+{Cs}s = {C}; D: {Dc}c+{Ds}s = {D}")
      
      # total number of animals for C (not more than 100)
      for C in irange(1, 100):
        # total number of animals for D (not more than C)
        for D in irange(0, C):
          # B = C + D (not more than 100)
          B = C + D
          if B > 100: break
          # A = C + 2D or 2C + D (more than 100)
          for A in (B + D, B + C):
            if not (A > 100): continue
            # one of these values must be 1.8x a lower value
            if not any(5 * x == 9 * y for (x, y) in subsets((A, B, C, D), size=2)): continue
            # solve the puzzle
            solve(A, B, C, D)
      

      Solution: The son receiving over 100 animals received 6 cattle.

      The full solution is:

      A: 6 cattle + 111 sheep = 117 animals; 117 = 81 (B) + 36 (D)
      B: 18 cattle + 63 sheep = 81 animals; 81 = 45 (C) + 36 (D) = 1.8 × 45 (C)
      C: 30 cattle + 15 sheep = 45 animals; 30 is twice 15
      D: 33 cattle + 3 sheep = 36 animals

      Assigning values of cattle = 4, sheep = 1, each brother receives animals to a value of 135.

      In total there are 279 animals = 87 cattle (not a multiple of 4) + 192 sheep.

      Like

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

    Brain-Teaser 653: Hymn numbers 

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

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

    What were last Sunday’s numbers?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill).

    [teaser653]

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

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

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

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

      Run: [ @replit ]

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

      Solution: Psalm 149. Hymn 263. Hymn 587.

      Like

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

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

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

      Like

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

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

      Like

  • Jim Randell 8:49 am on 28 August 2022 Permalink | Reply
    Tags: by: R. K. Brown   

    Brain-Teaser 906: Truthful salesman 

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

    Seated happily one Sunday morning in my local pub, solving the Brain-teaser, I was accosted by the slightly inebriated resident bore. He said: “I have a puzzle for you which concerns seven people who were either engineers or salesmen and, of course, salesmen always tell the truth and engineers always lie. I will make four statements from which you must deduce how many salesmen there are:

    1. B and E are salesmen;
    2. A and C have different occupations;
    3. D says that G is an engineer;
    4. A says that B declares that C insists that D asserts that E affirms that F states that G is a salesman”.

    I said: “That’s a very old one”, and told him the answer. Grinning, he then replied, “Yes, that would be the correct answer if all the four statements were true, but it is not the correct answer because I intentionally made one of the four statements false. Further, if were to tell you how many salesmen there were under these new circumstances, you would be able to tell me which statement was false”.

    “Ah”, I said, “that’s a much more interesting problem”. I thought for a moment and indeed I was able to tell him not only which statement was false but also how many salesmen there really were.

    Which statement was false and how many salesmen were there really?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser906]

     
    • Jim Randell 8:50 am on 28 August 2022 Permalink | Reply

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

      Run: [ @replit ]

      from collections import defaultdict
      from enigma import (subsets, peek, printf)
      
      # salesman or engineer?
      jobs = (Sales, Eng) = (True, False)
      
      # does X make statements s?
      check = lambda X, s: (X == s)
      
      # map number of salesmen to index of false statement
      m = defaultdict(set)
      
      # choose jobs for A - G
      for (A, B, C, D, E, F, G) in subsets(jobs, size=7, select="M"):
      
        # evaluate the statements:
        ss = [
          # 1. "B and E are salesmen"
          (B == E == Sales),
          # 2. "A and C have different occupations"
          (A != C),
          # 3. "D says that G is an engineer"
          check(D, G == Eng),
          # 4. "A says that B declares that C insists that D asserts that E affirms that F states that G is a salesman
          check(A, check(B, check(C, check(D, check(E, check(F, G == Sales)))))),
        ]
      
        # exactly one statement is false
        if ss.count(False) == 1:
          m[[A, B, C, D, E, F, G].count(Sales)].add(ss.index(False))
      
      # look for unique values
      for (k, vs) in m.items():
        if len(vs) == 1:
          v = peek(vs) + 1
          printf("{k} salesmen -> statement {v} is false")
      

      Solution: The false statement is number 4. And there are 4 salesmen.

      And there are 4 possible arrangements of salesmen:

      A, B, D, E
      A, B, E, G
      B, C, D, E
      B, C, E, G


      If all statements are true then there are 5 salesmen. The possible arrangements are:

      A, B, D, E, F
      A, B, E, F, G
      B, C, D, E, F
      B, C, E, F, G

      Like

    • John Crabtree 3:19 am on 30 August 2022 Permalink | Reply

      I was introduced to the original puzzle in Paul Nahin’s book, “The Logician and Engineer”, Princeton University Press, 2013, pp 58-59 and 65-66. He gave these references:
      i) D.M. McCallum and J.B. Smith, “Mechanized Reasoning: Logical Computers and their Design”, Electronic Engineering (a UK magazine), April 1951, pp 126-133 (page nos. not given by Nahin)
      ii) C. E. Shannon, “Computers and Automata”, Proc. of the Institute of Radio Engineers (U.S.A.), Oct 1953, pp 1234-1241. Nahin notes that there was a typo in Shannon’s representation of McCallum and Smith’s problem. I believe that it does not affect the nature of, or the solution to, the problem.

      A asserts B may be written as A XNOR B = 1, ie both true or both false.
      St. 4, if true, may be written as A XNOR (B XNOR (C XNOR (D XNOR (E XNOR (F XNOR G))))) = 1
      And so A XNOR B XNOR C XNOR D XNOR E XNOR F XNOR G = 1
      X XNOR Y XNOR Z = 1 is equivalent to X XOR Y XOR Z = 1
      And so St. 4, if true, may be written as
      A XOR B XOR C XOR D XOR E XOR F XOR G = 1
      This is familiar to electrical engineers as the expression for a half-adder circuit ie binary addition ignoring the carry. And so if St. 4 is true, there are an odd number of salesman, and if it is false there are an even number of salesmen. (This method is not given by Nahin).

      If all of the statements are true, there are 5 salesmen including F. If St. 1 is false, there are 3 salesmen. If Sts. 2 or 3 are false, there can be 3 or 5 salesmen.

      And so St. 4 is false and there are 4 salesmen (with F being an engineer).

      Like

  • Jim Randell 9:29 am on 16 August 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 732: … a Frenchman, an Italian, … 

    From The Sunday Times, 27th July 1975 [link]

    The staff of a certain international organisation, in their day-to-day work, use four languages — English, French, German and Italian — and every employee is required to be fluent in two of them, viz. his or her own, and one of the other three.

    The four heads of branches of the Circumlocution Division are of different nationalities; their second languages are also all different. Each has a woman secretary whose second language is the native language of her chief, but in no case is either language of the chief the native language of his secretary.

    All eight are colleagues of mine. John and Mary are English, Jules and Adèle French, Otto and Heidi German, and Nino and Gina Italian.

    The man of the same nationality as John’s secretary has German as his second language.

    The secretary of the man whose native language is Jules’s second language is of the same nationality as Heidi’s chief.

    Who is Jules’s secretary? And which man has Italian as his second language?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill).

    [teaser732]

     
    • Jim Randell 9:30 am on 16 August 2022 Permalink | Reply

      This Python program runs in 58ms. (Internal run time is 649µs).

      Run: [ @replit ]

      from enigma import (subsets, update, singleton, map2str, printf)
      
      # languages (English, French, German, Italian)
      langs = ['en', 'fr', 'de', 'it']
      
      # chiefs and secretaries (in language order)
      chiefs = ['John', 'Jules', 'Otto', 'Nino']
      secs = ['Mary', 'Adele', 'Heidi', 'Gina']
      
      # lang1: map names to first languages
      lang1 = dict(zip(chiefs + secs, langs + langs))
      # nat: map first language (nationality) to chief
      nat = dict(zip(langs, chiefs))
      
      # sec: maps chief -> secretary
      # chief: maps secretary -> chief
      for ss in subsets(secs, size=len, select="P"):
        sec = dict(zip(chiefs, ss))
        chief = dict(zip(ss, chiefs))
        # chiefs and secretaries do not share first languages
        if any(lang1[k] == lang1[v] for (k, v) in sec.items()): continue
      
        # lang2: map names to second languages
        # the second language of the secretary is the first language of the chief
        lang2a = dict((v, lang1[k]) for (k, v) in sec.items())
      
        # assign second languages to the chiefs (different from first languages)
        for vs in subsets(langs, size=len, select="D"):
          lang2 = update(lang2a, chiefs, vs)
          # the second language of the chief is not the first language of the secretary
          if any(lang2[k] == lang1[v] for (k, v) in sec.items()): continue
      
          # check conditions:
          # "The man of the same nationality as John's secretary, has German as his second language"
          if not (lang2[nat[lang1[sec['John']]]] == 'de'): continue
      
          # "The secretary of the man whose native language is Jules's
          # second language is of the same nationality as Heidi's chief"
          if not (lang1[sec[nat[lang2['Jules']]]] == lang1[chief['Heidi']]): continue
      
          # output solution(s), and maps
          printf("sec[Jules] = {x}; lang2[{y}] = it", x=sec['Jules'], y=singleton(y for y in chiefs if lang2[y] == 'it'))
          printf("-> sec = {sec}", sec=map2str(sec, sort=0))
          printf("-> lang2 = {lang2}", lang2=map2str(lang2, sort=0))
          printf()
      

      Solution: Mary is Jules’ secretary. John has Italian as a second language.

      The complete assignments are:

      John (English + Italian) & Adèle (French + English)
      Jules (French + German) & Mary (English + French)
      Otto (German + French/English) & Gina (Italian + German)
      Nino (Italian + English/French) & Heidi (German + Italian)

      Otto and Nino’s second languages are English and French in some order.

      Like

    • GeoffR 7:05 pm on 16 August 2022 Permalink | Reply

      Circumlocution looks like an interesting reference to Charles Dickens.

      The Circumlocution Office, a fictitious governmental department featured in the Charles Dickens novel Little Dorrit. Dickens took an existing word in the English language, circumlocution, and applied it to satirise that department.

      The word circumlocution describes the use of an unnecessarily amount of words to get to the point, where just a few would do.

      Like

    • NigelR 1:42 pm on 17 August 2022 Permalink | Reply

      I think I now need a lie down with a cold towel round my head after keeping track of nested dictionaries! For some reason my shell (Thonny) crashes when I run the enigma timer.

      from itertools import permutations as perm
      #timer.start()
      def test():    
          y=[z for z in Chiefs if Chiefs[z][0]==Chiefs['Jules'][1]][0] #Chief with nat of Jules 2nd lang (aka y)
         #y's secretary is [Chiefs[y][2]] and her nationality is Secs[Chiefs[y][2]][0])       
          for x in Chiefs:
              if Secs[Chiefs[x][2]][0]==Chiefs[x][0]:return False #check Sec native lang against chief nat.
              if Secs[Chiefs[x][2]][1]!=Chiefs[x][0]:return False # check Sec 2nd is chief's nat
              if Chiefs[x][0] == Secs[Chiefs[x][2]][0] or Chiefs[x][1] == Secs[Chiefs[x][2]][0]:return False # check neither sec lang is chief's nat
      #The man of the same nationality as John’s secretary has German as his second language
              if Chiefs[x][0]==Secs[Chiefs['John'][2]][0] and Chiefs[x][1]!='Ge':return False 
      #The secretary of the man whose native language is Jules’s second language is of the same nationality as Heidi’s chief
              if Chiefs[x][2]=='Heidi' and Secs[Chiefs[y][2]][0]!=Chiefs[x][0]:return False            
          return True
              
      Chief = ['John','Jules','Otto','Nino']
      Sec = ['Mary','Adele','Heidi','Gina']
      Nat = ['En','Fr','Ge','It']
      Chiefs={Chief[i]:[Nat[i],0,0] for i in range(4)}
      Secs = {Sec[i]:[Nat[i],0] for i in range(4)}
      
      for Clang in perm(Nat): #generate second languages for Chiefs
          for i, x in enumerate(Chiefs):
              Chiefs[x][1] = Clang[i]
          if any([i for i in Chiefs if Chiefs[i][0]==Chiefs[i][1]]):continue # second lang cannot be same as first
          for Slang in perm(Nat): #generate second language for secretaries
              for i, x in enumerate(Secs):
                  Secs[x][1] = Slang[i]
              if any([i for i in Secs if Secs[i][0]==Secs[i][1]]): continue  # second lang cannot be same as first     
              for Sal in perm(Secs):
                  for i , x  in enumerate(Chiefs):
                      Chiefs[x][2] = Sal[i]  #allocate secretaries to chiefs
                  if not test():continue
                  #Who is Jules’s secretary? And which man has Italian as his second language?
                  I2l = [i for i in Chiefs if Chiefs[i][1]=='It'][0]
                  print('Jules secretary is', Chiefs['Jules'][2],'.', I2l, 'has Italian as second language'   )
                  print (Chiefs)
      #timer.stop()
      #timer.report()
      

      Like

    • GeoffR 3:27 pm on 17 August 2022 Permalink | Reply

      @NigelR:

      Excellent work.

      I added ‘from enigma import timer’ to your code after the permutation import. (Before timer.start() )

      This worked OK for me in the Idle interface

      [timing] total time: 0.0838237s (83.82ms)

      Like

    • Frits 6:03 pm on 17 August 2022 Permalink | Reply

         
      from itertools import product
      
      # languages (English, French, German, Italian)
      langs = ['en', 'fr', 'de', 'it']
       
      # chiefs and secretaries (in language order)
      chiefs = ['John', 'Jules', 'Otto', 'Nino']
      secs = ['Mary', 'Adele', 'Heidi', 'Gina']
      
      # options per chief
      lst = [[] for _ in range(4)]
      
      # generate all combinations of chief, secretary and chief's second language
      for chf, sec, c_lang_2 in product(*[chiefs, secs, langs]):
        chf_i = chiefs.index(chf)
        c_lang_1 = langs[chf_i]
        s_lang_1, s_lang_2 = langs[secs.index(sec)], c_lang_1
        # in no case is either language of the chief 
        # the native language of his secretary
        if c_lang_1 == c_lang_2 or s_lang_1 == s_lang_2 or \
           s_lang_1 in {c_lang_1, c_lang_2}: continue
        
        lst[chf_i].append([c_lang_1, c_lang_2, sec, s_lang_1, s_lang_2])
      
      # check all combinations of options per chief
      for p in product(*lst):
        # chiefs have different secretaries and ...
        if len(set(chf[2] for chf in p)) != 4: continue
        
        # ... their second languages are also all different
        if len(set(chf[1] for chf in p)) != 4: continue
        
        Jo, Ju, _, _ = p
        
        # the man of the same nationality as 
        # John's secretary has German as his second language
        if [chf for chf in p[1:] if chf[0] == Jo[3]][0][1] != 'de':
          continue
            
        # The secretary of the man whose native language 
        # is Jules's second language is of the same nationality as Heidi's chief
        lang_sec = [chf[3] for i, chf in enumerate(p) 
                   if i != 1 and chf[0] == Ju[1]][0]
        lang_chf_H = [chf[0] for chf in p if chf[2] == 'Heidi'][0]
        if lang_sec != lang_chf_H: continue
        
        for z in zip(chiefs, p):
          print(z)
        print(f"Jules' secretary: {Ju[2]}, John's second language: {Jo[1]}")
        print()
      

      Like

  • Jim Randell 9:17 am on 26 July 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 727: Right hand 

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

    When South, who was bored with being dummy, had left the card room of the Logic Club for good, West extracted four kings, three queens and two jacks from the pack, showed them round, shuffled them, and dealt three cards to each of the three.

    “Forget the suits”, he said. “Let each of us look at his own three cards and see if he can make a sure statement about any kings, queens or jacks in the next man’s hand; we will use as evidence what we see in our own hands and what we hear each other say.

    They played with the full rigours of logic. West began by announcing that he could say nothing about North’s hand; North then said that he could say nothing about East’s hand; thereupon East said that he could deduce the value of one card — and one only — in West’s hand.

    What can you say about the cards that were dealt to East?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill).

    [teaser727]

     
    • Jim Randell 9:18 am on 26 July 2022 Permalink | Reply

      There are 9 possible hands that could be dealt to W, and in each case W would be able to say something about the cards that are dealt to N:

      KKK → N holds at most 1K
      KKQ → N holds at most 2K, at most 2Q
      KKJ → N holds at most 2K, at most 1J
      KQQ → N holds at most 1Q
      KQJ → N holds at most 2Q, at most 1J
      KJJ → N holds no J
      QQQ → N holds at least 1K, no Q
      QQJ → N holds at least 1K, at most 1Q, at most 1J
      QJJ → N holds at least 1K, at most 2Q, no J

      So let’s use a tighter definition that each must declare if they know for certain cards that are held by the next player.

      This reduces the list to:

      QQQ → N holds at least 1K
      QQJ → N holds at least 1K
      QJJ → N holds at least 1K

      And this allows us to find an answer to the puzzle.

      This Python program runs in 58ms. (Internal runtime is 1.2ms).

      Run: [ @replit ]

      from collections import defaultdict
      from enigma import (multiset, join, printf)
      
      # format a hand
      fmt = lambda x: join(x).upper()
      
      # deal groups of <k> cards from <cards>
      def deal(cards, k, ss=[]):
        if not cards:
          yield ss
        else:
          # choose the first hand
          for hand in cards.subsets(size=3):
            yield from deal(cards.difference(hand), k, ss + [tuple(hand.sorted())])
      
      # initial cards
      cards = multiset(K=4, Q=3, j=2)
      values = sorted(cards.keys())
      
      # possible deals
      hands = list(deal(cards, 3))
      
      # look at W's hand, record the number of Ks, Qs, Js in N's
      w = defaultdict(lambda: defaultdict(set))
      for (W, N, E) in hands:
        for k in values:
          w[W][k].add(N.count(k))
      
      # W can't be sure of any cards held by N
      Ws = set(W for (W, d) in w.items() if all(0 in d[k] for k in values))
      printf("[W = {Ws}]", Ws=join(map(fmt, Ws), sep=", ", enc="{}"))
      
      # look at N's hand, record the number of Ks, Qs, Js in E's
      n = defaultdict(lambda: defaultdict(set))
      for (W, N, E) in hands:
        if W not in Ws: continue
        for k in values:
          n[N][k].add(E.count(k))
      
      # N can't be sure of any cards held by E
      Ns = set(N for (N, d) in n.items() if all(0 in d[k] for k in values))
      printf("[N = {Ns}]", Ns=join(map(fmt, Ws), sep=", ", enc="{}"))
      
      # look at E's hand, record the number of Ks, Qs, Js in W's
      e = defaultdict(lambda: defaultdict(set))
      for (W, N, E) in hands:
        if W not in Ws or N not in Ns: continue
        for k in values:
          e[E][k].add(W.count(k))
      
      # E can be sure of exactly 1 card held by W
      for (E, d) in e.items():
        if sorted(min(v) for v in d.values()) == [0, 0, 1]:
          printf("E = {E}", E=fmt(E))
      

      Solution: East held a King, a Queen, and a Jack.

      Like

      • John Crabtree 6:50 pm on 26 July 2022 Permalink | Reply

        In manually reaching the same solution, I concluded that West must hold at least one King, and that North must hold at least one King and one Queen. That gives three possible sets of hands.
        Do you agree? Thanks.

        Like

        • Jim Randell 8:20 am on 27 July 2022 Permalink | Reply

          @John: Yes, I found three possible sets of hands:

          W = KQJ; N = KKQ; E = KQJ
          W = KKJ; N = KQQ; E = KQJ
          W = KKQ; N = KQJ; E = KQJ

          Like

  • Jim Randell 10:32 am on 5 July 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 731: A little list 

    From The Sunday Times, 20th July 1975 [link]

    (1) In this list there is a true statement and a false statement whose numbers add up to give the number of a false statement.

    (2) Either statement 4 is false or there are three  consecutive true statements.

    (3) The number of the last false statement subtracted from the product of the numbers of the first and last true statements gives the number of a statement which is false.

    (4) The number of even-numbered true statements equals the number of false statements.

    (5) One of the first and last statements is true and the other is false.

    (6) When I first sent this problem to the editor, thanks to a typing error no sixth statement was included. However the answer to the following question was the same then as it is now:

    Which statements are false?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser724]

     
    • Jim Randell 10:33 am on 5 July 2022 Permalink | Reply

      This Python program runs in 62ms. (Internal runtime is 680µs).

      Run: [ @replit ]

      from enigma import (subsets, filter2, irange, first, product, tuples, icount, printf)
      
      # solve the puzzle with k (= 5 or 6) statements, considering only the first 5 statements
      def solve(k):
      
        # consider the puzzle with k statements
        for ss in subsets((True, False), size=k, select="M"):
          # check the first 5 statements
          (s1, s2, s3, s4, s5) = first(ss, 5)
          (ts, fs) = filter2((lambda n: ss[n - 1]), irange(1, k))
      
          # 1: "in this list there is a true statement and a false statement
          # whose numbers add up to give the number of a false statement"
          if not (s1 == any(x + y in fs for (x, y) in product(ts, fs))): continue
      
          # 2: "either statement 4 is false, or there are three consecutive
          # true statements"
          if not (s2 == ((s4 is False) != (any(all(xs) for xs in tuples(ss, 3))))): continue
      
          # 3: "the number of the last false statement subtracted from the
          # product of the numbers of the first and last true statements gives
          # the number of a statement which is false"
          if not (s3 == (ts and fs and ts[0] * ts[-1] - fs[-1] in fs)): continue
      
          # 4: "the number of even numbered true statements equals the number
          # of false statements"
          if not (s4 == (icount(n for n in ts if n % 2 == 0) == len(fs))): continue
      
          # 5: "one of the first and last statements is true and the other is false"
          if not (s5 == (s1 != ss[-1])): continue
      
          # return the numbers of the false statements
          yield fs
      
      # look for solutions for the first 5 statements of the 6 statement
      # version of the puzzle, grouped by the value of statement 6
      (fs6t, fs6f) = filter2((lambda fs: 6 not in fs), solve(6))
      
      # if statement 6 is false, we don't have to worry about the 5 statement version
      printf("false statements = {fs6f}")
      
      # if statement 6 is true, the solution(s) must be the same as the 5 statement version
      fs5 = list(solve(5))
      if fs5 == fs6t:
        printf("false statements = {fs6t}")
      

      Solution: Statements 3, 4, 6 are false.

      So statements 1, 2, 5 are true.

      1. (true) “in this list there is a true statement and a false statement whose numbers add up to give the number of a false statement”.
      There are two: 1 + 3 = 4, 2 + 4 = 6.

      2. (true) “either statement 4 is false, or there are three consecutive true statements”.
      4 is false; there are not three consecutive true statements.

      3. (false) “the number of the last false statement subtracted from the product of the numbers of the first and last true statements gives the number of a statement which is false”.
      1×5 − 6 = −1, is not the number of a statement.

      4. (false) “the number of even numbered true statements equals the number of false statements”.
      There is 1 even numbered true statement, and there are 3 false statements.

      5. (true) “one of the first and last statements is true and the other is false”.
      1 is true; 6 is false.

      6. (false) “When I first sent this problem to the editor, thanks to a typing error no sixth statement was included. However the answer to the following question was the same then as it is now: Which statements are false”.
      The first part could be false (there was no error). But if it was true there are no solutions given only the first 5 statements, so the answer to the puzzle could not be the same if the 6th statement was included.

      Like

  • Jim Randell 9:54 am on 28 June 2022 Permalink | Reply
    Tags: by: M C Breach   

    Brain-Teaser 724: Counting sheep … 

    From The Sunday Times, 1st June 1975 [link]

    In a primitive eastern country a shepherd was counting his
    sheep into four separate pens. In the first were 75 sheep, so he wrote in his records:

    In the second were 255 and he wrote:

    In the third were 183 and he wrote:

    After counting the sheep in the fourth pen he wrote:

    How many sheep were in the fourth pen?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser724]

     
    • Jim Randell 9:55 am on 28 June 2022 Permalink | Reply

      I think this puzzle is a bit open ended.

      I called the symbols A (for the triangle), D (for the box), and X (for the loop).

      For all we know DXA, ADX, XAD could be words that happen to mean 75, 255, 183.

      Or it could be some system with strange rules (like roman numerals, for instance).

      So, I made some assumptions:

      I assumed each symbol stands for a different non-negative integer value (but the same value whenever it appears). And as the same symbols appear in a different order for each of the given values I assumed position is important (as it is in our decimal system).

      My first thought was maybe it was just a substituted base system where each digit is represented by one of the symbols.

      For 255 to be represented by 3-digits the base must be 7 – 15.

      For 75 to be represented by 3-digits the base must be: 5 – 8.

      So we can look at values 75, 255, 183 in base 7 and 8:

      base 7: 75 → “135”; 255 → “513”; 183 → “351”
      base 8: 75 → “113”; 255 → “377”; 183 → “267”

      Only for the case of base 7 do the same 3 digits appear in different orders.

      And in this case the substitution: A=5, D=1, X=3, gives us:

      DAX = 153 [base 7] = 87 [base 10]

      So: 87 is certainly a possible answer (and it is the published answer).


      But there are other solutions where the positions represent different values, but not necessarily increasing powers of some base.

      For instance instead of the positions standing for (49, 7, 1) and the symbols being A=5, D=1, X=3, we swap them over to give:

      positions = (1, 5, 3); A=7, D=49, X=1
      DXA = 49×1 + 1×5 + 7×3 = 75
      ADX = 7×1 + 49×5 + 1×3 = 255
      XAD = 1×1 + 7×5 + 49×3 = 183
      DAX = 49×1 + 7×5 + 1×3 = 87

      But we can also use decreasing position values:

      positions = (39, 11, 7); A=6, D=0, X=3
      DXA = 0×39 + 3×11 + 6×7 = 75
      ADX = 6×39 + 0×11 + 3×7 = 255
      XAD = 3×39 + 6×11 + 0×7 = 183
      DAX = 0×39 + 6×11 + 3×7 = 87

      Note: this combination of positions is able to express all numbers greater than 59.

      positions = (117, 33, 21); A=2, D=0, X=1 (Note: symbols are 0, 1, 2):
      DXA = 0×117 + 1×33 + 2×21 = 75
      ADX = 2×117 + 0×33 + 1×21 = 255
      XAD = 1×117 + 2×33 + 0×21 = 183
      DAX = 0×117 + 2×33 + 1×21 = 87

      Note: this combination of positions can only be used to express multiples of 3.

      Or mixed positions:

      positions = (7, 31, 19); A=1, D=8, X=0
      DXA = 8×7 + 0×31 + 1×19 = 75
      ADX = 1×7 + 8×31 + 0×19 = 255
      XAD = 0×7 + 1×31 + 8×19 = 183
      DAX = 8×7 + 1×31 + 0×19 = 87

      Note: this combination of positions is able to express all numbers greater than 86.

      And we can also find viable answers of 159 or 267.

      positions = (31, 19, 7); A=8, D=0, X=1
      DXA = 0×31 + 1×19 + 8×7 = 75
      ADX = 8×31 + 0×19 + 1×7 = 255
      XAD = 1×31 + 8×19 + 0×7 = 183
      DAX = 0×31 + 8×19 + 1×7 = 159

      positions = (33, 21, 117); A=0, D=1, X=2 (Note: symbols are 0, 1, 2)
      DXA = 1×33 + 2×21 + 0×117 = 75
      ADX = 0×33 + 1×21 + 2×117 = 255
      XAD = 2×33 + 0×21 + 1×117 = 183
      DAX = 1×33 + 0×21 + 2×117 = 267

      These values can also be found by permuting the (49, 7, 1) positions of the base 7 representation.

      Like

      • Jim Randell 11:13 pm on 28 June 2022 Permalink | Reply

        Here is a simple program using the [[ SubstitutedExpression ]] solver from the enigma.py library to solve the puzzle as a standard positional base system (which is how the puzzle is intended).

        It runs in 65ms.

        from enigma import (SubstitutedExpression, map2str, printf)
        
        # consider bases 7 and 8
        for b in [7, 8]:
          # create the alphametic puzzle
          p = SubstitutedExpression(
            [ "DXA == 75", "ADX == 255", "XAD == 183" ],
            base=b,
            answer="DAX",
            verbose=0,
          )
          # solve it
          for (s, ans) in p.solve():
            # output solution
            printf("DAX={ans} [b={b}; {s}]", s=map2str(s, enc=""))
        

        Like

    • GeoffR 10:06 am on 29 June 2022 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      % test for numerical bases 7 and 8 only, as Jim's analysis
      % i.e. symbols A (for the triangle), D (for the box), and X (for the loop)
      
      var 1..7:D; var 1..7:A; var 1..7:X;
      
      constraint  % base 7
      (D < 7 /\ A < 7 /\ X < 7 
      /\ (7 * 7 * D + 7 * X + A == 75) 
      /\ (7 * 7 * A + 7 * D + X == 255) 
      /\ (7 * 7 * X + 7 * A + D == 183))
      \/  % base 8
      (  (8 * 8 * D + 8 * X + A == 75) 
      /\ (8 * 8 * A + 8 * D + X == 255)
      /\ (8 * 8 * X + 8 * A + D == 183));
      
      solve satisfy;
      
      output ["[D, A, X ] = " ++ show([D, A, X ]) ];
      
      % [D, A, X ] = [1, 5, 3]  
      % ----------
      % ==========
      % DXA = 135, ADX = 513, XAD = 351, DAX = 153 (base 7)
      % DAX (base 7) = 1*7*7 + 5*7 + 3 = 87 ( base 10)
      
      
      

      Like

  • Jim Randell 11:16 am on 21 June 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 719: Wotta-Woppa! 

    From The Sunday Times, 27th April 1975 [link]

    On the Island of Imperfection there are three tribes; the Pukkas who always tell the truth, the Wotta-Woppas who never tell the truth, and the Shilli-Shallas who make statements which are alternately true and false, or false and true. This story is about three inhabitants; Ugly, Stupid and Toothless, whose names give some indication of the nature of their imperfections.

    The island currency is a Hope. The weekly wage of a Pukka is between 10 and 19 Hopes, of a Shilli-Shalla between 20 and 29, and of a Wotta-Woppa between 30 and 39 (in each case a whole number of Hopes). They make three statements each, anonymously:

    A says: (1) B is a Wotta-Woppa. (2) My wages are 25% less than 20% more than one of the others. (3) I get 10 hopes more than B.

    B says: (1) The wages of one of us is different from the sum of those of the other two. (2) We all belong to the same tribe. (3) More of C‘s statements are true than mine.

    C says: (1) Ugly earns more than Toothless. (2) The wages of one of us are 15% less than the wages of another. (3) Stupid is a Shilli-Shalla.

    Find C‘s name, tribe and wages.

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser718]

     
    • Jim Randell 11:17 am on 21 June 2022 Permalink | Reply

      This is similar to Puzzle 15 (and Puzzle 66).

      Some obvious initial analysis:

      Statement B1 (“The wages of one of us is different from the sum of those of the other two”) must always be true, as it it not possible for each of the three to have wages that are equal to the sum of the other two. (Who would have the largest value?)

      So B cannot be a Wotta-Woppa. (And so A1 is false).

      Which means B3 must be also be true. So C must make more true statements than B, so B cannot be a Pukka. Hence B is a Shilli-Shalla, and C is a Pukka. (And B2 is false).

      And so C3 must be true, and “Stupid” is a Shilli-Shalla.

      And as A1 is false, then A cannot be a Pukka, and A3 must also be false.

      I incorporated these findings into a generic solution to get a program that runs in 71ms.

      Run: [ @replit ]

      from enigma import (subsets, tuples, irange, product, div, intersect, printf)
      
      # Pukka - always tell the truth
      def P(ss):
        return all(ss)
      
      # Wotta-Woppa - never tells the truth
      def W(ss):
        return not any(ss)
      
      # Shilli-Shalla - alternates between true and false
      def S(ss):
        return all(a != b for (a, b) in tuples(ss, 2))
      
      # wages, by tribe
      wage = { P: irange(10, 19), S: irange(20, 29), W: irange(30, 39) }
      
      # assign tribes to the participants
      for ts in ((W, S, P), (S, S, P)):
        (A, B, C) = ts
      
        # B's statements
        Bs = [True, False, True]
        assert B(Bs)
      
        # assign names to the participants
        for ns in subsets("STU", size=3, select="P"):
          (nA, nB, nC) = ns
          if nC == 'S': continue  # not possible
          (iS, iT, iU) = (ns.index(x) for x in "STU")
      
          # assign wages to the participants
          for ws in product(wage[A], wage[B], wage[C]):
            (wA, wB, wC) = ws
      
            # check A's statements
            As = [False, div(10 * wA, 9) in {wB, wC}, wA == wB + 10]
            if not A(As): continue
      
            # check C's statements
            Cs = [
              ws[iU] > ws[iT],
              ts[iS] is S,
              bool(intersect([{ div(85 * x, 100) for x in ws }, ws])),
            ]
            if not C(Cs): continue
      
            # check B's 3rd statement
            assert Cs.count(True) > Bs.count(True)
      
            # output solution
            f = lambda x: x.__name__
            printf("A={A} {nA} {wA}; B={B} {nB} {wB}; C={C} {nC} {wC} [{As}; {Bs}; {Cs}]", A=f(A), B=f(B), C=f(C))
      

      Solution: C is Toothless, he is a Pukka, his wage is 17 Hopes.

      A is Ugly, he is a Wotta-Woppa, his wage is 31 – 39 Hopes.

      B is Stupid, he is a Shilli-Shalla, his wage is 20 Hopes.

      C‘s wage (17) is 15% less than that of B (20).

      Like

      • Frits 3:04 pm on 21 June 2022 Permalink | Reply

        @Jim, You swapped the ranges of Wotta-Woppa and Shilli-Shalla.

        Like

      • Jim Randell 12:25 pm on 27 June 2022 Permalink | Reply

        Inspired by Frits’ solution (below) I wrote a solution that uses the [[ SubstitutedExpression ]] solver from the enigma.py library, and is just a restatement of the puzzle text with no analysis.

        The values assigned for the tribes make it easy to express the wages alphametically.

        This Python program runs in 73ms.

        Run: [ @replit ]

        from enigma import (SubstitutedExpression, irange, tuples, div, printf)
        
        #    statements  tribe  name  wage
        # A: a d g       p      x     ps
        # B: b e h       q      y     qt
        # C: c f i       r      z     ru
        #
        # statements: 0 (false), 1 (true)
        # tribe: 1 (P), 2 (SS), 3 (WW)
        # name: 1 (S), 2 (T), 3 (U)
        # wage (units): 0 - 9
        
        # check statements for a tribe
        def tribe(t, ss):
          if t == 1: return all(ss)
          if t == 2: return all(a != b for (a, b) in tuples(ss, 2))
          if t == 3: return not any(ss)
        
        # B1: one of the wages is different from the sum of the other two
        def B1(*ws):
          t = sum(ws)
          return any(w != t - w for w in ws)
        
        # C1: Ugly (3) earns more than Toothless (2)
        def C1(ns, ws):
          d = dict(zip(ns, ws))
          return d[3] > d[2]
        
        # C2: the wages of one of us is 85% the wages of another
        def C2(*ws):
          for w in ws:
            x = div(85 * w, 100)
            if x in ws: return True
          return False
        
        # expressions to evaluate
        exprs = [
          # tribes and statements
          "tribe({p}, [{a}, {d}, {g}])",
          "tribe({q}, [{b}, {e}, {h}])",
          "tribe({r}, [{c}, {f}, {i}])",
        
          # A1: "B is a Wotta-Woppa (3)" = {a}
          "int({q} == 3) = {a}",
          # A2: "My wages are [0.9] one of the others" = {d}
          "int(div({ps} * 10, 9) in { {qt}, {ru} }) = {d}",
          # A3: "I get 10 Hopes more than B" = {g}
          "int({ps} == {qt} + 10) = {g}",
          # B1: "The wages of one of us is different from the sum of the wages
          # of the other two" = {b}
          "int(B1({ps}, {qt}, {ru})) = {b}",
          # B2: "We all belong the same tribe" = {e}
          "int({p} == {q} == {r}) = {e}",
          # B3: "More of C's statements are true than mine" = {h}
          "int({c} + {f} + {i} > {b} + {e} + {h}) = {h}",
          # C1: "Ugly earns more than Toothless" = {c}
          "int(C1([{x}, {y}, {z}], [{ps}, {qt}, {ru}])) = {c}",
          # C2: "The wages of one of us are 0.85 the wages of another" = {f}
          "int(C2({ps}, {qt}, {ru})) == {f}",
          # C3: "Stupid (1) is a Shilli-Shalla (2)" = {i}
          "int({ {x}: {p}, {y}: {q}, {z}: {r} }[1] == 2) = {i}",
        ]
        
        # invalid digit / symbol assignments
        d2i = dict()
        for d in irange(0, 9):
          vs = set()
          if d > 1: vs.update('abcdefghi')
          if d < 1 or d > 3: vs.update('pqrxyz')
          d2i[d] = vs
        
        # create the puzzle
        p = SubstitutedExpression(
          exprs,
          distinct="xyz",
          d2i=d2i,
          env=dict(tribe=tribe, B1=B1, C1=C1, C2=C2),
          verbose=0,
        )
        
        # solve the puzzle and output solutions
        Ts = { 1: "Pukka", 2: "Shilli-Shalla", 3: "Wotta-Woppa" }
        Ns = { 1: "Stupid", 2: "Toothless", 3: "Ugly" }
        for s in p.solve():
          (a, b, c, d, e, f, g, h, i, p, q, r, s, t, u, x, y, z) = (s[k] for k in 'abcdefghipqrstuxyz')
          printf("A: {N:9s}  {T:13s}  {p}{s}  [{a} {d} {g}]", N=Ns[x], T=Ts[p])
          printf("B: {N:9s}  {T:13s}  {q}{t}  [{b} {e} {h}]", N=Ns[y], T=Ts[q])
          printf("C: {N:9s}  {T:13s}  {r}{u}  [{c} {f} {i}]", N=Ns[z], T=Ts[r])
          printf()
        

        Like

        • Frits 1:31 pm on 29 June 2022 Permalink | Reply

          Nice idea to let tribes go from 1 to 3 so you can use the tribe value as a tens unit of the wage.

          Like

    • Frits 3:30 pm on 21 June 2022 Permalink | Reply

      For all tribes the veracity of the first and third statement must be the same.

         
      from enigma import SubstitutedExpression
      
      # tribes 0 = Wotta-Woppa, 1 = Shilli-Shilla, 2 = Pukka 
      # names: 0 = Stupid, 1 = Toothless, 2 = Ugly
      
      #     stmnts  tribe  wages   name
      #      0/1    0/1/2         0/1/2
      # A:  D E D     P     UV      K
      # B:  F G F     Q     WX      L
      # C:  H I H     R     YZ      M
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          # if Wotta-Woppa all statements are false
          "P != 0 or D == E == 0",
          "Q != 0 or F == G == 0",
          "R != 0 or H == I == 0",
          
          # if Shilli-Shalla all statements are alternating true/false
          "P != 1 or D + E == 1",
          "Q != 1 or F + G == 1",
          "R != 1 or H + I == 1",
          
          # if Pukka all statements are true
          "P != 2 or D == E == 1",
          "Q != 2 or F == G == 1",
          "R != 2 or H == I == 1",
          
          # ranges: Pukka 10-19, Shilli-Shalla 20-29 and Wotta-Woppa 30-39
          "(P != 0 or U == 3) and (P != 1 or U == 2) and (P != 2 or U == 1)",
          "(Q != 0 or W == 3) and (Q != 1 or W == 2) and (Q != 2 or W == 1)",
          "(R != 0 or Y == 3) and (R != 1 or Y == 2) and (R != 2 or Y == 1)",
          
          # A1: B is a Wotta-Woppa
          "(F == G == 0) == D",
          # A2: My wages are 25% less than 20% more than one of the others
          "(UV in {0.9 * WX, 0.9 * YZ}) == E",
          # A3: I get 10 hopes more than B
          "(UV == WX + 10) == D",
          
          # B1: The wages of one of us is different from the sum of those 
          # of the other two
          "(UV != WX + YZ or UV + WX != YZ or UV + YZ != WX) == F",
          # B2: We all belong to the same tribe
          "(P == Q == R) == G",
          # B3: More of C's statements are true than mine
          "(2* H + I >  2 * F + G) == F",
          
          
          # C1: Ugly earns more than Toothless
          "((UV if K == 2 else WX if L == 2 else YZ) > \
            (UV if K == 1 else WX if L == 1 else YZ)) == H",
          # C2: The wages of one of us are 15% less than the wages of another
          "((20 * UV in {17 * YZ, 17 * WX}) or \
            (20 * WX in {17 * YZ, 17 * UV}) or \
            (20 * YZ in {17 * UV, 17 * WX})) == I",
          # C3: Stupid is a Shilli-Shalla.
          "((K == 0 and P == 1) or \
            (L == 0 and Q == 1) or \
            (M == 0 and R == 1)) == H",
        ],
        answer="(D, E, F, G, H, I), (P, Q, R), (UV, WX, YZ), (K, L, M)",
        d2i=dict([(0, "UWY")] +
                 [(2, "DEFGHI")] +
                 [(3, "DEFGHIKLMPQR")] +
                 [(k, "DEFGHIKLMPQRUWY") for k in range(4, 10)]),
        distinct="KLM",
        verbose=0,
      )
      
      # print answers
      for (_, ans) in p.solve():
        print(f"{ans}")
      

      Like

  • Jim Randell 12:30 pm on 14 June 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 718: An eccentric race 

    From The Sunday Times, 20th April 1975 [link]

    After the tea-party Alice persuaded the Mad Hatter, the March Hare and the Dormouse to run with her round a nearby circular track, promising that they should all four win the race by reaching the winning-post at the same moment — so long as they did not vary their speeds!

    Round the track were twelve posts equally-spaced a whole number of feet apart, No. 12 being at the start, which was also the finishing-post. At each post one of the Flamingoes was stationed as umpire. We will call them F1, F2, …, F12. F12 acted as starter. The umpires reported as follows:

    (1) All four runners maintained their own constant speeds.

    (2) F2 noted that Hatter passed Dormouse at his post exactly 30 seconds after the start.

    (3) F3 reported that Hare passed Hatter at his post exactly 45 seconds after the start.

    (4) F8 said that Hare passed Alice at his post, at which time Alice was passing his post for the third time and Hare for the sixth time.

    (5) The umpires reported no more overtakings, although obviously there were others.

    The speeds of the four runners, in feet per second, were whole numbers between 5 and 20.

    How many laps had they all completed when they all won. And how many seconds did the race last?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser718]

     
    • Jim Randell 12:33 pm on 14 June 2022 Permalink | Reply

      I am assuming they all set off from the start at the same time (although this is not explicitly mentioned).

      This Python program runs in 55ms. (Internal run time is 326µs).

      Run: [ @replit ]

      from enigma import (irange, inf, div, divisors, fdiv, printf)
      
      # distance units for passing post <n> for the <k>th time
      dist = lambda n, k: n + 12 * (k - 1)
      
      # check velocity <v> passed post <n> at time <t>
      def check(v, n, t, d):
        x = div(v * t, d)
        return (x is not None) and (x % 12 == n)
      
      # calculate the laps and time for a race with velocities <vs>
      def race(vs, lap):
        # find the slowest speed
        s = min(vs)
        # consider total number of laps for the slowest
        for n in irange(1, inf):
          # calculate number of laps for the others
          laps = list(div(v * n, s) for v in vs)
          if None not in laps:
            return (laps, fdiv(n * lap, s))
      
      # choose a speed for the Hare (M)
      for M in irange(7, 20):
      
        # Alice (A) passed post 8 for the 3rd time, when the Hare passed it
        # for the 6th time
        A = div(M * dist(8, 3), dist(8, 6))
        if A is None: continue
      
        # choose speed for D
        for D in irange(5, M - 2):
          # Dormouse passes post 2 at exactly 30s
          # consider possible values for d
          for d in divisors(30 * D):
            if not check(D, 2, 30, d): continue
      
            # choose speed for Hatter (H)
            for H in irange(D + 1, M - 1):
      
              # Hatter passes post 2 at exactly 30s
              if not check(H, 2, 30, d): continue
      
              # Hare and Hatter pass post 3 at exactly 45s
              if not (check(H, 3, 45, d) and check(M, 3, 45, d)): continue
      
              # output solution
              ((lM, lA, lH, lD), t) = race([M, A, H, D], 12 * d)
              printf("M={lM} A={lA} H={lH} D={lD}; d={d} lap={lap} -> t={t:g}", lap=12 * d)
      

      Solution: At the end of the race the number of laps was: Alice = 8, Mad Hatter = 13, March Hare = 17, Dormouse = 7. The race lasted 180 seconds.

      The distance between posts was 15ft, so one lap was 180ft.

      The velocity of each participant, expressed in feet per second, is the same as the number of laps run during the race.

      Like

      • Frits 10:57 pm on 15 June 2022 Permalink | Reply

        check(M, 3, 45, d) can already be done before the loop over H.

        Another idea is to choose H before D and then d must be in the intersection of the divisors of (30 * H) and (45 * H).

        Like

  • Jim Randell 7:52 am on 7 June 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 713: Better and better 

    From The Sunday Times, 16th March 1975 [link]

    When the four modest gamblers — Al, Bill, Carl and Don — sat down for their usual game of poker, each of the four placed on the table, as their stake money for the evening, a different whole number of pence.

    After a number of hands, Al found that he had exactly doubled the number of pence with which he had started. it was his turn to provide the evening’s refreshments, and he bought the first round of drinks. After a few more hands, Al exactly doubled the number of pence he had had left after buying the first round and he then bought a second round. Thereafter he repeated the process, i.e. he exactly doubled his remaining pence and bought a third round.

    During the fourth session, Al took every penny his three opponents still had on the table and found that he had exactly doubled the number of pence he had had left after buying the third round. He then bought a fourth round — which took every penny he himself had!

    Each of the four rounds of drinks cost precisely the same whole number of pence.

    Carl began the game with a number of pence (between 50 and 100) which was exactly half the total number of pence which Bill and Don had between them at the start.

    How many pence did Al have when the game began?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser713]

     
    • Jim Randell 7:55 am on 7 June 2022 Permalink | Reply

      If the starting amounts are (A, B, C, D). Then at the end of the first session A now has 2A and buys a round (costing R), so starts the second session with (2A − R).

      He ends the second session with twice his starting amount, i.e. (4A − 2R), and buys another round. So starts the third session with (4A − 3R).

      He ends the third session with twice his starting amount, i.e. (8A − 6R), and buys another round. So starts the fourth session with (8A − 7R).

      He ends the fourth session with twice his starting amount, i.e. (16A − 14R), and buys another round, leaving him with no money.

      Hence:

      16A − 15R = 0
      R = (16/15)A

      Also the purchasing of the 4 rounds used up all of the money:

      4R = A + B + C + D
      (4(16/15) − 1)A = B + C + D
      A = (15/49)(B + C + D)

      Now: C = (B + D) / 2, so:

      B + D = 2C
      A = (15/49)(3C)
      A = (45/49)C

      And:

      R = (16/15)A
      R = (48/49)C

      48 has no factors of 7, so C must have (at least) 2 factors of 7. And as C is in [50, 100] we must have:

      C = 98
      B + D = 196
      A = 90
      R = 96

      All that remains is to ensure B and D can be assigned values such that each of the four starts with a different stake.

      If we keep keep the stakes below 100p we can assign B and D to 97p and 99p (in some order). The four starting amounts are then (90p, 97p, 98p, 99p).

      Solution: Al started the game with 90p.

      So Al’s progress is:

      1: 90p → 180p; spends 96p
      2: 84p → 168p; spends 96p
      3: 72p → 144p; spends 96p
      4: 48p → 96p; spends 96p

      Like

  • Jim Randell 12:59 pm on 31 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 707: The reigning king 

    From The Sunday Times, 2nd February 1975 [link]

    In the World Chess Championships, under the old rules, the reigning champion wins when he has scored at least 12 points (1 for a win and ½ for a draw) or the challenger wins when he has scored 12½ points.

    When the system was last used the contest required its full 24 games, the challenger’s won games with the White pieces was equal to the total number of draws, and the reigning champion’s lost games with the White pieces was either one more or one less than this number.

    What was the result of the final game?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser707]

     
    • Jim Randell 1:00 pm on 31 May 2022 Permalink | Reply

      After 23 games (and 23 points distributed), the reigning champion (R) must have less than 12 points, and the challenger (C) must have less than 12.5 points.

      Possibilities [and outcomes for the final match] are:

      R=11.5, C=11.5 [draw → R=12.0, C=12.0; R wins → R=12.5, C=11.5; C wins → R=11.5, C=12.5]
      R=11.0, C=12.0 [draw → R=11.5, C=12.5; R wins → R=12.0, C=12.0; C wins → R=11.0, C=13.0]

      If, after 24 games, there were x draws. And C won x games playing white. And R lost (x ± 1) games playing white.

      The games lost by R playing white were won by C playing black, so C’s total number of points is:

      C = 0.5x + x + (x ± 1)
      C = 2.5x ± 1

      And C’s total points must be in (11.5, 12.0, 12.5, 13.0):

      11.5 = 2.5x + 1 ⇒ x = 4.2 [impossible]
      11.5 = 2.5x − 1 ⇒ x = 5
      12.0 = 2.5x + 1 ⇒ x = 4.4 [impossible]
      12.0 = 2.5x − 1 ⇒ x = 5.2 [impossible]
      12.5 = 2.5x + 1 ⇒ x = 4.6 [impossible]
      12.5 = 2.5x − 1 ⇒ x = 5.4 [impossible]
      13.0 = 2.5x + 1 ⇒ x = 4.8 [impossible]
      13.0 = 2.5x − 1 ⇒ x = 5.6 [impossible]

      The only possibility is x = 5, and so C drew 5 games, won 5 games playing white, and won 4 games playing black, giving a total of 5 draws, 9 wins and 11.5 points.

      So R had 12.5 points, and has drawn 5 and won 10 games, and the championship.

      Solution: The final game (and championship) was won by the reigning champion.

      And before the final game each player was on 11.5 points (9 wins + 5 draws).

      Like

  • Jim Randell 11:19 am on 24 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 704: … Go! 

    From The Sunday Times, 12th January 1975 [link]

    Peter and Robert run a ten-lap race on the track. They each have their own standard fast and slow times for a lap and they change pace only after complete laps.

    Peter runs his first five laps in the order: slow, slow, fast, slow, fast and repeats the sequence in the second half of the race. Robert starts with a slow lap and then runs alternate fast and slow laps throughout.

    Robert takes an immediate lead but his time advantage after 6 laps is halved by the finish. Peter takes the lead only once to reach a maximum advantage of one second, but he also once draws level only to fall behind again.

    What is Robert’s greatest time lead, and by what margin does he win the race?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser703]

     
    • Jim Randell 11:21 am on 24 May 2022 Permalink | Reply

      P and R both start at their slow pace, and R takes the lead, so P’s slow pace is slower than R’s slow pace.

      If we consider the time taken to complete a lap. P’s slow pace will be the longest time.

      Let the number of seconds to complete a lap be:

      P (slow): x
      R (slow): x − a
      P (fast): x − b
      R (fast): x − c

      where a, b, c are positive values, and c > a.

      P’s laps are: (slow, slow, fast, slow, fast) ×2, so his cumulative times are:

      1: x
      2: 2x
      3: 3x − b
      4: 4x − b
      5: 5x − 2b
      6: 6x − 2b
      7: 7x − 2b
      8: 8x − 3b
      9: 9x − 3b
      10: 10x − 4b

      R’s laps are (slow, fast) ×5 , so his cumulative times are:

      1: x − a
      2: 2x − a − c
      3: 3x − 2a − c
      4: 4x − 2a − 2c
      5: 5x − 3a − 2c
      6: 6x − 3a − 3c
      7: 7x − 4a − 3c
      8: 8x − 4a − 4c
      9: 9x − 5a − 4c
      10: 10x − 5a − 5c

      We can then calculate the time advantage R has over P at the end of each lap (= P’s time − R’s time):

      1: a
      2: a + c
      3: 2a − b + c
      4: 2a − b + 2c
      5: 3a − 2b + 2c
      6: 3a − 2b + 3c
      7: 4a − 2b + 3c
      8: 4a − 3b + 4c
      9: 5a − 3b + 4c
      10: 5a − 4b + 5c

      And we have 3 constraints on the values a, b, c.

      [1] P’s time advantage after 6 laps is twice the advantage after 10 laps:
      [2] One of the advantages has a value of −1
      [3] And another one has a value of 0.

      The equations can then be solved manually or programatically.

      This Python program chooses laps for the −1 and 0 seconds advantages, and then solves the 3 equations to find positive values for a, b, c.

      We then check P has a positive advantage on 8 of the laps, and this gives the result.

      This Python program runs in 62ms. (The internal run time is 5ms).

      Run: [ @replit ]

      from enigma import (subsets, matrix, printf)
      
      # R's time advantage at the end of each lap
      adv = {
         #   a   b  c
         1: (1,  0, 0),
         2: (1,  0, 1),
         3: (2, -1, 1),
         4: (2, -1, 2),
         5: (3, -2, 2),
         6: (3, -2, 3),
         7: (4, -2, 3),
         8: (4, -3, 4),
         9: (5, -3, 4),
        10: (5, -4, 5),
      }
      
      ks = sorted(adv.keys())
      
      # choose laps to have an advantage of -1 and 0
      for (a0, a1) in subsets(adv.keys(), size=2, select="P"):
        # adv 0 is not in lap 1 or lap 10
        if a0 in {1, 10}: continue
      
        # construct the equations
        eqs = [
          # adv[6] = 2 * adv[10]
          (tuple(x - 2 * y for (x, y) in zip(adv[6], adv[10])), 0),
          # adv[a0] = 0
          (adv[a0], 0),
          # adv[a1] = -1
          (adv[a1], -1),
        ]
      
        # and solve them
        try:
          (a, b, c) = matrix.linear(eqs)
        except ValueError:
          continue
      
        # we are interested in positive a, b, c
        if not (c > a > 0 and b > 0): continue
      
        # calculate the P's advantage in each lap
        advs = dict((k, sum(x * y for (x, y) in zip((a, b, c), adv[k]))) for k in ks)
      
        # there should be 8 laps where P has a positive advantage
        if sum(v > 0 for v in advs.values()) != 8: continue
      
        # output solution
        printf("[a={a} b={b} c={c}]")
        for k in ks:
          printf("[lap {k}: adv = {v}]", v=advs[k])
        printf("max adv = {m}; win margin = {w}", m=max(advs.values()), w=advs[10])
        printf()
      

      Solution: Robert’s greatest lead is 6 seconds. He won the race by 2 seconds.

      The time advantage (in seconds) for R on each lap is:

      1: +1
      2: +6
      3: 0
      4: +5
      5: −1
      6: +4
      7: +5
      8: +3
      9: +4
      10: +2

      Like

      • Frits 1:01 pm on 25 May 2022 Permalink | Reply

        @Jim,

        Maybe you have coded it already but did you also prevent a draw immediately followed by a lead for Peter?

        Like

        • Jim Randell 1:14 pm on 25 May 2022 Permalink | Reply

          @Frits: I didn’t explicitly check for this, but it turns out it is not the case.

          In any case we know R must win the race, so that P must fall behind by the end of the race.

          Like

    • Frits 8:33 am on 26 May 2022 Permalink | Reply

      More analysis.

       
      from (6) = 2 * (10) we can deduce:
      
      3a - 2b + 3c = 2 * (5a - 4b + 5c)
      
      7a - 6b + 7b = 0 --> a + c = 6/7 b
      
      1: a              -->   a
      2: a + c          -->   6/7 b
      3: 2a - b + c     -->   a - 1/7 b
      4: 2a - b + 2c    -->   5/7 b 
      5: 3a - 2b + 2c   -->   a - 2/7 b
      6: 3a - 2b + 3c   -->   4/7 b
      7: 4a - 2b + 3c   -->   a + 4/7 b
      8: 4a - 3b + 4c   -->   3/7 b
      9: 5a - 3b + 4c   -->   a + 3/7 b
      10: 5a - 4b + 5c  -->   2/7 b
      
      Only lap 3 and 5 can be nonpositive, so lap 3 must be 0 and lap 5 must be -1.
      Difference between (5) and (3) is -1/7 b which must be equal to -1 --> b = 7.
      So a = 1 and c = 5. The answers follow from substitution.
      

      Like

  • Jim Randell 12:08 pm on 17 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 703: Wheels of fortune 

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

    A small gaming machine has proved popular in East Bernia. Inside the machine three wheels spin independently on a common axle and on the “tread” of each wheel are three single-figured numbers. When a handle is pulled the wheels spin, and when they come to rest one number on each wheel is shown at the front of the machine.

    The nine numbers used are 1, 2, 3, 4, 5, 6, 7, 8 and 9. When added together, the three numbers on the first wheel total one less than the three on the second wheel, which total one less than the three on the third wheel.

    The method of scoring is to add the three “disclosed” numbers together, and the numbers are so placed on the wheels that it is possible to make any one of 17 different scores (16 of which are consecutive numbers). It is impossible to get the score of 7.

    What are the numbers on each wheel?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser703]

     
    • Jim Randell 12:09 pm on 17 May 2022 Permalink | Reply

      This Python program runs in 58ms. (Internal run time is 896µs).

      Run: [ @replit ]

      from enigma import (irange, div, partitions, cproduct, printf)
      
      # available digits
      digits = list(irange(1, 9))
      
      # calculate the required sums
      x = div(sum(digits), 3)
      ts = { x - 1, x, x + 1 }
      
      # does <ns> contains a <k>-length run of consecutive numbers
      # ns is a sorted sequence of distinct integers
      def is_consecutive(ns, k=2):
        k -= 1
        (i, j) = (0, k - len(ns))
        while j < 0:
          if ns[i] + k == ns[j] : return (i, j)
          i += 1
          j += 1
      
      # allocate the digits to the wheels
      for ws in partitions(digits, 3):
        if set(sum(w) for w in ws) != ts: continue
      
        # construct possible sums
        ss = set(sum(xs) for xs in cproduct(ws))
        if 7 in ss or len(ss) != 17: continue
      
        # look for a consecutive run of 16
        ss = sorted(ss)
        if not is_consecutive(ss, 16): continue
      
        # output solution
        (w1, w2, w3) = sorted(ws, key=sum)
        printf("{w1} {w2} {w3} -> {ss}")
      

      Solution: The numbers on the wheels are: (3, 5, 6) (2, 4, 9) (1, 7, 8).

      The 17 scores that can be made are: 6 and 8 – 23.

      Like

    • Frits 7:05 pm on 17 May 2022 Permalink | Reply

      Some easy deduction:

      As scores 6, 7, 23, and 24 can only be made with one combination we can conclude that 1, 2 and 3 must be on different wheels (same for 6, 8 and 9). So we know the scores 6 and 8 – 23.

      Also 3 and 4 cannot be on the same wheel as it leads to score 7.

      Like

  • Jim Randell 8:18 am on 10 May 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 695: Sitting on a gate 

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

    Tom’s mother fetches him by car from school every day, starting out from home so that she arrives at school at 4:15 pm. He is waiting for her and gets into the car at once, and is driven home without delay. This is the normal routine.

    One day Tom expects to be delayed at school for an hour and his mother delays her time of leaving home accordingly. The expected delay does not take place so instead of telephoning home Tom sets out at 4:15 to walk towards home. After walking for half an hour he sits on a convenient roadside gate and waits for the car.

    When it comes he gets in at once and is driven straight home, where they arrive 48 minutes later than usual.

    For how many minutes was Tom sitting on the gate?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser695]

     
    • Jim Randell 8:19 am on 10 May 2022 Permalink | Reply

      We assume that the car is driven at a constant speed, which does not depend on the time of day.

      Tom sets off walking from the school at 4:15 pm and arrives at the gate at 4:45 pm.

      Mum sets off 60 minutes later than usual, and arrives back home 48 minutes later than usual.

      So Tom’s walking has cut 12 minutes off the car journey. Hence the gate is 6 minutes drive from the school.

      So, on a normal day, Mum would pass the gate 6 minutes before arriving at the school, i.e. at 4:09 pm.

      On the abnormal day, she left an hour later, and so arrives at the gate at 5:09 pm, by which time Tom has been waiting 24 minutes.

      Solution: Tom has been waiting at the gate for 24 minutes.

      Like

  • Jim Randell 7:58 am on 3 May 2022 Permalink | Reply
    Tags: by: E T Tweddell   

    Brain-Teaser 693: Ready … 

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

    The Phytteness School marathon attracted 16 entrants this year. Each of the five houses entered a team of three runners, and the field was made up by the maths master, Des Chappelle. The school houses were competing for the trophy. The number of points by each entrant would be equal to his finishing position.

    The five houses tied for the cup, their totals being equal, although no two competitors tied for the same position. In order to determine the order in which the houses would hold the cup (they had agreed to hold it for 73 days each), they multiplied the finishers’ positions together in each house. The house with the smallest product, Black, would hold the cup first and so on to the house with the largest product, Blue, who hold it last. Unfortunately, Green and White houses still tied, and had to be separated by the toss of a coin.

    Des noted later that no house had had two finishers in consecutive positions, although Green house would have achieved this had he not managed to get between two of their runners right on the line.

    In what positions did the Red house runners finish?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser693]

     
    • Jim Randell 8:00 am on 3 May 2022 Permalink | Reply

      There are T(16) = 136 points available, but Des finished between two of the other runners, so he must have finished with 2 – 15 points. And the remaining positions must be shared between the 5 houses (3 positions each) to give the same sum.

      This Python program chooses a position for Des that give viable possible totals for each house, and then partitions the remaining positions among the houses. The groupings are then sorted by product, and the 3 groups in the middle of the field must have 2 products between them. The group with the unique product are Red.

      The program runs in 59ms (internal run time is 312µs).

      Run: [ @replit ]

      from enigma import (tri, tuples, irange, div, diff, multiply, group, filter2, singleton, printf)
      
      # total number of points
      T = tri(16)
      
      # check increasing integer sequence <s> has no consecutive elements
      check = lambda s: all(y - x > 1 for (x, y) in tuples(s, 2))
      
      # partition the values into <k>-length groups, each sums to <t>
      def partition(vs, k, t, ss=[]):
        if len(vs) == k:
          if sum(vs) == t and check(vs):
            yield ss + [tuple(vs)]
        else:
          v0 = vs[0]
          for (i, v1) in enumerate(vs):
            if i == 0: continue
            v2 = t - (v0 + v1)
            if not(v2 > v1): break
            if v2 in vs:
              s = (v0, v1, v2)
              if check(s):
                yield from partition(diff(vs, s), k, t, ss + [s])
      
      # consider position for D (between 2 others)
      for D in irange(2, 15):
        # calculate points for each house
        H = div(T - D, 5)
        if H is None: continue
      
        # partition the remaining positions into groups of 3
        for ss in partition(diff(irange(1, 16), [D]), 3, H):
          # sort the groups by product (largest first)
          ss.sort(key=multiply, reverse=1)
          # find the products of the middle three (G, W, R)
          p = group(ss[1:-1], by=multiply)
          if len(p.keys()) != 2: continue
          Rs = Ws = Gs = None
          for (k, vs) in p.items():
            if len(vs) == 1:
              # red has a unique product
              Rs = vs[0]
            else:
              # green/white have shared products; green has D - 1 and D + 1
              (Gs, Ws) = filter2((lambda s: D - 1 in s and D + 1 in s), vs, fn=singleton)
          if Rs and Ws and Gs:
            # output solutions
            printf("D={D}; H={H}")
            printf("houses = {ss}")
            printf("-> blue = {ss[0]}")
            printf("-> green = {Gs}")
            printf("-> white = {Ws}")
            printf("-> red = {Rs}")
            printf("-> black = {ss[-1]}")
            printf()
      

      Solution: Red house finished 2nd, 9th, 14th.

      The points awarded were:

      Des = 11
      Blue = (5, 7, 13); sum = 25; product = 455
      Green = (3, 10, 12); sum = 25; product = 360
      White = (4, 6, 15); sum = 25; product = 360
      Red = (2, 9, 14); sum = 25; product = 252
      Black = (1, 8, 16); sum = 25; product = 128

      Like

    • GeoffR 10:32 am on 3 May 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 2..15: des;  % Maths master - Des Chappelle
      
      var 1..16: a; var 1..16: b;var 1..16: c;  % Blue's points
      constraint b - a != 1 /\ c - b != 1 /\ b > a /\ c > b;
      
      var 1..16: d; var 1..16: e;var 1..16: f;  % Green's points
      constraint e - d != 1 /\ f - e != 1 /\ e > d /\ f > e;
      
      var 1..16: g; var 1..16: h;var 1..16: i;  % White's points 
      constraint h - g != 1 /\ i - h != 1 /\ h > g /\ i > h;
      
      var 1..16: j; var 1..16: k;var 1..16: l;  % Red's points 
      constraint k - j != 1 /\ l - k != 1 /\ k > j /\ l > k;
      
      var 1..16: m; var 1..16: n;var 1..16: o;  % Black's points
      constraint n - m != 1 /\ o - n != 1 /\ n > m /\ o > n;
      
      % No two competitors tied for the same position
      constraint all_different ([des,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o]);
      
      % The five houses tied for the cup, their totals being equal
      constraint a + b + c == d + e + f /\ a + b + c == g + h + i
      /\ a + b + c == j + k + l /\ a + b + c == m + n + o;
      
      % Black's points have the smallest product
      constraint m * n * o < j * k * l /\  m * n * o < g * h * i
      /\ m * n * o < d * e * f /\ m * n * o < a * b * c;
      
      % Blue's points have the largest product
      constraint a * b * c > d * e * f /\ a * b * c > g * h * i
      /\ a * b * c > j * k * l /\ a * b * c > m * n * o;
      
      % The product of Green's points equals the product of White's points
      constraint d * e * f == g * h * i;
      
      % Location of Des' points in relation to the Green house points
      constraint (des - d == 1 /\ e - des == 1 ) 
      \/ (des - e == 1 /\ f - des == 1);
      
      solve satisfy;
      
      output ["Des = " ++ show(des) ++ "\n" ++
      "Blue:   [a,b,c] = " ++ show([a,b,c]) ++ "\n" ++
      "Green:  [d,e,f] = " ++ show([d,e,f]) ++ "\n" ++
      "White:  [g,h,i] = " ++ show([g,h,i]) ++ "\n" ++
      "Red:    [j,k,l] = " ++ show([j,k,l]) ++ "\n" ++
      "Black:  [m,n,o] = " ++ show([m,n,o]) ];
      
      % Des = 11
      % Blue:   [a,b,c] = [5, 7, 13]
      % Green:  [d,e,f] = [3, 10, 12]
      % White:  [g,h,i] = [4, 6, 15]
      % Red:    [j,k,l] = [2, 9, 14]  <<< Answer
      % Black:  [m,n,o] = [1, 8, 16]
      % ----------
      % ==========
      
      

      Like

  • Jim Randell 9:39 am on 26 April 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 690: … And two poles 

    From The Sunday Times, 6th October 1974 [link]

    In my garden, which is level, there are two vertical flagpoles and although there is little apparent difference in their respective heights I know that their actual heights are each an exact but different number of inches.

    As part of their anchoring taut wires stretch from the very top of each to the foot of the other. The crossing-point of these two wires is exactly eleven feet from the ground.

    What are the heights of the flagpoles?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser690]

     
    • Jim Randell 9:42 am on 26 April 2022 Permalink | Reply

      When resistors with values r1 and r2 are connected in parallel, the value of the combined resistance R is given by:

      1/R = 1/r1 + 1/r2

      (See: Enigma 3058).

      The value of R can also be calculated using the following diagram:

      Where the height of the crossing point gives the combined resistance.

      The flagpoles in the question also correspond to this diagram. Their heights being r1 and r2 and the crossing point being 11ft = 132in above the ground corresponds to R.

      So we need to find two different values whose reciprocals sum to 1/132. And the [[ reciprocals() ]] function in the enigma.py library can do this for us.

      The following Python program runs in 59ms.

      Run: [ @replit ]

      from enigma import (Accumulator, reciprocals, sprintf as f)
      
      # find (a, b) such that 1/a + 1/b = 1/132
      # record the smallest difference (i.e. the final candidate)
      r = Accumulator(fn=min)
      for (a, b) in reciprocals(2, 132):
        # a, b must be different
        if a == b: continue
        r.accumulate_data(b - a, (a, b))
        print(f("[a={a} b={b}]"))
      
      # format <x> inches as "<f>ft <i>in"
      def fmt(x):
        (d, r) = divmod(x, 12)
        return f("{d}ft {r}in")
      
      # output solution
      (a, b) = r.data
      print(f("A = {a}; B = {b}", a=fmt(a), b=fmt(b)))
      

      Solution: The heights of the flagpoles are: 21ft 1in, 23ft 0in.

      This is the solution where the flagpoles are as close as possible in height (we have: 1/253 + 1/276 = 1/132).

      But there are other candidate solutions where the difference in height is greater, e.g. the next solution would be 1/231 + 1/308 = 1/132 to give values of 19ft 3in and 25ft 8in.

      So to narrow the solution down to a single candidate we could say the flagpoles had a difference in height of less than 6ft.

      Like

    • GeoffR 11:12 am on 26 April 2022 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Assume Max flagpole height = 30 ft.
      var 1..360:a;
      var 1..360:b;
      
      % 1/a + 1/b = 1/132
      constraint 132 * (b + a) == a * b;
      
      % extra constraint for a single solution
      % ...the flagpoles had a difference in height of less than 6ft
      constraint a > b /\ a - b  < 72;
      
      solve minimize(a - b);
      
      output ["Heights of flagpoles = " ++ show(a) ++ 
      " and " ++ show(b) ++  " inches." ];
      
      % Heights of flagpoles = 276 and 253 inches.
      % ----------
      % ==========
      
      

      Like

  • Jim Randell 9:38 am on 19 April 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 668: Sweepstakes sixfold 

    From The Sunday Times, 28th April 1974 [link]

    A party of six racegoers arrived on the course with precisely £6 each with which to speculate. There were six races on the card and six runners in each race, so they decided to hold sweepstakes among themselves on each of the six races, the stake being £1 per race per person.

    The runners in each race were numbered 1, 2, 3, 4, 5 and 6, and each of the racegoers drew one of these numbers out of a hat. Each player’s number remained the same throughout the six races. There were thus, in effect, six separate sweepstakes, the holder of the winning number drawing £6 on each race. To add a little interest to the proceedings it was arranged that the winner on any one of the races would be permitted to buy (at cost price of £1) one additional chance in one or more of the races subsequent to his win, from one of the other players. Only a player who had not yet had a win could sell his chance.

    At the conclusion of the events it transpired that three players had made a net profit; the holder of No. 1 who won £4, the holder of No. 5 who won £1, and the holder of No. 4. The holder of No. 2 lost £3, and the holder of No. 6 lost £6.

    Three winning chances were sold, but none of these by the holders of Nos 1, 4 and 5. The holder of No. 1 did not have any transaction with the holders of Nos 2 and 3. There were no dead-heats and no number appeared in the winner’s frame in consecutive races.

    What, in order, were the numbers of the six winning horses?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser668]

     
    • Jim Randell 9:40 am on 19 April 2022 Permalink | Reply

      At the start of the races each participant has a profit of −6 (having bought the 6 tickets with their own number on).

      And in each race, the outcome for each participant is:

      +6 = no transaction, win (with own ticket)
      +5 = buy ticket, win (with own or bought ticket)
      +1 = sell ticket
      0 = no transaction, don’t win
      −1 = buy ticket, don’t win (with either ticket)

      And there are some additional restrictions: a +5 or −1 can only come after a +6, and a +1 cannot come after a +6.

      And we can use these facts to check that there is a sequence of viable outcomes for each participant (without this the following code takes a lot longer to run, but you can change valid() to always return True to try the slower version, but it will take a lot longer).

      This Python program looks for a viable sequence of winners and ticket transactions for each race. It runs in 4.28s.

      Run: [ @replit ]

      from enigma import irange, subsets, product, diff, first, printf
      
      # update a dictionary with given deltas
      def update(d, *kvs):
        d = d.copy()
        for (ks, v) in kvs:
          for k in ks:
            d[k] += v
        return d
      
      # find possible length k sequences that give total t
      def complete(k, t, f=0, ss=[]):
        xs = [[0, 1, 6], [-1, 0, 5, 6]][f]
        if k == 0:
          if t == 0:
            yield ss
        elif k == 1:
          if t in xs:
            yield ss + [t]
        else:
          # choose an outcome
          for x in xs:
            yield from complete(k - 1, t - x, f or (x == 6), ss + [x])
      
      # is there a valid completion?
      valid = lambda k, t, f: bool(first(complete(k, t, f)))
      
      # totals we are given (also #3 not >0; #4 >0)
      totals = { 1: 4, 5: 1,  2: -3, 6: -6 }
      
      # solve the puzzle
      # n = remaining races
      # vs = ticket values
      # m = money for each participant
      # ws = winning tickets for each race
      # ps = winning players for each race
      # bs = potential buyers
      def solve(n, vs, m, ws=list(), ps=list(), bs=set()):
        if n == 0:
          # check totals
          if not(m[4] > 0 and not(m[3] > 0) and all(m[k] == v for (k, v) in totals.items())): return
          # check three winning tickets were sold
          if not(sum(x != y for (x, y) in zip(ws, ps)) == 3): return
          # viable solution
          yield (m, ws, ps)
        else:
          # choose some buyers and sellers
          b = len(bs)
          for k in irange(0, min(b, 6 - b)):
            for (bs1, ss1) in product(subsets(bs, size=k), subsets(diff(vs, bs), size=k, select="P")):
              #if not all(m[k] > 0 for k in bs1): continue
              # determine who gets the winnings
              d = dict(zip(ss1, bs1))
              # but 1 did not have any transactions with 2 or 3
              if any(d.get(k, 0) == v for (k, v) in [(1, 2), (1, 3), (2, 1), (3, 1)]): continue
              # perform the transactions
              m1 = update(m, (bs1, -1), (ss1, +1))
              # at this point we can check the targets of any sellers are reachable
              if any(not valid(n - 1, totals[k] - m1[k], k in bs) for k in ss1 if k in totals): continue
      
              # choose a winner for the race
              for w in vs:
                # no consecutive winners
                if ws and ws[-1] == w: continue
                # has the winning ticket been sold?
                p = d.get(w, w)
                if p != w:
                  # winning ticket was sold, but not by 1, 4, 5
                  if w in {1, 4, 5}: continue
                  # and only 3 winning tickets were solve
                  if sum(x != y for (x, y) in zip(ws, ps)) > 2: continue
                # allocate the winnings
                m_ = update(m1, ([p], +6))
                bs_ = bs.union([p])
                # check all (non-seller) targets are reachable
                if any(not valid(n - 1, v - m_[k], k in bs_) for (k, v) in totals.items() if k not in ss1): continue
                # solve for the remaining races
                yield from solve(n - 1, vs, m_, ws + [w], ps + [p], bs_)
      
      # initial values
      vs = list(irange(1, 6)) # available tickets
      m = dict((k, -6) for k in vs) # initial amounts
      for (m, ws, ps) in solve(6, vs, m):
        printf("winning tickets = {ws}; profits = {m}", m=list(m[i] for i in vs))
      

      Solution: The numbers of the winning horses were: 5, 4, 2, 3, 2, 1.


      One scenario is:

      Initially: All on −6
      Race 1: 5 wins; #5 → 0
      Race 2: #5 buys 1; 4 wins; #1 → −5, #4 → 0, #5 → −1
      Race 3: #4 buys 1, #5 buys 2; 2 wins; #1 → −4, #2 → −5, #4 → −1, #5 → +4
      Race 4: #4 buys 3, #5 buys 1; 3 wins; #1 → −3, #3 → −5, #4 → +4, #5 → +3
      Race 5: #4 buys 2, #5 buys 1; 2 wins; #1 → −2, #2 → −4, #4 → +9, #5 → +2
      Race 6: #4 buys 3, #5 buys 2; 1 wins; #1 → +4, #2 → −3, #3 → −4, #4 → +8, #5 → +1

      There are alternative scenarios, in each we have (#3, #4) → (−4, +8) or (−5, +9).

      But in each scenario the winning tickets are (5, 4, 2, 3, 2, 1).

      Like

  • Jim Randell 9:44 am on 12 April 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 687: Fair deal 

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

    From a normal pack of playing cards, Bill, George, Harry and Joe were each dealt an Ace, a King, a Queen, a Jack and a ten.

    Harry’s five cards were in three different suits and consisted of three red and two black cards.

    Joe’s five cards were also in three different suits, his Ace being in the same suit as his Queen, and his King in the same suit as his Jack.

    George held more than one black card.

    Bill’s five cards were all in the same suit.

    Harry held the King of spades, and Joe the ten of diamonds.

    What were the cards in George’s hand?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser687]

     
    • Jim Randell 9:45 am on 12 April 2022 Permalink | Reply

      I used the [[ SubstitutedExpression ]] solver from the enigma.py library to fill out the suits of the cards.

      Here is the run file:

      #! python3 -m enigma -r
      
      # assign values 0-3 (S=0; C=1; H=2; D=3) to:
      #
      #     A K Q J X
      #  B: a b c d e
      #  G: f g h i j
      #  H: k m n p q
      #  J: r s t u v
      
      SubstitutedExpression
      
      --base=4
      --distinct="afkr,bgms,chnt,dipu,ejqv"
      
      # red cards and black cards
      --code="red = lambda x: x > 1"
      --code="black = lambda x: x < 2"
      
      # H's cards were in 3 different suits
      "len({{k}, {m}, {n}, {p}, {q}}) == 3"
      # 3 red [and 2 black]
      "icount([{k}, {m}, {n}, {p}, {q}], red) == 3"
      
      # J's cards were in 3 different suits
      "len({{r}, {s}, {t}, {u}, {v}}) == 3"
      # suit of A is the same as Q
      "{r} = {t}"
      # suit of K is the same as J
      "{s} = {u}"
      
      # G has more than 1 black card
      "icount([{f}, {g}, {h}, {i}, {j}], black) > 1"
      
      # B's cards are in the same suit
      "{a} = {b}" "{b} = {c}" "{c} = {d}" "{d} = {e}"
      
      # H held KS (i.e. m = S = 0)
      --assign="m,0"
      
      # J held XD (i.e. v = D = 3)
      --assign="v,3"
      

      And then I wrote a short Python program to output the hands dealt.

      The whole thing runs in 64ms.

      Run: [ @replit ]

      from enigma import SubstitutedExpression, join, printf
      
      # load the run-file
      p = SubstitutedExpression.from_file("teaser687.run")
      
      # solve the puzzle
      for s in p.solve(verbose=0):
        # output hands
        printf("   A K Q J X")
        hand = lambda xs: join(("SCHD"[s[x]] for x in xs), sep=" ")
        printf("B: {h}", h=hand("abcde"))
        printf("G: {h}", h=hand("fghij"))
        printf("H: {h}", h=hand("kmnpq"))
        printf("J: {h}", h=hand("rstuv"))
        printf()
      

      Solution: George held: A♣, K♦, Q♣, J♠, 10♠.

      The complete set of hands is fully determined:

      B: A♥, K♥, Q♥, J♥, 10♥.
      G: A♣, K♦, Q♣, J♠, 10♠.
      H: A♦, K♠, Q♦, J♦, 10♣.
      J: A♠, K♣, Q♠, J♣, 10♦.

      Like

  • Jim Randell 9:44 am on 15 March 2022 Permalink | Reply
    Tags:   

    Brain-Teaser 661: The logical choice 

    From The Sunday Times, 10th March 1974 [link]

    Four members who hold office in the Logic Club approve of each other. They recently decided among themselves that next year each of the four would hold an office now held by one of the other three.

    Other members wanted to know whether this was legal and who was moving to which office. So we asked them to explain themselves under the Club’s Friday rules: these rules say that a member must either make two correct statements or two incorrect statements.

    We unfortunately forgot that we were questioning them on a Tuesday, when members may keep the Friday rules or break them, just as they please.

    The President said: “What we are doing is legal. The man who will be Chairman next year keeps the Friday rules on Tuesdays”.

    The Chairman said: “I am keeping the Friday rules. I shall not become Secretary next year”.

    The Secretary said: “The Treasurer will become President next year. I am not keeping the Friday rules”.

    The Treasurer said: “The man who will be Secretary next year does not keep the Friday rules on Tuesdays. What we are doing is legal”.

    Which of them will be next year’s Chairman? Which will be next year’s Treasurer? Is what they are doing legal?

    This puzzle was included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981, edited by Victor Bryant and Ronald Postill). The puzzle text above is taken from the book.

    [teaser661]

     
    • Jim Randell 9:45 am on 15 March 2022 Permalink | Reply

      If the questioning takes place on a Tuesday, where “members may keep the Friday rules (give two truth values the same) or break them (give two different truth values), just as they please”, to my mind this means the answers to the questions on a Tuesday can be any truth values, and we are none the wiser.

      I suspect we are meant to suppose that on a Tuesday the members either consistently keep the Friday rules (each pair of statements has the same truth value), or consistently break them (each pair of statements has one truth and one falsehood), and their pre-determined behaviour is known to the other officers. Then we can make progress.

      This Python program looks at all possible scenarios to find those which correspond to the described situation. It runs in 47ms.

      Run: [ @replit ]

      from enigma import subsets, product, multiset, map2str, printf
      
      # f = keeping Friday rules; x, y = statements
      check = lambda f, x, y: f == (x == y)
      
      # we can populate the following variables:
      #  j: map(current job -> new job);  p: map(new job -> current job)
      #  f: map(current job -> keeping Friday rules
      #  legal: boolean
      
      # collect results (<next C>, <next T>, <legal>)
      qs = multiset()
      
      # consider possible assignments
      people = (P, C, S, T) = "PCST"
      js = subsets(people, size=4, select="D")
      fs = subsets((0, 1), size=4, select="M")
      for (jv, fv, legal) in product(js, fs, (0, 1)):
        j = dict(zip(people, jv))
        p = dict(zip(jv, people))
        f = dict(zip(people, fv))
      
        # P: "is legal; next C keeps Friday rules
        if not check(f[P], legal, f[p[C]]): continue
        
        # C: "C is keeping Friday rules; C will not become S"
        if not check(f[C], f[C], j[C] != S): continue
      
        # S: "T will become P; S is not keeping Friday rules"
        if not check(f[S], j[T] == P, not(f[S])): continue
      
        # T: next S does not keep Friday rules; is legal"
        if not check(f[T], not(f[p[S]]), legal): continue
        
        printf("[legal={legal} j={j} f={f}]", j=map2str(j, arr="->"), f=map2str(f))
        qs.add((p[C], p[T], legal))
      
      # output results
      for ((q1, q2, q3), n) in qs.most_common():
        printf("(1) {q1} -> C; (2) {q2} -> T; (3) legal = {q3} [{n} solutions]", q3=bool(q3))
      

      Solution:(1) The Secretary plans to become the next Chairman; (2) The President plans to become the next Treasurer; (3) It is not legal.

      The plan for the assignment of jobs is:

      P → T
      C → P
      S → C
      T → S

      They can choose to follow Friday rules or not as they wish, except P must be the opposite of S.

      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
%d bloggers like this: