2011-11-17 234 views
4

我想在Python中计算从点A到点B经过中间点列表的路径的长度。 我知道如何去做,但我确实想使用减少内置功能Python:减少元组的元组

为什么我试过到目前为止,请注意,这是完全错误,是这样的:

reduce(lambda x,y: math.sqrt((y[1]-y[0])**2+(x[1]-x[0])**2) , ((1,2),(3,4),(1,8))) 

任何想法?

谢谢。

+2

我不认为你可以使用'reduce'来做到这一点,或者至少它不是最佳的方式imo。从理论上讲,'reduce'会给你迄今为止计算出来的距离以及一个点作为参数,但是你需要两点来计算距离。也许有一种奇特的方式来做到这一点,但为什么不只是遍历列表? –

+0

简单的加速技巧是对广场进行操作,最后做一个sqrt。 – qba

+2

@qba:你在哪里得到了总和的sqrt等于sqrt总和的想法?例如:'sqrt(2)+ sqrt(2)!= sqrt(2 + 2)' – KillianDS

回答

6

在缩小之前,您应该映射。

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方法。

+2

在数学中有函数hypot –

+0

+1 @raiu,我已经编辑过使用'math.hypot()'。 –

+0

这个解决方案缓存输入,所以对于更长的序列来说这将是昂贵的,除非'zip'表达式被重构了一点。 – wberry

1

reduce不起作用,你从一个初始值a开始,这个初始值是你指定的或者是你的迭代中的第一个元素。之后,将一个next_element传递给函数(lambda),并将结果存储在a中,直到所有元素都被迭代为止。

你可以做你想做的额头和地图通过首先计算从一个点都距离下一个,然后总结他们什么:

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:])) 
+1

尽管命名约定和问题中的代码不正确,你的'x'实际上是坐标第一点,你的'y'是下一点的坐标。所以这不会返回正确的距离。 –

+0

@迈克尔霍夫曼:的确,更正 – KillianDS

2

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()调用的最后一个参数是出发点和初始值距离。

+0

我会对downvotes的基本原理感兴趣。 –

+1

+1。你的回答正是OP要求的。也许人们正在降低自己而不是你的答案。 –

+0

还好,谢谢 – lc2817

4

这是不漂亮,但它可以做到:-)

>>> 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 
+0

为了避免缓冲更长的序列,'zip'表达式需要一些重构。但是对于聪明的黑客来说却很赞。 +1 – wberry

+0

如果序列长度值得关注,您可以随时替换* itertools.izip *。感谢upvote :-) –

+0

我指的是用切片实现的切换,即使使用“izip”也会使用原始序列的两倍内存成本。 – wberry

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

通过使用inspect.getargspec来计算其函数参数所需的参数数量,redux函数可以推广到在滑动窗口中一次支持两个以上的参数。

1

这只是你想写的代码而已。 Reduce不是一个好的解决方案。

我建议迭代一个。 这将是最可读,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) 
0

我知道我即将提出的建议并不理想,但我认为这是尽可能接近我的贡献。这是一个有趣的问题,即使它不是最传统的减少应用程序。

关键问题似乎是跟踪点到点之间的距离而不覆盖点本身 - 为每个点添加另一个“尺寸”会为您提供一个可跟踪跑步距离的字段。

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)) 
+0

这基本上实现了两步缩小,使用每个元组中的第三个元素作为第一步的结果。 – wberry

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解决方案更有效,但实际上没有检查。