球面多余的三角形


15

球面多余的三角形

众所周知,任何平面三角形的角度之和等于180度。

但是,对于球形三角形,角度之和始终大于 180度。球面三角角之和与180度之间的差称为球面过剩。任务是计算具有给定顶点坐标的三角形的球面超出量。

一些背景

球形三角形是球的三个大圆所定义的球的一部分。

球面三角形的两边和角度都是用角度测量来测量的,因为每一边都可以看作是球体与某个顶角位于球体中心的平面的交集:

球形三角形说明

每三个不同的大圆定义8个三角形,但我们只需要适当的三角形考虑在内,即。角度和侧面尺寸满足的三角形

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

根据地理坐标系定义三角形的顶点很方便。在给定端圆的经度λ和纬度Φ的情况下,要计算圆弧的长度,我们可以使用以下公式:

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

,在哪里

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

或更明确地:

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)

(来源:https : //en.wikipedia.org/wiki/Haversine_formula

可用于求解球形三角形的两个基本公式是:

  • 余弦定律:

\ 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

  • 罪法:

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

(来源:https : //en.wikipedia.org/wiki/Spherical_trigonometry#Cosine_rules_and_sine_rules

给定三个边,使用余弦规则很容易计算角度:

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}

最后,定义一个三角形的球面多余部分:

E = A + B + C-\ pi

三角形的球面过盈量与其面积之间的关系有趣的是:

S = E \ cdot R ^ 2

因此,在单位球面上,三角形的多余部分等于该三角形的面积!

任务

编写一个函数或程序,在给定三角形顶点坐标的情况下,以度为单位计算三角形的球面超出量。顶点坐标是根据地理坐标系提供的。

每个顶点都应以形式传递[latitude in degrees][N|S][longitude in degrees][E|W]。当纬度为90时,可以跳过经度和E,,,是正确的顶点描述,同时或不是。W 90N90S10N100E30S20W80N55S

在测试案例中,纬度和经度始终是整数。

误差小于1度的答案将被接受(如下例所示)。因此,可以根据实际情况将结果呈现为实数或整数。

例子

输入值

90N0E
0N0E
0N90E

输出量

89.999989

输入值

90N
0N0E
0N90E

输出量

89.999989

输入值

0N0E
0N179E
90N0E

输出量

178.998863

输入值

10N10E
70N20W  
70N40E

输出量

11.969793

在所有测试案例中,经度和纬度均为整数。解析顶点坐标是任务的一部分,所以顶点必须按单一的字符串/文字传递,这不是允许通过80N20E四个参数/字符串:80N20E

这样可以确保所有顶点都是截然不同的,并且三个顶点中的两个都不构成对映点对。

计分

这是,因此最短的代码获胜。


1
前几个测试用例的正确输出是90度和179度。我知道您是说不必强制要求它们,但是要求多少个小数位精度?
Level River St

@steveverrill更新了任务。一度的精度就足够了。
pawel.boczarski 2015年

@ pawel.boczarski纬度/经度是否总是整数?
瑕疵的

@flawr是的,我更新了任务。
pawel.boczarski 2015年

Answers:


4

Matlab,288 266字节

这里的注释版本应该解释发生了什么:

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

完全打高尔夫球(可以删除换行符):

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)

3

Ruby,修订版3264255字节

主要变化:

r定义新常数= 180 / PI,并在整个函数中使用。e必须将其初始化为+ PI,因此多余的费用现在开始递减并在返回之前取反。

t[]消除:Ruby允许将分配给的数据t[]直接分配给u,v,w.

单循环i可完成两个?:任务之间的三元运算符切换。

许多其他小的变化。

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}

红宝石,版本1 283 277个字节

需要3个字符串的数组。

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}

总览

单位球面上的三角形边的长度等于描述两个点的向量之间的角度。但是我们不需要知道那个角度。知道角度的余弦就足够了,可以使用点积从笛卡尔坐标轻松获得该角度的余弦。

说明

输入的字符串将转换为数组的字符串表示形式,然后对其进行评估并存储在中t,如下所示。如果给出两个坐标,则不需要最终的零。如果仅给出纬度90,则零将被解释为经度。

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

点产品的形式如下a.b=ax*bx+ay*by+az*bz。由于向量均为单位长度,因此点积等于向量之间角度的余弦值。

为了计算它们,将循环迭代6次,两次遍历输入数据。在偶数迭代0、2、4上,将变量x,y,z设置为1以开始新的计算。在每次迭代中,这些变量都使用存储在t[0],t[1]其中的经度和纬度数据(也出于高尔夫原因也分配给u,v)将每个矢量的x,y和z分量相乘。将变量的总和写入数组n(偶数迭代中的垃圾值被奇数迭代中的正确值覆盖),以便最后n包含3个点积[a.b, c.a, b.c]

对于余弦法则,我们需要顶点之间三个夹角的余弦,但是我们也需要正弦。这些是作为获得的sqrt(1-cosine**2)。当正弦乘以在一起时,可以重新排列表达式,以便只需要调用一次即可sqrt。我们不知道正弦是正还是负的事实并不重要,因为Haversine公式始终会给出正弦。重要的物理量是点之间的距离,该距离是绝对的,因此始终为正。

对于每次迭代i=0..2,我们i-1使用其他元素i和计算相对数组元素的角度值i-2。像这样的负数组下标在Ruby中是合法的,它们只是环绕到数组的开头。

取消测试程序

在同一条线上需要三组坐标,并且在它们之间有空格。

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]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.