Python 2.7 - 续分数扩展 - 理解错误

时间:2016-06-29 10:49:06

标签: python division fractions integer-division continued-fractions

我编写了这段代码,用Euclidean算法计算有理数N的连续分数展开:

from __future__ import division

def contFract(N):
    while True:
        yield N//1
        f = N - (N//1)
        if f == 0:
            break
        N = 1/f

如果说N是3.245,则函数永远不会结束,因为显然f永远不等于0.扩展的前10个术语是:

  

[3.0,4.0,12.0,3.0,1.0,247777268231.0,4.0,1.0,2.0,1.0]

这显然是一个错误,因为实际扩展只是:

  

[3; 4,12,3,1]或[3; 4,12,4]

造成这个问题的原因是什么?这是某种舍入错误吗?

3 个答案:

答案 0 :(得分:4)

问题是你正在测试f == 0(整数0),这对于浮点数来说几乎都不是真的。所以循环会永远持续下去。

相反,检查一些等于0(which can be wrong sometimes)的精度:

>>> from __future__ import division
>>>
>>> def contFract(N):
...     while True:
...         yield N//1
...         f = N - (N//1)
...         if f < 0.0001:  # or whatever precision you consider close enough to 0
...             break
...         N = 1/f
...
>>>
>>> list(contFract(3.245))
[3.0, 4.0, 12.0, 3.0, 1.0]
>>>

如果f可能是否定的,请执行-0.0001 < f < 0.0001abs(f) < 0.0001。这也被认为是不准确的,请参阅链接的文章。

我的评论使用int(N)代替N//1,因为它更清晰 - 略微效率低下:

>>> import timeit
>>> timeit.timeit('N = 2.245; N//1', number=10000000)
1.5497028078715456
>>> timeit.timeit('N = 2.245; int(N)', number=10000000)
1.7633858824068103

答案 1 :(得分:1)

您正在使用float进行操作,遗憾的是,有些数字无法用二进制表示形式表示。

有两种选择如何解决它,首先 - 假设你的数字“足够接近”(即使是新的Python 3.5.2引入math.isclose),或者你正在使用不同的浮动实现,例如: Decimal您可以获得正确的结果。

N.b。这就是为什么对于所有金融系统,没有人使用浮点数,只有int / bigint或Decimals。

 In [21] > N = decimal.Decimal('3.245')

 In [22] > while True:
    print 'N: %s' % (N//1,)
    f = N - N//1
    print 'f: %s' % f
    if f == 0:
        break
    N = 1/f

N: 3
f: 0.245
N: 4
f: 0.081632653061224489795918367
N: 12
f: 0.25000000000000000000000005
N: 3
f: 0.999999999999999999999999200
N: 1
f: 8.00E-25
N: 1250000000000000000000000
f: 0

答案 2 :(得分:-1)

显然,这个错误是由4.0的整数除法引起的.4.0浮点表示有一个错误(如果您对浮点表示有所了解)。所以实际上是int(4.0)&lt;这导致N // 1变为3.0,分数f就像0.999999 ......所以这永远不会结束。在每次迭代中打印N和f并进行游戏。你会意识到的。