Rozwiązywanie trójkątów za pomocą trygonometrii

13

Czas wykopać stare notatki z trygonometrii ze szkoły średniej! Wyzwanie polega na rozwiązaniu nieznanych boków i kątów różnych trójkątów. I jak to zwykle jest w golfie kodu, wygrywa najmniejszy działający kod.

To nie jest trywialny problem; moja referencyjna implementacja w Pythonie ma obecnie 838 837 znaków, ale jestem pewien, że będziesz w stanie grać w golfa o wiele mniejsze.

Dodatkowo, jeśli utkniesz, ta sekcja na Wikipedii powinna zacząć: Trójkąt: Obliczanie boków i kątów .

Wejście

Poniższy trójkąt pokazuje nazwy boków i kątów zastosowanych w tym wyzwaniu. Pamiętaj, że boki są małe, a kąty są wielkie.

Trójkąt

Dane wejściowe są podawane jako sześć wartości oddzielonych spacjami, w stdinargumentach wiersza polecenia lub jako argumenty (do wyboru). Sześć wartości odpowiada bokom a, b, ci kątom A, B, C. Nieznane strony podano jako znaki zapytania ( ?). Kąty wejściowy i wyjściowy muszą być wyrażone w radianach. Możesz założyć, że wartości wejściowe są prawidłowe (nie musisz niczego sprawdzać). Możesz również założyć, że trójkąt wejściowy nie jest zdegenerowany, a wszystkie boki i kąty są niezerowe.

Poniższy przykładowy przykład mówi, że bok ajest 8, bok bjest, 12a kąt Ato 0.5radiany:

8 12 ? 0.5 ? ?

Wynik

Dane wyjściowe są podawane w tym samym formacie co dane wejściowe - sześć liczb oddzielonych spacją stdout. Jedynym wyjątkiem jest sytuacja, w której nie można rozwiązać trójkąta wejściowego - wówczas "No solution"należy zapisać ciąg znaków stdout. Jeśli możliwe są dwa rozwiązania, oba są generowane z nową linią między nimi.

Poniżej przedstawiono wynik dla powyższego wejścia:

8.0 12.0 16.0899264342 0.5 0.802561439714 1.83903121388
8.0 12.0 4.97205505116 0.5 2.33903121388 0.302561439714

Dane wyjściowe nie muszą mieć dużej precyzji, ale wymagane jest co najmniej kilka miejsc po przecinku.

Zasady

  • Dane wejściowe są odczytywane z stdinargumentów wiersza polecenia
  • Dane wyjściowe są zapisywane do stdout
  • Jeśli dla danego wejścia możliwe są dwa rozwiązania, wypisz oba
  • Jeśli jest zbyt mało informacji, aby uzyskać jedną lub dwie klarowne roztwory, uważają, że jest to "No solution"przypadek
  • Nie można użyć wbudowanego lub wcześniej istniejącego kodu (oczywiście można używać funkcji wyzwalacza, ale nie „ solveTriangle” lub podobnych)
  • Najkrótszy kod wygrywa

Przypadki testowe

W   3 4 5 ? ? ?

Na zewnątrz 3.0 4.0 5.0 0.643501108793 0.927295218002 1.57079630572


W   ? 4 ? 0.64 0.92 1.57

Na zewnątrz 3.00248479301 4.0 5.02764025486 0.64 0.92 1.57


W   ? ? 5 ? 0.92 ?

Na zewnątrz No solution


W   ? ? 5 ? 0.92 1.57

Na zewnątrz 3.03226857833 3.97800936148 5.0 0.65159265359 0.92 1.57


W   8 12 ? 0.5 ? ?

Out (dwa rozwiązania)

8.0 12.0 16.0899264342 0.5 0.802561439714 1.83903121388
8.0 12.0 4.97205505116 0.5 2.33903121388 0.302561439714

W   8 12 ? ? .5 ?

Na zewnątrz 8.0 12.0 18.3912222133 0.325325285223 0.5 2.31626736837

Powodzenia!

Społeczność
źródło
Czy możemy założyć, że trójkąt nie jest zdegenerowany, a wszystkie długości i kąty są dodatnie (w szczególności niezerowe)?
stoisko
@boothby Tak, możesz. Zaktualizuję OP.
1
Ponadto ... jeśli chcesz, abyśmy wydrukowali wszystkie rozwiązania, musisz podać przynajmniej jedną stronę. W przeciwnym razie, wiesz, nieskończone rozwiązania.
stoisko
@boothby, prawdopodobnie byłem tu zbyt niejasny. Chodziło mi o to, że jeśli istnieją dwa rozwiązania wejścia, musisz wyprowadzić oba.

Odpowiedzi:

7

Python, 441 znaków

from math import*
V=[map(float,raw_input().replace('?','0').split())+[0]]
for i in' '*9:
 W=[]
 for a,b,c,A,B,C,R in V:
  if B and C:A=A or pi-B-C
  if a:
   if A:R=R or a/sin(A)
   else:
    if b and c:A=acos((b*b+c*c-a*a)/2/b/c)
    elif R:N=asin(a/R);W+=[(b,c,a,B,C,N,R)];A=pi-N
  else:a=R*sin(A)
  W+=[(b,c,a,B,C,A,R)]
 V=W
V=[T for T in V if all(t>0 for t in T)]
if V:
 for T in V:print' '.join(map(str,T[:-1]))
else:print'No solution'

Czy Twój typowy wyzwalacz oblicza odpowiedź. Aktualne możliwe rozwiązania są przechowywane w krotkach w V. Wszelkie nieznane wartości są zapisywane jako 0. Siódma zmienna R jest wartością a/sin(A)==b/sin(B)==c/sin(C).

Używam sztuczki, w której wartości a / b / c są cyklicznie zmieniane przy każdej iteracji, aby uniknąć mnóstwa redundantnej logiki. Pętla wewnętrzna musi jedynie obliczyć wartości strony A lub kąta.

Keith Randall
źródło
Używam podobnej sztuczki zmieniania zmiennych, ale na pewno pokonałeś moje rozwiązanie. +1, nauczyłem się z tego kilku nowych sztuczek :)
Nawiasem mówiąc, jest problem z twoim kodem: spróbuj 8 12 ? ? .5 ?.
1
Możesz uzyskać go do 419 bajtów, jeśli zgolisz linię końca linii i zastąpisz dwa najbardziej wewnętrzne wcięcia odpowiednio jedną i dwiema zakładkami.
Joey,
Hah, to też wygląda bardzo podobnie do mojego rozwiązania, chociaż nie zauważyłem „wszystkich rozwiązań” aż do momentu, kiedy to opublikowałeś. Można zaoszczędzić nawet więcej, jeśli zastąpi if asię if not ai spłaszczyć dół warunkowe do 1 poziomu.
stoisko
4

Zwykły C, 565 555 530 znaków

C chyba nie jest najlepszym językiem dla Code Golf, więc jest po prostu dla zabawy.

float t[6],u[6],P=3.1415;x,w,j,k,D,E;
#define y(V) for(V=0;V<6;++V)
#define Y if(p[j]&&p[k]&&
#define A(o,s,a,b,c,A,B,C) z(float*p){y(D)y(E)if(j=D%3,k=E%3,j-k){Y c)w=C=acos((a*a+b*b-c*c)/2/a/b);if(A&&B)w=C=P-A-B;Y C)w=c=sqrt(a*a+b*b-2*a*b*cos(C));if(A&&B&&a)w=b=s(B)*a/s(A);Y A&&!B&&!C)w=B=(x=A<P/2&&a<b&&p==u,1-2*x)*(asin(b*s(A)/a)-x*P);}y(j)k=w&&(p==t||x>0)&&o("%f ",a);o("\n");}main(int l,char*q[]){y(j)sscanf(*++q,"%f",t+j),u[j]=t[j];z(t);z(u);j=w||o("No solution\n");}
A(printf,sin,p[j],p[k],p[3-j-k],p[j+3],p[k+3],p[6-j-k])

Kompilowany z cc -o trig trig.c -lm. Odczytuje dane wejściowe jako argumenty wiersza poleceń.

Alexander Bakulin
źródło
To rozwiązanie również się nie udaje 8 12 ? ? .5 ?- dodałem je jako dodatkowy przypadek testowy w OP.
1
Naprawiony! Długość zredukowana jako efekt uboczny :)
Alexander Bakulin
1

Perl - 412 znaków

Jako perl-one-liner, oparty na Pythonie: Keith Randall:

use Math::Trig;@V=((map{tr/?/0/;$_}@ARGV),0);map{my@W;while(($a,$b,$c,$A,$B,$C,$R)=splice@V,0,7){$A||=pi-$B-$C if($B*$C);if($a){if($A){$R||=$a/sin$A;}else{if($b*$c){$A=acos(($b*$b+$c*$c-$a*$a)/2/$b/$c);}elsif($R){$N=asin($a/$R);push@W,$b,$c,$a,$B,$C,$N,$R;$A=pi-$N;}}}else{$a=$R*sin$A;}push@W,$b,$c,$a,$B,$C,$A,$R if($a*$b*$c>=0);}@V=@W;}(1..9);print($V[0]?join' ',map{(((6-$i++)%7)?$_:"\n")}@V:"No solution\n");

Tutaj w bardziej czytelnej formie:

use Math::Trig;
@V = ( ( map { tr/?/0/; $_ } @ARGV ), 0 );
map {
    my @W;
    while ( ( $a, $b, $c, $A, $B, $C, $R ) = splice @V, 0, 7 ) {
        $A ||= pi- $B - $C
             if ( $B * $C );
        if ($a) {
            if ($A) { $R ||= $a / sin $A; }
            else {
                if ( $b * $c ) {
                    $A = acos(
                        ( $b * $b + $c * $c - $a * $a ) / 2 / $b / $c );
                } elsif ($R) {
                    $N = asin( $a / $R );
                    push @W, $b, $c, $a, $B, $C, $N, $R;
                    $A = pi- $N;
                }
            }
        } else {
            $a = $R * sin $A;
        }
        push @W, $b, $c, $a, $B, $C, $A, $R
            if ( $a * $b * $c >= 0 );
    }
    @V = @W;
} ( 1 .. 9 );

print( $V[0]
         ? join ' ', map { ( ( ( 6 - $i++ ) % 7 ) ? $_ : "\n" ) } @V
         : "No solution\n" );
xxfelixxx
źródło