這個問題可以簡單地通過簡化它並遞歸地思考來解決。因此,我們首先假設輸入序列中的所有元素都是唯一的,那麼「唯一」置換的集合就是排列的集合。
我們找到序列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