Skip to content

Commit af84c75

Browse files
committed
feat: add Reversort sorting algorithm (#11800)
- Add reversort.py with Reversort algorithm implementation - Algorithm from Google Code Jam 2021 Qualification Round - Time complexity: O(n²) - Space complexity: O(n) due to slicing, can be O(1) with in-place reversal - Includes reversort() for sorting and reversort_cost() for computing cost - Comprehensive doctests included
1 parent 841e947 commit af84c75

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

sorts/reversort.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""
2+
Reversort is a sorting algorithm described in Google Code Jam 2021 Qualification Round.
3+
4+
Algorithm:
5+
1. For i from 1 to N-1:
6+
a. Find the position j of the minimum element in the subarray from position i to N
7+
b. Reverse the subarray from position i to j
8+
9+
Time Complexity: O(n²) - For each position, we find the minimum and reverse
10+
Space Complexity: O(n) - Due to list slicing in Python
11+
(can be O(1) with in-place reversal)
12+
13+
For doctests run following command:
14+
python3 -m doctest -v reversort.py
15+
16+
For manual testing run:
17+
python reversort.py
18+
"""
19+
20+
from typing import Any
21+
22+
23+
def reversort(collection: list[Any]) -> list[Any]:
24+
"""
25+
Sort a list using the Reversort algorithm.
26+
27+
Reversort works by repeatedly finding the minimum element in the unsorted
28+
portion and reversing the subarray from the current position to where the
29+
minimum element is located.
30+
31+
:param collection: A mutable ordered collection with comparable items
32+
:return: The sorted collection in ascending order
33+
34+
Examples:
35+
>>> reversort([4, 2, 1, 3])
36+
[1, 2, 3, 4]
37+
>>> reversort([0, 5, 3, 2, 2])
38+
[0, 2, 2, 3, 5]
39+
>>> reversort([])
40+
[]
41+
>>> reversort([-2, -5, -45])
42+
[-45, -5, -2]
43+
>>> reversort([1])
44+
[1]
45+
>>> reversort([5, 4, 3, 2, 1])
46+
[1, 2, 3, 4, 5]
47+
>>> reversort([2, 1, 4, 3])
48+
[1, 2, 3, 4]
49+
>>> reversort([-23, 0, 6, -4, 34])
50+
[-23, -4, 0, 6, 34]
51+
>>> reversort([1, 2, 3, 4])
52+
[1, 2, 3, 4]
53+
>>> reversort([3, 3, 3, 3])
54+
[3, 3, 3, 3]
55+
>>> reversort([56])
56+
[56]
57+
>>> reversort([0, 5, 2, 3, 2]) == sorted([0, 5, 2, 3, 2])
58+
True
59+
>>> reversort([]) == sorted([])
60+
True
61+
>>> reversort([-2, -45, -5]) == sorted([-2, -45, -5])
62+
True
63+
>>> reversort([-23, 0, 6, -4, 34]) == sorted([-23, 0, 6, -4, 34])
64+
True
65+
>>> reversort(['d', 'a', 'b', 'e']) == sorted(['d', 'a', 'b', 'e'])
66+
True
67+
>>> reversort(['z', 'a', 'y', 'b', 'x', 'c'])
68+
['a', 'b', 'c', 'x', 'y', 'z']
69+
>>> reversort([1.1, 3.3, 5.5, 7.7, 2.2, 4.4, 6.6])
70+
[1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7]
71+
>>> reversort([1, 3.3, 5, 7.7, 2, 4.4, 6])
72+
[1, 2, 3.3, 4.4, 5, 6, 7.7]
73+
>>> import random
74+
>>> collection_arg = random.sample(range(-50, 50), 100)
75+
>>> reversort(collection_arg) == sorted(collection_arg)
76+
True
77+
>>> import string
78+
>>> collection_arg = random.choices(string.ascii_letters + string.digits, k=100)
79+
>>> reversort(collection_arg) == sorted(collection_arg)
80+
True
81+
"""
82+
arr = collection[:] # Create a copy to avoid modifying the original
83+
n = len(arr)
84+
85+
for i in range(n - 1):
86+
# Find the position of the minimum element in arr[i:]
87+
min_index = i
88+
for j in range(i + 1, n):
89+
if arr[j] < arr[min_index]:
90+
min_index = j
91+
92+
# Reverse the subarray from position i to min_index
93+
if min_index != i:
94+
arr[i:min_index + 1] = arr[i:min_index + 1][::-1]
95+
96+
return arr
97+
98+
99+
def reversort_cost(collection: list[Any]) -> int:
100+
"""
101+
Calculate the cost of sorting using Reversort.
102+
103+
The cost is defined as the sum of the lengths of all reversed segments.
104+
This is based on the Google Code Jam 2021 problem.
105+
106+
:param collection: A mutable ordered collection with comparable items
107+
:return: The total cost of sorting
108+
109+
Examples:
110+
>>> reversort_cost([4, 2, 1, 3])
111+
6
112+
>>> reversort_cost([1, 2])
113+
1
114+
>>> reversort_cost([7, 6, 5, 4, 3, 2, 1])
115+
12
116+
>>> reversort_cost([1, 2, 3, 4])
117+
3
118+
>>> reversort_cost([1])
119+
0
120+
>>> reversort_cost([])
121+
0
122+
"""
123+
arr = collection[:] # Create a copy to avoid modifying the original
124+
n = len(arr)
125+
total_cost = 0
126+
127+
for i in range(n - 1):
128+
# Find the position of the minimum element in arr[i:]
129+
min_index = i
130+
for j in range(i + 1, n):
131+
if arr[j] < arr[min_index]:
132+
min_index = j
133+
134+
# Reverse the subarray from position i to min_index
135+
arr[i:min_index + 1] = arr[i:min_index + 1][::-1]
136+
137+
# Cost is the length of the reversed segment
138+
total_cost += min_index - i + 1
139+
140+
return total_cost
141+
142+
143+
if __name__ == "__main__":
144+
import doctest
145+
from random import sample
146+
from timeit import timeit
147+
148+
doctest.testmod()
149+
150+
user_input = input("Enter numbers separated by a comma:\n").strip()
151+
unsorted = [int(item) for item in user_input.split(",")]
152+
print(f"Sorted list: {reversort(unsorted)}")
153+
print(f"Sort cost: {reversort_cost(unsorted)}")
154+
155+
# Benchmark
156+
num_runs = 1000
157+
test_arr = sample(range(-50, 50), 100)
158+
timer = timeit("reversort(test_arr[:])", globals=globals(), number=num_runs)
159+
print(f"\nProcessing time: {timer:.5f}s for {num_runs:,} runs on 100 elements")

0 commit comments

Comments
 (0)