Как упростить математическую формулу, максимально сократив ее

Постараюсь быть объективным в вопросе.

У меня есть база данных с миллионами математических формул, созданных приложением, это формулы, которые соответствуют логике:

1) Всего 5 математических операций: сложение, вычитание, умножение, деление и степень (+ - * / ^)

2) Каждая операция разделена / сгруппирована круглыми скобками.

«Параметры» могут быть простыми, такими как константа или переменная.

Пример: (x + 15)

В сложном случае у меня могут быть:

(x * (x + 15))

Важно помнить, что правильный порядок операций гарантируется при использовании круглых скобок.

Пример:

((x * (x + 15)) / 15)

Моя задача - сократить количество этих формул.

Они бесполезны или часто повторяются, когда операции одинаковы и независимо от порядка факторов.

Пример:

((((x + 4) + 8) - x) / 12)

Это равно 1, это не формула, это константа, и я должен ее игнорировать.

И двойственности вроде ((x + 4) + 8) и ((8 + x) + 4) необходимо устранить.

Вот почему мне нужно уменьшить.

На заметку: все формулы стандартизированы с использованием скобок и пробелов между операциями.

Я создал процедуру на PHP, используя регулярные выражения для подстановки, мне удалось добиться огромного прогресса, сократив конкретную формулу с 8 тысяч вариантов до менее чем трех сотен.

Однако по мере увеличения размера (а не сложности, потому что они не сложные) формул моя рутина перестает быть эффективной.

Мне нужен алгоритм, а не рутина, чтобы применить математическую редукцию, которую мы выучили в школе.

Я думаю, что это возможно, поскольку формулы стандартизированы и ограничены 5 основными математическими операциями.

Я использую класс EvalMath, полученный в GitHub, чтобы помочь в сокращении и выполнении формул.

Чтобы дать вам лучшее представление, формулы являются абстрактными, и каждый символ «@» заменяется в реальном времени константами и переменными.

(@ + @)
(@ / @)
(@ ** @)
((@ + @) + @)
((@ + @) ** @)
((@ - @) + @)
((@ - @) / @)
((@ - @) ** @)
((@ * @) + @)
((@ * @) / @)
((@ * @) ** @)
((@ / @) / @)

Вот фрагмент кода PHP, который является частью моей процедуры сокращения.

Внутри WHILE (TRUE) я сгруппировал правила, которые повторяются до тех пор, пока не будут произведены замены.

В конце концов, у меня есть массив с множеством дубликатов, полученный путем некоторого сокращения и переупорядочения элементов формулы, что решает array_unique ().

Мне очень нужна помощь, мой мозг взрывается от этой проблемы.

Спасибо.

<?php
    $Math_Formulas = array(
        '(((x + 7) ** 9) - 9)',
        '(((x ^ 3) - 9) - 5)',
        '(((2 + x) + x) * x)',
        '(((x + 3) / 6) / 8)',
        '(((x - 5) + 6) ** 2)',
        '(1024 ^ (x / 5))',
        '((3 - (x + 6)) + 3)',
        '(((x ^ 3) + 9) * 6)',
    );

    while (TRUE)
    {
        $changed = FALSE;

        // Rule 1: (x - x) = 0
        for ($i = 0; $i < count($Math_Formulas); $i++)
        {
            $Formula = trim(preg_replace_callback('/([^-]?)([a-z]+?) [-] ([a-z]+?)/', function($matches) {
                $Expression = $matches[0];
                if ($matches[2] == $matches[3])
                {
                    $Expression = $matches[1] . '0';
                }
                return($Expression);
            }, $Math_Formulas[$i]));
            if ($Formula != $Math_Formulas[$i])
            {
                $changed = TRUE;
                $Math_Formulas[$i] = $Formula;
            }
        }

        // Rule 2: ...

        if (!$changed)
        {
            break;
        }
    }

    $Math_Formulas = array_values(array_unique($Math_Formulas));
?>

ОБНОВЛЕНИЕ 1:

Я думаю, что если бы при создании формул использовалась «обратная полировка», все было бы намного проще, но с формулами, которые у меня есть, мне нужно переставить параметры (в возрастающем или убывающем алфавитном порядке), чтобы иметь возможность Сравните их.

В РПН:

(x + 4) + 5) становится "x 4 5 +"

(X + 5) + 4) становится "x 5 4 +"

Как сравнить оба? А как насчет более крупных функций?

Я думаю, что совершил ошибку, не детализировав технику, используемую в 14 «регулярных выражениях», которые я применяю для максимального упрощения этих формул. В этом процессе есть нечто большее, чем просто регулярное выражение:

Исходная формула: (((4-5) + x) + 8)

Шаг 1: Сложение (или вычитание, или умножение) двух к двум константам и сокращение выражения без скобок.

Формула: ((-1 + x) + 8)

Шаг 2: Удалите скобки для ((n + - n) + - n) или (n + - (n + - n)).

Формула: (-1 + x + 8)

Шаг 3. Измените порядок параметров в алфавитном порядке по убыванию.

Формула: (x + 8-1)

Шаг 4: В цикле снова выполняется шаг 1.

Окончательная формула: (x + 7)

Есть больше преобразований, например (x + x + x) становится (3 * x), (-x + x) становится 0.

Все это становилось прекрасным, но когда я наткнулся на такие функции, как ((x * 9) * (x * 5)) / 9), эта логика потеряла эффективность. Мне нужно было бы создать еще как минимум еще 14 вложенных правил.


person Suporte On    schedule 09.08.2017    source источник
comment
Используйте Djikstra, чтобы преобразовать формулу в нотацию обратной полировки, что поможет устранить множество дублирований; но это не что-то тривиальное, что можно сделать с помощью регулярного выражения   -  person Mark Baker    schedule 09.08.2017
comment
По сути, вы спрашиваете, как создать простую систему компьютерной алгебры на PHP, которая слишком широка для переполнения стека.   -  person John Coleman    schedule 09.08.2017
comment
Мне не ясно, действительно ли вы должны написать это самостоятельно (и использовать PHP) или вам просто нужно найти решение. Если вам просто нужно решение, я советую использовать существующую систему компьютерной алгебры, такую ​​как Maxima (maxima.sourceforge.net) или Sympy (sympy.org), в качестве серверной части. В Maxima вы можете вызывать ratsimp для упрощения выражений; наверное, в Sympy есть похожая функция. Если вам необходимо написать собственное решение, я советую взглянуть на небольшую систему алгебры, описанную в «Парадигмах программирования ИИ» П. Норвига, и адаптировать концепции к вашей проблеме.   -  person Robert Dodier    schedule 10.08.2017
comment
Здравствуйте, Марк, я обновил вопрос на основе вашего предложения. Я проверил Maxima, и ratsimp показался мне интересным, я проведу несколько тестов, чтобы сэкономить время, потому что в данный момент я не могу погрузиться в теоретическое решение, я должен представить быстрые результаты. Спасибо, Роберт. Я оценю Sympy, как только смогу.   -  person Suporte On    schedule 11.08.2017


Ответы (2)


Допуская только четыре операции (+ - * /), все эти выражения являются так называемыми рациональными дробями, то есть отношением двух многочленов.

Действительно, действуют следующие правила:

p/q + p'/q'   = (pq' + p'q)/pq
p/p.p'/'q     = (pp')/(qq')
(p/p')/(q/q') = (pq')/(p'q)

так что комбинация двух рациональных дробей также является рациональной дробью.

Вы можете реализовать систему, в которой каждое выражение описывается как пара многочленов (с целыми или рациональными коэффициентами), и интегрировать приведенные выше формулы. По сути, это требует полиномиального сложения и умножения.

Иногда для упрощения числитель и знаменатель будут иметь общие множители. Вы можете обнаружить их с помощью алгоритма Евкида на полиномах, давая полиномиальный НОД. (Тогда вам также понадобится полиномиальное деление.)

Благодаря этой системе упрощения придут сами по себе. Кроме того, выражение будет нормализовано, так что вы сможете обнаружить дубликаты.

Если есть одна переменная, многочлены будут одномерными, и их представление будет простым. Для нескольких переменных это становится немного сложнее.

Теперь, если вы разрешите возведение в степень, следует различать два случая:

  • экспоненты могут быть только положительными целыми числами: тогда вы можете расширить p^n и q^n до других многочленов по правилу многочлена (но размер быстро увеличится);

  • экспоненты могут быть рациональными константами или функциями переменной: весь метод ломается.

Заключительное замечание:

В конце концов, вам нужно будет составить упрощенное правило оценки, которое представляет собой отношение двух многочленов. Эффективный способ выразить такую ​​оценку - схема Хорнера. В некоторых случаях это вернет вас туда, откуда вы начали!

Пример:

Упрощать

(1 - x / y) (x + y) + (x * x) / y

Шаги:

1 - x / y                 => (y - x) / y
x + y                     => (x + y) / 1
(y - x) / y . (x + y) / 1 => (y² - x²) / y
x * x                     => x² / 1
y                         => y / 1
(x * x) / y               => x² / y
(y² - x²) / y + x² / y    => y³ / y²
y³ / y²                   => y / 1
person Yves Daoust    schedule 10.08.2017

Таким образом, общая схема состоит в том, чтобы сначала преобразовать выражение в формат дерева выражения. Это древовидная структура, в которой каждый узел представляет математическую операцию, число или переменную. Вы можете использовать алгоритм маневровой площадки для преобразования и можете обнаружить, что evalmath может быть изменен для создания дерева, а не просто для его оценки. Вероятно, существуют другие библиотеки Python, которые могут делать больше, чем evalmath.

Если у вас есть древовидная структура, манипулировать ею становится намного проще. Вы можете манипулировать небольшими поддеревьями для создания более простых форм, применяя некоторые математические правила, которые вы выучили в старшей школе.

Например, вы можете расширить скобки. Выражение (x + 3) * (x + 5) было бы представлено в виде дерева как

              *
        +           +
     x     3     x     5 

это дерево можно переписать как x ^ 2 + 8 x + 15

              +
        ^                +
     x     2        *        15
                 x     8

Общая схема, вероятно, будет расширять все ваши скобки. Затем соберите похожие термины и упростите.

person Salix alba    schedule 10.08.2017
comment
Это потрясающе (en.wikipedia.org/wiki/Shunting-yard_algorithm), спасибо . Я уверен, что собираюсь использовать его в другой работе, но мне не нужно беспокоиться о том, как будет выполняться формула, ключевой момент здесь - максимально сократить количество формул, которые я собираюсь использовать. работать с ними, потому что они будут полностью повторно использованы, и чем меньше у меня формул, тем дальше я могу идти. - person Suporte On; 11.08.2017