-
Notifications
You must be signed in to change notification settings - Fork 0
/
select_rank.py
79 lines (61 loc) · 2.5 KB
/
select_rank.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def selectRank(rank, L, fromIndex, toIndex):
"""
Returns the pair (value, index) of the element in
L[fromIndex : toIndex] with the desired rank.
"""
if toIndex == fromIndex + 1:
return (L[fromIndex], fromIndex)
pivot, pivotIndex = selectPivot(L, fromIndex, toIndex)
pivotIndex = partition(pivotIndex, L, fromIndex, toIndex)
pivotRank = pivotIndex - fromIndex + 1
if rank < pivotRank:
return selectRank(rank, L, fromIndex, pivotIndex)
if rank > pivotRank:
return selectRank(rank - pivotRank, L, pivotIndex+1, toIndex)
return (pivot, pivotIndex)
def selectPivot(L, fromIndex, toIndex):
"""
Divides L[fromIndex : toIndex] into chunks of five elements,
computes the median of each chunk, and applies selectRank to
compute the median of medians. The result is a pivot that
partitions the given segment approximately around its median.
"""
mediansFrontier = fromIndex
for fromChunkIndex in range(fromIndex, toIndex, 5):
toChunkIndex = min(fromChunkIndex + 5, toIndex)
moveMedianToFront(mediansFrontier, L, fromChunkIndex, toChunkIndex)
mediansFrontier += 1
medianOfMedians = (mediansFrontier - fromIndex + 1)//2
return selectRank(medianOfMedians, L, fromIndex, mediansFrontier)
def moveMedianToFront(position, L, fromIndex, toIndex):
"""
Computes the median of L[fromIndex : toIndex] and moves it
to the specified position.
"""
insertionSort(L, fromIndex, toIndex)
medianIndex = (fromIndex + toIndex - 1)//2
L[medianIndex], L[position] = L[position], L[medianIndex]
def insertionSort(L, fromIndex, toIndex):
"""
Sorts L[fromIndex : toIndex].
"""
for valueIndex in range(fromIndex+1, toIndex):
value = L[valueIndex]
index = valueIndex-1
while index >= fromIndex and L[index] > value:
L[index+1] = L[index]
index -= 1
L[index+1] = value
def partition(pivotIndex, L, fromIndex, toIndex):
"""
Partitions L[fromIndex : toIndex] around the given pivot.
"""
L[fromIndex], L[pivotIndex] = L[pivotIndex], L[fromIndex]
pivot = L[fromIndex]
pivotIndex = fromIndex
for index in range(fromIndex+1, toIndex):
if L[index] < pivot:
pivotIndex += 1
L[pivotIndex], L[index] = L[index], L[pivotIndex]
L[fromIndex], L[pivotIndex] = L[pivotIndex], pivot
return pivotIndex