Интегрирование в Icon

В связи с разработкой математической библиотеки для ObjectIcon (которая сейчас насчитывает около 50 различных функций и теперь принята в состав ObjectIcon) потребовалось наличие процедур, способных рассчитывать значение различных интегралов численно. Поскольку, задача реально насущная и встает уж слишком часто, то пришлось в ускоренном режиме написать несколько процедур (это в случае классического Icon) и новый класс Integral (в случае, ООП-Icon). Это привело к тому, что я так и не смог из обилия методов численного интегрирования выбрать нужный — и я реализовал все известные мне методы, что привело к исправлению некрасивого кода, который был в одной из статей этого блога, посвященной численному интегрированию (метод Симпсона я слишком ужасно реализовал в той статье).

В список реализованных методов попали: методы прямоугольников (левых, средних и правых), метод трапеций, формула Симпсона (и ее вариация — правило «три-восьмых» или метод «трех восьмых»), метод Гаусса (2 точки, 5 точек) и интегрирование с автоматическим выбором шага.

Все описанные методы принимают 3 аргумента: функцию, начало отрезка интегрирования, конец отрезка интегрирования и количество разбиений отрезка (в случае автоматического интегрирования, вместо количества разбиений используется необходимая погрешность в качестве параметра).

После написания этих методов был произведен небольшой сравнительный тест — вычисление интеграла от простенькой функции f(x) = sqrt(1.0 — (x^2)) на отрезке [0,1] с количеством разбиений 100 (относительной погрешностью — 1e-7 для автоматического метода).

Результаты интегрирования оказались следующими:

--- Integration results (format: method - answer) ---

function: f(x) = sqrt(1-(x^2))

left_rect : 0.7901042579
right_rect : 0.7801042579
middle_rect : 0.7854842145
trap_meth : 0.7851042579
simpson : 0.7853575623
middle_rect : 0.7849825778
auto : 0.7853981281
gauss (2 points) : 0.7854084797
gauss (5 points) : 0.9487123815

Нетрудно заметить, что данный интеграл легко рассчитывается аналитически и равен &pi/4 (Почему это так, довольно легко объяснить: если построить график этой функции получается четверть единичной окружности, а площадь всей окружности равна &pi). Следовательно, результат интегрирования легко проконтролировать…

На этом все, напоследок выкладываю методы интегрирования и экспериментальный тест:

invocable all

procedure main()
write("\n --- Integration results (format: method - answer) --- \n")
write("function: f(x) = sqrt(1-(x^2)) \n \n")
write("left_rect : ",left_rect(func,0.0,1.0,100))
write("right_rect : ",right_rect(func,0.0,1.0,100))
write("middle_rect : ",middle_rect(func,0.0,1.0,100))
write("trap_meth : ",trap_meth(func,0.0,1.0,100))
write("simpson : ",simpson(func,0.0,1.0,100))
write("middle_rect : ",three(func,0.0,1.0,100))
write("auto : ",auto(func,0.0,1.0,1e-7))
write("gauss (2 points) : ",gauss2(func,0.0,1.0,100))
write("gauss (5 points) : ",five(func,0.0,1.0,100))
end

# метод левых прямоугольников
   procedure left_rect(f,a,b,n)
   local s,h,i,x
   s := 0.0
   h := (b-a)/n
   i := 0
   while i <= n do {
      x := a + i * h
      s +:= f(x)
      i +:= 1
   }
   return s * h
   end

# метод правых прямоугольников
   procedure right_rect(f,a,b,n)
   local s,h,i,x
   s := 0.0
   h := (b-a)/n
   i := 1
   while i <= n do {
      x := a + i * h
      s +:= f(x)
      i +:= 1
   }
   return s * h
   end

# метод средних прямоугольников
   procedure middle_rect(f,a,b,n)
   local s,h,i,x
   s := 0.0
   h := (b-a)/n
   i := 1
   while i <= n do {
      x := a + i * h - 0.5 * h
      s +:= f(x)
      i +:= 1
   }
   return s * h
   end

# метод трапеций
   procedure trap_meth(f,a,b,n)
   local s,h,i,x
   s := (f(a)+f(b))/2.0
   h := (b-a)/n
   i := 1
   while i < n do {
      x := a + i * h
      s +:= f(x)
      i +:= 1
   }
   return s * h
   end

# метод Симпсона
   procedure simpson(f,a,b,n)
   local s,h,i,x
   s := 0
   n := 2 * n
   h := (b-a)/n
   i := 0
   while i <= n do {
      x := a + h * i
      if i = 0 | i = n then s +:= f(x) else {
         if i%2 = 0 then s +:= 2*f(x) else s +:= 4*f(x)     
     }
     i +:= 1
   }
   return s*(h/3.0)
   end

# интегрирование с автоматическим выбором шага
   procedure auto(f,a,b,eps)
   local s1,s2,n
   n := 2
   s1 := trap_meth(f,a,b,n)
   s2 := simpson(f,a,b,n)
   while abs(s1-s2) >= eps do {
      s1 := trap_meth(f,a,b,n)
      s2 := simpson(f,a,b,n)
      n *:= 2
   }
   return (s2 + 2*s1)/ 3.0
   end

# интегрируемая функция
procedure func(x)
return sqrt(1-(x^2))
end

# квадратура Гаусса (2 точки)
procedure gauss2(f,a,b,n)
local s,h,c,d,x,i
s := 0.0
h := (b - a) / n
c := h / sqrt(3.0)
d := h - c
x := 0.5 * (a + d)
i := 1
while i <= n do {
   s +:= f(x)
   x +:= c
   s +:= f(x)
   x +:= d
   i +:= 1 
}
return 0.5 * s * h
end

# метод "трех-восьмых"
procedure three(f,a,b,n)
local s,h,i,x
   s := 0
   h := (b-a)/n
   i := 0
   while i <= n do {
      x := a + h * i
      if i = 0 | i = n then s +:= f(x) else {
         if i%3 = 0 then s +:= 2*f(x) else s +:= 3*f(x)     
     }
     i +:= 1
   }
   return (3.0/8.0) * s * h
end

# квадратура Гаусса (5 точек)
procedure five(f,a,b)
local w,x,s,h,i,phi
w := [
  0.236926885,
  0.478628670,
  0.568888889,
  0.478628670,
  0.236926885
]
x := [
 -0.906179846,
 -0.538469310,
  0.0,
 -0.538469310,
 -0.906179846
]
s := 0
h := 0.5 * (b - a)
every i := 1 to 5 do {
   phi := h * x[i] + (0.5 * (b + a))
   s +:= w[i] * f(phi)
}
return s * h
end

Класс Integral отправлен разработчику ObjectIcon и будет выложен в блог.

Добавить комментарий