小町算 #3
下のダメダメなのは捨てて、有理数を導入したまじめなのを。
import sys import re from rational import Rational OPERATORS = '+ - * / '.split(' ') NUM_PAT = re.compile(r'\d+') def exp_eval(exp): r = Rational fixed_exp = NUM_PAT.sub(r'r(\g<0>)', exp) return eval(fixed_exp) def komachi(numbers, result): return (exp for exp in list_exp(numbers) if exp_eval(exp) == result) def list_exp(numbers): if len(numbers) == 1: yield str(numbers[0]) else: for op in OPERATORS: for exp in list_exp(numbers[1:]): yield str(numbers[0]) + op + exp def main(): n = 0 for exp in komachi(range(1, 9 + 1), 100): print exp + '=100' n += 1 print '%d pattern(s) found' % n if __name__ == '__main__': main()
rational.py は以下。Rational 以外のオペランドを指定するとエラーになるという手抜きっぷり。
def gcd(x, y): if y == 0: return x else: return gcd(y, x % y) def lcm(x, y): return x * y / gcd(x, y) class Rational(object): def __init__(self, numerator, denominator = 1): self.numerator = int(numerator) self.denominator = int(denominator) c = gcd(self.numerator, self.denominator) if c != 1: self.numerator /= c self.denominator /= c def __neg__(self): return Rational(-self.numerator, self.denominator) def __add__(self, other): denominator = lcm(self.denominator, other.denominator) a = denominator / self.denominator b = denominator / other.denominator return Rational(self.numerator * a + other.numerator * b, denominator) def __sub__(self, other): return self + (-other) def __mul__(self, other): return Rational(self.numerator * other.numerator, self.denominator * other.denominator) def __div__(self, other): return self * Rational(other.denominator, other.numerator) def __float__(self): return float(self.numerator) / self.denominator def __int__(self): return self.numerator / self.denominator def __eq__(self, other): if isinstance(other, Rational): return (self.numerator, self.denominator) == (other.numerator, other.denominator) elif isinstance(other, int): return self.denominator == 1 and self.numerator == other else: return float(self) == other def __str__(self): if self.denominator == 1: return str(self.numerator) else: return '%d/%d' % (self.numerator, self.denominator) def __repr__(self): return '%d/%d' % (self.numerator, self.denominator)
で、電卓版に対応するために演算子の優先順位を定義できるようにしてみたバージョン。実行時に -c を付けると電卓モード。
import sys import operator from rational import Rational def join(a, b): return a * Rational(10) + b OPERATORS = { '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.div, '': join, } OPERATOR_PRIORITIES_NORMAL = { '+': 2, '-': 2, '*': 1, '/': 1, '': 0, } OPERATOR_PRIORITIES_CALC = { '+': 1, '-': 1, '*': 1, '/': 1, '': 0, } def exp_eval(exp, priorities): level = 0 exp = list(exp) while len(exp) > 1: res = [] while exp: val = exp.pop(0) if isinstance(val, Rational): res.append(val) else: func = OPERATORS[val] priority = priorities[val] if priority == level: res.append(func(res.pop(), exp.pop(0))) else: res.append(val) level += 1 exp = res return exp[0] def list_exp(numbers): if len(numbers) == 1: yield numbers else: for op, func in OPERATORS.iteritems(): for exp in list_exp(numbers[1:]): yield numbers[0:1] + [op] + exp def komachi(numbers, result, operator_priorities): numbers = [Rational(i) for i in numbers] result = Rational(result) return (exp for exp in list_exp(numbers) if exp_eval(exp, operator_priorities) == result) def main(calc_mode = False): if calc_mode: operator_priorities = OPERATOR_PRIORITIES_CALC else: operator_priorities = OPERATOR_PRIORITIES_NORMAL numbers = range(1, 9 + 1) result = 100 n = 0 for exp in komachi(numbers, result, operator_priorities): print '%s=%d' % (''.join(map(str, exp)), result) n += 1 print '%d pattern(s) found' % n if __name__ == '__main__': import optparse parser = optparse.OptionParser() parser.add_option('-c', action='store_true', dest='calc_mode') (opts, args) = parser.parse_args() main(calc_mode = opts.calc_mode)
結果。
% time python komachi4.py 123+45-67+8-9=100 123+4*5-6*7+8-9=100 123+4-5+67-89=100 123-45-67+89=100 123-4-5-6-7+8-9=100 12+34+5*6+7+8+9=100 12+34-5+6*7+8+9=100 12+34-5-6+7*8+9=100 12+34-5-6-7+8*9=100 12+3+4+5-6-7+89=100 12+3+4-56/7+89=100 12+3*45+6*7-89=100 12+3*4+5+6+7*8+9=100 12+3*4+5+6-7+8*9=100 12+3*4-5-6+78+9=100 12+3-4+5+67+8+9=100 12*3-4+5-6+78-9=100 12*3-4*5+67+8+9=100 12*3-4-5-6+7+8*9=100 12-3+4*5+6+7*8+9=100 12-3+4*5+6-7+8*9=100 12-3-4+5*6+7*8+9=100 12-3-4+5*6-7+8*9=100 12-3-4+5-6+7+89=100 12/3+4*5*6*7/8-9=100 12/3+4*5*6-7-8-9=100 12/3+4*5-6-7+89=100 12/3/4+5*6+78-9=100 1+234*5*6/78+9=100 1+234*5/6-7-89=100 1+234-56-7-8*9=100 1+23*4+56/7+8-9=100 1+23*4+5-6+7-8+9=100 1+23*4-5+6+7+8-9=100 1+23-4+56+7+8+9=100 1+23-4+56/7+8*9=100 1+23-4+5+6+78-9=100 1+23-4-5+6+7+8*9=100 1+2+34*5+6-7-8*9=100 1+2+34-5+67-8+9=100 1+2+3+4+5+6+7+8*9=100 1+2+3*4*56/7-8+9=100 1+2+3*4*5/6+78+9=100 1+2+3*4-5-6+7+89=100 1+2+3-45+67+8*9=100 1+2+3-4+5+6+78+9=100 1+2+3-4*5+6*7+8*9=100 1+2*34-56+78+9=100 1+2*3+4+5+67+8+9=100 1+2*3+4*5-6+7+8*9=100 1+2*3*4*5/6+7+8*9=100 1+2*3-4+56/7+89=100 1+2*3-4-5+6+7+89=100 1+2-3*4+5*6+7+8*9=100 1+2-3*4-5+6*7+8*9=100 1*234+5-67-8*9=100 1*23+4+56/7*8+9=100 1*23+4+5+67-8+9=100 1*23*4-56/7/8+9=100 1*23-4+5-6-7+89=100 1*23-4-56/7+89=100 1*2+34+56+7-8+9=100 1*2+34+5+6*7+8+9=100 1*2+34+5-6+7*8+9=100 1*2+34+5-6-7+8*9=100 1*2+34-56/7+8*9=100 1*2+3+45+67-8-9=100 1*2+3+4*5+6+78-9=100 1*2+3*4+5-6+78+9=100 1*2+3-4+5*6+78-9=100 1*2*34+56-7-8-9=100 1*2*3+4+5+6+7+8*9=100 1*2*3*4+5+6+7*8+9=100 1*2*3*4+5+6-7+8*9=100 1*2*3*4-5-6+78+9=100 1*2*3-45+67+8*9=100 1*2*3-4+5+6+78+9=100 1*2*3-4*5+6*7+8*9=100 1*2-3+4+56/7+89=100 1*2-3+4*5-6+78+9=100 1*2-3+4-5+6+7+89=100 1*2/3+4*5/6+7+89=100 1-23+4*5+6+7+89=100 1-23-4+5*6+7+89=100 1-23-4-5+6*7+89=100 1-2+3+45+6+7*8-9=100 1-2+3*4+5+67+8+9=100 1-2+3*4*5+6*7+8-9=100 1-2+3*4*5-6+7*8-9=100 1-2*3+4*5+6+7+8*9=100 1-2*3-4+5*6+7+8*9=100 1-2*3-4-5+6*7+8*9=100 1-2-34+56+7+8*9=100 1-2-3+45+6*7+8+9=100 1-2-3+45-6+7*8+9=100 1-2-3+45-6-7+8*9=100 1-2-3+4*56/7+8*9=100 1-2-3+4*5+67+8+9=100 1/2*34-5+6-7+89=100 1/2*3/4*56+7+8*9=100 1/2/3*456+7+8+9=100 101 pattern(s) found python komachi4.py 37.97s user 0.00s system 99% cpu 38.083 total
% time python komachi4.py -c 123+45-67+8-9=100 123+4-5+67-89=100 123-45-67+89=100 123-45/6+78+9=100 123-4-5-6-7+8-9=100 12+3+4+5-6-7+89=100 12+3+4+5/6+7+89=100 12+3*4-56+7+89=100 12+3*4/5+6-7+89=100 12+3-4+5+67+8+9=100 12*3-4+5-6+78-9=100 12-3+4+5*6-7+8-9=100 12-3*4+56+7-8+9=100 12-3-4+5-6+7+89=100 12-3-4*5+6+78-9=100 12/3+4-5*6-7+89=100 12/3*4+5+6*7-89=100 1+23+4+5-6*7-89=100 1+23-4+56+7+8+9=100 1+23-4+5+6+78-9=100 1+23-4*5+6-7-8+9=100 1+23-4*5-6+7+8-9=100 1+23-4-5*6-7+8+9=100 1+2+34-5+67-8+9=100 1+2+3+4+5*6-7+8+9=100 1+2+3+4*5+67-8-9=100 1+2+3*4-5-6+78+9=100 1+2+3-4+5+6+78+9=100 1+2+3-4+5/6*78+9=100 1+2+3-4*5-6+7+89=100 1+2*3+4+5*6-7+8-9=100 1+2*3*4+56+7-8+9=100 1+2*3-4+5-6+7+89=100 1+2*3-4*5+6+78-9=100 1+2/3+4+5-6+7+89=100 1+2/3+4*5+6+78-9=100 1*23+4+5+67-8+9=100 1*23-4+5-6-7+89=100 1*23-4+5/6+7+89=100 1*2+34+56+7-8+9=100 1*2+3+45+67-8-9=100 1*2+3*4+56+7+8+9=100 1*2+3*4+5+6+78-9=100 1*2+3*4*5+6-7-8+9=100 1*2+3*4*5-6+7+8-9=100 1*2+3*4-5*6-7+8+9=100 1*2*34+56-7-8-9=100 1*2*3+4+5*6-7+8+9=100 1*2*3+4*5+67-8-9=100 1*2*3*4-5-6+78+9=100 1*2*3-4+5+6+78+9=100 1*2*3-4+5/6*78+9=100 1*2*3-4*5-6+7+89=100 1*2-3+4*5*6-7+8+9=100 1*2-3+4-5+6+7+89=100 1*2/3/4+5*6+78-9=100 1*2/3/4-5+6*78+9=100 1-2+3*4-5*6-7+89=100 1-2+3-4+5*6-7+89=100 1-2*3-4+5+6+7+89=100 1-2/3+4*5*6+7-8-9=100 1-2/3-4+5*6+7+89=100 1/2+3*4+5-6+78+9=100 1/2+3*4*5+6+7+8+9=100 1/2+3-4+5*6*7-89=100 1/2*34-5+6-7+89=100 1/2-3*4/5+6+7+89=100 1/2/3*456+7+8+9=100 68 pattern(s) found python komachi4.py -c 34.96s user 0.01s system 99% cpu 35.011 total