Sana N. Permütasyon veriyorum, sen bana N veriyorsun


20

Girdi: karakterlerinin çoklu kümesinin N inci * sözlükbilimsel permütasyonu olan bir büyük harf dizisi (ASCII [65; 90])

* permütasyonlar 0 veya 1'den yukarı doğru numaralandırılmıştır

Çıkış: baz-10 tamsayı N


Rulez

  • Çiftleri da olabilir (bu sayı nasıl bu meydan farklılık bu bir )
  • Karakterler ASCII değerlerine göre sıralanmıştır
  • 1'den küçük veya 1'e eşit uzunlukta bir giriş olması durumunda, girdi ilk permütasyon ve sonuç sırasıyla 0veya1
  • İlk permütasyon, en soldaki karakterin en düşük değere, en sağdaki karakterin en yüksek değere sahip olması ve ilk ve son karakter arasındaki karakter dizisinin, karakterlerinin çoklu kümesinin ilk permütasyonudur (özyinelemeli tanım!)
  • En kısa giriş kazanır

Misal

  • Girdi AABçıktı üretir0
  • Girdi ABAçıktı üretir1
  • Girdi BAAçıktı üretir2

  • Girdi ZZZçıktı üretir0
  • Girdi DCBAçıktı üretir23

DÜZENLE

Tüm permütasyonları üretmeyen ve daha sonra girdiyi arayan bir çözüm bulabilen kişiye ekstra kudos. Bu biraz zor.


Merhaba ve siteye hoş geldiniz. Bu soru şu anda belirsizdir. Permütasyonların nasıl sıralandığından gerçekten emin değilim. Sözlükbilimsel olarak düzenlenmişler mi? Bu, sorunuzda tanımlanmalıdır.
Buğday Sihirbazı

1
Ana sitemize göndermeden önce bu tür geri bildirimler alabilmeniz için bir korumalı alanımız da var. Önce orada yayınlamak zorunlu değildir, ancak birçok kez çok yararlıdır.
Buğday Sihirbazı

'Büyük harf' dediniz zzzve dcbabüyük harf değil.
Matthew Roh

@SIGSEGV düzeltildi
kyrill

Çıktı endeksi 0 tabanlı yerine 1 tabanlı olabilir mi?
Luis Mendo

Yanıtlar:




4

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

  1. 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.
  2. A [j] <a [k] olacak şekilde j'den büyük en büyük k dizinini bulun.
  3. A [j] 'nın değerini [k] ile değiştirin.
  4. [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_unrankve 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

Vaov! İnanılmaz bir çözüm! Python'un bazı parçaları var, şimdi tam olarak anlamak için yukarı bakmam gerekeceğine aşina değilim. Aferin!
David Conrad

Sen bunu değiştirebilir z=0ve yedek t[0]ve t[1:]onlar (şu anda kullanılan nereye hve t) 8 bayt kaydedin.
David Conrad

Tebrikler, ekstra kudos da aldın! Jörg Hülsermann ilk olmasına rağmen, sürümünüz yinelemeli, bu yüzden onunkiyle aynı değil.
kyrill

Teşekkürler, @kyrill Şimdi ters işlemi verimli bir şekilde nasıl yapacağımı merak ediyorum: permütasyonun indeksinden üretilmesi. Sanırım tekrarlar olmadan permütasyonlar için kullanılan normal faktöriyel tekniği değiştirmek çok zor olmamalı ...
PM 2Ring

1
İşte bulabileceğim en kısa şey. True1 veya daha düşük değerler için geri döner , ancak kodunuzla birlikte iyi olması gerektiğini düşünüyorum? f=lambda n:n<2or n*f(n-1)
ArBo


3

05AB1E , 5 bayt

œê¹Sk

Çevrimiçi deneyin!

Adnan'ın cevabından bağımsız olarak keşfedildi.


Seni 42 saniyede dövdü: D
kyrill

@kyrill Hala yanlış yanıtı kabul etmenize rağmen, Jelly cevabımla onu 5 dakika geçirdim.
Erik the Outgolfer

Ancak bu Jelly 1 endeksli çıktı üretir. Kurallar, permütasyonların 0'dan yukarı doğru numaralandığını belirtir. Bunu açıkça isteyen Luis Mendo'ya bir istisna verdim.
kyrill


6
Evet, belirli kullanıcılara istisnalar uygulamaktan hoşlanmıyor.
Erik the Outgolfer

3

PHP, 124 Bayt

$a=str_split($argn);sort($a);for($i=$j=join($a);$i<=strrev($j);$i++)$i==$argn?print+$n:(($c=count_chars)($i)!=$c($j)?:$n++);

PHP, 136 Bayt

foreach($t=($c=count_chars)($argn)as$k=>$v)$i=$s.=str_repeat(chr($k),$v);for(;$i<=strrev($s);$i++)$i==$argn?print+$n:($c($i)!=$t?:$n++);

Çevrimiçi sürüm

Şununla koş:

echo '<string>' | php -nR '<code>'

Faktöriyeli hesapla

Genişletilmiş Sürüm

function f($i){return array_product(range(1,$i));} #factorial
function p($s){
return f(strlen($s))/array_product(array_map("f",count_chars($s,1)));
} # factorial / divide through product of factorials for each char
$a=$argn;
$b="";
$r=[];
for($d=0;$a;$d++) # loop range before used chars in string 
{
    for($i=0;$i<strlen($a);$i++){ # loop for every char in the rest string 
        if($a[$i]<$a[0]) # if char is before first char order by ascii
        $r[$d.$a[$i]]=p(substr_replace($a,"",$i,1)); # add range before
    }
    $a=substr($a,1); # remove first char
}
echo array_sum($r); # Output the range before the used permutation

PPCG dizesi için çıktı

Array
(
    [0C] => 3    # in first run C is before P startposition = 3
    [0G] => 3    # in first run G is before P startposition = 3+3
    [1C] => 2    # in second run PC is before PP startposition = 3+3+2
    [1G] => 2    # in second run PG is before PP startposition = 3+3+2+2=8
)

Çevrimiçi sürüm


Bu ne tür bir büyü? Orijinal yaklaşım için bonus puanlarınız var, ancak yine de tüm permütasyonları üretiyorsunuz ve daha sonra girdi arayınız.
kyrill

@kyrill PHP dizeleri artırabilir php.net/manual/tr/dillan.operators.increment.php Mantık girdi aramaz. Girdi ile bir karşılaştırma
Jörg Hülsermann

@kyrill için 5 bayt daha print+$n´ with ´die("$n")´ and the loop will stop after the permutation is found. And I must add döngüde $ n = 0` yerine ben tamsayıya döküm bu değişiklik değil çalışır
Jörg Hülsermann

1
PHP okumuyorum ama genişletilmiş algoritmanızın benimkine oldukça benzediğini düşünüyorum. FWIW, cevabımı yazana kadar fark etmedim.
PM 2Ring

1
@ PM2Ring Gerçekten python sürümünüzü
okuyamadım

3

Julia, 121125 bayt

Yinelenen harfleri doğru bir şekilde ele almadığı için rekabet etmiyor. Bunu başka bir dilden, bir çözümün bir kısmından birkaç yıl önce yaptığım bir Project Euler problemine taşıdım ve ilk 121 baytlık sürümde bir hata vardı, çünkü izin verilen dizenin ve sıralı, kanonik referansın kullanımını aktarmıştım dize.

f(p)=(n=0;z=length(p)-1;s=join(sort(collect(p)));for c in p z-=(n+=factorial(z)*(search(s, c)-1);p=replace(s,c,"",1);1)end;n)

Büyük girdiler için, bu sürüm 8 baytlık maliyetle bignum kullanır:

f(p)=(n=0;z=BigInt(length(p)-1);s=join(sort(collect(p)));for c in p z-=(n+=factorial(z)*(search(s, c)-1);p=replace(s,c,"",1);1)end;n)

Ungolfed:

function f(perm)
    nth = 0
    size = length(perm) - 1
    sorted = join(sort(collect(p)))
    for ch in sorted
        index = search(perm, ch) - 1
        nth += factorial(size) * index
        perm = replace(perm, ch, "" , 1) # replace at most one copy
        size -= 1
    end
    return nth
end

Bir factoriadic sayı sistemi kullanır , qv Böylece, tüm permütasyonları üretmez ve büyük girdiler için olanlardan çok daha hızlı çalışır.

Örneğin, alfabeye "Kuvars glif işi vex'd cwm finks." Bu cümle, alfabedeki harflerin 259.985,607,122,410,643,097,474,123rd. Sözlükbilimsel permütasyonudur. (Yaklaşık 260 septillionth permütasyon.) Bu program makinemde yaklaşık 65 µs'de bulur.

julia> @time f("quartzglyphjobvexdcwmfinks")
  0.000065 seconds (570 allocations: 19.234 KB)
259985607122410643097474122

OP, permütasyonların 1 yerine 0'dan numaralanmasını istediğinden, sayının ... 123 yerine ... 122 ile biteceğini unutmayın.

Julia, 375 bayt

Girintiyi okunabilirlik için bıraktım, ancak bayt sayısı onsuz.

p(t,b="")=begin
    l=length
    if l(t)<2 return 0 end
    f=factorial
    g(w)=(a=[];
        while w!=""
            s=""
            for c in w if c==w[1] s="$s$c" end end
            w=replace(w,s,"")
            push!(a,s)
        end;a)
    b=b>""?b:join(sort(collect(t)))
    z=0
    for(i,c) in enumerate(b)
        w=b[1:i-1]*b[i+1:end]
        if c==t[1] return z+p(t[2:end],w)
        elseif i>1&&c==b[i-1] continue end
        n=f(l(w))
        for v in g(w) n=div(n,f(l(v))) end
        z+=n
    end
    z
end

Bu, PM 2Ring'in parlak Python çözümünün düz bir Julia limanıdır. Acıktım, bu yüzden her şeyden önce çerezi istediğime karar verdim. İki dil arasındaki benzerlikleri ve farklılıkları görmek ilginçtir. itertools.groupby(Sınırlı bir biçimde) olarak uyguladım g(w).

Ama mantık benim değil, o yüzden git ve PM 2Ring'in cevabını oyla .

P ("QUARTZGLYPHJOBVEXDCWMFINKS") gibi büyük girişleri işleyebilmek istiyorsanız f=factorialile değiştirin f(x)=factorial(BigInt(x)).


Mükemmel. Kurabiyeyi sen al! Sadece ungolfed sürümünde değişken isimlerini düzeltin.
kyrill

1
Aslında çerezimi geri istiyorum. Programınız BAA- beklenen 2, gerçek için yanlış sonuç döndürüyor 3.
kyrill

@kyrill Ah, görünüşe göre kopyaları yanlış anladım. Bu durumda, tüm permütasyonları üretmekten kaçınacak bir yol görebileceğimden emin değilim.
David Conrad

FWIW, cevabım benzer bir şey yapıyor, ancak tekrarlanan karakterlere sahip giriş dizeleri için.
PM 2Ring

3

MATL , 13 12 11 bayt

GB sayesinde 1 bayt kaydedildi !

tY@Xu=!Af1)

Çıktı 1 tabanlıdır.

Çevrimiçi deneyin! Veya tüm test senaryolarını doğrulayın .

açıklama

t      % Input string implicitly. Duplicate
Y@     % All permutations, sorted; each in a different row
Xu     % Unique rows
=      % Compare for equality, with broadcast
!      % Transpose
A      % All: true for columns that contain all entries true
f      % Find: indices of nonzero elements
1)     % Get first of those indices. Implicitly display

Şimdi sadece qhakkı kaldıracak mısın?
kyrill

@kyrill Tam olarak :-)
Luis Mendo

1
Peki ya S? Gerçekten permütasyondan önce sıralamanız gerekiyor mu?
GB

@GB İyi bir noktaya gerek yok! "Tüm permütasyonlar" fonksiyonunun indekslere değil, değerlere göre sıralandığını unuttum. Teşekkürler!
Luis Mendo

2

Mathematica, 33 31 bayt

Sorun spesifikasyonunun değiştirilmesi 2 baytlık tasarruflara izin verdi.

Permutations@Sort@#~Position~#&

Listeyi girdi olarak Nalan ve formda negatif olmayan bir tam sayı döndüren saf işlev {{N}}.


1
Bırakabilirsiniz -1.
Martin Ender

@MartinEnder Başlangıçta permütasyonların 0'dan indekslenmesi şartı vardı.
kyrill

@kyrill Evet, ancak kaldırdınız, böylece Greg bu iki baytı kaydedebilir.
Martin Ender

2

JavaScript (ES6), 130 bayt

s=>(o=new Set,p=(s,q)=>s.map((t,i)=>p(t=[...s],q.concat(t.splice(i,1))))[0]||o.add(q.join``))([...s],[])&&[...o].sort().indexOf(s)

Daha az golf

s=>{
  o = new Set; // use a set to avoid duplicates

  // recursive function to build all permutations (no cookie for me)
  p = (s, q) => 
  {
    y = s.map( (t,i) => // execute for each char at position i
          (
             t = [...s], // using t as local variable, store a copy of s
             x = t.splice(i,1), // remove char from t in position i, and store in array x
             p(t, q.concat(x)) // recursive call
          ));
    if (!y[0]) // if s was empty, I have a new permutation in q
      o.add( q.join('')) // convert to string and add to output set 
  }
  // call p to enumerate all permutations
  p( [...s], [] ) // convert string s to array, q starts empty

  o = [...o].sort() // get elements of o and sort lexicographically 
  return o.indexOf(s) // return the position of the input in o
}

Ölçek

F=
s=>(o=new Set,p=(s,q)=>s.map((t,i)=>p(t=[...s],q.concat(t.splice(i,1))))[0]||o.add(q.join``))([...s],[])&&[...o].sort().indexOf(s)

function update() {
  O.textContent = F(I.value)
}

update()
<input id=I value='DCBA' oninput='update()'>
<pre id=O></pre>


Bir çerez almazsınız, ancak permütasyon oluşturmak için kendi işlevinizi uygulamak için ekstra kredi alırsınız ;-)
kyrill



1

Scala, 40 bayt

s=>s.permutations.toSeq.sorted indexOf s

Kullanmak için bu işlevi bir değişkene atayın:

val f:(String=>Int)=s=>s.permutations.toSeq.sorted indexOf s
println(f("BAA"))

İdeone'de çevrimiçi deneyin

Ne yazık ki, permutationsbir sortedyöntemi olmayan bir yineleyici döndürür , bu yüzden birSeq


1

C ++, 96 bayt

Burada standart kütüphaneyi tam olarak kullanabiliriz. Harflerin listesi, standart C ++ stilinde başlangıç ​​/ bitiş yineleyicileri olarak geçirilir.

#include<algorithm>
int f(char*a,char*z){int i=0;while(std::prev_permutation(a,z))++i;return i;}

Tüm permütasyonları üretmeye ihtiyacımız yok - bir permütasyondan selefine bir dönüşümümüz olduğundan, sadece sıfırıncı değere ulaşmak için kaç iterasyon olduğunu sayarız.

Test programı:

#include<cstring>
#include<iostream>
int main(int argc, char **argv)
{
    while (*++argv)
        std::cout << *argv << ": "
                  << f(*argv, *argv+std::strlen(*argv)) << std::endl;
}

Test sonuçları:

BAA: 0
BAA: 1
BAA: 2
ZZZ: 0
DCBA: 23
: 0

Bu özgün bir yaklaşım. Size de ekstra kudos!
kyrill


0

Yakut, 50 bayt

Bunun daha kısa olmasını bekliyordum. Ben eklendi olmazdı sortdokümanlar söylemedi eğer "uygulama permütasyon vermiştir sırayla hakkında hiçbir garanti vermez."

->x{x.chars.permutation.map{|v|v*""}.sort.index x}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.