I'm trying to implement LSD radix sort in python, but my code used 4 loop, is there any way I can do it in three or less?
Here is the 4 loops:
First loop iterate through the input list, convert it to desired radix, and change each number to a reversed list, like so:
123 --> 173 --> [3, 7, 1] #radix == 8
Second loop iterate through the newly created list, make them have the same length by adding 0, add a pointer to the corresponding element in input list, then reverse the list.Like this:
input: [1234, 56, 7]
After first loop: [[4, 3, 2, 1], [6, 5], [7]]
After the second loop: [[0, 1, 2, 3, 4], [1, 0, 0, 5, 6], [2, 0, 0, 0, 7]]
Third loop sort element in LSD order using counting sort, code is below.
Fourth loop put every thing in a new list with sorted order using the pointers.
Here's the code for radix sort:
def radix_sort(collection, radix):
new_collect = []
max_len = 0
#loop through collection, convert them to desired form and put in new_collect
for i in collection:
num = []
while i >= radix:
remain = i % radix
num.append(remain)
i = i // radix
num.append(i)
new_collect.append(num)
if len(num) > max_len:
max_len = len(num)
#loop through new_collect, extend the each item to same length
for i in range(0, len(new_collect)):
space = max_len - len(new_collect[i])
patch = [0 for j in range(space)]
new_collect[i].extend(patch)
#add a pointer to the corresponding element in collection
new_collect[i].append(i)
new_collect[i].reverse()
#sort by digit with counting_sort
for i in range(-1, -1 - max_len, -1):
new_collect = counting_sort(new_collect, radix - 1, i)
#create a new list with same length as collection
return_list = list(range(len(collection)))
#put elements in collection to return_list, using the pointer in new_collect
for i in range(0, len(collection)):
return_list[i] = collection[new_collect[i][0]]
return return_list
And here is the code for counting sort:
def counting_sort(collection, d_range, digit = -1):
d_range += 1
B = list(range(len(collection) + 1))
C = list(range(d_range))
for i in range(d_range):
C[i] = 0
for j in collection:
#C[j] = |{key = j}|
C[j[digit]] += 1
for i in range(1, d_range):
#C[i] = |{key <= i}|
C[i] = C[i] + C[i - 1]
for i in range(len(collection) - 1, -1, -1):
B[C[collection[i][digit]]] = collection[i]
C[collection[i][digit]] -= 1
return B[1:]
Reply to Gareth Rees 's answer.
Wow, thank you for the detailed answer!
1.I guess I'm pretty bad at naming and documenting.
2.Never even tought of using |{key < k}| that's pretty neat!
3.The second argument is not the number of keys, at least I don't have the intention to make it so, it should be the range for the input, and I add 1 so that it includes 0. So I guess it's all possible value of keys. In your improved version of the code, the value of the seq could be greater than the length of the bound, which I think will cause a out of range error.
4.About what you said in 4.1, since my radix_sort function require a radix for sorting, and I use the range of value instead of number of keys, wouldn't it be quicker to just input radix-1 or radix instead of using max()?
5.About what you said in 4.2, in my radix sort function, i need to change the raidx of input to a given value, so I need to use a loop to change it, and since I don't want the letters get involved for radixs greater than 10, I need to separate each digit, that's why I used lists. So unless I'm missing something, I don't think I can use you method in this particular scenario.
6.Do you use timeit to calculate the runtime? Or some other modules?
Thanks again for the answer!
PS: Is there anything you can say about my radix sort function's general logic? Is there anyway I can improve it?