Excès sphérique d'un triangle

15

Excès sphérique d'un triangle

Comme nous le savons tous, la somme des angles de tout triangle plan est égale à 180 degrés.

Cependant, pour un triangle sphérique, la somme des angles est toujours supérieure à 180 degrés. La différence entre la somme des angles triangulaires sphériques et 180 degrés est appelée excès sphérique . La tâche consiste à calculer l'excès sphérique d'un triangle avec des coordonnées de sommet données.

Quelques antécédents

Un triangle sphérique est une partie de la sphère définie par trois grands cercles de la sphère.

Les deux côtés et les angles du triangle sphérique sont mesurés en terme de mesure d'angle, car chaque côté peut être considéré comme une intersection de la sphère et un angle plan avec un sommet au centre de la sphère:

Triangle sphérique expliqué

Chacun des trois grands cercles distincts définit 8 triangles, mais nous ne prenons en considération que les triangles appropriés , c'est-à - dire. triangles dont l'angle et les mesures latérales satisfont

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

Il est pratique de définir les sommets d'un triangle en termes de système de coordonnées géographiques. Pour calculer la longueur d'un arc de sphère étant donné la longitude λ et la latitude Φ de ses extrémités, nous pouvons utiliser la formule:

d = 2 r \ arcsin \ left (\ sqrt {\ operatorname {haversin} (\ phi_2 - \ phi_1) + \ cos (\ phi_1) \ cos (\ phi_2) \ operatorname {haversin} (\ lambda_2- \ lambda_1)} \droite)

, où

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

ou plus explicitement:

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

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

Les deux formules de base qui peuvent être utilisées pour résoudre un triangle sphérique sont:

  • la loi des cosinus:

\ 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 + \ sin a \ sin b \ cos C

  • la loi des sinus:

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

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

Étant donné trois côtés, il est facile de calculer les angles à l'aide de la règle du cosinus:

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

Enfin, l'excès sphérique d'un triangle est défini:

E = A + B + C - \ pi

Ce qui est intéressant à propos de la relation entre l'excès sphérique d'un triangle et sa surface:

S = E \ cdot R ^ 2

Donc, sur une sphère unitaire, l'excès d'un triangle est égal à l'aire de ce triangle!

La tâche

Écrivez une fonction ou un programme qui calculera l'excès sphérique d'un triangle en degrés étant donné les coordonnées des sommets du triangle. Les coordonnées des sommets sont fournies en termes de système de coordonnées géographiques.

Chaque sommet doit être transmis sous forme [latitude in degrees][N|S][longitude in degrees][E|W]. La longitude et Eou Wpeuvent être ignorés lorsque la latitude est de 90, c.-à-d. 90N, 90S, 10N100E, 30S20WSont des descriptions de sommet appropriées, alors que 80Nou 55Sne sont pas.

Les latitudes et longitudes sont toujours entières dans les cas de test.

Les réponses avec une erreur inférieure à un degré seront acceptées (comme dans les exemples ci-dessous). Le résultat peut donc être rendu à la fois réel ou entier, à votre convenance.

Exemples

Contribution

90N0E
0N0E
0N90E

Production

89.999989

Contribution

90N
0N0E
0N90E

Production

89.999989

Contribution

0N0E
0N179E
90N0E

Production

178.998863

Contribution

10N10E
70N20W  
70N40E

Production

11.969793

Dans tous les cas de test, la longitude et la latitude sont des nombres entiers. Les coordonnées de l' analyse sommet est la partie de la tâche, si un sommet doit être passé comme seule chaîne / littérale, il ne laisse passer 80N20Eque quatre paramètres / chaînes: 80, N, 20, E.

Cela garantit que les sommets sont tous distincts et que ni l'un ni l'autre des trois sommets ne forment une paire de points antipodaux.

Notation

C'est le , donc le code le plus court l'emporte.

pawel.boczarski
la source
1
Les sorties correctes pour les premiers cas de test sont de 90 degrés et 179 degrés. Je comprends que vous dites qu'ils n'ont pas besoin d'être précis, mais combien de décimales d'exactitude sont nécessaires?
Level River St
@steveverrill Mise à jour de la tâche. La précision d'un degré suffit.
pawel.boczarski
@ pawel.boczarski Les latitudes / longitudes sont-elles toujours des entiers?
flawr
@flawr Oui, j'ai mis à jour la tâche.
pawel.boczarski

Réponses:

4

Matlab, 288 266 octets

Voici la version commentée qui devrait expliquer ce qui se passe:

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

Entièrement golfé (les sauts de ligne peuvent être supprimés):

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
la source
3

Ruby, Rev 3 264 255 octets

Changements majeurs:

Nouvelle constante r= 180 / PI définie et utilisée tout au long de la fonction. ea dû être initialisé à + PI, donc l'excès compte maintenant vers le bas et est annulé avant de revenir.

t[]éliminé: Ruby permet d'affecter t[]directement les données qui ont été affectées àu,v,w.

iBoucle unique pour faire le travail de deux, l' ?:opérateur ternaire bascule entre les tâches.

Beaucoup d'autres changements mineurs.

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 octets

Nécessite un tableau de 3 chaînes.

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}

Aperçu

Les longueurs des côtés du triangle sur la sphère unitaire sont égales aux angles entre les vecteurs décrivant les deux points. Mais nous n'avons pas besoin de connaître cet angle. Il suffit de connaître le cosinus de l'angle, qui est facilement obtenu à partir des coordonnées cartésiennes à l'aide du produit scalaire.

Explication

Les chaînes d'entrée sont converties en une représentation sous forme de chaîne d'un tableau, qui est ensuite évaluée et stockée dans t, comme ci-dessous. Le zéro final n'est pas nécessaire si deux coordonnées sont données. Si seule la latitude 90 est donnée, le zéro est interprété comme la longitude.

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

Les produits Dot sont de la forme a.b=ax*bx+ay*by+az*bz. Comme les vecteurs sont tous de longueur unitaire, le produit scalaire est égal au cosinus de l'angle entre les vecteurs.

Afin de les calculer, une boucle est itérée 6 fois en passant deux fois dans les données d'entrée. Sur des itérations égales 0,2,4, les variables x,y,zsont mises à 1 pour commencer un nouveau calcul. À chaque itération, ces variables sont multipliées par les composantes x, y et z de chaque vecteur, en utilisant les données de longitude et de latitude stockées dans t[0],t[1](qui, pour des raisons de golf, sont également affectées u,v). La somme des variables est écrite dans un tableau n(les valeurs de poubelle sur les itérations paires étant écrasées par les valeurs correctes sur les itérations impaires) afin qu'à la fin ncontienne les 3 produits scalaires [a.b, c.a, b.c].

Pour la règle du cosinus, nous avons besoin des cosinus des trois angles inclus entre les sommets, mais nous avons également besoin des sinus. Ceux-ci sont obtenus comme sqrt(1-cosine**2). Lorsque les sinus sont multipliés, l'expression peut être réorganisée de sorte qu'un seul appel à sqrtsoit requis. Le fait que nous ne sachions pas si le sinus était positif ou négatif n'a pas d'importance, car la formule haversine donne toujours le sinus positif de toute façon. La grandeur physique importante est la distance entre les points, qui est absolue et donc toujours positive.

Pour chaque itération i=0..2, nous calculons la valeur de l'élément de tableau opposé à l'angle en i-1utilisant les autres éléments iet i-2. Les indices de tableau négatifs comme celui-ci sont légaux dans Ruby, ils se terminent juste au début du tableau.

Non testé dans le programme de test

Nécessite trois ensembles de coordonnées sur la même ligne, avec des espaces entre eux.

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
la source