私はあなたにN番目の順列を与えます、あなたは私に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
また、サンドボックスがあるため、メインサイトに投稿する前にこのようなフィードバックを得ることができます。最初にそこに投稿することは必須ではありませんが、多くの場合、非常に役立ちます。
小麦ウィザード

「大文字」と言いましたが、大文字zzzdcbaはありません。
マシュー盧

@SIGSEGV修正
kyrill

出力インデックスを0ベースではなく1ベースにすることはできますか?
ルイスメンドー

回答:




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の一部がありますが、私はそれを完全に理解するために今すぐ調べなければならないことに慣れていません。よくやった!
デビッドコンラッド

これを変更しz=0、使用する場所t[0]t[1:]場所(現在hおよびt)に置き換えて、8バイトを節約できます。
デビッドコンラッド

おめでとうございます、あなたは余分な称賛を得ます!JörgHülsermannが最初でしたが、あなたのバージョンは再帰的であるため、彼とは異なります。
キリル

ありがとう、@ kyrill今、私は逆プロセスを効率的に行う方法を疑問に思っています:インデックスから順列を生成します。私はすべきではないと思いすぎていつも変更するのは難しい階乗ベースの手法を繰り返しなしの順列のために使用さ...
PM 2Ring

1
これが私が思いつくことができる最短です。True1以下の値で返されますが、あなたのコードでは大丈夫だと思いますか?f=lambda n:n<2or n*f(n-1)
ArBo


3

05AB1E、5バイト

œê¹Sk

オンラインでお試しください!

アドナンの答えから独立して発見されました。


彼はあなたを42秒倒しました:D
kyrill

@kyrillあなたはまだ間違った答えを受け入れましたが、私はゼリーの答えで彼を5分倒しました。
エリックアウトゴルファー

しかし、そのJellyは1インデックスの出力を生成します。規則では、順列には0から上に番号が付けられています。私は明示的にそれを求めたルイス・メンドーに例外を与えました。
キリル


6
ええ、特定のユーザーに例外を与えることは嫌われています。
エリックアウトゴルファー

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 PHPは文字列をインクリメントできますphp.net/manual/en/language.operators.increment.phpロジックは入力を検索しません。入力に対する比較です
ヨルグヒュルザーマン

@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私は本当にあなたのpythonバージョンを読むことができなかった可能性があります
ヨルグHülsermann17年

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バイトの追加コストでbignumを使用します。

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の9兆回の置換。)このプログラムは、私のマシンで約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ポートです。お腹がすいたので、結局クッキーが欲しいと思いました。2つの言語の類似点と相違点を見るのは興味深いことです。私はitertools.groupby(限定された形式で)として実装しましたg(w)

しかし、論理は私のものではないので、PM 2Ringの答えに賛成してください

交換するf=factorialf(x)=factorial(BigInt(x))あなたはP(「QUARTZGLYPHJOBVEXDCWMFINKS」)のような大規模な入力を処理できるようにしたい場合。


優れた。クッキーを取得します!ungolfedバージョンの変数名を修正するだけです。
キリル

1
実際に、クッキーを戻したいです。あなたのプログラムは、BAA期待される2、実際のについて間違った結果を返します3
キリル

@kyrillあ、重複を誤解したようです。その場合、すべての順列の生成を回避する方法を見つけることができるかどうかわかりません。
デビッドコンラッド

FWIW、私の答えは同様のことを行いますが、文字が繰り返される入力文字列についてです。
PM 2Ring

3

MATL13 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まさに:-)
ルイスメンドー

1
そして、Sはどうですか?順列の前に本当にソートする必要がありますか?
GB

@GB良い点、それは必要ありません!「すべての順列」関数がインデックスではなく値に基づいてソートすることを忘れていました。ありがとう!
ルイスメンドー

2

Mathematica、33 31バイト

問題の仕様を変更すると、2バイトの節約が可能になりました。

Permutations@Sort@#~Position~#&

入力としてリストを取りN、形式で非負の整数を返す純粋な関数{{N}}


1
をドロップできます-1
マーティンエンダー

@MartinEnder元々、順列には0からインデックスを付ける必要がありました
。– kyrill

@kyrillはい、しかしあなたはそれを削除したので、グレッグはそれらの2バイトを保存できます。
マーティンエンダー

2

JavaScript(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

Scala、40バイト

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

それを使用するには、この関数を変数に割り当てます。

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

ideoneでオンラインで試す

残念ながら、メソッドpermutationsを持たないイテレータを返すsortedため、それを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;}

すべての順列を生成する必要はありません。1つの順列からその前の順列への変換があるため、単純にゼロ番目の値に到達するまでの反復回数をカウントします。

テストプログラム:

#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

それは独自のアプローチです。あなたにも特別な称賛を!
キリル


0

ルビー、50バイト

これはもっと短いと思った。sortドキュメントが「実装は置換が生成される順序について保証しない」と言わなかった場合、私は追加しなかったでしょう。

->x{x.chars.permutation.map{|v|v*""}.sort.index x}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.