凸包(Convex Hull)構造演算法——Graham掃描法
凸包(Convex Hull)
在圖形學中,凸包是一個非常重要的概念。簡明的說,在平面中給出N個點,找出一個由其中某些點作為頂點組成的凸多邊形,恰好能圍住所有的N個點。
這十分像是在一塊木板上釘了N個釘子,然後用一根繃緊的橡皮筋它們都圈起來,這根橡皮筋的形狀就是所謂的凸包。
計算凸包的一個著名演算法是Graham Scan法,它的時間複雜度與所採用的排序演算法時間複雜度相同,通常採用線性對數演算法,因此為\( O\left(N\mathrm{log}\left(N\right)\right) \)。
1. 找到所有點\( P_{0,1,...,N-1} \)中最下方的點,記為\( P_{L} \);
2. 計算所有其他的點\( P_{i}\left(i\neq L\right) \) 與 \( P_{L} \)構成的向量\( \overrightarrow{P_{L}P_{i}} \)相對於水平軸的夾角。因為所有的點都在該\( P_{L} \)上方,因此向量的取值範圍為\( \left(0, 180\right) \) ,所以可以用餘切值代替角度值;
3. 對所有其他的點按照第2步算出的角度進行排序,且\( P_{L} \)為排序後的陣列的第0位;
4. 從點\( P_{L} \)開始,依此連線每一個點(已經排序過),每連線一個點檢測連線的走向是否是逆時針的,如果是則留下該點的前一個點,反之去除前一個點,使之與前面第二個點直接連線,繼續這一檢測,直到是逆時針或者所有點都被檢測過為止。
判斷三個點依此連成兩條線段走向是否為逆時針,用這兩條線段向量的叉積判斷:叉積>0,逆時針;反之順時針或者共線。
這裡採用Qt 5.7實現了一個演算法的演示程式,其中演算法的部分如下(由於在Qt的座標系中,y向下增長,因此在計算縱座標差值時需要取相反數)。
void DisplayWidget::calConvexHull() { int size = m_points.size(); if (size < 3) { return; } // First: find the lowest pointint maxY = 0; int indexOfLowest = -1; for (int i = 0; i < size; i++) { if (m_points.at(i).y() > maxY) { maxY = m_points.at(i).y(); indexOfLowest = i; } } std::swap(*m_points.begin(), *(m_points.begin() + indexOfLowest)); QPoint &lowestPoint = *(m_points.begin()); // Second: calculate ctan(angles) double *ctanAngles = new double[size]; for (int i = 1; i < size; i++) { double deltaY = lowestPoint.y() - m_points.at(i).y() + DBL_EPSILON; double deltaX = m_points.at(i).x() - lowestPoint.x(); ctanAngles[i] = deltaX / deltaY; } // Third: Sort subscript int *subscript = new int[size]; for (int i = 1; i < size; i++) { subscript[i] = i; } std::sort(subscript + 1, subscript + size, [ctanAngles](int a1, int a2) { return ctanAngles[a2] < ctanAngles[a1]; }); // Fourth: Calculate convex hull std::vector<QPoint> convexHullPoints; convexHullPoints.push_back(*m_points.begin()); convexHullPoints.push_back(m_points.at(subscript[1])); for (int i = 2; i < size; i++) { convexHullPoints.push_back(m_points.at(subscript[i])); while (convexHullPoints.size() > 3 && !isAnticlockwise(*(convexHullPoints.end() - 3), *(convexHullPoints.end() - 2), *(convexHullPoints.end() - 1))) { *(convexHullPoints.end() - 2) = *(convexHullPoints.end() - 1); convexHullPoints.pop_back(); } } m_convexHullPoints = std::move(convexHullPoints); delete[] ctanAngles; delete[] subscript; }
效果如下:
程式原始碼:http://files.cnblogs.com/files/HolyChen/ConvexHull.rar
相關推薦
凸包(Convex Hull)構造演算法——Graham掃描法
凸包(Convex Hull) 在圖形學中,凸包是一個非常重要的概念。簡明的說,在平面中給出N個點,找出一個由其中某些點作為頂點組成的凸多邊形,恰好能圍住所有的N個點。 這十分像是在一塊木板上釘了N個釘子,然後用一根繃緊的橡皮筋它們都圈起來,這根橡皮筋的形狀就是所謂的凸包。 計算凸包的一個著名演
凸包(Convex Hull)
給定二維平面上的點集,凸包就是將最外層的點連線起來構成的凸多邊形 凸缺陷(convexity defects)是凸包比物體輪廓多的部分 函式:convexHull尋找凸包函式 程式:隨機生成一些座標點,然後對這些點構成的幾何圖形求凸包 int main() { Mat
ZOJ——3871(Convex Hull)(計算幾何+凸包問題)
Edward has n points on the plane. He picks a subset of points (at least three points), and defines the beauty of the subset as twice the are
計算幾何(一):凸包問題(Convex Hull)
### 引言 首先介紹下什麼是凸包?如下圖: ![](https://gitee.com//riotian/blogimage/raw/master/img/20200921200555.png) 在一個二維座標系中,有若干點雜亂排列著,將最外層的點連線起來構成的凸多邊型,它能包含給定的所有的點,這個多
【計算幾何 02】凸包問題(Convex Hull)
### 引言 首先介紹下什麼是凸包?如下圖: ![](https://gitee.com//riotian/blogimage/raw/master/img/20200921200555.png) 在一個二維座標系中,有若干點雜亂排列著,將最外層的點連線起來構成的凸多邊型,它能包含給定的所有的點,這個多
計算幾何_三維凸包(3d convex hull)
const double eps = 1e-8; typedef list<int>::iterator liit; inline int sign(double d){ if(d < -eps) return -1; return (d > e
凸包-Andrew演算法&&Graham掃描法
凸包簡介: 在二維平面上(二維凸包)給出若干個點,能夠包含這若干個點的面積最小的凸多邊形稱為凸包(可以想像有很多個釘子釘在牆上,然後用一個橡皮圈套在所有的釘子上,最後橡皮圈形成的就是一個凸包)。 Graham掃描法: Graham掃描法是一種基於極角排序的進行求解的
凸包演算法(Graham掃描法)
目錄 一、概念 二、演算法步驟 三、程式碼實現 轉自:https://www.cnblogs.com/aiguona/p/7232243.html 一、概念 凸包(Convex Hull)是一個計算幾何(圖形學)中的概念。 在一個實數向量空間V中,對於給定集合X,所有
最小凸包演算法(Convex Hull)(1)-Graham掃描法 -計算幾何-演算法導論
基本問題: 平面上有n個點p1,p2, ..., pn, 要求求出一個面積最小的凸多邊形,使得這個多邊形包含所有平面上的點。 根據演算法導論上提供的兩個方法做一些介紹: 演算法1: Graham掃描法 下面直接給出一段虛擬碼,方便描述: GRAHAM-SCAN(Q) {
通過ArcEngine接口建立凸包(一)
geb ppt esp sqrt rect urn tar 次循環 arcengine 準備步驟: 1.先要能實現選擇點的功能,可以利用矩形選點要素的方法axMapControl1.TrackRectangle()。 2.要建立凸包,必須有存凸包(線要素)的文件,然後通過下
1015 - 計算幾何之Graham掃描法求凸包 - Cows(POJ 3348)
傳送門 題意 給你一堆點,求這些點的凸包,並求出面積 分析 很久之前就做過的一道題了,還記得那是凱爺(凱爺好厲害好厲害的)講的,是Jarris步進法:按照橫縱座標對所有的點進行排序(橫座標優先) 然後就是和Graham類似的方法了,邊掃描邊
[BZOJ3533][Sdoi2014]向量集(凸包+線段樹+二分)
Address 洛谷P3309 BZOJ3533 LOJ#2197 Solution 先假設詢問物件是所有的向量,並且已經全部加入集合。 發現向量 (
[BZOJ4585][Apio2016]Fireworks 煙火表演(樹形 DP + 凸包 + 左偏樹)
Address 洛谷 P3642 BZOJ 4585 UOJ #205 LOJ #2568 Solution NOIP 2018 之後 A 掉的第一道題,祭 看上去是水題,但是發現邊權可以減一之後就不是那麼容易了 很容易想到狀態:
Graham's Scan演算法尋找離散點的凸多邊形(JavaScript版)
100個離散點的凸多邊形效果: JavaScript程式碼實現如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></
尋找凸包(Graham掃描法)
題意描述: 對任意給定的平面上的點集,求最小凸多邊形使得點集中的點要麼在凸多邊形的邊上,要麼在凸多邊形的內部。 Graham演算法描述: 在所有的點中找到一點p0,使得p0的縱座標值最小,在有多個最小縱座標的情況下,找橫座標最小的那一個。 將所有的點
凸包模板(分治 or Graham掃描法)
問題概述:空間上有很多點,現在要用一個凸多邊形將所有點全部包住,求哪些點在這個凸多邊形上 輸入樣例: 對應輸出:
【BZOJ1185】[HNOI2007]最小矩形覆蓋(凸包,旋轉卡殼)
tps 簡單的 double 所有 iostream ace str 覆蓋 cpp 【BZOJ1185】[HNOI2007]最小矩形覆蓋(凸包,旋轉卡殼) 題面 BZOJ 洛谷 題解 最小的矩形一定存在一條邊在凸包上,那麽枚舉這條邊,我們還差三個點,即距離當前邊的最遠點,以
【洛谷 P3187】 [HNOI2007]最小矩形覆蓋 (二維凸包,旋轉卡殼)
ref scanf const 維護 math int() 一個 數據 pre 題目鏈接 嗯,毒瘤題。 首先有一個結論,就是最小矩形一定有條邊和凸包重合。腦補一下就好了。 然後枚舉凸包的邊,用旋轉卡殼維護上頂點、左端點、右端點就好了。 上頂點用叉積,叉積越大三角形面積越大,
poj3348Cows(凸包求多邊形面積)
題目連結: 思路: 先對點進行排序,然後求出凸包。對凸包上的點進行面積計算,即將多邊形面積分成多個三角形,利用叉積計算即可。 程式碼: #include<stdio.h> #include<string.h> #include<math.h&
凸包(轉自水神百度文庫)
#include<stdio.h> #include<math.h> #include<algorithm> using namespace std; struct Point { double x,y,len; }Pt[2000