Advertisement
faubiguy

fourfours.py

Dec 7th, 2015
205
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.12 KB | None | 0 0
  1. import operator
  2. import sys
  3. import argparse
  4. from collections import namedtuple
  5.  
  6. # Parse command line arguments
  7. parser = argparse.ArgumentParser(description='Generate solutions to the four fours problem')
  8. parser.add_argument('-d', '--display', choices=['number', 'expression', 'both'], default='both', help='What to display for each result')
  9. parser.add_argument('-c', '--count', type=int, default=4, help='The number of fours to use')
  10. parser.add_argument('-n', '--number', type=int, default=4, help='The number to use in expressions')
  11. parser.add_argument('-p', '--positive', action='store_true', help='Only output positive numbers')
  12. parser.add_argument('-t', '--use-concat', action='store_true', help='Allow concatenating numbers (i.e. 4 and 4 to 44)')
  13. parser.add_argument('-m', '--max-exponent', type=int, default=4096, help='The maximum number to allow as an exponent')
  14. args = parser.parse_args()
  15.  
  16. # Initialize operators to use
  17. Operator = namedtuple('Operator', ['function', 'symbol', 'precedence', 'associativity', 'commutative'])
  18.  
  19. add = Operator(operator.add, '+', 3, 'left', True)
  20. sub = Operator(operator.sub, '-', 3, 'left', False)
  21. mul = Operator(operator.mul, '*', 2, 'left', True)
  22. div = Operator(operator.floordiv, '/', 2, 'left', False)
  23. pow = Operator(operator.pow, '^', 1, 'right', False)
  24. concat = Operator(lambda a, b: 10*a+b, '', 0, 'left', True)
  25.  
  26. operators = [add, sub, mul, div, pow]
  27.  
  28. if args.use_concat:
  29.     operators.append(concat)
  30.  
  31. # Class representing an operator in the expression tree
  32. class Node:
  33.     def __init__(self, op, left, right):
  34.         self.op = op
  35.         self.left, self.right = left, right
  36.  
  37.     # Evaluate the expression
  38.     def evaluate(self):
  39.         if type(self.left) == Node:
  40.             left = self.left.evaluate()
  41.         else:
  42.             left = self.left
  43.  
  44.         if type(self.right) == Node:
  45.             right = self.right.evaluate()
  46.         else:
  47.             right = self.right
  48.  
  49.         # Check arguments to '^' to avoid very large or fractional results
  50.         if self.op == pow and right > args.max_exponent or right < 0:
  51.             raise ValueError
  52.         # Check arguments to '/' to avoid divinding by 0 or fractional results
  53.         if self.op == div and (right == 0 or left % right != 0):
  54.             raise ValueError
  55.         return self.op.function(left, right)
  56.  
  57.     # Return the expression as a string
  58.     def __str__(self):
  59.         left = str(self.left)
  60.         right = str(self.right)
  61.         # Parenthesize argumements based on precedence, associativity, and commutativity
  62.         if (type(self.left) == Node and
  63.                 (self.left.op.precedence > self.op.precedence or
  64.                     (self.left.op.precedence == self.op.precedence and
  65.                         self.left.op.associativity == 'right' and not self.op.commutative))):
  66.             left = '(' + left +')'
  67.         if (type(self.right) == Node and
  68.                 (self.right.op.precedence > self.op.precedence or
  69.                     (self.right.op.precedence == self.op.precedence and
  70.                         self.right.op.associativity == 'left' and not self.op.commutative))):
  71.             right = '(' + right +')'
  72.         return left + self.op.symbol + right
  73.  
  74. # Generator to produce all expression trees.
  75. # Modified from http://nedbatchelder.com/blog/200802/enumerating_trees.html
  76. def trees(stack, fours):
  77.     if len(stack) == 1 and fours == 0:
  78.         yield stack[0]
  79.     if len(stack) > 1:
  80.         for op in operators:
  81.             left = stack[-2]
  82.             right = stack[-1]
  83.             if op.commutative and type(right) == Node and right.op == op:
  84.                 continue
  85.             if (op == concat and ((type(right) == Node and right.op != concat) or
  86.                     (type(left) == Node and left.op != concat))):
  87.                 continue
  88.             new_node = Node(op, left, right)
  89.             new_stack = stack[:-2]
  90.             new_stack.append(new_node)
  91.             for t in trees(new_stack, fours):
  92.                 yield t
  93.     if fours >= 0:
  94.         stack.append(args.number)
  95.         for t in trees(stack, fours - 1):
  96.             yield t
  97.  
  98. # Dictionary to store results in
  99. results = {}
  100.  
  101. # Loop through expressions, adding new ones to results
  102. for tree in trees([], args.count):
  103.     try:
  104.         result = tree.evaluate()
  105.     except (ValueError):
  106.         continue
  107.     if result in results:
  108.         continue
  109.     results[result] = str(tree)
  110.    
  111. # For integer to string conversions, first try to use gmpy2 for performance, then fallback to the standard library
  112. # Using gmpy2 is highly recommended if --count is increased
  113. try:
  114.     from gmpy2 import digits as int_to_str
  115. except ImportError:
  116.     int_to_str = str
  117.  
  118. # Loop through results to print. Key function puts positives first then orders by magnitude.
  119. for number in sorted(results.keys(), key=lambda n:(n<0, abs(n))):
  120.     if number < 0 and args.positive:
  121.         continue
  122.     output = ''
  123.     if args.display in ('number', 'both'):
  124.         output += int_to_str(number)
  125.     if args.display  == 'both':
  126.         output += ': '
  127.     if args.display in ('expression', 'both'):
  128.         output += results[number]
  129.     print(output)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement