順列から数へ:
K を文字クラスの数とします (例: AAABBC には 3 つの文字クラスがあります)。
N[K] を各文字クラスの要素数とします。(例: AAABBC の場合、N[K]=[3,2,1]、N= sum(N[K]) とします)
シーケンスのすべての正当な順列は、不完全な K-way ツリー内のパスに一意に対応します。
次に、順列の一意の番号は、K-ary ツリーターミナル ノードのポストオーダー トラバーサルにおけるツリー ノードのインデックスに対応します。
幸いなことに、実際にツリー トラバーサルを実行する必要はありません。ツリー内のターミナル ノードの数が辞書式に自分のノードより少ないことを知る必要があるだけです。ツリー内の任意のノードで、現在のノードの下にあるターミナル ノードの数は、シーケンス内の未使用の要素を使用した順列の数に等しいため、これは非常に簡単に計算できます。階乗。
したがって、元の 6 文字と順列の最初の要素が 'B' であるとすると、5!/3!1!1! と判断します。= 'A' で始まる 20 個の要素なので、順列数は 20 より大きい必要があります。最初の文字が 'C' だった場合、5!/2!2!1! と計算できます。(A ではない) + 5!/3!1!1! (not B) = 30+ 20、または代わりに 60 (合計) - 5!/3!2!0! (C) = 50
これを使用して、順列 (例: 'BAABCA') を取得し、次の計算を実行できます: Permuation #= (5!/2!2!1!) ('B') + 0('A') + 0(' A')+ 3!/1!1!1! ('B') + 2!/1!
= 30 + 3 +2 = 35
これが機能することの確認: CBBAAA は次のように対応します
(5!/2!2!1! (A ではない) + 5!/3!1!1! (B ではない)) 'C'+ 4!/2!2!0! (A ではない) 'B' + 3!/2!1!0! (A ではない) 'B' = (30 + 20) +6 + 3 = 59
同様に、AAABBC = 0 ('A') + 0 'A' + '0' A' + 0 'B' + 0 'B' + 0 'C = 0
実装例:
import math
import copy
from operator import mul
def computePermutationNumber(inPerm, inCharClasses):
permutation=copy.copy(inPerm)
charClasses=copy.copy(inCharClasses)
n=len(permutation)
permNumber=0
for i,x in enumerate(permutation):
for j in xrange(x):
if( charClasses[j]>0):
charClasses[j]-=1
permNumber+=multiFactorial(n-i-1, charClasses)
charClasses[j]+=1
if charClasses[x]>0:
charClasses[x]-=1
return permNumber
def multiFactorial(n, charClasses):
val= math.factorial(n)/ reduce(mul, (map(lambda x: math.factorial(x), charClasses)))
return val
数から順列へ: このプロセスは逆に行うことができますが、どれほど効率的かはわかりません。順列番号。
たとえば、順列数が 59 の場合、最初に 30 + 20 = 50 ('C') を減算して 9 を残します。次に、'B' (6) と 2 番目の 'B'(3) を減算して、元の値を再生成します。順列。