Современные численные методы в объектно-ориентированном изложении на C#

Обыкновенные дифференциальные уравнения

Разбить на страницы
Показывать лекцию целиком

Цель лекции: Показать эффективность объектно-ориентированного подхода к задаче нахождения численных решений задачи Коши для дифференциальных уравнений. Провести вычислительные эксперименты и сравнить различные методы.

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

Сначала перейдем к формальному определению постановки задачи Коши. Пусть $$D\subset\Bbb{R}^n$$ - область (ограниченная или нет) в $$n$$ -мерном пространстве. Пусть также задан отрезок $$[0,\Bbb{T}]$$, здесь $$\Bbb{T}>0$$ может быть и бесконечностью. Введем обозначение$$D_\Bbb{T}=\{(x,t):x\in\overline D,t\in[0,\Bbb{T}]\}.$$ Пусть в $$D_\Bbb{T}$$ задана функция $$f(x,t)$$ тогда можно рассматривать обыкновенное дифференциальное уравнение$$y'(t)=f(y(t),t),\ t\in[0,\Bbb{T}].$$ С этим уравнением связывается начальное условие$$y(0)=y^0,\ y_0\in D.$$ Задача нахождения функции $$y(t)$$ такой, что на некотором интервале $$[0,T]$$ функция $$y(t)$$ имеет непрерывную производную при $$t\in[0,T]$$ имеет место $$y(t)\in\overline{D}$$, и функция $$y(t)$$ удовлетворяет уравнению 16.1 и начальному условию 16.2.

В теории обыкновенных дифференциальных уравнений доказываются следующие теоремы.

Теорема 16.1. Пусть функция $$f(x,t)$$ является непрерывной в $$D_\Bbb{T}$$, тогда существует такое $$T>0$$, что на $$[0,T]$$ существует по крайней мере одно решение задачи 16.1-16.2.

В условиях теоремы существуют примеры задач Коши, которые имеют более одного решения. Действительно, если взять $$D=\Bbb{R}$$, $$\Bbb{T}=\infty$$, а в качестве функции $$f$$$$f(x,t)=\sqrt{x},$$ то задачка Коши$$y'(t)=\sqrt{y(t)},$$ $$y(0)=0$$ имеет бесконечное число решений:$$y(t)=\left\{% \begin{array}{ll} 0, t\le\alpha, \\ \frac{(t-\alpha)^2}{4}, t\ge\alpha, \\ \end{array}% \right.$$ где $$\alpha\ge0$$ - параметр семейства решений.

Чтобы гарантировать единственность решения задачи Коши, необходимо накладывать большие условия на функцию $$f$$.

Теорема 16.2. Пусть функция $$f(x,t)$$ является непрерывной в $$D_\Bbb{T}$$, и существует такая положительная константа $$L>0$$, зависящая только от $$\tau>0$$ что для любого отрезка $$[0,\tau]\subset[0,\Bbb{T}]$$ выполнено неравенство$$|f(x',t)-f(x'',t)|\le L|x'-x''|,$$ для всех $$x',x''\in D$$ и $$t\in[0,\tau]$$.

Тогда существует такое $$T>0$$, что на $$[0,T]$$ существует единственное решение задачи 16.1-16.2.

Условие 16.3 называется условием Липшица. Это условие будет заведомо выполнено, если непрерывная функция $$f$$ имеет ограниченные и непрерывные производные по $$x$$ в $$D$$.

Приведенные выше теоремы гарантируют только локальную разрешимость задачи Коши. Действительно, следующий пример задачи Коши не имеет решения на любом отрезке $$[0,T]$$, где $$T\ge\frac{\pi}{2}$$. Пусть снова $$D=\Bbb{R}^n$$, функция $$f(x,t)$$ имеет вид$$f(x,t)=x^2+1,$$ тогда задача Коши$$y'(t)=y^2(t)+1$$ $$y(0)=0,$$ имеет единственное решение$$y(t)=\tg t,$$ которое не продолжается вне интервала $$[0,\frac{\pi}{2})$$.

Чтобы гарантировать существование глобального по времени решения, необходимо накладывать дополнительные условия на рост правой части.

Теорема 16.3. Пусть выполнены все условия теоремы 16.2 и дополнительно функция $$f(x,t)$$ удовлетворяет условию в области $$D=\Bbb{R}^n$$$$|f(x,t)|\le M(|x|+1),$$ где константа $$M>0$$ не зависит ни от $$x$$, ни от $$t$$, тогда задача Коши имеет единственное решение на отрезке $$[0,\infty)$$.

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

Существует довольно много численных методов решения задачи Коши для дифференциальных уравнений. При этом многие методы были созданы еще в "до машинную эпоху". Такие методы часто ориентированы на ручной расчет и включают в себя использование аналитических выкладок, например, расчет производных от правой части. Наиболее часто используемым является метод Рунге-Кутта. Этот метод имеет весьма высокую точность, может иметь переменный шаг и может быть легко запрограммирован. Одним из самых простейших методов решения дифференциального уравнения является метод Эйлера. Мы подробно рассмотрим метод Эйлера и метод Рунге-Кутта $$4$$ -го порядка.

Метод Эйлера применялся еще Л.Эйлером для доказательства существования решения задачи Коши. Он имеет интуитивно понятную форму, легко может быть запрограммирован, но имеет низкую точность. И так, рассмотрим уравнение 16.1. Производную в этом уравнении приближенно представим с помощью конечной разности:$$y'(t)\approx\frac{y(t+h)-y(t)}{h},$$ где $$h>0$$ некоторое число. Используя это представление мы вместо дифференциального уравнения 16.1 получим разностное уравнение$$\frac{y(t+h)-y(t)}{h}=f(y(t),t).$$ От сюда найдем$$y(t+h)=y(t)+hf(y(t),t).$$ Пусть мы хотим найти приближенное решение на отрезке $$[0,T]$$. Разобьем этот отрезок конечным числом точек необязательно равномерно$$0=t_0<t_1<\dots<t_N=T.$$ Введем обозначения $$h_i=t_i-t_{i-1}$$, $$i=1,2,\dots,N$$. Для простоты будем предполагать, что функция $$f(x,t)$$ определена во всем пространстве $$\Bbb{R}^{n+1}$$. Тогда мы можем построить набор $$\{y_i\}_{i=0}^N\subset\Bbb{R}^n$$ по следующему правилу$$y_0=y^0,$$ $$y_i=y_{i-1}+h_if(y_{i-1},t_{i-1}),\ i=1,2,\dots,N.$$ Обозначим $$h=\max\{h_i:i=1,2,\dots,N\}$$. Вектора $$y_i$$ имеют смысл значений приближенного решения в точках $$t_i$$. Для нахождения приближенного решения внутри интервалов $$(t_{i-1},t_i)$$ можно применять различные методы интерполяции, например, линейной. Функция $$y^N(t)$$, построенная с помощью линейной интерполяции, называется ломанной Эйлера. Если для рассматриваемой задачи выполнены условия теоремы 16.2 и на отрезке $$[0,T]$$ существует решение $$y(t)$$, то приближенное решение $$y^N(t)$$ сходится к точному решению равномерно имеет место оценка$$\max\limits_{t\in[0,T]}|y(t)-y^N(t)|\le Ch.$$ Таким образов метод Эйлера имеет первый порядок точности.

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

$$\begin{verbatim} abstract class TODE { public int N; protected double t; // текущее время // искомое решение Y[0] - само решение, // Y[i] - i-тая производная решения public double[] Y; protected double[] YY; // внутренние переменные public TODE(int N) // N - размерность системы { this.N = N; Y = new double[N]; // создать вектор решения YY = new double[N]; // и внутренних решений } // установить начальные условия. // t0 - начальное время, Y0 - начальное условие public void SetInit(double t0, double[] Y0) { t = t0; int i; for (i = 0; i < N; i++) { Y[i] = Y0[i]; } } public double GetCurrent() // вернуть текущее время { return t; } abstract public void F(double t, double[] Y, ref double[] FY); // правые части системы. // следующий шаг, dt - шаг по времени abstract public void NextStep(double dt); } \end{verbatim}$$

На основе этого класса построим класс, реализующий метод Эйлера.

$$\begin{verbatim} abstract class TEuler : TODE { public TEuler(int N) : base(N) {} public override void NextStep(double dt) { int i; F(t, Y, ref YY); for (i = 0; i < N; i++) { Y[i] = Y[i] + dt * YY[i]; } t = t + dt; } } \end{verbatim}$$

Прежде чем испытать наш метод Эйлера, мы рассмотрим и реализуем метод Рунге-Кутта $$4$$ -го порядка. Метод Рунге-Кутта, также как и метод Эйлера, допускает перемену шага, но имеет значительно большую точность. Пусть $$t_i$$ и $$y_i$$ имеют тот же смысл, что и при рассмотрении метода Эйлера.

Правила построения точек $$y_i$$ следующие$$y_i=y_{i-1}+\frac{h_i}{6}(k_1+2k_2+2k_3+k_4),$$ где$$k_1=f(y_{i-1},t_{i-1}),$$ $$k_2=f\left(y_{i-1}+\frac{h}{2}k_1,t_{i-1}+\frac{h}{2}\right),$$ $$k_3=f\left(y_{i-1}+\frac{h}{2}k_2,t_{i-1}+\frac{h}{2}\right),$$ $$k_4=f(y_{i-1}+hk_3,t_{i-1}+h).$$ Как мы видим, для расчета решения на следующем шаге необходимо вычислить правую часть четыре раза, зато точность этого метода имеет четвертый порядок, при условии гладкости правой части.

Перейдем к реализации метода Рунге-Кутта на основе нашего класса $$TODE$$.

$$\begin{verbatim} abstract class TRungeKutta : TODE { double[] Y1, Y2, Y3, Y4; // внутренние переменные public TRungeKutta(int N) : base(N) { Y1 = new double[N]; Y2 = new double[N]; Y3 = new double[N]; Y4 = new double[N]; } \end{verbatim}$$ $$\begin{verbatim} // следующий шаг метода Рунге-Кутта, dt - шаг по времени override public void NextStep(double dt) { if (dt < 0) { return; } int i; F(t, Y, ref Y1); // рассчитать Y1 for (i = 0; i < N; i++) { YY[i] = Y[i] + Y1[i] * (dt / 2.0); } F(t + dt / 2.0, YY, ref Y2); // рассчитать Y2 for (i = 0; i < N; i++) { YY[i] = Y[i] + Y2[i] * (dt / 2.0); } F(t + dt / 2.0, YY, ref Y3); // рассчитать Y3 for (i = 0; i < N; i++) { YY[i] = Y[i] + Y3[i] * dt; } F(t + dt, YY, ref Y4); // рассчитать Y4 for (i = 0; i < N; i++) { // рассчитать решение на новом шаге Y[i] = Y[i] + dt / 6.0 * (Y1[i] + 2.0 * Y2[i] + 2.0 * Y3[i] + Y4[i]); } t = t + dt; // увеличить шаг } } \end{verbatim}$$

Теперь протестируем наши методы на примере задач Коши, описывающей гармонические колебания математического маятника. Мы решаем следующую задачу.$$y''(x)+y(x)=0$$ с начальными условиями$$y(0)=0,$$ $$y'(0)=1.$$ Эта задача имеет единственное решение - $$\sin t$$. Чтобы применить к этой задаче наши методы нужно записать ее в виде системы уравнений первого порядка.$$Y_0(t)=y(t),$$ $$Y_1(t)=y'(t).$$ После такой замены мы имеет следующую систему$$Y'_0(t)=Y_1(t)$$ $$Y'_1(t)=-Y_0(t) $$ с начальными условиями$$Y_0(0)=0,$$ $$Y_1(0)=1.$$ Реализуем для нашей системы методы Эйлера и Рунге-Кутты

$$\begin{verbatim} class THarmonicEuler : TEuler { public THarmonicEuler() : base(2) { } public override void F(double t, double[] Y, ref double[] FY) { FY[0] = Y[1]; FY[1] = -Y[0]; } } class THarmonicRK : TRungeKutta { public THarmonicRK() : base(2) { } public override void F(double t, double[] Y, ref double[] FY) { FY[0] = Y[1]; FY[1] = -Y[0]; } } \end{verbatim}$$

Испытаем наши классы

$$\begin{verbatim} double h = 0.1; double[] Y0 = { 0, 1.0 }; THarmonicEuler HarmonicEuler = new THarmonicEuler(); HarmonicEuler.SetInit(0, Y0); THarmonicRK HarmonicRK = new THarmonicRK(); HarmonicRK.SetInit(0, Y0); StreamWriter F = File.CreateText("harmonic.txt"); double t, Euler, RK, Sin; while (HarmonicEuler.GetCurrent() < (2 * Math.PI + h / 2.0)) { t = HarmonicEuler.GetCurrent(); Euler = HarmonicEuler.Y[0]; RK = HarmonicRK.Y[0]; Sin = Math.Sin(t); F.WriteLine("{0}\t{1}\t{2}\t{3}\t\t{4}\t{5}\t{6}", t, Euler, RK, Sin, t, Math.Abs(Sin - Euler), Math.Abs(Sin - RK)); HarmonicEuler.NextStep(h); HarmonicRK.NextStep(h); } F.Close(); \end{verbatim}$$

Результаты расчетов приведем на трех графиках. На рисунке 16.1 мы приводим график точного решения и приближенных, полученных методами Эйлера и Рунге-Кутты. Однако на этом графике не возможно отличить точное решение от приближенного решения, полученного методом Рунге-Кутты. На рисунках 16.2 и 16.3 мы показываем погрешности методов. Из анализа этих графиков видно, что точность метода Рунге-Кутты значительно выше, что обосновывается теоретически.

(рис 16.2) Точное и приближенное решение дифференциального уравнения(рис 16.1) Погрешность метода Эйлера(рис 16.3) Погрешность метода Рунге–Кутты

Ключевые термины

Глобальное решение - решение задачи Коши, существующее при всех $$t\ge0$$.

Задача Коши - дифференциальное уравнение с заданными начальными условиями на решение.

Метод Рунге-Кутта - наиболее распространенный метод численного интегрирования задачи Коши с точностью четвертого порядка.

Метод Эйлера - простейший метод численного интегрирования задачи Коши с точностью первого порядка.

Условие Липшица - условие на приращение функции, более сильное чем условие непрерывности, но слабее чем условие дифференцируемости.

Краткие итоги: Построены объектно-ориентированные средства для численного решения задачи Коши. Проведены вычислительные эксперименты, для сравнения методов Эйлера и Рунге-Кутты.

Страницы:

Цель лекции: Показать эффективность объектно-ориентированного подхода к задаче нахождения численных решений задачи Коши для дифференциальных уравнений. Провести вычислительные эксперименты и сравнить различные методы.

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

Сначала перейдем к формальному определению постановки задачи Коши. Пусть $$D\subset\Bbb{R}^n$$ - область (ограниченная или нет) в $$n$$ -мерном пространстве. Пусть также задан отрезок $$[0,\Bbb{T}]$$, здесь $$\Bbb{T}>0$$ может быть и бесконечностью. Введем обозначение$$D_\Bbb{T}=\{(x,t):x\in\overline D,t\in[0,\Bbb{T}]\}.$$ Пусть в $$D_\Bbb{T}$$ задана функция $$f(x,t)$$ тогда можно рассматривать обыкновенное дифференциальное уравнение$$y'(t)=f(y(t),t),\ t\in[0,\Bbb{T}].$$ С этим уравнением связывается начальное условие$$y(0)=y^0,\ y_0\in D.$$ Задача нахождения функции $$y(t)$$ такой, что на некотором интервале $$[0,T]$$ функция $$y(t)$$ имеет непрерывную производную при $$t\in[0,T]$$ имеет место $$y(t)\in\overline{D}$$, и функция $$y(t)$$ удовлетворяет уравнению 16.1 и начальному условию 16.2.

В теории обыкновенных дифференциальных уравнений доказываются следующие теоремы.

Теорема 16.1. Пусть функция $$f(x,t)$$ является непрерывной в $$D_\Bbb{T}$$, тогда существует такое $$T>0$$, что на $$[0,T]$$ существует по крайней мере одно решение задачи 16.1-16.2.

В условиях теоремы существуют примеры задач Коши, которые имеют более одного решения. Действительно, если взять $$D=\Bbb{R}$$, $$\Bbb{T}=\infty$$, а в качестве функции $$f$$$$f(x,t)=\sqrt{x},$$ то задачка Коши$$y'(t)=\sqrt{y(t)},$$ $$y(0)=0$$ имеет бесконечное число решений:$$y(t)=\left\{% \begin{array}{ll} 0, t\le\alpha, \\ \frac{(t-\alpha)^2}{4}, t\ge\alpha, \\ \end{array}% \right.$$ где $$\alpha\ge0$$ - параметр семейства решений.

Чтобы гарантировать единственность решения задачи Коши, необходимо накладывать большие условия на функцию $$f$$.

Теорема 16.2. Пусть функция $$f(x,t)$$ является непрерывной в $$D_\Bbb{T}$$, и существует такая положительная константа $$L>0$$, зависящая только от $$\tau>0$$ что для любого отрезка $$[0,\tau]\subset[0,\Bbb{T}]$$ выполнено неравенство$$|f(x',t)-f(x'',t)|\le L|x'-x''|,$$ для всех $$x',x''\in D$$ и $$t\in[0,\tau]$$.

Тогда существует такое $$T>0$$, что на $$[0,T]$$ существует единственное решение задачи 16.1-16.2.

Условие 16.3 называется условием Липшица. Это условие будет заведомо выполнено, если непрерывная функция $$f$$ имеет ограниченные и непрерывные производные по $$x$$ в $$D$$.

Приведенные выше теоремы гарантируют только локальную разрешимость задачи Коши. Действительно, следующий пример задачи Коши не имеет решения на любом отрезке $$[0,T]$$, где $$T\ge\frac{\pi}{2}$$. Пусть снова $$D=\Bbb{R}^n$$, функция $$f(x,t)$$ имеет вид$$f(x,t)=x^2+1,$$ тогда задача Коши$$y'(t)=y^2(t)+1$$ $$y(0)=0,$$ имеет единственное решение$$y(t)=\tg t,$$ которое не продолжается вне интервала $$[0,\frac{\pi}{2})$$.

Чтобы гарантировать существование глобального по времени решения, необходимо накладывать дополнительные условия на рост правой части.

Теорема 16.3. Пусть выполнены все условия теоремы 16.2 и дополнительно функция $$f(x,t)$$ удовлетворяет условию в области $$D=\Bbb{R}^n$$$$|f(x,t)|\le M(|x|+1),$$ где константа $$M>0$$ не зависит ни от $$x$$, ни от $$t$$, тогда задача Коши имеет единственное решение на отрезке $$[0,\infty)$$.

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

Существует довольно много численных методов решения задачи Коши для дифференциальных уравнений. При этом многие методы были созданы еще в "до машинную эпоху". Такие методы часто ориентированы на ручной расчет и включают в себя использование аналитических выкладок, например, расчет производных от правой части. Наиболее часто используемым является метод Рунге-Кутта. Этот метод имеет весьма высокую точность, может иметь переменный шаг и может быть легко запрограммирован. Одним из самых простейших методов решения дифференциального уравнения является метод Эйлера. Мы подробно рассмотрим метод Эйлера и метод Рунге-Кутта $$4$$ -го порядка.

Метод Эйлера применялся еще Л.Эйлером для доказательства существования решения задачи Коши. Он имеет интуитивно понятную форму, легко может быть запрограммирован, но имеет низкую точность. И так, рассмотрим уравнение 16.1. Производную в этом уравнении приближенно представим с помощью конечной разности:$$y'(t)\approx\frac{y(t+h)-y(t)}{h},$$ где $$h>0$$ некоторое число. Используя это представление мы вместо дифференциального уравнения 16.1 получим разностное уравнение$$\frac{y(t+h)-y(t)}{h}=f(y(t),t).$$ От сюда найдем$$y(t+h)=y(t)+hf(y(t),t).$$ Пусть мы хотим найти приближенное решение на отрезке $$[0,T]$$. Разобьем этот отрезок конечным числом точек необязательно равномерно$$0=t_0<t_1<\dots<t_N=T.$$ Введем обозначения $$h_i=t_i-t_{i-1}$$, $$i=1,2,\dots,N$$. Для простоты будем предполагать, что функция $$f(x,t)$$ определена во всем пространстве $$\Bbb{R}^{n+1}$$. Тогда мы можем построить набор $$\{y_i\}_{i=0}^N\subset\Bbb{R}^n$$ по следующему правилу$$y_0=y^0,$$ $$y_i=y_{i-1}+h_if(y_{i-1},t_{i-1}),\ i=1,2,\dots,N.$$ Обозначим $$h=\max\{h_i:i=1,2,\dots,N\}$$. Вектора $$y_i$$ имеют смысл значений приближенного решения в точках $$t_i$$. Для нахождения приближенного решения внутри интервалов $$(t_{i-1},t_i)$$ можно применять различные методы интерполяции, например, линейной. Функция $$y^N(t)$$, построенная с помощью линейной интерполяции, называется ломанной Эйлера. Если для рассматриваемой задачи выполнены условия теоремы 16.2 и на отрезке $$[0,T]$$ существует решение $$y(t)$$, то приближенное решение $$y^N(t)$$ сходится к точному решению равномерно имеет место оценка$$\max\limits_{t\in[0,T]}|y(t)-y^N(t)|\le Ch.$$ Таким образов метод Эйлера имеет первый порядок точности.

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

$$\begin{verbatim} abstract class TODE { public int N; protected double t; // текущее время // искомое решение Y[0] - само решение, // Y[i] - i-тая производная решения public double[] Y; protected double[] YY; // внутренние переменные public TODE(int N) // N - размерность системы { this.N = N; Y = new double[N]; // создать вектор решения YY = new double[N]; // и внутренних решений } // установить начальные условия. // t0 - начальное время, Y0 - начальное условие public void SetInit(double t0, double[] Y0) { t = t0; int i; for (i = 0; i < N; i++) { Y[i] = Y0[i]; } } public double GetCurrent() // вернуть текущее время { return t; } abstract public void F(double t, double[] Y, ref double[] FY); // правые части системы. // следующий шаг, dt - шаг по времени abstract public void NextStep(double dt); } \end{verbatim}$$

На основе этого класса построим класс, реализующий метод Эйлера.

$$\begin{verbatim} abstract class TEuler : TODE { public TEuler(int N) : base(N) {} public override void NextStep(double dt) { int i; F(t, Y, ref YY); for (i = 0; i < N; i++) { Y[i] = Y[i] + dt * YY[i]; } t = t + dt; } } \end{verbatim}$$

Прежде чем испытать наш метод Эйлера, мы рассмотрим и реализуем метод Рунге-Кутта $$4$$ -го порядка. Метод Рунге-Кутта, также как и метод Эйлера, допускает перемену шага, но имеет значительно большую точность. Пусть $$t_i$$ и $$y_i$$ имеют тот же смысл, что и при рассмотрении метода Эйлера.

Правила построения точек $$y_i$$ следующие$$y_i=y_{i-1}+\frac{h_i}{6}(k_1+2k_2+2k_3+k_4),$$ где$$k_1=f(y_{i-1},t_{i-1}),$$ $$k_2=f\left(y_{i-1}+\frac{h}{2}k_1,t_{i-1}+\frac{h}{2}\right),$$ $$k_3=f\left(y_{i-1}+\frac{h}{2}k_2,t_{i-1}+\frac{h}{2}\right),$$ $$k_4=f(y_{i-1}+hk_3,t_{i-1}+h).$$ Как мы видим, для расчета решения на следующем шаге необходимо вычислить правую часть четыре раза, зато точность этого метода имеет четвертый порядок, при условии гладкости правой части.

Перейдем к реализации метода Рунге-Кутта на основе нашего класса $$TODE$$.

$$\begin{verbatim} abstract class TRungeKutta : TODE { double[] Y1, Y2, Y3, Y4; // внутренние переменные public TRungeKutta(int N) : base(N) { Y1 = new double[N]; Y2 = new double[N]; Y3 = new double[N]; Y4 = new double[N]; } \end{verbatim}$$ $$\begin{verbatim} // следующий шаг метода Рунге-Кутта, dt - шаг по времени override public void NextStep(double dt) { if (dt < 0) { return; } int i; F(t, Y, ref Y1); // рассчитать Y1 for (i = 0; i < N; i++) { YY[i] = Y[i] + Y1[i] * (dt / 2.0); } F(t + dt / 2.0, YY, ref Y2); // рассчитать Y2 for (i = 0; i < N; i++) { YY[i] = Y[i] + Y2[i] * (dt / 2.0); } F(t + dt / 2.0, YY, ref Y3); // рассчитать Y3 for (i = 0; i < N; i++) { YY[i] = Y[i] + Y3[i] * dt; } F(t + dt, YY, ref Y4); // рассчитать Y4 for (i = 0; i < N; i++) { // рассчитать решение на новом шаге Y[i] = Y[i] + dt / 6.0 * (Y1[i] + 2.0 * Y2[i] + 2.0 * Y3[i] + Y4[i]); } t = t + dt; // увеличить шаг } } \end{verbatim}$$

Теперь протестируем наши методы на примере задач Коши, описывающей гармонические колебания математического маятника. Мы решаем следующую задачу.$$y''(x)+y(x)=0$$ с начальными условиями$$y(0)=0,$$ $$y'(0)=1.$$ Эта задача имеет единственное решение - $$\sin t$$. Чтобы применить к этой задаче наши методы нужно записать ее в виде системы уравнений первого порядка.$$Y_0(t)=y(t),$$ $$Y_1(t)=y'(t).$$ После такой замены мы имеет следующую систему$$Y'_0(t)=Y_1(t)$$ $$Y'_1(t)=-Y_0(t) $$ с начальными условиями$$Y_0(0)=0,$$ $$Y_1(0)=1.$$ Реализуем для нашей системы методы Эйлера и Рунге-Кутты

$$\begin{verbatim} class THarmonicEuler : TEuler { public THarmonicEuler() : base(2) { } public override void F(double t, double[] Y, ref double[] FY) { FY[0] = Y[1]; FY[1] = -Y[0]; } } class THarmonicRK : TRungeKutta { public THarmonicRK() : base(2) { } public override void F(double t, double[] Y, ref double[] FY) { FY[0] = Y[1]; FY[1] = -Y[0]; } } \end{verbatim}$$

Испытаем наши классы

$$\begin{verbatim} double h = 0.1; double[] Y0 = { 0, 1.0 }; THarmonicEuler HarmonicEuler = new THarmonicEuler(); HarmonicEuler.SetInit(0, Y0); THarmonicRK HarmonicRK = new THarmonicRK(); HarmonicRK.SetInit(0, Y0); StreamWriter F = File.CreateText("harmonic.txt"); double t, Euler, RK, Sin; while (HarmonicEuler.GetCurrent() < (2 * Math.PI + h / 2.0)) { t = HarmonicEuler.GetCurrent(); Euler = HarmonicEuler.Y[0]; RK = HarmonicRK.Y[0]; Sin = Math.Sin(t); F.WriteLine("{0}\t{1}\t{2}\t{3}\t\t{4}\t{5}\t{6}", t, Euler, RK, Sin, t, Math.Abs(Sin - Euler), Math.Abs(Sin - RK)); HarmonicEuler.NextStep(h); HarmonicRK.NextStep(h); } F.Close(); \end{verbatim}$$

Результаты расчетов приведем на трех графиках. На рисунке 16.1 мы приводим график точного решения и приближенных, полученных методами Эйлера и Рунге-Кутты. Однако на этом графике не возможно отличить точное решение от приближенного решения, полученного методом Рунге-Кутты. На рисунках 16.2 и 16.3 мы показываем погрешности методов. Из анализа этих графиков видно, что точность метода Рунге-Кутты значительно выше, что обосновывается теоретически.

(рис 16.2) Точное и приближенное решение дифференциального уравнения(рис 16.1) Погрешность метода Эйлера(рис 16.3) Погрешность метода Рунге–Кутты

Ключевые термины

Глобальное решение - решение задачи Коши, существующее при всех $$t\ge0$$.

Задача Коши - дифференциальное уравнение с заданными начальными условиями на решение.

Метод Рунге-Кутта - наиболее распространенный метод численного интегрирования задачи Коши с точностью четвертого порядка.

Метод Эйлера - простейший метод численного интегрирования задачи Коши с точностью первого порядка.

Условие Липшица - условие на приращение функции, более сильное чем условие непрерывности, но слабее чем условие дифференцируемости.

Краткие итоги: Построены объектно-ориентированные средства для численного решения задачи Коши. Проведены вычислительные эксперименты, для сравнения методов Эйлера и Рунге-Кутты.

Вернуться к учебному плану