Skip to content

Instantly share code, notes, and snippets.

@chrisfalter
Last active April 20, 2022 03:36
Show Gist options
  • Save chrisfalter/4734b3df90c3604bb14628229566c83a to your computer and use it in GitHub Desktop.
Save chrisfalter/4734b3df90c3604bb14628229566c83a to your computer and use it in GitHub Desktop.
A reasonably robust binary search *with unit tests*
from typing import List, Tuple
import pytest
def binary_search(desired_member:Tuple[int, float], sorted_list:List[Tuple[int, float]]) -> int:
"""perform binary search over sorted list, return index of desired member"""
# check parameter type
if not (type(desired_member) is int or type(desired_member) is float):
raise TypeError("desired_member must be a number")
if not all(type(n) is int or type(n) is float for n in sorted_list):
raise TypeError("sorted list members must all be numbers")
# check boundaries
value_error_message = "desired_member not in sorted list"
if desired_member > sorted_list[-1] or desired_member < sorted_list[0]:
raise ValueError(value_error_message)
list_len = len(sorted_list)
prev_idx = 0
idx = int(list_len / 2)
selected_member = sorted_list[idx]
while selected_member != desired_member:
last_step = idx - prev_idx
prev_idx = idx
new_step = abs(int(last_step/2))
if new_step == 0:
new_step = 1
if selected_member > desired_member:
if (last_step == 1):
raise ValueError(value_error_message)
idx -= new_step
else:
if (last_step == -1):
raise ValueError(value_error_message)
idx += new_step
selected_member = sorted_list[idx]
return idx
# TESTS
test_list = [i * 2 + 1 for i in range(1024)]
def test_binary_search_finds_correct_index():
for i, target in enumerate(test_list):
assert i == binary_search(target, test_list)
def test_binary_search_raises_value_error_when_target_not_in_search_list():
targets = [0, 2, 4, 32, 48, 1024, 1026, 2046, 2048, 2050]
for t in targets:
with pytest.raises(ValueError):
binary_search(t, test_list)
def test_binary_search_raises_type_error_when_arg_not_numeric():
targets = ["1", True]
for t in targets:
with pytest.raises(TypeError):
binary_search(t, test_list)
with pytest.raises(TypeError):
binary_search("2", list("0123456"))
def test_binary_search_works_for_floats():
float_list = [float(n) for n in test_list]
for i, target in enumerate(test_list):
assert i == binary_search(target, test_list)
targets = [-1.1, 1.1, 3.1, 48.1, 513.1, 2048.9, 2049.3]
for t in targets:
with pytest.raises(ValueError):
binary_search(t, test_list)
# RUN THE TESTS
# in a build script, we would use `python -m pytest path/to/dir_with_tests` to run the whole suite
test_binary_search_finds_correct_index()
test_binary_search_raises_value_error_when_target_not_in_search_list()
test_binary_search_raises_type_error_when_arg_not_numeric()
test_binary_search_works_for_floats()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment