Skip to content

Refactored the BinarySearch.Java file #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 23 additions & 54 deletions src/main/java/com/williamfiset/algorithms/search/BinarySearch.java
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>Time Complexity: O(log(high-low))
*
* @author William Fiset, [email protected]
*/
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<Double> 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<Double> 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<Double> function = (x) -> (x * x);

double sqrtVal = binarySearch(lo, hi, target, function);
DoubleFunction<Double> 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<Double> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
25 changes: 15 additions & 10 deletions src/main/java/com/williamfiset/algorithms/search/TernarySearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Double> 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;
Expand All @@ -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<Double> 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);
}
Expand Down