Как стать автором
Обновить

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

Уровень сложностиСредний
Время на прочтение4 мин
Количество просмотров2.2K

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

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

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

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

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

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

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

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

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

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

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

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

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

Компиляция

Возможность компилировать математические выражения из строк была добавлена в версии 2.0.0. Эта функция позволяет компилировать любое математическое выражение в делегат, такой как Func<TResult> или Func<T, TResult>, что значительно повышает производительность при вычислении выражения. Однако, поскольку сама компиляция может занять время, она полезна, если необходимо вычислять одно и то же выражение многократно. Для получения дополнительной информации обратитесь к бенчмаркам.

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

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

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

var context = new MathContext();
context.BindFunction(Math.Sqrt);
context.BindFunction(d => Math.Log(d), "ln");

"ln(1/-x1 + Math.Sqrt(1/(x2*x2) + 1))"
    .Evaluate(new { x1 = 0.5, x2 = -0.5 }, context);

Для вычисления математического выражения на 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 или добавить контекст для поддержки спецификации LaTeX, зависит от потребностей и поддержки сообщества. Если вы считаете этот проект ценным, пожалуйста, рассмотрите возможность спонсирования его на GitHub.

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

Теги:
Хабы:
Всего голосов 6: ↑5 и ↓1+8
Комментарии25

Публикации

Истории

Работа

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
11 сентября
Митап по BigData от Честного ЗНАКа
Санкт-ПетербургОнлайн
14 сентября
Конференция Practical ML Conf
МоскваОнлайн
19 сентября
CDI Conf 2024
Москва
20 – 22 сентября
BCI Hack Moscow
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
25 сентября
Конференция Yandex Scale 2024
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн