Площадь полигона (рекурсивно с использованием Python)
Я пытаюсь решить упражнение из книги по изучению Python. Но, думаю, я не понимаю концепцию рекурсии. Я написал несколько рекурсивных функций. Поэтому я знаю некоторые аспекты. Но у меня недостаточно опыта. И я перестал изучать программирование около года назад.
В любом случае, позвольте мне задать вам полный вопрос:Несмотря на то, что вопрос уже давал формулу, я использовал другую формулу. Потому что, я сделал некоторые исследования о площади полигона. И если вы посмотрите на здесь формула другая.Многоугольник может быть представлен списком пар (x, y), где каждая пара это кортеж: [(x1, y1), (x2, y2), (x3, y3),... (xn, yn)]. Написать рекурсивная функция для вычисления площади многоугольника. Это может быть выполняется путем "отрезания" треугольника, используя тот факт, что треугольник с углами (x1, y1), (x2, y2), (x3, y3) имеет площадь (x1y1 + x2y2 + x3y2-y1x2-y2x3-y3x1) / 2.
И было бы лучше описать мою программу шаг за шагом, чтобы объяснить, чего я хочу. Хорошо, я должен был объявить глобальные области, из-за рекурсии:
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()
А вот мой полный код без комментариев:
'''
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()
EDIT One
Прочитав ваши ответы, я понял, что произошло. что-то не так с моим кодом. Поэтому я решил поделиться последней версией своей программы, чтобы получить некоторые другие подсказки. И снова мне пришлось объявить глобальные переменные. Как я могу применить функцию (lop_triangle) из senderle
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()
Пожалуйста, помогите мне улучшить мой код, не давая полного решение.
Редактировать два
После обсуждения с сендерле я понял, где проблема, и решение сендерле лучше, чем мое, поэтому я предлагаю вам использовать его. Так или иначе, он помог мне сделать мой код правильным.И мне снова пришлось менять свою формулу.area += x[i]*y[(i+1) % 3] - x[(i+1) % 3]*y[i]
Он также добавил, что для более длинных полигонов 3 должны быть len (вершины).
Спасибо всем за потраченное время.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). И тогда вы получите свой ответ.Кроме того, как бы то ни было, формула вольфрама может быть записана с большой ясностью в трех строках:
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
Реализация вашей формулы несовершенна. Он смотрит вперед на значения в ваших списках x, y, которые еще не были установлены с помощью
(x[i]*y[i+1] - x[i+1]*y[i])
Если вы поместите оператор print в блок 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, так что вы получите float точность, так как ваши точки-это Инты.Обновить
Вот исправленная версия вашего кода:
Обратите внимание, что вам не нужны глобальные списки x, Y. И вы также пропустили последнюю часть уравнения, где вы используете последнюю точку и первую точку.#!/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.
Используйте эту формулу.
Https://upload.wikimedia.org/wikipedia/en/math/c/b/b/cbb6a25439b51061adb913c2a6706484.png
Вы выполняете свою задачу в одном цикле for.