12

I'm new to programming, and I'm trying to write a Python function to find the inverse of a permutation on {1,2,3,...,n} using the following code:

def inv(str):
    result = []
    i = list(str).index(min(list(str)))
    while min(list(str)) < len(list(str)) + 1:
        list(str)[i : i + 1] = [len(list(str)) + 1]
        result.append(i + 1)
    return result

However, when I try to use the function, inv('<mypermutation>') returns []. Am I missing something? Is Python skipping over my while loop for some syntactical reason I don't understand? None of my google and stackoverflow searches on topics I think of are returning anything helpful.

13
  • 4
    Don't name a variable str; it's a built-in. Commented Feb 7, 2012 at 23:41
  • 5
    "When in doubt, print more out." Commented Feb 7, 2012 at 23:41
  • I tried renaming 'str' as 'permutation' and it still returned '[]'. Any other tips? Commented Feb 7, 2012 at 23:44
  • 3
    @Fingolfin: Oh, wait, are you literally executing inv('<mypermutation>')? Then the while conditions compares a string to an integer, which might have a False result, depending on the version the Python you use. In Python 3, for example, you'll get an error. Commented Feb 8, 2012 at 0:13
  • 2
    Fingolfin, I think you need to show a literal, unmodified example of how you call this function to help people figure this out. Commented Feb 8, 2012 at 0:27

7 Answers 7

24

Other answers are correct, but for what it's worth, there's a much more performant alternative using numpy:

inverse_perm = np.argsort(permutation)

EDIT: and the fourth function below is even faster.

Timing code:

def invert_permutation_list_scan(p):
    return [p.index(l) for l in range(len(p))]

def invert_permutation_list_comp(permutation):
    return [i for i, j in sorted(enumerate(permutation), key=lambda i_j: i_j[1])]

def invert_permutation_numpy(permutation):
    return np.argsort(permutation)

def invert_permutation_numpy2(permutation):
    inv = np.empty_like(permutation)
    inv[permutation] = np.arange(len(inv), dtype=inv.dtype)
    return inv

x = np.random.randn(1000)
perm = np.argsort(x)
permlist = list(perm)
assert np.array_equal(invert_permutation_list_scan(permlist), invert_permutation_numpy(perm))
assert np.array_equal(invert_permutation_list_comp(perm), invert_permutation_numpy(perm))
assert np.array_equal(invert_permutation_list_comp(perm), invert_permutation_numpy2(perm))
%timeit invert_permutation_list_scan(permlist)
%timeit invert_permutation_list_comp(perm)
%timeit invert_permutation_numpy(perm)
%timeit invert_permutation_numpy2(perm)

Results:

82.2 ms ± 7.28 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
479 µs ± 9.19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
18 µs ± 1.17 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
4.22 µs ± 388 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Sign up to request clarification or add additional context in comments.

4 Comments

Well, if you're going for speed, then you can do less work: . ``` def invert_permutation_numpy2(permutation): x = np.empty_like(permutation) x[permutation] = np.arange(len(permutation), dtype=permutation.dtype) return x ``` . On the mini-benchmarks it beats numpy sorting 10-20x
@VF1 Nice. I've edited my answer to include this even faster function.
I think np.arange(len(permutation))[np.argsort(permutation)] equals np.argsort(permutation).
Good catch. Fixed!
12

If you only want the inverse permutation, you can use

def inv(perm):
    inverse = [0] * len(perm)
    for i, p in enumerate(perm):
        inverse[p] = i
    return inverse

perm = [3, 0, 2, 1]
print(inv(perm))
for i in perm:
    print(inv(perm)[i])

[1, 3, 2, 0]
0
1
2
3

Comments

4

I believe the best way to invert a permutation perm is

pinv = sorted(range(len(perm)), key=perm.__getitem__)

This avoids repeated calls to .index() (as in the answer by SeF), which may not be very efficient (quadratic time complexity, while sorting should only take O(n log n)).

Note, however, that this yields as a result a permutation of {0,1,...n-1}, regardless of whether the input was a permutation of {0,1,...,n-1} or of {1,2,...,n} (the latter is what is stated in the question). If the output is supposed to be a permutation of {1,2,...,n}, each element of the result has to be increased by one, for example, like this:

pinv = [i+1 for i in sorted(range(len(perm)), key=perm.__getitem__)]

Comments

3

Just since no one has recommended it here yet, I think it should be mentioned that SymPy has an entire combinatorics module, with a Permutation class:

from sympy.combinatorics import Permutation
o = [3, 0, 2, 1]
p = Permutation(o)
inv = p.__invert__()
print(inv.array_form) # [1, 3, 2, 0]

Using the SymPy class gives you access to a whole lot of other useful methods, such as comparison between equivalent permutations with ==.

You can read the sympy.combinatorics.Permutation source code here.

Other than that, I would recommend the answer on this page using np.arange and argsort.

Comments

2

Maybe there is a shorter way:

def invert(p):
    return [p.index(l) for l in range(len(p))] 

so that:

perm = [3, 0, 2, 1]; print(invert(perm))

returns

[1,3,2,0]

1 Comment

Note that this is inefficient, O(len(p) ^ 2) in time.
1

Correct me if I have this wrong, but I think the problem with my code comes when I change str to a list: str is a string, and list(str) is a list of string elements. However, since string elements can't be numerically compared to numbers, the code fails to produce a result (other than []).

Comments

1

A "functional style" version:

def invert_permutation(permutation):
    return [i for i, j in sorted(enumerate(permutation), key=lambda (_, j): j)]

Basically, sorting the indices i of the permutation by their values j in the permutation yields the desired inverse.

p = [2, 1, 5, 0, 4, 3]

invert_permutation(p)
# [3, 1, 0, 5, 4, 2]

# inverse of inverse = identity
invert_permutation(invert_permutation(p)) == p
# True

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.