我想出了一个相当优雅的解决方案(恕我直言),所以我忍不住发布它:
from bisect import bisect_left
class Interpolate(object):
def __init__(self, x_list, y_list):
if any(y - x <= 0 for x, y in zip(x_list, x_list[1:])):
raise ValueError("x_list must be in strictly ascending order!")
x_list = self.x_list = map(float, x_list)
y_list = self.y_list = map(float, y_list)
intervals = zip(x_list, x_list[1:], y_list, y_list[1:])
self.slopes = [(y2 - y1)/(x2 - x1) for x1, x2, y1, y2 in intervals]
def __getitem__(self, x):
i = bisect_left(self.x_list, x) - 1
return self.y_list[i] + self.slopes[i] * (x - self.x_list[i])
我映射到float,因此如果x1、x2、y1 和y2 都是某个迭代的整数,则整数除法(python
在__getitem__ 中,我利用 self.x_list 以升序排序这一事实,使用bisect_left 来(非常)快速找到小于x 在self.x_list 中的最大元素的索引.
像这样使用类:
i = Interpolate([1, 2.5, 3.4, 5.8, 6], [2, 4, 5.8, 4.3, 4])
# Get the interpolated value at x = 4:
y = i[4]
为简单起见,我在这里根本没有处理边界条件。实际上,i[x] for x < 1 将像从 (2.5, 4) 到 (1, 2) 的线已扩展到负无穷大一样工作,而 i[x] for x == 1 或 x > 6 将提高一个IndexError。最好在所有情况下都提出 IndexError ,但这留给读者作为练习。 :)