凸包算法与JAVA求解的基本思路

一、凸包

凸包(Convex Hull)是一个计算几何(图形学)中的概念。

在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,...Xn)的凸组合来构造。

简单来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它的内部包含了点集中其余所有的点。

二、凸包算法的基本思路

如下图所示:

①首先我们要从最左最下的点开始,如图中的B点(‘左’的优先级高,假设点B的正下方有一点Z,则从Z点开始);

②设一个向量以B点作为根部,方向朝正上方‘↑’;

③令向量绕根部顺时针旋转,直到找到第一个在向量所指向的点(如图中点G,点F是第二个向量所能指向的点),若向量方向上有多个点,则选取距离最远的点

④令该点成为向量新的起点,方向不变(该步中方向为向量\underset{BG}{\rightarrow}的方向);

⑤重复③、④步,直到③中所找的点为初始点(图中的B点);

⑥整个过程中找到的所有的点构成了凸包的点集,相邻的找到的点之间的连线构成了凸包多边形,该图中找到的点的顺序为B - G - D - K - H - J - B。

三、JAVA求解凸包的一些准备工作

      在最终求解凸包算法之前,我们需要准备一些辅助计算的方法:

(1)calculateRegularPolygonAngle(int sides)

①作用:输入多边形边数sides,返回多边形的内角度数总和

②代码实现:

public static double calculateRegularPolygonAngle(int sides) {
        //如果多边形边数小于3,则不是多边形,返回0
    	if(sides<3)return 0;
        //否则,代入公式计算内角和
    	double angle=(double)(sides-2)*180/sides;
    	return angle;
    }

(2)calculateBearingToPoint(double currentBearing, int currentX, int currentY, int targetX, int targetY)

①作用:输入目标点向量根部点坐标,以及向量方向与Y轴正方向的夹角,返回向量想要指向目标点所要转过的正角(正角:顺时针所要扫过的角度);

②代码实现:

public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY,
                                                 int targetX, int targetY) {
        //计算从根部点到目标点的向量的横坐标
    	double x=(double)(targetX-currentX);
        //同上,计算向量的纵坐标
    	double y=(double)(targetY-currentY);
        //调用Math类下的atan2方法,计算向量所要偏转的正角度
    	double degree=90-currentBearing-Math.toDegrees(Math.atan2(y, x));
        //如果角度为负,则转为正角
    	if(degree<0) degree+=360;
    	return degree;
    }

四、JAVA求解凸包的主函数

Set<Point> convexHull(Set<Point> points)

①作用:输入所有点构成的点集,返回凸包多边形的点集;

②代码实现:

public static Set<Point> convexHull(Set<Point> points) {
        //判断点的总数是否小于3,小于3则不能构成多边形
    	if(points.size() < 3) {
            return points;
        }
        //定义新的Set集合,其中不会有重复元素,符合我们的要求
        Set<Point> set = new HashSet<>();
        Point xmin = new Point(Double.MAX_VALUE, Double.MAX_VALUE);
        //运用for-each遍历的方式,在所有点中寻找最左的点
        for(Point item : points) {
            if (item.x() < xmin.x() || (item.x() == xmin.x() && item.y() < xmin.y()))
                xmin = item;
        }
        //设最左的点为初始起点
        Point nowPoint = xmin, tempPoint = xmin;
        //初始化指向角度为0
        double nowAngle = 0, minAngle = 360, tempAngle = 0;
        double distance;
        double maxdistance = 0;
        //无差别地遍历所有的点
        do{
        	set.add(tempPoint);
            //  遍历全部点,寻找下一个在凸包上的点
            for(Point item : points) {
                //当某一点不在点集之中或者该点为起始点
                if ((!set.contains(item) || item == xmin)  ) {
                    //调用判断calculateBearingToPoint方法计算所需要偏转的角度
                    tempAngle = calculateBearingToPoint(nowAngle, (int)nowPoint.x(), (int)nowPoint.y(), (int)item.x(), (int)item.y());
                    //计算目标点与所在点之间的距离
                    distance = (item.x() - nowPoint.x())*(item.x() - nowPoint.x()) + (item.y() - nowPoint.y())*(item.y() - nowPoint.y());
                    /*如果某一点的偏转角比之前所找到的最小角度还要小
                      则该角度成为了最小偏转角
                      多个点在同一方向上时取距离所在点最远的目标点*/
                    if(tempAngle < minAngle || ((tempAngle == minAngle) && (distance > maxdistance))) {
                        minAngle = tempAngle;
                        tempPoint = item;
                        maxdistance = distance;
                    }
                }
            }
            //遍历完所有点后,初始化判断指标,从刚刚找到的目标点再次出发,重复上述步骤
            nowAngle = minAngle;
            minAngle = 360;
            nowPoint = tempPoint;
        } while(nowPoint != xmin);  // 当下一个点为第一个点时找到了凸包上的全部点,退出循环
        return set;
    
 	}

最终返回的Set类型的集合就是我们所要求得的凸包多边形点集。

五、实现过程中需要的Math类中的方法

(1)Math.round

①方法原型:long Math.round(double a)

②作用:输入一个浮点型小数,返回四舍五入后的值

(2)Math.toDegrees

①方法原型:double Math.toDegrees(double radian)

②作用:输入一个弧度制,返回对应的角度值

(3)Math.atan2(y,x)

①方法原型:double Math.atan2(double y,double x)        (注意:输入的坐标值中纵坐标y在前面)

②作用:输入一个以原点为起点的向量所指向的一个点的坐标(x,y),返回该向量与X轴所夹正角的弧度值arctan(y/x)

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Java语言中实现凸包算法可以使用 Graham 扫描算法。以下是一个简单的示例代码: ```java import java.util.*; public class ConvexHull { public static List<Point> convexHull(List<Point> points) { // 如果点的数量小于等于1,则返回点列表 if (points.size() <= 1) { return points; } // 对点按照x坐标进行排序 Collections.sort(points); // 创建一个栈来保存凸包上的点 Stack<Point> stack = new Stack<>(); // 将前两个点加入栈中 stack.push(points.get(0)); stack.push(points.get(1)); // 从第三个点开始,依次判断是否为凸包上的点 for (int i = 2; i < points.size(); i++) { Point top = stack.pop(); while (!stack.isEmpty() && orientation(stack.peek(), top, points.get(i)) <= 0) { top = stack.pop(); } stack.push(top); stack.push(points.get(i)); } // 将栈中的点转换成列表 List<Point> hull = new ArrayList<>(stack); Collections.reverse(hull); return hull; } // 判断点a、b、c的方向 public static int orientation(Point a, Point b, Point c) { return (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y); } public static void main(String[] args) { List<Point> points = new ArrayList<>(); points.add(new Point(0, 0)); points.add(new Point(1, 1)); points.add(new Point(2, 2)); points.add(new Point(3, 3)); points.add(new Point(0, 3)); List<Point> hull = convexHull(points); System.out.println("Convex Hull:"); for (Point p : hull) { System.out.println(p); } } } class Point implements Comparable<Point> { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } @Override public int compareTo(Point other) { if (this.x == other.x) { return Integer.compare(this.y, other.y); } return Integer.compare(this.x, other.x); } @Override public String toString() { return "(" + x + ", " + y + ")"; } } ``` 在上面的代码中,我们定义了一个 `Point` 类来表示一个二维点,然后实现了 `convexHull` 方法来计算给定点集的凸包。最后,我们在 `main` 方法中测试了这个算法,并输出了计算结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值