我对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做相同的事情。
我觉得答案很明显或晦涩难懂。如果有人有解决方案,我将非常感谢。
谢谢
答案 0 :(得分:4)
0.2截止只是一个巧合。真正发生的是浮点舍入。
float
值不能精确表示大多数分数;它们只是给您最接近您想要的数字的52位二进制分数。导致舍入错误。
因此,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:
一个更好的解决方案是以某种速度为代价,对Fraction
和w
使用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
但是最好的选择可能只是跟踪w
是1/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