这个问题可以简单地通过简化它并递归地思考来解决。因此,我们首先假设输入序列中的所有元素都是唯一的,那么“唯一”置换的集合就是排列的集合。
我们找到序列a_1, a_2, a_3, ..., a_n
的排名进入其排列集合,我们可以的:
排序的序列,以获得b_1, b_2, ..., b_n
。这个定义的排列有排名0
。
现在我们比较a_1
和b_1
。如果它们相同,那么我们可以简单地将它们从问题中移除:a_1, a_2, ..., a_n
的排名将与仅仅a_2, ..., a_n
的排名相同。
否则b_1 < a_1
,但随后所有排列与b_1
开始会比a_1, a_2, ..., a_n
小。这种排列的数量很容易计算,它只是(n-1)! = (n-1)*(n-2)*(n-3)*...*1
。
但是,我们可以继续看看我们的序列b_1, ..., b_n
。如果b_2 < a_1
,所有以b_2
开头的排列都将更小。 所以我们应该再添加(n-1)!
到我们的等级。
我们这样做,直到我们找到一个索引j
其中b_j == a_j
,然后我们点结束2.
这可以实现很容易:
import math
def permutation_rank(seq):
ref = sorted(seq)
if ref == seq:
return 0
else:
rank = 0
f = math.factorial(len(seq)-1)
for x in ref:
if x < seq[0]:
rank += f
else:
rank += permutation_rank(seq[1:]) if seq[1:] else 0
return rank
的解决方案是相当快速:
In [24]: import string
...: import random
...: seq = list(string.ascii_lowercase)
...: random.shuffle(seq)
...: print(*seq)
...: print(permutation_rank(seq))
...:
r q n c d w s k a z b e m g u f i o l t j x p h y v
273956214557578232851005079
在等元素的问题上:t嘿发挥作用是(n-1)!
是排列的数量,考虑到每个元素不同于其他元素。如果你有一个长度为n
的序列,由符号s_1, ..., s_k
和符号s_j
出现c_j
次,那么唯一排列的数目是'(n-1)! /(c_1!* c_2!* ... * c_k!)。
这意味着,我们不必仅仅添加(n-1)!
,而是将其除以该数字,并且我们还希望减少我们正在考虑的当前符号的计数c_t
。
import math
from collections import Counter
from functools import reduce
from operator import mul
def permutation_rank(seq):
ref = sorted(seq)
counts = Counter(ref)
if ref == seq:
return 0
else:
rank = 0
f = math.factorial(len(seq)-1)
for x in sorted(set(ref)):
if x < seq[0]:
counts_copy = counts.copy()
counts_copy[x] -= 1
rank += f//(reduce(mul, (math.factorial(c) for c in counts_copy.values()), 1))
else:
rank += permutation_rank(seq[1:]) if seq[1:] else 0
return rank
我敢肯定有一种方法,以避免拷贝数的字典,但现在我已经厌倦了,所以我会告知为:
这可以通过这种方式来完成为读者锻炼。
仅供参考,最终的结果是:
In [44]: for i,x in enumerate(sorted(set(it.permutations('aabc')))):
...: print(i, x, permutation_rank(x))
...:
0 ('a', 'a', 'b', 'c') 0
1 ('a', 'a', 'c', 'b') 1
2 ('a', 'b', 'a', 'c') 2
3 ('a', 'b', 'c', 'a') 3
4 ('a', 'c', 'a', 'b') 4
5 ('a', 'c', 'b', 'a') 5
6 ('b', 'a', 'a', 'c') 6
7 ('b', 'a', 'c', 'a') 7
8 ('b', 'c', 'a', 'a') 8
9 ('c', 'a', 'a', 'b') 9
10 ('c', 'a', 'b', 'a') 10
11 ('c', 'b', 'a', 'a') 11
,并表明它是有效的:
In [45]: permutation_rank('zuibibzboofpaoibpaybfyab')
Out[45]: 246218968687554178
看看我的解决方案,它花了3秒钟来计算长度为100,000的字符串的独特排列:) – Sawel
嘿锤子,我不认为你的作品。例如,在'abba'上,这应该返回第二个索引。 'aabb','abab',然后排序列表中的第3个元素将是'abba'。你的回报为6。我认为Bakuriu的密切/正确,我正在看。 – WhitneyChia
在你的问题的某些部分,你说你想找到*所有*的排列。这只是**不可能,他们太多了。例如,你说“你想找到这个字符串的所有独特的排列。”那么为什么你的函数计算的是排名呢?此外,为什么该函数被称为'find_all'而不是'rank' /'permutation_rank'。 – Bakuriu