How to resolve the algorithm Binary search step by step in the JavaScript programming language
How to resolve the algorithm Binary search step by step in the JavaScript programming language
Table of Contents
Problem Statement
A binary search divides a range of values into halves, and continues to narrow down the field of search until the unknown value is found. It is the classic example of a "divide and conquer" algorithm. As an analogy, consider the children's game "guess a number." The scorer has a secret number, and will only tell the player if their guessed number is higher than, lower than, or equal to the secret number. The player then uses this information to guess a new number. As the player, an optimal strategy for the general case is to start by choosing the range's midpoint as the guess, and then asking whether the guess was higher, lower, or equal to the secret number. If the guess was too high, one would select the point exactly between the range midpoint and the beginning of the range. If the original guess was too low, one would ask about the point exactly between the range midpoint and the end of the range. This process repeats until one has reached the secret number.
Given the starting point of a range, the ending point of a range, and the "secret value", implement a binary search through a sorted integer array for a certain number. Implementations can be recursive or iterative (both if you can). Print out whether or not the number was in the array afterwards. If it was, print the index also. There are several binary search algorithms commonly seen. They differ by how they treat multiple values equal to the given value, and whether they indicate whether the element was found or not. For completeness we will present pseudocode for all of them. All of the following code examples use an "inclusive" upper bound (i.e. high = N-1 initially). Any of the examples can be converted into an equivalent example using "exclusive" upper bound (i.e. high = N initially) by making the following simple changes (which simply increase high by 1): The algorithms are as follows (from Wikipedia). The algorithms return the index of some element that equals the given value (if there are multiple such elements, it returns some arbitrary one). It is also possible, when the element is not found, to return the "insertion point" for it (the index that the value would have if it were inserted into the array). Recursive Pseudocode: Iterative Pseudocode: The following algorithms return the leftmost place where the given element can be correctly inserted (and still maintain the sorted order). This is the lower (inclusive) bound of the range of elements that are equal to the given value (if any). Equivalently, this is the lowest index where the element is greater than or equal to the given value (since if it were any lower, it would violate the ordering), or 1 past the last index if such an element does not exist. This algorithm does not determine if the element is actually found. This algorithm only requires one comparison per level. Recursive Pseudocode: Iterative Pseudocode: The following algorithms return the rightmost place where the given element can be correctly inserted (and still maintain the sorted order). This is the upper (exclusive) bound of the range of elements that are equal to the given value (if any). Equivalently, this is the lowest index where the element is greater than the given value, or 1 past the last index if such an element does not exist. This algorithm does not determine if the element is actually found. This algorithm only requires one comparison per level. Note that these algorithms are almost exactly the same as the leftmost-insertion-point algorithms, except for how the inequality treats equal values. Recursive Pseudocode: Iterative Pseudocode: Make sure it does not have overflow bugs. The line in the pseudo-code above to calculate the mean of two integers: could produce the wrong result in some programming languages when used with a bounded integer type, if the addition causes an overflow. (This can occur if the array size is greater than half the maximum integer value.) If signed integers are used, and low + high overflows, it becomes a negative number, and dividing by 2 will still result in a negative number. Indexing an array with a negative number could produce an out-of-bounds exception, or other undefined behavior. If unsigned integers are used, an overflow will result in losing the largest bit, which will produce the wrong result. One way to fix it is to manually add half the range to the low number: Even though this is mathematically equivalent to the above, it is not susceptible to overflow. Another way for signed integers, possibly faster, is the following: where >>> is the logical right shift operator. The reason why this works is that, for signed integers, even though it overflows, when viewed as an unsigned number, the value is still the correct sum. To divide an unsigned number by 2, simply do a logical right shift.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Binary search step by step in the JavaScript programming language
The JavaScript code you provided consists of three functions for performing binary search on an array: binary_search_recursive, binary_search_iterative, and findIter. Additionally, there's an unnamed function that acts as the main entry point of the script. Let's examine each of these functions in detail:
- binary_search_recursive: This function implements a recursive binary search algorithm. It takes three parameters: a sorted array a, the value value to be searched for, and the lower (lo) and higher (hi) indices of the current search range. The function begins by checking if the search range (hi < lo) is valid. If not, it means the element was not found, and the function returns null. Otherwise, it calculates the middle index mid as the average of lo and hi, using the Math.floor function to obtain an integer index. If the element at index mid is greater than the value, the search continues recursively in the lower half of the array (binary_search_recursive(a, value, lo, mid - 1)). If the element at index mid is less than the value, the search continues recursively in the upper half of the array (binary_search_recursive(a, value, mid + 1, hi)). If the element at index mid is equal to the value, the function returns mid, indicating that the element has been found at that index.
- binary_search_iterative: This function implements an iterative binary search algorithm. It takes two parameters: a sorted array a and the value value to be searched for. The function initializes three variables: mid (to store the middle index), lo (set to 0), and hi (set to the length of the array minus 1). It enters a while loop that continues as long as lo is less than or equal to hi. Within the loop, it calculates the middle index mid as the average of lo and hi, using the Math.floor function to obtain an integer index. If the element at index mid is greater than the value, it updates the hi index to mid - 1. If the element at index mid is less than the value, it updates the lo index to mid + 1. If the element at index mid is equal to the value, it breaks out of the loop and returns mid. If the loop completes without finding the value, it means the element was not found, and the function returns null.
- findIter: This function implements an iterative binary search algorithm using the scan function provided by the library. It takes two parameters: a sorted array xs and the value x to be searched for. The function initializes three variables: m (to store the middle index), l (set to 0), and h (set to the length of the array minus 1). It calls the until function to execute the search loop until l is greater than h or l is equal to mid. Within the until function, it calculates the middle index m as the average of l and h, using the div function to obtain an integer index. It compares the element at index m with the value x to determine the next search range. If the element at index m is greater than x, it updates h to m - 1. If the element at index m is less than x, it updates l to m + 1. If the element at index m is equal to x, it sets both l and h to m to effectively stop the loop. After the until function completes, it checks if l is greater than h, which indicates that the element was not found, and returns Left('not found'). Otherwise, it returns Right(m) to indicate that the element was found at index m.
- Main Function: The unnamed function acts as the main entry point of the script. It defines a sorted array xs containing strings and uses the map function to apply the either function to each element in a shuffled version of xs. The either function takes three arguments: A function to handle the case when the element is not found, which formats a string stating that the element was not found. A function to handle the case when the element is found, which formats a string stating that the element was found at a specific index. The result of either findRecursive or findIter, which is the Either instance representing the search result. It returns a JSON string representation of an array containing the results of the binary search operations.
In summary, the script provides two implementations of binary search in JavaScript, one recursive and one iterative, along with a test harness that demonstrates the search functionality for both implementations.
Source code in the javascript programming language
function binary_search_recursive(a, value, lo, hi) {
if (hi < lo) { return null; }
var mid = Math.floor((lo + hi) / 2);
if (a[mid] > value) {
return binary_search_recursive(a, value, lo, mid - 1);
}
if (a[mid] < value) {
return binary_search_recursive(a, value, mid + 1, hi);
}
return mid;
}
function binary_search_iterative(a, value) {
var mid, lo = 0,
hi = a.length - 1;
while (lo <= hi) {
mid = Math.floor((lo + hi) / 2);
if (a[mid] > value) {
hi = mid - 1;
} else if (a[mid] < value) {
lo = mid + 1;
} else {
return mid;
}
}
return null;
}
(() => {
'use strict';
const main = () => {
// findRecursive :: a -> [a] -> Either String Int
const findRecursive = (x, xs) => {
const go = (lo, hi) => {
if (hi < lo) {
return Left('not found');
} else {
const
mid = div(lo + hi, 2),
v = xs[mid];
return v > x ? (
go(lo, mid - 1)
) : v < x ? (
go(mid + 1, hi)
) : Right(mid);
}
};
return go(0, xs.length);
};
// findRecursive :: a -> [a] -> Either String Int
const findIter = (x, xs) => {
const [m, l, h] = until(
([mid, lo, hi]) => lo > hi || lo === mid,
([mid, lo, hi]) => {
const
m = div(lo + hi, 2),
v = xs[m];
return v > x ? [
m, lo, m - 1
] : v < x ? [
m, m + 1, hi
] : [m, m, hi];
},
[div(xs.length / 2), 0, xs.length - 1]
);
return l > h ? (
Left('not found')
) : Right(m);
};
// TESTS ------------------------------------------
const
// (pre-sorted AZ)
xs = ["alpha", "beta", "delta", "epsilon", "eta", "gamma",
"iota", "kappa", "lambda", "mu", "nu", "theta", "zeta"
];
return JSON.stringify([
'Recursive',
map(x => either(
l => "'" + x + "' " + l,
r => "'" + x + "' found at index " + r,
findRecursive(x, xs)
),
knuthShuffle(['cape'].concat(xs).concat('cairo'))
),
'',
'Iterative:',
map(x => either(
l => "'" + x + "' " + l,
r => "'" + x + "' found at index " + r,
findIter(x, xs)
),
knuthShuffle(['cape'].concat(xs).concat('cairo'))
)
], null, 2);
};
// GENERIC FUNCTIONS ----------------------------------
// Left :: a -> Either a b
const Left = x => ({
type: 'Either',
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: 'Either',
Right: x
});
// div :: Int -> Int -> Int
const div = (x, y) => Math.floor(x / y);
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = (fl, fr, e) =>
'Either' === e.type ? (
undefined !== e.Left ? (
fl(e.Left)
) : fr(e.Right)
) : undefined;
// Abbreviation for quick testing
// enumFromTo :: (Int, Int) -> [Int]
const enumFromTo = (m, n) =>
Array.from({
length: 1 + n - m
}, (_, i) => m + i);
// FOR TESTS
// knuthShuffle :: [a] -> [a]
const knuthShuffle = xs => {
const swapped = (iFrom, iTo, xs) =>
xs.map(
(x, i) => iFrom !== i ? (
iTo !== i ? x : xs[iFrom]
) : xs[iTo]
);
return enumFromTo(0, xs.length - 1)
.reduceRight((a, i) => {
const iRand = randomRInt(0, i)();
return i !== iRand ? (
swapped(i, iRand, a)
) : a;
}, xs);
};
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) =>
(Array.isArray(xs) ? (
xs
) : xs.split('')).map(f);
// FOR TESTS
// randomRInt :: Int -> Int -> IO () -> Int
const randomRInt = (low, high) => () =>
low + Math.floor(
(Math.random() * ((high - low) + 1))
);
// reverse :: [a] -> [a]
const reverse = xs =>
'string' !== typeof xs ? (
xs.slice(0).reverse()
) : xs.split('').reverse().join('');
// until :: (a -> Bool) -> (a -> a) -> a -> a
const until = (p, f, x) => {
let v = x;
while (!p(v)) v = f(v);
return v;
};
// MAIN ---
return main();
})();
You may also check:How to resolve the algorithm Balanced ternary step by step in the ALGOL 68 programming language
You may also check:How to resolve the algorithm Integer comparison step by step in the Maple programming language
You may also check:How to resolve the algorithm JortSort step by step in the Quackery programming language
You may also check:How to resolve the algorithm Execute Brain step by step in the Burlesque programming language
You may also check:How to resolve the algorithm Harshad or Niven series step by step in the Haskell programming language