ทรงกลมส่วนเกินของสามเหลี่ยม


15

ทรงกลมส่วนเกินของสามเหลี่ยม

อย่างที่เราทราบกันว่าผลรวมของมุมของสามเหลี่ยมภาพถ่ายใด ๆ เท่ากับ 180 องศา

อย่างไรก็ตามสำหรับรูปสามเหลี่ยมทรงกลมผลรวมของมุมอยู่เสมอมากขึ้นกว่า 180 องศา ความแตกต่างระหว่างผลรวมของมุมสามเหลี่ยมทรงกลมกับ 180 องศาเรียกว่าส่วนเกินทรงกลมเกินกว่าทรงกลมงานคือการคำนวณส่วนเกินทรงกลมของรูปสามเหลี่ยมที่มีพิกัดจุดสุดยอดที่กำหนด

พื้นหลังบางส่วน

สามเหลี่ยมทรงกลมเป็นส่วนหนึ่งของทรงกลมที่กำหนดโดยวงกลมใหญ่สามวง

ทั้งสองด้านและมุมของทรงกลมรูปสามเหลี่ยมนั้นถูกวัดในเทอมของการวัดมุมเนื่องจากแต่ละด้านถือได้ว่าเป็นจุดตัดของทรงกลมและมุมระนาบบางส่วนที่มีจุดยอดที่จุดศูนย์กลางของทรงกลม:

สามเหลี่ยมทรงกลมอธิบาย

วงกลมใหญ่สามวงที่แตกต่างกันกำหนดรูปสามเหลี่ยม 8 รูป แต่เรานำรูปสามเหลี่ยมที่เหมาะสมมาพิจารณาเท่านั้นเช่น สามเหลี่ยมที่มุมและด้านข้างของมาตรการตอบสนอง

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

สะดวกในการกำหนดจุดยอดของรูปสามเหลี่ยมในแง่ของระบบพิกัดทางภูมิศาสตร์ ในการคำนวณความยาวของส่วนโค้งของทรงกลมที่กำหนดลองจิจูด latitude และละติจูดΦของจุดสิ้นสุดของมันเราสามารถใช้สูตร:

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

ที่ไหน

\ operatorname {haversin} (\ theta) = \ บาป ^ 2 \ left (\ frac {\ theta} {2} \ ขวา) = \ 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 \ บาป 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]แต่ละจุดสุดยอดควรจะผ่านในรูปแบบ ลองจิจูดและEหรือWสามารถข้ามได้เมื่อละติจูดเป็น 90 เช่น 90N, 90S, 10N100E, 30S20Wเป็นคำอธิบายจุดสุดยอดที่เหมาะสมในขณะที่80Nหรือ55Sไม่ได้

ละติจูดและลองจิจูดเป็นจำนวนเต็มเสมอในกรณีทดสอบ

คำตอบที่มีข้อผิดพลาดน้อยกว่าหนึ่งองศาจะได้รับการยอมรับ (ตามตัวอย่างด้านล่าง) ผลลัพธ์สามารถแสดงผลเป็นทั้งจริงหรือเลขจำนวนเต็มได้ตามความสะดวกของคุณ

ตัวอย่าง

อินพุต

90N0E
0N0E
0N90E

เอาท์พุต

89.999989

อินพุต

90N
0N0E
0N90E

เอาท์พุต

89.999989

อินพุต

0N0E
0N179E
90N0E

เอาท์พุต

178.998863

อินพุต

10N10E
70N20W  
70N40E

เอาท์พุต

11.969793

ในทุกกรณีการทดสอบลองจิจูดและละติจูดเป็นตัวเลขจำนวนเต็ม แยกพิกัดจุดสุดยอดเป็นส่วนหนึ่งของงานเพื่อจุดสุดยอดจะต้องผ่านการเป็นสายเดียว / ตัวอักษรก็ไม่ได้รับอนุญาตให้ผ่าน80N20Eเป็นพารามิเตอร์สี่ / สตริง: 80, N, 20,E ,

สิ่งนี้รับประกันได้ว่าจุดยอดทั้งหมดนั้นแตกต่างกันและไม่มีจุดยอดสองจุดจากสามจุดที่ทำให้เกิดจุดคู่ขนาน

เกณฑ์การให้คะแนน

นี่คือดังนั้นรหัสที่สั้นที่สุดชนะ


1
ผลลัพธ์ที่ถูกต้องสำหรับกรณีทดสอบสองสามครั้งแรกคือ 90 องศาและ 179 องศา ฉันได้รับว่าคุณกำลังบอกว่าพวกเขาไม่จำเป็นต้องมองเห็น แต่ต้องมีความแม่นยำทศนิยมกี่ตำแหน่ง?
เลเวลริเวอร์

@steveverrill อัพเดทงาน ความแม่นยำระดับหนึ่งก็เพียงพอแล้ว
pawel.boczarski

@ pawel.boczarski ละติจูด / ลองจิจูดเป็นจำนวนเต็มเสมอหรือไม่
ข้อบกพร่อง

@flawr ใช่ฉันได้อัปเดตงานแล้ว
pawel.boczarski

คำตอบ:


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)

golfed อย่างเต็มที่ (สามารถลบ linebreaks ได้):

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, Rev 3 264 255 ไบต์

การเปลี่ยนแปลงที่สำคัญ:

ใหม่คงที่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}

Ruby, Rev 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}

ภาพรวม

ความยาวของด้านสามเหลี่ยมบนทรงกลมของหน่วยมีค่าเท่ากับมุมระหว่างเวกเตอร์ที่อธิบายจุดสองจุด แต่เราไม่จำเป็นต้องรู้มุมนั้น มันพอเพียงที่จะรู้โคไซน์ของมุมซึ่งสามารถหาได้ง่ายจากพิกัดคาร์ทีเซียนโดยใช้ผลิตภัณฑ์ Dot

คำอธิบาย

สตริงการป้อนข้อมูลจะถูกแปลงเป็นตัวแทนสตริงของอาร์เรย์ซึ่งจะถูกประเมินและเก็บไว้ในtด้านล่าง ไม่จำเป็นต้องมีศูนย์สุดท้ายหากได้รับสองพิกัด หากระบุละติจูด 90 เท่านั้นศูนย์จะตีความว่าเป็นลองจิจูด

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

ผลิตภัณฑ์ Dot a.b=ax*bx+ay*by+az*bzมีของแบบฟอร์ม เนื่องจากเวกเตอร์มีความยาวหน่วยทั้งหมด, ผลคูณของจุดเท่ากับโคไซน์ของมุมระหว่างเวกเตอร์

ในการคำนวณลูปจะวนซ้ำ 6 ครั้งเพื่อส่งผ่านข้อมูลอินพุตสองครั้ง ในการวนซ้ำ 0,2,4 ตัวแปรx,y,zจะถูกตั้งค่าเป็น 1 เพื่อเริ่มการคำนวณใหม่ ในการวนซ้ำแต่ละครั้งตัวแปรเหล่านี้จะถูกคูณด้วยองค์ประกอบ x, y และ z ของแต่ละเวกเตอร์โดยใช้ข้อมูลลองจิจูดและละติจูดที่เก็บไว้ในt[0],t[1](ซึ่งสำหรับเหตุผลเกี่ยวกับการเล่นกอล์ฟยังได้รับมอบหมายด้วยu,v) ผลรวมของตัวแปรจะถูกเขียนไปยังอาร์เรย์n(ค่าขยะบนซ้ำแม้จะถูกแทนที่ด้วยค่าที่ถูกต้องในการทำซ้ำคี่) เพื่อที่ในตอนท้ายมีผลิตภัณฑ์n 3 จุด[a.b, c.a, b.c]

สำหรับกฎโคไซน์เราต้องการโคไซน์ของมุมทั้งสามรวมระหว่างจุดยอด แต่เราก็ต้องการไซน์ด้วย สิ่งเหล่านี้ได้sqrt(1-cosine**2)มาจาก เมื่อไซน์ถูกคูณเข้าด้วยกันนิพจน์สามารถจัดเรียงใหม่ได้ดังนั้นจึงsqrtจำเป็นต้องเรียกเพียงครั้งเดียว ความจริงที่ว่าเราไม่ทราบว่าไซน์เป็นบวกหรือลบไม่สำคัญเพราะสูตรแฮเวอรีนจะให้ไซน์บวกอยู่เสมอ ปริมาณทางกายภาพที่สำคัญคือระยะห่างระหว่างจุดซึ่งแน่นอนและเป็นบวกเสมอ

สำหรับแต่ละซ้ำi=0..2เราจะคำนวณค่าสำหรับองค์ประกอบอาร์เรย์มุมตรงข้ามi-1โดยใช้องค์ประกอบอื่น ๆและi i-2ตัวห้อยอาร์เรย์เชิงลบเช่นนี้ถูกต้องตามกฎหมายใน Ruby พวกเขาเพิ่งตัดไปจนถึงจุดเริ่มต้นของอาร์เรย์

Ungolfed ในโปรแกรมทดสอบ

ต้องการพิกัดสามชุดในบรรทัดเดียวกันโดยมีช่องว่างระหว่างกัน

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]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.