简体   繁体   English

在 Python 中反转排列

[英]Inverting permutations in Python

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:我是编程新手,我正在尝试编写一个 Python 函数来使用以下代码在 {1,2,3,...,n} 上找到排列的倒数:

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 [] .但是,当我尝试使用该函数时, inv('<mypermutation>')返回[] Am I missing something?我错过了什么吗? Is Python skipping over my while loop for some syntactical reason I don't understand? Python 是否因为某种我不明白的语法原因跳过了我的 while 循环? None of my google and stackoverflow searches on topics I think of are returning anything helpful.我对我想到的主题的 google 和 stackoverflow 搜索都没有返回任何有用的东西。

Other answers are correct, but for what it's worth, there's a much more performant alternative using numpy:其他答案是正确的,但就其价值而言,使用 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)

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

I believe the best way to invert a permutation perm is我相信反转排列perm的最佳方法是

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)).这避免了对.index()重复调用(如.index()的回答),这可能不是很有效(二次时间复杂度,而排序只需要 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).但是请注意,无论输入是 {0,1,...,n-1} 的排列还是{1,2,...,n} (后者是问题中所述的内容)。 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:如果输出应该是 {1,2,...,n} 的排列,则结果的每个元素都必须加一,例如,如下所示:

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

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.基本上,按照排列中的值j对排列的索引i进行排序会产生所需的逆。

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

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,3,2,0]

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:既然这里还没有人推荐它,我认为应该提到 SymPy 有一个 完整的组合模块,带有一个Permutation类:

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 == .使用 SymPy 类可以让您访问许多其他有用的方法,例如使用==比较等效排列。

You can read the sympy.combinatorics.Permutation source code here .您可以在此处阅读sympy.combinatorics.Permutation源代码。

Other than that, I would recommend the answer on this page using np.arange and argsort .除此之外,我会推荐使用np.arangeargsort在此页面上的答案

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.如果我有这个错误,请纠正我,但我认为当我将str更改为列表时,我的代码会出现问题: str是一个字符串,而list(str)是一个字符串元素列表。 However, since string elements can't be numerically compared to numbers, the code fails to produce a result (other than [] ).但是,由于字符串元素不能在数字上与数字进行比较,因此代码无法产生结果(除了[] )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM