三个点内形成三角形的整数点数是多少?

时间:2009-06-26 14:21:21

标签: algorithm geometry

实际上,这是一个典型的问题,因为SO用户Victor将其置于(另一个SO question中,询问在面试期间要询问的任务)。

我不能在一小时内完成(叹气)所以计算三角形内整数点数的算法是什么?

编辑:假设顶点位于整数坐标处。 (否则,它会成为找到三角形内所有点然后减去所有浮点的问题,只留下整数点;这是一个不太优雅的问题。)

13 个答案:

答案 0 :(得分:36)

假设顶点位于整数坐标处,您可以通过在三角形周围构建一个矩形来获得答案,如Kyle Schultz的An Investigation of Pick's Theorem中所述。

对于j x k 矩形,内部点数为

I = (j – 1)(k – 1).

对于下面的5 x 3矩形,有8个内部点。

alt text
(来源:uga.edu

对于具有垂直腿(j)和水平腿(k)的三角形,内部点的数量由

给出
I = ((j – 1)(k – 1) - h) / 2

其中h是矩形内部与三角形的斜边(而不是长度)重合的点数。

alt text
(来源:uga.edu

对于具有垂直边或水平边的三角形,内部点(I)的数量由

给出

alt text
(来源:uga.edu

其中j,k,h1,h2和b在下图中标记

alt text
(来源:uga.edu

最后,没有垂直或水平边的三角形的情况可以分成两个子情况,一个是三角形周围的区域形成三个三角形,另一个是周围区域形成三个三角形和一个矩形(见下图)。

第一个子案例中的内部点数(I)由

给出

alt text
(来源:uga.edu

其中所有变量都标记在下图中

alt text
(来源:uga.edu

第二个子案例中的内部点数(I)由

给出

alt text
(来源:uga.edu

其中所有变量都标记在下图中

alt text
(来源:uga.edu

答案 1 :(得分:13)

Pick定理(http://en.wikipedia.org/wiki/Pick%27s_theorem)表明放置在整数点上的简单多边形的表面由下式给出:

A = i + b/2 - 1

这里A是三角形的表面,i是内部点的数量,b是边界点的数量。通过对每条线的斜率的最大公约数求和,可以很容易地计算出边界点b的数量:

b =   gcd(abs(p0x - p1x), abs(p0y - p1y)) 
    + gcd(abs(p1x - p2x), abs(p1y - p2y)) 
    + gcd(abs(p2x - p0x), abs(p2y - p0y))

也可以计算表面。对于计算表面的公式,请参阅https://stackoverflow.com/a/14382692/2491535。结合这些已知值,我可以通过以下方式计算:

i = A + 1 - b/2

答案 2 :(得分:11)

我的下意识反应是蛮力:

  • 查找x和y方向上三角形的最大和最小范围。
  • 遍历这些范围内的所有整数点组合。
  • 对于每组点,使用标准测试之一(例如Same side or Barycentric techniques)来查看该点是否位于三角形内。由于此类计算是用于检测光线/线段和三角形之间的交叉点的算法的组成部分,因此您还可以检查this link以获取更多信息。

答案 3 :(得分:5)

好的我会提出一种算法,它不会很棒,但它会起作用。

首先,我们需要一个三角形测试点。我建议使用这篇优秀文章中所解释的“重心技术”:

http://www.blackpawn.com/texts/pointinpoly/default.html

现在算法:

  1. let(x1,y1)(x2,y2)(x3,y3)为三角形顶点

  2. 让ymin = floor(min(y1,y2,y3))ymax = ceiling(max(y1,y2,y3))xmin = floor(min(x1,x2,x3))ymax = ceiling(最大值(X1,x2,3))

  3. 从xmin迭代到xmax,ymin到ymax,你可以枚举包含三角形的矩形区域中的所有整数点

  4. 使用三角形测试点,您可以测试枚举中的每个点,看它是否在三角形上。

  5. 这很简单,我认为它可以在不到半小时内完成编程。

答案 4 :(得分:5)

这被称为“三角形点”测试。

以下是一篇文章,其中包含多个此问题的解决方案:Point in the Triangle Test

alt text

检查点是否在三角形中的常用方法是找到将点连接到每个三角形的三个顶点的矢量,并将与角度相加向量。如果角度之和为 2 * pi (360度),则该点在<三角形内,否则不是。

答案 5 :(得分:1)

对于非暴力方法,我只有半个答案。如果顶点是整数,则可以减小它以确定如何找到边相交的整数点数。使用该数字和三角形的面积(Heron公式),您可以使用Pick's定理来查找内部整数点的数量。

编辑:对于另一半,找到与边相交的整数点,我怀疑它是点之间的x和y之差减去1之间的最大公分母,或者如果距离减去1,如果其中一个x或y差异为零。

答案 6 :(得分:1)

这是另一种方法,不一定是最好的方法,但肯定会给任何面试官留下深刻印象。

首先,调用具有最低X co-ord'L'的点,具有最高X co-ord'R'的点和剩余的点'M'(左,右和中)。

然后,设置两个Bresenham线算法实例。参数化一个实例从L到R绘制,第二个从L到M绘制。同时运行算法X = X [L]到X [M]。但是,不是绘制任何线条或打开任何像素,而是计算线条之间的像素。

从X [L]步进到X [M]后,将第二个Bresenham的参数改为从M到R,然后继续同时运行算法,X = X [M]到X [R]。

这与Erwin Smout 7小时前提出的解决方案非常相似,但是使用Bresenham而不是线斜率公式。

我认为,为了计算像素列,你需要确定M是否位于LR线的上方或下方,当然,当两个点具有相同的X或Y坐标时,会出现特殊情况。但到了这个时候,你的面试官会非常敬畏,你可以继续讨论下一个问题。

答案 7 :(得分:0)

快速n'dirty伪代码:

-- Declare triangle
p1 2DPoint = (x1, y1);
p2 2DPoint = (x2, y2);
p3 2DPoint = (x3, y3);
triangle [2DPoint] := [p1, p2, p3];

-- Bounding box
xmin float = min(triangle[][0]);
xmax float = max(triangle[][0]);
ymin float = min(triangle[][1]);
ymax float = max(triangle[][1]);

result [[float]];

-- Points in bounding box might be inside the triangle
for x in xmin .. xmax {
  for y in ymin .. ymax {
    if a line starting in (x, y) and going in any direction crosses one, and only one, of the lines between the points in the triangle, or hits exactly one of the corners of the triangle {
      result[result.count] = (x, y);
    }
  }
}

答案 8 :(得分:0)

我有这个想法 -

设A(x1,y1),B(x2,y2)和C(x3,y3)是三角形的顶点。设'count'是形成三角形的整数点数。

如果我们需要三角形边上的点然后使用欧几里德距离公式http://en.wikipedia.org/wiki/Euclidean_distance,则可以确定所有三个边的长度。 所有三个边的总长度 - 3,都可以计算出来。

要找到三角形内部的点数,我们需要使用三角形填充算法,而不是执行实际渲染,即执行drawpixel(x,y),只需通过循环并继续更新计数,因为我们循环。

中的三角形填充算法
  

计算机图形学基础   Peter Shirley,Michael Ashikhmin

应该有所帮助。它在这里提到http://www.gidforums.com/t-20838.html

欢呼声

答案 9 :(得分:0)

我会这样:

取三角形的最高点(Y坐标最高的那个)。从那时起有两个“斜坡”。这不是一般的解决方案,但为了便于可视化,可以考虑“向左移动”(减少x坐标)和另一个“向右移动”。

从这两个斜率和任何小于最高点的给定Y坐标,您应该能够计算斜率设置的边界内出现的整数点数。迭代减少Y坐标,将所有这些点数加在一起。

当您的Y坐标减少到达三角形的第二高点时停止。

你现在已经计算了所有积分“高于第二高点”,你现在留下了“计算一些(小得多!!!)三角形中的所有点的问题,你知道它的上层侧面平行于X轴。

重复相同的步骤,但现在采用“最左边的点”而不是“最上面的”,然后执行“通过增加x”,而不是“减少y”。

之后,你会遇到计算a,再次小得多的三角形中的所有整数点的问题,你知道它的上边与X轴平行,而它的左侧与Y-平行。轴。

不断重复(重复),直到你没有计算剩下的三角形中的点数。

(我现在为你做作业吗?)

答案 10 :(得分:0)

(wierd)伪代码,比蛮力更强(它应该有O(n))
我希望你明白我的意思

n=0
p1,p2,p3 = order points by xcoordinate(p1,p2,p3)
for int i between p1.x and p2.x do
  a = (intersection point of the line p1-p2 and the line with x==i).y 
  b = (intersection point of the line p1-p3 and the line with x==i).y
  n += number of integers between floats (a, b)
end
for i between p2.x+1 and p3.x do
  a = (intersection point of the line p2-p3 and the line with x==i).y 
  b = (intersection point of the line p1-p3 and the line with x==i).y
  n += number of integers between floats (a, b)
end

这个算法很容易扩展为float类型的顶点(只需要在“for i ..”部分有一些回合,p2.x的特殊情况是整数(那里,向下舍入=向上舍入))
并且在实际实现中有一些优化的机会

答案 11 :(得分:0)

我找到了一个非常有用的链接,清楚地解释了这个问题的解决方案。我在坐标几何方面很弱,所以我使用这个解决方案并用Java编写它可以工作(至少对于我试过的测试用例..)

http://mathforum.org/library/drmath/view/55169.html

public int points(int[][] vertices){
      int interiorPoints = 0;
      double triangleArea = 0;
      int x1 = vertices[0][0], x2 = vertices[1][0], x3 = vertices[2][0];
      int y1 = vertices[0][1], y2 = vertices[1][1], y3 = vertices[2][1];

      triangleArea = Math.abs(((x1-x2)*(y1+y2))                             
                + ((x2-x3)*(y2+y3))
                + ((x3-x1)*(y3+y1)));

      triangleArea /=2;
      triangleArea++;

      interiorPoints = Math.abs(gcd(x1-x2,y1-y2))
                + Math.abs(gcd(x2-x3, y2-y3))
                + Math.abs(gcd(x3-x1, y3-y1));

      interiorPoints /=2;

      return  (int)(triangleArea - interiorPoints);
 }

答案 12 :(得分:0)

这是@Prabhala's solution的Python实现:

from collections import namedtuple
from fractions import gcd


def get_points(vertices):
    Point = namedtuple('Point', 'x,y')
    vertices = [Point(x, y) for x, y in vertices]

    a, b, c = vertices

    triangle_area = abs((a.x - b.x) * (a.y + b.y) + (b.x - c.x) * (b.y + c.y) + (c.x - a.x) * (c.y + a.y))
    triangle_area /= 2
    triangle_area += 1

    interior = abs(gcd(a.x - b.x, a.y - b.y)) + abs(gcd(b.x - c.x, b.y - c.y)) + abs(gcd(c.x - a.x, c.y - a.y))
    interior /= 2

    return triangle_area - interior

用法:

print(get_points([(-1, -1), (1, 0), (0, 1)]))  # 1
print(get_points([[2, 3], [6, 9], [10, 160]]))  # 289