译者序
本篇blog实际上是Bob大叔对xreborner的一连串的发贴给于的回复(xreborner在上篇blog中对Bob大叔提出了一系列犀利的维护C++权益的观点)。
正文
我在最近的一篇blog中对比了C++、Java和Ruby的时间消耗,其中一个参与者(xreborner)提交了一个convex hull的凸包算法代码。我花了好久来研究其中的蹊跷,直到把算法绘制于图上,才发现自己是蒙在鼓里了。
xreborner用的算法像是一种Graham Scan算法,它的复杂度呈O(nLogn)级别。实际上,这个算法的时间主要是消耗在其中的快速排序上了。
还有一个算法叫做Jarvis March,也称作giftwrapping算法。它的复杂度呈O(kn)级别,k代表了凸包顶点的数目。
我非常好奇这两个算法哪个会更快些,显然,只有在logn < k的时候,O(nLogn)才会比O(kn)的快。不过也有另一种考量,因为Jarvis March比Graham Scan简单的多,所以我认为在绝大多数情况下,Jarvis March算法都会是个更优的选择,因为只有在凸包顶点数目非常大的情形下Graham算法的效率才会反超。于是我自己编写了Jarvis March算法,并且和xreborner的Graham Scan做了比较,结果如下:
是不是很有趣?我构造了1000个随机点并绘制了时间和凸包顶点数量的曲线。交叉点看起来正好位于这些随机的凸包顶点数量的中间位置,而且大概是在37点位!我猜想这是个巧合,不过也够神奇的啊。
噢,作为一个锻炼,看看你们是不是能读懂Jarvis March,也再看看能不能弄明白Graham Scan。
import java.util.*;
public class JarvisMarch {
Points pts;
private Points hullPoints = null;
private List<Double> hy;
private List<Double> hx;
private int startingPoint;
private double currentAngle;
private static final double MAX_ANGLE = 4;
public JarvisMarch(Points pts) {
this.pts = pts;
}
/**
* The Jarvis March, sometimes known as the Gift Wrap Algorithm.
* The next point is the point with the next largest angle.
* <p/>
* Imagine wrapping a string around a set of nails in a board. Tie the string to the leftmost nail
* and hold the string vertical. Now move the string clockwise until you hit the next, then the next, then
* the next. When the string is vertical again, you will have found the hull.
*/
public int calculateHull() {
initializeHull();
startingPoint = getStartingPoint();
currentAngle = 0;
addToHull(startingPoint);
for (int p = getNextPoint(startingPoint); p != startingPoint; p = getNextPoint(p))
addToHull(p);
buildHullPoints();
return hullPoints.x.length;
}
public int getStartingPoint() {
return pts.startingPoint();
}
private int getNextPoint(int p) {
double minAngle = MAX_ANGLE;
int minP = startingPoint;
for (int i = 0; i < pts.x.length; i++) {
if (i != p) {
double thisAngle = relativeAngle(i, p);
if (thisAngle >= currentAngle && thisAngle <= minAngle) {
minP = i;
minAngle = thisAngle;
}
}
}
currentAngle = minAngle;
return minP;
}
private double relativeAngle(int i, int p) {return pseudoAngle(pts.x[i] - pts.x[p], pts.y[i] - pts.y[p]);}
private void initializeHull() {
hx = new LinkedList<Double>();
hy = new LinkedList<Double>();
}
private void buildHullPoints() {
double[] ax = new double[hx.size()];
double[] ay = new double[hy.size()];
int n = 0;
for (Iterator<Double> ix = hx.iterator(); ix.hasNext();)
ax[n++] = ix.next();
n = 0;
for (Iterator<Double> iy = hy.iterator(); iy.hasNext();)
ay[n++] = iy.next();
hullPoints = new Points(ax, ay);
}
private void addToHull(int p) {
hx.add(pts.x[p]);
hy.add(pts.y[p]);
}
/**
* The PseudoAngle is a number that increases as the angle from vertical increases.
* The current implementation has the maximum pseudo angle < 4. The pseudo angle for each quadrant is 1.
* The algorithm is very simple. It just finds where the angle intesects a square and measures the
* perimeter of the square at that point. The math is in my Sept '06 notebook. UncleBob.
*/
public static double pseudoAngle(double dx, double dy) {
if (dx >= 0 && dy >= 0)
return quadrantOnePseudoAngle(dx, dy);
if (dx >= 0 && dy < 0)
return 1 + quadrantOnePseudoAngle(Math.abs(dy), dx);
if (dx < 0 && dy < 0)
return 2 + quadrantOnePseudoAngle(Math.abs(dx), Math.abs(dy));
if (dx < 0 && dy >= 0)
return 3 + quadrantOnePseudoAngle(dy, Math.abs(dx));
throw new Error("Impossible");
}
public static double quadrantOnePseudoAngle(double dx, double dy) {
return dx / (dy + dx);
}
public Points getHullPoints() {
return hullPoints;
}
public static class Points {
public double x[];
public double y[];
public Points(double[] x, double[] y) {
this.x = x;
this.y = y;
}
// The starting point is the point with the lowest X
// With ties going to the lowest Y. This guarantees
// that the next point over is clockwise.
int startingPoint() {
double minY = y[0];
double minX = x[0];
int iMin = 0;
for (int i = 1; i < x.length; i++) {
if (x[i] < minX) {
minX = x[i];
iMin = i;
} else if (minX == x[i] && y[i] < minY) {
minY = y[i];
iMin = i;
}
}
return iMin;
}
}
}
(原文链接网址:http://www.butunclebob.com/ArticleS.UncleBob.ConvexHullTiming; Robert C. Martin的英文blog网址:http://www.butunclebob.com/ArticleS.UncleBob)
作者简介:Robert C. Martin是Object Mentor公司总裁,面向对象设计、模式、UML、敏捷方法学和极限编程领域内的资深顾问。他不仅是Jolt获奖图书《敏捷软件开发:原则、模式与实践》(中文版)(《敏捷软件开发》(英文影印版))的作者,还是畅销书Designing Object-Oriented C++ Applications Using the Booch Method的作者。Martin是Pattern Languages of Program Design 3和More C++ Gems的主编,并与James Newkirk合著了XP in Practice。他是国际程序员大会上著名的发言人,并在C++ Report杂志担任过4年的编辑。
分享到:
相关推荐
在给定的"java-convexhull"项目中,开发者针对凸包算法进行了优化,特别是针对处理大数时的整数溢出问题。 凸包算法有很多种实现方式,如 Graham's Scan、 Jarvis March(Gift Wrapping) 和 Andrew's Monotone ...
在计算机科学和图形学中,凸包(Convex Hull)是一个重要的概念,它是包含所有点的最小凸多边形。在MATLAB中实现凸包算法,可以有效地处理一系列几何问题,如碰撞检测、图像处理和机器学习等。本教程将详细介绍如何在...
完整凸包算法,标准C++编译,包含注释完整有效,内涵博客链接。
在"The Quickhull algorithm for convex hulls"这篇文献中,作者深入探讨了算法的细节,包括算法的实现、性能分析以及与其他凸包算法(如Graham扫描和 Jarvis步进)的比较。文献可能还涵盖了如何处理特殊情况,例如...
### 计算三维Convex Hull凸体体积和面积的程序 #### 一、引言 在计算机图形学、几何建模以及数据可视化等领域,计算三维空间中的Convex Hull(凸包)体积和面积是一个非常重要的任务。Convex Hull是指一个包含给...
ConvexHull2D 一个周末项目,使用 C++ 和标准库实现各种算法以查找一组 2D 点的凸包。 包括 Graham 的扫描、礼品包装算法、单调链算法和 QuickHull。 为清楚起见,代码没有考虑重复或共线的点。
该文探讨了几种**原地**(In-Place)算法来计算平面点集的凸壳。所谓**原地算法**是指算法的输出结果直接覆盖输入数据,并且除了输入数据之外只使用少量额外内存空间。 #### 二、凸壳概念 给定点集 \( S = \{S[0],...
本项目"ConvexHull-main.zip"显然关注的是如何在编程中实现凸包算法。 在二维平面上,常见的凸包算法有格雷码顺序( Graham's Scan)、 Jarvis March( Gift Wrapping 或者是 Convex Hull Trick)以及 Andrew's ...
凸包算法是计算机科学中的一种重要算法,主要应用于几何计算和数据处理...提供的压缩包文件"ConvexHull"可能包含了实现这种算法的源代码,读者可以通过学习和理解这段代码,深入掌握凸包算法及其在实际问题中的应用。
凸面船体 该模块提供了确定一组地理坐标(纬度和经度)的凸包的功能。 单位应以度为单位。 返回值是经度和纬度坐标的数组。...var convexHull = calculateConvexHull ( points ) ; 归因 从这里进行最小的修改: :
例子 var convexHull = require ( './convexhull.js' ) ;// Create some initial points.var initialPoints = [ { x : 10 , y : 10 } , { x : 50 , y : 10 } , { x : 5 , y : 30 } , { x : 50 , y : 60 } , ] ;// ...
计算几何中,计算点集凸包,使用方便,结果可靠!
在计算机科学领域,凸包(Convex Hull)是几何计算中的一个重要概念,它是指一个包含在二维或三维空间中所有点的最小凸多边形。在这个项目“TuBao.rar”,作者实现了一个针对2维点集的凸包算法,特别强调了使用快速...
在给定的Java程序中,“计算几何求凸包算法的java实现”是解决这个问题的关键。 这个Java代码实现了对离散点集进行求解凸包的过程。在图形用户界面中,用户可以通过鼠标点击生成点,程序会实时地计算这些点的凸包并...
凸包(Convex Hull)是指在一个二维平面上,由给定的一组点构成的最小多边形,该多边形包含了所有的点且具有凸性,即任意两点间的线段均在多边形内部或边界上。常见的凸包算法有 Graham's Scan、 Jarvis March( ...
二维快速凸包算法是计算机图形学、几何计算和算法设计中的一个重要概念,它在路径规划、碰撞检测、图像处理等领域有着广泛的应用。本篇将详细解释这个算法的原理、实现方式以及C++代码中的关键部分。 二维凸包是指...
c++实现的GraHam算法,解决凸包问题
本文将探讨Python中的ConvexHull算法及其可视化方法。 首先,我们需要了解的是Qhull库,它是用于计算凸包、 Voronoi图和Delaunay三角剖分的开源库。在Python中,我们通常通过SciPy库的`scipy.spatial`模块来调用...
**Melkman凸包算法详解** Melkman算法是一种在线算法,主要用于计算简单多边形的边界凸包。在计算机图形学、机器学习和数据结构领域,凸包问题是一个非常重要的概念,它可以帮助我们找到一组点中最外层的点集,这些...
显著性检测是计算机视觉领域中的一个重要任务,其目标是识别图像中最具吸引力或最突出的区域,这些区域...通过深入理解GR算法的原理和实现细节,我们可以更好地理解和利用这个强大的工具,提升图像分析的效率和效果。