我正在尝试编写一个Python程序来使用Tanh-sinh求积来计算值:
但是虽然程序收敛到一个合理的值,并且在每种情况下都没有错误,但它没有收敛到正确的值(这是特定积分的pi),我找不到问题。
该程序不是要求达到所需的准确度,而是要求所需的功能评估数量,以便更容易地将收敛与更简单的集成方法进行比较。评估的数量需要是奇数,因为使用的近似值是
任何人都可以提出我可能做错的事吗?
import math
def func(x):
# Function to be integrated, with singular points set = 0
if x == 1 or x == -1 :
return 0
else:
return 1 / math.sqrt(1 - x ** 2)
# Input number of evaluations
N = input("Please enter number of evaluations \n")
if N % 2 == 0:
print "The number of evaluations must be odd"
else:
print "N =", N
# Set step size
h = 2.0 / (N - 1)
print "h =", h
# k ranges from -(N-1)/2 to +(N-1)/2
k = -1 * ((N - 1) / 2.0)
k_max = ((N - 1) / 2.0)
sum = 0
# Loop across integration interval
while k < k_max + 1:
# Compute abscissa
x_k = math.tanh(math.pi * 0.5 * math.sinh(k * h))
# Compute weight
numerator = 0.5 * h * math.pi * math.cosh(k * h)
denominator = math.pow(math.cosh(0.5 * math.pi * math.sinh(k * h)),2)
w_k = numerator / denominator
sum += w_k * func(x_k)
k += 1
print "Integral =", sum
答案 0 :(得分:2)
对于它的价值,Scipy具有数值积分功能
例如,
from scipy import integrate
check = integrate.quad(lambda x: 1 / math.sqrt(1 - x ** 2), -1, 1)
print 'Scipy quad integral = ', check
给出结果
Scipy quad integral =(3.141592653589591,6.200897573194197e-10)
其中元组中的第二个数字是绝对错误。
那就是说,我能够让你的程序进行一些调整(尽管这只是一次初步尝试):
1)按照this paper
的建议,将步长h设置为0.0002(大约1/2 ^ 12)但请注意 - 本文实际上建议迭代地改变步长 - 使用固定的步长,您将达到sinh或cosh对于足够大的 kh 值而言变得太大的点。根据该论文的方法尝试实施可能会更好。
但坚持手头的问题,
2)确保为集成设置足够的迭代以真正收敛,即math.fabs(w_k * func(x_k))&lt;的足够迭代次数。 1.0E-9
通过这些调整,我能够使用&gt;将积分收敛到正确的pi值到4个有效数字。 30000次迭代。
例如,对于31111次迭代,pi计算的值为3.14159256208
带有修改的示例代码(注意我用sum替换了sum,sum是Python内置函数的名称):
import math
def func(x):
# Function to be integrated, with singular points set = 0
if x == 1 or x == -1 :
return 0
else:
return 1 / math.sqrt(1 - x ** 2)
# Input number of evaluations
N = input("Please enter number of evaluations \n")
if N % 2 == 0:
print "The number of evaluations must be odd"
else:
print "N =", N
# Set step size
#h = 2.0 / (N - 1)
h=0.0002 #(1/2^12)
print "h =", h
# k ranges from -(N-1)/2 to +(N-1)/2
k = -1 * ((N - 1) / 2.0)
k_max = ((N - 1) / 2.0)
thesum = 0
# Loop across integration interval
actual_iter =0
while k < k_max + 1:
# Compute abscissa
x_k = math.tanh(math.pi * 0.5 * math.sinh(k * h))
# Compute weight
numerator = 0.5 * h * math.pi * math.cosh(k * h)
dcosh = math.cosh(0.5 * math.pi * math.sinh(k * h))
denominator = dcosh*dcosh
#denominator = math.pow(math.cosh(0.5 * math.pi * math.sinh(k * h)),2)
w_k = numerator / denominator
thesum += w_k * func(x_k)
myepsilon = math.fabs(w_k * func(x_k))
if actual_iter%2000 ==0 and actual_iter > k_max/2:
print "Iteration = %d , myepsilon = %g"%(actual_iter,myepsilon)
k += 1
actual_iter += 1
print 'Actual iterations = ',actual_iter
print "Integral =", thesum
答案 1 :(得分:2)
使用multiprecision库mpmath
:
from mpmath import *
mp.dps = 100
h = mpf(2**-12);
def weights(k):
num = mpf(0.5)*h*pi*cosh(k*h)
den = cosh(mpf(0.5)*pi*sinh(k*h))**2
return (num/den)
def abscissas(k):
return tanh(mpf(0.5)*pi*sinh(k*h))
def f(x):
return 1/sqrt(1 - mpf(x)**2)
N = 20000
result = 0
for k in range(-N, N+1):
result = result + weights(k)*f(abscissas(k))
print result - pi
给出错误
-3.751800610920472803216259350430460844457732874052618682441090144344372471319795201134275503228835472e-45
答案 2 :(得分:1)
我认为部分问题可能是由于范围和步长造成的。我修改了代码 所以你可以分别放入范围和步长,并重写一些数学。它似乎给出了正确的答案。尝试使用示例5和0.1作为输入。
一个特别的问题是计算math.cosh(0.5 * math.pi * math.sinh(k * h))
为k * h
变大math.sinh(k * h)
呈指数增长而计算math.cosh可能很难。
导入数学
def func(x):
# return 1 # very simple test function
# Function to be integrated, with singular points set = 0
if x == 1 or x == -1 :
return 0
else:
return 1 / math.sqrt(1 - x ** 2)
# Input number of evaluations
N = input("Please enter max value for range \n")
print "N =", N
h = input("Please the step size \n")
print "h =", h
k = -N
k_max = N
sum = 0
count = 0
print "k ", k , " " , k_max
# Loop across integration interval
while k < k_max + 1:
# Compute abscissa
v = k
u = math.pi * 0.5 * math.sinh(v)
x_k = math.tanh(u)
#print u
# Compute weight
numerator = 0.5 * math.pi * math.cosh(v)
csh = math.cosh(u)
denominator = csh*csh
w_k = numerator / denominator
print k, v, x_k , w_k
sum += w_k * func(x_k)
count += 1
k += h # note changed
res = sum * h
print "Integral =", sum * h
答案 3 :(得分:1)
你必须意识到+1和-1是你的被积函数的奇点,f(x)-->+infinity
是x-->+1,-1
。因此,您可以从边界点使用您最喜欢的正交公式远,但您必须根据f(x)
的局部扩展计算出特殊的正交公式在他们附近。
方法草图:
选择一些epsilon<<1
。
将积分I
分解为平滑和奇异的部分:
I_smooth
是[-1+epsilon, 1-epsilon]
I_singular
是来自[-1, -1+epsilon]
和[1-epsilon, 1]
的积分。在[-1+epsilon, 1-epsilon]
区间内应用标准正交规则
得到I_smooth
。
围绕奇点进行局部展开(例如 x = 1 ):
f(x) = 1/sqrt(1-x) * (a0 + a1*(1-x) + a2*(1-x)^2 + ...)
= f0(x-1) + f1(x-1) + f2(x-1) + ..
这只是关于x=1
f(x)*sqrt(1-x)
1/sqrt(1-x)
预乘fn(x-1) = an*(1-x)^n/sqrt(1-x
的泰勒扩张。 (不幸的是,你必须做一些数学计算并解决泰勒扩展问题
除非您有Mathematica或者您可以在某处找到它。)
每个单词Fn
)都可以完全集成(它只是一个幂函数)。让fn
成为1-epsilon
从1
到I_singular = F0 + F1 + F2 + ...
的积分。
大致I = I_smooth + I_singular
达到您想要的顺序。
最后:
epsilon
注意:为了提高准确度,你不应该使{{1}}太小,因为积分的爆破会使问题在数值上受到限制,而是增加泰勒展开的顺序。
答案 4 :(得分:0)
tanh-sinh求积有很多陷阱,其中一个就是需要非常接近间隔边界,在小于机器精度的距离(例如,在原始示例中为1.0 - 1.0e-20
。当评估这一点时,它会四舍五入到1.0
,其中f
具有奇异性,并且可能发生任何事情。这就是为什么要先转换函数,以使奇点为0的原因。
在1 / sqrt(1 - x**2)
的情况下,左奇点和右奇点均为1 / numpy.sqrt(-x**2 + 2*x)
。有了quadpy(属于我的一个项目),一个人就会得到
import numpy
import quadpy
# def f(x):
# return 1 / numpy.sqrt(1 - x ** 2)
val, error_estimate = quadpy.line_segment.tanh_sinh_lr(
[lambda x: 1 / numpy.sqrt(-x**2 + 2*x)], # = 1 / sqrt(1 - (x-1)**2)
[lambda x: 1 / numpy.sqrt(-x**2 + 2*x)], # = 1 / sqrt(1 - (-(x-1))**2)
2, # length of the interval
1.0e-10
)
print(val, val - numpy.pi)
3.1415926533203944 -2.693987255497632e-10