Rust Program to Implement Exponential Search

Rust Program to Implement Exponential Search

Exponential Search is a powerful searching algorithm designed for sorted arrays, combining the speed of exponential growth with the precision of binary search. It works by first finding a range where the target element may exist and then performing a binary search within that range. This approach is particularly useful when the element is located near the beginning of a large array because it quickly identifies the search interval.

Pluralsight Logo
Accelerate your tech career
with hands-on learning.
Whether you're a tech newbie or a total pro,
get the skills and confidence to land your next move.
Start 10-Day Free Trial

For beginners, implementing Exponential Search in Rust is a fantastic way to learn about loops, recursion, and the importance of efficient searching algorithms. It is commonly applied in situations where datasets are sorted, such as database indexing or searching in numeric sequences, and it demonstrates how combining multiple strategies can improve algorithm performance. By practicing this, you also gain confidence in structuring Rust programs that balance clarity and efficiency.

Program 1: Iterative Exponential Search on Integers

This program demonstrates Exponential Search using loops on a sorted array of integers. It first finds the search bound and then applies binary search inside that interval.

fn binary_search(arr: &[i32], target: i32, left: usize, right: usize) -> Option<usize> {

    let mut l = left;
    let mut r = right;

    while l <= r {

        let mid = l + (r - l) / 2;

        if arr[mid] == target {
            return Some(mid);
        } else if arr[mid] < target {
            l = mid + 1;
        } else {
            if mid == 0 { break; } 
            r = mid - 1;
        }

    }

    None

}

fn exponential_search(arr: &[i32], target: i32) -> Option<usize> {

    if arr.is_empty() {
        return None;
    }

    if arr[0] == target {
        return Some(0);
    }

    let mut i = 1;

    while i < arr.len() && arr[i] <= target {
        i *= 2;
    }

    let left = i / 2;
    let right = if i < arr.len() { i } else { arr.len() - 1 };

    binary_search(arr, target, left, right)

}

fn main() {

    let numbers = [2, 4, 6, 8, 10, 12, 14, 16];
    let target = 10;

    println!("Array: {:?}", numbers);

    match exponential_search(&numbers, target) {

        Some(idx) => println!("Element {} found at index {}", target, idx),
        None => println!("Element {} not found", target),

    }

}

This iterative version is straightforward: it grows the index exponentially to quickly locate the probable interval. Then, binary search efficiently finds the target. Beginners can easily see how combining two algorithms improves performance.

Program 2: Recursive Exponential Search

This program shows a recursive approach to Exponential Search, highlighting how recursion can make the code concise while maintaining clarity.

fn binary_search_recursive(arr: &[i32], target: i32, left: usize, right: usize) -> Option<usize> {

    if left > right {
        return None;
    }

    let mid = left + (right - left) / 2;

    if arr[mid] == target {
        return Some(mid);
    } else if arr[mid] < target {
        return binary_search_recursive(arr, target, mid + 1, right);
    } else {
        if mid == 0 { return None; } 
        return binary_search_recursive(arr, target, left, mid - 1);
    }

}

fn exponential_search_recursive(arr: &[i32], target: i32) -> Option<usize> {

    fn helper(arr: &[i32], target: i32, bound: usize) -> Option<usize> {

        if bound >= arr.len() || arr[bound] > target {

            let left = bound / 2;
            let right = if bound < arr.len() { bound } else { arr.len() - 1 };

            return binary_search_recursive(arr, target, left, right);

        }

        helper(arr, target, bound * 2)

    }

    if arr.is_empty() { return None; }
    if arr[0] == target { return Some(0); }

    helper(arr, target, 1)

}

fn main() {

    let numbers = [1, 3, 5, 7, 9, 11, 13, 15];
    let target = 7;
    println!("Array: {:?}", numbers);

    match exponential_search_recursive(&numbers, target) {

        Some(idx) => println!("Element {} found at index {}", target, idx),
        None => println!("Element {} not found", target),

    }

}

Recursion helps beginners see the divide-and-conquer logic more clearly. Each call expands the bound exponentially until the correct interval is found, and the binary search locates the element.

Program 3: Exponential Search with Floating-Point Numbers

This version works on arrays of floats, useful when searching in decimal sequences.

fn binary_search_floats(arr: &[f64], target: f64, left: usize, right: usize) -> Option<usize> {

    let mut l = left;
    let mut r = right;

    while l <= r {

        let mid = l + (r - l) / 2;

        if (arr[mid] - target).abs() < std::f64::EPSILON {
            return Some(mid);
        } else if arr[mid] < target {
            l = mid + 1;
        } else {
            if mid == 0 { break; } 
            r = mid - 1;
        }

    }

    None

}

fn exponential_search_floats(arr: &[f64], target: f64) -> Option<usize> {

    if arr.is_empty() { return None; }
    if (arr[0] - target).abs() < std::f64::EPSILON { return Some(0); }

    let mut i = 1;

    while i < arr.len() && arr[i] <= target {
        i *= 2;
    }

    let left = i / 2;
    let right = if i < arr.len() { i } else { arr.len() - 1 };

    binary_search_floats(arr, target, left, right)

}

fn main() {

    let numbers = [0.5, 1.5, 2.5, 3.5, 4.5, 5.5];
    let target = 3.5;
    println!("Array: {:?}", numbers);

    match exponential_search_floats(&numbers, target) {

        Some(idx) => println!("Element {} found at index {}", target, idx),
        None => println!("Element {} not found", target),

    }

}

Handling floating-point numbers requires careful comparison using EPSILON. This teaches beginners to consider precision issues when adapting algorithms for different data types.

Program 4: Exponential Search with Debug Prints

This program adds print statements to illustrate how the bound expands and how the binary search proceeds.

fn exponential_search_debug(arr: &[i32], target: i32) -> Option<usize> {

    if arr.is_empty() { return None; }
    if arr[0] == target { return Some(0); }

    let mut i = 1;

    while i < arr.len() && arr[i] <= target {

        println!("Checking index {}", i);
        i *= 2;

    }

    let left = i / 2;
    let right = if i < arr.len() { i } else { arr.len() - 1 };

    println!("Binary search between indexes {} and {}", left, right);

    let mut l = left;
    let mut r = right;

    while l <= r {

        let mid = l + (r - l) / 2;

        println!("Binary search mid: {} ({})", mid, arr[mid]);

        if arr[mid] == target {
            return Some(mid);
        } else if arr[mid] < target {
            l = mid + 1;
        } else {
            if mid == 0 { break; } 
            r = mid - 1;
        }

    }

    None

}

fn main() {

    let numbers = [10, 20, 30, 40, 50, 60, 70, 80];
    let target = 50;

    println!("Array: {:?}", numbers);

    match exponential_search_debug(&numbers, target) {

        Some(idx) => println!("Element {} found at index {}", target, idx),
        None => println!("Element {} not found", target),

    }

}

Debug prints make it easier for beginners to understand how exponential growth identifies the probable interval before binary search finds the element.

Program 5: Exponential Search with Early Exit Optimization

This program exits immediately if the target is outside the array bounds, improving efficiency.

fn exponential_search_early_exit(arr: &[i32], target: i32) -> Option<usize> {

    if arr.is_empty() || target < arr[0] || target > arr[arr.len() - 1] {
        return None;
    }

    if arr[0] == target { return Some(0); }

    let mut i = 1;
    while i < arr.len() && arr[i] <= target {
        i *= 2;
    }

    let left = i / 2;
    let right = if i < arr.len() { i } else { arr.len() - 1 };

    let mut l = left;
    let mut r = right;

    while l <= r {

        let mid = l + (r - l) / 2;
        if arr[mid] == target { return Some(mid); }
        else if arr[mid] < target { l = mid + 1; }
        else { if mid == 0 { break; } r = mid - 1; }
    }

    None

}

fn main() {

    let numbers = [3, 6, 9, 12, 15, 18];
    let target = 20;

    println!("Array: {:?}", numbers);

    match exponential_search_early_exit(&numbers, target) {

        Some(idx) => println!("Element {} found at index {}", target, idx),
        None => println!("Element {} not found", target),

    }

}

Early exit prevents unnecessary computations if the target is outside the array bounds, which is very helpful in large datasets.

Frequently Asked Questions (FAQ)

Q1: What is the time complexity of Exponential Search?
Exponential Search runs in O(log n) due to the binary search step. The initial bound finding is O(log p), where p is the position of the target.

Q2: Can Exponential Search work on unsorted arrays?
No. The array must be sorted for this algorithm to work correctly.

Q3: When is Exponential Search better than Binary Search?
It is efficient when the element is near the beginning of a large array, as the exponential step quickly identifies a small interval.

Q4: Can it handle floating-point numbers?
Yes, but use a small threshold (EPSILON) for comparisons to handle precision issues.

Q5: Is Exponential Search recursive or iterative?
It can be implemented both ways. Iterative approaches are memory-efficient, while recursion can make the code cleaner and easier to understand.

Conclusion

Exponential Search is an efficient algorithm for sorted arrays that combines rapid interval expansion with precise binary search. Implementing it in Rust demonstrates key programming concepts like loops, recursion, and handling edge cases. Beginners can gain confidence by trying both integer and floating-point versions, adding debug prints, and using early exit optimizations. Practicing this algorithm strengthens understanding of how combining techniques can lead to faster and smarter solutions in real-world programming.

Scroll to Top