在分配和交换法则下找到相同的结果

时间:2012-02-11 16:14:36

标签: python operators

我有一个循环超过5个数字向量的脚本,并将它们与搜索组合的所有四个操作混合,从而产生一定的目标数字。

脚本打印输出,如:

312 / 130 x 350 - 122 + 282 = 1000.0
312 / 130 x 350 + 282 - 122 = 1000.0
312 - 282 x 372 / 15 + 256 = 1000.0
142 + 350 - 372 x 125 / 15 = 1000.0
142 + 350 - 372 / 15 x 125 = 1000.0
350 / 130 x 312 + 282 - 122 = 1000.0
350 + 142 - 372 x 125 / 15 = 1000.0

每一行都是根据数字列表和操作列表进行格式化的。

我想做的是删除等效结果,即输出如下:

312 / 130 x 350 - 122 + 282 = 1000.0
312 - 282 x 372 / 15 + 256 = 1000.0
142 + 350 - 372 x 125 / 15 = 1000.0

作为一种解决方案,起初,我想到了“记住”已经给出1000并跳过这些数字的数字,但后来我意识到它可能会影响新结果,所以我不知道该怎么做。

如何在分配和交换法律下找到相应的结果?

注意:在显示的输出中,未显示括号,但顺序类似于reduce,这意味着,例如:

142 + 350 - 372 x 125 / 15 = 1000.0

计算如下:

(((142 + 350) - 372) x 125) / 15 = 1000.0

这是我到目前为止的代码:

import operator
from itertools import permutations, product, count
from functools import reduce

vectors = [[87, 125, 209, 312],
           [29, 122, 254, 372],
           [15, 130, 277, 369],
           [142, 197, 282, 383],
           [64, 157, 256, 350]]

OPER = {operator.add: '+', operator.sub: '-', operator.mul: 'x', 
        operator.truediv: '/'}

def format_result(nums, ops, res):
    s = ' '.join('{} {}'.format(n,OPER[op]) for n,op in zip(nums, ops))
    s += ' {} = {}'.format(nums[-1], res)
    return s

def calc(vectors, test=lambda x: x == 1000.):
    for vv in permutations(vectors):
        for indexes in product((0,1,2,3), repeat=5):
            numbers = tuple(v[i] for i,v in zip(indexes, vv))
            for operations in permutations(OPER):
                res = reduce(lambda x,y,n=count(0): operations[next(n)](x,y),
                             numbers)
                if test(res):
                    print(format_result(numbers, operations, res))

calc(vectors)

1 个答案:

答案 0 :(得分:1)

我认为可以通过根据对它们执行的操作对操作数进行分组来解决问题。例如:

312 / 130 x 350 + 122 + 282   => (/, [312, 130]), (x, [350]), (+, [122, 282])

然后,您可以对这些组的顺序设置某些约束:

  1. -群组不能直接在+群组
  2. 之前发生
  3. /群组不能直接在*群组
  4. 之前发生
  5. 在每个组中,数字的顺序必须是升序
  6. 可能的分组看起来像这样:

    • “前3个asc。操作数与+连接,然后2个asc。操作数与*连接”
    • “第一个asc。操作数与+连接,然后是2个asc。操作数与*连接,然后是2个asc。操作数与/连接”

    不可能是这样的:

    • “前2个asc。操作数与-连接,然后3个asc。操作数与+连接”(将与“与+连接的前3个asc。操作数冲突,然后2个asc。操作数与-
    • 相关联

    我尝试了一种蛮力的方法来创建和填充这样的分组,但它的速度令人难以忍受。也许你可以优化它以提高效率:)也可能是那里有一些微妙的bug,但不幸的是我没有时间去研究它了:

    import operator
    import fractions
    from itertools import permutations, product, count
    from functools import reduce
    
    vectors = [[87, 125, 209, 312],
               [29, 122, 254, 372],
               [15, 130, 277, 369],
               [142, 197, 282, 383],
               [64, 157, 256, 350]]
    vectors = [[fractions.Fraction(x) for x in v] for v in vectors]
    
    operators = {
        '+': operator.add,
        '-': operator.sub,
        '*': operator.mul,
        '/': operator.div,
        }
    
    def create_groupings(n, exclude = ()):
      if n <= 0: yield ()
      for i in range(1, n+1):
        if not '+' in exclude:
          for rest in create_groupings(n - i, ('+',)):
            yield ((i, '+'),) + rest
        if not '-' in exclude:
          for rest in create_groupings(n - i, ('+', '-')):
            yield ((i, '-'),) + rest
        if not '*' in exclude:
          for rest in create_groupings(n - i, ('*',)):
            yield ((i, '*'),) + rest
        if not '/' in exclude:
          for rest in create_groupings(n - i, ('/', '*')):
            yield ((i, '/'),) + rest
    
    def fill_grouping(groups, vectors):
      if len(groups) == 0:
        yield ()
        return
    
      (group_size, op), grest = groups[0], groups[1:]
      for vv in permutations(vectors):
        vecs, vrest = vectors[:group_size], vectors[group_size:]
        for operands in map(list, product(*vecs)):
          # enforce ascending ordering to avoid collisions
          # like A + B == B + A
          if operands != sorted(operands): continue
          for rest in fill_grouping(grest, vrest):
            yield ((op, operands),) + rest
    
    groupings = create_groupings(5)
    for g in groupings:
      for groups in fill_grouping(g, vectors):
        evaluated = ((op, reduce(operators[op], x)) for (op, x) in groups)
        _, value = reduce(lambda (_, x), (op, y): (None, operators[op](x,y)), evaluated)
        if 1000 == value:
          print groups
    

    希望这有助于(至少是这个想法:)