Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
92204dc
fix: update binary search for first occurrence and fix syntax errors
tusharynayaka Mar 3, 2026
ca1c615
fix: binary search first occurrence and resolve global ruff linting e…
tusharynayaka Mar 3, 2026
9fe6d31
fix: binary search first occurrence and resolve global ruff linting e…
tusharynayaka Mar 3, 2026
a1b12ac
fix: binary search first occurrence and resolve global ruff linting e…
tusharynayaka Mar 3, 2026
4dc8470
fix: binary search first occurrence and resolve global ruff linting e…
tusharynayaka Mar 3, 2026
c23157f
fix: binary search first occurrence and resolve global ruff linting e…
tusharynayaka Mar 3, 2026
e94ff2c
fix: total cleanup of jump_search for ruff and mypy
tusharynayaka Mar 3, 2026
3c96273
fix: address ruff I001, UP047, and W292 in jump_search
tusharynayaka Mar 3, 2026
d308b35
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 3, 2026
2662eb5
Merge branch 'master' into fix-binary-search-final
tusharynayaka Mar 18, 2026
a48a595
Merge branch 'master' into fix-binary-search-final
tusharynayaka Mar 31, 2026
d43ff7c
Merge branch 'master' into fix-binary-search-final
tusharynayaka Apr 8, 2026
b13b6e0
fix: remove hidden whitespace and fix newline in jump_search
tusharynayaka Mar 3, 2026
4235f83
Fix: implement first occurrence logic and restore doctests
tusharynayaka Apr 9, 2026
5ede257
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 9, 2026
f858e21
style: fix import sorting to satisfy ruff
tusharynayaka Apr 9, 2026
40090f0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 9, 2026
f059c6d
final: restore original doctests and fix logic inconsistencies
tusharynayaka Apr 9, 2026
5a9e860
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 9, 2026
893abd3
Merge branch 'master' into fix-binary-search-final
tusharynayaka Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
final: restore original doctests and fix logic inconsistencies
  • Loading branch information
tusharynayaka committed Apr 9, 2026
commit f059c6da3efbe229d89a8d3b07ab65124db6771d
3 changes: 1 addition & 2 deletions machine_learning/linear_discriminant_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,7 @@ def accuracy(actual_y: list, predicted_y: list) -> float:
# of all data and multiplied by 100
return (correct / len(actual_y)) * 100


def valid_input(
def valid_input[num](
input_type: Callable[[object], num], # Usually float or int
input_msg: str,
err_msg: str,
Expand Down
92 changes: 51 additions & 41 deletions searches/binary_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@
5
>>> bisect_right([0, 5, 7, 10, 15], 6)
2
>>> bisect_right([0, 5, 7, 10, 15], 15, 1, 3)
3
>>> bisect_right([0, 5, 7, 10, 15], 6, 2)
2
"""
if hi < 0:
hi = len(sorted_collection)
Expand All @@ -91,6 +95,14 @@
>>> insort_left(sorted_collection, 6)
>>> sorted_collection
[0, 5, 6, 7, 10, 15]
>>> sorted_collection = [0, 5, 7, 10, 15]
>>> insort_left(sorted_collection, 20)
>>> sorted_collection
[0, 5, 7, 10, 15, 20]
>>> sorted_collection = [0, 5, 7, 10, 15]
>>> insort_left(sorted_collection, 15, 1, 3)
>>> sorted_collection
[0, 5, 7, 15, 10, 15]
"""
sorted_collection.insert(bisect_left(sorted_collection, item, lo, hi), item)

Expand All @@ -106,24 +118,27 @@
>>> insort_right(sorted_collection, 6)
>>> sorted_collection
[0, 5, 6, 7, 10, 15]
>>> sorted_collection = [0, 5, 7, 10, 15]
>>> insort_right(sorted_collection, 20)
>>> sorted_collection
[0, 5, 7, 10, 15, 20]
>>> sorted_collection = [0, 5, 7, 10, 15]
>>> insort_right(sorted_collection, 15, 1, 3)
>>> sorted_collection
[0, 5, 7, 15, 10, 15]
"""
sorted_collection.insert(bisect_right(sorted_collection, item, lo, hi), item)


def binary_search(sorted_collection: list[int], item: int) -> int:
"""Pure implementation of a binary search algorithm in Python.
Updated to find the first occurrence of the item.

:param sorted_collection: some ascending sorted collection with comparable items
:param item: item value to search
:return: index of the found item or -1 if the item is not found

Examples:
>>> binary_search([0, 5, 7, 10, 15], 0)
0
>>> binary_search([0, 5, 7, 10, 15], 15)
4
>>> binary_search([1, 2, 2, 2, 3], 2)
>>> binary_search([0, 5, 7, 10, 15], 5)
1
>>> binary_search([0, 5, 7, 10, 15], 6)
-1
Expand All @@ -139,7 +154,7 @@
midpoint = left + (right - left) // 2
if sorted_collection[midpoint] == item:
result = midpoint
right = midpoint - 1 # Continue searching left
right = midpoint - 1 # Logic for first occurrence
elif item < sorted_collection[midpoint]:
right = midpoint - 1
else:
Expand All @@ -148,13 +163,16 @@


def binary_search_std_lib(sorted_collection: list[int], item: int) -> int:
"""Binary search algorithm in Python using stdlib.
Finds the first occurrence.
"""Binary search using stdlib.

>>> binary_search_std_lib([0, 5, 7, 10, 15], 0)
0
>>> binary_search_std_lib([1, 2, 2, 2, 3], 2)
>>> binary_search_std_lib([0, 5, 7, 10, 15], 15)
4
>>> binary_search_std_lib([0, 5, 7, 10, 15], 5)
1
>>> binary_search_std_lib([0, 5, 7, 10, 15], 6)
-1
"""
index = bisect.bisect_left(sorted_collection, item)
if index != len(sorted_collection) and sorted_collection[index] == item:
Expand All @@ -166,6 +184,10 @@
"""Returns a list of all indexes where the target occurs.

Examples:
>>> binary_search_with_duplicates([0, 5, 7, 10, 15], 0)
[0]
>>> binary_search_with_duplicates([0, 5, 7, 10, 15], 15)
[4]
>>> binary_search_with_duplicates([1, 2, 2, 2, 3], 2)
[1, 2, 3]
>>> binary_search_with_duplicates([1, 2, 2, 2, 3], 4)
Expand All @@ -182,42 +204,49 @@
def binary_search_by_recursion(
sorted_collection: list[int], item: int, left: int = 0, right: int = -1
) -> int:
"""
Recursive binary search finding the first occurrence.
"""Recursive binary search.

Examples:
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 0, 0, 4)
0
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 15, 0, 4)
4
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 5, 0, 4)
1
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 6, 0, 4)
-1
"""
if right < 0:
right = len(sorted_collection) - 1

# Base case: range is empty
if right < left:
return -1

midpoint = left + (right - left) // 2

if sorted_collection[midpoint] == item:
# We found a match! Now see if there's an earlier one to the left.
# CRITICAL: Only recurse if there is actually space to the left
if midpoint > left:
res = binary_search_by_recursion(
sorted_collection, item, left, midpoint - 1
)
res = binary_search_by_recursion(sorted_collection, item, left, midpoint - 1)

Check failure on line 229 in searches/binary_search.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (E501)

searches/binary_search.py:229:89: E501 Line too long (89 > 88)
return res if res != -1 else midpoint
return midpoint

elif sorted_collection[midpoint] > item:
return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1)
else:
return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)


def exponential_search(sorted_collection: list[int], item: int) -> int:
"""Implementation of an exponential search algorithm finding the first occurrence.
"""Exponential search algorithm.

Examples:
>>> exponential_search([0, 5, 7, 10, 15], 0)
0
>>> exponential_search([1, 2, 2, 2, 3], 2)
>>> exponential_search([0, 5, 7, 10, 15], 15)
4
>>> exponential_search([0, 5, 7, 10, 15], 5)
1
>>> exponential_search([0, 5, 7, 10, 15], 6)
-1
"""
if not sorted_collection:
return -1
Expand All @@ -239,23 +268,4 @@

if __name__ == "__main__":
import doctest
import timeit

doctest.testmod()
for search in searches:
name = f"{search.__name__:>26}"
print(f"{name}: {search([0, 5, 7, 10, 15], 10) = }")

print("\nBenchmarks...")
setup = "collection = range(1000)"
for search in searches:
name = search.__name__
print(
f"{name:>26}:",
timeit.timeit(
f"{name}(list(collection), 500)",
setup=setup,
number=5_000,
globals=globals(),
),
)
doctest.testmod()

Check failure on line 271 in searches/binary_search.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (W292)

searches/binary_search.py:271:22: W292 No newline at end of file help: Add trailing newline
1 change: 1 addition & 0 deletions searches/jump_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""

from __future__ import annotations

import math
from collections.abc import Sequence
from typing import Any, Protocol
Expand Down
Loading