F# Program to Implement Ternary Search

F# Program to Implement Ternary Search

When it comes to searching in a sorted array, Binary Search is the most commonly used method, dividing the array into two halves. But there is another interesting algorithm called Ternary Search, which divides the array into three parts instead of two. By checking two midpoints, Ternary Search can sometimes reduce the number of comparisons needed and is especially useful in scenarios like finding maximum or minimum values in unimodal functions or searching efficiently in large datasets.

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

Ternary Search is beginner-friendly because it extends the idea of Binary Search and demonstrates how we can divide the search space more finely to potentially save steps. Learning Ternary Search also reinforces concepts like recursion, loops, and condition-based logic in F#. For anyone starting with search algorithms, understanding Ternary Search provides deeper insight into optimizing searches beyond simple methods.

Program 1: Iterative Ternary Search

This program shows Ternary Search using a loop to iteratively divide the array into three parts and check where the key might exist.

open System

let ternarySearchIterative (arr : int[]) key =

    let mutable left = 0
    let mutable right = arr.Length - 1
    let mutable foundIndex = -1

    while left <= right && foundIndex = -1 do

        let third = (right - left) / 3
        let mid1 = left + third
        let mid2 = right - third

        if arr.[mid1] = key then
            foundIndex <- mid1
        elif arr.[mid2] = key then
            foundIndex <- mid2
        elif key < arr.[mid1] then
            right <- mid1 - 1
        elif key > arr.[mid2] then
            left <- mid2 + 1
        else
            left <- mid1 + 1
            right <- mid2 - 1

    foundIndex


[<EntryPoint>]
let main argv =

    let numbers = [| 1; 3; 5; 7; 9; 11; 13 |]
    let key = 7
    let result = ternarySearchIterative numbers key

    if result <> -1 then
        printfn "Element %d found at index %d" key result
    else
        printfn "Element %d not found" key

    0

In this approach, we calculate two midpoints and reduce the search space based on comparisons. This helps beginners understand how dividing into more parts can still efficiently locate the target.

Program 2: Recursive Ternary Search

Recursion fits naturally with Ternary Search, letting each call handle a smaller portion of the array. This program demonstrates the recursive approach.

open System

let rec ternarySearchRec (arr:int[]) key left right =

    if left > right then -1
    else

        let third = (right - left) / 3
        let mid1 = left + third
        let mid2 = right - third

        if arr.[mid1] = key then mid1
        elif arr.[mid2] = key then mid2
        elif key < arr.[mid1] then ternarySearchRec arr key left (mid1 - 1)
        elif key > arr.[mid2] then ternarySearchRec arr key (mid2 + 1) right
        else ternarySearchRec arr key (mid1 + 1) (mid2 - 1)


[<EntryPoint>]
let main argv =

    let numbers = [| 2; 4; 6; 8; 10; 12; 14 |]
    let key = 10
    let result = ternarySearchRec numbers key 0 (numbers.Length - 1)

    if result <> -1 then
        printfn "Element %d found at index %d" key result
    else
        printfn "Element %d not found" key

    0

This recursive version helps beginners visualize how the array is repeatedly divided into three parts, and the search zone is narrowed down recursively until the key is found or the search ends.

Program 3: Ternary Search with Negative and Positive Numbers

Ternary Search works for arrays with both negative and positive numbers. As long as the array is sorted, the same logic applies.

open System

let rec ternarySearchMixed (arr:int[]) key left right =

    if left > right then -1
    else

        let third = (right - left) / 3
        let mid1 = left + third
        let mid2 = right - third

        if arr.[mid1] = key then mid1
        elif arr.[mid2] = key then mid2
        elif key < arr.[mid1] then ternarySearchMixed arr key left (mid1 - 1)
        elif key > arr.[mid2] then ternarySearchMixed arr key (mid2 + 1) right
        else ternarySearchMixed arr key (mid1 + 1) (mid2 - 1)


[<EntryPoint>]
let main argv =

    let numbers = [| -20; -10; 0; 10; 20; 30; 40 |]
    let key = -10
    let result = ternarySearchMixed numbers key 0 (numbers.Length - 1)

    if result <> -1 then
        printfn "Element %d found at index %d" key result
    else
        printfn "Element %d not found" key

    0

By using this approach, beginners can practice with a broader range of numbers and understand that the algorithm works as long as the array is sorted.

Program 4: Ternary Search Returning Option Type

Returning an option type is more idiomatic in F#, making the result safer. This program demonstrates that.

open System

let rec ternarySearchOption (arr:int[]) key left right =

    if left > right then None
    else

        let third = (right - left) / 3
        let mid1 = left + third
        let mid2 = right - third

        if arr.[mid1] = key then Some mid1
        elif arr.[mid2] = key then Some mid2
        elif key < arr.[mid1] then ternarySearchOption arr key left (mid1 - 1)
        elif key > arr.[mid2] then ternarySearchOption arr key (mid2 + 1) right
        else ternarySearchOption arr key (mid1 + 1) (mid2 - 1)


[<EntryPoint>]
let main argv =

    let numbers = [| 1; 2; 3; 4; 5; 6 |]
    let key = 4

    match ternarySearchOption numbers key 0 (numbers.Length - 1) with
    | Some index -> printfn "Element %d found at index %d" key index
    | None -> printfn "Element %d not found" key

    0

Using option types teaches beginners to handle search results safely without relying on sentinel values like -1.

Program 5: Step-by-Step Ternary Search

This program prints each midpoint checked during the search, making it easier to visualize how the array is divided into three parts.

open System

let ternarySearchStep (arr : int[]) key =

    let mutable left = 0
    let mutable right = arr.Length - 1
    let mutable foundIndex = -1

    // Loop stops naturally once we find the key
    while left <= right && foundIndex = -1 do

        let third = (right - left) / 3
        let mid1 = left + third
        let mid2 = right - third

        printfn "Checking mid1=%d and mid2=%d" mid1 mid2

        if arr.[mid1] = key then
            foundIndex <- mid1
        elif arr.[mid2] = key then
            foundIndex <- mid2
        elif key < arr.[mid1] then
            right <- mid1 - 1
        elif key > arr.[mid2] then
            left <- mid2 + 1
        else
            left <- mid1 + 1
            right <- mid2 - 1

    foundIndex


[<EntryPoint>]
let main argv =

    let numbers = [| 1; 3; 5; 7; 9; 11; 13 |]
    let key = 11
    let result = ternarySearchStep numbers key

    if result <> -1 then
        printfn "Element %d found at index %d" key result
    else
        printfn "Element %d not found" key

    0

Printing midpoints helps beginners understand how Ternary Search divides the array and progressively narrows the search space.

Frequently Asked Questions (FAQ)

This section answers common beginner questions about Ternary Search.

Q1. What is Ternary Search?
Ternary Search is a searching algorithm that divides a sorted array into three parts and checks two midpoints to locate a key.

Q2. Does the array need to be sorted?
Yes, Ternary Search only works on sorted arrays, similar to Binary Search.

Q3. Is Ternary Search faster than Binary Search?
Ternary Search can be slightly faster in certain scenarios, but in practice, the difference is often small. It is more of an optimization example for large datasets.

Q4. Can it handle negative numbers?
Yes, Ternary Search works with both negative and positive numbers as long as the array is sorted.

Q5. Should beginners learn Ternary Search?
Yes, it introduces advanced search logic, recursion, and how dividing the search space more finely can optimize searches.

Conclusion

Ternary Search is an advanced searching technique that divides a sorted array into three parts, checking two midpoints at a time. Through multiple F# programs, we explored iterative and recursive approaches, handling negative numbers, returning option types, and visualizing each step.

For beginners, practicing Ternary Search strengthens understanding of recursive thinking, optimized searching, and safe result handling in F#. Experimenting with different arrays and keys helps solidify the concept and prepares learners for more advanced search algorithms.

Scroll to Top