Mengidentifikasi segitiga

11

Menghitung jumlah segitiga dalam gambar adalah tugas yang biasa digunakan dalam tes otak. Anda diberi gambar yang berisi bentuk yang terdiri dari segitiga. Anda kemudian harus menemukan semua kemungkinan segitiga dalam gambar.

Tugas

Anda diberi daftar garis dalam format pilihan Anda. Anda kemudian harus menampilkan daftar segitiga yang ditemukan di dalamnya

Memasukkan

Anda diberi daftar garis, masing-masing diberikan oleh empat koordinat bilangan bulat (mis. x1 y1 x2 y2). Anda dapat memilih format input, asalkan terdokumentasi dengan jelas. Contoh:

0 4 8 1
0 4 9 5
8 1 9 5
2 8 0 4
9 5 2 8

[[0, 4, 8, 1], [0, 4, 9, 5], [8, 1, 9, 5], [2, 8, 0, 4], [9, 5, 2, 8]]

Berikut input yang sama dengan gambar:

gambar segitiga

Yang lain, dengan persimpangan (hanya dalam satu format untuk menghemat ruang):

[[2, 1, 5, 0], [2, 1, 2, 7], [5, 0, 6, 6], [5, 0, 2, 7], [6, 6, 2, 1], [2, 7, 6, 6]]

gambar segitiga

Keluaran

Anda harus menampilkan daftar semua segitiga, masing-masing diberikan oleh enam koordinat floating-point (mis. x1 y1 x2 y2 x3 y3), Pada gambar yang ditentukan oleh input. Ini mungkin bukan bilangan bulat, karena garis-garis mungkin melintas di titik mana pun. Anda dapat memilih format output, asalkan terdokumentasi dengan jelas. Contoh output untuk input contoh di atas:

0 4 8 1 9 5
0 4 9 5 2 8

[[0, 4, 8, 3, 9, 5], [0, 4, 9, 5, 2, 8]]
[[2, 1, 5, 0, 2, 7], [2, 1, 5, 0, 6, 6], [5, 0, 6, 6, 2, 7], [2, 1, 6, 6, 2, 7], [2, 1, 5, 0, 3.674, 3.093], [5, 0, 6, 6, 3.674, 3.093], [6, 6, 2, 7, 3.674, 3.093], [2, 7, 2, 1, 3.674, 3.093]]

Anda mungkin menganggap itu

  • tidak ada kasus tepi di mana garis melintasi persimpangan tetapi tidak ada garis, seperti

    [[0, 9, 1, 8], [1, 8, 2, 9], [2, 9, 3, 8], [3, 8, 4, 9], [4, 9, 0, 9]]
    
  • tidak ada sudut lebih dari 179 derajat, seperti

    [[0, 0, 0, 1], [0, 1, 0, 2], [0, 2, 0, 0]]
    

Aturan

  • Anda dapat menggunakan bahasa apa pun yang Anda inginkan.
  • Tidak ada sumber daya eksternal yang harus digunakan.
  • Celah standar berlaku.

Mencetak gol

Ini adalah , jadi jawaban tersingkat dalam byte menang.

PurkkaKoodari
sumber
Apakah cukup untuk mengidentifikasi 3-siklus atau apakah kita harus menangani kasus tepi yang lebih kompleks? Misalnya "pentagon" yang didefinisikan oleh [0,9],[1,8],[2,9],[3,8],[4,9]sebenarnya adalah W dengan garis yang ditarik di bagian atas. Apakah itu tidak ada segitiga atau 2 segitiga?
Level River St
@steveverrill Katakanlah kasus tepi dapat diabaikan.
PurkkaKoodari
Baik. Dan Dan [0,0],[1,0],[2,0],[1,2]"segi empat" dengan satu sudut 180 derajat. Tidak ada segitiga atau 1 segitiga?
Level River St
Itu tidak akan menjadi segitiga, tetapi Anda mungkin menganggap itu tidak datang juga.
PurkkaKoodari

Jawaban:

1

PostGIS, 162

Saya pikir ini sesuai dengan aturan, Ini adalah permintaan untuk PostGIS, yang merupakan perpanjangan dari PostgreSQL. Input diasumsikan sebagai tabel koordinat untuk setiap baris yang disebut L Output adalah seperangkat baris dengan definisi poligon untuk segitiga yang dibentuk.

SELECT ST_AsText(D)FROM(SELECT(ST_Dump(ST_Polygonize(B))).geom D FROM(SELECT ST_Union(ST_MakeLine(ST_Point(A,B),ST_Point(C,D)))B FROM L)A)B WHERE ST_NPoints(D)=4;

Dalam penggunaannya terlihat seperti berikut ini

-- Create a table for the input
CREATE TABLE L (A INT, B INT, C INT,D INT);
INSERT INTO L VALUES(2, 1, 5, 0), (2, 1, 2, 7), (5, 0, 6, 6), (5, 0, 2, 7), (6, 6, 2, 1), (2, 7, 6, 6);

SELECT ST_AsText(D)FROM(SELECT(ST_Dump(ST_Polygonize(B))).geom D FROM(SELECT ST_Union(ST_MakeLine(ST_Point(A,B),ST_Point(C,D)))B FROM L)A)B WHERE ST_NPoints(D)=4;

-- Cleanup
DROP TABLE L;

Outputnya adalah sebagai berikut

POLYGON((5 0,2 1,3.67441860465116 3.09302325581395,5 0))
POLYGON((6 6,5 0,3.67441860465116 3.09302325581395,6 6))
POLYGON((3.67441860465116 3.09302325581395,2 7,6 6,3.67441860465116 3.09302325581395))
POLYGON((2 7,3.67441860465116 3.09302325581395,2 1,2 7))
MickyT
sumber
7

Mathematica 915 395 401 405

Memperbarui

Tantangan pemrograman ini jauh lebih sulit daripada yang pertama kali terlihat.

Pendekatan saat ini bekerja dengan kasus-kasus sederhana, di mana hanya ada satu persimpangan sepanjang panjang segmen garis apa pun. Dengan beberapa persimpangan sepanjang satu segmen, perlu untuk melacak semua titik persimpangan di sepanjang setiap garis dan membuat sub-segmen baru (karenanya tepi grafik tambahan) yang menghubungkan persimpangan baru ke semua titik persimpangan di sepanjang garis target.

Terlepas dari keterbatasan itu, mungkin ada baiknya berbagi logika yang mendasari pendekatan saat ini.


Segmen jalur input diperlakukan sebagai daerah. Jika mereka berpotongan, pusat massa akan menjadi koordinat persimpangan. Kita perlu menghilangkan persimpangan yang terjadi pada simpul segmen garis. Garis-garis yang tidak berpotongan akan memiliki pusat massa tak tentu.

Empat tepi baru dibuat untuk setiap titik persimpangan. Mereka menghubungkan titik persimpangan ke empat simpul dari dua garis berpotongan.

Grafik seperti yang di bawah ini di sebelah kanan dihasilkan menggunakan kedua sisi yang lama dan yang baru.

Verteks adalah koordinat dari masing-masing titik. Siklus, yaitu, loop tertutup dari tiga simpul akan menjadi segitiga asalkan ketiga simpul tersebut tidak collinear.

Saat ini kami memeriksa untuk melihat apakah "segitiga" memiliki area yang tidak ditentukan. (Untuk beberapa alasan itu tidak mengembalikan area 0 untuk tiga titik collinear.)


Contoh sederhana

Di bawah ini adalah (a) gambar yang diplot dalam bidang koordinat dan (b) grafik yang menunjukkan node yang diberikan serta simpul persimpangan {114/23, 314/69},. Dalam yang terakhir, simpul tidak terletak di koordinat Cartesius masing-masing.

Mungkin terlihat bahwa ujungnya lebih banyak di kanan daripada di kiri. Tetapi ingat bahwa ada sisi grafik yang tumpang tindih di sebelah kiri. Setiap diagonal sebenarnya sesuai dengan 3 tepi grafik!


grafik

    f@w_ :=(h@{a_, b_, c_, d_} := (r = RegionCentroid@RegionIntersection[Line@{a, b}, Line@{c, d}];
     {r <-> a, r <-> b, r <-> c, r <-> d});
      Cases[FindCycle[Graph[Union@Join[w /. {{a_, b_Integer}, {c_, d_}} :> {a, b} <-> {c, d},
      Cases[Flatten[h /@ Cases[{Length[Union@#] < 4, #} & /@ (FlattenAt[#, {{1}, {2}}] & /@ 
      Subsets[w, {2}]),{False, c_} :> c]], Except[{Indeterminate, _} <-> _]]]], {3}, 50],
      x_ /; NumericQ[RegionMeasure@Triangle[x[[All, 1]]]]][[All, All, 1]]//N//Grid)

Setiap baris di bawah adalah segitiga.

f[{{{2,8},{8,1}},{{0,4},{8,1}},{{0,4},{9,5}},{{8,1},{9,5}},{{2,8},{0,4}},{{9,5},{2,8}}}]

coords


Contoh yang lebih kompleks

f@{{{9, 5}, {0, -10}}, {{9, 5}, {0, 2}},  {{9, 5}, {2, -1}}, {{0, -10}, {2, -1}}, {{0, -10}, {-2, -1}}, {{-9, 5}, {0, -10}}, {{-9, 5}, {0, 2}}, {{-9, 5}, {-2, -1}}, {{0, 2}, {0, -10}}, {{-9, 5}, {2, -1}}, {{9, 5}, {-2, -1}}, {{-9, 5}, {9, 5}}}

Inilah grafik yang sesuai dengan koordinat input . Verteks berada pada koordinat Kartesius yang diharapkan. (Jika Anda menjalankan kode golf itu akan menampilkan simpul di tempat lain sambil menghormati label dan tepi simpul. Untuk keterbacaan, saya menetapkan koordinat titik menggunakan segelintir kode tambahan, tidak perlu untuk solusi.)

grafik2


Inilah grafik yang diturunkan.
Ini mencakup titik potong persimpangan (0,1/11), tempat beberapa garis input bersilangan.

sembilan belas

Kode menemukan 19 segitiga. Sembilan dari mereka memiliki titik, (0,1/11)sebagai salah satu simpul.

sembilan belas2

DavidC
sumber
Baik. Sekarang dalam bentuk fungsi.
DavidC
4

Java, 1051 1004

(Program sepenuhnya bekerja)

Saya pikir ini adalah tantangan yang bagus bukan hanya untuk bermain golf beberapa kode, tetapi terutama untuk berlatih menulis fungsi matematika.

Dan untuk menggambar "garis dasar" saya membuatnya di Jawa * Menunggu semua orang mulai tertawa * .

Kode

import java.util.*;class P{double x,y;static P l(double... i){double a=i[0],b=i[1],c=i[2],d=i[3],e=i[4],f=i[5],k,l,x=(k=i[7]-f)*(c-a)-(l=i[6]-e)*(d-b),n=(l*(b-f)-k*(a-e))/x,m=((c-a)*(b-f)-(d-b)*(a-e))/x;P p=new P();p.x=a+n*(c-a);p.y=b+n*(d-b);return(n>=0&n<=1&m>=0&m<=1&x!=0)?p:null;}public static void main(String[]p){Set<String>v=new HashSet();P q,w,e;Integer a,b,c,d,k,f,g,h,i,j,m,l,r,t,y,z;int[][]x=new int[l=p.length/4][4];for(c=0;c<l;c++){for(d=0;d<4;){x[c][d]=l.parseInt(p[c*4+d++]);}}z=x.length;for(r=0;r<z;r++){a=x[r][0];b=x[r][1];c=x[r][2];d=x[r][3];for(t=0;t<z;t++){if(t!=r){k=x[t][0];f=x[t][1];g=x[t][2];h=x[t][3];q=l(a,b,c,d,k,f,g,h);if(q!=null){for(y=0;y<z;y++){if(y!=r&y!=t){i=x[y][0];j=x[y][1];m=x[y][2];l=x[y][3];w=l(a,b,c,d,i,j,m,l);e=l(k,f,g,h,i,j,m,l);if(w!=null&&e!=null&&q.x!=e.x&q.y!=e.y&!v.contains(""+r+y+t)){v.add(""+r+t+y);v.add(""+r+y+t);v.add(""+t+r+y);v.add(""+t+y+r);v.add(""+y+r+t);v.add(""+y+t+r);System.out.printf("%s %s %s %s %s %s\n",q.x,q.y,w.x,w.y,e.x,e.y);}}}}}}}}}

Memasukkan

Bilangan bulat yang dipisahkan ruang. Berpasangan 4 (x1, y1, x2, y2)

2 1 5 0 2 1 2 7 5 0 6 6 5 0 2 7 6 6 2 1 2 7 6 6

Output (output nyata tidak membulatkan ke 3 desimal)

Setiap garis mengandung satu segitiga. Setiap garis terdiri dari titik-titik mengambang yang dipisahkan ruang dalam pasangan 2 (x1, y1, x2, y2, x3, y3). (Catatan: urutan 3 titik yang membentuk segitiga tidak terdefinisi.)

5.0 0.0 2.0 1.0 6.0 6.0
5.0 0.0 2.0 1.0 2.0 7.0
5.0 0.0 2.0 1.0 3.674 3.093
2.0 7.0 2.0 1.0 3.674 3.093
2.0 1.0 2.0 7.0 6.0 6.0
5.0 0.0 6.0 6.0 3.674 3.093
5.0 0.0 6.0 6.0 2.0 7.0
3.674 3.093 2.0 7.0 6.0 6.0

Penjelasan

Saya mulai menulis metode untuk menemukan persimpangan antara dua baris yang tidak terbatas. Metode yang dihasilkan adalah untuk gaya Java yang cukup pendek (246). Alih-alih membiarkan input metode terdiri dari 8 Poin ganda atau dua (P) saya memilih untuk menggunakan parameter arbitrer untuk mengamankan sejumlah besar karakter. Untuk meminimalkan penggunaan array operator setiap parameter yang digunakan lebih dari 2 kali ditempatkan dalam variabel itu sendiri.

static P l(double... i){double a=i[0],b=i[1],c=i[2],d=i[3],e=i[4],f=i[5],k,l,x=(k=i[7]-f)*(c-a)-(l=i[6]-e)*(d-b),n=(l*(b-f)-k*(a-e))/x,m=((c-a)*(b-f)-(d-b)*(a-e))/x;P p=new P();p.x=a+n*(c-a);p.y=b+n*(d-b);return(n>=0&n<=1&m>=0&m<=1&x!=0)?p:null;}

Penjelasan lebih lanjut untuk ditambahkan ... (jawaban ini mungkin dapat golf lebih)

Rolf ツ
sumber
0

BBC BASIC

Emulator di http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

Saya berharap ini golf turun ke 400-an.

Input output

Setiap kali pengguna memasuki baris baru, program memeriksa apakah ada segitiga baru yang telah dibuat, dan langsung mengeluarkannya, lihat di bawah.

Segitiga baru dibuat di mana pun garis baru berpotongan dengan dua garis yang sudah ada sebelumnya yang juga saling berpotongan (kecuali ketika ketiga garis berpotongan pada suatu titik, yang merupakan kasus khusus yang harus ditangani.)

masukkan deskripsi gambar di sini

Kode

Program utama adalah sesederhana mungkin. Pada akhirnya adalah fungsi, yang melakukan tugas kompleks mendeteksi persimpangan, sesuai dengan rumus di http://en.wikipedia.org/wiki/Line%E2%80809393_line_intersection

Fungsi mengembalikan nol jika tidak ada persimpangan dan nomor titik mengambang bukan nol jika ada. Ini juga memiliki efek samping: koordinat persimpangan ditambahkan ke string z $. Selain itu, di BBC basic variabel fungsi dapat dilihat oleh program utama asalkan program utama tidak memiliki variabel dengan nama yang sama (bahkan setelah fungsi selesai.)

Oleh karena itu program utama memiliki akses ke variabel xdan y, dan mdan n, yang menyimpan koordinat persimpangan saat ini dan sebelumnya. Ini digunakan untuk mendeteksi jika kita benar-benar menemukan segitiga dan bukan hanya tiga garis berpotongan pada suatu titik.

  DIM a(99),b(99),c(99),d(99)                                                    :REM declare 4 arrays to hold the ata
  y=0                                                                            :REM x and y are only initialized
  x=0                                                                            :REM to avoid a no such varialbe error later
  FOR i=0 TO 99                                                                  :REM for each input line
    INPUT a(i),b(i),c(i),d(i)
    FOR j=0 TO i-1                                                               :REM iterate through all combinations of 2 previous lines
      FOR k=0 TO j-1
        z$=""                                                                    :REM clear z$, three function calls on next line will write the triangle (if found) to it
        IF i>j AND j>k AND FNf(i,j)*FNf(i,k)*FNf(j,k)<>0 IF x<>m OR y<>n PRINT z$:REM to avoid printing the same triangle twice, print only if j,k,i in lexicographic order. Also reject if x,y (3rd FNf call) and m,n (2nd FNf call) are the same: this means a point, not a triangle.
      NEXT
    NEXT
  NEXT

  DEF FNf(g,h)                                                                   :REM returns zero if no intersection found, otherwise a floating point value
  m=x                                                                            :REM backup previous x and y
  n=y                                                                            :REM to use in test for point versus triangle
  p=a(g)-c(g)
  q=b(g)-d(g)
  r=a(h)-c(h)
  s=b(h)-d(h)
  t=a(g)*d(g)-b(g)*c(g)
  u=a(h)*d(h)-b(h)*c(h)
  e=p*s-q*r                                                                      :REM following method in wikipedia, calculate denominator of expression
  IF e<>0 x=(t*r-u*p)/e : y=(t*s-u*q)/e: z$=z$+" "+STR$(x)+" "+STR$(y)           :REM if denominator not zero, calculate x and y and append a string copy to z$
  IF (a(g)-x)*(c(g)-x)>0 OR (b(g)-y)*(d(g)-x)>0 OR(a(h)-x)*(c(h)-x)>0 OR(b(h)-y)*(d(h)-y)>0 e=0
  =e          :REM return e                                                      :REM previous line sets e to zero if the intersection falls outside the line segment. This is detected when both are on the same side of the intersection, which yields a positive multiplication result.
Level River St
sumber