Calculer le point de Fermat d'un triangle

13

Ceci est quelque peu similaire aux centres d'un triangle , mais avec un point différent. Le point de Fermat est le point P dans le triangle ABC de sorte que la valeur de AP + BP + CP soit minimisée. Il y a deux cas:

S'il y a un angle supérieur à 120 degrés, ce sommet est le point de fermat. Sinon, dessinez des triangles équilatéraux sur chacun des côtés de ABC. Connectez le sommet éloigné de chaque triangle équilatéral au sommet opposé du triangle ABC. Faire cela pour chacun des trois triangles équilatéraux résulte en un seul point d'intersection commun pour les trois lignes, qui est le point de Fermat.

Il devrait fonctionner dans les 5 secondes sur une machine raisonnable.

Entrée : un ensemble de 3 points, pas nécessairement des entiers. Cela peut être considéré comme un tableau imbriqué, une chaîne, une liste de tuples, etc. (selon ce qui convient à votre langue).

Sortie : Les coordonnées du point Fermat, encore une fois, mais votre langue gère le mieux les points. Les inexactitudes en virgule flottante ne seront pas prises en compte pour vous.

Cas de test :

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

C'est le golf de code donc le code le plus court gagne!

soktinpk
la source
1
Est-il OK d'essayer tous les points par incréments de précision en virgule flottante et de sélectionner celui qui minimise la distance totale?
xnor
1
@xnor Si vous pouvez le faire dans les 5 secondes.
soktinpk
Jusqu'à combien de chiffres significatifs la sortie doit-elle être précise? En outre, est-ce correct si la -0.0sortie remplace certains 0.0s?
R. Kap
@R. Kap je dirais environ 5 ou 6 chiffres significatifs. Il n'y a pas tant que les erreurs d'arrondi devraient être un problème. Quant à la deuxième question, cela semble correct.
soktinpk

Réponses:

3

Haskell, 346 291 285 octets

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)

Le même code avec quelques explications

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

Tests:

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

Production:

[1.2113248654051871,1.7886751345948126]
[-1.0,-1.0]
[0.0,-0.42264973081037427]
[0.0,0.0]
[0.0,0.0]
Damien
la source
Comment comptez-vous les octets? £ et ¤ sont chacun 2 octets en UTF-8, et je ne connais pas de compilateur Haskell qui accepte ISO-8859-1. (Cependant, vous avez beaucoup d'opérateurs ASCII 1 octet gratuits, donc c'est facile à corriger.)
Anders Kaseorg
Je le compte avec mon éditeur qui compte réellement les caractères. Je ne savais pas qu'il s'agissait de 2 octets, mais de toute façon, comme vous l'avez dit, je pourrais le remplacer par d'autres opérateurs de 1 octet. Ce code se compile avec GHC 7.8.3
Damien
GHC compile votre code lorsqu'il est encodé en UTF-8 avec £et ¤comme opérateurs à 2 octets, mais pas lorsqu'il est encodé en ISO-8859-1 avec £et en ¤tant qu'opérateurs à 1 octet. Les opérateurs disponibles 1 octets dans UTF-8 sont !, #, %, &, ?. Vous devez remplacer les opérateurs à 2 octets ou ajuster votre nombre d'octets.
Anders Kaseorg
2

Python, 475 448 440 octets

Toute aide au golf est appréciée.

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

Non golfé:

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

Contribution:

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

Production:

[1.2113248652983113, 1.7886751347016887]
[-1, -1]
[0.0, -0.42264973086764884]
[0, 0]
[0, 0]
abybaddi009
la source
2
from math import*est un golf assez commun. Cela vous permettra également de l'utiliser piau lieu de le coder en dur (même longueur pour 2*pi/3). Vous pouvez également déposer beaucoup d'espaces comme: d=lambda x,y:(....
FryAmTheEggman
2

Python 3.5, 1019 1016 998 982 969 953 octets:

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]

Incroyablement long par rapport aux autres réponses, mais bon, au moins ça marche! Je ne pourrais pas être plus heureux du résultat que j'ai obtenu car cela doit être l'un des défis les plus difficiles que j'ai jamais fait. Je suis tellement content que ça marche vraiment! : D Maintenant, sur les notes plus techniques:

  • Cette fonction prend chaque paire ordonnée en liste ou en tuple. Par exemple, H((1,1),(2,2),(1,2))cela fonctionnera, mais il en sera de même H([1,1],[2,2],[1,2]).
  • Produit les coordonnées des points dans une liste d'entiers ou de points flottants selon qu'il existe ou non un angle supérieur ou égal à 120 °.
  • Cela peut sortir -0.0à la place de 0.0pour certaines entrées. Par exemple, la sortie de l'entrée [-1, -1], [1, -1], [0, 1]est [-0.0, -0.4226497308103744]. J'espère que cela va bien, même si ce n'est pas le cas, je vais le changer, même si cela me coûtera quelques octets de plus. C'est correct, comme l' OP l'a confirmé .
  • Doit être précis jusqu'à au moins 13jusqu'à 14des chiffres significatifs.

Je vais essayer de jouer au golf avec le temps. Une explication, peut-être très longue, à venir bientôt.

Essayez-le en ligne! (Ideone)

R. Kap
la source
1

Mathematica, 39 octets

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

Construit une équation basée sur les distances entre les sommets et un point {x,y}. Utilise ensuite la NArgMinfonction pour trouver un minimum global pour cette équation, qui sera le point de Fermat par définition.

Exemple

miles
la source
1
39 octets, quand la prochaine réponse la plus courte est 285 ...
Bálint