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);
}
}
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;
}
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);
}