找回密码
 快速注册
搜索
查看: 59|回复: 2

2D凸包算法

[复制链接]

3149

主题

8386

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65391
QQ

显示全部楼层

hbghlyj 发表于 2023-5-7 06:59 |阅读模式
本帖最后由 hbghlyj 于 2023-5-7 12:27 编辑
描述单调凸包算法的动画
ezgif-5-a7d1c20d4d.gif
Andrew's monotone chain convex hull algorithm
Andrew算法求凸包的过程。首先把所有点以横坐标为第一关键字,纵坐标为第二关键字排序。
显然排序后最小的元素和最大的元素一定在凸包上。
而且因为是凸多边形,我们如果从一个点出发逆时针走,轨迹总是“左拐”的,
一旦出现右拐,就说明这一段不在凸包上。
因此我们可以用一个单调栈来维护上下凸壳。

OIWiki Rotating Calipers
Luogu wjyyy's blog
旋转卡壳
Caliper_detail_view.jpeg Caliper (卡尺)
917714-20160520110032529-8641355.gif 首先使用任何一种凸包算法求出给定所有点的凸包,有着最长距离的点对一定在凸包上。
而由于凸包的形状,我们发现,逆时针地遍历凸包上的边,对于每条边都找到离这条边最远的点,那么这时随着边的转动,对应的最远点也在逆时针旋转,不会有反向的情况。
rotating-calipers1.png 这意味着我们可以在逆时针枚举凸包上的边时,记录并维护一个当前最远点,并不断计算、更新答案。
求出凸包后的数组自然地是按照逆时针旋转的顺序排列,不过要记得提前将最左下角的 1 节点补到数组最后,这样在挨个枚举边 $[(i,i+1)]$ 时,才能把所有边都枚举到。

3149

主题

8386

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65391
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2023-5-7 07:40

尝试写一下💻不知对不对

用Asymptote编写一个函数,输入一个点列表,应用Andrew算法按逆时针顺序输出凸包上的点列表。
注意:返回列表中的最后一个点与第一个点相同。
workspace(1).png 输入5个点的列表$\{(-0.2,0.1),(0.5,0),(-1,-1),(-1,1),(1,0)\}$
输出凸包

  1. pair[] convex_hull(pair[] P){
  2.     int n = P.length, k = 0;
  3.     if (n <= 3) return P;
  4.     pair[] H=new pair[2*n];
  5.     P=sort(P,new bool(pair A, pair B){return A.x<B.x||(A.x==B.x&&A.y<B.y);});
  6.     for (int i = 0; i < n; ++i) {
  7.         while (k >= 2 && orient(H[k-2], H[k-1], P[i]) <= 0) --k;
  8.         H[k] = P[i];++k;
  9.     }   
  10.     for (int i = n-1, t = k+1; i > 0; --i) {
  11.         while (k >= t && orient(H[k-2], H[k-1], P[i-1]) <= 0) --k;
  12.         H[k] = P[i-1];++k;
  13.     }
  14.     H.delete(k,H.length-1);
  15.     return H;
  16. }
  17. unitsize(4cm);
  18. path p;
  19. pair[] points={(-0.2,0.1),(0.5,0),(-1,-1),(-1,1),(1,0)};
  20. for(pair P:points){
  21.   dot(P);
  22.   label((string)P,P);
  23. }
  24. for(pair P:convex_hull(points)){
  25.   p=P--p;
  26. }
  27. draw(p);
复制代码

3149

主题

8386

回帖

6万

积分

$\style{scale:11;fill:#eff}꩜$

积分
65391
QQ

显示全部楼层

 楼主| hbghlyj 发表于 2023-5-7 08:43
本帖最后由 hbghlyj 于 2023-5-7 12:54 编辑 可以计算$(x^2-1)(x^2-2),x∈[-1.5,1.5]$的图象(有2个拐点)的凸包
workspace(2).png
  1. pair[] convex_hull(pair[] P){
  2.     int n = P.length, k = 0;
  3.     if (n <= 3) return P;
  4.     pair[] H=new pair[2*n];
  5.     P=sort(P,new bool(pair A, pair B){return A.x<B.x||(A.x==B.x&&A.y<B.y);});
  6.     for (int i = 0; i < n; ++i) {
  7.         while (k >= 2 && orient(H[k-2], H[k-1], P[i]) <= 0) --k;
  8.         H[k] = P[i];++k;
  9.     }   
  10.     for (int i = n-1, t = k+1; i > 0; --i) {
  11.         while (k >= t && orient(H[k-2], H[k-1], P[i-1]) <= 0) --k;
  12.         H[k] = P[i-1];++k;
  13.     }
  14.     H.delete(k,H.length-1);
  15.     return H;
  16. }
  17. unitsize(4cm);
  18. path p,q;pair[] points;
  19. settings.render=5;
  20. for(real x:uniform(-1.5,1.5,30)){
  21.   points.push((x,(x^2-1)*(x^2-2)));
  22. }
  23. for(pair P:points){
  24.   q=P--q;
  25. }
  26. for(pair P:convex_hull(points)){
  27.   p=P--p;
  28. }
  29. fill(p--cycle,lightblue);draw(q,red+linewidth(2));
复制代码

手机版|悠闲数学娱乐论坛(第3版)

GMT+8, 2025-3-4 12:16

Powered by Discuz!

× 快速回复 返回顶部 返回列表