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


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.