Enigmatic Code

Programming Enigma Puzzles

Enigma 265: The parable of the wise fool

From New Scientist #1412, 31st May 1984 [link]

And in the evening it came to pass that the father summoned his four sons from the field and said unto them, “You have all found favour in my sight. My store is greater by two and twenty sacks of corn”.

Then spake Absalom to his father saying, “Father thou has told me that I did fill more sacks than any of my brothers. Give me therefore the reward, for I am the most worthy”. And the father rebuked Absalom saying, “Thy worth is no greater than thy brothers for each gave of his best, yet no two of you did fill the same number of sacks. Let instead the reward be to him who showeth the greatest wisdom”.

And he said unto Absalom, “Tell me how many sacks did each of thy brothers fill?” And Absalom knew not.

And he said unto Benjamin, “Thy sacks and the sacks of thy brother Caleb did together equal the sacks of Absalom. How many were the sacks of Caleb?” And Benjamin knew not.

And Caleb hearing these things yet did not know the sacks of Benjamin.

Then spake David saying, “Father, thou hast known me as a fool in the shadow of my brothers, yet if I may assume them to be perfect in the ways of logic and deduction then even I can give thee the numbers”. And he spake truly and was rewarded. And his father said: “Verily the fool who listens well with receive wisdom”.

If Benjamin didn’t bring back the fewest sacks, what was David’s answer?

This is the 800th Enigma puzzle to go up on the site. In total just under 45% of all Enigmas published are now available (there are about 980 to go).

[enigma265]

4 responses to “Enigma 265: The parable of the wise fool

  1. Jim Randell 14 March 2015 at 9:56 am

    This Python 3 program runs in 58ms.

    from collections import defaultdict
    from enigma import irange, filter2, flatten, printf
    
    # look for values from <vs>, selected by <s> that have a unique measure <m>
    # return a partition of the original list (<unique values>, <non-unique values>)
    def filter_unique(vs, m, s):
      u = defaultdict(set)
      r = defaultdict(list)
      for v in vs:
        i = m(v)
        u[i].add(s(v))
        r[i].append(v)
      (r1, r2) = ([], [])
      for (k, v) in u.items():
        (r1 if len(v) == 1 else r2).extend(r[k])
      return (r1, r2)
    
    
    # generate decompositions of t into n distinct positive integers
    def decompose(t, n, s=()):
      if n == 1:
        if t not in s:
          yield s + (t,)
      else:
        for x in irange(1, t - 1):
          if x not in s:
            yield from decompose(t - x, n - 1, s + (x,))
    
    
    # indices for A, B, C, D in tuples
    (A, B, C, D) = (0, 1, 2, 3)
    
    # A has filled the largest number of sacks (and the numbers of sacks filled are all different)
    (sacks, _) = filter2((lambda s: s[A] > s[B] and s[A] > s[C] and s[A] > s[D]), decompose(22, 4))
    
    # A cannot determine the answer
    (x, sacks) = filter_unique(sacks, (lambda s: s[A]), (lambda s: s))
    printf("[A: eliminates {x}]")
    
    
    # B knows A = B + C
    (sacks, x) = filter2((lambda s: s[A] == s[B] + s[C]), sacks)
    
    # B cannot determine C
    (x, sacks) = filter_unique(sacks, (lambda s: s[B]), (lambda s: s[C]))
    printf("[B: eliminates {x}]")
    
    
    # C cannot determine B
    (x, sacks) = filter_unique(sacks, (lambda s: s[C]), (lambda s: s[B]))
    printf("[C: eliminates {x}]")
    
    
    # now D can determine the entire sequence
    (sacks, x) = filter_unique(sacks, (lambda s: s[D]), (lambda s: s))
    printf("[D: eliminates {x}]")
    printf("[D: candidates {sacks}]")
    
    # in the actual case B is not the smallest number
    for s in sacks:
      if not(s[B] < s[C] and s[B] < s[D]):
        printf("A={A} B={B} C={C} D={D}", A=s[A], B=s[B], C=s[C], D=s[D])
    

    Solution: David’s answer is that Absalom filled 9 sacks, Benjamin filled 6 sacks and Caleb filled 3 sacks.

    David himself must have filled 4 sacks to bring the total to 22.

    As observers we can determine that by the end that (A, B, C, D) is one of (8, 1, 7, 6) or (9, 6, 3, 4). As we are told B is not the smallest we can then determine the answer.

  2. Paul 14 March 2015 at 1:35 pm

    I am having a few issues with this one. In Mathematica ‘IntegerPartitions’ orders the list left to right in size order, for example the Partitions of 12 into 3 parts is:-
    {{9,2,1}, {8,3,1}, {7,4,1}, {7,3,2}, {6,5,1}, {6,4,2}, {5,4,3}}. This naturally preserves the logic that A>B and that B is not the smallest. So using this and the other logic in this MMA code. I get multiple solutions that would all seem correct.

    s=22;
    Timing[Do[list={};
    AppendTo[list,Cases[IntegerPartitions[n,{3}],{a_,b_,c_}/;a!=b!=c&&a==b+c]];
    k=Flatten[list];
    l=Cases[Partition[Riffle[k,s-n,{4,-1,4}],4],{a_,b_,c_,d_}/;a!=b!=c!=d&&a>b&&a>c&&a>d];
    If[Length[l]>0,Print[l]],{n,s-2,10,-2}]]
    
    {{10,9,1,2},{10,7,3,2},{10,6,4,2}}
    {{9,8,1,4},{9,7,2,4},{9,6,3,4}}
    {{8,7,1,6},{8,5,3,6}}.

    The 4th digit in each set is the number of sacks David filled, i.e. {2, 4, 6}. So what other logic is needed or I missed that would exclude the superfluous solutions?. btw the timing for this was <1ms.

    Paul.

    • Jim Randell 14 March 2015 at 5:50 pm

      B knows that A + B + C + D = 22, A > (B, C, D), (B, C, D) are distinct, A = B + C, and also he knows the value of B.

      But when he is asked if he knows the value of C he cannot say, so there must be multiple possibilities for the value of C given the value of B.

      So, as observers, we can eliminate any possibilities where given the value of B there is a single corresponding value of C.

      These are: (B, C) = (2, 7), (4, 6), (5, 3), (8, 1), (9, 1).

      Which eliminates the following values for (A, B, C, D): (9, 2, 7, 4), (10, 4, 6, 2), (8, 5, 3, 6), (9, 8, 1, 4), (10, 9, 1, 2).

      Similarly we can eliminate further values given the responses of C and D, until we are left with just 2 candidate assignments for (A, B, C, D).

      We (as the observers) are then told that B is not the smallest value, and this eliminates one of the 2 candidate assignments, leaving only (A, B, C, D) = (9, 6, 3, 4).

  3. Brian Gladman 14 March 2015 at 6:54 pm
    from collections import defaultdict
    
    # collect the possible distributions of sacks between A, B, C and D
    # and store them in a dictionary indexed on A
    a_to_lbcd = defaultdict(list)
    for a in range(3, 11):
      # since B + C = A, 2.A + D = 22 so D = 22 - 2.A 
      d = 2 * (11 - a)
      # B must be less than A (since B + C = A)
      for b in range(1, a):
        c = a - b
        if len(set((a, b, c, d))) == 4 and a > max(b, c, d):
          a_to_lbcd[a] += [(b, c, d)]
    
    # consider whether A, knowing his number, can work out the distribution
    # store retained distributions in a dictionary indexed on B and C
    bc_to_lad = defaultdict(lambda: defaultdict(list))
    for a, l_bcd in a_to_lbcd.items():
      # retain only those distributions where he cannot
      if len(l_bcd) > 1:
        for b, c, d in l_bcd:
          bc_to_lad[b][c] += [(a, d)]
    
    # now consider whether B, knowing his number, can determine C
    # store retained distributions in a dictionary indexed on C and B
    cb_to_lad = defaultdict(lambda: defaultdict(list))
    for b, c_to_lad in bc_to_lad.items():
      # retain only those distributions where B values are associated
      # with more than one C value
      if len(c_to_lad) > 1:
        for c, l_ad in c_to_lad.items():
          cb_to_lad[c][b] += l_ad
    
    # now consider whether C, knowing his number, can determine B
    # store retained distributions in a dictionary indexed on D
    d_to_labc = defaultdict(list)
    for c, b_to_lad in cb_to_lad.items():
      # retain only those distributions where C values are associated
      # with more than one B value
      if len(b_to_lad) > 1:
        for b, l_ad in b_to_lad.items():
          for a, d in l_ad:
            d_to_labc[d] += [(a, b, c)]
    
    # now consider those distributions where that D can determine and
    # in which B is not the smallest of (A, B, C, D)
    for d, l_abc in d_to_labc.items():
      if len(l_abc) == 1:
        (a, b, c) = l_abc[0]
        if b != min(a, b, c, d):
          print('A = {}, B = {}, C = {} and D = {}'.format(a, b, c, d))
    

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.