計算幾何--最小圓覆蓋與最小球覆蓋


參考書籍《算法競賽入門到進階》

  最小圓覆蓋問題:給定n個點的平面坐標,求一個半徑最小的圓,把n個點全部包圍,部分點在圓上。(兩種算法:幾何算法和模擬退火算法)

  幾何算法:(1)加第1個點P1。C1的圓心就是P1,半徑為0。

       (2)加第二個點P2。新的C2的圓心是線段P1P2的中心,半徑為兩點距離的一半。這一步操作是兩點定圓。

       (3)加第三個點P3。若P3在圓內或圓上,忽略;若不在,則以P3為圓心,重復(1)和(2),若還是不行則用三點定圓。

       (4)加第四個點P4。若P4在圓內或圓上,忽略;若不在,則以P4為圓心,從前三個點中選擇一個點重復(1)和(2)即兩點定圓,若還是不行則選三個點進行三點定圓(一定有)。

       (5)繼續加入新的點。

  復雜度分析:3層for循環,貌似是O(n3),但是當點的分布是隨機的時候,可以通過概論計算得到實際復雜度接近O(n),代碼中使用random_shuffle()函數實現。

題目鏈接:https://www.luogu.org/problem/P1742

代碼如下:

 1 #include <bits/stdc++.h>  2 using namespace std;  3 #define eps 1e-8  4 const int maxn = 100000+5;  5 int sgn(double x)  6 {  7 if (fabs(x)<eps) return 0;  8 else return x<0? -1:1;  9 } 10 struct Point 11 { 12 double x,y; 13 }; 14 double Distance(Point A, Point B){return hypot(A.x-B.x,A.y-B.y);} 15 //求三角形abc的外接圓圓心 16 Point circle_center(const Point a, const Point b, const Point c) 17 { 18  Point center; 19 double a1 = b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2; 20 double a2 = c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2; 21 double d=a1*b2-a2*b1; 22 center.x=a.x+(c1*b2-c2*b1)/d; 23 center.y=a.y+(a1*c2-a2*c1)/d; 24 return center; 25 } 26 //求最小圓覆蓋,返回圓心c和半徑r: 27 void min_cover_circle(Point *p, int n,Point &c, double &r) 28 { 29 random_shuffle(p,p+n); //打亂所有點 30 c = p[0];r = 0; //第一個點 31 for (int i = 1; i < n; ++i) //剩下所有點 32  { 33 if (sgn(Distance(p[i],c)-r)>0) //pi在圓外部 34  { 35 c=p[i];r=0; //將圓心設為pi半徑為0 36 for (int j = 0; j < i; ++j) //重新檢查前面的點 37  { 38 if (sgn(Distance(p[j],c)-r)>0)//兩點定圓 39  { 40 c.x=(p[i].x+p[j].x)/2; 41 c.y=(p[i].y+p[j].y)/2; 42 r=Distance(p[j],c); 43 for (int k = 0; k < j; ++k) 44  { 45 if (sgn(Distance(p[k],c)-r)>0) 46  { 47 c=circle_center(p[i],p[j],p[k]); 48 r=Distance(p[i],c); 49  } 50  } 51  } 52  } 53  } 54  } 55 } 56 int main(int argc, char const *argv[]) 57 { 58 int n; 59  Point p[maxn]; 60 Point c;double r; 61 while(~scanf("%d",&n)&&n) 62  { 63 for (int i = 0; i < n; ++i) 64  { 65 scanf("%lf%lf",&p[i].x,&p[i].y); 66  } 67  min_cover_circle(p,n,c,r); 68 printf("%.10lf\n%.10lf %.10lf\n",r,c.x,c.y); 69  } 70 return 0; 71 }

模擬退火算法:由於復雜度高於幾何算法故不作詳解,該算法在數據量小的情況下可用。

代碼如下

 1 void min_cover_circle(Point *p, int n,Point &c, double &r)  2 {  3 double T = 100.0; //初始溫度  4 double delta = 0.98; //降溫系數  5 c = p[0];  6 int pos;  7 while(T>eps) //eps終止溫度  8  {  9 pos = 0; r = 0; 10 for (int i = 0; i <= n-1; ++i) //初始:p[0]是圓心,半徑是0 11  { 12 if (Distance(c,p[i])>r) 13  { 14 r = Distance(c,p[i]); //距圓心最遠的點看到 15 pos = i; 16  } 17 c.x += (p[pos].x-c.x)/r * T; //逼近最后的解 18 c.y += (p[pos].y-c.y)/r * T; 19 T *= delta; 20  } 21  } 22 }

替換上述同名函數。

 

 

  最小球覆蓋:給定n個點的三維坐標,求一個半徑最小的球,把n個點全部包圍進來。(同樣是兩種算法:幾何算法和模擬退火算法)

  幾何算法:(1)加第1個點P1。C1的球心就是P1,半徑為0。

       (2)加第二個點P2。新的C2的球心是線段P1P2的中心,半徑為兩點距離的一半。

       (3)加第三個點P3。三角形P1P2P3的外接球。球心為三角形的外心,半徑為球心到某個點的距離。

       (4)加第四個點P4。若四點共面,則為(3),若不共面,四面體P1P2P3P4一定可以唯一確定一個外接球。

       (5)加第四個點P5。最小球必為其中四個點的外接球。

  但是在該算法中,求三角形外心和四面體的外界球,方程很復雜,代碼量很大,有沒有簡單點的方法呢?

  答案是肯定的,使用模擬退火算法,根據上述推導過程我們知道最小覆蓋球的球心一定與他距離最遠的點有且最多有4個等距離的點。那么我們可以先隨便假設一個點為球心,找到與他距離最遠的點,並移動球心靠近該點,不斷重復此過程,就能找到最小球覆蓋的球心了。

題目鏈接:http://poj.org/problem?id=2069

代碼如下:

 1 #include <bits/stdc++.h>  2 using namespace std;  3 const double eps = 1e-8;  4 const int inf = 0x3f3f3f3f;  5 const double start_T = 1000;  6 struct point3d  7 {  8 double x,y,z;  9 }data[150]; 10 int n; 11 double dis(point3d a, point3d b) 12 { 13 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z)); 14 } 15 double solve() 16 { 17 double step=start_T,ans=inf,mt; 18  point3d z; 19 z.x=z.y=z.z=0; 20 int s=0; 21 while(step>eps) 22  { 23 for (int i = 0; i < n; ++i) 24  { 25 if (dis(z,data[s])<dis(z,data[i])) s=i; 26  } 27 mt=dis(z,data[s]); 28 ans=min(ans,mt); 29 z.x+=(data[s].x-z.x)/start_T*step; 30 z.y+=(data[s].y-z.y)/start_T*step; 31 z.z+=(data[s].z-z.z)/start_T*step; 32 step*=0.97; 33  } 34 return ans; 35 } 36 int main(int argc, char const *argv[]) 37 { 38 double ans; 39 cin>>n; 40 for (int i = 0; i < n; ++i) 41 scanf("%lf%lf%lf",&data[i].x,&data[i].y,&data[i].z); 42 ans=solve(); 43 printf("%.8f\n",ans); 44 return 0; 45 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM