对于宽度小于0.2的宽度,Python是否不运行Euler方法?

时间:2018-08-04 04:01:37

标签: python

我对python编码有好奇的问题。我有一个简单的代码,用于执行欧拉逼近,在数值上近似于微分方程的解。通过截取曲线的一部分并将其划分为等宽“ w”的间隔来实现。

代码是:

import math

x = 0
y = 2
w = 0.5

while x < 1:
    dydx = 1 - 2*x + y
    deltaY = dydx*w
    y = y + deltaY
    x += w
print(x,y)

奇怪的是,我发现该代码适用于从1到1/5的'w',但不是更小的。

例如,使用w = 1/5,代码可以正确输出(1.0,5.48832 ...)

或使用w = 1/4。代码正确输出(1.0,5.4414 ...)

但是如果我使用w = 1/6,则输出为(1.16667,6.27523 ...)

我为运行Euler修改方法和Romberg方法(近似于同一事物)的程序改编了相同的代码,并且它们对w <1/5做相同的事情。

我觉得答案很明显或晦涩难懂。如果有人有解决方案,我将非常感谢。

谢谢

1 个答案:

答案 0 :(得分:4)

0.2截止只是一个巧合。真正发生的是浮点舍入。

float值不能精确表示大多数分数;它们只是给您最接近您想要的数字的52位二进制分数。导致舍入错误。

  • 如果两次将1/2与0相加,您将得到正1。
  • 如果将1/3乘以0等于3,则会得到一个比1大一点的数字,但是1实际上是最接近该数字的二进制分数。
  • 如果将1/4乘以0等于四,则得到的正是1。
  • 如果五次加1/5到0,则得到的数字比1大一点。
  • 如果将1/6乘以0等于6,则得到的数字比1小一点。
  • 如果将1/7加0等于7,则得到的数字比1小一点。
  • 如果将1/8乘以0等于四,则得到的正是1。

因此,1/3很好,因为无论如何它会四舍五入为1。 1/5很好,因为当x稍大于1时,x < 1为假,并且循环停止。但是1/6和1/7不合适,因为当x比1小一点时,x < 1仍然是正确的,因此您的循环次数太多了。


最简单的解决方法是使用isclose

while not math.isclose(x, 1):

…尽管如果x不太接近一元分数,那将意味着无限循环。当然,您的方法不适用于此类值,但最好是得到一个错误或不正确的结果,而不要等到Universe结束。因此,您可能想做一些更聪明的事情,例如:

while x < 0.999999:

一个更好的解决方案是以某种速度为代价,对Fractionw使用x类型而不是float。您仍然可以将y保留为float,因此,当您只是寻找一个近似值时,您的计算将不会消耗所有的内存和时间,而用可笑的分母来构建分数:

import fractions

x = 0
y = 2.0
w = fractions.Fraction(1, 6)

while x < 1:
    dydx = 1 - 2*x + y
    deltaY = dydx*w
    y = y + deltaY
    x += w
print(x,y)

现在您将获得:

1 5.521626371742112

但是最好的选择可能只是跟踪w1/6的事实,就像这样:

import math

x = 0
y = 2
w_inv = 6
w = 1/w_inv

for _ in range(w_inv):
    dydx = 1 - 2*x + y
    deltaY = dydx*w
    y = y + deltaY
    x += w
print(x,y)

现在,舍入错误不再是问题;无论如何,我们肯定会循环6次。

0.9999999999999999 5.521626371742112