Вычисление интеграла
Давайте более подробно рассмотрим ситуацию с функциями высшего порядка на примере задачи вычисления определенного интеграла с заданной точностью. С этой целью создадим класс, в котором будет описан делегат, определяющий контракт, коему должны удовлетворять подынтегральные функции. В этом же классе определим метод, вычисляющий интеграл. По сути самой задачи этот метод представляет собой функцию высшего порядка. Приведу программный код, описывающий класс:
public class HighOrderIntegral { //delegate public delegate double SubIntegralFun(double x); public double EvalIntegral(double a, double b, double eps,SubIntegralFun sif) { int n=4; double I0=0, I1 = I( a, b, n,sif); for( n=8; n < Math.Pow(2.0,15.0); n*=2) { I0 =I1; I1=I(a,b,n,sif); if(Math.Abs(I1-I0)<eps) break; } if(Math.Abs(I1-I0)< eps) Console.WriteLine("Требуемая точность достигнута! "+ " eps = {0}, достигнутая точность ={1}, n= {2}", eps,Math.Abs(I1-I0),n); else Console.WriteLine("Требуемая точность не достигнута! "+ " eps = {0}, достигнутая точность ={1}, n= {2}", eps,Math.Abs(I1-I0),n); return(I1); } private double I(double a, double b, int n, SubIntegralFun sif) { //Вычисляет частную сумму по методу трапеций double x = a, sum = sif(x)/2, dx = (b-a)/n; for (int i= 2; i <= n; i++) { x += dx; sum += sif(x); } x = b; sum += sif(x)/2; return(sum*dx); } }//class HighOrderIntegral
Прокомментирую этот текст:
- Класс HighOrderIntegral предназначен для работы с функциями. В него вложено описание функционального класса - делегата SubIntegralFun, задающего класс функций с одним аргументом типа double и возвращающих значение этого же типа.
- Метод EvalIntegral - основной метод класса позволяет вычислять определенный интеграл. Этот метод есть функция высшего порядка, поскольку одним из его аргументов является подынтегральная функция, принадлежащая классу SubIntegralFun.
- Для вычисления интеграла применяется классическая схема. Интервал интегрирования разбивается на n частей, и вычисляется частичная сумма по методу трапеций, представляющая приближенное значение интеграла. Затем n удваивается, и вычисляется новая сумма. Если разность двух приближений по модулю меньше заданной точности eps, то вычисление интеграла заканчивается, иначе процесс повторяется в цикле. Цикл завершается либо по достижении заданной точности, либо когда n достигнет некоторого предельного значения (в нашем случае - 215).
- Вычисление частичной суммы интеграла по методу трапеций реализовано закрытой процедурой I.
- Впоследствии класс может быть расширен, и помимо вычисления интеграла он может вычислять и другие характеристики функций.
Чтобы продемонстрировать работу с классом HighOrderIntegral, приведу еще класс Functions, где описано несколько функций, удовлетворяющих контракту, который задан классом SubIntegralFun:
class functions { //подынтегральные функции static public double sif1(double x) { int k = 1; int b = 2; return (double)(k*x +b); } static public double sif2(double x) { double a = 1.0; double b = 2.0; double c= 3.0; return (double)(a*x*x +b*x +c); } }//class functions
А теперь рассмотрим метод класса клиента, выполняющий создание нужных объектов и тестирующий их работу:
public void TestEvalIntegrals() { double myint1=0.0; HighOrderIntegral.SubIntegralFun hoisif1 = new HighOrderIntegral.SubIntegralFun(functions.sif1); HighOrderIntegral hoi = new HighOrderIntegral(); myint1 = hoi.EvalIntegral(2,3,0.1e-5,hoisif1); Console.WriteLine("myintegral1 = {0}",myint1); HighOrderIntegral.SubIntegralFun hoisif2 = new HighOrderIntegral.SubIntegralFun(functions.sif2); myint1= hoi.EvalIntegral(2,3,0.1e-5,hoisif2); Console.WriteLine("myintegral2 = {0}",myint1); }//EvalIntegrals
Здесь создаются два экземпляра делегата и объект класса HighOrderIntegral, вызывающий метод вычисления интеграла. Результаты работы показаны на 20.2.
Рис. 20.2. Вычисление интеграла с использованием функций высших порядков