Python, 302 287 bayt
Dead Possum zaten kısa bir Pythonic çözümü gönderdi, bu yüzden ekstra kudos almaya karar verdim. Bu çözüm yok değil bütün permütasyon üretir. Oldukça büyük bir dizenin permütasyon indeksini hızlı bir şekilde hesaplayabilir; ayrıca boş bir dizeyi doğru şekilde işler.
from math import factorial as f
from itertools import groupby as g
def p(t,b=''):
if len(t)<2:return 0
z,b=0,b or sorted(t)
for i,c in enumerate(b):
w=b[:i]+b[i+1:]
if c==t[0]:return z+p(t[1:],w)
if i<1 or c!=b[i-1]:
n=f(len(w))
for _,v in g(w):n//=f(len(list(v)))
z+=n
Test kodu:
def lexico_permute_string(s):
''' Generate all permutations of `s` in lexicographic order '''
a = sorted(s)
n = len(a) - 1
while True:
yield ''.join(a)
for j in range(n-1, -1, -1):
if a[j] < a[j + 1]:
break
else:
return
v = a[j]
for k in range(n, j, -1):
if v < a[k]:
break
a[j], a[k] = a[k], a[j]
a[j+1:] = a[j+1:][::-1]
def test_all(base):
for i, s in enumerate(lexico_permute_string(base)):
rank = p(s)
assert rank == i, (i, s, rank)
print('{:2} {} {:2}'.format(i, s, rank))
print(repr(base), 'ok\n')
for base in ('AAB', 'abbbbc'):
test_all(base)
def test(s):
print('{!r}\n{}\n'.format(s, p(s)))
for s in ('ZZZ', 'DCBA', 'a quick brown fox jumps over the lazy dog'):
test(s)
çıktı
0 AAB 0
1 ABA 1
2 BAA 2
'AAB' ok
0 abbbbc 0
1 abbbcb 1
2 abbcbb 2
3 abcbbb 3
4 acbbbb 4
5 babbbc 5
6 babbcb 6
7 babcbb 7
8 bacbbb 8
9 bbabbc 9
10 bbabcb 10
11 bbacbb 11
12 bbbabc 12
13 bbbacb 13
14 bbbbac 14
15 bbbbca 15
16 bbbcab 16
17 bbbcba 17
18 bbcabb 18
19 bbcbab 19
20 bbcbba 20
21 bcabbb 21
22 bcbabb 22
23 bcbbab 23
24 bcbbba 24
25 cabbbb 25
26 cbabbb 26
27 cbbabb 27
28 cbbbab 28
29 cbbbba 29
'abbbbc' ok
'ZZZ'
0
'DCBA'
23
'a quick brown fox jumps over the lazy dog'
436629906477779191275460617121351796379337
Golfsiz sürüm:
''' Determine the rank (lexicographic index) of a permutation
The permutation may contain repeated items
Written by PM 2Ring 2017.04.03
'''
from math import factorial as fac
from itertools import groupby
def lexico_permute_string(s):
''' Generate all permutations of `s` in lexicographic order '''
a = sorted(s)
n = len(a) - 1
while True:
yield ''.join(a)
for j in range(n-1, -1, -1):
if a[j] < a[j + 1]:
break
else:
return
v = a[j]
for k in range(n, j, -1):
if v < a[k]:
break
a[j], a[k] = a[k], a[j]
a[j+1:] = a[j+1:][::-1]
def perm_count(s):
''' Count the total number of permutations of sorted sequence `s` '''
n = fac(len(s))
for _, g in groupby(s):
n //= fac(sum(1 for u in g))
return n
def perm_rank(target, base):
''' Determine the permutation rank of string `target`
given the rank zero permutation string `base`,
i.e., the chars in `base` are in lexicographic order.
'''
if len(target) < 2:
return 0
total = 0
head, newtarget = target[0], target[1:]
for i, c in enumerate(base):
newbase = base[:i] + base[i+1:]
if c == head:
return total + perm_rank(newtarget, newbase)
elif i and c == base[i-1]:
continue
total += perm_count(newbase)
base = 'abcccdde'
print('total number', perm_count(base))
for i, s in enumerate(lexico_permute_string(base)):
rank = perm_rank(s, base)
assert rank == i, (i, s, rank)
#print('{:2} {} {:2}'.format(i, s, rank))
print('ok')
hakkında lexico_permute_string
Narayana Pandita nedeniyle bu algoritma
https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order adresinden alınmıştır.
Sözcük dağınık diziliminde bir sonraki permütasyonu üretmek a
- A [j] <a [j + 1] olacak şekilde en büyük j dizinini bulun. Böyle bir dizin yoksa, permütasyon son permütasyon olur.
- A [j] <a [k] olacak şekilde j'den büyük en büyük k dizinini bulun.
- A [j] 'nın değerini [k] ile değiştirin.
- [J + 1] 'den son eleman a [n]' ya kadar olan diziyi tersine çevirin.
FWIW, burada bu işlevin açıklamalı bir sürümünü görebilirsiniz .
FWIW, işte ters fonksiyon.
def perm_unrank(rank, base, head=''):
''' Determine the permutation with given rank of the
rank zero permutation string `base`.
'''
if len(base) < 2:
return head + ''.join(base)
total = 0
for i, c in enumerate(base):
if i < 1 or c != base[i-1]:
newbase = base[:i] + base[i+1:]
newtotal = total + perm_count(newbase)
if newtotal > rank:
return perm_unrank(rank - total, newbase, head + c)
total = newtotal
# Test
target = 'a quick brown fox jumps over the lazy dog'
base = ''.join(sorted(target))
rank = perm_rank(target, base)
print(target)
print(base)
print(rank)
print(perm_unrank(rank, base))
çıktı
a quick brown fox jumps over the lazy dog
aabcdeefghijklmnoooopqrrstuuvwxyz
436629906477779191275460617121351796379337
a quick brown fox jumps over the lazy dog
Ve işte geliştirirken yazdığım perm_unrank
ve alt hesapların dökümünü gösteren bir işlev .
def counts(base):
for i, c in enumerate(base):
newbase = base[:i] + base[i+1:]
if newbase and (i < 1 or c != base[i-1]):
yield c, perm_count(newbase)
for h, k in counts(newbase):
yield c + h, k
def show_counts(base):
TAB = ' ' * 4
for s, t in counts(base):
d = len(s) - 1
print('{}{} {}'.format(TAB * d, s, t))
# Test
base = 'abccc'
print('total number', perm_count(base))
show_counts(base)
çıktı
a 4
ab 1
abc 1
abcc 1
ac 3
acb 1
acbc 1
acc 2
accb 1
accc 1
b 4
ba 1
bac 1
bacc 1
bc 3
bca 1
bcac 1
bcc 2
bcca 1
bccc 1
c 12
ca 3
cab 1
cabc 1
cac 2
cacb 1
cacc 1
cb 3
cba 1
cbac 1
cbc 2
cbca 1
cbcc 1
cc 6
cca 2
ccab 1
ccac 1
ccb 2
ccba 1
ccbc 1
ccc 2
ccca 1
cccb 1