난 당신에게 N 번째 순열을 줘


20

입력 : 일련의 대문자 (ASCII [65; 90]) 로, 문자의 다중 집합에 대한 N 번째 * 사전 표기 순열

* 순열은 0 또는 1부터 번호가 매겨집니다.

출력 : 밑이 10 인 정수 N


룰즈

  • 중복이있을 수 있습니다 (즉, 얼마나에서이 문제를 다릅니다 이 하나 )
  • 문자는 ASCII 값으로 정렬됩니다
  • 1보다 작거나 동일한 길이의 입력의 경우, 입력은 제 순열이고 결과는 0또는 1각각
  • 첫 번째 순열은 가장 왼쪽 문자의 값이 가장 낮고 맨 오른쪽 문자의 값이 가장 높으며 첫 문자와 마지막 문자 사이의 문자 시퀀스는 해당 문자의 다중 집합에 대한 첫 번째 순열입니다 (재귀 정의!).
  • 최단 참가작

  • 입력 AAB은 출력을 생성0
  • 입력 ABA은 출력을 생성1
  • 입력 BAA은 출력을 생성2

  • 입력 ZZZ은 출력을 생성0
  • 입력 DCBA은 출력을 생성23

편집하다

모든 순열을 생성하지 않고 입력을 검색하는 솔루션을 제안 할 수있는 사람에게 추가로 도움이됩니다. 그것은 약간의 도전입니다.


안녕하세요. 사이트에 오신 것을 환영합니다. 이 질문은 현재 다소 불분명합니다. 순열이 어떻게 정렬되는지 잘 모르겠습니다. 그것들은 사전 식으로 주문됩니까? 이것은 귀하의 질문에 정의되어야합니다.
위트 마법사

1
또한 기본 사이트에 게시하기 전에 이러한 종류의 피드백을 얻을 수 있도록 샌드 박스가 있습니다. 먼저 게시해야하는 것은 아니지만 많은 경우 매우 유용합니다.
밀 마법사

'대문자'라고 말 zzz했으며 dcba대문자가 아닙니다.
Matthew Roh

@SIGSEGV 수정
kyrill

출력 색인이 0 기반이 아닌 1 기반 일 수 있습니까?
Luis Mendo

답변:




4

Python, 302 287 바이트

Dead Possum은 이미 짧은 Pythonic 솔루션을 게시 했으므로 추가 정보를 얻기로 결정했습니다. 이 솔루션은 모든 순열을 생성 하지는 않습니다 . 다소 큰 문자열의 순열 인덱스를 빠르게 계산할 수 있습니다. 또한 빈 문자열을 올바르게 처리합니다.

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

테스트 코드 :

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)

산출

 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

골퍼가 아닌 버전 :

''' 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')

lexico_permute_string

Narayana Pandita로 인한이 알고리즘은 https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order 에서 온 것입니다.

사전 식 순서의 다음 순열을 생성하려면 a

  1. a [j] <a [j + 1]가되도록 가장 큰 지수 j를 찾으십시오. 이러한 인덱스가 없으면 순열이 마지막 순열입니다.
  2. a [j] <a [k]가되도록 j보다 큰 가장 큰 인덱스 k를 찾으십시오.
  3. a [j]의 값을 a [k]의 값으로 바꾸십시오.
  4. 시퀀스를 a [j + 1]에서 최종 요소 a [n]까지 포함시킵니다.

FWIW, 여기 에 해당 기능의 주석이 달린 버전이 있습니다 .


FWIW, 여기에 역함수가 있습니다.

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))

산출

a quick brown fox jumps over the lazy dog
        aabcdeefghijklmnoooopqrrstuuvwxyz
436629906477779191275460617121351796379337
a quick brown fox jumps over the lazy dog

그리고 여기에 제가 개발 perm_unrank하는 동안 작성한 함수 는 하위 카운트의 고장을 보여줍니다.

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)

산출

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

와우! 놀라운 해결책! 여기에 익숙하지 않은 Python의 일부가 있습니다.이를 완전히 이해하려면 지금 찾아야합니다. 잘 했어!
David Conrad

당신은 그것을 바꿀 수 z=0와의 대체 t[0]t[1:]그들이 (현재 사용중인 곳 ht) 8 바이트를 저장합니다.
David Conrad

축하합니다, 당신은 여분의 kudos도 얻습니다! Jörg Hülsermann이 처음 이었지만 버전은 재귀 적이므로 그의 버전과 동일하지 않습니다.
kyrill

감사합니다, @kyrill 이제 역 프로세스를 효율적으로 수행하는 방법이 궁금합니다. 인덱스에서 순열을 생성하십시오. 반복없이 순열에 사용되는 일반적인 요인 기반 기술 을 수정하는 것이 너무 어렵지 않아야한다고 생각합니다 .
PM 2Ring

1
여기에 내가 가장 짧은 것이 있습니다. True1 이하의 값을 반환 하지만 코드가 괜찮을 것이라고 생각합니까? f=lambda n:n<2or n*f(n-1)
ArBo


3

05AB1E , 5 바이트

œê¹Sk

온라인으로 사용해보십시오!

Adnan의 답변에서 독립적으로 발견되었습니다.


그는 당신을 42 초 이겼습니다 : D
kyrill

@kyrill 당신은 여전히 ​​오답을 받아 들였지만 5 분 내 젤리 답변으로 그를 이겼습니다.
Outgolfer Erik

그러나 Jelly는 1 인덱스 출력을 생성합니다. 규칙에 따르면 순열은 0에서 위로 번호가 매겨집니다. 나는 그것을 요구 한 Luis Mendo에게 예외를 주었다.
kyrill


6
예, 특정 사용자에게 예외를주는 것은 눈살을 찌푸리게합니다.
Outgolfer Erik

3

PHP, 124 바이트

$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 바이트

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++);

온라인 버전

로 실행

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

계승으로 계산

확장 버전

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에 대한 출력

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
)

온라인 버전


이것은 어떤 종류의 마술입니까? 원래 접근 방식에 대한 보너스 포인트가 있지만 여전히 모든 순열을 생성 한 다음 입력을 검색합니다.
kyrill

@kyrill PHP는 문자열을 증가시킬 수 있습니다. php.net/manual/en/language.operators.increment.php 논리는 입력을 검색하지 않습니다. 입력과의 비교
Jörg Hülsermann

@kyrill 5 바이트에 대한 더 많은 나는 대체 할 수있는 print+$n´ with ´die("$n")´ and the loop will stop after the permutation is found. And I must add 다음 정수로 캐스팅이 변경하지 작동 루프에 $ N = 0`을
요 르그 Hülsermann

1
나는 PHP를 읽지 않지만 확장 된 알고리즘은 내 것과 상당히 유사하다고 생각합니다. FWIW, 나는 대답을 썼을 때까지 그것을 알지 못했습니다.
PM 2Ring

1
PM2Ring @이되고 난 정말 당신의 파이썬 버전 읽을 수 없습니다 수
요 르그 Hülsermann

3

줄리아, 121125 바이트

중복 문자를 올바르게 처리하지 않기 때문에 비경쟁. 솔루션의 일부에서 몇 년 전에 Project Euler 문제에 이르기까지 다른 언어로 포팅했으며 첫 번째 121 바이트 버전은 순열 문자열과 정렬 된 표준 참조의 사용을 바꿨 기 때문에 버그가있었습니다. 끈.

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)

큰 입력의 경우이 버전은 8 바이트의 추가 비용으로 큰 숫자를 사용합니다.

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)

언 골프 드 :

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

팩토리 아 숫자 시스템 , qv를 사용하므로 모든 순열을 생성하지는 않으며 큰 입력의 경우 큰 입력이 실행하는 것보다 훨씬 빠르게 실행됩니다.

예를 들어, 알파벳은 "Quartz glyph job vex'd cwm finks"라고 생각되는 문장으로 치환 될 수 있습니다. 그 문장은 알파벳 글자의 259,985,607,122,410,643,097,474,123 사전 사전 순열입니다. (약 260 septillionth 순열)이 프로그램은 내 컴퓨터에서 약 65 µs에 있음을 발견합니다.

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

OP는 순열이 1이 아닌 0부터 시작하도록 요청했기 때문에 ... 123이 아니라 ... 122로 끝납니다.

줄리아, 375 바이트

가독성을 위해 들여 쓰기를 남겨 두었지만 바이트 수는 없습니다.

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

이것은 PM 2Ring의 훌륭한 Python 솔루션의 단순한 Julia 포트입니다. 배가 고파서 쿠키를 먹고 싶다고 결심했습니다. 두 언어의 유사점과 차이점을 보는 것은 흥미 롭습니다. 나는 itertools.groupby(제한된 형식으로)로 구현 했습니다 g(w).

그러나 논리는 내 것이 아니므로 PM 2Ring의 답변을 찬성하십시오 .

교체 f=factorialf(x)=factorial(BigInt(x))는 P ( "QUARTZGLYPHJOBVEXDCWMFINKS")와 같은 큰 입력을 처리 할 수 있어야합니다.


우수한. 쿠키를 얻습니다! ungolfed 버전에서 변수 이름을 수정하십시오.
kyrill

1
사실 나는 쿠키를 다시 원한다. 프로그램이 실제 BAA예상 결과에 대해 잘못된 결과를 반환합니다 . 23
kyrill

@ kyrill Ah, 중복을 오해 한 것 같습니다. 이 경우 모든 순열을 생성하지 않는 방법을 알 수 없습니다.
David Conrad

FWIW, 내 대답 은 비슷한 일을하지만 반복되는 문자가있는 입력 문자열에 대해서도 마찬가지입니다.
PM 2Ring

3

MATL , 13 12 11 바이트

GB 덕분에 1 바이트 절약 !

tY@Xu=!Af1)

출력은 1 기반입니다.

온라인으로 사용해보십시오! 또는 모든 테스트 사례를 확인하십시오 .

설명

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

이제 q오른쪽을 제거 하시겠습니까?
kyrill

@kyrill 정확히 :-)
Luis Mendo

1
그리고 S는 어떻습니까? 순열 전에 정렬해야합니까?
GB

@ GB 좋은 지적, 필요하지 않습니다! "모든 순열"함수는 인덱스가 아닌 값을 기준으로 정렬하는 것을 잊었습니다. 감사!
Luis Mendo

2

Mathematica, 33 31 바이트

문제점 스펙을 변경하면 2 바이트를 절약 할 수있었습니다.

Permutations@Sort@#~Position~#&

순수 함수는 목록을 입력으로 취하고 음이 아닌 정수 N형식으로 리턴합니다 {{N}}.


1
을 (를) 삭제할 수 있습니다 -1.
Martin Ender

@MartinEnder 원래 순열은 0부터 색인되어야한다는 요구 사항이있었습니다.
kyrill

@kyrill 예, 그러나 제거했기 때문에 Greg는이 두 바이트를 저장할 수 있습니다.
Martin Ender

2

자바 스크립트 (ES6), 130 바이트

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)

덜 골프

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
}

테스트

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>


글쎄, 쿠키는 얻지
못하지만



1

스칼라, 40 바이트

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

이를 사용하려면이 함수를 변수에 지정하십시오.

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

온라인으로 사용해보십시오

불행히도 메서드 permutations가없는 반복자를 반환 sorted하므로 a로 변환해야합니다.Seq


1

C ++, 96 바이트

표준 라이브러리를 최대한 활용할 수 있습니다. 문자 목록은 표준 C ++ 스타일의 시작 / 종료 반복자로 전달됩니다.

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

모든 순열을 생성 할 필요는 없습니다. 하나의 순열에서 이전의 순열로의 변환이 있으므로 0 값에 도달하는 데 걸리는 반복 횟수를 계산합니다.

테스트 프로그램 :

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

시험 결과:

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

그것은 원래의 접근법입니다. 당신에게 여분의 kudos도!
kyrill


0

루비, 50 바이트

나는 이것이 더 짧을 것으로 예상했다. sort문서에서 "구현이 순열의 순서에 대해 보장하지 않는다"고 말하지 않았다면 추가 하지 않았을 것입니다.

->x{x.chars.permutation.map{|v|v*""}.sort.index x}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.