Вычисление любого математического выражения на C# (.NET)

Для улучшения возможностей научных вычислений на C# я реализовал evaluator, способный вычислять любое математическое строковое выражение с исключительной производительностью. Он также поддерживает пользовательские переменные и функции. Библиотека .NET под названием MathEvaluator и её документация доступны на GitHub.

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

Минимизация выделения памяти

Для минимизации выделения памяти при разборе математических выражений evaluator использует ReadOnlySpan. Эта структура позволяет эффективно манипулировать подстроками без необходимости в дополнительных выделениях памяти, что обеспечивает значительный прирост производительности. Именно поэтому эта библиотека нацелена на .NET Standard 2.1 или выше.

Избегание регулярных выражений

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

Статические методы

Используя статические методы, evaluator получает выгоду от оптимизаций при компиляции. Статические методы обычно быстрее, так как для их вызова не требуется экземпляр класса, что уменьшает накладные расходы, связанные с вызовами методов экземпляра.

Эффективный поиск

Для эффективного поиска и управления переменными и функциями evaluator использует префиксное дерево, также известное как trie. Trie позволяет быстро искать по ключам (именам) и весьма эффективно при работе с большим количеством переменных и функций. Эта структура обеспечивает быстрый поиск и добавление, что делает её идеальной для расширения математических контекстов пользовательскими переменными и функциями.

Сравнение производительности

Давайте сравним, например, производительность вычисления математического выражения:

22888.32 * 30 / 323.34 / .5 - -1 / (2 + 22888.32) * 4 - 6

Ниже приведено сравнение производительности с библиотекой NCalc:

cd3c45fbce772b22349e892a08411a5e.jpg

Поддерживаемые математические функции, операторы и константы

Библиотека MathEvaluator уже включает поддержку контекстов для вычисления научных, программных и математических выражений на C#. Чтобы просмотреть полный список поддерживаемых функций, операторов и констант, обратитесь к документации. Эти контексты можно расширить, унаследовав их и добавив другие операторы, константы или функции. На основе отзывов разработчиков я могу расширить эти контексты для удовлетворения специфических потребностей, но надеюсь на высокую заинтересованность самого сообщества в развитии этого проекта.

Пример использования пользовательского математического контекста:

var context = new MathContext();
context.BindVariable(0.5, "x1");
context.BindVariable(-0.5, "x2");
context.BindFunction(Math.Sqrt);
context.BindFunction(Math.Log, "ln");

"ln(1/-x1 + Math.Sqrt(1/(x2*x2) + 1))"
    .SetContext(context)
    .Evaluate();

Для вычисления математического выражения на C# используйте DotNetStandardMathContext. Этот программный математический контекст .NET Standard 2.1 поддерживает все константы и функции, предоставляемые классом System.Math.

Пример:

"-2 * Math.Log(1/0.5f + Math.Sqrt(1/Math.Pow(0.5d, 2) + 1L)"
    .Evaluate(new DotNetStandartMathContext());

Дополнительные примеры и подробная информация в документации.

Заключение

MathEvaluator обеспечивает высокую скорость за счёт минимизации выделения памяти, избегания сложности регулярных выражений и сокращения накладных расходов, связанных со структурами данных типа стек или очередь. Он использует префиксное дерево для эффективного поиска пользовательских переменных и функций, что делает его мощным инструментом для научных вычислений в .NET. Библиотека следует правилам математических вычислений и может вычислять сложные выражения с поразительной скоростью и эффективностью, что подтверждается прохождением более 1000 тестов и benchmarks, включая сложные математические выражения, такие как sin-3/cos1 или -3^4sin (-π/2).

Для развития проекта можно добавить поддержку вычисления выражений на Python, формул Excel или добавить контекст для поддержки спецификации MathML, зависит от потребностей и поддержки сообщества. Если вы считаете этот проект ценным, пожалуйста, рассмотрите возможность спонсирования его на GitHub.

Спасибо! Если у вас есть идеи или предложения, пожалуйста, оставляйте их в комментариях.

© Habrahabr.ru