在 Python 2.7 中四舍五入时分数不太准确

Less accurate fractions when rounding in Python 2.7

大家好,我是 python 的新手,这是我的第一个项目。我正在创建一个计算器,当我输入圆的周长时,它会为我计算直径。例如摩托车上的把手,无需取下把手。

我让它工作了,但我真的需要它不太准确,比如四舍五入到最近的 1/8。如果我输入 2.75,它会完美运行,但如果我输入 2.76,它会给出疯狂的分数。我仍然希望它读到 7/8,直到它超过 1.0

的一半

这是我目前的所有信息,如果您能提供任何帮助,我们将不胜感激。

from fractions import Fraction

print "Do you wish to calculate the Diameter or Radius from a perimeter measurement? Type 'd' or 'r'"
var = raw_input("Please type 'd' or 'r'")


if var == 'd':
    print "Now type the perimeter measurement for the diameter you would like calculate."
    cir = float(raw_input())
    answer = cir/3.1415926535
    rnd = round(answer, 3 )
    frac = Fraction(rnd).limit_denominator()
    print rnd
    print "The diameter of a %s perimeter is" % cir, frac
elif var == 'r':
    print "Now type the perimeter measurement for the Radius you would like to calculate."
    cir = float(raw_input())
    answer = cir/3.1415926535/2
    print "The Radius of a %s perimeter is" % cir, answer 
else:
    print "Error: You did not type 'd' or 'r' Please run again"

您可以四舍五入到最接近的 1/8。

In [1]: from fractions import Fraction

In [14]: Fraction(round(2.6*8)/8).limit_denominator()
Out[14]: Fraction(21, 8)

In [4]: Fraction(round(2.75*8)/8).limit_denominator()
Out[4]: Fraction(11, 4)

In [5]: Fraction(round(2.9*8)/8).limit_denominator()
Out[5]: Fraction(23, 8)

要同分母就简单了

denominator = 8
rnd = round(answer * denominator)
frac = Fraction(int(rnd), denonimator)

之前的所有方法都使用了错误的round(x*8)/8方法;当然,当我着急的时候,我有时(不经常)使用它,但这是一种错误的方法。

方法一(最正统)

根据连分数理论,有四个最佳近似到2.76,即:

2, 3, 11/4, 69/25

您可能会争辩说 11/4 与 22/8(如所问)相同,但您不能指望它对任何数字都适用。以 Pi 为例; round(3.1415926*8)/8 是 25/8,与 22/7 相比,这是一个非常差的近似值:22/7 实际上将 Pi 近似为 1/8,但甚至更好(虽然比 25/8 更简单) ! 楼主想要的是1/8的近似值。看看以下三个分数:22/7、25/8、314/100;都将 Pi 近似为 1/8,但您会选择哪一个(可能是第一个;现在:第一个不仅是三个分数中最简单的;它也是最准确的)?比 22/7 更好的最简单分数跳到小于 1e-4 的精度(即 333/106)!

此外,如果你想接近0.5,你不能满足于4/8!当然 fractions 模块可能会在初始化时取消它,但这无论如何都是一个非常糟糕的迹象:你的方法不想产生像 4/8 这样的东西,即使是作为中间步骤也是如此!

开个玩笑:拿0.33333333...试试round(x*8)/8方法;你会发现 3/8 近似于 0.33333333... 到 1/8,这当然是正确的,但是... 怎么礼貌地说呢?

看看下面的函数;你可以放心地依靠它来近似一个数字:

from fractions import Fraction                                              

def rationalize(x, maxden=1000):
    p0, p1 = 0, 1
    q0, q1 = 1, 0
    a = int(x) if x >= 0 else int(x-1)
    while q1 <= maxden:
        p = a*p1 + p0
        q = a*q1 + q0
        p0, p1 = p1, p
        q0, q1 = q1, q
        if x==a:
            if q1 <= maxden: return Fraction(p1, q1)
            else: return Fraction(p0, q0)
        x = 1/(x-a)
        a = int(x)
    return Fraction(p0, q0)

如果你用 maxden=8 与 Pi 一起尝试,你会得到预期的 22/7(不再是疯狂的 25/8)。

函数returns 收敛 到您的数字,该数字是从数字的连分式扩展中的连续部分商构建的。请参阅 https://en.wikipedia.org/wiki/Diophantine_approximation

中的说明

方法二

一些计算机代数系统也使用线性整数关系算法(例如PSLQ)来解决这个问题;例如 Sympy 使用 mpmath.pslq 方法来完成它。这非常方便并且给出了很好的结果,因为 PSLQ returns 向量给出了 1 和 x 之间具有最小范数的整数关系。但还是比另一种方法复杂。