Это был забавный вопрос. Вот мое полное решение:
ExprEval[nums_, ops_] := Fold[
#2[[1]][#1, #2[[2]]] &,
First@nums,
Transpose[{ops, Rest@nums}]]
SymbolicEval[nums_, ops_] := ExprEval[nums, ToString /@ ops]
GetExpression[nums_, ops_, target_] := Select[
Tuples[ops, Length@nums - 1],
(target == ExprEval[nums, #]) &]
Пример использования:
nums = {-1, 1, 2, 3};
ops = {Plus, Subtract, Times, Divide};
solutions = GetExpression[nums, ops, 3]
ExprEval[nums, #] & /@ solutions
SymbolicEval[nums, #] & /@ solutions
Выходы:
{{Plus, Times, Plus}, {Plus, Divide, Plus}, {Subtract, Plus,
Plus}, {Times, Plus, Times}, {Divide, Plus, Times}}
{3, 3, 3, 3, 3}
{"Plus"["Times"["Plus"[-1, 1], 2], 3],
"Plus"["Divide"["Plus"[-1, 1], 2], 3],
"Plus"["Plus"["Subtract"[-1, 1], 2], 3],
"Times"["Plus"["Times"[-1, 1], 2], 3],
"Times"["Plus"["Divide"[-1, 1], 2], 3]}
Как это работает
Функция ExprEval
принимает числа и операции и применяет их, используя (я думаю) RPN:
ExprEval[{1, 2, 3}, {Plus, Times}] == (1 + 2) * 3
Это осуществляется путем непрерывного сложения пар чисел с использованием соответствующей операции.
Теперь, когда у меня есть способ оценить дерево выражений, мне просто нужно было сгенерировать их. Используя Tuples
, я могу генерировать все различные операторы, которые я буду распределять между числами.
Как только вы получите все возможные операции, я использовал Select
, чтобы выбрать те, которые соответствуют целевому числу.
Недостатки
Решение выше - действительно медленно. Генерация всех возможных кортежей экспоненциальна во времени. Если есть k операций и n чисел, это порядка O (k ^ n).
Для n = 10
на Win 7 x64, Core i7 860, 12 ГБ ОЗУ потребовалось 6 секунд. Сроки запусков почти точно соответствуют теоретической сложности времени:

Красная линия - теоретическая, синяя - экспериментальная. Ось X - это размер ввода чисел, а ось Y - время в секундах для перечисления всех решений.
Приведенное выше решение также решает проблему с использованием стиля функционального программирования. Это выглядит красиво, но эта штука также поглощает массу памяти, так как она сохраняет полные результаты практически на каждом этапе.
Он даже не использует распараллеливание, и я не совсем уверен, как вы распараллелите решение, которое я создал.
Некоторые ограничения
г. Мастер обратил мое внимание на то, что этот код решает только для определенного набора решений. Учитывая некоторый ввод, такой как {a, b, c, d, e, ... }
, он только переставляет операторы между числами. Это не переставляет порядок чисел. Если бы он также переставлял числа, сложность времени возрастала бы до O(k^n * n!)
, где k
- количество операторов, а n
- длина массива входных чисел.
Следующее создаст множество решений для любой перестановки входных чисел и операторов:
(* generates a lists of the form
{
{number permutation, {{op order 1}, {op order 2}, ... }
}, ...
}*)
GetAllExpressions[nums_, ops_, target_] :=
ParallelMap[{#, GetExpression[#, ops, target]} &,
Tuples[nums, Length@nums]]