Resolvendo triângulos com trigonometria

13

Hora de desenterrar suas notas antigas de trigonometria do ensino médio! O desafio é resolver os lados e ângulos desconhecidos de diferentes triângulos. E como é habitual no código de golfe, o menor código de trabalho vence.

Este não é um problema trivial; minha implementação de referência em python atualmente tem 838 837 caracteres, mas tenho certeza de que você poderá usar soluções muito menores.

Além disso, se você estiver preso, esta seção da Wikipedia deve ajudá-lo: Triângulo: Computando os lados e os ângulos .

Entrada

O triângulo a seguir mostra os nomes dos lados e ângulos usados ​​neste desafio. Observe que os lados são minúsculos e os ângulos são maiúsculos.

Triângulo

A entrada é fornecida como seis valores separados por espaço, stdincomo argumentos de linha de comando ou (sua escolha). Os seis valores correspondem aos lados a, b, ce ângulos A, B, C. Os lados desconhecidos são dados como pontos de interrogação ( ?). Os ângulos de entrada e saída devem estar em radianos. Você pode assumir que os valores de entrada estão corretos (não é necessário validar nada). Você também pode assumir que o triângulo de entrada não é degenerado e que todos os lados e ângulos são diferentes de zero.

A entrada de exemplo a seguir informa que lado aé 8, lado bé 12e ângulo Aé 0.5radianos:

8 12 ? 0.5 ? ?

Resultado

A saída é fornecida no mesmo formato da entrada - seis números separados por espaço ativados stdout. A única exceção é quando não é possível resolver o triângulo de entrada - a sequência "No solution"deve ser gravada stdout. Se duas soluções são possíveis, ambas são produzidas com uma nova linha entre elas.

A seguir, é apresentada a saída da entrada acima:

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

A saída não é necessária para ter muita precisão, mas pelo menos algumas casas decimais são necessárias.

Regras

  • A entrada é lida stdinou argumentos de linha de comando
  • A saída é gravada em stdout
  • Se duas soluções são possíveis com a entrada fornecida, produza
  • Se houver pouca informação para obter uma ou duas soluções claras, considere um "No solution"caso
  • Nenhum código embutido ou pré-existente pode ser usado (é claro que você pode usar funções trigonométricas, mas não " solveTriangle" ou coisa parecida)
  • O código mais curto vence

Casos de teste

Dentro   3 4 5 ? ? ?

Fora 3.0 4.0 5.0 0.643501108793 0.927295218002 1.57079630572


Dentro   ? 4 ? 0.64 0.92 1.57

Fora 3.00248479301 4.0 5.02764025486 0.64 0.92 1.57


Dentro   ? ? 5 ? 0.92 ?

Fora No solution


Dentro   ? ? 5 ? 0.92 1.57

Fora 3.03226857833 3.97800936148 5.0 0.65159265359 0.92 1.57


Dentro   8 12 ? 0.5 ? ?

Fora (duas soluções)

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

Dentro   8 12 ? ? .5 ?

Fora 8.0 12.0 18.3912222133 0.325325285223 0.5 2.31626736837

Boa sorte!

Comunidade
fonte
Podemos assumir que o triângulo não é degenerado, com todos os comprimentos e ângulos positivos (em particular, diferentes de zero)?
usar o seguinte comando
@ boothby Sim, você pode. Vou atualizar o OP.
1
Além disso ... se você quiser imprimir todas as soluções, precisará fornecer pelo menos um lado. Caso contrário, você sabe, soluções infinitas.
Boothby
@ boothby, eu provavelmente não estava muito clara aqui. O que eu quis dizer é que, se houver duas soluções para a entrada, você precisará gerar as duas.

Respostas:

7

Python, 441 caracteres

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'

Seu trigonométrico típico calcula a resposta. As soluções possíveis atuais são armazenadas como tuplas em V. Quaisquer valores desconhecidos são registrados como 0. Uma sétima variável R é o valor a/sin(A)==b/sin(B)==c/sin(C).

Eu uso um truque em que os valores a / b / c são alternados a cada iteração para evitar muita lógica redundante. O loop interno precisa apenas calcular valores do lado ou ângulo A.

Keith Randall
fonte
Eu uso um truque semelhante para alternar as variáveis, mas você certamente venceu minha solução. +1, aprendi um par novos truques deste :)
A propósito, há um problema com o seu código: tente 8 12 ? ? .5 ?.
1
Você pode obter 419 bytes se raspar a quebra de linha à direita e substituir as duas reentrâncias mais internas por uma e duas guias, respectivamente.
Joey
Ah, isso também parece muito com a minha solução, embora eu não tenha notado as "todas as soluções" até logo depois que você postou isso. Você pode economizar ainda mais se você substituir if acom if not ae achatar as condicionais para um nível.
Boothby
4

Plain C, 565 555 530 caracteres

C não é o melhor idioma para o Code Golf, eu acho, então é só por diversão.

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

Compilado com cc -o trig trig.c -lm. Lê a entrada como argumentos da linha de comando.

Alexander Bakulin
fonte
Essa solução também falha 8 12 ? ? .5 ?- eu a adicionei como um caso de teste adicional no OP.
1
Fixo! O comprimento reduzido como um efeito colateral :)
Alexander Bakulin
1

Perl - 412 caracteres

Como one-liner perl, baseado na solução Python de 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");

Aqui de uma forma mais legível:

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
fonte