找到固定大小圆圈中包含的最多点

时间:2010-01-27 23:32:27

标签: algorithm optimization geometry

当一位朋友谈到编程竞赛时,我们想到了最好的方法是什么:

给定一个点列表,找到覆盖最多点的预定大小的圆心。如果有几个这样的圈子,唯一重要的是找到其中一个。

示例输入:1000点,500x500空间和60直径圆。

4 个答案:

答案 0 :(得分:4)

除非我错过了一些明显的东西,否则我认为有一个简单的答案。

对于矩形区域MxN,点数P,半径R:

  • 将MxN区域的地图(例如int的2D数组)初始化为全零
  • 对于每个P点
    • 将半径R内的所有地图点增加1
  • 查找具有最大值的地图元素 - 这将是您要查找的圆圈的中心

这是O(P),假设P是感兴趣的变量。

答案 1 :(得分:2)

非常快速的想法,不一定是正确的:

  • 对于每个点P,您计算一个“候选覆盖区域” - 一个连续的点,其覆盖圆的中心可能是。当然,它也是直径为D的圆,其中心为P。
  • 对于每个点P,您将其候选覆盖区域与其他点的相应区域相交。一些候选覆盖区域可以与P交叉并且彼此交叉。对于每个交叉点,您可以计算相交区域的数量。与大多数候选区域相交的图形是覆盖圆的中心的候选区域,其覆盖P和尽可能多的其他点。
  • 找到交叉点数最多的候选区域

似乎是N ^ 2的复杂性,只要计算圆形区域的交点很容易

答案 2 :(得分:2)

到目前为止,我最好的方法是:

每个包含点的圆都必须有一个最左边的点。因此,它会列出一个可能位于圆的范围内的点右侧的所有点。它首先按x对点进行排序,以使扫描更加清晰。

然后它再次对它们进行排序,这次是它们所拥有的邻居的数量,以便首先检查具有最多邻居的点。

然后检查每个点,并且对于右边的每个点,它计算一个圆,其中这对点位于左边界。然后计算这样一个圆圈内的点数。

由于这些点已按潜力分类,因此一旦认为可能导致更好解决方案的所有节点,它就可以提前退出。

import random, math, time
from Tkinter import * # our UI

def sqr(x):
    return x*x

class Point:
    def __init__(self,x,y):
        self.x = float(x)
        self.y = float(y)
        self.left = 0
        self.right = []
    def __repr__(self):
        return "("+str(self.x)+","+str(self.y)+")"
    def distance(self,other):
        return math.sqrt(sqr(self.x-other.x)+sqr(self.y-other.y))

def equidist(left,right,dist):
    u = (right.x-left.x)
    v = (right.y-left.y)
    if 0 != u:
        r = math.sqrt(sqr(dist)-((sqr(u)+sqr(v))/4.))
        theta = math.atan(v/u)
        x = left.x+(u/2)-(r*math.sin(theta))
        if x < left.x:
            x = left.x+(u/2)+(r*math.sin(theta))
            y = left.y+(v/2)-(r*math.cos(theta))
        else:
            y = left.y+(v/2)+(r*math.cos(theta))
    else:
        theta = math.asin(v/(2*dist))
        x = left.x-(dist*math.cos(theta))
        y = left.y + (v/2)
    return Point(x,y)

class Vis:
    def __init__(self):
        self.frame = Frame(root)
        self.canvas = Canvas(self.frame,bg="white",width=width,height=height)
        self.canvas.pack()
        self.frame.pack()
        self.run()
    def run(self):
        self.count_calc0 = 0
        self.count_calc1 = 0
        self.count_calc2 = 0
        self.count_calc3 = 0
        self.count_calc4 = 0
        self.count_calc5 = 0
        self.prev_x = 0
        self.best = -1
        self.best_centre = []
        for self.sweep in xrange(0,len(points)):
            self.count_calc0 += 1
            if len(points[self.sweep].right) <= self.best:
                break
            self.calc(points[self.sweep])
        self.sweep = len(points) # so that draw() stops highlighting it
        print "BEST",self.best+1, self.best_centre # count left-most point too
        print "counts",self.count_calc0, self.count_calc1,self.count_calc2,self.count_calc3,self.count_calc4,self.count_calc5
        self.draw()
    def calc(self,p):
        for self.right in p.right:
            self.count_calc1 += 1
            if (self.right.left + len(self.right.right)) < self.best:
                # this can never help us
                continue
            self.count_calc2 += 1
            self.centre = equidist(p,self.right,radius)
            assert abs(self.centre.distance(p)-self.centre.distance(self.right)) < 1
            count = 0
            for p2 in p.right:
                self.count_calc3 += 1
                if self.centre.distance(p2) <= radius:
                    count += 1
            if self.best < count:
                self.count_calc4 += 4
                self.best = count
                self.best_centre = [self.centre]
            elif self.best == count:
                self.count_calc5 += 5
                self.best_centre.append(self.centre)
            self.draw()
            self.frame.update()
            time.sleep(0.1)
    def draw(self):
        self.canvas.delete(ALL)
        # draw best circle
        for best in self.best_centre:
            self.canvas.create_oval(best.x-radius,best.y-radius,\
                best.x+radius+1,best.y+radius+1,fill="red",\
                outline="red")
        # draw current circle
        if self.sweep < len(points):
            self.canvas.create_oval(self.centre.x-radius,self.centre.y-radius,\
                self.centre.x+radius+1,self.centre.y+radius+1,fill="pink",\
                outline="pink")
        # draw all the connections
        for p in points:
            for p2 in p.right:
                self.canvas.create_line(p.x,p.y,p2.x,p2.y,fill="lightGray")
        # plot visited points
        for i in xrange(0,self.sweep):
            p = points[i]
            self.canvas.create_line(p.x-2,p.y,p.x+3,p.y,fill="blue")
            self.canvas.create_line(p.x,p.y-2,p.x,p.y+3,fill="blue")
        # plot current point
        if self.sweep < len(points):
            p = points[self.sweep]
            self.canvas.create_line(p.x-2,p.y,p.x+3,p.y,fill="red")
            self.canvas.create_line(p.x,p.y-2,p.x,p.y+3,fill="red")
            self.canvas.create_line(p.x,p.y,self.right.x,self.right.y,fill="red")
            self.canvas.create_line(p.x,p.y,self.centre.x,self.centre.y,fill="cyan")
            self.canvas.create_line(self.right.x,self.right.y,self.centre.x,self.centre.y,fill="cyan")
        # plot unvisited points
        for i in xrange(self.sweep+1,len(points)):
            p = points[i]
            self.canvas.create_line(p.x-2,p.y,p.x+3,p.y,fill="green")
            self.canvas.create_line(p.x,p.y-2,p.x,p.y+3,fill="green")

radius = 60
diameter = radius*2
width = 800
height = 600

points = []

# make some points
for i in xrange(0,100):
    points.append(Point(random.randrange(width),random.randrange(height)))

# sort points for find-the-right sweep
points.sort(lambda a, b: int(a.x)-int(b.x))

# work out those points to the right of each point
for i in xrange(0,len(points)):
    p = points[i]
    for j in xrange(i+1,len(points)):
        p2 = points[j]
        if p2.x > (p.x+diameter):
            break
        if (abs(p.y-p2.y) <= diameter) and \
            p.distance(p2) < diameter:
            p.right.append(p2)
            p2.left += 1

# sort points in potential order for sweep, point with most right first
points.sort(lambda a, b: len(b.right)-len(a.right))

# debug
for p in points:
    print p, p.left, p.right

# show it
root = Tk()
vis = Vis()
root.mainloop()

答案 3 :(得分:0)

如何使用聚类算法识别点群集。然后确定具有最大点数的簇。取最大点为圆心的簇的平均点,然后绘制圆圈。

MATLAB支持implementationk-means algorithm,它会返回一个集群平均值和相应集群ID的二维数组(精确的矩阵)。

k-means的一个众所周知的反面是在手之前决定k(簇的数量)。这可以解决 - 人们可以从数据点学习k的值。请查看此paper

我希望这会有所帮助。

欢呼声