Calcola il punto di Fermat di un triangolo


13

Questo è in qualche modo simile ai centri di un triangolo , ma con un punto diverso. Il Fermat Point è il punto P nel triangolo ABC in modo tale che il valore di AP + BP + CP sia ridotto al minimo. Esistono due casi:

Se c'è un angolo maggiore di 120 gradi, quel vertice è il punto di arresto. Altrimenti, disegna triangoli equilateri su ciascuno dei lati di ABC. Collegare il vertice lontano di ciascun triangolo equilatero al vertice opposto del triangolo ABC. In questo modo per ciascuno dei tre triangoli equilateri si ottiene un unico punto di intersezione comune per tutte e tre le linee, che è il punto di Fermat.

Dovrebbe funzionare entro 5 secondi su una macchina ragionevole.

Input : un set di 3 punti, non necessariamente numeri interi. Questo può essere preso come un array nidificato, una stringa, un elenco di tuple, ecc. (Qualunque cosa si adatti alla tua lingua).

Output : le coordinate del punto Fermat, ancora una volta, tuttavia la tua lingua gestisce meglio i punti. Le imprecisioni in virgola mobile non verranno conteggiate nei tuoi confronti.

Casi di prova :

[[1, 1], [2, 2], [1, 2]] --> [1.2113248654051871, 1.788675134594813]
[[-1, -1], [-2, -1], [0, 0]] --> [-1, -1]
[[-1, -1], [1, -1], [0, 1]] --> [0, -0.42264973081037427]
[[0, 0], [0.5, 0.8660254037844386], [-5, 0]] --> [0, 0]
[[0, 0], [0, -5], [-0.8660254037844386, 0.5]] --> [0, 0]

Questo è il codice golf, quindi vince il codice più corto!


1
È corretto provare tutti i punti con incrementi della precisione in virgola mobile e selezionare quello che minimizza la distanza totale?
xnor

1
@xnor Se riesci a farlo entro 5 secondi.
soktinpk,

Fino a quante cifre significative deve essere preciso l'output? Inoltre, va bene se l' -0.0output è al posto di alcuni 0.0s?
R. Kap,

@R. Kap direi circa 5 o 6 cifre significative. Non c'è così tanto che gli errori di arrotondamento dovrebbero essere un problema. Per quanto riguarda la seconda domanda, sembra che vada bene.
soktinpk,

Risposte:


3

Haskell, 346 291 285 byte

infixl 5£
z=zipWith
(?)=z(-)
t[a,b]=[-b,a]
a¤b=sum$z(*)a b
a%b=t a¤b
r a b c=[c%b/a%b,c%a/a%b]
x£y=2*x¤y<= -sqrt(x¤x*y¤y)
f[a,b,c]|a?b£c?b=b|a?c£b?c=c|b?a£c?a=a|[n,m,p,o]<-c?k a b c++a?k b c a=r[m,o][n,p][c%[n,m],a%[p,o]]
k a b c=map(/2)$z(+)a b?map(signum((b?a)%(c?a))*sqrt 3*)(t$b?a)

Lo stesso codice con alcune spiegazioni

infixl 5£
z=zipWith

-- operator ? : difference of two vectors
(?)=z(-)            

-- function t : rotate a vector by +90 degrees
t[a,b]=[-b,a]       

-- operator ¤ : scalar product of two vectors ( a¤b = a0 * b0 + a1 * b1 )
a¤b=sum$z(*)a b     

-- operator % : "cross product" of two vectors ( a%b = a0 * b1 - a1 * b0 )
--      this returns actually the z coordinate of the 3d cross vector
--      other coordinates are nul since a and b are in the xy plan
a%b=t a¤b

-- function r : solves the system of two linear equations with two variables x0,x1 :
--      a0*x0 - b0*x1 = c0
--      a1*x0 - b1*x1 = c1
r a b c=[c%b/a%b,c%a/a%b]

-- operator £ : returns true if the angle between two vectors is >= 120 degrees
--      x¤y = ||x|| * ||y|| * cos(xyAngle)
--      so xyAngle>=120° is equivalent to : x¤y / (||x|| * ||y||) <= -0.5
x£y=2*x¤y<= -sqrt(x¤x*y¤y)

-- function k : takes 3 points A B C of a triangle and constructs the point C' 
--              of the equilateral triangle ABC' which is opposite to C:
--              C' = (A+B)/2 - ((+/-) sqrt(3)/2 * t(AB))
--
--      the sign +/- is given by the sign of the cross vector of AB an AC ((b?a)%(c?a))
--      which is >0 if the angle between AB and AC is positive
--      and <0 otherwise.
k a b c=map(/2)$z(+)a b?map(signum((b?a)%(c?a))*sqrt 3*)(t$b?a)

-- function f : returns the fermat point of a triangle
f[a,b,c]
    |a?b£c?b=b  -- return B if angle ABC >= 120°
    |a?c£b?c=c  -- return C if angle BCA >= 120°
    |b?a£c?a=a  -- return A if angle CAB >= 120°
    |[n,m,p,o]<-c?k a b c++a?k b c a= -- calculate the two segments C'C and A'A
        r[m,o][n,p][c%[n,m],a%[p,o]]  -- return their intersection

test:

main = do 
    print $ f [[1, 1], [2, 2], [1, 2]]
    print $ f [[-1, -1], [-2, -1], [0, 0]]
    print $ f [[-1, -1], [1, -1], [0, 1]]
    print $ f [[0, 0], [0.5, 0.8660254037844386], [-5, 0]]
    print $ f [[0, 0], [0, -5], [-0.8660254037844386, 0.5]]

Produzione:

[1.2113248654051871,1.7886751345948126]
[-1.0,-1.0]
[0.0,-0.42264973081037427]
[0.0,0.0]
[0.0,0.0]

Come stai contando i byte? £ e ¤ sono 2 byte ciascuno in UTF-8 e non conosco un compilatore Haskell che accetta ISO-8859-1. (Hai un sacco di operatori ASCII a 1 byte gratuiti, tuttavia, quindi è facile da risolvere.)
Anders Kaseorg,

Lo sto contando con il mio editor che in realtà conta i personaggi. Non sapevo che fossero 2 byte, ma comunque, come hai detto, avrei potuto sostituirlo con altri operatori da 1 byte. Questo codice viene compilato con GHC 7.8.3
Damien,

GHC compila il tuo codice quando codificato come UTF-8 con £e ¤come operatori a 2 byte, ma non quando codificato come ISO-8859-1 con £e ¤come operatori a 1 byte. Gli operatori 1 byte disponibili a UTF-8 sono !, #, %, &, ?. È necessario sostituire gli operatori a 2 byte o regolare il conteggio dei byte.
Anders Kaseorg,

2

Pitone, 475 448 440 byte

Qualsiasi aiuto per il golf è ulteriormente apprezzato.

from math import *
d=lambda x,y:((x[0]-y[0])**2+(x[1]-y[1])**2)**0.5
s=lambda A,B,C:(d(B,C), d(C,A), d(A,B))
j=lambda a,b,c:acos((b*b+c*c-a*a)/(2*b*c))
t=lambda a,b,c:1/cos(j(a,b,c)-pi/6)
b=lambda A,B,C,p,q,r:[(p*A[i]+q*B[i]+r*C[i])/(p+q+r) for i in [0,1]] 
f=lambda A,B,C:A if j(*s(A,B,C)) >= 2*pi/3 else B if j(*s(B,C,A)) >= 2*pi/3 else C if j(*s(C,A,B)) >= 2*pi/3 else b(A,B,C,d(B,C)*t(*s(A,B,C)),d(C,A)*t(*s(B,C,A)),d(A,B)*t(*s(C,A,B)))

Ungolfed:

from math import *
#distance between two points
d = lambda x,y: ((x[0]-y[0])**2+(x[1]-y[1])**2)**0.5

#given the points, returns the sides 
s = lambda A,B,C : (d(B,C), d(C,A), d(A,B))

#given the sides, returns the angle
j = lambda a,b,c : acos((b*b+c*c-a*a)/(2*b*c))

#given the sides, returns secant of that angle
t = lambda a,b,c: 1/cos(j(a,b,c)-pi/6)

#given the sides and the Trilinear co-ordinates, returns the Cartesian co-ordinates
b = lambda A,B,C,p,q,r: [(p*A[i]+q*B[i]+r*C[i])/(p+q+r) for i in [0,1]] 

#this one checks if any of the angle is >= 2π/3 returns that point else computes the point
f = lambda A,B,C: A if j(*s(A,B,C)) >= 2*pi/3 else B if j(*s(B,C,A)) >= 2*pi/3 else C if j(*s(C,A,B)) >= 2*pi/3 else b(A,B,C,d(B,C)*t(*s(A,B,C)),d(C,A)*t(*s(B,C,A)),d(A,B)*t(*s(C,A,B)))

Ingresso:

print('{}'.format(f([1, 1], [2, 2], [1, 2])))
print('{}'.format(f([-1, -1], [-2, -1], [0, 0])))
print('{}'.format(f([-1, -1], [1, -1], [0, 1])))
print('{}'.format(f([0, 0], [0.5, 0.8660254037844386], [-5, 0])))
print('{}'.format(f([0, 0], [0, -5], [-0.8660254037844386, 0.5])))

Produzione:

[1.2113248652983113, 1.7886751347016887]
[-1, -1]
[0.0, -0.42264973086764884]
[0, 0]
[0, 0]

2
from math import*è un golf abbastanza comune. Questo ti permetterà anche di usarlo piinvece di codificarlo (stessa lunghezza per 2*pi/3). Si può anche cadere un sacco di spazio bianco come: d=lambda x,y:(....
FryAmTheEggman,

2

Python 3.5, 1019 1016 998 982 969 953 byte:

from math import*
def H(z,a,b):c=complex;T=lambda A,B:abs(c(*A)-c(*B));d=T(z,a);e=T(z,b);f=T(a,b);g=[d,e,f];h=max(g);g.remove(h);i=acos((sum(i*i for i in g)-(h*h))/(2*g[0]*g[-1]));_=[[z,a],[z,b],[a,b]];j,s,t=cos,sin,atan;N=[[b,a]for a,b in zip([b,a,z],[max(i,key=i.get)if i!=''else''for i in[{(g[0]+(h*j(t(l))),g[1]+(h*s(t(l)))):T(k,(g[0]+(h*j(t(l))),g[1]+(h*s(t(l))))),(g[0]-(h*j(t(l))),g[1]-(h*s(t(l)))):T(k,(g[0]-(h*j(t(l))),g[1]-(h*s(t(l)))))}if l else{(g[0]+h,g[1]):T(k,(g[0]+h,g[1])),(g[0]-h,g[1]):T(k,(g[0]-h,g[1]))}if l==0else''for g,h,l,k in zip([((a[0]+b[0])/2,(a[1]+b[1])/2)for a,b in _],[(3**0.5)*(i/2)for i in[d,e,f]],[-1/p if p else''if p==0else 0for p in[((a[1]-b[1])/(a[0]-b[0]))if a[0]-b[0]else''for a,b in _]],[b,a,z])]])if b!=''];I=N[0][0][1];J=N[0][0][0];K=N[1][0][1];G=N[1][0][0];A=(N[0][1][1]-I)/(N[0][1][0]-J);B=I-(A*J);C=(K-N[1][1][1])/(G-N[1][1][0]);D=K-(C*G);X=(D-B)/(A-C);Y=(A*X)+B;return[[X,Y],[[a,b][h==d],z][h==f]][i>2.0943]

Incredibilmente lungo rispetto ad altre risposte, ma ehi, almeno funziona! Non potrei essere più felice del risultato che ho ottenuto in quanto questa deve essere una delle sfide più difficili che abbia mai fatto. Sono così felice che funzioni davvero! : D Ora, sulle note più tecniche:

  • Questa funzione accetta ogni coppia ordinata come un elenco o una tupla. Ad esempio, H((1,1),(2,2),(1,2))funzionerà, ma lo sarà anche H([1,1],[2,2],[1,2]).
  • Emette le coordinate dei punti in un elenco di numeri interi o punti mobili a seconda che esista o meno un angolo maggiore o uguale a 120º.
  • Questo può essere emesso -0.0al posto di 0.0alcuni input. Ad esempio, l'output per l'input [-1, -1], [1, -1], [0, 1]è [-0.0, -0.4226497308103744]. Spero che vada bene, anche se in caso contrario lo cambierò, anche se mi costerà qualche byte in più. Questo va bene, come confermato dall'OP .
  • Dovrebbe essere accurato almeno 13fino a 14cifre significative.

Proverò a giocare a golf più nel tempo. Una spiegazione, forse molto lunga, in arrivo.

Provalo online! (Ideone)


1

Mathematica, 39 byte

Sum[Norm[p-{x,y}],{p,#}]~NArgMin~{x,y}&

Costruisce un'equazione in base alle distanze tra i vertici e un punto {x,y}. Quindi utilizza la NArgMinfunzione per trovare un minimo globale per tale equazione, che per definizione sarà il punto di arresto.

Esempio


1
39 byte, quando la risposta più breve successiva è 285 ...
Bálint,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.