1. 程式人生 > >凸包(Convex Hull)構造演算法——Graham掃描法

凸包(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 point
int 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——3871Convex 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掃描 - CowsPOJ 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