Площадь многоугольника (рекурсивно с использованием Python) - PullRequest
6 голосов
/ 25 февраля 2012

Я пытаюсь выполнить упражнение из книги «Изучение 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()

РЕДАКТИРОВАТЬ Один

Прочитав ваши ответы, я понял, что случилось с моим кодом. Поэтому я решил поделиться последней версией моей программы, чтобы получить другие подсказки. Опять же, мне пришлось объявить глобальные переменные. Как я могу применить (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()

Пожалуйста, помогите мне улучшить мой код без полного решения.

РЕДАКТИРОВАТЬ Два

После обсуждения с senderle я понял, в чем проблема, и решение senderle лучше моего, поэтому я предлагаю вам воспользоваться им. В любом случае, он помог мне исправить мой код. И мне пришлось снова изменить формулу.

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

Он также добавлен для более длинных полигонов 3 должен быть len (вершины). Спасибо всем за уделенное время.

Ответы [ 3 ]

5 голосов
/ 25 февраля 2012

Реализация вашей формулы несовершенна.Он смотрит вперед на значения в ваших списках x, y, которые еще не были установлены с (x[i]*y[i+1] - x[i+1]*y[i])

Если вы поместите инструкцию print в свой блок try-Кроме того, вы увидите, что вы просто умножаете на нольи получение нулевой области:

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.И вы также пропустили последнюю часть уравнения, где вы используете последнюю точку и первую точку.

5 голосов
/ 26 февраля 2012

Суть рекурсии заключается в следующем:

  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).И тогда у вас есть свой ответ.

Кроме того, формула вольфрама может стоить с большой ясностью в три строки:

1 голос
/ 25 февраля 2012

Используйте эту формулу.

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

Вы выполняете свою задачу за один цикл.

...