|
高斯勒让德求积分Fortran程序——自带求任意阶高斯点及相应权重的程序 句号大师(王培杰)编写,岸边的鱼(小超超小鱼)整理,标注
最近,因为要求解一些剧烈变化(且积分区间跨度比较大)的函数积分问题,想到了高斯型求积分公式,随查阅各种算法书籍,但无奈算法书上给出的都是利用五阶高斯勒让德公式求解积分的程序。经石子儿兄弟指点,认识到五阶高斯勒让德公式求解精度实在是有点小低,(解决办法有两个,一是本文的增加高斯结点,而是采用变步长高斯求积分法,此法将另文讨论。)石子儿兄弟建议将精度至少提高到300—500之间,那将面对自己计算高斯点及权系数的问题。无奈之下,遍寻各大编程网站,终于在我们亲爱的fcode.cn网站上找到了句号大师的一篇帖子,随私聊句号大师,感谢慷慨的句号大师将修改好的程序无私的传给我,感动之余,觉得有必要将这一成果整理,标注后发回我们的网站上供广大网友下载使用。
声明:下文程序整体由句号大师(王培杰)传递给我,仅对其表示由衷的感谢。我负责添加标注,设置了两个输出到文件的功能。另,句号大师提醒广大网友,高斯型求积公式不是高斯点越多越精确,高斯点变多后会有很多的截断误差。
[Fortran] 纯文本查看 复制代码 module GSLD !高斯—勒让德积分高斯点及权重的求解模块
implicit none
integer,parameter::n=5 !设置求解高斯点的个数
integer,parameter::nn=16 !设置kind的数值
contains
real(kind=nn) function f(x) !定义四阶精度的函数f(x)
implicit none
integer::i
real(kind=nn)::a(n),x !a(n)代表n阶勒让德多项式
a(1)=x !1阶勒让德多项式
a(2)=1.5_16*(x**2)-0.5_16 !2阶勒让德多项式
do i=3,n
a(i)=(2*i-1)*x*a(i-1)/i-(i-1)*a(i-2)/i
!利用递推关系产生n阶勒让德多项式
enddo
f=a(n) !生成的n阶勒让德多项式
end function
real(kind=nn) function f1(x)
implicit none
integer::i
real(kind=nn)::a(n),x
a(1)=x
a(2)=1.5_16*x**2-0.5_16
do i=3,n-1
a(i)=(2*i-1)*x*a(i-1)/i-(i-1)*a(i-2)/i
enddo
f1=a(n-1) !生成的(n-1)阶勒让德多项式
endfunction
real(kind=nn) function g(x)
implicit none
integer::i
real(kind=nn)::a(n),x
a(1)=x
a(2)=1.5_16*x**2-0.5_16
do i=3,n
a(i)=(2*i-1)*x*a(i-1)/i-(i-1)*a(i-2)/i
enddo
g=n*a(n-1)/(1-x**2)-n*x*a(n)/(1-x**2)
!生成n阶勒让德多项式的导数表达式
endfunction
real(kind=nn) function bis(a,b) !二分法求解函数的解
implicit none
real(kind=nn)::a,b,c
!a,b是传递进来的划分好的有一个解存在的区间
do while(.true.)
c=(a+b)/2.0_16
if(f(c)*f(a)<0)then
b=c
else
a=c
endif
if((b-a)<1E-34)exit !这个精度设置的。。(有点狠了)
enddo
bis=c !bis即是利用二分法求得的解
end function
subroutine fn0(fn,ak)
implicit none
real(kind=nn)::m,fn(n),ak(n)
!定义数组,大小n由module开始声明。
integer::i,j
open(10,file='高斯点+权重.dat')
j=0 !赋值控制循环变量的初值
m=-1.000001 !设置计算域[-1,1] 的下限,即代替-1
do i=1,200000 !这个循环次数应该是由步长0.00001决 定,计算方法:200000=2/0.000001
if(f(m)*f(m+0.00001)<0)then !从下限处开始往上逐步累加,
!由步长0.00001说明最多求解10^5个解
j=j+1 !记录这是第几个解
fn(j)=bis(m,m+0.00001)
!调用二分法求解程序在分好的一小段上求解,
!将解存储在fn(j)
ak(j)=2.0_16/(n*f1(fn(j))*g(fn(j))) !高斯点的权重
write(*,*) '高斯点序号',j
write(*,*) '高斯点' ,fn(j)
write(*,*) '高斯点权重',ak(j)
write(10,*) '高斯点',fn(j)
write(10,*) '权重' ,ak(j)
endif
m=m+0.00001 !执行完一次判断m向前推进一步
enddo
close (10)
endsubroutine
real(kind=nn) function y(x) !被积函数
implicit none
real(kind=nn)::x
y=x**2/3.0_16 !每次计算在这里修改被积函数即可
end function
end module
program www_fcode_cn
use GSLD
implicit none
real(kind=nn)::fn(n),ak(n),x,a,b,answer
integer::i
open(11,file='GS_LD_result.dat')
call fn0(fn,ak) !调用求高斯零点和权重的子函数
a=0.0_16 !积分上限
b=1.0_16 !积分下限
answer=0.0_16 !求积分结果赋初始值
do i=1,n
answer=answer+ak(i)*y((a+b)/2.0_16+(b-a)/2.0_16*fn(i))
!部分高斯勒让德求积分公式
enddo
answer=answer*(b-a)/2.0_16
write(11,*) '高斯_勒让德求积分结果',answer
write(*,*) '高斯_勒让德求积分结果',answer
close(11)
stop
end program www_fcode_cn
运行结果:
以下是使用高斯勒让德函数计算三重积分的例子:
[Fortran] 纯文本查看 复制代码 module GSLD
implicit none
integer,parameter::n=5
contains
real*16 function f(x)
integer::i
real*16::a(n),x
a(1)=x
a(2)=1.5*(x**2)-0.5
do i=3,n
a(i)=(2*i-1)*x*a(i-1)/i-(i-1)*a(i-2)/i
enddo
f=a(n)
endfunction
real*16 function f1(x)
integer::i
real*16::a(n),x
a(1)=x
a(2)=1.5*x**2-0.5
do i=3,n
a(i)=(2*i-1)*x*a(i-1)/i-(i-1)*a(i-2)/i
enddo
f1=a(n-1)
endfunction
real*16 function g(x)
integer::i
real*16::a(n),x
a(1)=x
a(2)=1.5d0*x**2-0.5d0
do i=3,n
a(i)=(2*i-1)*x*a(i-1)/i-(i-1)*a(i-2)/i
enddo
g=n*a(n-1)/(1-x**2)-n*x*a(n)/(1-x**2)
endfunction
real*16 function bis(a,b)
real*16::a,b,c
do
c=(a+b)/2d0
if(f(c)*f(a)<0)then
b=c
else
a=c
endif
if((b-a)<1E-34)exit
enddo
bis=c
endfunction
subroutine fn0(fn,ak)
real*16::m,fn(n),ak(n)
integer::i,j
j=0
m=-1.00000001
do i=1,200000
if(f(m)*f(m+0.00001d0)<0)then
j=j+1
fn(j)=bis(m,m+0.00001)
ak(j)=2/(n*f1(fn(j))*g(fn(j)))
write(*,*)j,fn(j),ak(j)
endif
m=m+0.00001
enddo
endsubroutine
real*16 function y(x,x1,x2) !被积函数
real*16::x,x1,x2
y=2*x+2*x1+2*x2
endfunction
endmodule
program www_fcode_cn
use GSLD
implicit none
real*16::fn(n),ak(n),x,x1,x2,a,b,a1,b1,a2,b2,answer
integer::i,j,k
call fn0(fn,ak)
a=0
b=1 !积分上下限
a1=0
b1=1
a2=0
b2=1
answer=0
do k=1,n
do j=1,n
do i=1,n answer=answer+ak(i)*ak(j)*ak(k)*y((a+b)/2+(b-a)/2*fn(i),(a1+b1)/2+(b1-a1)/2*fn(j),(a1+b1)/2+(b1-a1)/2*fn(k))
enddo
enddo
enddo
answer=answer*(b-a)/2*(b1-a1)/2*(b2-a2)/2
write(*,*)answer
pause
end program www_fcode_cn
|
|