Enigmatic Code

Programming Enigma Puzzles

Tantalizer 435: Compleat idiots

From New Scientist #986, 5th February 1976 [link]

The landlord of the Compleat Idiot likes to add spice to the day’s angling. Each angler starts by predicting everyone’s catch and there is a double scotch for each correct prediction afterwards. Yesterday no one was right about anyone, each man having predicted too few for those who beat him and too many for those who did not (including himself). Everyone caught at least one fish and all caught a different number. If I tell you the predictions (predictors down the left, persons predicted for across the top), can you work out the actual catches?

[tantalizer435]

One response to “Tantalizer 435: Compleat idiots

  1. Jim Randell 20 February 2019 at 7:26 am

    This Python 3 program reuses the [[ Interval() ]] class I wrote for Enigma 70 and Enigma 201.

    The program considers the possible orders of the anglers (from smallest catch to largest catch), and sees if intervals can be created that satisfy all the predictions.

    It runs in 89ms.

    Run: [ @repl.it ]

    from itertools import permutations
    from enigma import irange, sprintf, join, printf
    
    # represent an interval by its min and max values
    class Interval(object):
     
      def __init__(self, min=None, max=None):
        self.min = 0 # lower limit
        self.max = 999 # upper limit
        self.update(min, max)
     
      def __repr__(self):
        return sprintf("<{self.min},{self.max}>")
     
      # update the min/max values
      def update(self, min=None, max=None):
        if min is not None and min > self.min: self.min = min
        if max is not None and max < self.max: self.max = max
        if self.min > self.max: raise ValueError(sprintf("Bad Interval {self}"))
    
      # iterate through the values in an interval
      def __iter__(self):
        yield from irange(self.min, self.max)
    
      # an interval that is the intersection of this interval and another
      def intersect(self, other):
        return Interval(min=max(self.min, other.min), max=min(self.max, other.max))
    
    
    # label the anglers
    anglers = irange(0, 5)
    
    # the table
    table = (
      (9, 4, 8, 3, 1, 6),
      (6, 7, 5, 1, 6, 7),
      (6, 9, 16, 5, 10, 3),
      (4, 2, 4, 8, 9, 5),
      (9, 5, 7, 9, 12, 9),
      (8, 5, 10, 7, 5, 11),
    )
    
    # generate intervals for a given order
    def intervals(order):
      # split the order
      for (i, X) in enumerate(order):
        I = Interval(min=1)
        # everyone before X underestimates their catch
        for Y in order[:i]:
          I.update(min=table[Y][X] + 1)
        # everyone after X overestimates their catch
        for Y in order[i:]:
          I.update(max=table[Y][X] - 1)
        yield I
    
    # generate strictly increasing sequences from a collection of intervals
    def sequences(intervals, s=[]):
      # are we done?
      if not intervals:
        yield s
      else:
        i = intervals[0]
        if s: i = i.intersect(Interval(min=s[-1] + 1))
        for x in i:
          yield from sequences(intervals[1:], s + [x])
      
    
    # count the solutions
    n = 0
    
    # consider possible orders
    for order in permutations(anglers):
    
      try:
        s = list(intervals(order))
      except ValueError:
        continue
    
      printf("order = ({order}), intervals = {s}", order=join(("ABCDEF"[i] for i in order), sep=" "))
    
      # try to find solutions
      for r in sequences(s):
        printf(" -> {r}")
        n += 1
    
    printf("[{n} solutions]")
    

    Solution: The actual catches are: A=7, B=1, C=6, D=2, E=11, F=8.

    There is only one order that gives non-empty intervals for each angler. That is (B, D, C, A, F, E), from smallest catch to largest catch. And in this case each of the intervals defines a single possible amount.

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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

%d bloggers like this: