我给你第N个排列,你给我N


20

输入:一个大写字母序列(ASCII [65; 90]),是其字符多组的N *个字典编排

*排列从0或1向上编号

输出:以10为底的整数N


鲁兹

  • 有可能是重复的 (这就是如何从这一挑战不同 这一个
  • 字符按其ASCII值排序
  • 如果输入的长度小于或等于1,则输入是第一个排列,结果分别是01
  • 第一个置换是最左边的字符具有最低的值,最右边的字符具有最高的值,并且第一个字符与最后一个字符之间的字符序列是其字符多集的第一个置换(递归定义!)
  • 进入时间最短的获胜者

  • 输入AAB产生输出0
  • 输入ABA产生输出1
  • 输入BAA产生输出2

  • 输入ZZZ产生输出0
  • 输入DCBA产生输出23

编辑

那些想出一个不会产生所有排列并随后寻找输入的解决方案的人特别感谢。那是一个挑战。


您好,欢迎来到该网站。这个问题目前还不清楚。我不太确定排列的顺序。他们按字典顺序排序吗?这应该在您的问题中定义。
小麦巫师

1
我们还有一个沙箱,因此您可以在将其发布到我们的主站点之前获得这种反馈。并非必须先在此发布,但很多时候非常有帮助。
小麦巫师

您说的是“大写”,zzzdcba不是大写。
马修·罗

@SIGSEGV已更正
kyrill

输出索引可以从1开始而不是从0开始吗?
路易斯·门多

Answers:




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. 找到最大索引j,使a [j] <a [j + 1]。如果不存在这样的索引,则该排列为最后的排列。
  2. 找到大于j的最大索引k,以使a [j] <a [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:]使用它们的位置(当前ht)以节省8个字节。
戴维·康拉德

恭喜,您也获得了额外的荣誉!即使JörgHülsermann是第一位,但您的版本是递归的,因此与他的版本不同。
kyrill

谢谢,@ kyrill现在,我想知道如何有效地执行逆向过程:根据其索引产生排列。我猜想修改用于置换的通常基于阶乘的技术应该不会太难了...
PM 2Ring

1
这是我能想到的最短的。它返回True的值等于或小于1,但是我认为您的代码应该没问题吗?f=lambda n:n<2or n*f(n-1)
ArBo


3

05AB1E,5个字节

œê¹Sk

在线尝试!

从Adnan的答案中独立发现。


他以42秒击败了您:D
kyrill

@kyrill尽管您仍然接受了错误的答案,但我在果冻答案上打败了他5分钟。
暴民埃里克(Erik the Outgolfer)'17年

但这果冻产生1索引的输出。规则指出排列从0向上编号。我给路易斯·门多(Luis Mendo)明确要求的例外。
kyrill


6
是的,拒绝为某些用户提供例外。
Egg the Outgolfer

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逻辑不搜索输入。它更是一个对比的输入
约尔格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,直到写完答案后我才注意到。
下午17年

1
@ PM2Ring它可以被我无法真正读懂你的Python版本
约尔格Hülsermann

3

朱莉娅,121个是125字节

不竞争,因为它不能正确处理重复的字母。我从另一种语言移植了该语言,这是我几年前对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)

对于大型输入,此版本使用bignums,但要多花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

请注意,数字以... 122而不是... 123结尾,因为OP要求将排列从0而不是1编号。

朱莉娅,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)

但是逻辑不是我的,所以去支持2Ring的回答

如果您希望能够处理p(“ QUARTZGLYPHJOBVEXDCWMFINKS”)之类的大输入,请替换f=factorialf(x)=factorial(BigInt(x))


优秀。你得到了饼干!只需在未发布版本中修复变量名称即可。
kyrill

1
其实我要我的饼干。对于program BAA预期的2,实际的程序返回错误的结果3
kyrill

@kyrill啊,看来我误解了重复项。在那种情况下,我不确定是否可以找到避免产生所有排列的方法。
大卫·康拉德

FWIW,我的回答做了类似的事情,但是对于具有重复字符的输入字符串。
下午17年

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

@kyrill恰好:-)
路易斯·门多

1
那S呢?您是否真的需要在排列之前对其进行排序?
GB

@GB好点了,不需要!我忘记了“所有排列”功能是基于值而不是基于索引进行排序的。谢谢!
路易斯·门多

2

Mathematica,33 31个字节

更改问题规范可以节省2个字节。

Permutations@Sort@#~Position~#&

以列表为输入并N以形式返回非负整数的纯函数{{N}}


1
您可以删除-1
Martin Ender

@MartinEnder最初要求将排列索引从0开始
。– kyrill

@kyrill是的,但是您将其删除,因此Greg可以保存这两个字节。
Martin Ender

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>


好吧,您没有得到cookie,但是您因实现自己的函数以生成排列而获得额外的荣誉;-)
kyrill 17-4-4



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;}

我们不需要生成所有排列-因为我们已经从一个排列转换到其排列的前身,所以我们只需计算达到零值所需的迭代次数。

测试程序:

#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

那是原始的方法。也给您额外的荣誉!
kyrill


0

Ruby,50个字节

我希望这会更短。sort如果文档未说“该实现不保证排列产生的顺序,我将不会添加”。

->x{x.chars.permutation.map{|v|v*""}.sort.index x}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.