【问题标题】:Find the most points enclosed in a fixed size circle找到固定大小的圆圈中的最多点
【发布时间】:2011-01-10 04:55:34
【问题描述】:

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

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

示例输入:1000 个点,在 500x500 的空间中,一个直径为 60 的圆。

【问题讨论】:

    标签: algorithm optimization geometry


    【解决方案1】:

    很快的想法,不一定是对的:

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

    似乎是 N^2 复杂度,前提是计算圆形区域的交点很容易

    【讨论】:

    • 所以问题是:我们如何有效地计算/存储圆的交点? :)
    【解决方案2】:

    如何使用聚类算法来识别点的聚类。然后确定具有最大点数的聚类。以具有最大点的簇的平均点为圆心,然后画圆。

    MATLAB 支持 implementationk-means algorithm,它会返回一个二维数组(准确地说是一个矩阵),其中包含聚类均值和相应的聚类 ID。

    众所周知,k-means 的另一面是事先决定 k(簇数)。不过,这可以解决——可以从数据点中学习 k 的值。请检查此paper

    我希望这会有所帮助。

    干杯

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

      • 这适用于整数网格,但如果点坐标是真实值,您可能会遇到问题。
      • (原始海报)让我想起了我最不公正的投票之一:stackoverflow.com/questions/244452/… :)
      • @Mark - 好点 - 我认为如果我们将地图中的每个元素都视为一个“bin”,可能仍然可以应用相同的技术,但这可能仍然会留下一些我们赢得的边缘情况'找不到使用这个方法。
      • 这是 O(P^2),因为简单地计算圆中的点已经是 O(P)。
      【解决方案4】:

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

      每个包含点的圆都必须有一个最左边的点。因此,它列出了一个点右侧可能在圆范围内的所有点。它首先按 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()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-18
        • 2020-06-24
        • 2022-01-15
        相关资源
        最近更新 更多