Tôi cho bạn hoán vị Nth, bạn cho tôi N


20

Đầu vào: một chuỗi các chữ cái in hoa (ASCII [65; 90]) là hoán vị từ điển thứ N của nhiều ký tự của nó

* hoán vị được đánh số từ 0 hoặc 1 trở lên

Đầu ra: cơ sở 10 số nguyên N


Quy tắc

  • Có thể có sự trùng lặp (đó là cách thử thách này khác với thử thách này )
  • Các ký tự được sắp xếp theo giá trị ASCII của chúng
  • Trong trường hợp đầu vào có độ dài nhỏ hơn hoặc bằng 1, đầu vào là hoán vị đầu tiên và kết quả là 0hoặc 1tương ứng
  • Hoán vị đầu tiên là trong đó ký tự ngoài cùng bên trái có giá trị thấp nhất, ký tự ngoài cùng bên phải có giá trị cao nhất và chuỗi ký tự giữa ký tự đầu tiên và ký tự cuối cùng là hoán vị đầu tiên của đa nhân vật của nó (định nghĩa đệ quy!)
  • Chiến thắng ngắn nhất

Thí dụ

  • Đầu vào AABtạo ra đầu ra0
  • Đầu vào ABAtạo ra đầu ra1
  • Đầu vào BAAtạo ra đầu ra2

  • Đầu vào ZZZtạo ra đầu ra0
  • Đầu vào DCBAtạo ra đầu ra23

CHỈNH SỬA

Thêm danh tiếng cho người có thể đưa ra giải pháp không tạo ra tất cả các hoán vị và sau đó tìm kiếm đầu vào. Đó là một số thách thức.


Xin Chào và Chào Mừng đến với trang. Câu hỏi này hiện chưa rõ ràng. Tôi không thực sự chắc chắn làm thế nào các hoán vị được ra lệnh. Có phải họ đang theo thứ tự từ vựng? Điều này nên được xác định trong câu hỏi của bạn.
Thuật sĩ lúa mì

1
Chúng tôi cũng có một hộp cát để bạn có thể nhận được loại phản hồi này trước khi đăng lên trang web chính của chúng tôi. Nó không bắt buộc phải đăng ở đó trước, nhưng rất nhiều lần nó rất hữu ích.
Thuật sĩ lúa mì

Bạn đã nói 'Chữ hoa' zzzdcbakhông viết hoa.
Matthew Roh

@SIGSEGV đã sửa
kyrill

Chỉ số đầu ra có thể dựa trên 1 thay vì dựa trên 0 không?
Luis Mendo

Câu trả lời:




4

Python, 302 287 byte

Dead Possum đã đăng một giải pháp Pythonic ngắn, vì vậy tôi quyết định chọn thêm kudos. Giải pháp này không tạo ra tất cả các hoán vị. Nó có thể nhanh chóng tính toán chỉ số hoán vị của một chuỗi khá lớn; nó cũng xử lý một chuỗi rỗng chính xác.

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

Mã kiểm tra:

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)

đầu ra

 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

Phiên bản không chơi gôn:

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

Trong khoảng lexico_permute_string

Thuật toán này, do Narayana Pandita, là từ https://en.wikipedia.org/wiki/Permuting#Generation_in_lexicographic_order

Để tạo ra hoán vị tiếp theo theo thứ tự từ điển a

  1. Tìm chỉ số lớn nhất j sao cho [j] <a [j + 1]. Nếu không có chỉ số như vậy tồn tại, hoán vị là hoán vị cuối cùng.
  2. Tìm chỉ số lớn nhất k lớn hơn j sao cho a [j] <a [k].
  3. Hoán đổi giá trị của [j] với giá trị của [k].
  4. Đảo ngược trình tự từ [j + 1] lên đến và bao gồm phần tử cuối cùng a [n].

FWIW, bạn có thể thấy một phiên bản chú thích của chức năng đó ở đây .


FWIW, đây là hàm nghịch đảo.

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

đầu ra

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

Và đây là một chức năng mà tôi đã viết trong khi phát triển perm_unrankcho thấy sự cố của số lượng con.

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)

đầu ra

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

Ồ Giải pháp tuyệt vời! Có một số bit của Python ở đây Tôi không quen thuộc rằng tôi sẽ phải tìm kiếm ngay bây giờ để hiểu đầy đủ về nó. Làm tốt!
David Conrad

Bạn có thể thay đổi nó thành z=0và thay thế trong t[0]t[1:]nơi chúng được sử dụng (hiện tại ht) để lưu 8 byte.
David Conrad

Xin chúc mừng, bạn cũng nhận được thêm danh tiếng! Mặc dù Jörg Hülsermann là lần đầu tiên, nhưng phiên bản của bạn được đệ quy nên nó không giống với phiên bản của anh ấy.
kyrill

Cảm ơn, @kyrill Bây giờ tôi đang tự hỏi làm thế nào để thực hiện quá trình ngược lại một cách hiệu quả: tạo ra hoán vị từ chỉ mục của nó. Tôi đoán không nên quá khó để sửa đổi kỹ thuật cơ sở giai thừa thông thường được sử dụng cho hoán vị mà không lặp lại ...
PM 2Ring

1
Đây là ngắn nhất tôi có thể đến với. Nó trả về Truegiá trị 1 hoặc thấp hơn, nhưng tôi nghĩ với mã của bạn sẽ ổn chứ? f=lambda n:n<2or n*f(n-1)
ArBo


3

05AB1E , 5 byte

œê¹Sk

Hãy thử trực tuyến!

Độc lập phát hiện ra từ câu trả lời của Adnan.


Anh ta đánh bại bạn sau 42 giây: D
kyrill

@kyrill Mặc dù bạn vẫn chấp nhận câu trả lời sai, tôi đã đánh bại anh ta bằng câu trả lời Jelly của tôi sau 5 phút.
Erik the Outgolfer

Nhưng Jelly đó tạo ra đầu ra 1 chỉ mục. Quy tắc nêu các hoán vị được đánh số từ 0 trở lên. Tôi đã đưa ra một ngoại lệ cho Luis Mendo, người đã yêu cầu rõ ràng về nó.
kyrill


6
Vâng, đưa ra ngoại lệ cho một số người dùng nhất định được tán thành.
Erik the Outgolfer

3

PHP, 124 byte

$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 byte

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

Phiên bản trực tuyến

Chạy với

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

Tính với giai thừa

Phiên bản mở rộng

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

Đầu ra cho chuỗi 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
)

Phiên bản trực tuyến


Đây là loại phép thuật gì? Bạn có điểm thưởng cho cách tiếp cận ban đầu, nhưng bạn vẫn tạo ra tất cả các hoán vị và sau đó tìm kiếm đầu vào.
kyrill

@kyrill PHP có thể tăng chuỗi php.net/manual/en/language.operators.increment.php Logic không tìm kiếm đầu vào. Nó là một sự so sánh với đầu vào
Jörg Hülsermann

@kyrill để có thêm 5 byte Tôi có thể thay thế print+$n´ with ´die("$n")´ and the loop will stop after the permutation is found. And I must add $ n = 0` trong vòng lặp sau đó chuyển sang số nguyên không hoạt động trong thay đổi này
Jörg Hülsermann

1
Tôi không đọc PHP, nhưng tôi nghĩ thuật toán mở rộng của bạn khá giống với thuật toán của tôi. FWIW, tôi đã không nhận thấy điều đó cho đến khi tôi viết câu trả lời của mình.
PM 2Ring

1
@ PM2Ring Có thể tôi không thể đọc phiên bản trăn của bạn thực sự
Jörg Hülsermann

3

Julia, 121 125 byte

Không soạn thảo, vì nó không xử lý các chữ cái trùng lặp chính xác. Tôi đã chuyển ngôn ngữ này từ một ngôn ngữ khác, từ một phần của giải pháp sang vấn đề Project Euler mà tôi đã làm cách đây vài năm và phiên bản 121 byte đầu tiên có lỗi vì tôi đã chuyển đổi việc sử dụng chuỗi được hoán vị và tham chiếu chính tắc được sắp xếp chuỗi.

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)

Đối với đầu vào lớn, phiên bản này sử dụng bignums với chi phí 8 byte thêm:

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)

Ung dung:

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

Sử dụng một hệ thống số factoriadic , qv Do đó, nó không tạo ra tất cả các hoán vị và cho các đầu vào lớn sẽ chạy rất nhanh so với các đầu vào.

Ví dụ, bảng chữ cái có thể được hoán vị vào câu khá giả tạo "Quartz glyph job vex'd cwm finks." Câu đó là 259.985.607.122.410.643.097.474.123rd hoán vị từ vựng của các chữ cái trong bảng chữ cái. (Khoảng 260 lần hoán vị thứ hai.) Chương trình này nhận thấy rằng trong khoảng 65 bản nhạc trên máy của tôi.

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

Lưu ý rằng số kết thúc bằng ... 122 thay vì ... 123 vì OP yêu cầu các hoán vị được đánh số từ 0 thay vì từ 1.

Julia, 375 byte

Tôi đã để lại vết lõm để dễ đọc, nhưng số byte thì không có.

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

Đây chỉ là một cổng Julia trực tiếp của giải pháp Python tuyệt vời của PM 2Ring. Tôi đã đói, vì vậy tôi quyết định tôi muốn cookie. Thật thú vị khi thấy sự tương đồng và sự khác biệt giữa hai ngôn ngữ. Tôi đã thực hiện itertools.groupby(ở dạng hạn chế) nhưg(w) .

Nhưng logic không phải là của tôi, vì vậy hãy đi và nâng cao câu trả lời của PM 2Ring .

Thay thế f=factorialbằng f(x)=factorial(BigInt(x))nếu bạn muốn có thể xử lý các đầu vào lớn như p ("QUARTZGLYPHJOBVEXDCWMFINKS").


Xuất sắc. Bạn nhận được cookie! Chỉ cần sửa tên biến trong phiên bản chưa được chỉnh sửa.
kyrill

1
Thật ra tôi muốn cookie của tôi trở lại. Chương trình của bạn trả về kết quả sai cho BAA- dự kiến 2, thực tế 3.
kyrill

@kyrill Ah, có vẻ như tôi đã hiểu nhầm các bản sao. Trong trường hợp đó, tôi không chắc mình có thể thấy một cách để làm điều đó sẽ tránh tạo ra tất cả các hoán vị.
David Conrad

FWIW, câu trả lời của tôi làm một điều tương tự, nhưng đối với các chuỗi đầu vào có ký tự lặp lại.
PM 2Ring

3

MATL , 13 12 11 byte

Lưu 1 byte nhờ GB !

tY@Xu=!Af1)

Đầu ra là 1 dựa.

Hãy thử trực tuyến! Hoặc xác minh tất cả các trường hợp thử nghiệm .

Giải trình

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

Bây giờ bạn sẽ loại bỏ qđúng không?
kyrill

@kyrill Chính xác :-)
Luis Mendo

1
Còn chữ S thì sao? Bạn có thực sự cần phải sắp xếp nó trước khi hoán vị?
GB

@GB Điểm tốt, không cần thiết! Tôi quên rằng hàm "tất cả hoán vị" sắp xếp dựa trên các giá trị, không phải trên các chỉ số. Cảm ơn!
Luis Mendo

2

Toán học, 33 31 byte

Thay đổi thông số kỹ thuật cho phép tiết kiệm 2 byte.

Permutations@Sort@#~Position~#&

Hàm thuần túy lấy một danh sách làm đầu vào và trả về một số nguyên không âm Ntrong biểu mẫu {{N}}.


1
Bạn có thể thả -1.
Martin Ender

@MartinEnder Ban đầu có một yêu cầu là các hoán vị được lập chỉ mục từ 0.
kyrill

@kyrill Có, nhưng bạn đã xóa nó, vì vậy Greg có thể lưu hai byte đó.
Martin Ender

2

JavaScript (ES6), 130 byte

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)

Ít chơi gôn

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
}

Kiểm tra

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>


Chà, bạn không nhận được cookie, nhưng bạn có thêm tín dụng để thực hiện chức năng của riêng mình để tạo hoán vị ;-)
kyrill



1

Scala, 40 byte

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

Để sử dụng nó, gán hàm này cho một biến:

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

Dùng thử trực tuyến tại ideone

Thật không may, permutationstrả về một iterator, không có sortedphương thức, vì vậy nó phải được chuyển đổi thành mộtSeq


1

C ++, 96 byte

Chúng tôi có thể sử dụng đầy đủ các thư viện tiêu chuẩn ở đây. Danh sách các chữ cái được truyền dưới dạng các vòng lặp start / end theo kiểu C ++ tiêu chuẩn.

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

Chúng ta không cần phải tạo ra tất cả các hoán vị - vì chúng ta có một phép biến đổi từ một hoán vị này sang tiền thân của nó, chúng ta chỉ cần đếm số lần lặp để đạt được giá trị zeroth.

Chương trình kiểm tra:

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

Kết quả kiểm tra:

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

Đó là một cách tiếp cận ban đầu. Thêm kudos cho bạn quá!
kyrill


0

Ruby, 50 byte

Tôi dự kiến ​​điều này sẽ ngắn hơn. Tôi sẽ không thêm vào sortnếu các tài liệu không nói "việc triển khai không đảm bảo về thứ tự mà các hoán vị được mang lại."

->x{x.chars.permutation.map{|v|v*""}.sort.index x}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.