Общее решение криптарифметической головоломки в Python 3

Я застрял с этой постановкой проблемы. Мой код работает, но я использовал itertools.permutations, и это делает его очень медленным. Более того, я не знаю, как сделать его универсальным для всех или любого ввода. Я думаю, что мне нужно использовать откат, но я не знаю, как его использовать здесь.

Приветствуются любые ценные предложения, советы или коды. И да, это задание, и я не прошу весь код. Спасибо!

Вот постановка задачи:

Подставьте разные цифры (0, 1, 2, .., 9) вместо разных букв ниже, чтобы соответствующее сложение было правильным, а полученное значение M O N E Y было как можно больше. Какова ценность?

ПОКАЗАТЬ + Я + ЭТО = ДЕНЬГИ

Уравнению удовлетворяют 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())

person Pooshan Vyas    schedule 13.03.2016    source источник
comment
вы просто ожидаете добавления в качестве входных данных или также вычитаний или еще более сложных уравнений?   -  person Felix    schedule 14.03.2016
comment
О, и еще один вопрос: решения, которые вы опубликовали, - это сопоставления для ДЕНЬГИ, верно? Итак, 10376 означает M=1, O=0, ... как насчет сопоставления других букв?   -  person Felix    schedule 14.03.2016
comment
SO — это не решение домашних заданий и/или головоломок.   -  person zaph    schedule 14.03.2016
comment
@caenyon, да только дополнение, да это картографирование за деньги и ты прав... М=1,О=0 и так далее.   -  person Pooshan Vyas    schedule 14.03.2016
comment
@zaph, извини, не понял!   -  person Pooshan Vyas    schedule 14.03.2016
comment
я думаю, вы должны опубликовать часть своего кода, чтобы мы могли взять его за отправную точку.   -  person Felix    schedule 14.03.2016
comment
@caenyon, конечно, я разместил свой код   -  person Pooshan Vyas    schedule 14.03.2016
comment
хорошо, я прав, что можно заменить две разные буквы одним и тем же числом? так например S=9 и E=9?   -  person Felix    schedule 14.03.2016
comment
@caenyon, уникальное письмо имеет уникальный номер. одна и та же буква имеет тот же номер.   -  person Pooshan Vyas    schedule 14.03.2016
comment
В вопросе SHOW + ME + THE = MONEY, но в коде есть send + more == money.   -  person zaph    schedule 14.03.2016
comment
@zaph, да, в вопросе есть SHOW + ME + THE = MONEY, но чтобы попробовать, я использовал send + more == money, но это не работает должным образом. Я не уверен, как работать для любого входного решения. Я не знаю, как сделать общее решение для любого ввода в этом коде?   -  person Pooshan Vyas    schedule 14.03.2016
comment
Ваш заголовок говорит «общее решение», но ваше тело подразумевает «Распечатайте их все, но правильное решение — самое большое» (в этом примере наиболее эффективным способом является перебор возможных перестановок ('M','O',' N','E','Y') в порядке убывания от 9..0 или 9..1)). Самое общее решение просто найдет все решения; то, что вы делаете с ними помимо этого (сортировка, ранжирование), является постобработкой.   -  person smci    schedule 06.02.2021


Ответы (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')

Если вас интересует только максимальное значение для правильного слова, вы можете изменить перестановку, чтобы она начиналась с наибольшего числа для правильного слова (например, 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. 

Вы поняли идею?

person Felix    schedule 13.03.2016
comment
Спасибо большое, попробую по вашему совету! - person Pooshan Vyas; 14.03.2016
comment
@cenyon, спасибо за ваши усилия. У меня есть вопрос, здесь мы оба используем перестановку, поэтому, если есть большой ввод, это будет очень медленно. Я думаю, что я должен использовать возврат, чтобы сократить время. Но я не знаю, как реализовать поиск с возвратом в криптоарифметике. Не могли бы вы помочь или дать совет? Спасибо! - person Pooshan Vyas; 14.03.2016
comment
Спасибо за ваши усилия, позвольте мне попробовать это, я не буду использовать метод грубой силы! :) - person Pooshan Vyas; 14.03.2016