【问题标题】:Area of a polygon (Recursively using Python)多边形的面积(递归使用 Python)
【发布时间】:2012-03-15 21:01:06
【问题描述】:

我正在尝试解决 Exploring Python 书中的一个练习。但是,我想我不明白递归的概念。我写了一些递归函数。所以我知道一些方面。但是,我没有足够的经验。而且我已经停止学习编程大约一年了。

无论如何,让我给你一个完整的问题:

一个多边形可以用一个 (x, y) 对的列表来表示,其中每一对 是一个元组:[ (x1, y1), (x2, y2), (x3, y3) , ... (xn, yn)]。写一个 计算多边形面积的递归函数。这可以是 通过“切断”一个三角形来完成,使用的事实是 带角的三角形 (x1, y1), (x2, y2), (x3, y3) 的面积为 (x1y1 + x2y2 + x3y2 – y1x2 –y2x3 – y3x1) / 2.

尽管问题已经给出了公式,但我使用了另一个公式。因为,我对多边形的面积做了一些研究。如果你看看here,公式就不同了。

并且逐步描述我的程序会更好,以便解释我想要什么。 好的,由于递归,我必须声明全局范围:

area = 0
x = [0] * 3
y = [0] * 3 

然后,我创建了一个递归函数。结果,此函数始终返回零。所以我真正的问题是:

def areaofpolygon(polygon, i):
    global area, x, y # My variables 
    try: # I prefered using try statement from using if-else statements. So it is the easier I guess.
        x[i], y[i] = polygon[i] # X and Y coordinates from tuple
        area += (x[i]*y[i+1] - x[i+1]*y[i]) #My formula
    except IndexError:
        return area/2
    
    areaofpolygon(polygon, i+1)   # Here, this is my weird recursion

还有我的主要功能:

  def main():
      mypolygon = [(1,2), (2,5), (1,4)] # I declared polygon as tuples
      # I called my function and started to count from zero, and the result will be prompted.
      print(areaofpolygon(mypolygon,0))
        
      return 0
  if __name__ == '__main__':
      main()

这是我没有 cmets 的完整代码:

'''
Created on Feb 24, 2012

@author: msarialp
'''
area = 0
x = [0] * 3
y = [0] * 3
def areaofpolygon(polygon, i):
    global area, x, y
    try:
        x[i], y[i] = polygon[i]
        area += (x[i]*y[i+1] - x[i+1]*y[i])
    except IndexError:
        return area/2
    
    areaofpolygon(polygon, i+1)   
def main():
    mypolygon = [(1,2), (2,5), (1,4)]
    print(areaofpolygon(mypolygon,0))
    
    return 0
if __name__ == '__main__':
    main()

编辑一个

阅读您的答案后,我明白我的代码出了什么问题。所以我决定分享我的程序的最新版本以获得其他帮助。 同样,我必须声明全局变量。如何从 senderle 应用 (lop_triangle) 函数

area = 0
x = [0] * 3
y = [0] * 3

分割元组并获取 x 和 y 坐标的函数。

def sides_of_polygon(polygon, i):
    global x, y
    try:
        x[i], y[i] = polygon[i]
        return sides_of_polygon(polygon, i+1)
    except IndexError:
        return x, y

我的函数计算多边形的面积(和以前一样)

def area_of_polygon(x, y, i):
    global area
    try:
        area += x[i]*y[i+1] - x[i+1]*y[i]
        return area_of_polygon(x, y, i+1)
    except IndexError:
        return area/2.0

我的主要功能...

def main():
    mypolygon = [(1,2), (2,5), (1,4)]
    dx, dy = sides_of_polygon(mypolygon, 0)
    print(area_of_polygon(dx,dy,0))
    
    return 0
if __name__ == '__main__':
    main()

请在不提供完整解决方案的情况下帮助我改进我的代码。

编辑两个

在和senderle讨论之后,我明白了问题出在哪里,senderle的解决方案比我的好,所以我建议你应该使用它。 无论如何,他帮助我使我的代码正确。我不得不再次更改我的公式。

area += x[i]*y[(i+1) % 3] - x[(i+1) % 3]*y[i]

他还添加了更长的多边形 3 必须是 len(vertices)。 感谢大家的宝贵时间。

【问题讨论】:

  • 快速说明:def areaofpolygon(polygon, i=0): 可能会澄清一点。
  • 问题:四边形多边形的公式是(x1y1 + x2y2 + x3y3 + x4y3 – y1x2 –y2x3 – y3x4 - y4x1) / 2吗?
  • 根据问题,是的。但是,在我看来,真正的人是不同的。它必须是 (x1*y2 + x2*y3 + x3*y4 - x2*y1 - x3*y2 - x4*y3) / 2
  • 好吧,你的部分问题会自行消除——从相同的值 yx (- y3x4) 中减去 xy (+ x3y2) 的最后一个配对。所以,所有对xy(保存最后一个)都相乘......但是后半部分呢?......嗯。
  • @Droogans 这里是关于公式的链接:mathworld.wolfram.com/PolygonArea.html

标签: python recursion polygon area


【解决方案1】:

递归的本质如下:

  1. 确定一个简单的基本情况并解决它。
  2. 设想一个过程,当重复时,将更复杂的情况简化为基本情况。
  3. 将#2 中的过程应用于问题,直到达到基本情况。

就您而言,第一步很简单。最小的多边形是三角形。三角形的面积是(x1y2 + x2y3 + x3y1 – y1x2 –y2x3 – y3x1) / 2。 (看起来他们在问题中错误地陈述了它......)

第二步也很简单,因为问题陈述给了你:给定一个 n 顶点多边形,切掉一个三角形,确定它的面积,并将其添加到结果 (n-1) 的面积上——顶点多边形。

我们会将其分解为多个部分。首先,解决#1的函数:

def area_of_triangle(points):
    (x1, y1), (x2, y2), (x3, y3) = points
    return abs(x1 * y2 + x2 * y3 + x3 * y1 - y1 * x2 - y2 * x3 - y3 * x1) / 2

简单。现在是解决 #2 的函数。我们所需要的只是一个截断三角形并返回它和生成的较小多边形的函数:

def lop_triangle(points):
    triangle = [points[0], points[-1], points[-2]]
    polygon = points[:-1]
    return triangle, polygon

如果不是很明显,这只是使用多边形的第一个和最后两个点创建一个三角形。然后它会删除多边形的最后一个点,这现在相当于切掉了三角形。 (绘制一个 n 多边形并将其顶点从 0 标记为 n 以查看它是如何工作的。)所以现在我们有了一个三角形和一个更简单的多边形。

现在,让我们把它们放在一起。这第三步在某些方面是最难的,但是因为我们已经解决了前两个问题,所以第三步更容易理解。

def area_of_polygon(points):
    if len(points) == 3:
        return area_of_triangle(points)
    else:
        triangle, polygon = lop_triangle(points)
        return area_of_triangle(triangle) + area_of_polygon(polygon)

所有的魔法都发生在最后一行。每次area_of_polygon 得到一个三角形,它只返回一个三角形的面积。但是当它得到一个更大的多边形时,它会切掉一个三角形,取那个三角形的面积,然后把它加到……一个较小的多边形的面积上。所以说多边形有5个顶点。第一次调用area_of_polygon (c1),它切掉一个三角形,获取它的面积,然后调用area_of_polygon (c2)再次,但这次是一个 4 顶点多边形。然后area_of_polygon 对一个三角形进行循环,并调用area_of_polygon (c3) 再次,但这次是一个 3 顶点多边形。然后它不必再次调用area_of_polygon。它只是将三角形的面积返回到上一次调用 (c2)。将结果与 (c2) 中的三角形相加,并将该值返回到 (c1)。然后你就有了答案。

另外,对于它的价值,wolfram formula 可以非常清晰地写成三行:

def area_of_polygon(vertices):
    pairs = zip(vertices, vertices[1:] + vertices[0:1])
    return sum(x1 * y2 - y1 * x2 for (x1, y1), (x2, y2) in pairs) / 2

【讨论】:

  • @Droogans,我以为你会做一些更像我帖子底部的函数的事情。
  • 这是完美的解释。非常感谢。但是,当我追踪你的代码时。我发现了一些错误。我想,它在你的公式中(我的意思是你的最后一个解决方案)。但我注意到了你的提示并创建了一个解决方案。我尝试了自己,因为这是一种锻炼......
  • @guguk,我很好奇你发现了什么错误。
  • 请再看一遍 wolfram 公式和我的代码并手动跟踪公式,因为结果不同。
  • @goguk,你能发布一个产生错误结果的点列表吗?我找不到。
【解决方案2】:

您的公式的实施存在缺陷。它会提前查看您的 x、y 列表中尚未使用 (x[i]*y[i+1] - x[i+1]*y[i]) 设置的值

如果您在 try-except 块中放置一个打印语句,您将看到您只是乘以零并得到零区域:

try:
    x[i], y[i] = polygon[i]
    area += (x[i]*y[i+1] - x[i+1]*y[i])
    print x[i], y[i+1], x[i+1], y[i]
except IndexError, e:
    return area/2

#1 0 0 2
#2 0 0 5

此外,您不会将递归调用的结果返回到 areaofpolygon,因此您永远不会得到 area/2。你想要:return areaofpolygon(polygon, i+1)。并确保您实际上除以 2.0,以便获得浮点精度,因为您的点是整数。

尝试仅使用您找到的公式或另一个问题中建议的公式。

更新

这是您的代码的固定版本:

#!/usr/bin/env python

from random import randint
from shapely.geometry import Polygon

area = 0

def areaofpolygon(polygon, i):
    global area
    if i == 0: 
        area = 0

    try:
        x1, y1 = polygon[i]
        x2, y2 = polygon[i+1]
        area += (x1*y2) - (x2*y1)

    except IndexError, e:
        x1, y1 = polygon[0]
        x2, y2 = polygon[-1]
        area += (x2*y1) - (x1*y2)
        return abs(area/2.0)

    return areaofpolygon(polygon, i+1)   

def main():
    mypolygon = [(randint(0, 100), randint(0, 100)) for _ in xrange(10)]
    print mypolygon

    area = areaofpolygon(mypolygon, 0)
    assert area == Polygon(mypolygon).area

    print "Test passed."
    return 0

if __name__ == '__main__':
    main()

### Output ###
$ ./test.py 
[(29, 76), (85, 49), (27, 80), (94, 98), (19, 1), (75, 6), (55, 38), (74, 62), (0, 25), (93, 94)]
Test passed.
$ ./test.py 
[(13, 37), (98, 74), (42, 58), (32, 64), (95, 97), (34, 62), (34, 59), (21, 76), (55, 32), (76, 31)]
Test passed.
$ ./test.py 
[(38, 67), (66, 59), (16, 71), (53, 100), (64, 52), (69, 31), (45, 23), (52, 37), (27, 21), (42, 74)]
Test passed.

请注意,您不需要全局 x,y 列表。而且您还错过了使用最后一点和第一点的方程式的最后一部分。

【讨论】:

  • 我试过你的代码,但它有一些问题。我想,你误解了公式。请再看一下公式。mathworld.wolfram.com/PolygonArea.html。顺便说一句,它必须是 (x1*y2 + x2*y3 - x2*y1 - x3*y2) 我已经像这样改变了,但没有任何改变...尝试:... x3, y3 = polygon[i+2] area += (x1*y2+x2*y3-x2*y1-x3*y2) ... x3, y3 = 多边形[-2] 面积 += (x1*y2+x2*y3-x2*y1-x3*y2 ) 根据我们的多边形,解必须为 2。
  • @guguk - 我想我所缺少的只是对最后区域的 abs() 的调用。我刚刚更新了示例,对形状优美的 Polygon 类进行了测试,以确保面积计算匹配。我的代码遵循公式。也许你对 except 块中的命名感到困惑,它真正使用最后一点,然后是第一点
【解决方案3】:

使用这个公式。

https://upload.wikimedia.org/wikipedia/en/math/c/b/b/cbb6a25439b51061adb913c2a6706484.png

您在一个 for 循环中完成任务。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    • 2013-11-13
    相关资源
    最近更新 更多