Hello! Welcome back.
In our last lesson, we established a solid framework for the Divide and Conquer strategy, understanding its core components: Divide, Conquer, Combine, and the Base Case. We saw how this pattern applies to familiar algorithms like Merge Sort, where the "Combine" step (merging) does the heavy lifting.
Today, we'll apply this framework to a more complex problem from computational geometry: finding the closest pair of points in a 2D plane. This is a classic example that showcases the real power and elegance of Divide and Conquer, particularly in how a clever "Combine" step can lead to a highly efficient solution. Our goal is to develop and analyze an algorithm that significantly outperforms the obvious brute-force approach.
The Problem and the Brute-Force Baseline
The problem statement is simple: given a set of points on a 2D plane, find the pair of points with the smallest Euclidean distance between them.
The most straightforward approach is to calculate the distance between every possible pair of points and keep track of the minimum distance found. Given your software engineering experience, you'll recognize this as a nested loop structure. With points, there are pairs, leading to an time complexity.
For a small number of points, this is fine. But as grows, quickly becomes impractical. Can we do better? Since this lesson is in the Divide and Conquer module, the answer is a resounding yes. Our goal is to achieve an solution.
Structuring a Divide and Conquer Solution
Let's apply the D&C framework we learned previously.
-
Divide: The most intuitive way to split a set of 2D points is to draw a vertical line that divides them into two roughly equal-sized subsets: a left half () and a right half (). To do this efficiently, we can presort all points based on their x-coordinate and find the median point. This dividing line will pass through that median point.
-
Conquer: We make two recursive calls: one to find the closest pair in and another for . Let's say these calls return minimum distances of and , respectively.
-
Combine: After the recursive calls, we know the closest pair distance is at most . However, the true closest pair might consist of one point from and one from . This "crossing pair" is the crux of the problem.
To find this crossing pair, we don't want to revert to an check. This is where the geometric insights begin.
The "Combine" Step: Finding the Crossing Pair
The key observation is that if a closer pair exists, where and , its distance must be less than . This implies that both and must lie very close to the dividing line. Specifically, both points must be within a distance of from the vertical line.
This insight allows us to restrict our search to a vertical "strip" of width centered on the dividing line.
The following video provides a clear, visual walkthrough of the brute-force method, the D&C setup, and this critical concept of the strip.
How to find the closest pair of points in O(nlogn)? - Inside code
This video from 'Inside code' provides a great introduction to the problem and the initial steps of the D&C solution.
Please watch from the beginning until 03:31. Focus on understanding the D&C setup and the logic behind creating the 2δ-width 'band' (or strip).
The Critical Optimization: Searching the Strip
We've narrowed our search to the points within the strip. However, in the worst case, all points could lie within this strip. A naive comparison of all points in the strip would still lead back to an complexity for the combine step. We need one more crucial insight.
Consider a single point in the strip. We need to find if there's any other point in the strip such that their distance is less than .
- To make this search efficient, we will process the points in the strip sorted by their y-coordinate.
- For a given point , any potential matching point must have a y-coordinate "close" to 's y-coordinate. Specifically, their vertical distance must be less than . This means we only need to check points in a rectangle centered at .
The magical part is this: how many points can possibly exist in that rectangle?
Recall that any two points on the left side of the dividing line are at least apart, and the same is true for any two points on the right. This constraint prevents too many points from being packed closely together. It turns out that for any point , we only need to check a small, constant number of subsequent points in the y-sorted strip.
The proof for this is a beautiful geometric argument. Let's watch a detailed explanation.
Finding the Closest Pair of Points on the Plane: Divide and Conquer
The following lecture from UC Davis provides a careful, step-by-step proof of why only a constant number of neighbors need to be checked for each point in the strip. This directly addresses the 'why' behind the algorithm's efficiency.
Watch from 18:32 to 39:11. The video will first explain the 'delta slice' (our strip) and then use a grid of boxes to prove that you only need to check a small, constant number of points (he arrives at 16, but the exact number isn't as important as the fact that it's constant).
The key takeaway from the video is that the region around point can be divided into small squares. Because we know the minimum distance within each half is , we can prove that at most one point can occupy each small square. This limits the total number of points to check to a small constant (different analyses yield numbers like 6, 7, 8, or 15, but all are constant).
This means the "Combine" step, which seemed like it could be , is actually only . We iterate through the points in the strip, and for each, we do a constant number of comparisons.
For a more formal, text-based proof of this property, you can refer to the resource below, which is an excerpt from the classic CLRS algorithms textbook. It proves that checking the next 7 points is sufficient.
[PDF] 33.4 Finding the closest pair of points
This PDF provides the formal proof found in many textbooks. It's a great supplement to the video's visual explanation.
Read the sections titled 'Combine' and 'Correctness'. Focus on the logic that limits the number of points in the δ × 2δ rectangle, leading to the conclusion that only 7 subsequent points in the y-sorted strip need to be checked.
The Full Algorithm and Its Complexity
Now, we can assemble the complete algorithm and analyze its runtime.
Algorithm: Closest-Pair(P)
- Presort: Create two lists of the input points:
Pxsorted by x-coordinate, andPysorted by y-coordinate. This takes . - Recursive Function
Closest-Util(Px, Py):- Base Case: If
|Px|3, solve by brute force. Return the minimum distance. - Divide: Find the median x-coordinate to partition
PxintoPx_LandPx_R. PartitionPyintoPy_LandPy_Rby iterating throughPyand checking if a point's x-coordinate is in the left or right half. This takes time. - Conquer: Recursively call
d_L = Closest-Util(Px_L, Py_L)andd_R = Closest-Util(Px_R, Py_R). Let . - Combine:
a. Create astriparray containing points fromPywhose x-distance from the dividing line is less than . Thisstriparray remains sorted by y-coordinate. This takes .
b. For each pointpinstrip, calculate its distance to the next ~7 points instrip. Update if a smaller distance is found. This takes time because the inner loop is constant. - Return .
- Base Case: If
The following video segment quickly recaps the algorithm and its complexity analysis.
How to find the closest pair of points in O(nlogn)? - Inside code
Let's return to the 'Inside code' video for a final summary and complexity analysis.
Watch from 06:16 to 11:32. This part recaps the entire process and then uses the Master Theorem to confirm the O(n log n) complexity.
Complexity Analysis
- The initial sorting takes .
- The recurrence relation for our recursive function is .
- comes from the two recursive calls on halves of the data.
- comes from the linear-time work in the Divide (splitting
Py) and Combine (creating and scanning the strip) steps.
As you'll recall from our first module, this recurrence solves to . Therefore, the entire algorithm is dominated by the presorting and the recursive part, resulting in an overall time complexity of .
Conclusion
In this lesson, we successfully applied the Divide and Conquer strategy to a non-trivial geometry problem. We saw that while the "Divide" and "Conquer" steps were relatively straightforward, the "Combine" step required significant ingenuity. By using a clever geometric argument, we reduced the work in the combine step from a potential to a much more efficient , enabling the entire algorithm to run in time.
Key Takeaways:
- The Closest Pair of Points problem can be solved in time using a Divide and Conquer approach.
- The "Combine" step is the most critical part of the algorithm's design.
- The search for a closer "crossing" pair can be restricted to a -wide vertical strip.
- Within this strip, a geometric proof guarantees that for any point, we only need to check a constant number of its neighbors (when sorted by y-coordinate). This is what makes the combine step linear.
- Presorting points by both x and y coordinates is a crucial implementation detail for achieving the desired time complexity.
Preview of the Next Lesson:
We have now explored two powerful applications of Divide and Conquer. Next, we will shift our focus to another fundamental algorithmic design paradigm: Greedy Algorithms. While D&C solves subproblems and then combines their results, a greedy algorithm makes a sequence of locally optimal choices with the hope of arriving at a globally optimal solution. We will begin by exploring the core properties of problems that are solvable with a greedy approach.