Python 使用实数参数计数

Python 使用实数参数计数count()函数接收非整形参数,可以定义count(0.5, 0.1)这样的表达式来提供浮点数。如果步长值没有合适的表示形式,会形成累积误差。最好使用(0.5 + x * 0.1 for x in count())等整形count()参数来避免累积误差出现。

下面介绍如何检验累积误差。探索实数近似值用到了函数式编程的一些实用技巧。

下面定义一个函数从迭代器中不断取值,直到满足给定条件。until()函数定义如下:

from typing import Callable, Iterator TypeVar
T_ = TypeVar("T_")
def until(
        terminate: Callable[[T_], bool],
        iterator: Iterator[T_]
    ) -> T_:
    i = next(iterator)
    if terminate(i):
        return i
    return until(terminate, iterator)

首先从迭代器对象中取一个值,类型由类型变量T_定义。如果这个值通过了测试,即取到了符合要求的值,迭代结束,返回值的类型也是由类型变量T_定义的;否则递归调用函数自身,测试后面的值。

下面是一个可迭代对象实例和一个测试函数。

Generator = Iterator[Tuple[float, float]]
source: Generator = zip(count(0, 0.1), (.1*c for c in count()))

Extractor = Callable[[Tuple[float, float]], float]
x: Extractor = lambda x_y: x_y[0]
y: Extractor = lambda x_y: x_y[1]

Comparator = Callable[[Tuple[float, float]], bool]
neq: Comparator = lambda xy: abs(x(xy)-y(xy)) > 1.0E-12

生成器source赋值语句中的类型标示表明它在二元组上迭代。两个抽取函数x()y()从二元组中抽取实数。比较函数neq()接收实数二元组,返回布尔值。

通过赋值语句创建匿名函数对象,其中的类型标示协助mypy工具确定参数和返回值类型。

mypy工具检查until()函数时,将类型变量T_关联到实际类型Tuple[float, float]2。通过这个关联,能确认source生成器和neq()函数可以作为until()函数的参数。

这是从原始数据文件中记录数据行号的常规做法。

until(neq, source)在每次迭代中比较实数的近似值,直到二者出现显著差异。count()函数给出其中一个近似值

Python 使用实数参数计数

生成器函数给出另一个近似值

Python 使用实数参数计数

理论上这两个值没有差别,但基于抽象表示的具体近似值存在差异。

计算结果如下所示:

>>> until(neq, source)
(92.799999999999, 92.80000000000001)

928次迭代后,累积误差达到了10^{-12},两个结果都没有精确的二进制表示。

 count()函数的示例接近了Python的递归上限。如果要测试更大的累积误差,需要用尾调用优化技术重写until()函数。

计算能检测到的最小误差的方法如下:

>>> until(lambda x, y: x != y, source)
(0.6, 0.6000000000000001)

用相等检测代替了误差范围检测。6次迭代后,count(0, 0.1)的累积误差达到了 10^{-16} ,用二进制表示\frac{1}{10}需要无限长的位数,实际存储时只能截取保存有限位数字,导致误差产生并累积。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程