Hitung Titik Fermat Segitiga

13

Ini agak mirip dengan pusat-pusat segitiga , tetapi dengan titik berbeda. The Fermat Titik adalah titik P di segitiga ABC sehingga nilai AP + BP + CP diminimalkan. Ada dua kasus:

Jika ada sudut lebih besar dari 120 derajat, titik itu adalah titik kulit. Jika tidak, gambar segitiga sama sisi pada masing-masing sisi ABC. Hubungkan titik jauh dari setiap segitiga sama sisi ke sudut berlawanan dari segitiga ABC. Melakukan ini untuk masing-masing dari tiga segitiga sama sisi menghasilkan titik persimpangan tunggal untuk ketiga garis, yaitu Fermat Point.

Ini harus berjalan dalam 5 detik pada mesin yang masuk akal.

Input : Satu set 3 poin, tidak harus bilangan bulat. Ini dapat diambil sebagai array bersarang, string, daftar tupel, dll. (Apa pun yang sesuai dengan bahasa Anda).

Output : Koordinat titik Fermat, sekali lagi, namun bahasa Anda paling baik menangani poin. Ketidakakuratan titik mengambang tidak akan dihitung terhadap Anda.

Kasus uji :

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

Ini golf kode sehingga kode terpendek menang!

soktinpk
sumber
1
Apakah boleh untuk mencoba semua titik dalam peningkatan ketelitian titik apung dan memilih satu yang meminimalkan jarak total?
xnor
1
@ xnor Jika Anda dapat melakukannya dalam 5 detik.
soktinpk
Sampai berapa banyak angka signifikan yang harus akurat untuk hasil? Juga, apakah tidak apa-apa jika -0.0output menggantikan beberapa 0.0s?
R. Kap
@R. Kap saya akan mengatakan sekitar 5 atau 6 angka penting. Tidak begitu banyak sehingga kesalahan pembulatan seharusnya menjadi masalah. Adapun pertanyaan kedua, itu sepertinya baik-baik saja.
soktinpk

Jawaban:

3

Haskell, 346 291 285 bytes

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)

Kode yang sama dengan beberapa penjelasan

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

Tes:

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

Keluaran:

[1.2113248654051871,1.7886751345948126]
[-1.0,-1.0]
[0.0,-0.42264973081037427]
[0.0,0.0]
[0.0,0.0]
Damien
sumber
Bagaimana Anda menghitung byte? £ dan ¤ masing-masing 2 byte di UTF-8, dan saya tidak tahu kompiler Haskell yang menerima ISO-8859-1. (Namun, Anda memiliki banyak operator ASCII 1-byte gratis, jadi ini mudah diperbaiki.)
Anders Kaseorg
Saya menghitungnya dengan editor saya yang sebenarnya menghitung karakter. Saya tidak tahu bahwa ini adalah 2 byte, tetapi bagaimanapun, seperti yang Anda katakan, saya bisa menggantinya dengan operator 1 byte lainnya. Kode ini dikompilasi dengan GHC 7.8.3
Damien
GHC mengkompilasi kode Anda ketika dikodekan sebagai UTF-8 dengan £dan ¤sebagai operator 2 byte, tetapi tidak ketika dikodekan sebagai ISO-8859-1 dengan £dan ¤sebagai operator 1 byte. Tersedia operator 1 byte dalam UTF-8 adalah !, #, %, &, ?. Anda harus mengganti operator 2 byte atau menyesuaikan jumlah byte Anda.
Anders Kaseorg
2

Python, 475 448 440 byte

Setiap bantuan untuk bermain golf lebih dihargai.

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

Tidak Disatukan:

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

Memasukkan:

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

Keluaran:

[1.2113248652983113, 1.7886751347016887]
[-1, -1]
[0.0, -0.42264973086764884]
[0, 0]
[0, 0]
abybaddi009
sumber
2
from math import*adalah golf yang cukup umum. Ini juga akan membiarkan Anda menggunakan pialih-alih mengkodekannya (dengan panjang yang sama 2*pi/3). Anda juga dapat menjatuhkan banyak spasi seperti: 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]

Sangat lama dibandingkan dengan jawaban lain, tapi hei, setidaknya itu berhasil! Saya tidak bisa lebih bahagia dengan hasil yang saya dapatkan karena ini harus menjadi salah satu tantangan tersulit yang pernah saya lakukan. Saya sangat senang bahwa itu benar-benar berfungsi! : D Sekarang, ke catatan yang lebih teknis:

  • Fungsi ini mengambil setiap pasangan yang dipesan sebagai daftar atau tupel. Misalnya, H((1,1),(2,2),(1,2))akan berhasil, tetapi juga akan H([1,1],[2,2],[1,2]).
  • Menghasilkan koordinat titik dalam daftar bilangan bulat atau titik mengambang tergantung pada apakah satu sudut lebih dari atau sama dengan 120º ada.
  • Ini dapat menghasilkan -0.0menggantikan 0.0beberapa input. Misalnya, output untuk input [-1, -1], [1, -1], [0, 1]adalah [-0.0, -0.4226497308103744]. Saya harap ini baik-baik saja, meskipun jika tidak, saya akan mengubahnya, meskipun akan dikenakan biaya beberapa byte lagi. Ini tidak apa-apa, seperti yang dikonfirmasi oleh OP .
  • Harus akurat setidaknya 13hingga 14angka signifikan.

Saya akan mencoba dan bermain golf ini seiring waktu. Penjelasan, mungkin sangat panjang, segera hadir.

Cobalah secara Online! (Ideone)

R. Kap
sumber
1

Mathematica, 39 byte

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

Membangun persamaan berdasarkan jarak antara titik dan titik {x,y}. Kemudian gunakan NArgMinfungsi untuk menemukan minimum global untuk persamaan itu, yang akan menjadi Fermat Point menurut definisi.

Contoh

mil
sumber
1
39 byte, ketika jawaban terpendek berikutnya adalah 285 ...
Bálint