旋转卡壳--凸包的直径

凸包的直径

例题:

poj 2187 直接求直径OK

凸包的直径

          多边形的直径被定义为多边形上任意两点间的最大距离的值。 在多边形上,决定直径的点可能不止一对。 事实上,如果一个多边形含有n个顶点,那么就最多有 n 对“直径点对”在。

 

 

       上图所示的是一个简单的多边形直径的实例。 直径点对在图中是被平行的切线(用红色表示)穿过的黑点. 直径是用淡蓝色高亮表示的。

       很明显, 确定凸多边形 P 直径的点对不可能在多边形 P 内部。 所以搜索应该在边界上进行。 事实上, 由于直径是多边形的平行切线的最远距离, 所以我们只需要查询对踵点。 Shamos (1978) 提供了一个 简单的 时间复杂度为O(n) 的计算凸包对踵点对的算法。这个算法是通过遍历这些点的列表, 得到最大距离即直径,然后输出这个点对。 下面是Shamos 算法的伪代码,1985年在 Preparata 和 Shamos 文章中进行发表。

输入是一个多边形 P={p1,...,pn}

begin
     p0:=pn;
     q:=NEXT[p];
     while (Area(p,NEXT[p],NEXT[q]) > Area(p,NEXT[p],q)) do
          q:=NEXT[q];
          q0:=q;
          while (q != p0) do
               begin
                    p:=NEXT[p];
                    Print(p,q);
                    while (Area(p,NEXT[p],NEXT[q]) > Area(p,NEXT[p],q) do
                         begin
                              q:=NEXT[q];
                              if ((p,q) != (q0,p0)) then Print(p,q)
                              else return
                         end;
                    if (Area(p,NEXT[p],NEXT[q]) = Area(p,NEXT[p],q)) then
                      if ((p,q) != (q0,p0)) then Print(p,NEXT[q])
                      else Print(NEXT[p],q)
               end
end.


 

注意 Print(p,q) 表示将 (p,q) 作为一个对踵点对输出, Area(p,q,r) 表示三角形 pqr 的有向面积。 

虽然直觉看这个过程与常规旋转卡壳算法不同, 但从本质上看是相同的, 并且避免了任何角度的计算。

下面试是一个比较直观的算法:

          1、计算出多边形 y 方向上的端点。 设为 ymin 和 ymax 。

           2、通过 ymin 和 ymax 构造两条水平切线。 由于他们已经是一对对踵点, 计算他们之间的距离并维护为一个当前最大值。

           3、同时旋转两条线直到其中一条与多边形的一条边重合。

           4、一对新的对踵点对此时产生。 计算新的距离, 并和当前最大值比较, 大于当前最大值则更新。

           5、重复步骤3和步骤4的过程直到再次产生对踵点对 (ymin,ymax) 。

           6、输出确定最大直径的对踵点对。

虽然上述的过程(给出的伪代码)变得非常有用, 但是我们仍可以从对踵点对中得到其他的信息, 如多边形的宽度 。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用旋转卡壳算法求解凸包直径的 Python 代码示例: ```python import numpy as np from scipy.spatial import ConvexHull def dist(p1, p2): return np.linalg.norm(p1 - p2) def diameter(hull): """Calculate the diameter of a convex hull using rotating calipers.""" if len(hull.vertices) < 2: return 0 elif len(hull.vertices) == 2: return dist(hull.points[hull.vertices[0]], hull.points[hull.vertices[1]]) else: edges = hull.points[hull.vertices] farthest_points = [None, None] max_distance = 0 i, j = 0, 1 for k in range(len(edges)): # Find the farthest point from the current edge while np.cross(edges[j] - edges[i], edges[(i+1)%len(edges)] - edges[i]) \ > np.cross(edges[j] - edges[i], edges[(j+1)%len(edges)] - edges[i]): j = (j+1)%len(edges) d = dist(edges[i], edges[j]) if d > max_distance: max_distance = d farthest_points[0] = edges[i] farthest_points[1] = edges[j] i = (i+1)%len(edges) return dist(*farthest_points) points = np.random.rand(10, 2) # 随机生成 10 个点 hull = ConvexHull(points) print("凸包直径:", diameter(hull)) ``` 这个代码示例中,我们先随机生成了 10 个点,然后使用 scipy 库的 ConvexHull 函数求解凸包。接着,我们使用旋转卡壳算法求解凸包直径,最后输出结果。 在旋转卡壳算法中,我们使用了叉积来判断两个向量的方向关系,从而找到凸包上距离当前边最远的点。这个算法的核心思想是,对于凸包上的每条边,我们都可以找到一个与其垂直的边,然后随着旋转角度的增加,这条垂直边的端点将会沿着凸包移动,直到另一条边成为新的垂直边。在这个过程中,我们可以计算出每个垂直边的长度,最终得到凸包直径
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值