Найти площадь многоугольника


9

Учитывая последовательные длины сторон s1, s2, s3... s_nn-гона, вписанного в круг, найдите его площадь. Вы можете предположить, что полигон существует. Кроме того, многоугольник будет выпуклым, а не самопересекающимся, чего достаточно, чтобы гарантировать уникальность. Встроенные модули, которые специально решают эту задачу, а также встроенные функции, которые вычисляют круговой луч или круговой центр, запрещены (это отличается от предыдущей версии этой задачи).

Вход: длина сторон циклического многоугольника; могут быть приняты в качестве параметров функции, стандартного ввода и т. д.

Вывод: площадь многоугольника.

Ответ должен быть точным с точностью до 6 знаков после запятой и должен выполняться в течение 20 секунд на разумном ноутбуке.

Это кодовый гольф, поэтому выигрывает самый короткий код!

Конкретные тестовые случаи:

[3, 4, 5] --> 6
[3, 4, 6] --> 5.332682251925386
[3, 4, 6, 7] --> 22.44994432064365
[5, 5, 5, 5] --> 25
[6, 6, 6, 6, 6] --> 61.93718642120281
[6.974973020933265, 2.2393294197257387, 5.158285083300981, 1.4845682771595603, 3.5957940796134173] --> 21.958390804292847
[7.353566082457831, 12.271766915518073, 8.453884922273897, 9.879017670784675, 9.493366404245332, 1.2050010402321778] --> 162.27641678140589

Генератор тестовых случаев:


7
Я знаю простой способ найти его периметр.
МИЛЛИБАЙТ

1
Я знаю простой способ узнать количество сторон
Луис Мендо

Эта проблема довольно проста, учитывая круговорот, но без нее это невероятно сложно.
poi830

Это также легко, если есть меньше пяти сторон, не то, чтобы это имело значение для гольф-кода.
Нил

Ответы:


5

Python 2, 191 байт

from math import*
C=sorted(input());l,h=C[-1]/2,sum(C)
while h-l>1e-9:m=l+h;a=[asin(c/m)for c in C[:-1]];f=pi-sum(a);l,h=[l,m/2,h][m*sin(f)<C[-1]:][:2]
print sum(l*l*sin(2*t)for t in a+[f])/2

Использует двоичный поиск, чтобы найти радиус, а затем вычисляет площадь каждого сегмента по углу / радиусу.

Он находит радиус, сначала суммируя все, кроме наибольшего угла хорды, и проверяя оставшийся угол до оставшейся хорды. Эти углы затем также используются для вычисления площади каждого сегмента. Площадь сегмента может быть отрицательной, если ее угол больше 180 градусов.

Читаемая реализация:

import math

def segment_angles(line_segments, r):
    return [2*math.asin(c/(2*r)) for c in line_segments]

def cyclic_ngon_area(line_segments):
    line_segments = list(sorted(line_segments))
    lo, hi = max(line_segments) / 2, sum(line_segments)
    while hi - lo > 1e-9:
        mid = (lo + hi) / 2
        angles = segment_angles(line_segments[:-1], mid)
        angles.append(2*math.pi - sum(angles))
        if 2 * mid * math.sin(angles[-1]/2) < line_segments[-1]:
            lo = mid
        else:
            hi = mid
    return sum([lo*lo * math.sin(a) / 2 for a in angles])

Это работает, если центр находится вне многоугольника? (Например, треугольник с длинами сторон 6, 7, 12). Иногда sqrt(4**2 - c**2/4)необходимо быть отрицательным, когда угол больше, чем pi.
soktinpk

@soktinpk Я исправил свой ответ.
orlp

0

Октава, 89 байт

r=sum(s=input(''));while sum(a=asin(s/2/r))<pi r*=1-1e-4;b=a;end;disp(sum(cos(b).*s/2*r))

объяснение

Угол , aнатянутое на отрезке длины sявляется 2*asin(s/2/r), дается описанной окружности r. Его площадь есть cos(a)*s/2*r.

Алгоритм

  1. Установите rчто-то слишком большое, например, периметр.
  2. Если накопленный угол меньше чем 2pi, уменьшите rи повторите шаг 2.
  3. Рассчитайте площадь.

В среднем, сколько итераций требуется для rустановки? (из любопытства)
soktinpk

Это никак не может обеспечить требуемую точность. Вы несколько раз умножаете радиус на 0,9999, чтобы уменьшить его, что позволяет очень легко получить требуемые 6 десятичных знаков точности.
orlp

@soktinpk около 15000 для r*=1-1e-4и 150000 для r*=1-1e-5.
Райнер П.

@RainerP. Эти два значения одинаковы.
Фонд Моника иск

1
@soktinpk, как правило, не стоит делать исключение для конкретного ответа.
Cyoce
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.