Enigmatic Code

Programming Enigma Puzzles

Enigma 1698: The pied piper

From New Scientist #2865, 19th May 2012 [link]

I found an old eight-digit calculator. As usual, each position of the display consisted of seven light segments – with, for example, seven lighting up to display an “8” and four lighting up to display a “4”.

To see if it still worked, I typed in the approximation for πœ‹, namely 22/7, and found that I saw a number, but not the correct one. After some investigation I found that one of the 56 segments always failed to light. I then used 22/7 again to calculate approximations for 2πœ‹, 3πœ‹, 4πœ‹, 5πœ‹, 6πœ‹, 7πœ‹, 8πœ‹, 9πœ‹ and 10πœ‹.

In two-thirds or more of these cases the answer looked like a number, but:

(a) how many of them were correct? and
(b) what was the original eight-digit number seen in the display?

[enigma1698]

Advertisements

8 responses to “Enigma 1698: The pied piper

  1. Jim Randell 16 May 2012 at 5:12 pm

    The following Python program runs in 39ms.

    It makes a number of (not unreasonable) assumptions about the specifics of the puzzle (i.e. the way 6, 7 and 9 are displayed on the 7-segment display, the way 7 × 22 Γ· 7 is displayed and also that the calculator truncates rather than rounding up in the final decimal place).

    from collections import defaultdict
    from itertools import permutations
    from enigma import irange, concat, printf
    
    # map digits to segments, arranged as:
    #   0
    # 1   2
    #   3
    # 4   5
    #   6
    segments = list(set(i) for i in (
      (0, 1, 2, 4, 5, 6), # 0
      (2, 5), # 1
      (0, 2, 3, 4, 6), # 2
      (0, 2, 3, 5, 6), # 3
      (1, 2, 3, 5), # 4
      (0, 1, 3, 5, 6), # 5
      (0, 1, 3, 4, 5, 6), # 6, or could be (1, 3, 4, 5, 6)
      (0, 2, 5), # 7, or could be (0, 1, 2, 5)
      (0, 1, 2, 3, 4, 5, 6), # 8
      (0, 1, 2, 3, 5, 6), # 9, or could be (0, 1, 2, 3, 5)
    ))
    
    # find digits that display a different digit if one of the segments fails
    m = defaultdict(lambda: defaultdict(int))
    for (i, j) in permutations(range(10), 2):
      if i == j: continue
      (a, b) = (segments[i], segments[j])
      if not a.issubset(b): continue
      s = b.difference(a)
      if not len(s) == 1: continue
      # record: actual digit => failed segment => displayed digit
      m[str(j)][str(i)] = s.pop()
    
    # what are the displayed numbers (assuming no rounding)
    # we reverse the numbers to keep the digit indices consistent
    numbers = list('{:.8g}'.format(n * 22.0 / 7.0).replace('.', '')[7::-1] for n in irange(1, 10))
    initial = list(numbers.pop(0))
    
    # check the numbers when digit i, segment f has failed
    def check(i, f, d):
      (t, c) = (0, 0) # count invalid and changed numbers
      for n in numbers:
        # is the digit at position i in this number?
        if not(i < len(n)): continue
        # check the failed segment should be lit in this number
        if f not in segments[int(n[i])]: continue
        # and that the failure doesn't make a valid digit
        if f in m[n[i]].values():
          c += 1
        else:
          t += 1
          # at most three numbers are allowed to invalid
          if t > 3: return
      # so, we've found a solution... what was the initial display?
      x = initial[::]
      x[i] = d
      printf("correct={c} display={x} [fault in digit {i}, segment {f}]", c=len(numbers)-(t+c), x=concat(*x)[::-1])
    
    # find a digit in the initial number that is changed by the failed segment
    for (i, n) in enumerate(initial):
      if n not in m: continue
      for (d, f) in m[n].items():
        # failure in digit i, segment f, actual digit = n, displayed digit = d
        check(i, f, d)
    

    Solution: (a) 7 of the numbers were displayed correctly. (b) the original number seen in the display was 3.1429571.

    The officially published solution gives 6 numbers displaying correctly for part (a) of the answer, although in order to arrive at this solution the calculator would have to display “22” as “22.000000” (fixed point mode), which although not impossible seems less likely than the display of “22”. If you prefer this solution you can simply replace the '{:.8g}' format string with '{:.8f}' when the numbers to check are generated. I did request clarification on this matter from New Scientist at the time the puzzle was published, but got no response.

  2. arthurvause 16 May 2012 at 7:51 pm

    Another one best done on excel rather than searching using python or SQL code.

    This spreadsheet solution is a bit cryptic but here it is anyway.
    https://www.dropbox.com/s/j4do5hledklxqm3/Enigma1698.xlsx

  3. Brian Gladman 17 May 2012 at 12:24 am

    Here is my solution — my answer is different because I make a different assumption about the displayed value of 7 * (22 / 7) :

    
    # map from digits to lit segemnts
    digit_to_segments = {
      '0':0b1110111, '1':0b0010010, '2':0b1011101, '3':0b1011011, '4':0b0111010,
      '5':0b1101011, '6':0b1101111, '7':0b1010010, '8':0b1111111, '9':0b1111011 }
    
    # map from lit segemnts to digits
    segments_to_digit = {v:k for k,v in digit_to_segments.items()}
    
    # translate a number to displayed result with a bad segment
    def num(x, bad_digit, bad_segment):
      # get segments for each digit
      seg = [digit_to_segments[c] for c in str(x)]
      # turn bad segment off
      seg[bad_digit] &= ~(2 ** bad_segment)
      # return displayed number when possible, otherwise None
      try:
        return int(''.join(segments_to_digit[s] for s in seg))
      except KeyError:
        return None
    
    fs = ('{0:d} were correct, the original display was {1:s}, '
         '(bad: digit {2:d}, segment {3:d})')
    pi = 22 / 7
    for bad_digit in range(8):
      for bad_segment in range(7):
        correct_pi = round(pi * 10 ** 7)
        display_pi = num(correct_pi, bad_digit, bad_segment)
        if display_pi and display_pi != correct_pi:  
          number, correct = 0, 0
          for i in range(2, 11):
            correct_mpi = round(i * pi * 10 ** (7 if i < 4 else 6))
            display_mpi = num(correct_mpi, bad_digit, bad_segment)
            if display_mpi:
              number += 1
              if display_mpi == correct_mpi:
                correct += 1
          if number >= 6:
            sd = str(display_pi)
            sd = sd[0] + '.' + sd[1:]
            print(fs.format(correct, sd, bad_digit + 1, 7 - bad_segment))
    
    • arthurvause 17 May 2012 at 1:48 pm

      I hadn’t spotted the case (22/7)*7. Here is a spruced up spreadsheet. https://www.dropbox.com/s/j4do5hledklxqm3/Enigma1698.xlsx

      The way of displaying a “7” with 3 segments opens up another avenue to explore (shown in new spreadsheet), but one which doesn’t lead to a possible solution anyway, so the original number seen has to be 3.1429571

      My old casio fx-361 displays numbers as shown in the spreadsheet, i.e. 4 segments for a “7”, 6 segments for a “6” and a “9”

  4. Jim Randell 17 May 2012 at 4:57 pm

    I think the question could have been a little clearer. But I tried to make the most reasonable assumptions I could.

    I think most calculators (even old ones) would display “22.” rather than “22.000000” (unless forced into fixed point mode). Although depending on which one of these you plump for you get a different answer to part (a) of the solution. Maybe New Scientist will permit both answers.

    Final digit rounding doesn’t affect the answer, as the final digit can’t be the one with the failed segment.

    I also dug out my old fx-450, and found it also uses the 4-segment “7”, and 6-segment “6” and “9”. Changing to the 4-segment “7” doesn’t affect my solution (although it removes the possibility of “7” displaying as “1” if a segment fails). However, using the 5-segment versions of “6” and “9” yields no solutions.

    • Jim Randell 20 May 2012 at 10:06 am

      Interestingly it seems that old Casio LCD watches use the 3-segment “7”, whereas old Casio calculators use the 4-segment version.

    • Jim Randell 20 May 2012 at 9:49 pm

      I used to have an HP-11C which didn’t do suppression of trailing decimal zeros in the fractional part, so would display 22 as “22.00000000” (it had a 10 digit display), although it was an LCD display, and the question implies an LED display.

      I did have a Commodore calculator, which had an LED display, and I think it was an 8 digit display, so it would be the closest to the one described in the question. But I’ve no idea where it is now.

      • Hugh Casement 30 October 2014 at 7:23 pm

        Over the years I’ve acquired a number of pocket calculators of various makes. They all suppress trailing zeros after the decimal point.

        The wording “I then used 22/7 again …” is a bit strange. Most calculators have an automatic constant, so having displayed the first quotient you press + and then = repeatedly for successive multiples.

        And are people really still using 22/7 as an approximation to pi? 355/113 is so much more accurate (better than 7 digits) and easy to remember, with its pairs of odd digits.

        Strange how calculators all seem to use four segments for 7, while clocks and watches (not just Casio) use three.

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: