Skip to content

Added Median of Medians Algorithm #9864

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 8, 2023
Merged
Changes from all commits
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
107 changes: 107 additions & 0 deletions searches/median_of_medians.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"""
A Python implementation of the Median of Medians algorithm
to select pivots for quick_select, which is efficient for
calculating the value that would appear in the index of a
list if it would be sorted, even if it is not already
sorted. Search in time complexity O(n) at any rank
deterministically
https://en.wikipedia.org/wiki/Median_of_medians
"""


def median_of_five(arr: list) -> int:
"""
Return the median of the input list
:param arr: Array to find median of
:return: median of arr

>>> median_of_five([2, 4, 5, 7, 899])
5
>>> median_of_five([5, 7, 899, 54, 32])
32
>>> median_of_five([5, 4, 3, 2])
4
>>> median_of_five([3, 5, 7, 10, 2])
5
"""
arr = sorted(arr)
return arr[len(arr) // 2]


def median_of_medians(arr: list) -> int:
"""
Return a pivot to partition data on by calculating
Median of medians of input data
:param arr: The data to be checked (a list)
:return: median of medians of input array

>>> median_of_medians([2, 4, 5, 7, 899, 54, 32])
54
>>> median_of_medians([5, 7, 899, 54, 32])
32
>>> median_of_medians([5, 4, 3, 2])
4
>>> median_of_medians([3, 5, 7, 10, 2, 12])
12
"""

if len(arr) <= 5:
return median_of_five(arr)
medians = []
i = 0
while i < len(arr):
if (i + 4) <= len(arr):
medians.append(median_of_five(arr[i:].copy()))
else:
medians.append(median_of_five(arr[i : i + 5].copy()))
i += 5
return median_of_medians(medians)


def quick_select(arr: list, target: int) -> int:
"""
Two way partition the data into smaller and greater lists,
in relationship to the pivot
:param arr: The data to be searched (a list)
:param target: The rank to be searched
:return: element at rank target

>>> quick_select([2, 4, 5, 7, 899, 54, 32], 5)
32
>>> quick_select([2, 4, 5, 7, 899, 54, 32], 1)
2
>>> quick_select([5, 4, 3, 2], 2)
3
>>> quick_select([3, 5, 7, 10, 2, 12], 3)
5
"""

# Invalid Input
if target > len(arr):
return -1

# x is the estimated pivot by median of medians algorithm
x = median_of_medians(arr)
left = []
right = []
check = False
for i in range(len(arr)):
if arr[i] < x:
left.append(arr[i])
elif arr[i] > x:
right.append(arr[i])
elif arr[i] == x and not check:
check = True
else:
right.append(arr[i])
rank_x = len(left) + 1
if rank_x == target:
answer = x
elif rank_x > target:
answer = quick_select(left, target)
elif rank_x < target:
answer = quick_select(right, target - rank_x)
return answer


print(median_of_five([5, 4, 3, 2]))