Searching

2 problems from Rosalind — Algorithmic Heights. Press Run on any block to execute it in your browser.

BINS — Binary Search

solvedbrowser + CLI Problem statement Open in the workbench Download .bl

# Rosalind: BINS — Binary Search
# https://rosalind.info/problems/bins/
#
# Given: Two positive integers n and m, a sorted array A[1..n], and m keys.
# Return: For each key, an index j with A[j] = key, or -1 if there is none.

let a = [10, 20, 30, 40, 50]
let queries = [40, 10, 35, 15, 40, 20]

# Halve the live window until it is empty. Rosalind indexes from 1, so the
# answer is the zero-based position plus one.
fn binary_search(sorted, key) {
    let lo = 0
    let hi = len(sorted) - 1
    let found = -1
    while lo <= hi and found == -1 {
        let mid = int((lo + hi) / 2)
        let value = sorted[mid]
        if value == key then {
            found = mid + 1
        } else {
            if value < key then lo = mid + 1 else hi = mid - 1
        }
    }
    found
}

let result = queries |> map(|k| str(binary_search(a, k))) |> join(" ")

println("Result:   " + result)
println("Expected: 4 1 -1 -1 4 2")

fn test_bins_locates_every_key() {
    assert result == "4 1 -1 -1 4 2", "BINS: got " + result
}

MED — Median

solvedbrowser + CLI Problem statement Open in the workbench Download .bl

Quickselect: recurses into one side only, so it answers the question without sorting. Cross-checked against sorting in the assertion.

# Rosalind: MED — Median
# https://rosalind.info/problems/med/
#
# Given: A positive integer n, an array A[1..n] of integers, and a number k.
# Return: The k-th smallest element of A.

let a = [2, 36, 5, 21, 8, 13, 11, 20, 5, 4, 1]
let k = 8

# Quickselect: partition around a pivot, then recurse into the side that holds
# the k-th element instead of both. Sorting would answer the question too and do
# more work than asked — this is the problem's point. The array holds 5 twice,
# so the equal region is kept separate rather than folded into one side.
fn select_kth(xs, rank) {
    if len(xs) <= 1 {
        xs[0]
    } else {
        let pivot = xs[int(len(xs) / 2)]
        let below = xs |> filter(|v| v < pivot)
        let equal = xs |> filter(|v| v == pivot)
        let above = xs |> filter(|v| v > pivot)

        if rank <= len(below) {
            select_kth(below, rank)
        } else {
            if rank <= len(below) + len(equal) {
                pivot
            } else {
                select_kth(above, rank - len(below) - len(equal))
            }
        }
    }
}

let result = select_kth(a, k)

println("Result:   " + str(result))
println("Expected: 13")
println("Sorted:   " + str(sort(a)))

fn test_med_kth_smallest() {
    assert result == 13, "MED: got " + str(result)
    assert result == sort(a)[k - 1], "MED: disagrees with sorting, got " + str(result)
}