圆圈形成的区域数

Number of regions formed by circles

提问人:technotigr 提问时间:9/30/2023 最后编辑:technotigr 更新时间:10/3/2023 访问量:190

问:

我有一个圆圈数组,它们的中心 x 和 y 坐标以及相应的半径。

返回由这些圆圈创建的区域计数作为响应。

例:我有 3 个圆圈:

[x,y,r]
[-2,0,1]
[0,0,2]
[2,0,1]

答:

Number of regions formed are 6.

解释:

enter image description here

5 个区域在圆圈内,1 个区域在圆圈外。

约束:

x & y range from -50 to 50
radius range is 1 to 50

我是 Java 开发人员,我尝试学习圆圈,但仍然无法想出解决这个问题的想法。

解决这个问题的方法是什么?

算法 数学 几何

评论

0赞 Matt Timmermans 9/30/2023
你说的“发现”,是指“计数”?
0赞 technotigr 9/30/2023
@MattTimmermans,是的,我已经更新了我的帖子
0赞 WJS 9/30/2023
所以一个圆会产生两个区域?没有一个圆圈会只显示一个区域?
0赞 technotigr 9/30/2023
@WJS,是的,单个圆圈表示 2 个区域或区域,没有圆圈表示 1 个区域
1赞 Matt Timmermans 10/1/2023
对于这个问题,我能想到的所有解决方案都非常困难。您确定没有任何其他限制吗?

答:

1赞 Luatic 10/2/2023 #1

正如 user85421 的评论中所指出的,我们可以使用欧拉公式:

v - e + f = 2

其中 v 是顶点(交点)的数量,e 是边的数量(交点之间的弧),f 是要求解的面(面积)的数量。我们得到 f = 2 + e - v。每个交点添加的边数是顶点的两倍,因此我们有 e = 2v,因此 f = 2 + 2v - v = 2 + v。

为简单起见,我将做出一个强有力的假设,即没有三个或更多圆在同一点上相交。这意味着两个圆之间的每个交点都是一个节点/顶点。如果无法做出此假设,则必须使用交叉点的哈希集之类的方法对共享交点进行重复数据删除。但是,交点不需要有理坐标,因此由于舍入误差,这可能会有问题;你可以离散化到一定的精度,或者你可以尝试计算出交点的精确表示(假设有理圆坐标),如果可能的话,这很棘手。

在此假设下,您可以只检查每个圆在 O(n²) 时间内与所有其他圆的交集(如果您想加快速度,请查看空间索引数据结构,例如 k-d 树,或者此处的 M 树),将每个交点计为一个顶点。请注意,这将对所有顶点进行两次计数(对两个涉及的圆进行一次计算)。

在对两个圆的交集的案例分析中,我们还需要小心:

  • 两个圆可以相等,在无限多的点上“相交”。我们假设输入中并非如此。
  • 一个圆圈可能在另一个圆圈内;那时他们可能在一个点上接触,或者他们可能根本没有接触。
  • 否则,它们可能在一点、两点或根本不相交。

在我的代码中,我使用了两个精确的比较来比较在一个点上接触的圆。原则上,这是浮点数/双精度的问题,双精度的整数算术是精确的,因此这将适用于某些纯整数的情况。但请注意,由于代码没有使用 ,您可以让它使用在 Java 之上实现的有理数(尽管这超出了本答案的范围)来执行精确计算。sqrtBigInteger

法典:

record Circle(double x, double y, double r) {
    int intersections(Circle other) {
        // If the circles were equal, we would have infinitely many intersections
        assert !equals(other);
        // No need to sqrt (precision and possibly performance concern),
        // just square the radii as well and compare with the squared distance.
        var dx = x - other.x;
        var dy = y - other.y;
        var dSq = dx*dx + dy*dy;
        // Deal with one circle being inside the other
        var dr = Math.abs(r - other.r);
        var drSq = dr*dr;
        if (dSq < drSq) return 0;
        if (dSq == drSq) return 1;
        // Circle isn't inside the other one,
        // compare against total radius
        var tr = r + other.r;
        var trSq = tr*tr;
        if (dSq > trSq) return 0;
        if (dSq == trSq) return 1;
        return 2;
    }
    static int regions(Circle[] circles) {
        int intersections = 0;
        for (int i = 0; i < circles.length; i++) {
            int iIntersections = 0;
            for (int j = 0; j < circles.length; j++) {
                // Don't count intersections of circle with itself
                if (i == j) continue;
                int ijIntersections = circles[i].intersections(circles[j]);
                // This makes the strong assumption that no more than two circles intersect in the same point.
                iIntersections += ijIntersections;
            }
            // In a planar graph, a lone circle is modeled as a single node connected to itself;
            // HACK treat that as two intersections, which has the same effect of adding one region.
            intersections += (iIntersections == 0) ? 2 : iIntersections;
        }
        // Account for the fact that we counted intersections twice.
        assert intersections % 2 == 0;
        return 2 + intersections / 2;
    }
}

这至少可以正确处理您的示例:

System.out.println(Circle.regions(new Circle[] {
    new Circle(-2, 0, 1),
    new Circle(0, 0, 2),
    new Circle(2, 0, 1),
})); // 6