在分配律和交换律下找到等效结果

发布于 2025-01-04 19:20:08 字数 1920 浏览 0 评论 0原文

我有一个脚本,它循环 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)

I have a script that loops over 5 vectors of numbers and mix them with all the four operations searching for a combination that give a certain target number as a result.

The script prints an output like:

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

Each line is formatted from a list of the numbers and a list of the operations.

What I'd like to do, is to remove the equivalent results, i.e. having an output like:

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

As a solution, at first, I thought about "remembering" the numbers that already gave 1000 and skip those, but then I realized it might shadow new results, so I don't know what to do.

How can I find the equivalent results under distributive and commutative laws?

Note: In the presented output the parentheses are NOT shown, but the order is reduce-like, meaning that, for example:

142 + 350 - 372 x 125 / 15 = 1000.0

is calculated like:

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

This is the code I have so far:

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

太阳哥哥 2025-01-11 19:20:08

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

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

然后,您可以对这些组的顺序施加某些限制:

  1. - 组不能直接出现在 + 组之前
  2. / 组不能直接出现在 + 组之前 每个组内的*
  3. ,数字的顺序必须是升序

因此,可能的分组如下所示:

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

不可能是这样的:

  • “前2个asc与-连接,然后3个asc与+连接。 ” (会与“前 3 个 asc. 操作数与 + 连接,然后 2 个 asc.操作数与 - 连接)发生冲突

我尝试了一种强力方法来创建和填充此类分组,但它慢得难以忍受。也许你可以优化它以提高效率:)也可能是那里有一些微妙的错误,但不幸的是我没有更多的时间来处理它:

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

希望这有帮助(在至少这个想法:)

I think the problem can be approached by grouping the operands according to the operations performed on them. Example:

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

You can then place certain constraints on the order of these groups:

  1. - groups cannot occur directly before + groups
  2. / groups cannot occur directly before * groups
  3. within each group, the order of the numbers must be ascending

Possible groupings would thus look like this:

  • "first 3 asc. operands connected with a +, then 2 asc. operands connected with a *"
  • "first 1 asc. operand connected with a +, then 2 asc. operands connected with a *, then 2 asc. operands connected with /"

Impossible would be something like this:

  • "first 2 asc. operands connected with a -, then 3 asc. operands connected with +" (would collide with "first 3 asc. operands connected with a +, then 2 asc. operands connected with -

I tried a brute-force approach to create and fill such groupings, but it's unbearably slow. Maybe you can optimize it to be more efficient :) Could also be that there's some subtle bug in there, but unfortunately I don't have any more time to work on it:

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

Hope this helps (at least the idea :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文