1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
/// Search in sorted sequences by checking the next position based on an
/// linear interpolation of the search key.
///
/// # Parameters
///
/// * `arr`: Slice to search in.
/// * `target`: Object to search for.
///
/// # Notes
///
/// Since interpolations only be applied on numeric types, we choose `i32` to
/// avoid overkilling. If you desire a full functional trait of numeric types,
/// [num][1] crate would meet your needs.
///
/// [1]: https://github.com/rust-num/num
pub fn interpolation_search(arr: &[i32], target: &i32) -> Result<usize, usize> {
// 1. Handle empty sequence.
if arr.is_empty() {
return Err(0);
}
// 2. Setup variable storing iteration informaion.
// hi -> upper bound of search range.
// lo -> lower bound of search range.
// interpolant -> position to probe in the sequence
let mut hi = arr.len() - 1;
let mut lo = 0_usize;
let mut interpolant = 0_usize;
// 3. Main loop to calculate the interpolant.
loop {
let lo_val = arr[lo];
let hi_val = arr[hi];
// 3.1. Three condition to exit the loop
// a. hi and lo flag overlapping -> all elements are scanned.
// b. target value is less than the lowest value
// c. target value exceeds the highest value
if hi <= lo || *target < lo_val || *target > hi_val {
break;
}
// 3.2. The linear interpolation part
let offset = (*target - lo_val) * (hi - lo) as i32 / (hi_val - lo_val);
interpolant = lo + offset as usize;
let mid_val = arr[interpolant];
// 3.3. Comparison between the interpolant and targert value.
// New boundaries must step one index further to avoid infinite searching.
if mid_val > *target {
hi = interpolant - 1;
} else if mid_val < *target {
lo = interpolant + 1;
} else {
break;
}
}
// 4. Determine whether the returning interpolant equals to target value.
// `Result::Err` here maps to a position safe to insert while remains ordering.
if *target > arr[hi] {
Err(hi + 1)
} else if *target < arr[lo] {
Err(lo)
} else {
Ok(interpolant)
}
}
#[cfg(test)]
mod base {
use super::*;
sorted_no_duplicate_cases!(interpolation_search);
}