ระบุรูปสามเหลี่ยม


11

การนับจำนวนสามเหลี่ยมในรูปภาพเป็นงานที่ใช้กันทั่วไปในการทดสอบสมอง คุณจะได้รับรูปภาพที่มีรูปร่างประกอบด้วยสามเหลี่ยม จากนั้นคุณจะต้องค้นหารูปสามเหลี่ยมที่เป็นไปได้ทั้งหมดในภาพ

งาน

คุณจะได้รับรายการบรรทัดในรูปแบบที่คุณเลือก จากนั้นคุณจะต้องแสดงรายการสามเหลี่ยมที่พบในนั้น

อินพุต

คุณจะได้รับรายชื่อของเส้นแต่ละเส้นกำหนดโดยพิกัดจำนวนเต็มสี่ตัว (เช่นx1 y1 x2 y2) คุณสามารถเลือกรูปแบบการป้อนข้อมูลตราบใดที่มีการบันทึกไว้อย่างชัดเจน ตัวอย่าง:

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

นี่คืออินพุตเดียวกันกับภาพ:

การวาดรูปสามเหลี่ยม

อีกอันหนึ่งพร้อมทางแยก (ในรูปแบบเดียวเท่านั้นเพื่อประหยัดพื้นที่):

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

การวาดรูปสามเหลี่ยม

เอาท์พุต

คุณต้องแสดงรายการของสามเหลี่ยมทั้งหมดซึ่งแต่ละอันกำหนดโดยพิกัดจุดลอยตัวหกจุด (เช่น. x1 y1 x2 y2 x3 y3) ในภาพที่ระบุโดยอินพุต สิ่งเหล่านี้อาจไม่ใช่จำนวนเต็มเนื่องจากเส้นอาจตัดกันที่จุดใดก็ได้ คุณสามารถเลือกรูปแบบผลลัพธ์ตราบใดที่มีการบันทึกไว้อย่างชัดเจน ตัวอย่างเอาต์พุตสำหรับอินพุตตัวอย่างด้านบน:

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

คุณอาจคิดว่า

  • ไม่มีกรณีขอบที่เส้นข้ามตัดกัน แต่ไม่มีบรรทัดใด ๆ เช่น

    [[0, 9, 1, 8], [1, 8, 2, 9], [2, 9, 3, 8], [3, 8, 4, 9], [4, 9, 0, 9]]
    
  • ไม่มีมุมใดเกิน 179 องศา

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

กฎระเบียบ

  • คุณสามารถใช้ภาษาใดก็ได้ที่คุณต้องการ
  • ไม่ต้องใช้ทรัพยากรภายนอก
  • ช่องโหว่มาตรฐานใช้

เกณฑ์การให้คะแนน

นี่คือดังนั้นคำตอบที่สั้นที่สุดในหน่วยไบต์ชนะ


มันเพียงพอแล้วหรือไม่ที่จะระบุ 3 รอบหรือเราต้องจัดการกับขอบที่ซับซ้อนมากขึ้น? ตัวอย่างเช่น "เพนตากอน" ที่กำหนดโดย[0,9],[1,8],[2,9],[3,8],[4,9]จริง ๆ แล้วเป็น W ที่มีเส้นลากผ่านด้านบน นั่นคือไม่มีสามเหลี่ยมหรือ 2 สามเหลี่ยม?
Level River St

@stververill สมมติว่ากรณีขอบสามารถละเว้นได้
PurkkaKoodari

ตกลง. และ[0,0],[1,0],[2,0],[1,2]A "รูปสี่เหลี่ยมสามมิติ" ที่มีมุมหนึ่ง 180 องศา ไม่มีรูปสามเหลี่ยมหรือสามเหลี่ยม 1 รูป?
เลเวลริเวอร์เซนต์

นั่นไม่ใช่สามเหลี่ยม แต่คุณอาจคิดว่ามันไม่ได้มาด้วย
PurkkaKoodari

คำตอบ:


1

PostGIS, 162

ฉันคิดว่าสิ่งนี้สอดคล้องกับกฎมันเป็นแบบสอบถามสำหรับ PostGIS ซึ่งเป็นส่วนเสริมของ PostgreSQL อินพุตถือว่าเป็นตารางพิกัดสำหรับแต่ละบรรทัดที่เรียกว่า L เอาต์พุตคือชุดของแถวที่มีคำจำกัดความรูปหลายเหลี่ยมสำหรับรูปสามเหลี่ยมที่เกิดขึ้น

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;

ในการใช้งานดูเหมือนว่าต่อไปนี้

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

ผลลัพธ์มีดังนี้

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

7

Mathematica 915 395 401 405

ปรับปรุง

ความท้าทายในการเขียนโปรแกรมนี้ยากกว่าที่คิดไว้ก่อนมาก

วิธีการปัจจุบันทำงานร่วมกับกรณีง่าย ๆ ที่มี แต่จุดตัดเดียวตามความยาวของส่วนของเส้นใด ๆ ด้วยการข้ามหลายจุดในส่วนเดียวจำเป็นต้องติดตามจุดตัดทั้งหมดตามแต่ละบรรทัดและสร้างกลุ่มย่อยใหม่ (ดังนั้นกราฟกราฟเพิ่มเติม) ที่เชื่อมต่อจุดตัดใหม่กับจุดแยกทั้งหมดตามแนวเป้าหมาย

แม้จะมีข้อ จำกัด นั้นมันอาจคุ้มค่าที่จะแบ่งปันตรรกะที่เป็นพื้นฐานของวิธีการในปัจจุบัน


เซ็กเมนต์บรรทัดอินพุตถูกใช้เป็นภูมิภาค ถ้าพวกมันตัดกันเซนทรอยด์จะเป็นพิกัดของจุดตัด เราจำเป็นต้องกำจัดจุดตัดเหล่านั้นที่เกิดขึ้นที่จุดยอดของส่วนของเส้น เส้นที่ไม่ตัดกันจะมีเซนทรอยด์ไม่แน่นอน

มีการสร้างขอบใหม่สี่จุดสำหรับแต่ละจุดแยก พวกเขาเชื่อมต่อจุดตัดกับจุดยอดทั้งสี่ของเส้นตัดกันทั้งสอง

กราฟเช่นด้านล่างด้านขวาจะถูกสร้างขึ้นโดยใช้ทั้งขอบเก่าและใหม่

จุดยอดคือพิกัดของจุดที่เกี่ยวข้อง วงรอบคือวงปิดของจุดยอดสามจุดจะเป็นรูปสามเหลี่ยมหากว่าจุดยอดทั้งสามนั้นไม่ใช่เส้นโค้ง

ขณะนี้เราตรวจสอบเพื่อดูว่า "สามเหลี่ยม" ใด ๆ มีพื้นที่ไม่แน่นอน (ด้วยเหตุผลบางอย่างมันไม่ส่งคืนพื้นที่ 0 สำหรับจุด collinear สามจุด)


ตัวอย่างง่ายๆ

ด้านล่างนี้คือ (ก) รูปที่พล็อตในระนาบและ (ข) {114/23, 314/69}ประสานงานกราฟแสดงโหนดที่กำหนดเช่นเดียวกับโหนดสี่แยก ในหลังจุดยอดไม่ได้อยู่ในพิกัดคาร์ทีเซียนที่เกี่ยวข้อง

อาจปรากฏว่ามีขอบในรูปด้านขวามากกว่าด้านซ้าย แต่อย่าลืมว่ามีขอบกราฟซ้อนทับอยู่ทางซ้าย แต่ละเส้นทแยงมุมสอดคล้องกับกราฟ 3 เส้น


กราฟ

    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)

แต่ละแถวด้านล่างเป็นรูปสามเหลี่ยม

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


ตัวอย่างที่ซับซ้อนมากขึ้น

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

นี่คือกราฟที่สอดคล้องกับพิกัดอินพุต จุดยอดอยู่ที่พิกัดคาร์ทีเซียนที่คาดหวัง (หากคุณเรียกใช้รหัส golfed มันจะแสดงจุดยอดที่อื่น ๆ ในขณะที่เคารพป้ายชื่อและขอบของจุดยอดเพื่อความสะดวกในการอ่านฉันได้กำหนดพิกัดจุดยอดโดยใช้รหัสเพิ่มเติมซึ่งไม่จำเป็นสำหรับการแก้ปัญหา)

กราฟ 2


นี่คือกราฟที่ได้มา
ซึ่งรวมถึงจุดตัดกัน(0,1/11)ที่ได้รับซึ่งบางส่วนของสายอินพุตข้าม

สิบเก้า

รหัสพบสามเหลี่ยม 19 อัน เก้าคนมีจุด(0,1/11)เป็นหนึ่งในจุดยอด

nineteen2


ตกลง. ขณะนี้อยู่ในรูปแบบของฟังก์ชัน
DavidC

4

Java, 1051 1004

(โปรแกรมทำงานอย่างเต็มที่)

ฉันคิดว่านี่เป็นความท้าทายที่ดีไม่เพียงแค่การเขียนโค้ดเท่านั้น แต่ส่วนใหญ่จะฝึกการเขียนฟังก์ชั่นทางคณิตศาสตร์

และการวาด "พื้นฐาน" ฉันทำอย่างใดอย่างหนึ่งในชวานี้* รอให้ทุกคนเริ่มหัวเราะ *

รหัส

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

อินพุต

จำนวนเต็มคั่นด้วยช่องว่าง ในคู่ที่ 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

เอาท์พุท (เอาท์พุทที่แท้จริงไม่ได้รอบถึง 3 ทศนิยม)

แต่ละบรรทัดมีหนึ่งสามเหลี่ยมแต่ละบรรทัดประกอบด้วยจุดลอยที่คั่นด้วยช่องว่างเป็นคู่ที่ 2 (x1, y1, x2, y2, x3, y3) (หมายเหตุ: ลำดับของ 3 จุดที่เป็นรูปสามเหลี่ยมนั้นไม่ได้กำหนด)

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

คำอธิบาย

ฉันเริ่มเขียนวิธีการค้นหาจุดตัดระหว่างสองบรรทัดที่ไม่มีที่สิ้นสุด วิธีการที่เกิดขึ้นสำหรับสไตล์ Java หนึ่งสั้นสวย (246) แทนที่จะปล่อยให้วิธีการป้อนข้อมูลประกอบด้วย 8 คะแนนสองเท่าหรือสองคะแนน (P) ฉันเลือกใช้พารามิเตอร์ที่กำหนดเองเพื่อให้ได้อักขระจำนวนมากอย่างปลอดภัย เพื่อลดการใช้ตัวดำเนินการอาร์เรย์แต่ละพารามิเตอร์ที่ใช้มากกว่า 2 ครั้งจะถูกวางไว้ในตัวแปรของตัวเอง

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;}

คำอธิบายเพิ่มเติมที่จะเพิ่ม ... (คำตอบนี้อาจจะ golfed มากยิ่งขึ้น)


0

BBC BASIC

โปรแกรมจำลองที่http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

ฉันคาดหวังว่าสิ่งนี้จะตีกอล์ฟในยุค 400

อินพุต / เอาต์พุต

ทุกครั้งที่ผู้ใช้เข้าสู่บรรทัดใหม่โปรแกรมจะตรวจสอบว่ามีสามเหลี่ยมใหม่ถูกสร้างขึ้นหรือไม่และส่งออกโดยทันทีดูด้านล่าง

สามเหลี่ยมใหม่จะถูกสร้างขึ้นทุกที่ที่บรรทัดใหม่ตัดกันด้วยสองบรรทัดที่มีอยู่ก่อนที่จะถูกตัดกันเช่นกัน (ยกเว้นเมื่อทั้งสามเส้นตัดกันที่จุดซึ่งเป็นกรณีพิเศษที่ต้องจัดการด้วย)

ป้อนคำอธิบายรูปภาพที่นี่

รหัส

โปรแกรมหลักเกี่ยวกับเรียบง่ายที่สุดเท่าที่จะทำได้ ในตอนท้ายคือฟังก์ชั่นที่ทำงานที่ซับซ้อนในการตรวจหาจุดแยกตามสูตรในhttp://en.wikipedia.org/wiki/Line%E2%80%93line_intersection

ฟังก์ชันจะคืนค่าศูนย์ถ้าไม่มีทางแยกและหมายเลขทศนิยมที่ไม่ใช่ศูนย์ถ้ามี นอกจากนี้ยังมีผลข้างเคียง: พิกัดของจุดตัดจะต่อท้ายสตริง z $ นอกจากนี้ใน BBC พื้นฐานตัวแปรของฟังก์ชั่นจะปรากฏให้เห็นในโปรแกรมหลักหากโปรแกรมหลักไม่มีตัวแปรที่มีชื่อเหมือนกัน (แม้หลังจากที่ฟังก์ชันเสร็จสิ้นแล้ว)

ดังนั้นโปรแกรมหลักมีการเข้าถึงตัวแปรxและyและmและnที่เก็บพิกัดของทางแยกปัจจุบันและก่อนหน้า สิ่งนี้ใช้เพื่อตรวจสอบว่าเราพบสามเหลี่ยมจริงๆหรือไม่และไม่ใช่แค่สามบรรทัดตัดกันที่จุด

  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.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.