逆排列索引


17

介绍

具有n个元素的列表的字典排列可以从0到n编号!-1.例如3!= 6个置换(1,2,3)将是(1,2,3)(1,3,2)(2,1,3)(2,3,1)(3,1,2)(3,2,1)

将排列应用于列表时,其元素的排列顺序与排列中的数字相同。例如,将置换(2,3,1)应用于l = (a,b,c)yield (l[2],l[3],l[1]) = (b,c,a)

排列的逆定义为颠倒此操作的排列,即应用排列,然后其逆(反之亦然)不会修改数组。例如,(2,3,1)is 的逆(3,1,2),因为将其应用于(b,c,a)yields (a,b,c)

同样,应用于排列本身的排列的逆值会产生整数1… n。例如,应用(3,1,2)(2,3,1)产率(1,2,3)

现在,我们将函数revindx)定义为索引为x的排列的逆排列的索引。(如果您有兴趣,这是A056019。)

由于与指数置换只修改了最后ķ列表中的项目当且仅当 0≤ < ķ!,我们可以添加任意数量的元素到列表的开始,而不会影响revind)。因此,列表的长度不影响结果。

挑战

您的任务是实现revindx)。您将编写一个完整的程序或函数,以单个非负整数x作为输入/参数,并以单个非负整数输出/返回结果。

输入和输出可以是0索引或1索引,但是它们之间必须保持一致。

禁止按索引生成排列,返回排列的索引或找到逆排列的内建函数。(允许生成所有排列或下一个排列的构建体。)

适用标准规则。

例子

下面的示例是0索引的。

Input    Output
0        0
1        1
2        2
3        4
4        3
5        5
6        6
13       10
42       51
100      41
1000     3628
2000     3974
10000    30593
100000   303016

参考实现(Python 3)

def revind(n):
    from math import factorial
    from itertools import permutations, count
    l = next(filter(lambda x: factorial(x) > n, count(1)))
    pms = list(permutations(range(l)))
    return [k for k in range(len(pms)) if tuple(pms[n][i] for i in pms[k]) == pms[0]][0]


1
为了理解这一挑战,我必须查找逆排列的定义。我发现您的例子(a,b,c)非常不清楚。请适当说明反向排列是什么。
致命

@Fatalize这很难简单地解释。现在好点了?
PurkkaKoodari '16

果冻有一个原子(升序),该原子按数组的相应值对数组的索引进行排序。这恰好颠倒的排列1,...,N,但它并不适用于其它置换工作。被禁止的内建?
丹尼斯,

@丹尼斯困难的问题。从技术上讲,在将其应用于任何严格增加的列表后,它会找到所有排列的逆。因此,我要说不允许。(如果有人严格不同意,请随时发表评论。如果社区愿意,我可以更改此设置。)
PurkkaKoodari

Answers:


5

果冻,6 个字节

ịŒ!⁺iR

I / O使用基于1的索引。非常慢且需要大量内存。

验证

只要输入不超过8!= 40320,考虑数组[1,…,8]的所有排列就足够了。对于最后一个测试用例,[1,…,9]的排列就足够了。

使用仅考虑前89个正整数的排列的稍微修改的代码,您可以在线尝试!验证所有剩余的测试用例

怎么运行的

ịŒ!⁺iR  Main link. Argument: n

 Œ!     Yield all permutations of [1, ..., n].
ị       At-index; retrieve the n-th permutation.
   ⁺    Duplicate the Œ! atom, generating all permutations of the n-th permutation.
     R  Range; yield [1, ..., n].
    i   Index; find the index of [1, ..., n] in the generated 2D array.

替代方法,6个字节(无效)

Œ!Ụ€Ụi

它一样长,并且使用了禁止升级的原子,但是(可以说)更加惯用了。

通过添加8(或最后一个测试用例为9),我们实际上可以在线尝试!

怎么运行的

Œ!Ụ€Ụi  Main link. Argument: n

Œ!      Yield all permutations of [1, ..., n].
  Ụ€    Grade up each; sort the indices of each permutation by the corresponding
        values. For a permutation of [1, ..., n], this inverts the permutation.
    Ụ   Grade up; sort [1, ..., n!] by the corresponding inverted permutations
        (lexicographical order).
     i  Index; yield the 1-based index of n, which corresponds to the inverse of
        the n-th permutation.

6

Mathematica,74个字节

Max@k[i,Flatten@Outer[i=Permutations[j=Range@#];k=Position,{i[[#]]},j,1]]&

使用1索引。效率很低。(当输入为时,使用〜11GB的内存11

说明

j=Range@#

生成一个从1到N的列表。将其存储在中j

i=Permutations[...]

找到的所有排列j。将其存储在中i

k=Position

Position函数存储在中k。(以减少Position再次使用时的字节数)

Flatten@Outer[...,{i[[#]]},j,1]

找到第N个排列的逆排列。

Max@k[i,...]

查找kPosition在逆置换的)i(所有排列)

使用内置的46 43字节

a[(a=Ordering)/@Permutations@Range@#][[#]]&

1个索引。


2
“禁止...找到反向排列的附件”
Greg Martin

@GregMartin,啊,我不知何故错过了那部分,只看到了“返回排列索引”部分。愚蠢的我...新代码没有这个问题。
JungHwan Min

是的,我同意很容易错过。74个字节,仍然令人印象深刻!
格雷格·马丁

5

MATL,15字节

:Y@tGY)Z)G:=!Af

输入和输出基于1。

类似于@MartinEnder的CJam答案,但通过将所有可能的排列与输入指定的排列组合,并查看已成为标识排列来找到逆排列。

它在在线编译器中的内存不足时无法输入10

在线尝试!

说明

:      % Implicitly input N. Push range [1 2 ... N]
Y@     % Matrix witll all permutations of size N. Each permutation is a row
tGY)   % Duplicate. Get the N-th row
Z)     % Use that as a column index into the matrix of all permutations
G:=    % Compare each row with [1 2 ... N]
!Af    % Find index of the row that matches. Implicitly display

5

Pyth,12个字节

xJ.phQxL@JQh

测试套件

0索引。

说明:

xJ.phQxL@JQh
xJ.phQxL@JQhQ    Implicit variable introduction
                 Q = eval(input())
  .phQ           Form all permutations of range(Q+1), namely [0, 1, .. Q]
 J               Save to J.
        @JQ      Take the Qth element of J.
      xL   hQ    Map all elements of [0, 1, ..., Q] to their index in above
x                Find the index in J of the above.

5

05AB1E14 13字节

内存效率很低。现在,内存效率甚至更高(但缩短了1个字节)。
从0开始的范围。
使用CP-1252编码。

ƒ¹ÝœD¹èNkˆ}¯k

在线尝试! 或作为修改后的测试套件

说明

ƒ               # for N in range[0 .. x]
 ¹ÝœD           # generate 2 copies of all permutations of range[0 .. x]
     ¹è         # get permutation at index x
       Nkˆ      # store index of N in that permutation in global list
         }      # end loop
          ¯k    # get index of global list (inverse) in list of permutations

4

CJam,16字节

ri_)e!_@=_$\f#a#

索引从0开始。

在线尝试!

我没有比这低得多的效率了……用Java的默认设置(大于输入)将内存用完8(但在给定的时间和内存数量足够的情况下,原则上适用于任意输入)。

说明

ri    e# Read input and convert to integer N.
_)e!  e# Duplicate N, get all permutations of [0 1 ... N].
_@=   e# Duplicate permutations, get the Nth permutation.
_$    e# Duplicate and sort to get the sorted range [0 1 ... N].
\f#   e# For each of these values, get its index in the Nth permutation.
      e# This inverts the permutation.
a#    e# Find the index of this new permutation in the list of all permutations.

3

GAP,108字节

h:=l->n->PositionProperty(l,p->l[n]*p=());
f:=n->h(Set(SymmetricGroup(First([1..n],k->Factorial(k)>=n))))(n);

1个索引。换行符不计算在内,因此不需要。我真的不必将最终功能分配给一个名称,但是...

h是一个curried函数,它接受排列列表和该列表的索引,然后返回反排列的索引。没有限制,我只会做Position(l,l[n]^-1)f使用足够大的对称组和给定的排序排列来调用该函数n

我可以只编写SymmetricGroup(n),然后可以为最大9的值计算该函数。由于已经有了更小的解决方案,我希望能够做到这一点:

gap> f(100001);
303017

一个真正有效的0索引解决方案,适用于99以下的参数!(并且可以使它用于小于999!的参数!只需花费一个字节)就是这个:

f:=function(n)
 local m,l,p,i,g;
 m:=First([1..99],k->Factorial(k)>n);
 g:=List([m-1,m-2..0],Factorial);
 l:=[1..m];
 p:=[];
 for i in g do
  Add(p,Remove(l,QuoInt(n,i)+1));
  n:=n mod i;
 od;
 return Sum(ListN(List([1..m],i->Number([1..Position(p,i)],j->p[j]>i)),g,\*));
end;

删除空格后,它有255个字节。


干得好!我也希望得到一些有效的解决方案。
PurkkaKoodari '16

3

的JavaScript(ES6),163个 120 110字节

f=(n,a=[],i=0,r=0,[j,...b]=a)=>n?a.splice(n%-~i,0,i)|f(n/++i|0,a,i):i?f(n,b,i-1,b.reduce((r,k)=>r+=k>j,r*i)):r
<input type=number min=0 oninput=o.textContent=f(+this.value)><pre id=o>

0索引。通过将索引转换为排列,将其反转,然后转换回索引来工作。编辑:通过进行f反向和反向排列,然后g将反向排列转换回索引,可以节省大约25%的时间。通过将两个递归调用合并为一个函数,又节省了10个字节。取消高尔夫:

function index(n) {
    var a = [0];
    for (var i = 1; n = Math.floor(n / i); i++) {
        var j = i - n % (i + 1);
        for (var k = 0; k < i; k++) {
            if (a[k] > j) a[k]++;
        }
        a.push(j);
    }
    a = [...a.keys()].map(k => a.indexOf(k));
    while (i) {
        n *= i--;
        j = a.pop();
        for (k = 0; k < i; k++) {
            if (a[k] > j) n++;
        }
    }
    return n;
}

1
@JonathanAllan抱歉,我以为我发现了最后一秒的9字节保存,但是我没有对其进行彻底的测试。我已恢复到以前的版本。
尼尔

现在执行起来非常缓慢。
乔纳森·艾伦

1
@JonathanAllan事实是,如果我能f反转排列顺序而不是g... ,那会变得更加呆滞
Neil

3

J,55 50字节

g=:/:~i.@#
[:(#\.#.+/@(<{.)\.)@g(-i.)@>:g@g@,/@#:]

基于排列索引的J论文。

此代码仅需要大约的内存,n但是会花费更多时间,因为它对列表n时间进行排序并n为每个索引搜索时间。

使用/:能够找到列表等级和排列逆的内建函数,有一个42字节的解决方案效率更高。

[:(#\.#.+/@(<{.)\.)@/:(-i.)@>:/:@/:@,/@#:]

与另一个版本需要105秒相比,此版本仅需要44秒即可计算出最后一个测试用例。

用法

   g =: /:~i.@#
   f =: [:(#\.#.+/@(<{.)\.)@g(-i.)@>:g@g@,/@#:]
   (,.f"0) 0 1 2 3 4 5 6 13 42 100 1000 2000 10000
    0     0
    1     1
    2     2
    3     4
    4     3
    5     5
    6     6
   13    10
   42    51
  100    41
 1000  3628
 2000  3974
10000 30593
   timex 'r =: f 100000'
105.787
   r
303016

+1是高尔夫语言无法触及的记忆效率。
魔术章鱼缸

2

果冻14 13 9 字节

-4字节感谢@Dennis(他golfed进一步使用快速他的回答

Œ!ịịŒ!$iR

另一个非常慢的实现。
这里采用基于1的索引,因此预期结果为:

input:  1 2 3 4 5 6 7 8  9 10 11
output: 1 2 3 5 4 6 7 8 13 19  9

甚至没有建立在线IDE链接也没有意义,因为TIO在输入时被杀死10。本地结果(最后一个非常慢,需要大量内存!):

C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 1
1
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 2
2
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 3
3
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 4
5
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 5
4
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 6
6
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 7
7
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 8
8
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 9
13
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 10
19
C:\Jelly\jelly-master>python jelly -fu D:\jelly_scripts\revPerm.txt 11
9

怎么样?

Œ!ịịŒ!$iR - Main link 1: n
      $   - last two links as a monad
    Œ!    -     permutations of implicit range [1,2,3,...,n]
   ị      -     value at index n (the nth permutation)
Œ!        - permutations of implicit range [1,2,3,...,n]
  ị       - value at index (the indexes of the permuted values in the nth permutation)
       i  - index of
        R - range [1,2,3,...,n]

注意:无需对排列进行排序,因为我们使用相同的顺序来查找排列,并且它是相反的。


无法通过我的手机进行测试,但是您能否摆脱链接2并成为主要链接ÇịịÇ$iR
丹尼斯,

实际上,R之前Œ!是隐式的,所以Œ!ịịŒ!$iR应该做好这项工作。
丹尼斯,

是的,在与朋友见面之前,这是一个非常匆忙的条目。
乔纳森·艾伦

2

Python 2中,116个 114字节

from itertools import*
def f(n):r=range(n+1);l=list(permutations(r));print l.index(tuple(l[n].index(v)for v in r))

代表

从0开始。速度慢,内存不足,但字节不足。


不使用置换功能;既节省内存又节省时间。289285字节

@Christian Sievers,-4个字节(已形成完整排列)

h=lambda n,v=1,x=1:v and(n>=v and h(n,v*x,x+1)or(v,x-1))or n and h(n-1,0,n*x)or x
i=lambda p,j=0,r=0:j<len(p)and i(p,j+1,r+sum(k<p[j]for k in p[j+1:])*h(len(p)-j-1,0))or r
def f(n):t,x=h(n);g=range(x);o=g[:];r=[];exec"t/=x;x-=1;r+=[o.pop(n/t)];n%=t;"*x;return i([r.index(v)for v in g])

我知道这是代码高尔夫球,但我认为@ Pietu1998也对有效的实现感兴趣。

repl.it上查看其运行情况

尽管与参考实现相比,此方法使用的字节数比参考实现要多n=5000000

ref:    6GB 148s  
this: 200KB <1ms

f 是反向索引函数。

f首先获取下一个阶乘以上nt且其阶乘的整数就是x通过调用h(n)和设置g=range(x)的项目,将弥补置换,o=g[:]以及置换持有人,r=[]

接下来,它n通过依次从这些项pop中获取的阶乘基本表示的索引来构造索引处的置换,并将它们附加到。阶乘基本表示由DIV和的模发现与其中由div'd 和递减降到。nornttxx1

最后,它通过调用反向排列来找到反向排列的索引i[r.index(v)for v in g]

h 是双重用途函数,用于计算非负整数的阶乘或计算非负整数上方的下一个阶乘和构成该阶乘的整数。

在默认状态下v=1,它通过乘以(也初始为)并递增直到至少等于v来实现后者,然后返回并以元组为单位。x1xnvx-1

为了计算n!一个呼叫h(n,0),其倍数x(最初1)由n和递减n直到n0当它返回x

i通过将每个索引的阶乘基的阶乘的乘积相加来提供p项的排列词典索引,以及索引右边的项小于该索引的值。[0,1,...n]h(len(p)-j-1,0)sum(k<p[j]for k in p[j+1:])


我认为您在构建排列时不需要对最后一项进行特殊处理。我没有使用255字节的GAP解决方案。
Christian Sievers,2016年

我在结尾处分别添加它,因为否则它会出现被零除的错误t/=x
乔纳森·艾伦

我花了一段时间,看看:循环已经做这一切,你可以替换(r+o)r
Christian Sievers,2016年

嗯,你是对的!非常感谢。
乔纳森·艾伦

1

Python 2中,130个 129字节

p=lambda x,k,j=1:x[j:]and p(x,k/j,j+1)+[x.pop(k%j)]
n=input();r=range(n+2);k=0
while[p(r*1,n)[i]for i in p(r*1,k)]>r:k+=1
print k

1

事实上18 11个字节

该答案使用Dennis Jelly答案中的算法,但索引为0。欢迎打高尔夫球!在线尝试!

4╞r;)╨E╨♂#í

开球

      Implicit input n.
4╞    Push 4 duplicates of n. Stack: n, n, n, n
r;)   Push the range [0...n], and move a duplicate of that range to BOS for later.
╨E    Push the n-length permutations of [0...n] and get perm_list[n].
        Stack: perm_list[n], n, [0...n]
╨     Push the n-length permutations of perm_list[n].
♂#    Convert every "list" in the zip to an actual list.
        Stack: perm(perm_list[n]), [0...n]
í     Get the index of [0...n] in the list of permutations of perm_list[n].
      Implicit return.
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.