How to brute force arithmetic puzzle?

这一生的挚爱 提交于 2020-06-24 08:58:32

问题


A friend shared this puzzle:

How to make 21 from the numbers 1, 5, 6, 7?

You can use the operations of addition, subtraction, multiplication and division, as well as brackets. You must use each number once.

I eventually solved it on paper—two days later. No doubt a computer could brute force all solutions in a second.

How though? I tried generating all strings a.b.c.d inserting the numbers for letters and operations for dots, but it missed my solution.


Bonus puzzles:

  • How to make 11 from 1,5,6,7?
  • How to make 16 from 1,5,6,7?

回答1:


An obvious approach would be the folllwing:

  1. You start with a vector S of four numbers: S = ( 1, 5, 6, 7 )
  2. Pick any two numbers a and b from S, remove them from S
  3. Apply an arbitrary arithmetic operation to a and b, thus obtaining a new number c (take care to avoid division by zero and verify exact division, if the problem requires it)
  4. Include c into S, thus obtaining S'
  5. Continue from step 1, now using S' in place of S

Brute-force branching is performed at step 2 (selecting two numbers), and step 3 (selecting operation). The cycle should be repeated until S reduces to only 1 element, which is the result for this particular brute-force branch.

Brackets are not used explicitly, but they are present implicitly.

If one branch ends with 21 in S, you have a solution, at which point you can either terminate, or continue branching to search for other solutions.




回答2:


Here's the "pop two, combine, recurse" algorithm as suggested by AnT, coded in Python. The hardest part was assembling the expressions after the recursion. I used find-and-replace.

#!python
import operator
import itertools
from fractions import Fraction

operations = dict()
operations['+'] = operator.add
operations['-'] = operator.sub
operations['/'] = operator.truediv
operations['*'] = operator.mul

def solve(target, numbers):
    """List ways to make target from numbers."""
    numbers = [Fraction(x) for x in numbers]
    return solve_inner(target, numbers)

def solve_inner(target, numbers):
    if len(numbers) == 1:
        if numbers[0] == target:
            yield str(target)
        return

    # combine a pair of numbers with an operation, then recurse
    for a,b in itertools.permutations(numbers, 2):
        for symbol, operation in operations.items():
            try:
                product = operation(a,b)
            except ZeroDivisionError:
                continue

            subnumbers = list(numbers)
            subnumbers.remove(a)
            subnumbers.remove(b)
            subnumbers.append(product)

            for solution in solve_inner(target, subnumbers):
                # expand product (but only once)
                yield solution.replace(str(product), "({0}{1}{2})".format(a, symbol, b), 1)

if __name__ == "__main__":
    numbers = [1, 5, 6, 7]
    target = 21
    solutions = solve(target, numbers)
    for solution in solutions:
        print("{0}={1}".format(target, solution))

Solutions to the three puzzles:

>>> solve(21,[1,5,6,7])
6/(1-(5/7))
>>> solve(11,[1,5,6,7])
(6*(7-5))-1
((7-5)*6)-1
>>> solve(16,[1,5,6,7])
[]

(The third puzzle is impossible)



来源:https://stackoverflow.com/questions/29353375/how-to-brute-force-arithmetic-puzzle

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!