Üçgenin Fermat Noktasını Hesaplama


13

Bu, bir üçgenin merkezlerine biraz benziyor , ancak farklı bir noktaya sahip. Fermat noktası , AP + BP + CP değeri en aza indirecek üçgen ABC noktası P. İki durum söz konusudur:

120 dereceden daha büyük bir açı varsa, bu tepe fermat noktasıdır. Aksi takdirde, ABC'nin her iki tarafına eşkenar üçgenler çizin. Her eşkenar üçgenin uzak tepe noktasını ABC üçgeninin ters tepe noktasına bağlayın. Bunu üç eşkenar üçgenin her biri için yapmak, Fermat Noktası olan üç hattın tümü için tek bir ortak kesişme noktası ile sonuçlanır.

Makul bir makinede 5 saniye içinde çalışmalıdır.

Girdi : 3 noktadan oluşan bir küme, tamsayı olması gerekmez. Bu, yuvalanmış bir dizi, dize, tuples listesi, vb. (Dilinize uygun olan ne olursa olsun) olarak alınabilir.

Çıktı : Fermat noktasının koordinatları yine de, diliniz en iyi noktaları ele alıyor. Kayan nokta yanlışlıkları size karşı sayılmaz.

Test Durumları :

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

Bu kod golf çok kısa kod kazanır!


1
Tüm noktaları kayan nokta hassasiyeti artışlarıyla denemek ve toplam mesafeyi en aza indiren noktayı seçmek uygun mudur?
xnor

1
@xnor 5 saniye içinde yapabilirseniz.
soktinpk

Çıktı ne kadar anlamlı rakama kadar doğru olmalıdır? Ayrıca, -0.0bazı 0.0s yerine çıktı olup olmadığı iyi mi?
R. Kap

@R. Kap 5 ya da 6 önemli rakam söyleyebilirim. Yuvarlama hatalarının bir sorun olması gerektiği çok fazla değil. İkinci soruya gelince, bu iyi görünüyor.
soktinpk

Yanıtlar:


3

Haskell, 346 291 285 bayt

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)

Bazı açıklamalar ile aynı kod

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

Testler:

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

Çıktı:

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

Baytları nasıl sayıyorsunuz? £ ve ¤ UTF-8'de 2 bayttır ve ISO-8859-1'i kabul eden bir Haskell derleyicisi bilmiyorum. (Yine de 1 baytlık ücretsiz ASCII operatörleriniz var, bu yüzden bunu düzeltmek kolay.)
Anders Kaseorg

Gerçekten karakterleri sayan editörümle sayıyorum. Bunların 2 bayt olduğunu bilmiyordum, ama yine de, dediğin gibi, diğer 1 bayt operatörleriyle değiştirebilirim. Bu kod GHC 7.8.3
Damien

GHC, kodunuzu UTF-8 olarak £ve ¤2 bayt işleçleriyle kodlandığında, ancak ISO-8859-1 olarak £ve ¤1 bayt işleçleriyle kodlandığında kodlamaz . UTF-8 mevcuttur 1 bayt operatörleri !, #, %, &, ?. 2 bayt operatörlerini değiştirmeli veya bayt sayınızı ayarlamalısınız.
Anders Kaseorg

2

Python, 475 448 440 bayt

Golf için herhangi bir yardım takdir edilmektedir.

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

Giriş:

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

Çıktı:

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

2
from math import*oldukça yaygın bir golf. Bu pi, sabit kodlama yerine kullanmanıza da izin verecektir (aynı uzunlukta 2*pi/3). Ayrıca böyle bir çok bosluk bırakın: d=lambda x,y:(....
FryAmTheEggman

2

Python 3.5, 1019 1016 998 982 969 953 bayt:

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]

Diğer cevaplara kıyasla inanılmaz derecede uzun, ama hey, en azından işe yarıyor! Yaptığım sonuçtan daha mutlu olamazdım, çünkü bu şimdiye kadar yaptığım en zor zorluklardan biri olmalı. Gerçekten çalıştığı için çok mutluyum ! : D Şimdi, daha teknik notlara bakalım:

  • Bu işlev, sipariş edilen her çifti bir liste veya bir demet olarak alır. Örneğin, H((1,1),(2,2),(1,2))işe yarayacak, ama işe yarayacak H([1,1],[2,2],[1,2]).
  • 120 angle değerine eşit veya daha fazla bir açı olup olmadığına bağlı olarak, bir tamsayı veya kayan nokta listesindeki noktaların koordinatlarını verir.
  • Bu, bazı girişlerin -0.0yerine çıktı alabilir 0.0. Örneğin, girdi çıktı [-1, -1], [1, -1], [0, 1]olan [-0.0, -0.4226497308103744]. Umarım bu iyidir, eğer değilse, değiştireceğim, ancak bana birkaç bayt daha mal olacak. OP tarafından onaylandığı gibi bu sorun değil .
  • En azından doğru yukarı olmalı 13kadar 14önemli rakamlar.

Ben zaman içinde bu daha golf çalışacağız. Muhtemelen çok uzun bir açıklama yakında gelecek.

Çevrimiçi Deneyin! (Ideone)


1

Mathematica, 39 bayt

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

Köşeler ve nokta arasındaki mesafelere dayalı bir denklem oluşturur {x,y}. Daha sonra NArgMinbu denklem için tanım gereği Fermat Noktası olacak global bir minimum bulmak için işlevi kullanır .

Misal


1
39 bayt, bir sonraki en kısa cevap 285 olduğunda ...
Bálint
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.