最小圆覆盖算法

5 篇文章 0 订阅
4 篇文章 0 订阅

算法目的:最小圆覆盖算法可以在线性时间复杂度内求出覆盖n个点的最小圆

算法步骤
     ①首先现将所有点随机排列

     ②按顺序把点一个一个的加入(一步一步的求前i个点的最小覆盖圆),每加入一个点就进入③

     ③如果发现当前i号点在当前的最小圆的外面,那么说明点i一定在前i个点的最小覆盖圆边界上,我们转到④来进一步确定这个圆,否则前i个点的最小覆盖圆与前i-1个点的最小覆盖圆是一样的,则不需要更新,返回②

     ④此时已经确认点i一定在前i个点的最小覆盖圆的边界上了,那么我们可以把当前圆的圆心设为第i个点,半径为0,然后重新把前i-1个点加入这个圆中(类似上面的步骤,只不过这次我们提前确定了点i在圆上,目的是一步一步求出包含点i的前j个点的最小覆盖圆),每加入一个点就进入⑤

     ⑤如果发现当前j号点在当前的最小圆的外面,那么说明点j也一定在前j个点(包括i)的最小覆盖圆边界上,我们转到⑥来再进一步确定这个圆,否则前j个点(包括i)的最小覆盖圆与前i-1个点(包括i)的最小覆盖圆是一样的,则不需要更新,返回④

     ⑥此时已经确认点i,j一定在前j个点(包括i)的最小覆盖圆的边界上了,那么我们可以把当前圆的圆心设为第i个点与第j的点连线的中点,半径为到这两个点的距离(就是找一个覆盖这两个点的最小圆),然后重新把前j-1个点加入这个圆中(还是类似上面的步骤,只不过这次我们提前确定了两个点在圆上,目的是求出包含点i,j的前k个点的最小覆盖圆),每加入一个点就进入⑦

     ⑦如果发现当前k号点在当前的最小圆的外面,那么说明点k也一定在前k个点(包括i,j)的最小覆盖圆边界上,我们不需要再进一步确定这个圆了(因为三个点能确定一个圆!),直接求出这三点共圆,否则前k个点(包括i,j)的最小覆盖圆与前k-1个点(包括i,j)的最小覆盖圆是一样的,则不需要更新。

时间复杂度:O(N)

空间复杂度:O(N)

复杂度证明:空间复杂度显然,下面来证时间复杂度

      首先,因为我们已经将点打乱顺序,所以我们可以认为点是随机生成的

      我们知道,当点完全随机时,第i个点在前i-1个点的最小覆盖圆外的几率是3/i

             证明:我们先随机生成i个点,求出他们的最小覆盖圆,我们可以认为这个圆是由现在在边界上的那三个点来确定的,也就是说当随机生成的最后一个点不是那三个点时,新生成的点就在圆内,否则在圆外,所以第i个点在前i-1个点的最小覆盖圆外的概率只有3/i

      所以 O()=i=1in(i3iO(1)+3iO())

      而 O()=i=1in(i3iO(1)+3iO())

      易知 O()=O(N)

      所以可以推出,这个做法是线性的

注意事项:
以上时间复杂度的证明全部基于点的排列随机,如果点的排列不随机,那么时间复杂度将有可能达到 O(N3)
所以最小圆覆盖算法只能在O(N)时间内求出N的点的最小覆盖圆,而不能在O(N)的时间内求出所有的前i个点的最小覆盖圆


下面是一段给定n个点求最小覆盖圆圆心及半径的代码(BZOJ1336/1337)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100010
using namespace std;
struct node{double x,y;}b[N];
node O;
double R;
double sqr(double x){return x*x;}
double dis(node x,node y)
{
    return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));
}
bool incircle(node x)
{
    if(dis(O,x)<=R) return true;
    return false;
}
node solve(double a,double b,double c,double d,double e,double f)
{
    double y=(f*a-c*d)/(b*d-e*a);
    double x=(f*b-c*e)/(a*e-b*d);
    return (node){x,y};
}
int main()
{
    int n;
    scanf("%d",&n);
    int i,j,k;
    for(i=1;i<=n;i++)
    scanf("%lf%lf",&b[i].x,&b[i].y);
    random_shuffle(b+1,b+n+1);
    R=0;
    for(i=1;i<=n;i++)
    if(!incircle(b[i]))
    {
        O.x=b[i].x;O.y=b[i].y;R=0;
        for(j=1;j<i;j++)
        if(!incircle(b[j]))
        {
            O.x=(b[i].x+b[j].x)/2;
            O.y=(b[i].y+b[j].y)/2;
            R=dis(O,b[i]);
            for(k=1;k<j;k++)
            if(!incircle(b[k]))
            {
                O=solve(
                b[i].x-b[j].x,b[i].y-b[j].y,(sqr(b[j].x)+sqr(b[j].y)-sqr(b[i].x)-sqr(b[i].y))/2,
                b[i].x-b[k].x,b[i].y-b[k].y,(sqr(b[k].x)+sqr(b[k].y)-sqr(b[i].x)-sqr(b[i].y))/2 
                );
                R=dis(b[i],O);
            }
        }
    }
    printf("%.10lf\n%.10lf %.10lf",R,O.x,O.y);
}
  • 15
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
最小覆盖问题是一个经典的计算几何问题,可以使用 Welzl 算法(也称作随机增量法)在 O(n) 的时间复杂度内解决。下面是 C# 实现的最小覆盖算法: ```csharp using System; using System.Collections.Generic; namespace MinCircleCover { public class Point { public double x; public double y; public Point(double x, double y) { this.x = x; this.y = y; } } public class Circle { public double x; public double y; public double r; public Circle(double x, double y, double r) { this.x = x; this.y = y; this.r = r; } } public class MinCircleCover { private static Random rand = new Random(); private static bool IsInCircle(Point p, Circle c) { double dx = p.x - c.x; double dy = p.y - c.y; double d = Math.Sqrt(dx * dx + dy * dy); return d <= c.r; } private static Circle MinCircleWithTwoPoints(List<Point> points, Point p, Point q) { double cx = (p.x + q.x) / 2; double cy = (p.y + q.y) / 2; double r = Math.Sqrt((p.x - q.x) * (p.x - q.x) + (p.y - q.y) * (p.y - q.y)) / 2; Circle c = new Circle(cx, cy, r); foreach (Point point in points) { if (!IsInCircle(point, c)) { c = MinCircleWithTwoPoints(points, p, point); if (!IsInCircle(point, c)) { c = MinCircleWithTwoPoints(points, q, point); } } } return c; } private static Circle MinCircleWithOnePoint(List<Point> points, Point p) { Circle c = new Circle(p.x, p.y, 0); foreach (Point point in points) { if (!IsInCircle(point, c)) { c = MinCircleWithTwoPoints(points, p, point); } } return c; } private static Circle MinCircle(List<Point> points, List<Point> boundary) { if (boundary.Count == 3) { return new Circle(boundary[0].x, boundary[0].y, Math.Sqrt(Math.Max(Math.Max((boundary[1].x - boundary[0].x) * (boundary[1].x - boundary[0].x) + (boundary[1].y - boundary[0].y) * (boundary[1].y - boundary[0].y), (boundary[2].x - boundary[0].x) * (boundary[2].x - boundary[0].x) + (boundary[2].y - boundary[0].y) * (boundary[2].y - boundary[0].y)), (boundary[2].x - boundary[1].x) * (boundary[2].x - boundary[1].x) + (boundary[2].y - boundary[1].y) * (boundary[2].y - boundary[1].y))) / 2); } else if (boundary.Count == 2 || points.Count == 0) { return MinCircleWithOnePoint(points, boundary[0]); } else { int index = rand.Next(points.Count); Point p = points[index]; points.RemoveAt(index); Circle c = MinCircle(points, boundary); if (!IsInCircle(p, c)) { boundary.Add(p); c = MinCircle(points, boundary); boundary.RemoveAt(boundary.Count - 1); } points.Insert(index, p); return c; } } public static Circle MinCircleCover(List<Point> points) { List<Point> boundary = new List<Point>(); return MinCircle(points, boundary); } } } ``` 其中 Point 和 Circle 类分别代表点和的结构体,MinCircleCover 类实现了最小覆盖算法算法递归地处理点集和边界点集,每次从点集中随机选择一个点,如果该点在当前最小中,就直接忽略;否则,将该点加入边界点集,并递归处理点集和边界点集,最后返回最小

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值