1.題目描述:
設S是平面上n個點的集合,在這一節中,我們考慮在S中找到一個點對p和q的問題,使其相互距離最短。換句話說,希望在S中找到具有這樣性質的兩點p1 = (x1,y1)和p2 = (x2,y2),使它們間的距離在所有S中點對間爲最小
2.解題思路
一共分爲三種情況
情況1:點數小於等於二時:直接計算,求該兩點之間的距離。
情況2:集合中有三個點:兩兩比較,求三個點中的最近的兩個點距離。
情況3:點數大於三時:首先劃分集合S爲SL和SR,使得SL中的每一個點位於SR中每一個點的左邊,並且SL和SR中點數相同。分別在SL和SR中解決最近點對問題,得到DL和DR,分別表示SL和SR中的最近點對的距離。令d=min(DL,DR)。如果S中的最近點對(P1,P2)。P1、P2兩點一個在SL和一個在SR中,那麼P1和P2一定在以L爲中心的間隙內,以L-d和L+d爲界。如圖1.21
圖 1.21
對於情況3, 如果在SL中的點P和在SR中的點Q成爲最近點對,那麼P和Q的距離必定小於d。因此對間隙中的每一個點,在合併步驟中,只需要檢驗yp+d和yp-d內的點即可。
步驟1:根據點的y值和x值對S中的點排序。
步驟2:找出中線L將S劃分爲SL和SR
步驟3:將步驟2遞歸的應用解決SL和SR的最近點對問題,並令d=min(dL,dR)。
步驟4:將L-d~L+d內的點以y值排序,對於每一個點(x1,y1)找出y值在y1-d~y1+d內的接下來的7個點,計算距離爲d’。如果d’小於d,令d=d’,最後的d值就是答案。
對於合併結果的解釋:對於劃分後的圖如圖2.2.2
圖 2.2.2
解釋:
- 設d = min{dll, drr},如果最近點對由Sl中的某個點pl與Sr中的某個點pr組成,則pl和pr一定在劃分線L的距離d內。這樣,如果令S’l和S’r分別表示爲在線L距離內的Sl和Sr 中的點,則pl一定在S’l中, pr一定在S’r中。
- 假設d’≤ d ,則存在兩點pl∈S’l和pr∈S’r ,有d(pl, pr) = d’,從而pl和pr之間的垂直距離不超過d
- 因爲pl,pr這兩點都在以垂直線L爲中心的d×2d矩形區內或其邊界上
- 設T是兩個垂直帶內的點的集合
- 如果在d×2d矩形區內,任意兩點間的距離一定不超過d,則這個矩形最多能容納8個點,其中至多4個點屬於Sl, 4個點屬於Sr。
- T中的每個點最多需要和T中按照y軸排序後鄰接的7個點進行比較。
3.代碼如下:
1 // Closest-Pair.cpp: 定義控制檯應用程序的入口點。
2 //
3
4 #include "stdafx.h"
5 #include<ctime>
6 #include<iostream>
7 #include<cmath>
8 #include<algorithm>
9
10 using namespace std;
11 //定義橫縱座標對大範圍
12 #define RANGE 100.0
13 //定義無限大數字
14 #define INFINATE_DISTANCE 65535
15 //point 點對結構體
16 typedef struct Point {
17 double x;
18 double y;
19 }Point;
20 //隨機生成一定數量座標
21 void SetPoints(Point *points, int length) {
22 srand(unsigned(time(NULL)));
23 for (int i = 0; i < length; i++) {
24 points[i].x = (rand() % (int)(RANGE * 200)) / RANGE - RANGE;
25 points[i].y = (rand() % (int)(RANGE * 200)) / RANGE - RANGE;
26 }
27 }
28 //求出兩點之間距離->Distance
29 double Distance(Point point1, Point point2) {
30 return sqrt((point1.x - point2.x)*(point1.x - point2.x) + (point1.y - point2.y)*(point1.y - point2.y));
31
32 }
33 //自定義排序,按照x座標排序
34 bool CompX(Point a, Point b) {
35 return a.x < b.x;
36 }
37 //自定義排序,按照Y座標排序
38 bool CompY(Point a, Point b) {
39 return a.y < b.y;
40 }
41 //利用分治思想進行查找最近點對->ClosestPair()
42 double ClosestPair(Point points[], int length, Point &a, Point &b) {
43 double distance;
44 double d1, d2;//左右兩邊子集各自的最近距離
45 Point a1, b1, a2, b2;//分割後生成的左右兩邊子集的最近點對
46 int i, j, k,m=0;
47 //先判斷length的個數
48 if (length <2) {
49 distance = INFINATE_DISTANCE;
50 }
51 else if (length == 2) {
52 a = points[0];
53 b = points[1];
54 distance = Distance(a, b);
55 }
56 else {
57
58
59 //如果長度大於3,則生成兩個子集
60
61 //先將points按照x座標進行升序排序
62 sort(points, points + length, CompX);
63 //將升序後的左半邊子集賦值給ptsl,右半邊賦值給ptsr
64 Point *ptsl=new Point[length];
65 Point *ptsr = new Point[length];
66 for (i = 0; i < (length) / 2; i++) {
67 ptsl[i] = points[i];
68 }
69 for (j = 0, k = length / 2; k < length; k++) {
70 ptsr[j++] = points[k];
71 }
72 //對左半邊子集進行遞歸操作,返回d1
73 d1 = ClosestPair(ptsl, length / 2, a1, b1);
74 d2 = ClosestPair(ptsr, length - (length / 2), a2, b2);
75 //對右半邊子集進行遞歸操作,返回d2
76
77 //比較d1,d2,求出兩者之間最小的距離
78 //distance = d1 < d2 ? d1 : d2;
79 if (d1 < d2) {
80 distance = d1; a = a1; b = b1;
81 }
82 else {
83 distance = d2; a = a2; b = b2;
84 }
85 //取得距離中線2δ距離的共K個點
86 double mid = points[(length - 1) / 2].x;//中線
87 Point *pts = new Point[length];
88 for (i = 0, k = 0; i < length; i++) {
89 if (abs(points[i].x - mid) <= distance) {
90 pts[k++] = points[i];
91 }
92 }
93 //將這K個點按照y座標排序
94 sort(pts, pts + k, CompY);
95 //將左邊的點與右半邊的排好序的6個點進行距離比較
96 for (i = 0; i < k; i++) {
97 //只判斷左側部分的點
98 if (pts[i].x - mid > 0)
99 continue;
100 m = 0;
101 //只對按照Y排序後的鄰接6個點進行比較
102 for (j = i + 1; j < i + 6 + m&&j<k; j++) {
103 if (pts[j].x - mid < 0) {
104 //只判斷中線右邊的點
105 m++;
106 continue;
107 }
108 if (Distance(pts[i], pts[j]) < distance) {
109 distance = Distance(pts[i], pts[j]);
110 a = pts[i];
111 b = pts[j];
112 }
113 }
114
115 }
116 }
117 return distance;
118 }
119
120
121 int main()
122 {
123 int num;
124 Point a, b;
125 Point *points;
126 double distance;
127 cout << "請輸入點對的個數:";
128 cin >> num;
129 if (num < 2) {
130 cout << "請輸入數目超過2的個數" << endl;
131 }
132 else {
133
134 points = new Point[num];
135 SetPoints(points, num);
136 cout << "隨機生成的二維點對如下:" << endl;
137 for (int i = 0; i < num; i++) {
138 cout << "(" << points[i].x << "," << points[i].y << ")" << endl;
139
140 }
141 distance = ClosestPair(points, num, a, b);
142 cout << endl <<endl << "按照橫座標排序後的點對爲:" << endl;
143 for (int i = 0; i < num; i++) {
144 cout << "(" << points[i].x << "," << points[i].y << ")" << endl;
145 }
146 cout << "最近距離的點對爲:" << endl;
147 cout << "(" << a.x << "," << a.y << ")" <<" "<< "(" << b.x << "," << b.y << ")" << endl;
148 cout << "最近點對之間的距離爲:" << endl;
149 cout << distance << endl;
150 }
151 return 0;
152 }
4.結果截圖