Excesso esférico de um triângulo

15

Excesso esférico de um triângulo

Como todos sabemos, a soma dos ângulos de qualquer triângulo plano é igual a 180 graus.

No entanto, para um triângulo esférico, a soma dos ângulos é sempre maior que 180 graus. A diferença entre a soma dos ângulos do triângulo esférico e 180 graus é denominada excesso esférico . A tarefa é calcular o excesso esférico de um triângulo com determinadas coordenadas de vértice.

Alguma experiência

Um triângulo esférico é uma parte da esfera definida por três grandes círculos da esfera.

Ambos os lados e ângulos do triângulo esférico são medidos no termo medida de ângulo, porque cada lado pode ser considerado como uma interseção da esfera e algum ângulo plano com vértice no centro da esfera:

Triângulo esférico explicado

Cada três grandes círculos distintos definem 8 triângulos, mas apenas levamos em consideração os triângulos apropriados . triângulos cujas medidas angulares e laterais satisfazem

0 <a, b, c, A, B, C <\ pi

É conveniente definir vértices de um triângulo em termos de sistema de coordenadas geográficas. Para calcular o comprimento de um arco de esfera, dada a longitude λ e latitude Φ de suas extremidades, podemos usar a fórmula:

d = 2 r \ arcsin \ left (\ sqrt {\ nome do operador {haversin} (\ phi_2 - \ phi_1) + \ cos (\ phi_1) \ cos (\ phi_2) \ nome do operador {haversin} (\ lambda_2- \ lambda_1)} \certo)

, Onde

\ nome do operador {haversin} (\ theta) = \ sin ^ 2 \ left (\ frac {\ theta} {2} \ right) = \ frac {1- \ cos (\ theta)} {2}

ou mais explicitamente:

d = 2 r \ arcsin \ left (\ sqrt {\ sin ^ 2 \ left (\ frac {\ phi_2 - \ phi_1} {2} \ right) + \ cos (\ phi_1) \ cos (\ phi_2) \ sin ^ 2 \ left (\ frac {\ lambda_2 - \ lambda_1} {2} \ right)} \ right)

(fonte: https://en.wikipedia.org/wiki/Haversine_formula )

As duas fórmulas básicas que podem ser usadas para resolver um triângulo esférico são:

  • a lei dos cossenos:

\ cos a = \ cos b \ cos c + \ sin b \ sin c \ cos A, \ cos b = \ cos c \ cos a + \ sin c \ sin a \ cos B, \ cos c = \ cos a \ cos b + \ sen a \ sen b \ cos C

  • a lei dos senos:

\ frac {\ sin A} {\ sin a} = \ frac {\ sin B} {\ sin b} = \ frac {\ sin C} {\ sin c}

(fonte: https://en.wikipedia.org/wiki/Spherical_trigonometry#Cosine_rules_and_sine_rules )

Dado três lados, é fácil calcular os ângulos usando a regra do cosseno:

A = \ arccos \ frac {\ cos a - \ cos b \ cos c} {\ sin b \ sin c}, B = \ arccos \ frac {\ cos b - \ cos c \ cos a} {\ sen c \ sin a}, C = \ arccos \ frac {\ cos c - \ cos a \ cos b} {\ sin a \ sin b}

Finalmente, o excesso esférico de um triângulo é definido:

E = A + B + C - \ pi

O que é interessante sobre a relação entre o excesso esférico de um triângulo e sua área:

S = E \ cdot R ^ 2

Assim, em uma esfera unitária, o excesso de um triângulo é igual à área desse triângulo!

A tarefa

Escreva uma função ou um programa que calcule o excesso esférico de um triângulo em graus, dadas as coordenadas dos vértices do triângulo. As coordenadas do vértice são fornecidas em termos de sistema de coordenadas geográficas.

Cada vértice deve ser passado em forma [latitude in degrees][N|S][longitude in degrees][E|W]. A longitude e Eou Wpode ser ignorada quando a latitude é 90, ou seja. 90N, 90S, 10N100E, 30S20WSão descrições de vértice adequadas, enquanto 80Nou 55Snão são.

As latitudes e longitudes são sempre inteiras nos casos de teste.

As respostas com erro menor que um grau serão aceitas (como nos exemplos abaixo). O resultado pode ser renderizado como real ou inteiro, portanto, conforme sua conveniência.

Exemplos

Entrada

90N0E
0N0E
0N90E

Resultado

89.999989

Entrada

90N
0N0E
0N90E

Resultado

89.999989

Entrada

0N0E
0N179E
90N0E

Resultado

178.998863

Entrada

10N10E
70N20W  
70N40E

Resultado

11.969793

Em todos os casos de teste, longitude e latitude são números inteiros. Analisando as coordenadas dos vértices é a parte da tarefa, para um vértice deve ser passado como uma string única / literal, não é permitida a passagem 80N20Ede quatro parâmetros / strings: 80, N,20 , E.

Isso garante que os vértices sejam todos distintos e nenhum dos três vértices forma um par de pontos antipodais.

Pontuação

Isso é , então o código mais curto vence.

pawel.boczarski
fonte
1
As saídas corretas para os primeiros casos de teste são 90 graus e 179 graus. Entendo que você está dizendo que eles não precisam estar no local, mas quantas casas decimais de precisão são necessárias?
Level River St
@steveverrill Atualizou a tarefa. Precisão de um grau é suficiente.
pawel.boczarski
@ pawel.boczarski As latitudes / longitudes são sempre inteiras?
flawr
@ flawr Sim, atualizei a tarefa.
pawel.boczarski

Respostas:

4

Matlab, 288 266 bytes

Aqui a versão comentada que deve explicar o que está acontecendo:

                                  %parsing the input
for k=1:3;
    s=input('','s');              %request input
    if sum(s>57)<2;               %if we have only one letter, add arbitrary second coordinate
        s=[s,'0E'];
    end;
    S=1-2*(s(s>57)>80);           %calculate the sign of the coordinates
    s(s>57)=44;                   %replace letters with comma
    L(k,:)=eval(['[',s,']']).*S;  %evaluates string as list and multiply with signs
end;
i=[2,3,1];
                                  %calculate the angular distance between each pair of points
a=arrayfun(@distance,L(:,1),L(:,2),L(i,1),L(i,2))*pi/180;
                                  %evaluate the spherical excess
f=@(a,b,c)sum(acos((cos(a)-cos(b).*cos(c))./(sin(b).*sin(c))))-pi;
disp(f(a,a(i),a([3,1,2]))*180/pi)

Totalmente jogado (quebras de linha podem ser removidas):

for k=1:3;s=input('','s');if sum(s>57)<2;s=[s,'0E'];end;
s(s>57)=44;L(k,:)=eval([91,s,93]).*(1-2*(s(s<48)>80));end;
i=[2,3,1];p=pi/180;a=arrayfun(@distance,L(:,1),L(:,2),L(i,1),L(i,2))*p;
b=a(i);disp((sum(acos((cos(a([3,1,2]))-cos(b).*cos(a))./(sin(b).*sin(a))))-pi)/p)
flawr
fonte
3

Ruby, Rev 3 264 255 bytes

Principais mudanças:

Nova constante r= 180 / PI definida e usada em toda a função. eteve que ser inicializado para + PI, então o excesso agora conta para baixo e é negado antes de retornar.

t[]eliminado: Ruby permite que os dados atribuídos t[]sejam diretamente atribuídos au,v,w.

iLoop único para fazer o trabalho de dois ?:operadores ternários alterna entre tarefas.

Muitas outras pequenas mudanças.

include Math
->s{r=180/e=PI
x=y=z=n=[]
9.times{|i|i<6?(u,v,w=eval(?[+s[i%3].gsub(/[NE]/,"/r,").gsub(/[SW]/,"/-r,")+"0]")
i%2<1&&x=y=z=1
n[i/2]=(z*=sin(u))+(y*=cos(v)*w=cos(u))+x*=w*sin(v)):e-=acos((n[i-7]-(c=n[i-6])*d=n[i-8])/sqrt((1-c*c)*(1-d*d)))}
-e*r}

Ruby, Rev 1.283 277 bytes

Requer uma matriz de 3 strings.

include Math 
->s{x=y=z=n=[]
6.times{|i|t=eval(?[+s[i%3].gsub(/[NE]/,k="*PI/180,").gsub(/[SW]/,"*-1"+k)+"0]")
i%2<1&&x=y=z=1
n[i/2]=(z*=sin(u=t[0]))+(y*=cos(u)*cos(v=t[1]))+(x*=cos(u)*sin(v))}
e=-PI
3.times{|i|e+=acos((n[i-1]-n[i]*d=n[i-2])/sqrt((1-n[i]**2)*(1-d**2)))}
e/PI*180}

Visão geral

Os comprimentos dos lados do triângulo na esfera unitária são iguais aos ângulos entre os vetores que descrevem os dois pontos. Mas não precisamos conhecer esse ângulo. Basta conhecer o cosseno do ângulo, que é facilmente obtido a partir de coordenadas cartesianas usando o Produto Dot.

Explicação

As cadeias de entrada são convertidas em uma representação de cadeia de uma matriz, que é então avaliada e armazenada t, como abaixo. O zero final não é necessário se duas coordenadas forem fornecidas. Se apenas a latitude 90 for fornecida, o zero será interpretado como a longitude.

Example:  70N20W --> [70*PI/180,20*-1*PI/180,0]

Os produtos Dot são da forma a.b=ax*bx+ay*by+az*bz. Como os vetores têm comprimento unitário, o produto escalar é igual ao cosseno do ângulo entre os vetores.

Para calculá-los, um loop é repetido 6 vezes, passando duas vezes pelos dados de entrada. Mesmo nas iterações 0,2,4, as variáveis x,y,zsão definidas como 1 para iniciar um novo cálculo. Em cada iteração, essas variáveis ​​são multiplicadas pelos componentes x, ye z de cada vetor, usando os dados de longitude e latitude armazenados em t[0],t[1](aos quais também é atribuído por razões de golfe u,v). A soma das variáveis ​​é gravada na matriz n(os valores de lixo nas iterações pares são substituídos pelos valores corretos nas iterações ímpares) para que no final ncontenha os produtos de 3 pontos [a.b, c.a, b.c].

Para a regra do cosseno, precisamos dos cossenos dos três ângulos incluídos entre os vértices, mas também precisamos dos senos. Estes são obtidos como sqrt(1-cosine**2). À medida que os senos são multiplicados, a expressão pode ser reorganizada, de modo que apenas uma chamada parasqrt seja necessária. O fato de não sabermos se o seno foi positivo ou negativo não importa, pois a fórmula do haversine sempre fornece o seno positivo de qualquer maneira. A quantidade física importante é a distância entre os pontos, que é absoluta e, portanto, sempre positiva.

Para cada iteração i=0..2, calculamos o valor do ângulo oposto ao elemento da matriz i-1usando os outros elementos ie i-2. Subscritos de matriz negativa como este são legais em Ruby, eles apenas envolvem o início da matriz.

Ungolfed in program program

Requer três conjuntos de coordenadas na mesma linha, com espaços entre eles.

include Math
g=->s{
  n=[]         #array for dot products
  x=y=z=1      #it's required to use these variables once before the loop, for some bizarre reason
  6.times{|i|
    t=eval(?[+s[i%3].gsub(/[NE]/,k="*PI/180,").gsub(/[SW]/,"*-1"+k)+"0]")
    i%2<1&&x=y=z=1
    n[i/2]=(z*=sin(u=t[0]))+(y*=cos(u)*cos(v=t[1]))+(x*=cos(u)*sin(v))
  }

  e=-PI        #set e to -PI and begin accumulating angles
  3.times{|i|
    e+=acos((n[i-1]-n[i]*n[i-2])/sqrt((1-n[i]**2)*(1-n[i-2]**2)))
  }

e/PI*180}      #return value

puts g[gets.split]
Level River St
fonte