凸包
数学定义
- 平面的一个子集S被称为是凸的,当且仅当对于任意两点A,B属于S,线段PS都完全属于S
- 过于基础就不详细介绍了
凸包的计算
- github上找到了别人的代码,用4种方式实现了凸包的计算,把他放在这里
- 链接地址https://github.com/MiguelVieira/ConvexHull2D
- 先放这个代码的用法吧
int main() {vector<point> v = getPoints();
//1vector<point> h = quickHull(v);cout << "quickHull point count: " << h.size() << endl;print(h);//2h = giftWrapping(v);cout << endl << "giftWrapping point count: " << h.size() << endl;print(h);//3h = monotoneChain(v);cout << endl << "monotoneChain point count: " << h.size() << endl;print(h);//4h = GrahamScan(v);cout << endl << "GrahamScan point count: " << h.size() << endl;print(h);
- 这是具体的代码,直接写在一个头文件里,包含一下就能在程序里实现上面的诸多用法了
#include <algorithm>
#include <iostream>
#include <vector>
#include<random>using namespace std;
namespace ch{struct point {float x;float y;point(float xIn, float yIn) : x(xIn), y(yIn) { }};// The z-value of the cross product of segments // (a, b) and (a, c). Positive means c is ccw// from (a, b), negative cw. Zero means its collinear.float ccw(const point& a, const point& b, const point& c) {return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);}// Returns true if a is lexicographically before b.bool isLeftOf(const point& a, const point& b) {return (a.x < b.x || (a.x == b.x && a.y < b.y));}// Used to sort points in ccw order about a pivot.struct ccwSorter {const point& pivot;ccwSorter(const point& inPivot) : pivot(inPivot) { }bool operator()(const point& a, const point& b) {return ccw(pivot, a, b) < 0;}};// The length of segment (a, b).float len(const point& a, const point& b) {return sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));}// The unsigned distance of p from segment (a, b).float dist(const point& a, const point& b, const point& p) {return fabs((b.x - a.x) * (a.y - p.y) - (b.y - a.y) * (a.x - p.x)) / len(a, b);}// Returns the index of the farthest point from segment (a, b).size_t getFarthest(const point& a, const point& b, const vector<point>& v) {size_t idxMax = 0;float distMax = dist(a, b, v[idxMax]);for (size_t i = 1; i < v.size(); ++i) {float distCurr = dist(a, b, v[i]);if (distCurr > distMax) {idxMax = i;distMax = distCurr;}}return idxMax;}// The gift-wrapping algorithm for convex hull.// https://en.wikipedia.org/wiki/Gift_wrapping_algorithmvector<point> giftWrapping(vector<point> v) {// Move the leftmost point to the beginning of our vector.// It will be the first point in our convext hull.swap(v[0], *min_element(v.begin(), v.end(), isLeftOf));vector<point> hull;// Repeatedly find the first ccw point from our last hull point// and put it at the front of our array. // Stop when we see our first point again.do {hull.push_back(v[0]);swap(v[0], *min_element(v.begin() + 1, v.end(), ccwSorter(v[0])));} while (v[0].x != hull[0].x && v[0].y != hull[0].y);return hull;}// The Graham scan algorithm for convex hull.// https://en.wikipedia.org/wiki/Graham_scanvector<point> GrahamScan(vector<point> v) {// Put our leftmost point at index 0swap(v[0], *min_element(v.begin(), v.end(), isLeftOf));// Sort the rest of the points in counter-clockwise order// from our leftmost point.sort(v.begin() + 1, v.end(), ccwSorter(v[0]));// Add our first three points to the hull.vector<point> hull;auto it = v.begin();hull.push_back(*it++);hull.push_back(*it++);hull.push_back(*it++);while (it != v.end()) {// Pop off any points that make a convex angle with *itwhile (ccw(*(hull.rbegin() + 1), *(hull.rbegin()), *it) >= 0) {hull.pop_back();}hull.push_back(*it++);}return hull;}// The monotone chain algorithm for convex hull.vector<point> monotoneChain(vector<point> v) {// Sort our points in lexicographic order.sort(v.begin(), v.end(), isLeftOf);// Find the lower half of the convex hull.vector<point> lower;for (auto it = v.begin(); it != v.end(); ++it) {// Pop off any points that make a convex angle with *itwhile (lower.size() >= 2 && ccw(*(lower.rbegin() + 1), *(lower.rbegin()), *it) >= 0) {lower.pop_back();}lower.push_back(*it);}// Find the upper half of the convex hull.vector<point> upper;for (auto it = v.rbegin(); it != v.rend(); ++it) {// Pop off any points that make a convex angle with *itwhile (upper.size() >= 2 && ccw(*(upper.rbegin() + 1), *(upper.rbegin()), *it) >= 0) {upper.pop_back();}upper.push_back(*it);}vector<point> hull;hull.insert(hull.end(), lower.begin(), lower.end());// Both hulls include both endpoints, so leave them out when we // append the upper hull.hull.insert(hull.end(), upper.begin() + 1, upper.end() - 1);return hull;}// Recursive call of the quickhull algorithm.void quickHull(const vector<point>& v, const point& a, const point& b,vector<point>& hull) {if (v.empty()) {return;}point f = v[getFarthest(a, b, v)];// Collect points to the left of segment (a, f)vector<point> left;for (auto p : v) {if (ccw(a, f, p) > 0) {left.push_back(p);}}quickHull(left, a, f, hull);// Add f to the hullhull.push_back(f);// Collect points to the left of segment (f, b)vector<point> right;for (auto p : v) {if (ccw(f, b, p) > 0) {right.push_back(p);}}quickHull(right, f, b, hull);}// QuickHull algorithm. // https://en.wikipedia.org/wiki/QuickHullvector<point> quickHull(const vector<point>& v) {vector<point> hull;// Start with the leftmost and rightmost points.point a = *min_element(v.begin(), v.end(), isLeftOf);point b = *max_element(v.begin(), v.end(), isLeftOf);// Split the points on either side of segment (a, b)vector<point> left, right;for (auto p : v) {ccw(a, b, p) > 0 ? left.push_back(p) : right.push_back(p);}// Be careful to add points to the hull// in the correct order. Add our leftmost point.hull.push_back(a);// Add hull points from the left (top)quickHull(left, a, b, hull);// Add our rightmost pointhull.push_back(b);// Add hull points from the right (bottom)quickHull(right, b, a, hull);return hull;}vector<point> getPoints() {vector<point> v;std::default_random_engine e(std::random_device{}());std::uniform_real_distribution<double> dist_x(0.05, 0.95);std::uniform_real_distribution<double> dist_y(0.05, 0.95);for (int i = 0; i < 30; ++i) {v.push_back(point(dist_x(e), dist_y(e)));}return v;}
}
可视化实现
这个exe小程序我放在主页了,大家可以免费下载,之后会创建一个仓库把代码公开出来,大家就可以在我的基础上实现更多的几何算法。