几何算法:求多边形面积

上学时曾读过一本算法竞赛书,其中一章介绍几何算法。因为平时很少接触,所以当时在我看来,几何问题都有一种瑰丽神奇的色彩,难度很大而不敢触碰。我一直好奇,计算机只能计算数字,那他是怎样处理图形的呢?

提起几何,我们会想起图形,长度,角度,周长,面积等要素,如果想让计算机认知图形,就必须讲图形数字化,然后运用数形结合的思维去计算。提起来数形结合,我第一反应是解析几何,那是用方程驾驭图形。其实在学习解析几何之前,我们在中学就学到过更简单的数形结合的工具——向量。

向量是既有方向又有大小的量,也叫矢量。两个点之间做减法,就能产生向量,它等于从起点到终点的位移。

关于向量的运算有以下几种:

  1. 加法
  2. 减法
  3. 点积
  4. 叉积
  5. 与实数的乘法
  6. 旋转
    加法和减法都可用通过平行四边形法则求解。点积常用作求两个向量之间的夹角[ 0 - π ]。叉积常用来求有向面积。

写到这里不难发现,无论是向量还是解析几何,都是把图形放在坐标系中数字化,通过计算数字来认知图形。

几何中最基本的单元是点。

class Point {
    double x;
    double y;

    private Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    static Point create(double x, double y) {
        return new Point(x, y);
    }

    Vector substract(Point a) {
        return new Vector(x - a.x, y - a.y);
    }
}

再写一个向量类

class Vector {
    double x;
    double y;

    public Vector(double x, double y) {
        this.x = x;
        this.y = y;
    }

    Vector mutiply(double n) {
        return new Vector(x * n, y * n);
    }

    static double dot(Vector v, Vector w) {
        return v.x * w.x + v.y * w.y;
    }

    static double length(Vector v) {
        return Math.sqrt(dot(v, v));
    }

    static double angle(Vector v, Vector w) {
        return Math.acos(dot(v, w) / length(v) / length(w));
    }

    // 根据两角和公式推导
    static Vector rotate(Vector v, double rad) {
        return new Vector(v.x * Math.cos(rad) - v.y * Math.sin(rad), v.x * Math.sin(rad) + v.y * Math.cos(rad));
    }

    static double cross(Vector v, Vector w) {
        return v.x * w.y - v.y * w.x;
    }

    static double area(Point a, Point b, Point c) {
        Vector v = new Vector(a.x - b.x, a.y - b.y);
        Vector w = new Vector(a.x - c.x, a.y - c.y);
        return cross(v, w);
    }

    static Point getLineIntersection(Point p, Vector v, Point q, Vector w) {
        Vector u = p.substract(q);
        double t = cross(w, u) / cross(v, w);
        v = v.mutiply(t);
        Point point = create(v.x, v.y);
        return create(p.x + point.x, p.y + point.y);
    }
}

三角形是最基本的图形,我们可以根据叉积来求出三角形的面积。所以尝试把多边形拆成若干三角形求面积,然后累加得解。

class Polygon {

    Point[] points;

    public Polygon(Point[] points) {
        this.points = points;
    }

    double area() {
        double area = 0.0D;
        for (int i = 1; i < points.length - 1; i++) {
            area += Vector.area(points[0], points[i], points[i+1]);
        }
        return area / 2;
    }

    public static void main(String[] args) {
        Point[] points = {create(3, 0), create(1, 2), create(-1, 2), create(-3, 0), create(-1, -2), create(1, -2)};
        Polygon polygon = new Polygon(points);
        System.out.println(polygon.area());
    }
}

测试数据是一个面积是16的六边形。注意测试数据必须按逆时针顺序的各个顶点。如果按顺时针排列,那么得到的是负数。

值得一提,上述代码也可以求非凸多边形的面积,因为求出的面积是有向的,所以外面的部分可以正负抵消。

鸣谢:《算法竞赛入门经典训练指南》,两角和公式_百度百科

你可能感兴趣的:(几何算法:求多边形面积)