[英]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.arange
和argsort
在此页面上的答案。
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.