Encontre a área de um polígono

9

Dado o comprimento lateral consecutivo s1, s2, s3... s_nde um n-gon inscrito em um círculo, encontre sua área. Você pode assumir que o polígono existe. Além disso, o polígono será convexo e não se entrecruzará, o que é suficiente para garantir a exclusividade. Os internos que resolvem especificamente esse desafio, bem como as funções internas que calculam o perímetro ou o perímetro, são banidos (isso é diferente da versão anterior deste desafio).

Entrada: os comprimentos laterais do polígono cíclico; podem ser tomados como parâmetros para uma função, stdin, etc.

Saída: a área do polígono.

A resposta deve ter precisão de 6 casas decimais e deve ser executada em 20 segundos em um laptop razoável.

Este é o código de golfe, então o código mais curto vence!

Casos de teste específicos:

[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

Gerador de caso de teste:

soktinpk
fonte
7
Conheço uma maneira fácil de encontrar seu perímetro.
MIllIbyte 16/04
11
Eu sei uma maneira fácil de encontrar o número de lados
Luis Mendo
Esse problema é bastante fácil, dado o circumradius, mas sem ele é incrivelmente difícil.
poi830
Também é fácil se houver menos de cinco lados, não que isso importe no código de golfe.
194 Neil

Respostas:

5

Python 2, 191 bytes

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

Utiliza uma pesquisa binária para encontrar o raio e calcula a área de cada segmento pelo ângulo / raio.

Ele encontra o raio somando primeiro todos os ângulos, exceto o maior ângulo da corda, e verificando o ângulo restante da corda restante. Esses ângulos também são usados ​​para calcular a área de cada segmento. A área de um segmento pode ser negativa, se o ângulo for maior que 180 graus.

Implementação legível:

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
fonte
Isso funciona se o centro estiver fora do polígono? (Por exemplo, um triângulo com comprimentos laterais 6, 7, 12). Às vezes, sqrt(4**2 - c**2/4)precisa ser negativo, quando o ângulo é maior que pi.
soktinpk
@soktinpk Corrigi minha resposta.
orlp
0

Oitava, 89 bytes

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

Explicação

O ângulo amedido por um segmento de comprimento sé 2*asin(s/2/r), dado um perímetro r. Sua área é cos(a)*s/2*r.

Algoritmo

  1. Defina rpara algo muito grande, como o perímetro.
  2. Se o ângulo acumulado for menor que 2pi, reduza re repita a etapa 2.
  3. Calcule a área.
Rainer P.
fonte
Em média, quantas iterações são necessárias para rserem definidas? (por curiosidade)
soktinpk
Não há como ter a precisão necessária. Você multiplica repetidamente o raio por 0,9999 para reduzi-lo, facilitando muito a redução das 6 decimais de precisão necessárias.
orlp
@soktinpk por volta de 15000 r*=1-1e-4e 150000 para r*=1-1e-5.
Rainer P.
@RainerP. Esses dois valores são os mesmos.
Fund Monica's Lawsuit
11
@soktinpk geralmente não é uma boa idéia abrir uma exceção para uma resposta específica.
Cyoce