Skip to content

Commit 17de908

Browse files
Added Median of Medians Algorithm (#9864)
* Added Median of Medians Algorithm * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update median_of_medians.py as per requested changes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent c8f6f79 commit 17de908

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

Diff for: searches/median_of_medians.py

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""
2+
A Python implementation of the Median of Medians algorithm
3+
to select pivots for quick_select, which is efficient for
4+
calculating the value that would appear in the index of a
5+
list if it would be sorted, even if it is not already
6+
sorted. Search in time complexity O(n) at any rank
7+
deterministically
8+
https://en.wikipedia.org/wiki/Median_of_medians
9+
"""
10+
11+
12+
def median_of_five(arr: list) -> int:
13+
"""
14+
Return the median of the input list
15+
:param arr: Array to find median of
16+
:return: median of arr
17+
18+
>>> median_of_five([2, 4, 5, 7, 899])
19+
5
20+
>>> median_of_five([5, 7, 899, 54, 32])
21+
32
22+
>>> median_of_five([5, 4, 3, 2])
23+
4
24+
>>> median_of_five([3, 5, 7, 10, 2])
25+
5
26+
"""
27+
arr = sorted(arr)
28+
return arr[len(arr) // 2]
29+
30+
31+
def median_of_medians(arr: list) -> int:
32+
"""
33+
Return a pivot to partition data on by calculating
34+
Median of medians of input data
35+
:param arr: The data to be checked (a list)
36+
:return: median of medians of input array
37+
38+
>>> median_of_medians([2, 4, 5, 7, 899, 54, 32])
39+
54
40+
>>> median_of_medians([5, 7, 899, 54, 32])
41+
32
42+
>>> median_of_medians([5, 4, 3, 2])
43+
4
44+
>>> median_of_medians([3, 5, 7, 10, 2, 12])
45+
12
46+
"""
47+
48+
if len(arr) <= 5:
49+
return median_of_five(arr)
50+
medians = []
51+
i = 0
52+
while i < len(arr):
53+
if (i + 4) <= len(arr):
54+
medians.append(median_of_five(arr[i:].copy()))
55+
else:
56+
medians.append(median_of_five(arr[i : i + 5].copy()))
57+
i += 5
58+
return median_of_medians(medians)
59+
60+
61+
def quick_select(arr: list, target: int) -> int:
62+
"""
63+
Two way partition the data into smaller and greater lists,
64+
in relationship to the pivot
65+
:param arr: The data to be searched (a list)
66+
:param target: The rank to be searched
67+
:return: element at rank target
68+
69+
>>> quick_select([2, 4, 5, 7, 899, 54, 32], 5)
70+
32
71+
>>> quick_select([2, 4, 5, 7, 899, 54, 32], 1)
72+
2
73+
>>> quick_select([5, 4, 3, 2], 2)
74+
3
75+
>>> quick_select([3, 5, 7, 10, 2, 12], 3)
76+
5
77+
"""
78+
79+
# Invalid Input
80+
if target > len(arr):
81+
return -1
82+
83+
# x is the estimated pivot by median of medians algorithm
84+
x = median_of_medians(arr)
85+
left = []
86+
right = []
87+
check = False
88+
for i in range(len(arr)):
89+
if arr[i] < x:
90+
left.append(arr[i])
91+
elif arr[i] > x:
92+
right.append(arr[i])
93+
elif arr[i] == x and not check:
94+
check = True
95+
else:
96+
right.append(arr[i])
97+
rank_x = len(left) + 1
98+
if rank_x == target:
99+
answer = x
100+
elif rank_x > target:
101+
answer = quick_select(left, target)
102+
elif rank_x < target:
103+
answer = quick_select(right, target - rank_x)
104+
return answer
105+
106+
107+
print(median_of_five([5, 4, 3, 2]))

0 commit comments

Comments
 (0)