计算三角形的费马点


13

这有点类似于三角形的中心,但是具有不同的点。在费马点是在三角形ABC中的点P,使得AP + BP + CP的值被最小化。有两种情况:

如果角度大于120度,则该顶点为费马点。否则,在ABC的每一侧上绘制等边三角形。将每个等边三角形的远顶点连接到三角形ABC的相反顶点。对三个等边三角形中的每一个进行此操作会导致所有三条线的单个公共交点,即费马点。

它应在合理的计算机上运行5秒钟。

输入:一组3点,不一定是整数。可以将其视为嵌套数组,字符串,元组列表等(无论哪种语言)。

输出:同样,费马点的坐标,但是您的语言最能处理点。浮点数错误将不算在您身上。

测试用例

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

这是代码高尔夫球,所以最短的代码获胜!


1
是否可以尝试以浮点精度为增量的所有点,然后选择最小化总距离的点?
xnor

1
@xnor如果可以在5秒钟内完成操作。
soktinpk

输出必须精确到多少位有效数字?另外,如果-0.0输出代替某些0.0s也可以吗?
卡普

@R。Kap我会说大约5或6个有效数字。舍入错误应该不是什么大问题。至于第二个问题,这似乎很好。
soktinpk

Answers:


3

Haskell中,346个 291 285字节

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)

相同的代码并有一些解释

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

测试:

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

输出:

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

您如何计数字节?在UTF-8中,£和¤均为2个字节,我不知道接受ISO-8859-1的Haskell编译器。(不过,您有很多免费的1字节ASCII运算符,因此很容易解决。)
Anders Kaseorg 2016年

我用我的编辑器来计算它,它实际上是在计算字符。我不知道这些是2字节,但是无论如何,正如您所说,我可以用其他1字节运算符代替它。该代码使用GHC 7.8.3
Damien

当使用£¤用作2字节运算符编码为UTF-8时,GHC编译您的代码,但是使用£¤作为1字节运算符编码为ISO-8859-1时,GHC 不会编译。在UTF-8可用1个字节运营商!#%&?。您应该替换2个字节的运算符或调整字节数。
Anders Kaseorg '16

2

蟒蛇, 475 448 440字节

进一步打高尔夫球的任何帮助,不胜感激。

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

取消高尔夫:

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

输入:

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

输出:

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

2
from math import*是很普通的高尔夫。这也可以让您使用它pi而不是对其进行硬编码(的长度相同2*pi/3)。您还可以删除很多空白,例如:d=lambda x,y:(...
FryAmTheEggman '16

2

Python的3.5,1019个 1016 998 982 969 953字节:

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]

与其他答案相比,时间长得令人难以置信,但嘿,至少它能起作用!我对获得的结果感到满意,因为这一定是我做过的最困难的挑战之一。我真的很高兴它真的有效!:D现在,进入更多技术说明:

  • 此函数将每个有序对作为列表或元组。例如,H((1,1),(2,2),(1,2))将起作用,但是将起作用H([1,1],[2,2],[1,2])
  • 根据是否存在大于或等于120º的角度,输出整数列表或浮点列表中的点的坐标。
  • 这可以-0.0代替0.0某些输入而输出。例如,输出为输入[-1, -1], [1, -1], [0, 1][-0.0, -0.4226497308103744]我希望这是可以的,尽管如果不能,我将对其进行更改,尽管这将花费我更多的字节。OP确认,这没关系。
  • 至少应精确1314重要数字。

随着时间的推移,我会尝试更多的高尔夫运动。很快就会有一个解释,可能很长。

在线试用!(爱迪生)


1

Mathematica,39个字节

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

根据顶点和点之间的距离构造一个方程{x,y}。然后使用该NArgMin函数为该方程式找到一个全局最小值,根据定义,该最小值将成为费马点。

例


1
39个字节,下一个最短的答案是285 ...
巴林特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.