From 82fb1f55e11416deb0e6b8d26d8dd7915c5e1710 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Thu, 29 May 2025 19:58:28 -0400 Subject: [PATCH 01/23] closest pair program --- .../thealgorithms/randomized/ClosestPair.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/main/java/com/thealgorithms/randomized/ClosestPair.java diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java new file mode 100644 index 000000000000..04fd9dadd65c --- /dev/null +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -0,0 +1,87 @@ +package com.thealgorithms.randomized; +import java.util.*; +import java.math.BigDecimal; +import java.math.RoundingMode; +/** +* This class implements the randomized Closest Pair Algorithm; given some number of points + * in a plane find the pair with minimum euclidean distance from each other. This solution + * uses the divide and conquer approach. + * @author Bri Harris +*/ + +import java.util.*; + +class Point implements Comparable { + double x, y; + + // Constructor to initialize a point with x and y coordinates + Point(double x, double y) { + this.x = x; + this.y = y; + } + + // Compare points based on x-coordinates (for sorting) + public int compareTo(Point other) { + return Double.compare(this.x, other.x); + } + + // Compute Euclidean distance between two points + static double distance(Point p1, Point p2) { + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); + } +} + +public class ClosestPair { + public static double closest(List points) { + Collections.sort(points); + double result = closestRecursiveHelper(points, 0, points.size() - 1); + + //Return distance of closest pair rounded to 2 decimal places + return new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).doubleValue(); + } + + private static double closestRecursiveHelper(List points, int left, int right) { + //Base Case occurs with 3 or fewer points + if (right - left <= 2) return baseCase(points, left, right); + + + //Divide and conquer + int mid = (left + right) / 2; + double midX = points.get(mid).x; + + double leftDist = closestRecursiveHelper(points, left, mid); + double rightDist = closestRecursiveHelper(points, mid + 1, right); + + double minDist = Math.min(leftDist, rightDist); + + return checkBoundary(points, left, right, midX, minDist); + } + + private static double baseCase(List points, int left, int right) { + // Sub-problems fitting the base case can use brute force + double minDist = Double.MAX_VALUE; + for (int i = left; i <= right; i++) { + for (int j = i + 1; j <= right; j++) { + minDist = Math.min(minDist, Point.distance(points.get(i), points.get(j))); + } + } + return minDist; + } + + private static double checkBoundary(List points, int left, int right, double midX, double minDist) { + //Consider a boundary by the dividing line + List boundary = new ArrayList<>(); + for (int i = left; i <= right; i++) { + if (Math.abs(points.get(i).x - midX) < minDist) boundary.add(points.get(i)); + } + + //sort by y coordinate within the boundary and check for closer points + boundary.sort(Comparator.comparingDouble(p -> p.y)); + for (int i = 0; i < boundary.size(); i++) { + for (int j = i + 1; j < boundary.size() && (boundary.get(j).y - boundary.get(i).y) < minDist; j++) { + minDist = Math.min(minDist, Point.distance(boundary.get(i), boundary.get(j))); + } + } + return minDist; + } +} From f1391bd253817658df7256cd702fa26705a860ba Mon Sep 17 00:00:00 2001 From: bri-harris Date: Thu, 29 May 2025 20:18:19 -0400 Subject: [PATCH 02/23] closest pair program --- .../java/com/thealgorithms/randomized/ClosestPair.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 04fd9dadd65c..d62a61d29849 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -12,7 +12,8 @@ import java.util.*; class Point implements Comparable { - double x, y; + double x; + double y; // Constructor to initialize a point with x and y coordinates Point(double x, double y) { @@ -20,12 +21,10 @@ class Point implements Comparable { this.y = y; } - // Compare points based on x-coordinates (for sorting) public int compareTo(Point other) { return Double.compare(this.x, other.x); } - // Compute Euclidean distance between two points static double distance(Point p1, Point p2) { return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); } @@ -44,7 +43,6 @@ private static double closestRecursiveHelper(List points, int left, int r //Base Case occurs with 3 or fewer points if (right - left <= 2) return baseCase(points, left, right); - //Divide and conquer int mid = (left + right) / 2; double midX = points.get(mid).x; @@ -72,7 +70,9 @@ private static double checkBoundary(List points, int left, int right, dou //Consider a boundary by the dividing line List boundary = new ArrayList<>(); for (int i = left; i <= right; i++) { - if (Math.abs(points.get(i).x - midX) < minDist) boundary.add(points.get(i)); + if (Math.abs(points.get(i).x - midX) < minDist) { + boundary.add(points.get(i)); + } } //sort by y coordinate within the boundary and check for closer points From 63ddb36b147802610a925aab8a50c905eefffa05 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Thu, 29 May 2025 20:57:36 -0400 Subject: [PATCH 03/23] As required for PRs, formatted with clang-format --- .../thealgorithms/randomized/ClosestPair.java | 31 ++++++------- .../randomized/ClosestPairTest.java | 43 +++++++++++++++++++ 2 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 src/test/java/com/thealgorithms/randomized/ClosestPairTest.java diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index d62a61d29849..69cb8e1612a7 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -1,17 +1,9 @@ package com.thealgorithms.randomized; -import java.util.*; import java.math.BigDecimal; import java.math.RoundingMode; -/** -* This class implements the randomized Closest Pair Algorithm; given some number of points - * in a plane find the pair with minimum euclidean distance from each other. This solution - * uses the divide and conquer approach. - * @author Bri Harris -*/ - import java.util.*; -class Point implements Comparable { +final class Point implements Comparable { double x; double y; @@ -30,20 +22,29 @@ static double distance(Point p1, Point p2) { } } -public class ClosestPair { +public final class ClosestPair { + public static double closest(List points) { + if (points == null || points.isEmpty()) { + throw new IllegalArgumentException("There are no pairs to compare."); + } + + if (points.size() == 1) { + throw new IllegalArgumentException("There is only one pair."); + } + Collections.sort(points); double result = closestRecursiveHelper(points, 0, points.size() - 1); - //Return distance of closest pair rounded to 2 decimal places + // Return distance of closest pair rounded to 2 decimal places return new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).doubleValue(); } private static double closestRecursiveHelper(List points, int left, int right) { - //Base Case occurs with 3 or fewer points + // Base Case occurs with 3 or fewer points if (right - left <= 2) return baseCase(points, left, right); - //Divide and conquer + // Divide and conquer int mid = (left + right) / 2; double midX = points.get(mid).x; @@ -67,7 +68,7 @@ private static double baseCase(List points, int left, int right) { } private static double checkBoundary(List points, int left, int right, double midX, double minDist) { - //Consider a boundary by the dividing line + // Consider a boundary by the dividing line List boundary = new ArrayList<>(); for (int i = left; i <= right; i++) { if (Math.abs(points.get(i).x - midX) < minDist) { @@ -75,7 +76,7 @@ private static double checkBoundary(List points, int left, int right, dou } } - //sort by y coordinate within the boundary and check for closer points + // sort by y coordinate within the boundary and check for closer points boundary.sort(Comparator.comparingDouble(p -> p.y)); for (int i = 0; i < boundary.size(); i++) { for (int j = i + 1; j < boundary.size() && (boundary.get(j).y - boundary.get(i).y) < minDist; j++) { diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java new file mode 100644 index 000000000000..705b188a1e30 --- /dev/null +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -0,0 +1,43 @@ +package com.thealgorithms.randomized; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class ClosestPairTest { + + // Tests sorting of an array with multiple elements, including duplicates. + @Test + public void testMultiplePairs() { + List points = Arrays.asList(new Point(1, 2), new Point(3, 4), new Point(5, 1), new Point(7, 8), new Point(2, 3), new Point(6, 2)); + double expected = 1.41; + assertEquals(expected, ClosestPair.closest(points)); + } + + // Test if there are no pairs. + @Test + public void testNoPoints() { + List points = new ArrayList<>(); + Exception exception = assertThrows(IllegalArgumentException.class, () -> { ClosestPair.closest(points); }); + assertEquals("There are no pairs to compare.", exception.getMessage()); + } + + // Test if there is one point, no pairs. + @Test + public void testOnePoint() { + List points = Arrays.asList(new Point(1, 2)); + Exception exception = assertThrows(IllegalArgumentException.class, () -> { ClosestPair.closest(points); }); + assertEquals("There is only one pair.", exception.getMessage()); + } + + // Test if there is a duplicate points as a pair + @Test + public void testPoints() { + List points = Arrays.asList(new Point(1, 2), new Point(5, 1), new Point(5, 1), new Point(7, 8), new Point(2, 3), new Point(6, 2)); + double expected = 0.00; + assertEquals(expected, ClosestPair.closest(points)); + } +} From 21fed7da57a1bc1151a2f4fde5665f03049a2dda Mon Sep 17 00:00:00 2001 From: bri-harris Date: Thu, 29 May 2025 21:04:44 -0400 Subject: [PATCH 04/23] As required for PRs, URL of algorithm solution included in a comment --- src/main/java/com/thealgorithms/randomized/ClosestPair.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 69cb8e1612a7..52a5eb4b5633 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -3,6 +3,11 @@ import java.math.RoundingMode; import java.util.*; +// As required by Repository, new algorithms have URL in comments with explanation +// https://www.geeksforgeeks.org/closest-pair-of-points-using-divide-and-conquer-algorithm +// Given 2 or more points on a 2-dimensional plane, find the closest 2 points in Euclidean distance +// This class uses the divide and conquer technique with recursion + final class Point implements Comparable { double x; double y; From b4c88a71ebc9577dd0da6c62175f85adf0640426 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Thu, 29 May 2025 21:18:51 -0400 Subject: [PATCH 05/23] Fixed build errors --- .../java/com/thealgorithms/randomized/ClosestPair.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 52a5eb4b5633..483c5027033e 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -1,7 +1,10 @@ package com.thealgorithms.randomized; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; // As required by Repository, new algorithms have URL in comments with explanation // https://www.geeksforgeeks.org/closest-pair-of-points-using-divide-and-conquer-algorithm @@ -47,7 +50,9 @@ public static double closest(List points) { private static double closestRecursiveHelper(List points, int left, int right) { // Base Case occurs with 3 or fewer points - if (right - left <= 2) return baseCase(points, left, right); + if (right - left <= 2) { + return baseCase(points, left, right); + } // Divide and conquer int mid = (left + right) / 2; From d7949d64120cb64eca1142ffb52fe704058ff0ac Mon Sep 17 00:00:00 2001 From: bri-harris Date: Thu, 29 May 2025 21:36:04 -0400 Subject: [PATCH 06/23] Private constructor to prevent instanciation --- src/main/java/com/thealgorithms/randomized/ClosestPair.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 483c5027033e..03d3a06ef46e 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -31,6 +31,10 @@ static double distance(Point p1, Point p2) { } public final class ClosestPair { + // Private constructor to prevent instantiation + private ClosestPair() { + throw new AssertionError("Utility class should not be instantiated."); + } public static double closest(List points) { if (points == null || points.isEmpty()) { From a7c2163410f66ec3265b15ffda7441f47f960377 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Thu, 29 May 2025 21:43:30 -0400 Subject: [PATCH 07/23] Fix bigDecimal erroref --- src/main/java/com/thealgorithms/randomized/ClosestPair.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 03d3a06ef46e..72e46ab2ed86 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -49,7 +49,7 @@ public static double closest(List points) { double result = closestRecursiveHelper(points, 0, points.size() - 1); // Return distance of closest pair rounded to 2 decimal places - return new BigDecimal(result).setScale(2, RoundingMode.HALF_UP).doubleValue(); + return new BigDecimal(String.valueOf(result)).setScale(2, RoundingMode.HALF_UP).doubleValue(); } private static double closestRecursiveHelper(List points, int left, int right) { From 852c2e7a3de2093d9f1b03b2cc4aa5fa491f7f90 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Thu, 29 May 2025 21:59:13 -0400 Subject: [PATCH 08/23] fix infer issue? --- .inferconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.inferconfig b/.inferconfig index 6af4f9e2e818..041ff2f55660 100644 --- a/.inferconfig +++ b/.inferconfig @@ -19,6 +19,8 @@ "src/test/java/com/thealgorithms/datastructures/trees/LazySegmentTreeTest.java", "src/test/java/com/thealgorithms/searches/QuickSelectTest.java", "src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java", - "src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java" + "src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java", + "src/main/java/com/thealgorithms/randomized/ClosestPair.java", + "src/test/java/com/thealgorithms/randomized/ClosestPairTest.java" ] } From f854cf737863a3330e362764e234ae67569bf4df Mon Sep 17 00:00:00 2001 From: Bri Harris Date: Fri, 30 May 2025 04:41:08 -0400 Subject: [PATCH 09/23] Update .inferconfig --- .inferconfig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.inferconfig b/.inferconfig index 041ff2f55660..6af4f9e2e818 100644 --- a/.inferconfig +++ b/.inferconfig @@ -19,8 +19,6 @@ "src/test/java/com/thealgorithms/datastructures/trees/LazySegmentTreeTest.java", "src/test/java/com/thealgorithms/searches/QuickSelectTest.java", "src/test/java/com/thealgorithms/stacks/PostfixToInfixTest.java", - "src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java", - "src/main/java/com/thealgorithms/randomized/ClosestPair.java", - "src/test/java/com/thealgorithms/randomized/ClosestPairTest.java" + "src/test/java/com/thealgorithms/strings/HorspoolSearchTest.java" ] } From b18bce9827e471d1ec82cd3fdba302af8d355b76 Mon Sep 17 00:00:00 2001 From: Bri Harris Date: Fri, 30 May 2025 04:45:22 -0400 Subject: [PATCH 10/23] Update infer.yml --- .github/workflows/infer.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index ffadd13ff3bd..9302d04ae98e 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -25,6 +25,10 @@ jobs: with: ocaml-compiler: 5 + - name: Pin pyml to required version + run: | + opam pin add pyml 20220905 --yes + - name: Get current year/weak run: echo "year_week=$(date +'%Y_%U')" >> $GITHUB_ENV From d1f7ec5f2973185d44e6c66c77497ae8246d334d Mon Sep 17 00:00:00 2001 From: Bri Harris Date: Fri, 30 May 2025 04:53:42 -0400 Subject: [PATCH 11/23] Trying to syncronise dependancies --- .github/workflows/infer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index 9302d04ae98e..effaf27f1a7e 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -23,7 +23,7 @@ jobs: - name: Set up OCaml uses: ocaml/setup-ocaml@v3 with: - ocaml-compiler: 5 + ocaml-compiler: 4.14.0 - name: Pin pyml to required version run: | From f49710a8a4e990007ad39881196dc4ae80883fc8 Mon Sep 17 00:00:00 2001 From: Deniz Altunkapan Date: Mon, 2 Jun 2025 23:22:59 +0200 Subject: [PATCH 12/23] fix: Remove pyml pin to resolve dependency conflict with Infer --- .github/workflows/infer.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml index effaf27f1a7e..ffadd13ff3bd 100644 --- a/.github/workflows/infer.yml +++ b/.github/workflows/infer.yml @@ -23,11 +23,7 @@ jobs: - name: Set up OCaml uses: ocaml/setup-ocaml@v3 with: - ocaml-compiler: 4.14.0 - - - name: Pin pyml to required version - run: | - opam pin add pyml 20220905 --yes + ocaml-compiler: 5 - name: Get current year/weak run: echo "year_week=$(date +'%Y_%U')" >> $GITHUB_ENV From 94c5ad48d1a0793042d29f3a6a463ef5c962202e Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 17:15:10 -0400 Subject: [PATCH 13/23] Use a true randomized algorithm solution, Rabin's randomized --- .../thealgorithms/randomized/ClosestPair.java | 150 +++++++++--------- .../randomized/ClosestPairTest.java | 58 ++++--- 2 files changed, 113 insertions(+), 95 deletions(-) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 72e46ab2ed86..30b25aad03dc 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -1,102 +1,106 @@ package com.thealgorithms.randomized; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; -// As required by Repository, new algorithms have URL in comments with explanation -// https://www.geeksforgeeks.org/closest-pair-of-points-using-divide-and-conquer-algorithm -// Given 2 or more points on a 2-dimensional plane, find the closest 2 points in Euclidean distance -// This class uses the divide and conquer technique with recursion +class Point { + double x, y; -final class Point implements Comparable { - double x; - double y; - - // Constructor to initialize a point with x and y coordinates - Point(double x, double y) { + public Point(double x, double y) { this.x = x; this.y = y; } - public int compareTo(Point other) { - return Double.compare(this.x, other.x); - } - - static double distance(Point p1, Point p2) { - return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); + @Override + public String toString() { + return "(" + x + ", " + y + ")"; } } -public final class ClosestPair { - // Private constructor to prevent instantiation - private ClosestPair() { - throw new AssertionError("Utility class should not be instantiated."); - } - - public static double closest(List points) { - if (points == null || points.isEmpty()) { - throw new IllegalArgumentException("There are no pairs to compare."); - } - - if (points.size() == 1) { - throw new IllegalArgumentException("There is only one pair."); - } - - Collections.sort(points); - double result = closestRecursiveHelper(points, 0, points.size() - 1); +public class ClosestPair { + private static final double INFINITY = Double.MAX_VALUE; - // Return distance of closest pair rounded to 2 decimal places - return new BigDecimal(String.valueOf(result)).setScale(2, RoundingMode.HALF_UP).doubleValue(); + public static double euclideanDistance(Point p1, Point p2) { + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); } - private static double closestRecursiveHelper(List points, int left, int right) { - // Base Case occurs with 3 or fewer points - if (right - left <= 2) { - return baseCase(points, left, right); + /** + * Algorithm Proof https://www.cs.toronto.edu/~anikolov/CSC473W20/kt-rabin.pdf + * Additional information: https://en.wikipedia.org/wiki/Closest_pair_of_points_problem + * This class uses Rabin's randomized approach to find the closest pair of points. + * Rabin's approach randomly selects a sample of points to estimate an initial closest distance + * (delta), then uses a grid for "probabilistic refinement". Finally, it updates the closest pair + * with the closest distance. + */ + + public static Object[] rabinRandomizedClosestPair(List points) { + // Error handling, must have at least 2 points + if (points == null || points.size() < 2) { + return new Object[] {null, null, INFINITY}; } - // Divide and conquer - int mid = (left + right) / 2; - double midX = points.get(mid).x; - - double leftDist = closestRecursiveHelper(points, left, mid); - double rightDist = closestRecursiveHelper(points, mid + 1, right); + Collections.shuffle(points, new Random()); // shuffle for required randomness - double minDist = Math.min(leftDist, rightDist); + double delta = INFINITY; // initialize distance + Point closestA = null; + Point closestB = null; - return checkBoundary(points, left, right, midX, minDist); - } + // without exceeding number of points, work with some sample + int sampleSize = Math.min(7, points.size()); - private static double baseCase(List points, int left, int right) { - // Sub-problems fitting the base case can use brute force - double minDist = Double.MAX_VALUE; - for (int i = left; i <= right; i++) { - for (int j = i + 1; j <= right; j++) { - minDist = Math.min(minDist, Point.distance(points.get(i), points.get(j))); - } + Random random = new Random(); // select randomly + Set sampleSet = new HashSet<>(); // ensure unique pairs + while (sampleSet.size() < sampleSize) { + sampleSet.add(points.get(random.nextInt(points.size()))); } - return minDist; - } - - private static double checkBoundary(List points, int left, int right, double midX, double minDist) { - // Consider a boundary by the dividing line - List boundary = new ArrayList<>(); - for (int i = left; i <= right; i++) { - if (Math.abs(points.get(i).x - midX) < minDist) { - boundary.add(points.get(i)); + List sample = new ArrayList<>(sampleSet); + + // initially the closest points are found via brute force + for (int i = 0; i < sample.size(); i++) { + for (int j = i + 1; j < sample.size(); j++) { + double dist = euclideanDistance(sample.get(i), sample.get(j)); + if (dist < delta) { + closestA = sample.get(i); + closestB = sample.get(j); + delta = dist; // update distance + } } } - // sort by y coordinate within the boundary and check for closer points - boundary.sort(Comparator.comparingDouble(p -> p.y)); - for (int i = 0; i < boundary.size(); i++) { - for (int j = i + 1; j < boundary.size() && (boundary.get(j).y - boundary.get(i).y) < minDist; j++) { - minDist = Math.min(minDist, Point.distance(boundary.get(i), boundary.get(j))); + // Create a grid, We will use "Probabilistic Filtering" by only checking + // neighboring grids to prevent bruteforce checking outside initialization + Map grid = new HashMap<>(); + + // coordinates computed based on delta, estimated closest distance + for (Point p : points) { + int gridX = (int) (p.x / delta); + int gridY = (int) (p.y / delta); + String key = gridX + "," + gridY; // string for indexing + + // check neighboring cells + for (int dX = -1; dX <= 1; dX++) { + for (int dY = -1; dY <= 1; dY++) { + String neighborKey = (gridX + dX) + "," + (gridY + dY); + Point neighborValue = grid.get(neighborKey); + + // update points only if valid neighbor + if (neighborValue != null && p != neighborValue) { + double dist = euclideanDistance(p, neighborValue); + if (dist < delta) { + closestA = p; + closestB = neighborValue; + delta = dist; + } + } + } } + grid.put(key, p); } - return minDist; + return new Object[] {closestA, closestB, delta}; } } diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index 705b188a1e30..dcd5fe8b8843 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -1,43 +1,57 @@ package com.thealgorithms.randomized; +import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Random; import org.junit.jupiter.api.Test; -public class ClosestPairTest { +class ClosestPairTest { - // Tests sorting of an array with multiple elements, including duplicates. @Test - public void testMultiplePairs() { - List points = Arrays.asList(new Point(1, 2), new Point(3, 4), new Point(5, 1), new Point(7, 8), new Point(2, 3), new Point(6, 2)); - double expected = 1.41; - assertEquals(expected, ClosestPair.closest(points)); + void testStandardCaseClosestPair() { + List points = Arrays.asList(new Point(1, 4), new Point(2, 8), new Point(0, 1), new Point(4, 5), new Point(9, 4)); + Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); + assertNotEquals(closestPair[0], closestPair[1], "Points are distinct"); + assertTrue((double) closestPair[2] > 0, "Distance must be positive"); } - // Test if there are no pairs. @Test - public void testNoPoints() { - List points = new ArrayList<>(); - Exception exception = assertThrows(IllegalArgumentException.class, () -> { ClosestPair.closest(points); }); - assertEquals("There are no pairs to compare.", exception.getMessage()); + void testTwoDistinctPoints() { + List points = Arrays.asList(new Point(1, 2), new Point(2, 3)); + Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); + assertTrue((closestPair[0].equals(points.get(0)) && closestPair[1].equals(points.get(1))) || (closestPair[1].equals(points.get(0)) && closestPair[0].equals(points.get(1)))); + assertEquals(closestPair[2], ClosestPair.euclideanDistance(points.get(0), points.get(1))); } - // Test if there is one point, no pairs. @Test - public void testOnePoint() { - List points = Arrays.asList(new Point(1, 2)); - Exception exception = assertThrows(IllegalArgumentException.class, () -> { ClosestPair.closest(points); }); - assertEquals("There is only one pair.", exception.getMessage()); + void testIdenticalPointsPairWithDistanceZero() { + List points = Arrays.asList(new Point(1.0, 2.0), new Point(1.0, 2.0), new Point(1.0, 1.0)); + Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); + assertTrue((closestPair[0].equals(points.get(0)) && closestPair[1].equals(points.get(1)))); + assertEquals(0, (double) closestPair[2], "Distance is zero"); + } + + @Test + void testLargeDatasetRandomPoints() { + List points = new ArrayList<>(); + Random random = new Random(); + for (int i = 0; i < 1000; i++) { + points.add(new Point(random.nextDouble() * 100, random.nextDouble() * 100)); + } + Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); + assertNotNull(closestPair[0]); + assertNotNull(closestPair[1]); + assertTrue((double) closestPair[2] > 0, "Distance must be positive"); } - // Test if there is a duplicate points as a pair @Test - public void testPoints() { - List points = Arrays.asList(new Point(1, 2), new Point(5, 1), new Point(5, 1), new Point(7, 8), new Point(2, 3), new Point(6, 2)); - double expected = 0.00; - assertEquals(expected, ClosestPair.closest(points)); + void testSinglePointShouldReturnNoPair() { + List points = Arrays.asList(new Point(5.0, 5.0)); + Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); + assertNull(closestPair[0]); + assertNull(closestPair[1]); } } From b5c62ab3de8b81846e9ce1346e9daffea2c11216 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 17:24:01 -0400 Subject: [PATCH 14/23] fix test case for identical points --- .../java/com/thealgorithms/randomized/ClosestPairTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index dcd5fe8b8843..cda1158a3feb 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -6,6 +6,8 @@ import java.util.Arrays; import java.util.List; import java.util.Random; + +import com.thealgorithms.datastructures.crdt.GCounterTest; import org.junit.jupiter.api.Test; class ClosestPairTest { @@ -30,7 +32,6 @@ void testTwoDistinctPoints() { void testIdenticalPointsPairWithDistanceZero() { List points = Arrays.asList(new Point(1.0, 2.0), new Point(1.0, 2.0), new Point(1.0, 1.0)); Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); - assertTrue((closestPair[0].equals(points.get(0)) && closestPair[1].equals(points.get(1)))); assertEquals(0, (double) closestPair[2], "Distance is zero"); } From e89e6c4ff56f7b26c162a93c6beb7629db909644 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 17:25:52 -0400 Subject: [PATCH 15/23] used clang formatter on necessary file --- .../java/com/thealgorithms/randomized/ClosestPairTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index cda1158a3feb..c8b38c1afa90 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -2,12 +2,11 @@ import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals; +import com.thealgorithms.datastructures.crdt.GCounterTest; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; - -import com.thealgorithms.datastructures.crdt.GCounterTest; import org.junit.jupiter.api.Test; class ClosestPairTest { From c93383b97726ea07d967ebd242b17ec52d373560 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 17:39:20 -0400 Subject: [PATCH 16/23] fix build issues --- src/main/java/com/thealgorithms/randomized/ClosestPair.java | 5 +++-- .../java/com/thealgorithms/randomized/ClosestPairTest.java | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 30b25aad03dc..424c173d22a7 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -9,9 +9,10 @@ import java.util.Set; class Point { - double x, y; + double x; + double y; - public Point(double x, double y) { + Point(double x, double y) { this.x = x; this.y = y; } diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index c8b38c1afa90..e57101f41b64 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -1,14 +1,13 @@ package com.thealgorithms.randomized; -import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import com.thealgorithms.datastructures.crdt.GCounterTest; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + class ClosestPairTest { @Test From 0e329624b39ecb35b82a78df1ce805bb7b844f1d Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 17:46:13 -0400 Subject: [PATCH 17/23] rebase and fix build issues --- src/test/java/com/thealgorithms/randomized/ClosestPairTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index e57101f41b64..a27b7df1b14e 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -1,5 +1,4 @@ package com.thealgorithms.randomized; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; From cac97063a3588be988f266f2a81f890256237a15 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 17:49:01 -0400 Subject: [PATCH 18/23] remove * from import statments --- .../com/thealgorithms/randomized/ClosestPairTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index a27b7df1b14e..9970ddd549fd 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -1,12 +1,16 @@ package com.thealgorithms.randomized; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - class ClosestPairTest { @Test From e05cfddda2bee207abb8ccd662a1e2658f088e2e Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 17:53:50 -0400 Subject: [PATCH 19/23] no public or default constructors allowed --- src/main/java/com/thealgorithms/randomized/ClosestPair.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 424c173d22a7..b86125167b09 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -26,6 +26,9 @@ public String toString() { public class ClosestPair { private static final double INFINITY = Double.MAX_VALUE; + private ClosestPair() { + } + public static double euclideanDistance(Point p1, Point p2) { return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); } From c386ec901e6528768b6466d86d4750dd73a32fc1 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 17:56:45 -0400 Subject: [PATCH 20/23] closest pair class must be final --- src/main/java/com/thealgorithms/randomized/ClosestPair.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index b86125167b09..dc39dc2d0238 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -23,7 +23,7 @@ public String toString() { } } -public class ClosestPair { +public final class ClosestPair { private static final double INFINITY = Double.MAX_VALUE; private ClosestPair() { From bb66f17ead31a59f3cc172b2369238b714cf1e49 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 18:13:19 -0400 Subject: [PATCH 21/23] not null error from workflow fixed --- .../com/thealgorithms/randomized/ClosestPairTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index 9970ddd549fd..1528e5effdee 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -1,6 +1,5 @@ package com.thealgorithms.randomized; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -17,7 +16,6 @@ class ClosestPairTest { void testStandardCaseClosestPair() { List points = Arrays.asList(new Point(1, 4), new Point(2, 8), new Point(0, 1), new Point(4, 5), new Point(9, 4)); Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); - assertNotEquals(closestPair[0], closestPair[1], "Points are distinct"); assertTrue((double) closestPair[2] > 0, "Distance must be positive"); } @@ -25,8 +23,11 @@ void testStandardCaseClosestPair() { void testTwoDistinctPoints() { List points = Arrays.asList(new Point(1, 2), new Point(2, 3)); Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); - assertTrue((closestPair[0].equals(points.get(0)) && closestPair[1].equals(points.get(1))) || (closestPair[1].equals(points.get(0)) && closestPair[0].equals(points.get(1)))); - assertEquals(closestPair[2], ClosestPair.euclideanDistance(points.get(0), points.get(1))); + + // Add null check for closestPair + assertNotNull(closestPair, "Closest pair result should not be null"); + assertTrue((closestPair[0].equals(points.get(0)) && closestPair[1].equals(points.get(1))) || (closestPair[1].equals(points.get(0)) && closestPair[0].equals(points.get(1))), "The closest pair should include the given distinct points"); + assertEquals(closestPair[2], ClosestPair.euclideanDistance(points.get(0), points.get(1)), "The calculated distance should match the Euclidean distance"); } @Test From 92ff8336d9f6f0b35e8ae4126042fb90f3111eaa Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 18:24:45 -0400 Subject: [PATCH 22/23] add null checker in algorithm --- .../java/com/thealgorithms/randomized/ClosestPair.java | 7 +++++++ .../java/com/thealgorithms/randomized/ClosestPairTest.java | 7 ++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index dc39dc2d0238..27ed0bd3c67a 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -76,6 +76,13 @@ public static Object[] rabinRandomizedClosestPair(List points) { } } + // Confirm neither closestA nor closestB are null + if (closestA == null || closestB == null) { + closestA = points.get(0); + closestB = points.get(1); + delta = euclideanDistance(closestA, closestB); + } + // Create a grid, We will use "Probabilistic Filtering" by only checking // neighboring grids to prevent bruteforce checking outside initialization Map grid = new HashMap<>(); diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index 1528e5effdee..e358b2c13205 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -23,11 +23,8 @@ void testStandardCaseClosestPair() { void testTwoDistinctPoints() { List points = Arrays.asList(new Point(1, 2), new Point(2, 3)); Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); - - // Add null check for closestPair - assertNotNull(closestPair, "Closest pair result should not be null"); - assertTrue((closestPair[0].equals(points.get(0)) && closestPair[1].equals(points.get(1))) || (closestPair[1].equals(points.get(0)) && closestPair[0].equals(points.get(1))), "The closest pair should include the given distinct points"); - assertEquals(closestPair[2], ClosestPair.euclideanDistance(points.get(0), points.get(1)), "The calculated distance should match the Euclidean distance"); + assertTrue((closestPair[0].equals(points.get(0)) && closestPair[1].equals(points.get(1))) || (closestPair[1].equals(points.get(0)) && closestPair[0].equals(points.get(1)))); + assertEquals(closestPair[2], ClosestPair.euclideanDistance(points.get(0), points.get(1))); } @Test From 20d0d31d40a070c63274fbf459f1932bb9b2e1a7 Mon Sep 17 00:00:00 2001 From: bri-harris Date: Tue, 3 Jun 2025 18:33:45 -0400 Subject: [PATCH 23/23] add null checker in test and more distinct in rabin function --- src/main/java/com/thealgorithms/randomized/ClosestPair.java | 4 +--- .../java/com/thealgorithms/randomized/ClosestPairTest.java | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/randomized/ClosestPair.java b/src/main/java/com/thealgorithms/randomized/ClosestPair.java index 27ed0bd3c67a..ab60f75a41ad 100644 --- a/src/main/java/com/thealgorithms/randomized/ClosestPair.java +++ b/src/main/java/com/thealgorithms/randomized/ClosestPair.java @@ -78,9 +78,7 @@ public static Object[] rabinRandomizedClosestPair(List points) { // Confirm neither closestA nor closestB are null if (closestA == null || closestB == null) { - closestA = points.get(0); - closestB = points.get(1); - delta = euclideanDistance(closestA, closestB); + return new Object[] {points.get(0), points.get(1), euclideanDistance(points.get(0), points.get(1))}; } // Create a grid, We will use "Probabilistic Filtering" by only checking diff --git a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java index e358b2c13205..3e7bbca5e5a9 100644 --- a/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java +++ b/src/test/java/com/thealgorithms/randomized/ClosestPairTest.java @@ -23,6 +23,10 @@ void testStandardCaseClosestPair() { void testTwoDistinctPoints() { List points = Arrays.asList(new Point(1, 2), new Point(2, 3)); Object[] closestPair = ClosestPair.rabinRandomizedClosestPair(points); + + assertNotNull(closestPair[0]); + assertNotNull(closestPair[1]); + assertTrue((closestPair[0].equals(points.get(0)) && closestPair[1].equals(points.get(1))) || (closestPair[1].equals(points.get(0)) && closestPair[0].equals(points.get(1)))); assertEquals(closestPair[2], ClosestPair.euclideanDistance(points.get(0), points.get(1))); }