Python3:python3

时间:2016-03-13 20:55:42

标签: python algorithm python-3.x recursive-backtracking cryptarithmetic-puzzle

我遇到了这个问题陈述,我的代码确实有效,但是我使用了排列,这使得它很慢,而且,我不知道如何使它对所有或任何输入都是通用的。我想我必须使用回溯,但我不会在这里使用它如何使用它。

欢迎任何有价值的建议或建议或代码。是的,这是一项任务,我不是要求整个代码。谢谢!

这是问题陈述:

为下面的不同字母替换不同的数字(0,1,2,..,9),以使相应的加法正确,并且得到的M O N E Y值尽可能大。有什么价值?

SHOW + ME + THE = MONEY

有3种解决方案满足等式:10376,10267,10265。因此,正确的解是10376.如果有多个映射评估到相同的最大值,则输出所有这些。

作业: 用Python编写程序,总能为这类问题找到正确的解决方案。

import time
import itertools


def timeit(fn):
    def wrapper():
        start = time.clock()
        ret = fn()
        elapsed = time.clock() - start
        print("%s took %2.fs" % (fn.__name__, elapsed))
        return ret
    return wrapper


@timeit
def solve1():
    for s in range(1, 10):
        for e in range(0, 10):
            for n in range(0, 10):
                for d in range(0, 10):
                    for m in range(1, 10):
                        for o in range(0, 10):
                            for r in range(0, 10):
                                for y in range(0, 10):
                                    if distinct(s, e, n, d, m, o, r, y):
                                        send = 1000 * s + 100 * e + 10 * n + d
                                        more = 1000 * m + 100 * o + 10 * r + e
                                        money = 10000 * m + 1000 * o + 100 * n + 10 * e + y
                                        if send + more == money:
                                            return send, more, money


def distinct(*args):
    return len(set(args)) == len(args)


@timeit
def solve2():
    #letters = input("Enter your string: ")
    #letters1 = list(letters)
    letters = ('s', 'h', 'o', 'w', 'm', 'e', 't')
    digits = range(10)
    for perm in itertools.permutations(digits, len(letters)):
        sol = dict(zip(letters, perm))
        if sol['s'] == 0 or sol['m'] == 0:
            continue
        send = 1000 * sol['s'] + 100 * sol['e'] + 10 * sol['n'] + sol['d']
        more = 1000 * sol['m'] + 100 * sol['o'] + 10 * sol['r'] + sol['e']
        money = 10000 * sol['m'] + 1000 * sol['o'] + 100 * sol['n'] + 10 * sol['e'] + sol['y']
        if send + more == money:
            return send, more, money


print(solve1())
print(solve2())

2 个答案:

答案 0 :(得分:1)

我以solve2方法为起点,为方程'word [+ word]*n = word'实现了一个简单的解析器。函数get_value在用字段中的所有字母替换其相关数字后计算得到的整数值。其余的只是像你一样经历排列,并将左词的总和与正确的词进行比较。

以下是代码:

import itertools


def get_value(word, substitution):
    s = 0
    factor = 1
    for letter in reversed(word):
        s += factor * substitution[letter]
        factor *= 10
    return s


def solve2(equation):
    # split equation in left and right
    left, right = equation.lower().replace(' ', '').split('=')
    # split words in left part
    left = left.split('+')
    # create list of used letters
    letters = set(right)
    for word in left:
        for letter in word:
            letters.add(letter)
    letters = list(letters)

    digits = range(10)
    for perm in itertools.permutations(digits, len(letters)):
        sol = dict(zip(letters, perm))

        if sum(get_value(word, sol) for word in left) == get_value(right, sol):
            print(' + '.join(str(get_value(word, sol)) for word in left) + " = {} (mapping: {})".format(get_value(right, sol), sol))

if __name__ == '__main__':
    solve2('SEND + MORE = MONEY')

如果您只对正确单词的最大值感兴趣,则可以更改以右侧单词的最大数字开头的排列(例如,对于MONEY为98765)并逐个下降直到它找到第一场比赛。

<强>回溯

好的,这里的想法是逐个建立替换,并检查每个步骤之间是否可以满足等式。

For example:
1. set S = 9
2. check if equation can be fulfilled:
    2.1. if yes: set a value for the next letter and go to 2
    2.2. if no: select next value for S

在这种情况下,检查方程式是否可以实现并不容易。

我尝试以下方法:

min:到目前为止尚未用于替换的范围(10)中的最小值

max:到目前为止尚未用于替换的范围(10)中的最大值

用最小/最大值替换到目前为止尚未被替换的左侧的每个字母,并用最大/最小值代替后将该总和与右侧的数字进行比较。

Example:
equation = 'SEND + MORE = MONEY'
1. substitute M = 2
2. check:
   max = 9, min = 0
   compare max on left side with min on right side: 9999 + 2999 = 20000
   compare min on left side with max on right side: 0000 + 2000 = 29999
   if max_left < min_right or min_left > max_right:
       the current chosen substitutions (M = 2 in this example) can not lead to a valid solution. 

你明白了吗?

答案 1 :(得分:0)

    def isCryptSolution(crypt, solution):
        newsol = list(zip(*reversed(solution)))
        newstring1 = ''
        total = 0
        for word in range(len(crypt)-1):
            subtotal, sol_total = 0, 0
            newstring = ''
            for char in crypt[word]:
                idx = newsol[0].index(char)
                newstring = newstring + newsol[1][idx]
                subtotal = int(newstring)
                # if newstring[0] == '0':
                #     return False
            total = total + subtotal
        for char1 in crypt[-1]:
            nidx = newsol[0].index(char1)
            newstring1 = newstring1 + newsol[1][nidx]
            sol_total = int(newstring1)
        if total == sol_total and newstring[0] != '0':
            return True
        elif total == 0 and newstring[0] == '0' and len(newstring) == 1:
            return True
        else:
            return False

crypt = ["SEND", "MORE", "MONEY"]
solution = [['O', '0'],
            ['M', '1'],
            ['Y', '2'],
            ['E', '5'],
            ['N', '6'],
            ['D', '7'],
            ['R', '8'],
            ['S', '9']]

isCryptSolution(crypt, solution)