Znajdź obszar wielokąta

9

Biorąc pod uwagę kolejne długości boku s1, s2, s3... s_nn-gonu wpisanego w okrąg, znajdź jego obszar. Możesz założyć, że wielokąt istnieje. Ponadto wielokąt będzie wypukły i nie będzie się przecinał, co wystarczy, aby zagwarantować wyjątkowość. Wbudowane rozwiązania, które konkretnie rozwiązują to wyzwanie, a także wbudowane funkcje, które obliczają obwód lub okrążenie, są zakazane (różni się to od poprzedniej wersji tego wyzwania).

Dane wejściowe: długości boków wielokąta cyklicznego; mogą być brane jako parametry funkcji, standardu itp.

Dane wyjściowe: obszar wielokąta.

Odpowiedź powinna być dokładna do 6 miejsc po przecinku i musi zostać uruchomiona w ciągu 20 sekund na rozsądnym laptopie.

To jest golf golfowy, więc wygrywa najkrótszy kod!

Konkretne przypadki testowe:

[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

Generator przypadków testowych:

soktinpk
źródło
7
Znam prosty sposób na znalezienie jego obwodu.
mIllIbyte
1
Znam prosty sposób na znalezienie liczby stron
Luis Mendo
Ten problem jest dość łatwy, biorąc pod uwagę obwód, ale bez niego jest niezwykle trudny.
poi830 16.04.16
Jest to również łatwe, jeśli jest mniej niż pięć stron, nie ma to znaczenia w golfie kodowym.
Neil

Odpowiedzi:

5

Python 2, 191 bajtów

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

Korzysta z wyszukiwania binarnego, aby znaleźć promień, a następnie oblicza powierzchnię każdego segmentu na podstawie kąta / promienia.

Wyszukuje promień, najpierw sumując wszystko oprócz największego kąta cięciwy i sprawdzając pozostały kąt do pozostałego cięciwy. Kąty te są następnie wykorzystywane również do obliczania powierzchni każdego segmentu. Pole segmentu może być ujemne, jeśli jego kąt jest większy niż 180 stopni.

Czytelne wdrożenie:

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])
orlp
źródło
Czy to działa, jeśli środek znajduje się poza wielokątem? (Na przykład trójkąt o długości boku 6, 7, 12). Czasami sqrt(4**2 - c**2/4)musi być ujemna, gdy kąt jest większy niż pi.
soktinpk
@soktinpk Naprawiłem swoją odpowiedź.
orlp
0

Oktawa, 89 bajtów

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))

Wyjaśnienie

Kąt arozpięty o odcinek długości sjest 2*asin(s/2/r)podany wokół obwodu r. Jego powierzchnia to cos(a)*s/2*r.

Algorytm

  1. Ustaw rna coś zbyt dużego, na przykład na obwód.
  2. Jeśli łączny kąt jest mniejszy niż 2pi, zmniejsz ri powtórz krok 2.
  3. Oblicz obszar.
Rainer P.
źródło
Ile średnio rtrzeba ustawić iteracji ? (z ciekawości)
soktinpk
Nie ma mowy, żeby miała wymaganą precyzję. Kilkukrotnie pomnożono promień przez 0,9999, aby go zmniejszyć, dzięki czemu bardzo łatwo jest osiągnąć wymaganą dokładność 6 miejsc po przecinku.
orlp
@soktinpk około 15000 for r*=1-1e-4i 150000 for r*=1-1e-5.
Rainer P.
@RainerP. Te dwie wartości są takie same.
Pozew Fund Moniki w dniu
1
@soktinpk generalnie nie jest dobrym pomysłem zrobić wyjątek dla konkretnej odpowiedzi.
Cyoce