接上文:http://blog.csdn.net/jj12345jj198999/article/details/6619347
最近對【Q7】【標題3】
問題:給定平面上一個點的即可,找出最近兩點之間的距離。
最直接使用歸納法的方法是移去一個點,解決n-1個點的問題,然後再考慮那個額外的點。然而,如果從對n-1個點的情況的求解中僅僅知道他們中間的最小距離,那麼從額外點到其餘n-1個點的距離都需要被計算出來。這樣使得計算距離的總次數變成n-1+n-2+...+1=n(n-1)/2。(這其實是一個直接比較每兩點的演算法)我們想找到一個更快的解法。
一種分治演算法
歸納假設如下所示:
歸納假設:我們已經知道在平面上如何去尋找少於n個點中任意兩點之間的最短距離。
既然我們假設我們能夠解決少於n個點的子問題,我們可以把問題縮減為兩個含有n/2個點的子問題。我們假設n是2的冪,這樣我們總可以把一個集合分成數量兩個相等的集合。(我們再後面再討論這個假設)有很多方法可以把一個點集分成兩個相等的部分。我們可以自由選擇最好的方法。我們想要從規模較小問題的解法中獲得儘可能多有用的資訊,因此我們想要在考慮整個問題時儘可能多的保持有效。看起來這裡把問題分解為兩個不相關各含一半元素的部分是有道理的。當我們找到每個子集中最小的距離後,我們只需要關心那些靠近集合邊界的點之間的距離。距離來說,對所有點進行排序最簡單的方法是按照它們的x座標進行排序,把平面用一條垂直線進行分割從而把集合一分為2(見圖4)。(如果有一些點位於垂直線上我們任意把它們分配給兩個集合中的一個,從而確保兩個集合有相等的元素)我們選擇這樣劃分的原因是使得合并解答時要做的工作儘可能少。如果這兩個部分以某種方式交叉著,那樣會使得對最近對點的檢查變得更複雜。排序需要僅被執行一次。
給定一個集合P,我們按照上面的做法把它分解為P1和P2兩個數量相等的子集。然後我們通過歸納法去尋找每個子集中最近的兩點距離。我們假設P1中最短的距離是d1,P2中是d2。不失一般性的,我們進一步假設d1≤d2。我們需要找到整個集合中最小的距離,也就是說我們需要去尋找P1中的一個點到P2中的一個點是否有小於d1的距離。首先我們注意到只需要考慮以兩部分中間的垂直線為中心寬度為2d1的帶狀範圍內的點就足夠了(見圖4)。在該地區外的點中任意兩點之間不可能擁有比d1更小的距離。通過上面的觀察我們通常能夠把很多點從考慮中排除,但是在最壞的情況下所有的點都可能仍位於這條帶狀地區中,對於它們我們無法使用直接的演算法。
圖4:最近對問題
另一個不是那麼明顯的觀察點是對於在帶中的任意點p,在另一邊有很少的點到p點的距離要比d1小。原因在於在帶中一邊的所有點至少間隔為d1.假設p是帶中的一個點,它的y座標為yp,那麼只需要考慮在另一邊有座標yq且滿足|yp-yq|<d1的點即可。在帶的每一邊最多有六個這樣的點(見圖5中最壞的情況)。結果是如果我們以點的y座標針對所有帶中的點進行排序並且按順序掃描這些點,我們只需要檢查每一個點和按序排列中它的常數個鄰居即可(而不是全部n-1個點)。我們在這裡省略這個事實的證明(見例子[15])。
圖5:最壞情況下六個相隔均為d1的點
最近對演算法{首次嘗試}
{p1,p2...pn:平面上的點}
begin
按照點的x座標組點進行排序{該排序只在開始時運行一次}
把集合劃分成兩個相等的部分
遞迴計算每個部分中最小的距離
把d賦值為兩個最小距離中的最小值
排除出分割線d距離範圍外的點
按照y座標組剩下的點進行排序
按照y順序掃描這些點並計算每個點和它的五個鄰居之間的距離{事實上,4個就夠了}
if 這些距離中有小於d的 then
更新d值
end;
複雜度:按照x座標進行排序需要花費O(nlogn)時間,但是這僅僅需要執行一次。然後我們解決規模為n/2的兩個子問題。排除出帶狀地區以外的點可以在O(n)的時間內完成。接下來在最壞的情況下按照y座標組剩下的點進行排序需要O(nlogn)步。最終,掃描帶中的點並在序列中把它與常數個鄰居進行比較需要O(n)步。總的來說,為了在n個點中尋找最近對,我們在含有n/2個點的子集中
找到兩個最近對,然後花費O(nlogn)的時間去尋找兩個子集之間的最近對(加上一次按照x座標進行排序的時間O(nlogn))。帶來的遞推關係如下:
T(n)=2T(n/2)+O(nlogn), T(2)=1.
這個關係的解答是T(n)=O(n(logn)^2)。這比一個二次的演算法要好,但是我們可以做的更好。現在我們來看看歸納法更巧妙的使用方法。【9/14】