Skip to content
Open
Changes from 1 commit
Commits
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
71 changes: 71 additions & 0 deletions maths/median_of_medians.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""
Median of Medians Algorithm
Guarantees O(n) worst-case time complexity for finding the k-th smallest element.
Reference: https://en.wikipedia.org/wiki/Median_of_medians
"""


def partition(arr: list, pivot: int) -> tuple:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As there is no test file in this pull request nor any test function or class in the file maths/median_of_medians.py, please provide doctest for the function partition

"""Partition array into elements less than, equal to, and greater than pivot."""
low = [x for x in arr if x < pivot]
high = [x for x in arr if x > pivot]
equal = [x for x in arr if x == pivot]
return low, equal, high


def median_of_medians(arr: list, k: int) -> int:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide descriptive name for the parameter: k

"""
Find the k-th smallest element in an unsorted list using Median of Medians.

Args:
arr: List of comparable elements
k: 1-based index of the desired smallest element

Returns:
The k-th smallest element in arr

Raises:
ValueError: If k is out of range

Examples:
>>> median_of_medians([3, 1, 4, 1, 5, 9, 2, 6], 3)
2
>>> median_of_medians([7, 2, 10, 5], 1)
2
>>> median_of_medians([1, 2, 3, 4, 5], 5)
5
"""
if not 1 <= k <= len(arr):
raise ValueError(f"k={k} is out of range for array of length {len(arr)}")

Check failure on line 39 in maths/median_of_medians.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (EM102)

maths/median_of_medians.py:39:26: EM102 Exception must not use an f-string literal, assign to variable first help: Assign to variable; remove f-string literal

# Base case
if len(arr) <= 5:
return sorted(arr)[k - 1]

# Step 1: Divide into chunks of 5 and find median of each chunk
chunks = [arr[i : i + 5] for i in range(0, len(arr), 5)]
medians = [sorted(chunk)[len(chunk) // 2] for chunk in chunks]

# Step 2: Recursively find median of medians
pivot = median_of_medians(medians, len(medians) // 2 + 1)

# Step 3: Partition around pivot
low, equal, high = partition(arr, pivot)

# Step 4: Recurse into the correct partition
if k <= len(low):
return median_of_medians(low, k)
elif k <= len(low) + len(equal):
return pivot
else:
return median_of_medians(high, k - len(low) - len(equal))


if __name__ == "__main__":
import doctest
doctest.testmod()

Check failure on line 67 in maths/median_of_medians.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (W293)

maths/median_of_medians.py:67:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
sample = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
print(f"Array: {sample}")
for i in range(1, len(sample) + 1):
print(f" {i}-th smallest: {median_of_medians(sample, i)}")

Check failure on line 71 in maths/median_of_medians.py

View workflow job for this annotation

GitHub Actions / ruff

ruff (W292)

maths/median_of_medians.py:71:68: W292 No newline at end of file help: Add trailing newline
Loading