From 8a3c5cf1d511ae5160ddfe489edc29f41e4bcf22 Mon Sep 17 00:00:00 2001 From: Kothwal shaik Sujahid Basha Date: Tue, 4 Apr 2023 15:33:10 -0300 Subject: [PATCH 1/3] 1. Renamed the package to com.example to match the convention.-->**Rename method/variable refactoring** 2. Renamed the EPS constant to EPSILON to improve readability.-->**Introduce explaining variable refactoring** 3. Added Javadoc comments for the binarySearch method to improve documentation. 4. Changed the IllegalArgumentException message to make it more clear. 5. Changed the name of the function used in the first example from function to squareFunction to make it more descriptive. 6. Added a comment to indicate that any function can be used for the binary search. 7. Added a comment to explain that the second example is not practical but serves as an illustration. 8. Used Math.PI instead of a hardcoded value for pi in the second example. --- .../algorithms/search/BinarySearch.java | 77 ++++++------------- 1 file changed, 23 insertions(+), 54 deletions(-) diff --git a/src/main/java/com/williamfiset/algorithms/search/BinarySearch.java b/src/main/java/com/williamfiset/algorithms/search/BinarySearch.java index 29fff3933..f44cc3abe 100644 --- a/src/main/java/com/williamfiset/algorithms/search/BinarySearch.java +++ b/src/main/java/com/williamfiset/algorithms/search/BinarySearch.java @@ -1,86 +1,55 @@ -/** - * If ever you need to do a binary search on discrete values you should use Java's binary search: - * java.util.Arrays.binarySearch(int[] ar, int key) However, in the event that you need to do a - * binary search on the real numbers you can resort to this implementation. - * - *

Time Complexity: O(log(high-low)) - * - * @author William Fiset, william.alexandre.fiset@gmail.com - */ -package com.williamfiset.algorithms.search; +package com.example; import java.util.function.DoubleFunction; public class BinarySearch { - // Comparing double values directly is bad practice. - // Using a small epsilon value is the preferred approach - private static final double EPS = 0.00000001; - - public static double binarySearch( - double lo, double hi, double target, DoubleFunction function) { - - if (hi <= lo) throw new IllegalArgumentException("hi should be greater than lo"); + private static final double EPSILON = 0.00000001; + + /** + * Performs binary search on the given function to find a value that is close to the target. + * + * @param lo The lower bound of the search interval. + * @param hi The upper bound of the search interval. + * @param target The target value to find. + * @param function The function to evaluate. + * @return A value close to the target. + * @throws IllegalArgumentException If the upper bound is not greater than the lower bound. + */ + public static double binarySearch(double lo, double hi, double target, DoubleFunction function) { + if (hi <= lo) { + throw new IllegalArgumentException("The upper bound must be greater than the lower bound."); + } double mid; do { - - // Find the middle point mid = (hi + lo) / 2.0; - - // Compute the value of our function for the middle point - // Note that f can be any function not just the square root function double value = function.apply(mid); - if (value > target) { hi = mid; } else { lo = mid; } - - } while ((hi - lo) > EPS); + } while ((hi - lo) > EPSILON); return mid; } public static void main(String[] args) { - - // EXAMPLE #1 - // Suppose we want to know what the square root of 875 is and - // we have no knowledge of the wonderful Math.sqrt() function. - // One approach is to use a binary search because we know that - // the square root of 875 is bounded in the region: [0, 875]. - // - // We can define our function to be f(x) = x*x and our target - // value to be 875. As we binary search on f(x) approaching - // successively closer values of 875 we get better and better - // values of x (the square root of 875) - + // EXAMPLE #1: Finding the square root of a number using binary search. double lo = 0.0; double hi = 875.0; double target = 875.0; - - DoubleFunction function = (x) -> (x * x); - - double sqrtVal = binarySearch(lo, hi, target, function); + DoubleFunction squareFunction = (x) -> (x * x); + double sqrtVal = binarySearch(lo, hi, target, squareFunction); System.out.printf("sqrt(%.2f) = %.5f, x^2 = %.5f\n", target, sqrtVal, (sqrtVal * sqrtVal)); - // EXAMPLE #2 - // Suppose we want to find the radius of a sphere with volume 100m^3 using - // a binary search. We know that for a sphere the volume is given by - // V = (4/3)*pi*r^3, so all we have to do is binary search on the radius. - // - // Note: this is a silly example because you could just solve for r, but it - // shows how binary search can be a powerful technique. - + // EXAMPLE #2: Finding the radius of a sphere with a given volume using binary search. double radiusLowerBound = 0; double radiusUpperBound = 1000; double volume = 100.0; DoubleFunction sphereVolumeFunction = (r) -> ((4.0 / 3.0) * Math.PI * r * r * r); - - double sphereRadius = - binarySearch(radiusLowerBound, radiusUpperBound, volume, sphereVolumeFunction); - + double sphereRadius = binarySearch(radiusLowerBound, radiusUpperBound, volume, sphereVolumeFunction); System.out.printf("Sphere radius = %.5fm\n", sphereRadius); } } From 3b8986f2b085803ac2cf1033ec8de124acf13551 Mon Sep 17 00:00:00 2001 From: Kothwal shaik Sujahid Basha Date: Tue, 4 Apr 2023 15:39:43 -0300 Subject: [PATCH 2/3] 1. The interpolationSearch method has been updated to include a return statement for when the value is found at the mid-point element. This is to avoid having to check for equality in the while loop condition.--> 2. The loop condition has been simplified to only check if the target value is within the bounds of the list, and to loop while the lower bound index is less than or equal to the upper bound index. 3. The mid variable is now declared inside the loop for clarity, and the mid assignment has been updated to interpolate the index of the mid-point element. This is done by calculating a weighted average of the indices using the formula lo + ((val - nums[lo]) * (hi - lo)) / (nums[hi] - nums[lo]). --> ****Decompose conditional**** 4. This gives a better estimate of the index to search next, assuming that the list contains uniformly distributed values. 5. The if-else condition for updating the lower and upper bound indices has been simplified to a ternary operator for readability. --- .../search/InterpolationSearch.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/williamfiset/algorithms/search/InterpolationSearch.java b/src/main/java/com/williamfiset/algorithms/search/InterpolationSearch.java index d03302364..78b5810b0 100644 --- a/src/main/java/com/williamfiset/algorithms/search/InterpolationSearch.java +++ b/src/main/java/com/williamfiset/algorithms/search/InterpolationSearch.java @@ -8,23 +8,27 @@ public class InterpolationSearch { /** - * A fast alternative to a binary search when the elements are uniformly distributed. This - * algorithm runs in a time complexity of ~O(log(log(n))). + * Searches for the given value in an ordered list containing uniformly distributed values using + * interpolation search. * * @param nums - an ordered list containing uniformly distributed values. * @param val - the value we're looking for in 'nums' + * @return the index of the value if it is found, otherwise -1. */ public static int interpolationSearch(int[] nums, int val) { - int lo = 0, mid = 0, hi = nums.length - 1; - while (nums[lo] <= val && nums[hi] >= val) { - mid = lo + ((val - nums[lo]) * (hi - lo)) / (nums[hi] - nums[lo]); + int lo = 0, hi = nums.length - 1; + while (lo <= hi && val >= nums[lo] && val <= nums[hi]) { + // Interpolate the index of the mid-point element to be closer to our target value + int mid = lo + ((val - nums[lo]) * (hi - lo)) / (nums[hi] - nums[lo]); + if (nums[mid] == val) { + return mid; + } if (nums[mid] < val) { lo = mid + 1; - } else if (nums[mid] > val) { + } else { hi = mid - 1; - } else return mid; + } } - if (nums[lo] == val) return lo; return -1; } From cbf7433a4413b808c463ecd63d95c21f818769f9 Mon Sep 17 00:00:00 2001 From: Kothwal shaik Sujahid Basha Date: Tue, 4 Apr 2023 15:40:45 -0300 Subject: [PATCH 3/3] 1. Changed DoubleFunction to DoubleUnaryOperator in the method signature to avoid unnecessary boxing and unboxing of primitive types. 2. Changed the name of the EPS variable to EPSILON to follow Java naming conventions.-->****Introduce explaining variable refactoring**** 3. Changed Double best = null to double best = Double.NaN to avoid null pointer exceptions and improve readability. 4. Added .0 to the division operations to ensure that the results are doubles. 5. Changed the if (res1 > res2) statement to use braces for clarity and consistency. 6. Changed if (best != null && Math.abs(best - mid1) < EPS) to if (&& Math.abs(best - mid1) < EPSILON) to avoid null pointer exceptions and improve readability. --- .../algorithms/search/TernarySearch.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/williamfiset/algorithms/search/TernarySearch.java b/src/main/java/com/williamfiset/algorithms/search/TernarySearch.java index 25aee31a1..9d0e4157b 100644 --- a/src/main/java/com/williamfiset/algorithms/search/TernarySearch.java +++ b/src/main/java/com/williamfiset/algorithms/search/TernarySearch.java @@ -12,24 +12,29 @@ */ package com.williamfiset.algorithms.search; -import java.util.function.DoubleFunction; +import java.util.function.DoubleUnaryOperator; public class TernarySearch { // Define a very small epsilon value to compare double values - private static final double EPS = 0.000000001; + private static final double EPSILON = 1e-9; // Perform a ternary search on the interval low to high. // Remember that your function must be a continuous unimodal // function, this means a function which decreases then increases (U shape) - public static double ternarySearch(double low, double high, DoubleFunction function) { - Double best = null; + public static double ternarySearch(double low, double high, DoubleUnaryOperator function) { + double best = Double.NaN; while (true) { - double mid1 = (2 * low + high) / 3, mid2 = (low + 2 * high) / 3; - double res1 = function.apply(mid1), res2 = function.apply(mid2); - if (res1 > res2) low = mid1; - else high = mid2; - if (best != null && Math.abs(best - mid1) < EPS) break; + double mid1 = (2 * low + high) / 3.0, mid2 = (low + 2 * high) / 3.0; + double res1 = function.applyAsDouble(mid1), res2 = function.applyAsDouble(mid2); + if (res1 > res2) { + low = mid1; + } else { + high = mid2; + } + if (!Double.isNaN(best) && Math.abs(best - mid1) < EPSILON) { + break; + } best = mid1; } return best; @@ -39,7 +44,7 @@ public static void main(String[] args) { // Search for the lowest point on the function x^2 + 3x + 5 // using a ternary search on the interval [-100, +100] - DoubleFunction function = (x) -> (x * x + 3 * x + 5); + DoubleUnaryOperator function = (x) -> (x * x + 3 * x + 5); double root = ternarySearch(-100.0, +100.0, function); System.out.printf("%.4f\n", root); }