В связи с разработкой математической библиотеки для 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 и будет выложен в блог.