Temukan luas poligon

9

Dengan panjang sisi berurutan s1, s2, s3... s_nn-gon yang tertulis dalam lingkaran, temukan luasnya. Anda dapat mengasumsikan bahwa poligon itu ada. Selain itu, poligon akan menjadi cembung dan tidak berpotongan sendiri, yang cukup untuk menjamin keunikan. Built-in yang secara khusus mengatasi tantangan ini, serta fungsi built-in yang menghitung circumradius atau circumcenter, dilarang (ini berbeda dari versi sebelumnya dari tantangan ini).

Input: Panjang sisi poligon siklik; dapat diambil sebagai parameter ke suatu fungsi, stdin, dll.

Output: Area poligon.

Jawabannya harus akurat hingga 6 desimal dan harus berjalan dalam 20 detik pada laptop yang masuk akal.

Ini golf kode sehingga kode terpendek menang!

Kasus uji khusus:

[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 test case:

soktinpk
sumber
7
Saya tahu cara mudah untuk menemukan batasnya.
mIllIbyte
1
Saya tahu cara mudah untuk menemukan jumlah sisi
Luis Mendo
Masalah ini cukup mudah mengingat circumradius, tetapi tanpanya sangat sulit.
poi830
Ini juga mudah jika ada kurang dari lima sisi, bukan itu penting dalam kode golf.
Neil

Jawaban:

5

Python 2, 191 byte

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

Menggunakan pencarian biner untuk menemukan jari-jari, kemudian menghitung luas setiap segmen berdasarkan sudut / jari-jari.

Ia menemukan jari-jari dengan pertama-tama menjumlahkan semua kecuali sudut akor terbesar, dan memeriksa sudut yang tersisa ke akor yang tersisa. Sudut-sudut itu kemudian juga digunakan untuk menghitung luas setiap segmen. Area segmen bisa negatif, jika sudutnya lebih besar dari 180 derajat.

Implementasi yang mudah dibaca:

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
sumber
Apakah ini berfungsi jika pusat berada di luar poligon? (Misalnya, segitiga dengan panjang sisi 6, 7, 12). Terkadang sqrt(4**2 - c**2/4)kebutuhan menjadi negatif, ketika sudut lebih besar dari pi.
soktinpk
@soktinpk saya memperbaiki jawaban saya.
orlp
0

Oktaf, 89 byte

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

Penjelasan

Sudut amembentang oleh segmen panjang sadalah 2*asin(s/2/r), diberikan circumradius sebuah r. Daerahnya adalah cos(a)*s/2*r.

Algoritma

  1. Set r ke sesuatu yang terlalu besar, seperti perimeter.
  2. Jika sudut terakumulasi lebih kecil dari 2pi, kurangi rdan ulangi langkah 2.
  3. Hitung area.
Rainer P.
sumber
Rata-rata, berapa banyak iterasi yang diperlukan untuk rditetapkan? (penasaran)
soktinpk
Tidak mungkin ini memiliki ketelitian yang dibutuhkan. Anda berulang kali mengalikan jari-jari dengan 0,9999 untuk menguranginya, ini membuatnya sangat mudah untuk menurunkan 6 desimal yang diperlukan.
orlp
@soktinpk sekitar 15000 untuk r*=1-1e-4dan 150000 untuk r*=1-1e-5.
Rainer P.
@RainerP. Kedua nilai itu sama.
Dana Gugatan Monica
1
@soktinpk pada umumnya bukan ide yang baik untuk membuat pengecualian untuk jawaban tertentu.
Cyoce