Inverting permutations in Python

前端 未结 7 1117
余生分开走
余生分开走 2021-01-12 21:08

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(s         


        
相关标签:
7条回答
  • 2021-01-12 21:29

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

    inverse_perm = np.arange(len(permutation))[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.arange(len(permutation))[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:

    71.7 ms ± 859 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    466 µs ± 6.37 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    21.3 µs ± 1.26 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    3.87 µs ± 69.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    
    0 讨论(0)
  • 2021-01-12 21:29

    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 []).

    0 讨论(0)
  • 2021-01-12 21:29

    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__)]
    
    0 讨论(0)
  • 2021-01-12 21:34

    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.

    0 讨论(0)
  • 2021-01-12 21:46

    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
    
    0 讨论(0)
  • 2021-01-12 21:47

    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
    
    0 讨论(0)
提交回复
热议问题