我试图在 Python 中计算从 A 点到 B 点经过中间点列表的路径长度。我知道该怎么做,但我确实想使用 reduce 内置函数。
为什么我尝试到目前为止,请注意这是完全错误的,是这样的:
reduce(lambda x,y: math.sqrt((y[1]-y[0])**2+(x[1]-x[0])**2) , ((1,2),(3,4),(1,8)))
任何的想法?
谢谢。
我试图在 Python 中计算从 A 点到 B 点经过中间点列表的路径长度。我知道该怎么做,但我确实想使用 reduce 内置函数。
为什么我尝试到目前为止,请注意这是完全错误的,是这样的:
reduce(lambda x,y: math.sqrt((y[1]-y[0])**2+(x[1]-x[0])**2) , ((1,2),(3,4),(1,8)))
任何的想法?
谢谢。
你应该在减少之前映射。
points = [(1, 2), (3, 4), (1, 8)]
distances = (math.hypot(b[0]-a[0], b[1]-a[1])
for a, b in zip(points, points[1:]))
total_distance = sum(distances)
或者,如果您必须使用reduce()
, 虽然sum()
为此目的更好:
import operator
total_distance = reduce(operator.add, distances)
如果您有很多要点,您可能会发现 NumPy 有助于快速完成这一切:
import numpy
total_distance = numpy.hypot(*numpy.diff(numpy.array(points), axis=0)).sum()
编辑:使用math.hypot()
和添加 NumPy 方法。
它不漂亮,但可以做到:-)
>>> tot = ((1,2),(3,4),(1,8))
>>> reduce(lambda d,((x0,y0),(x1,y1)): d + ((x1-x0)**2+(y1-y0)**2)**0.5, zip(tot[1:], tot[0:]), 0.0)
7.3005630797457695
reduce()
只是用于此目的的错误工具。可以做到这一点,但reduce()
有点奇怪:
def distance((x, d), y):
return y, d + math.hypot(y[0] - x[0], y[1] - x[1])
print reduce(distance, [(3,4),(1,8)], ((1, 2), 0.0))[1]
印刷
7.30056307975
传递给reduce()
调用的最后一个参数是距离的起点和初始值。
reduce 不是这样工作的,您从一个初始值 a 开始,您指定或将其作为可迭代的第一个元素。之后,您将 a,next_element 传递给提供的函数 (lambda) 并将结果存储在 a 中,重复直到所有元素都被迭代。
您可以通过首先计算从一个点到下一个点的所有距离然后对它们求和来使用 sum 和 map 做你想做的事情:
path = [(1,2),(3,4),(1,8)]
sum(map(lambda x,y: math.sqrt((x[0]-y[0])**2+(x[1]-y[1])**2), path[:-1],path[1:]))
编辑:或使用hypot
功能(thx @ralu):
sum(map(lambda x,y: math.hypot(x[0]-y[0],x[1]-y[1]), path[:-1],path[1:]))
这是一个redux
元迭代器,可以与内置的组合reduce
以获得您想要的结果。此实现避免了输入序列的所有缓冲。
def redux(f):
def execute(iterable):
iterable = iter(iterable)
try:
state = iterable.next()
except StopIteration:
raise ValueError, 'empty sequences not supported'
while True:
newstate = iterable.next()
yield f(state, newstate)
state = newstate
return execute
f = redux(lambda x, y: math.sqrt((y[0] - x[0])**2 + (y[1] - x[1])**2))
print reduce(operator.add, f(((1,2),(3,4),(1,8))))
以上打印7.30056307975
。
该redux
函数可以概括为在滑动窗口中一次支持两个以上的参数,方法是使用inspect.getargspec
计算其函数参数所需的参数数量。
这不是您想要编写的那种代码。减少不会是一个好的解决方案。
我建议一个迭代的。它将是最具可读性、pythonic 和可维护性的解决方案。
import math
path = [(1,2),(3,4),(1,8)]
def calc_dist(waypoints):
dist = 0.0
for i in range(len(waypoints) - 1):
a = waypoints[i]
b = waypoints[i+1]
dist += math.hypot(a[0]-b[0], b[1]-a[1])
return dist
print calc_dist( path )
我知道我将要提出的建议并不理想,但我认为这是我的贡献所能得到的最接近的结果。这是一个有趣的问题,即使它不是最传统的 reduce 应用程序。
关键问题似乎是在不覆盖点本身的情况下跟踪点到点的距离 - 为每个点添加另一个“维度”会为您提供一个可以跟踪跑步距离的字段。
iterable = ((1,2,0), (3,4,0), (1,8,0))
# originally ((1,2), (3,4), (1,8))
from math import sqrt
def func(tup1, tup2):
'''function to pass to reduce'''
# extract coordinates
x0 = tup1[0]
x1 = tup2[0]
y0 = tup1[1]
y1 = tup2[1]
dist = tup1[2] # retrieve running total for distance
dx = x1 - x0 # find change in x
dy = y1 - y0 # find change in y
# add new distance to running total
dist += sqrt(dx**2 + dy**2)
# return 2nd point with the updated distance
return tup2[:-1] + (dist,) # e.g. (3, 4, 2.828)
现在减少:
reduce(func, iterable)[-1]
# returns 7.3005630797457695
这样,元组的中间元组(即,在一次“减少”之后)变为:
((3, 4, 2.8284271247461903), (1,8,0))
只是为了好玩,这里有一个替代解决方案,其方法与该reduce(sum, map(hypot, zip(...)))
方法略有不同。
tot = ((1,2),(3,4),(1,8))
reduce(lambda (d,(x,y)),b: (d+math.hypot(x-b[0],y-b[1]), b), tot, (0, tot[0]))[0]
请注意,reduce
实际返回元组(距离,最后一点),因此[0]
在末尾。我认为这将比zip
解决方案更有效,但尚未实际检查。