|
| 1 | +package com.thealgorithms.randomized; |
| 2 | +import java.util.*; |
| 3 | +import java.math.BigDecimal; |
| 4 | +import java.math.RoundingMode; |
| 5 | +/** |
| 6 | +* This class implements the randomized Closest Pair Algorithm; given some number of points |
| 7 | + * in a plane find the pair with minimum euclidean distance from each other. This solution |
| 8 | + * uses the divide and conquer approach. |
| 9 | + * @author Bri Harris |
| 10 | +*/ |
| 11 | + |
| 12 | +import java.util.*; |
| 13 | + |
| 14 | +class Point implements Comparable<Point> { |
| 15 | + double x, y; |
| 16 | + |
| 17 | + // Constructor to initialize a point with x and y coordinates |
| 18 | + Point(double x, double y) { |
| 19 | + this.x = x; |
| 20 | + this.y = y; |
| 21 | + } |
| 22 | + |
| 23 | + // Compare points based on x-coordinates (for sorting) |
| 24 | + public int compareTo(Point other) { |
| 25 | + return Double.compare(this.x, other.x); |
| 26 | + } |
| 27 | + |
| 28 | + // Compute Euclidean distance between two points |
| 29 | + static double distance(Point p1, Point p2) { |
| 30 | + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +public class ClosestPair { |
| 35 | + public static double closest(List<Point> points) { |
| 36 | + Collections.sort(points); |
| 37 | + double result = closestRecursiveHelper(points, 0, points.size() - 1); |
| 38 | + |
| 39 | + //Return distance of closest pair rounded to 2 decimal places |
| 40 | + return new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).doubleValue(); |
| 41 | + } |
| 42 | + |
| 43 | + private static double closestRecursiveHelper(List<Point> points, int left, int right) { |
| 44 | + //Base Case occurs with 3 or fewer points |
| 45 | + if (right - left <= 2) return baseCase(points, left, right); |
| 46 | + |
| 47 | + |
| 48 | + //Divide and conquer |
| 49 | + int mid = (left + right) / 2; |
| 50 | + double midX = points.get(mid).x; |
| 51 | + |
| 52 | + double leftDist = closestRecursiveHelper(points, left, mid); |
| 53 | + double rightDist = closestRecursiveHelper(points, mid + 1, right); |
| 54 | + |
| 55 | + double minDist = Math.min(leftDist, rightDist); |
| 56 | + |
| 57 | + return checkBoundary(points, left, right, midX, minDist); |
| 58 | + } |
| 59 | + |
| 60 | + private static double baseCase(List<Point> points, int left, int right) { |
| 61 | + // Sub-problems fitting the base case can use brute force |
| 62 | + double minDist = Double.MAX_VALUE; |
| 63 | + for (int i = left; i <= right; i++) { |
| 64 | + for (int j = i + 1; j <= right; j++) { |
| 65 | + minDist = Math.min(minDist, Point.distance(points.get(i), points.get(j))); |
| 66 | + } |
| 67 | + } |
| 68 | + return minDist; |
| 69 | + } |
| 70 | + |
| 71 | + private static double checkBoundary(List<Point> points, int left, int right, double midX, double minDist) { |
| 72 | + //Consider a boundary by the dividing line |
| 73 | + List<Point> boundary = new ArrayList<>(); |
| 74 | + for (int i = left; i <= right; i++) { |
| 75 | + if (Math.abs(points.get(i).x - midX) < minDist) boundary.add(points.get(i)); |
| 76 | + } |
| 77 | + |
| 78 | + //sort by y coordinate within the boundary and check for closer points |
| 79 | + boundary.sort(Comparator.comparingDouble(p -> p.y)); |
| 80 | + for (int i = 0; i < boundary.size(); i++) { |
| 81 | + for (int j = i + 1; j < boundary.size() && (boundary.get(j).y - boundary.get(i).y) < minDist; j++) { |
| 82 | + minDist = Math.min(minDist, Point.distance(boundary.get(i), boundary.get(j))); |
| 83 | + } |
| 84 | + } |
| 85 | + return minDist; |
| 86 | + } |
| 87 | +} |
0 commit comments