Я пишу какую-нибудь викторину и нуждаюсь в компьютере, чтобы решить одну игру в викторине, если игроки не смогут ее решить.
Данные:
- Список из 6 номеров для использования, например 4, 8, 6, 2, 15, 50.
- Целевое значение, где 0 < значение < 1000, например 590.
- Доступными операциями являются деление, сложение, умножение и деление.
- Можно использовать скобки.
Создать математическое выражение, оценка которого равна или как можно ближе к целевому значению. Например, для приведенных выше чисел выражение может быть: (6 + 4) * 50 + 15 * (8 - 2) = 590
Мой алгоритм выглядит следующим образом:
- Сгенерировать все перестановки всех подмножеств заданных чисел из (1) выше
- Для каждой перестановки генерируются все скобки и комбинации операторов
- Отслеживать ближайшее значение по мере запуска алгоритма
Я не могу придумать какую-либо умную оптимизацию для алгоритма грубой силы выше, что ускорит его на порядок. Также я должен оптимизировать для худшего случая, потому что многие викторины будут запускаться одновременно на сервере.
Код, написанный сегодня для решения этой проблемы (соответствующий материал, извлеченный из проекта):
from operator import add, sub, mul, div
import itertools
ops = ['+', '-', '/', '*']
op_map = {'+': add, '-': sub, '/': div, '*': mul}
# iterate over 1 permutation and generates parentheses and operator combinations
def iter_combinations(seq):
if len(seq) == 1:
yield seq[0], str(seq[0])
else:
for i in range(len(seq)):
left, right = seq[:i], seq[i:] # split input list at i`th place
# generate cartesian product
for l, l_str in iter_combinations(left):
for r, r_str in iter_combinations(right):
for op in ops:
if op_map[op] is div and r == 0: # cant divide by zero
continue
else:
yield op_map[op](float(l), r), \
('(' + l_str + op + r_str + ')')
numbers = [4, 8, 6, 2, 15, 50]
target = best_value = 590
best_item = None
for i in range(len(numbers)):
for current in itertools.permutations(numbers, i+1): # generate perms
for value, item in iter_combinations(list(current)):
if value < 0:
continue
if abs(target - value) < best_value:
best_value = abs(target - value)
best_item = item
print best_item
Он печатает: ((((4 * 6) +50) * 8) -2). Протестировал его немного с разными значениями и, похоже, работает правильно. Также у меня есть функция для удаления ненужных круглых скобок, но это не относится к вопросу, поэтому он не отправлен.
Проблема в том, что это происходит очень медленно из-за всех этих перестановок, комбинаций и оценок. На моем mac book air это работает несколько минут для примера. Я хотел бы запустить его за несколько секунд на одной машине, потому что многие экземпляры викторины будут запускаться одновременно на сервере. Итак, вопросы:
- Можно ли каким-то образом (по порядку) ускорить текущий алгоритм?
- Я пропустил какой-то другой алгоритм для этой проблемы, который будет работать намного быстрее?