Here's another program that evaluates all the well-formed RPN (Reverse Polish Notation) strings made up with numbers $\{5,10,15,20\}$ and operators $\{+,*,-,/,\hat{}\}$.
""" Make numbers in range(1,21) out of {5, 10, 15, 20} with {+ - * / ^}.
Each number must be used exactly once.
The program forms RPN strings and evaluates them, reporting those that
produce integers in the range from 1 to 20 (included).
"""
from __future__ import print_function
def eval_rpn(s):
""" Evaluates RPN string. """
global rcnt, ucnt
es = []
for x in s:
if x in numbers:
es.append(float(x))
else:
v2 = es.pop()
v1 = es.pop()
if x == '+':
es.append(v1+v2)
elif x == '*':
es.append(v1*v2)
elif x == '-':
es.append(v1-v2)
elif x == '/':
if v1 % v2 != 0:
raise ValueError
es.append(v1/v2)
elif x == '^':
es.append(v1**v2)
else:
raise RuntimeError()
if len(es) != 1:
raise RuntimeError()
value = es[0]
if value in range(1,21):
rcnt += 1
print(', '.join(s), '=', value)
if not value in sample:
sample[value] = s
if value in [14, 16]:
ucnt += 1
print('******** whoa! ********')
def pick_cand_tk(s, nums):
""" Picks all tokens that may be used to extend RPN string. """
cand = []
for n in numbers:
if not n in s:
cand.append(n)
if 2*nums - len(s) > 1:
cand.extend(['+', '*', '-', '/', '^'])
return cand
def rpn_recur(s,nums):
""" Recursively construct well-formed RPN strings. """
global tcnt
if len(s) == 7:
try:
eval_rpn(s)
except (OverflowError, ZeroDivisionError, ValueError):
pass
tcnt += 1
else:
for c in pick_cand_tk(s, nums):
rpn_recur(s + [c], nums + (c in numbers))
numbers = ['5', '10', '15', '20']
tcnt = 0 # total number of leaves
rcnt = 0 # number of leaves whose evaluation is in range(1,21)
ucnt = 0 # number of unexpected values
sample = {}
rpn_recur([], 0)
print('leaves: total =', tcnt, ' in-range =', rcnt, ' unxepected =', ucnt)
for k in sample:
print(int(k), '->', ', '.join(sample[k]))
The program examines 15000 RPN strings, finds that than 852 evaluate to an integer between 1 and 20, also finds that no string evaluates to either 14 or 16, and finally prints examples of how to obtain the other numbers in the range.
Computations are carried out with integer values throughout. This excludes results like $(20/5)^{15/10} = 8$. To include them, one comments out the check on the $0$ remainder in eval_rpn. In any case, $14$ and $16$ remain off limits.