1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
struct Point {
float x;
float y;
};
Pair< Pair< Point, Point>, float > brute_force_closest_pair(
vector<Point> vp
) {
float min = std::numeric_limits<float>::infinity();
Pair<Point, Point> CP;
int vpsz = vp.size();
// compare all of them
for ( int i = 0; i < vpsz; ++i ) {
for ( int j = 0; j < vpsz; ++j ) {
d = euclidean_distance( vp[i], vp[j] );
if ( d < min ) {
min = d;
CP = Pair<Point, Point>( vp[i], vp[j] );
}
}
}
return Pair< Pair< Point, Point>, float> ( CP, min );
}
// big-O time complexity is in O(nlogn)
Pair< Pair< Point, Point>, float > closest_pair(
unordered_set<Point> P,
vector<Point> X,
vector<Point> y
) {
int sz = P.size();
// base case
// because the brute force method is only called with a max of 3 points,
// the worst case is that the brute force algorithm will need to check
// 9 pairs of points implies this call has constant big-O time complexity
if ( sz <= 3 )
return brute_force_closest_pair( X );
// linear big-O time complexity
// because X is alread assumed to be sorted by the points x values,
// to parition into XL and XR, put the first half in XL, second in XR
vector<Point> XL( X.begin(), X.begin() + sz/2 - 1 );
vector<Point> XR( X.begin() + sz/2, X.end() );
// linear big-O time complexity ( unorder_set is hash based )
// make PL and PR from XL and XR
unordered_set<Point> PL( XL.begin(), XL.end() );
unordered_set<Point> PR( XR.begin(), XR.end() );
// linear big-O time complexity
// now split Y based on membership in PL and PR into YL and YR
// YL and YR remain sorted by y values because Y is assumed to be
// sorted by Y values, and we iterate through and insert into YL, YR,
// in order
for ( int i = 0; i < sz; ++i ) {
// constant big-O time complexity ( unordered_set is hash based )
unordered_set<Point>::iterator it = PL.find( Y[i] );
if ( it != PL.end() )
YL.push_back( Y[i] );
else
YR.push_back( Y[i] );
}
// recursive calls
// because with each recursive call, the problem is divided in half,
// these happens lgn times
Pair< Pair< Point, Point >, float > CPL = closest_pair( PL, XL, YL );
Pair< Pair< Point, Point >, float > CPR = closest_pair( PR, XR, YR );
// choose the closest pair of the two returned by the recursive call
Pair< Point, Point > CP = CPL.second < CPR.second ? CPL : CPR;
// represents the vertical line separating PL and PR
// make it's x value the average of the two most center points on x-axis
float lX = ( X[ sz/2 - 1 ] + X[ sz/2 ] ) / 2.0;
// make a new vector containing all points within the min distance of lX
// if the closest pair has one Point in XR, and the other in XL,
// then it must be within this 2*CP.second strip centered at lX
vector<Point> YP;
for ( int i = 0; i < sz; ++i )
if ( abs( Y[i].x - lX ) <= CP.second )
YP.push_back( Y[i] );
// time complexity for this part is linear.
// Now check for points within this strip ( in YP ), that are
// closer than the closest points either both in PL or both in PR.
// For each point in YP, we only need to check the next 7 because
// we know that each pair of points in the left side of the strip are
// at least CP.second away from eachother, and it follows that only
// 8 points can reside in the area we are interested in looking at.
// So we only need to check the next 7 points in YP.
int ypsz = YP.size();
for ( int i = 0; i < ypsz; ++i ) {
for ( int j = 0; j < 7 && i + j < ypsz; ++j ) {
float dP = distance( YP[i], YP[i+j] );
if ( dP < CP.second ){
CP.first = Pair< Point, Point>( YP[i], YP[i+j] );
CP.second = dP;
}
}
}
return CP;
}
|