JavaScript 角度扫描(给定半径的圆内可以包含的最大点数)

 JavaScript 角度扫描(给定半径的圆内可以包含的最大点数)【Angular Sweep (Maximum points that can be enclosed in a circle of given radius)】

给定二维平面上的 n 个点,求出最多有多少个点可以被半径为 R 的定半径圆包围。 

注意:即使点位于圆周上,也被认为位于圆内。

例子: 

输入:R = 1 
            points[] = {(6.47634, 7.69628), (5.16828 4.79915), 
                              (6.69533 6.20378)}
输出:2

最大点数为 2

输入:R = 1 
              points[] = {(6.65128, 5.47490), (6.42743, 6.26189) 
                                (6.35864, 4.61611), (6.59020 4.54228), (4.43967 5.70059) 
                                (4.38226, 5.70536), (5.50755 6.18163), (7.41971 6.13668) 
                                (6.71936, 3.04496), (5.61832, 4.23857), (5.99424, 4.29328) 
                                (5.60961, 4.32998), (6.82242, 5.79683), (5.44693, 3.82724) | 
                                (6.70906, 3.65736), (7.89087, 5.68000), (6.23300, 4.59530) 
                                (5.92401, 4.92329), (6.24168, 3.81389), (6.22671, 3.62210)}
输出:11

最大点数为 11

朴素算法:

    对于给定集合中的任意一对点(例如 A 和 B),构造半径为“R”且与这两个点都相切的圆。这样的圆最多有 2 个。正如我们在这里看到的,对于 CASE 1,即 2,最多可能有 2 个圆。

1、对于每个构造的圆,检查集合中的每个点是否位于圆内。

2、返回包含最大点数的圆。

时间复杂度:有n C ^ 2个点对,对应最多可以有2 n C ^ 2 个圆。对于每个圆,需要检查(n-2)个点。这使得朴素算法复杂度为 O(n ^ 3 )。

角度扫描算法:

    通过使用角度扫描 (Angular Sweep),我们可以在O(n 2 log n) 的复杂度内解决这个问题。该算法的基本逻辑思想如下。

    我们从给定集合中选取任意一个点 P。然后,我们围绕点 P 旋转一个半径为“R”的圆。在整个旋转过程中,P 位于圆的圆周上,我们保持圆中点数的计数为给定值 ?,其中参数 ? 决定了旋转角度。因此,由于半径是固定的,因此圆的状态可以通过单个参数 ? 来确定。 

    我们还可以看到,仅当集合中的点进入或退出圆时,维护的计数值才会改变。

在给定的图中,C1 是 ? = 0 的圆,C2 是将圆旋转至 ? 的某个值时构造的圆。
这样一来,问题就简化为如何维护 count 的值。 
对于除 P 之外的任何给定点(例如 Q),我们可以轻松计算出该点进入圆时 ? 的值(设 ?),以及该点离开圆时 ? 的值(设 ?)。 

我们对角度 A 和 B 的定义如下, 

    A 是 PQ 和 X 轴之间的角度。

    B 是 PC 和 PQ 之间的角度,其中 C 是圆心。

其中,x 和 y 表示点的坐标,“d”是 P 和 Q 之间的距离。
现在,从图中我们可以看到,

?= AB 
?= A + B

(注意:所有角度均相对于 X 轴。因此,它变成“A-B”而不是“B-A”)。

当 Q 进入圆时 

当 Q 退出圆时 

我们可以计算除 P 点之外所有点的 A 角和 B 角。找到这些角后,我们对它们进行排序,然后按升序遍历。现在我们维护一个计数器,它告诉我们在特定时刻圆内有多少个点。 
只有当一个点进入或退出圆时,计数才会改变。如果我们找到一个进入角,我们就将计数器加 1;如果我们找到一个退出角,我们就将计数器减 1。使用标志位可以轻松检查角度是进入角还是退出角。 
如此循环下去,计数器始终会返回一个有效值,用于表示特定状态下圆内点的数量。

重要提示: 'd'>2R 的点不需要考虑,因为它们永远不会进入或退出圆。

角度扫描算法可以描述为:

    1、计算每对n C 2点之间的距离并存储【n C 2如图:】。

    2、对于任意点(比如 P),使用 getPointsInside() 函数获取位于围绕 P 旋转的圆内的点的最大数量。

    3、返回的所有值中的最大值将作为最终答案。

以下是上述方法的实现:

// JavaScript program to find the maximum number of
// points that can be enclosed by a fixed-radius
// circle

const MAX_POINTS = 500;

// complex class which has
// been used to implement points. This helps to
// ensure greater functionality easily
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    subtract(other) {
        return new Point(this.x - other.x, this.y - other.y);
    }
    magnitude() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    }
    arg() {
        return Math.atan2(this.y, this.x);
    }
}
const arrPoints = [new Point(6.47634, 7.69628),
                    new Point(5.16828, 4.79915),
                    new Point(6.69533, 6.20378)];
const dis = new Array(MAX_POINTS).fill(0).map(() => new Array(MAX_POINTS).fill(0));

// This function returns the maximum points that
// can lie inside the circle of radius 'r' being
// rotated about point 'i'
function mycompare(A, B) {
    if (A.first < B.first) {
        return -1;
    } else if (A.first > B.first) {
        return 1;
    } else {
        return A.second == 1 ? -1 : 1;
    }
}
function getPointsInside(i, r, n) {
    
    // This vector stores alpha and beta and flag
    // is marked true for alpha and false for beta
    let angles = [];
    for (let j = 0; j < n; j++) {
        if (i != j && dis[i][j] <= 2 * r) {
            
            // acos returns the arc cosine of the complex
            // used for cosine inverse
            let B = Math.acos(dis[i][j] / (2 * r));
            
            // arg returns the phase angle of the complex
            let A = arrPoints[j].subtract(arrPoints[i]).arg();
            let alpha = A - B;
            let beta = A + B;
            angles.push([alpha, true]);
            angles.push([beta, false]);
        }
    }
    
    // angles vector is sorted and traversed
    angles.sort(mycompare);
    
    // count maintains the number of points inside
    // the circle at certain value of theta
    // res maintains the maximum of all count
    let count = 1;
    let res = 1;
    for (let i = 0; i < angles.length; i++) {
        
        // entry angle
        if (angles[i][1]) {
            count++;
        } 
        
        // exit angle
        else {
            count--;
        }
        if (count > res) {
            res = count;
        }
    }
    return res;
}

// Returns count of maximum points that can lie
// in a circle of radius r.
function maxPoints(arrPoints, n, r) {
    
    // dis array stores the distance between every
    // pair of points
    for (let i = 0; i < n - 1; i++) {
        for (let j = i + 1; j < n; j++) {
            
            // abs gives the magnitude of the complex
            // number and hence the distance between
            // i and j
            dis[i][j] = dis[j][i] = arrPoints[i].subtract(arrPoints[j]).magnitude();
        }
    }
    
    // This loop picks a point p
    let ans = 0;
    for (let i = 0; i < n; i++) {
        
        // maximum number of points for point arr[i]
        ans = Math.max(ans, getPointsInside(i, r, n));
    }
    return ans;
}

// Driver code
const n = arrPoints.length;
const r = 1;
console.log(`The maximum number of points are: ${maxPoints(arrPoints, n, r)}`);
// This Code is Contributed by Prasad Kandekar(prasad264)

输出: 

最大点数为:2

时间复杂度:我们调用 getPointsInside() 函数来获取 n 个点。该函数作用于 n-1 个点,得到大小为 2*(n-1) 的向量“角度”(一个入射角和一个出射角)。现在,对这个“角度”向量进行排序和遍历,使得 getPointsInside() 函数的复杂度等于 O(nlogn)。这使得角度扫描算法复杂度为 O(n 2 log n)。

空间复杂度:O(n),因为使用了辅助空间来存储向量。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hefeng_aspnet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值