以下是来自careercup.com的Google访谈部分的问题:
在2d空间中给出2 * n + 3个点,没有3个点共线且没有4个点位于圆上,
设计一种算法,找到一个圆圈,里面包含n个点,n在它外面,3个在它上面。
我可以想到一个O(n ^ 4)解决方案:
a)以[C(2n + 3,3)种方式]选择任意3个点并用这些(O(n ^ 3))制作一个圆圈
b)现在对于每个圆圈,检查其中是否有'n'个点位于O(n)
由于这是一种天真的方法,我想问一下是否有更好的方法 解决这个问题?即大约为O(n log n)
的东西答案 0 :(得分:7)
这是O(n)解决方案。
设S为点集。设p是S的最左边的点。然后p在S的凸包上。设q是S点最小化从p到左边的光线和从p开始并经过q的光线之间的角度。 p和q都可以在O(n)时间内找到。段pq是S的凸包边缘,S的任何一点都不在pq的左边。
取段pq的轴A.包含p和q的圆的中心正好是轴A上的点。对于A上的每个点c,令C(c; p,q)为以c为中心并包含p和q的圆。如果c是远离左边的A点,则C(c; p,q)内部没有S点。如果c是远离右边的A点,则C(c; p,q)具有除p,q之外的所有S的点(p和q在圆上)。如果我们从左向右移动c,则S的点逐个进入圆圈C(c; p,q)并且永不离开。所以介于两者之间,A上有一个点c,C(c; p,q)包含p,q和另外一个S点,并且内部恰好有n个其他点。
这可以在O(n logn)中清楚地找到:对于除p和q之外的S的每个点s,找到A上的点c,使得C(c; p,q)包含p,q和s。点c是轴A和段qs的轴的交点。请注意,当我们沿着A移动时,当我们经过c时,s进入了圆圈C(c; p,q)。通过增加x坐标对这些中心进行排序并取(n + 1)-st,因为这是S的正好n个点已经在C(c; p,q)内部并且p,q和另外一个点开启的点C(C; p,q)。要在O(n),you find the (n+1)-st without sorting them all中完成。
答案 1 :(得分:3)
另一种O(n)算法,但更简单。
这三点就是答案。
如下:并且因为角度C <角度A,因此圆圈外的C点(由A和x,y构成) 角度B>角度A,所以圆圈内的B点
答案 2 :(得分:0)
您可以考虑使用 QuadTree :Quadtree on wiki 然后使用此结构,您可以扫描每个节点的接近度。似乎有一个名为查询范围的函数。这个功能可以让你更精确,而不仅仅是彻底挑选我认为的圈子。
请注意,这不是解决方案,只是让您入门的想法。
答案 3 :(得分:0)
在理想条件下,当圆圈在外面有n个点,n在里面,3个在它上面时, 距正好 n点中心的距离将大于半径,正好 n小于半径..并且只有3点相等到半径..这将帮助您在特定瞬间快速查看外部/内部的数量。
在2D点上制作一个vornoi图,这有助于找到哪个点靠近哪个点。如果你需要知道这个概念,请搜索它。
首先从任意 3个相邻点制作一个圆圈。 将所有点的距离存储在一个数组中该圆的中心。距离大于R的点位于外侧,距离小于内侧。
逐步地,在圆上留一个点并选择一个最接近圆的边界的点,但在圆外面,以创建一个新的圆...然后重新计算数组。
如果我们将内部的点数称为Nin,将外部的数量称为Nout .. Nout将开始下降,Nin将开始缓慢增长。继续这样做,直到他们变得平等(如果是这样)或Nin超过Nout .. 如果发生这种情况,请通过在内选择点并在边界附近创建一个新圆圈。 继续这样做,直到你得到Nin == Nout,此时你有解决方案..
请注意,将新点移到圆外,会增加圆半径,并且在圆内取新点会减小半径。
这是我想到的第一个体面的解决方案。这可能需要在某些方面进行改进。我希望复杂性比蛮力解决方案好得多。我把计算留给你了。 :)
答案 4 :(得分:0)
在O(n)时间内解决此问题的简单方法是使用inversion in a circle。
一般大纲
从集合中选择任意点O(x,y)作为原点,并选择以所选点为中心的半径为r
的圆。如果所有点都以一定量(-x,-y)换算,那么计算将被简化,以便点O成为原点。
选择原点和圆后,对所选择的圆执行反转操作,直到该组中的所有其他点。在坐标中,我们将点P(x,y)映射到坐标为(x',y') = (x,y)*r^2/(x^2+y^2)
的新点P'。原点O(0,0)到达无穷远点。
通过反演转换,问题变为找到具有所需属性的线(通过两个转换点,因为一条线总是通过该点无穷大;此外,该线应将剩余的变换点分成两个相等的集合)。
请注意,反演将3个共线点和4个同心点的集合映射到3个共线点和4个同心点的集合上,因此在变换后的集合中我们没有3个共线点,也没有4个同心点。
为了构造圆,我们然后执行逆反演(与原始反演相同,因为圆中的反演是对合,自逆变换)。这会将线条拉回到通过第一个选定点的圆圈和原始集合中的另外两个点。其余一半在圆圈内,一半在外面。
与行相对应的问题
现在我们将问题减少到以下:在平面中给定2n + 2个点,找到一条穿过2个点的线,使得其余的2n个点,n在线的一侧,n是另一方面。
我们可以在O(n)时间内回答这个问题如下。首先,我们需要在集合的凸包边界中有一个点(我们不需要整个凸包)。我们可以通过找到坐标最小x值的集合中的点来找到这样的点P,这可以使用Quickselect算法在O(n)时间内完成。
现在我们知道我们所有的点都位于P右边的半空间中。下一步的想法是找到所有点的中间角相对于水平线h到P的点Q.同样,这可以使用Quickselect算法在O(n)时间内完成。不能有两个角度相同的点,因为不能有三个共线点。
线PQ通过其中两个点,并根据角度RPh是大于QPh还是小于QPh(负角度低于h)将剩余点R分成两个相等的部分。
可能会出现一个小问题:如果线穿过原点,则反转将其带到另一条线,而不是圆形。然而,这种情况不会出现,因为它意味着我们在原始集合中有3个共线点,保证不是问题陈述的情况。