You can use the built-in sorting functions with custom comparator.
By that, you get the desired permutation of a
.
Then, apply the swaps to actually put items of a
into right places.
Below is a step-by-step explanation.
First, let us pretend we have a list a
with functions compare
and swap
, and we also know its length n
:
a = [13, 77, 26, 44, 27, 35, 99, 27, 91, 18, 23, 48]
def compare (i, j):
return a[i] % 10 < a[j] % 10
def swap (i, j):
a[i], a[j] = a[j], a[i]
n = len (a)
In this example, compare
compares the last digits of the numbers in a
.
Now, in the question, we don't have direct access to a
.
Instead, we have to use the two functions.
Nevertheless, we will want to use a builtin Python sorting function.
What can we sort if a
is not accessible?
We can create a permutation p
: a list of positions in a
.
And then sort
it according to what would be the right order in a
.
The call to sort
will make O(n log n)
calls to compare
.
from functools import cmp_to_key
cmp = lambda i, j: 0 if i == j else -1 if compare (i, j) else +1
p = list (range (n))
p.sort (key = cmp_to_key (cmp))
print (p) # [8, 0, 10, 3, 5, 2, 1, 4, 7, 9, 11, 6]
The result [8, 0, 10, ..., 11, 6]
means the following:
"The right order is a[8]
, then a[0]
, then a[10]
, ..., then a[11]
, then a[6]
".
To learn more about sorting with a custom comparator, see here.
To learn how cmp_to_key
works, see there.
Permutation p
says "p[i]
is where to get the i
-th element of the answer".
For convenience, let us construct the inverse permutation q
instead.
Permutation q
will say "q[i]
is where to put the i
-th element of the input".
q = [0] * n
for i in range (n):
q[p[i]] = i
print (q) # [1, 6, 5, 3, 7, 4, 11, 8, 0, 9, 2, 10]
The result [1, 6, 5, ..., 2, 10]
means the following:
a[0]
goes into position 1;
a[1]
goes into position 6;
a[2]
goes into position 5;
...
a[10]
goes into position 2;
a[11]
goes into position 10.
Now, we finally move the elements of a
using swap
.
for i in range (n):
while q[i] != i:
j = q[i]
swap (i, j)
q[i], q[j] = q[j], q[i]
print (a) # [91, 13, 23, 44, 35, 26, 77, 27, 27, 18, 48, 99]
This says: while the element at position i
has to go somewhere else, let us put it there, at position j = q[i]
.
As we can only swap pairs of elements, the element which was at position j
simultaneously goes to position i
.
Now q[j]
is j
: this element went to its final place.
And q[i]
now is the former value of q[j]
: this element may still have to go somewhere.
Each operation in the body of the while
loop puts one element to its final place, and we don't move it after that.
Since there are n
elements in total, we will make no more than n
calls to swap
.
In total, we made O(n log n)
calls to compare
and O(n)
calls to swap
.
Complete code is below.
Your program will not have the first part with a
, compare
, and swap
, because they are defined externally.
a = [13, 77, 26, 44, 27, 35, 99, 27, 91, 18, 23, 48]
def compare (i, j):
return a[i] % 10 < a[j] % 10
def swap (i, j):
a[i], a[j] = a[j], a[i]
n = len (a)
from functools import cmp_to_key
cmp = lambda i, j: 0 if i == j else -1 if compare (i, j) else +1
p = list (range (n))
p.sort (key = cmp_to_key (cmp))
print (p) # [8, 0, 10, 3, 5, 2, 1, 4, 7, 9, 11, 6]
q = [0] * n
for i in range (n):
q[p[i]] = i
print (q) # [1, 6, 5, 3, 7, 4, 11, 8, 0, 9, 2, 10]
for i in range (n):
while q[i] != i:
j = q[i]
swap (i, j)
q[i], q[j] = q[j], q[i]
print (a) # [91, 13, 23, 44, 35, 26, 77, 27, 27, 18, 48, 99]