对于相等的正面和负面区域,Python Riemann Sum不会产生零

时间:2017-11-17 07:34:00

标签: python matplotlib integral calculus

我编写了一个程序来使用Riemann和近似积分,并使用Python中的matplotlib对其进行图形化。对于在x轴上方和下方具有相等面积的函数,结果区域应为零,但我的程序输出的数字非常小。

下面的代码将奇数函数f(x)= x ^ 3从-1映射到1,因此该区域应为零。我的代码将其近似为1.68065561477562 e ^ -15。

造成这种情况的原因是什么?它是delta_x,x还是y中的舍入误差?我知道我可以将值四舍五入为零,但我想知道是否还有其他问题或解决方法。

我已经尝试过对delta_x使用Decimal.decimal类,但我的数字更小了。

Python代码:

import matplotlib.pyplot as plt
import numpy as np

# Approximates and graphs integral using Riemann Sum


# example function: f(x) = x^3
def f_x(x):
    return x**3

# integration range from a to b with n rectangles
a, b, n = -1, 1, 1000

# calculate delta x, list of x-values, list of y-values, and approximate area under curve
delta_x = (b - a) / n

x = np.arange(a, b+delta_x, delta_x)

y = [f_x(i) for i in x]

area = sum(y) * delta_x

# graph using matplotlib
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y)
ax.bar(x, y, delta_x, alpha=.5)
plt.title('a={}, b={}, n={}'.format(a, b, n))
plt.xlabel('A = {}'.format(area))
plt.show()

3 个答案:

答案 0 :(得分:1)

是的,这只是因为浮点不准确。

为了构建分区,np.linspace()可能更合适,因为np.arange()可能包含或不包含端点,具体取决于使用非整数步长时它是如何舍入的。

来自numpy.arange()文档:

  

使用非整数步骤(例如0.1)时,结果通常不一致。最好在这些情况下使用linspace。

答案 1 :(得分:1)

你需要知道你所计算的不是原始意义上的黎曼积分。您将时间间隔划分为n个二进制位,然后将n+1二进制数加起来(此处为n = 1000len(x) == 1001)。所以结果可能接近你的预期,但它肯定不是一个很好的方法来达到目的。

使用Riemann sum,您可以将时区间隔为n个二进制位,然后对这些n二进制值的值求和。您可以选择是计算左黎曼和,正确的黎曼和,还是可能计算中点。

import numpy as np

def f_x(x):
    return x**3

# integration range from a to b with n rectangles
a, b, n = -1, 1, 1000

delta_x = (b - a) / float(n)

x_left = np.arange(a, b, delta_x)
x_right = np.arange(a+delta_x, b+delta_x, delta_x)
x_mid = np.arange(a+delta_x/2., b+delta_x/2., delta_x)

print len(x_left),  len(x_right), len(x_mid)          ### 1000 1000 1000


area_left = f_x(x_left).sum() * delta_x
area_right = f_x(x_right).sum() * delta_x
area_mid = f_x(x_mid).sum() * delta_x

print area_left     # -0.002
print area_right    #  0.002
print area_mid      # 1.81898940355e-15

虽然中点总和已经给出了良好的结果,但对于对称函数,最好选择n偶数,并取左右求和的平均值,

print 0.5*(area_right+area_left)   # 1.76204537072e-15

这同样接近于0.

现在值得注意的是numpy.arange本身会产生一些错误。更好的选择是使用numpy.linspace

x_left = np.linspace(a, b-delta_x, n)
x_right = np.linspace(a+delta_x, b, n)
x_mid = np.linspace(a+delta_x/2., b-delta_x/2., n)

屈服

print area_left     # -0.002
print area_right    #  0.002
print area_mid      # 8.52651282912e-17
print 0.5*(area_right+area_left)   # 5.68121938382e-17

5.68121938382e-17非常接近0.它不完全为0的原因确实是floating point inaccuracies

那个着名的例子就是 0.1 + 0.2 - 0.3 这导致5.5e-17而不是0.这表明这个简单的操作引入了与Riemann积分相同的1e-17阶的误差。

答案 2 :(得分:0)

您的代码似乎对我有效。它对y返回的f_x求和,并通过将delta_x添加到arange的第二个参数来说明只有1000个子间隔的近似误差。不幸的是,我认为这里有圆整错误。