识别三角形


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个三角形?
水平河圣

@steveverrill假设边缘情况可以忽略。
PurkkaKoodari 2015年

好。并且[0,0],[1,0],[2,0],[1,2]具有180度的一个角度的“四边形”。没有三角形或1个三角形?
水平河圣

那不是一个三角形,但您可能会认为也不会出现。
PurkkaKoodari 2015年

Answers:


1

PostGIS,162

我认为这符合规则,这是对PostGIS的查询,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

数学915 395 401 405

更新资料

这个编程挑战比最初看起来要困难得多。

本方法适用于简单情况,其中沿任何线段的长度只有一个相交。如果沿着一个线段有多个交叉点,则必须跟踪每条线上的所有交叉点,并创建新的子段(因此需要附加图形边缘),以将新的交叉点与沿目标线的所有交叉点相连。

尽管有这些限制,还是值得分享当前方法的基础逻辑。


输入线段被视为区域。如果它们相交,质心将是相交的坐标。我们需要消除在线段的顶点处发生的那些相交。不相交的线将具有不确定的质心。

为每个相交点创建四个新边。它们将相交点连接到两条相交线的四个顶点。

使用旧边缘和新边缘都会生成诸如右下方的图形。

顶点是各个点的坐标。如果三个顶点不共线,则循环(即三个顶点的闭环)将为三角形。

目前,我们检查是否有任何“三角形”具有不确定的区域。(由于某种原因,它对于三个共线点不会返回0的面积。)


一个简单的例子

下面是(a)在坐标平面上绘制的图,以及(b)显示给定节点以及相交节点的图{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}}}]

座标


一个更复杂的例子

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

这是与输入坐标对应的图形。顶点位于其预期的直角坐标上。(如果运行高尔夫球代码,则将在尊重顶点标签和边缘的情况下在其他位置显示顶点。为便于阅读,我使用少量附加代码分配了顶点坐标,这对于解决方案不是必需的。)

图2


这是派生图。
它包括派生的相交点(0,1/11),其中一些输入线相交。

十九

该代码找到了19个三角形。其中有9 (0,1/11)个是顶点,是顶点之一。

nineteen2


好。现在是函数形式。
DavidC

4

爪哇,1051 1004

(完整的工作程序)

我认为这不仅是挑战一些代码,而且主要是练习编写数学函数,这是一个不错的挑战。

为了画出一个“基线”,我用Java 编写了这个“基线”,等着大家开始大笑

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个双精度点或2个点(P)。为了最大程度地减少数组运算符的使用,每个使用两次以上的参数都放在其自己的变量中。

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

要添加更多的解释...(这个答案可能会打得更多)


0

英国广播公司

模拟器位于http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

我希望它能打入400年代。

输入输出

每次用户输入新行时,程序都会检查是否创建了任何新三角形,并立即将其输出,请参见下文。

每当新线与两条也彼此相交的现有线相交时,就会创建一个新三角形(除非所有三条线在一个点处相交,这是必须处理的特殊情况)。

在此处输入图片说明

主程序尽可能简单。最后一个功能是根据http://en.wikipedia.org/wiki/Line%E2%80%93line_intersection中的公式执行检测交点的复杂任务

如果没有交集,则函数返回零;如果存在,则函数返回非零浮点数。它还有一个副作用:交点的坐标被附加到字符串z $上。此外,在BBC基本版中,只要主程序没有相同名称的变量(即使在函数完成之后),该函数的变量也对主程序可见。

因此,主程序可以访问变量xy,和mn,它们存储当前交叉点和先前交叉点的坐标。这用于检测我们是否真的找到了三角形,而不仅仅是发现在一条线处相交的三条线。

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