New Scientist Enigma 970 – From Line to Line
by Keith Austin
From New Scientist #2125, 14th March 1998
Take a large sheet of lined paper. On line 1 write any number between 0 and 100 (but not necessarily a whole number). In turn, write a number on each of the lines, 2, 3, 4, .. 100, according to the following rule:
Suppose you have just written a number X on a line. If X is less than 50 then write 40 plus half of X on the next line, otherwise write 93 minus half of X.
(A) Can I say now, before you write your number on the 1st line, what the nearest whole number to the number you write on the 100th line will be? If yes, then what is that nearest whole number?
Take a second sheet of lined paper and repeat the above, except that the rule if X is less than 50 is changed; now write 20 plus half of X on the next line.
(B) My question now is as in question A.
Now take a third sheet of lined paper and repeat the procedure, except that the rule becomes the following. If X is less than 50 then write 40 plus half of X, otherwise, write 15 plus half of X.
(C) Once again, my question is as in question A.

Brian Gladman permalink12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152from collections import defaultdict# run <trials> trials with function <fn>def trial(trials, fn):# for collecting inputs indexed on outcomessm = defaultdict(list)# run <trials> trialsfor i in range(trials + 1):# use points unifromly disributed over 0..100v = x = 100 * i / trials# do the 99 'lines'for _ in range(99):x = fn(x)# record the nearest integer resultsm[round(x)].append(v)return sm# find input ranges that give each output valuedef intervals(r, vl):# collect values in 0.25 interval bucketsbkt = [0] * 401for v in vl:bkt[int(4 * v)] += 1# find continuous ranges in which buckets are not zeromn, mx, intv = 0, 0, []for i in range(401):# buckets transition from zero to nonzeroif (i == 0 or not bkt[i  1]) and bkt[i]:mn = i // 4# buckets transition from nonzero to zeroif bkt[i] and (i == 400 or not bkt[i + 1]):mx = (i + 3) // 4# record the nozero intervalintv.append(f"[{mn}..{mx}{')' if i < 400 else ']'}")return ' & '.join(intv)fns = ( lambda x: (80 + x if x < 50 else 186  x) / 2,lambda x: (40 + x if x < 50 else 186  x) / 2,lambda x: (80 + x if x < 50 else 30 + x) / 2 )for n, fn in zip('ABC', fns):sm = trial(400, fn)s = []for r, vl in sorted(sm.items()):iv = intervals(r, vl)s.append(f"{iv} > {r}")print(f"{n}: {['Yes ', 'No ='][len(sm.keys())  1]}==> {'; '.join(s)}")