Теоретическая часть.
Я тут не буду давать всю математику по полиномам Лежандра, а дам, только ту часть их теории, которая непосредственно касается использования их при численном интегрировании. Кто хочет узнать о этих полиномах поподробнее, могут найти учебниках и в интернете.
Полиномы Лежандра n степени являются многочленами n-ой степени следующего вида:
Где – число сочетаний из по k (биномиальный коэффициент), а [n] – целая часть числа.
Формула (1) для полиномов Лежандра очень громоздкая и для реализации на компьютере имеет определенные сложности по причине округления и при вычислении коэффициентов полиномов. Если вы будете вычислять коэффициенты для полиномов Лежандра в лоб, то для полиномов выше 15-ой степени столкнетесь с численным переполнением. В этом случае надо реализовать альтернативные вычисления. Самый эффективный альтернативный метод является метод вычисления значений полиномов Лежандра по рекурсивной формуле (соотношению Бонне):
Производная полинома Лежандра n-й степени вычисляется по следующей формуле:
Так как с помощью данной формулы нельзя определить значения производных при x=-1 и x=1 (знаменатель обращается в ноль), то производные полиномов Лежандра вычисляются по следующим формулам:
-
При x=-1:
-
При x=1
Для численного интегрирования используются полиномы Лежандра при аргументе принадлежащих отрезку [-1;1]. Это связано с тем, что именно на данном отрезке данные полиномы обладают свойством ортогональности с единичным весом (подробнее смотри мою статью на Дзене “Ортогональные системы функций и ряды Фурье.”). При этом норма квадрата полинома равна, что напрямую учитывается при расчете весовых коэффициентов квадратурной формулы.
Численное интегрирование по методу Гаусса-Лежандра производится по следующей квадратурной формуле:
Где xi – i-й корень полинома Лежандра n-степени; wi – соответствующие весовые коэффициенты.
Только для полиномов Лежандра 1, 2, 3 и 4 степени можно найти точные значения корней этих полиномов. Для всех полиномов, степени которых больше 4 корни можно найти только численными методами. Для их нахождения рекомендуется использовать рекуррентный метод нахождения корней нелинейных уравнений Исаака Ньютона. (Подробнее смотрите мою статью на Дзене “Решение нелинейных уравнений f(x)=0 методом Ньютона (касательных)”). При начальном приближении, вычисленной по следующей формуле:
1-3 итерации метода Ньютона гарантирует абсолютную точность порядка 10-10.
Весовые коэффициенты вычисляются по следующей формуле:
где xi – i-й корень полиномов Лежандра n-й степени.
Программная реализация
По данной методики мною было разработаны на языке C# классы и методы, с помощью которых реализован метод Гаусса-Лежандра численного интегрирования. Данная реализация позволят вычислять интегралы с использованием данного метода с любым количеством промежуточных точек так как все вычисляется на лету.
Нам понадобится вспомогательный метод, реализованный в классе Equation который ищет приближенно корень нелинейного уравнения методом Ньютона, если известно само уравнение и его производная:
Скрытый текст
/// <summary> /// Вспомогательный метод для оптимального вычисления /// i-го корня полиномов Лежандра n-й степени /// и соответственного веса для /// интегрирования методом Гаусса-Лежандра /// </summary> /// <param name="n">Степень полинома Лежандра</param> /// <param name="i">Порядковый номер корня и веса</param> /// <returns></returns> internal static NodeWeight GetNodeAndWeight(int n, int i) { double x = Math.Cos(Math.PI * (4.0 * (n - i) - 1.0) / (4.0 * n + 2.0)); // Встроенный метод Ньютона, оптимизированный под Лежандра double x0; double p = 0, dp = 0; do { x0 = x; // Считаем полином и производную одновременно, за один проход p = Polynom(x0, n); dp = DeffPolynom(x0, n); x = x0 - p / dp; } while (Math.Abs(x - x0) > 1e-12); // Прецизионная точность return new NodeWeight { Value = x, Weight = 2.0 / ((1.0 - x * x) * dp * dp) // Вес считается мгновенно из готовой производной! }; }
Класс LegendrePolynomials реализует все необходимые методы для работы с полиномами Лежандра любой степени, начиная с 3-й:
Скрытый текст
/// <summary> /// Вспомогательная структура для хранения /// пары корень полинома Лежандра /// и соответсвующего ему весового коэффициента /// </summary> internal struct NodeWeight { internal double Value; // Корень internal double Weight; // Вес } /// <summary> /// Класс для работы с полиномами Лежандра /// </summary> public static class LegendrePolynomials { /// <summary> /// Вычисление полинома Лежандра степени n, от аргумента x /// </summary> /// <param name="x"> аргумент полинома Лежандра </param> /// <param name="n"> степень полинома Лежандра</param> /// <returns>Вычисленное значение полинома Лежандра</returns> public static double Polynom(double x, int n) { switch(n) { case 0: { return 1; } case 1: { return x; } default: { double y1 = 1; double y2 = x; for(int i=2;i<=n;i++) { double y3 = ((2 * i - 1) * x * y2 - (i - 1) * y1) / i; y1 = y2; y2 = y3; } return y2; } } } /// <summary> /// Вычисление производной от полинома Лежандра степени n и аргумента x /// </summary> /// <param name="x">аргумент</param> /// <param name="n">степень полинома Лежандра</param> /// <returns></returns> public static double DeffPolynom(double x, int n) { // Защита от деления на ноль на краях интервала [-1; 1] if (Math.Abs(1.0 - x * x) < 1E-15) { return (x == 1.0 ? 1.0 : (n % 2 == 0 ? -1.0 : 1.0)) * n * (n + 1) / 2.0; } return n * (Polynom(x, n - 1) - x * Polynom(x, n)) / (1.0 - x * x); } /// <summary> /// Вычисление i-го корня полинома Лежандра степени n /// </summary> /// <param name="n">степень полинома </param> /// <param name="i"></param> /// <returns></returns> public static double Root(int n, int i) { if(n<=i || i<0) { return double.NaN; } double x = Math.Cos(Math.PI * (4*(n-i)-1) / (4 * n + 2)); Func<double, double> f = (double targetX) => Polynom(targetX, n); Func<double, double> df = (double targetX) => DeffPolynom(targetX, n); return Equation.Nuton(f, df, x, 1e-10); } /// <summary> /// Вычисляется i корень полинома Лежандра n-й степени /// </summary> /// <param name="n">Степень полинома Лежандра</param> /// <param name="i">Порядковый номер корня полинома, который требуется найти</param> /// <returns></returns> public static double Weight(int n, int i) { if (n <= i || i < 0) { return double.NaN; } double x = Root(n, i); double a = DeffPolynom(x, n); return 2 / ((1 - x * x) * a * a); } public static double Weight(int n, double x) { double a = DeffPolynom(x, n); return 2 / ((1 - x * x) * a * a); } /// <summary> /// Вспомогательный метод для оптимального вычисления /// i-го корня полиномов Лежандра n-й степени /// и соответственного веса для /// интегрирования методом Гаусса-Лежандра /// </summary> /// <param name="n">Степень полинома Лежандра</param> /// <param name="i">Порядковый номер корня и веса</param> /// <returns></returns> internal static NodeWeight GetNodeAndWeight(int n, int i) { double x = Math.Cos(Math.PI * (4.0 * (n - i) - 1.0) / (4.0 * n + 2.0)); // Встроенный метод Ньютона, оптимизированный под Лежандра double x0; double p = 0, dp = 0; do { x0 = x; // Считаем полином и производную одновременно, за один проход p = Polynom(x0, n); dp = DeffPolynom(x0, n); x = x0 - p / dp; } while (Math.Abs(x - x0) > 1e-12); // Прецизионная точность return new NodeWeight { Value = x, Weight = 2.0 / ((1.0 - x * x) * dp * dp) // Вес считается мгновенно из готовой производной! }; } }
В классе Integral реализован метод LegendreGause, реализующий численное интегрирование методом Гаусса-Лежандра.
Скрытый текст
public static double LegendreGause(Func<double,double> f,double Start, double End, int n=32) { double Result = 0; for(int i=0;i<n;i++) { NodeWeight node = LegendrePolynomials.GetNodeAndWeight(n,i); Result += node.Weight * f(((End - Start) * node.Value + End + Start) / 2); } return (End - Start) * Result / 2; }
Тестирование
Тестирование предложенной реализации численного интегрирования будем делать на следующем определенном интеграле:
Здесь 12 знаков после запятой верны.
Ниже в таблице приводится вычисления данного интеграла по методу Гаусса-Лежандра и для сравнения по методу Симпсона при разных количествах точках:
|
Кол-во точек |
Интегрирование методом Гаусса-Лежандра |
Интегрирование методом Симпсона |
|
8 |
0.8438108709747 |
0.789880436120 |
|
16 |
0.8438108127999 |
0.830828283985 |
|
32 |
0.8438108128003 |
0.840635210390 |
|
100 |
|
0.843490671272 |
|
1000 |
|
0.843807632997 |
Из таблицы видно, что интегрирование по методу Гаусса-Лежандра уже при 32 точек точность достигается до 12 знаком после запятой, так как метод интегрирования Симпсона при 1000 точках дает всего 5 верных знаков. Это говорит, что метод Гаусса-Лежандра более эффективен.
ссылка на оригинал статьи https://habr.com/ru/articles/1102060/