Índice de permutação inversa


17

Introdução

As permutações lexicográficas de uma lista com n elementos podem ser numeradas de 0 a n ! - 1. Por exemplo, os 3! = 6 permutações de (1,2,3)seria(1,2,3) , (1,3,2), (2,1,3), (2,3,1), (3,1,2), (3,2,1).

Quando uma permutação é aplicada a uma lista, seus elementos são ordenados na mesma ordem que os números na permutação. Por exemplo, aplicando a permutação (2,3,1)aos l = (a,b,c)rendimentos (l[2],l[3],l[1]) = (b,c,a).

O inverso de uma permutação é definido como a permutação que reverte essa operação, ou seja, aplicar uma permutação e, em seguida, seu inverso (ou vice-versa) não modifica a matriz. Por exemplo, o inverso de(2,3,1) é (3,1,2), uma vez que aplica isso a (b,c,a)rendimentos (a,b,c).

Além disso, o inverso de uma permutação aplicado à própria permutação produz os números inteiros 1… n . Por exemplo, aplicando (3,1,2)a (2,3,1)rendimentos (1,2,3).

Agora, definimos a função revind ( x ) como o índice da permutação inversa da permutação com index x . (Este é o A056019 , se você estiver interessado.)

Como uma permutação com o índice i modifica apenas os últimos k itens da lista, se 0 ≤ i < k !, Podemos adicionar qualquer número de elementos ao início da lista sem afetar revind ( i ). Portanto, o comprimento da lista não afeta o resultado.

Desafio

Sua tarefa é implementar revind ( x ). Você escreverá um programa ou função completo que usa um único número inteiro não negativo x como entrada / argumento e gera / retorna o resultado como um único número inteiro não negativo.

A entrada e a saída podem ser indexadas em 0 ou 1, mas isso deve ser consistente entre elas.

Builtins que geram permutações por índice, retornam o índice de uma permutação ou descobrem que a permutação inversa são banidos. (Construções que geram todas as permutações ou a próxima permutação são permitidas.)

Aplicam-se as regras padrão de .

Exemplos

Os exemplos abaixo são indexados em 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

Implementação de referência (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]


11
Eu tive que procurar a definição de permutação inversa para entender esse desafio. Acho o seu exemplo (a,b,c)extremamente claro. Inclua uma explicação adequada do que é uma permutação inversa.
Fatalize 11/10

@Fatalize Isso é meio difícil de explicar de maneira simples. Melhor agora?
PurkkaKoodari

A geléia tem o átomo (classificação acima) que classifica os índices de uma matriz pelos seus valores correspondentes. Isso acontece para inverter uma permutação de 1,…, n , mas não funciona para outras permutações. É um built-in proibido?
Dennis

@ Dennis pergunta difícil. Tecnicamente, encontra o inverso de qualquer permutação depois de ter sido aplicada a qualquer lista estritamente crescente. Portanto, eu vou dizer não permitido. (Se alguém estritamente discorda, não hesite em comentário que pode mudar isso se a comunidade assim o desejar..)
PurkkaKoodari

Respostas:


5

Gelatina , 6 bytes

ịŒ!⁺iR

AE / S usa indexação baseada em 1. Muito lento e com fome de memória.

Verificação

Enquanto a entrada não exceder 8! = 40320 , é suficiente considerar todas as permutações da matriz [1,…, 8] . Para o último caso de teste, as permutações de [1,…, 9] são suficientes.

Com código ligeiramente modificado que considera apenas as permutações dos primeiros 8 ou 9 números inteiros positivos, você pode experimentar online!ou verifique todos os casos de teste restantes .

Como funciona

ịŒ!⁺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.

Abordagem alternativa, 6 bytes (inválido)

Œ!Ụ€Ụi

É tão longo e usa o proibido átomo de grau , mas é (sem dúvida) mais idiomático.

Anexando 8 (ou 9 para o último caso de teste), podemos realmente experimentá-lo online!

Como funciona

Œ!Ụ€Ụ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 bytes

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

Usa indexação 1. Muito ineficiente. (usa ~ 11 GB de memória quando a entrada é 11)

Explicação

j=Range@#

Gere uma lista de 1 a N. Armazene isso em j.

i=Permutations[...]

Encontre todas as permutações de j. Armazene isso em i.

k=Position

Armazenar a Positionfunção k. (para reduzir a contagem de bytes ao usar Positionnovamente)

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

Encontre a permutação inversa da N-ésima permutação.

Max@k[i,...]

Encontre o k( Position) da permutação inversa em i(todas as permutações)

Usando built-ins, 46 43 bytes

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

1 indexado.


2
"Construídos que ... acham que a permutação inversa são banidos"
Greg Martin

@ GregMartin, ah, de alguma forma, perdi essa parte e só vi a parte "retornar o índice de uma permutação". Parvo eu ... O novo código não tem esse problema.
JungHwan Min

sim, eu concordo que foi fácil perder. 74 bytes - ainda bastante impressionante!
Greg Martin

5

MATL , 15 bytes

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

Entrada e saída são baseadas em 1.

Semelhante à resposta CJam do @ MartinEnder , mas encontra a permutação inversa compondo todas as permutações possíveis com a especificada pela entrada e vendo qual se tornou a permutação de identidade.

Ele fica sem memória no compilador online para entrada 10.

Experimente online!

Explicação

:      % 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

Pitão, 12 bytes

xJ.phQxL@JQh

Suíte de teste

0 indexado.

Explicação:

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

05AB1E , 14 13 bytes

Muito memória ineficiente. Agora, ainda mais memória ineficiente (mas 1 byte menor).
Intervalo baseado em 0.
Usa a codificação CP-1252 .

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

Experimente online! ou como um conjunto de testes modificado

Explicação

ƒ               # 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 bytes

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

Os índices são baseados em 0.

Experimente online!

Eu não fico muito mais ineficiente do que isso ... fica sem memória com as configurações padrão do Java para entradas maiores que 8(mas funciona em princípio para entradas arbitrárias, dado um número suficiente de universos de tempo e memória).

Explicação

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 bytes

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

1 indexado. Novas linhas não são contadas, não são necessárias. Eu realmente não tenho que atribuir a função final a um nome, mas ...

hé uma função ao curry que pega uma lista de permutações e um índice nessa lista e retorna o índice da permeação inversa. Sem restrições, eu apenas faria Position(l,l[n]^-1).fchama essa função com as permutações ordenadas de um grupo simétrico suficientemente grande e o dadon .

Eu poderia escrever SymmetricGroup(n), então a função poderia ser calculada para valores até 9. Como já existem soluções muito menores, prefiro poder fazer isso:

gap> f(100001);
303017

Uma solução realmente eficiente, indexada a 0, que funciona para argumentos abaixo de 99! (e pode funcionar para argumentos abaixo de 999! ao custo de um byte) é este:

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;

Após excluir o espaço em branco, isso tem 255 bytes.


Bom trabalho! Eu esperava obter algumas soluções eficientes também.
PurkkaKoodari

3

JavaScript (ES6), 163 120 110 bytes

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>

Indexado a 0. Funciona convertendo o índice em uma permutação, invertendo-o e convertendo-o novamente em um índice. Editar: economizou cerca de 25% ao finverter e reverter a permutação e, em seguida, gconverter a permutação revertida em um índice. Economizou mais 10 bytes combinando as duas chamadas recursivas em uma única função. Ungolfed:

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

11
@ JonathanAllan Desculpe, pensei ter visto uma economia de 9 bytes no último segundo, mas não consegui testá-la completamente. Voltei à minha versão anterior.
Neil

Implementação muito swish agora.
Jonathan Allan

11
@JonathanAllan acaba sendo ainda mais abafado se eu finverter a permutação em vez de g...
Neil

3

J, 55 50 bytes

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

Baseado no ensaio J sobre Índice de Permutação .

Esse código requer apenas memória na ordem de, nmas usa mais tempo, pois classifica os ntempos da lista e os pesquisan por cada índice.

Usando o built-in /:que é capaz de encontrar o grau de uma lista e o inverso de uma permutação, existe uma solução de 42 bytes que é mais eficiente.

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

Esta versão requer apenas 44 segundos para calcular o último caso de teste quando comparado com o outro, que requer 105 segundos.

Uso

   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 em eficiência de memória que os idiomas do golfe não conseguem tocar.
Magic Octopus Urn

2

Geléia , 14 13 9 bytes

-4 bytes, graças ao @ Dennis (que ele jogou golfe usando ainda mais a rápida em sua resposta )

Œ!ịịŒ!$iR

Outra implementação muito lenta.
Indexação baseada em 1 empregada aqui, portanto, os resultados esperados são:

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

Não faz sentido colocar um link IDE online, pois o TIO mata com uma entrada de 10. Resultados locais (o último é muito lento e requer uma tonelada de memória!):

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

Quão?

Œ!ịịŒ!$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]

Nota: não há necessidade de classificar as permutações, pois estamos usando a mesma ordem para encontrar a permutação ee é inversa.


Não é possível testá-lo no meu telefone, mas você não conseguiu se livrar do link 2 e criar o principal ÇịịÇ$iR?
Dennis

Na verdade, o RantesŒ! está implícito, por isso Œ!ịịŒ!$iRdeve fazer o trabalho.
Dennis

Sim, essa foi uma entrada muito apressada antes de encontrar amigos.
Jonathan Allan

2

Python 2, 116 114 bytes

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

repl.it

Baseado em 0. Lento e com fome de memória, mas com poucos bytes.


Usando nenhuma função de permutação; memória e tempo eficientes. 289 285 bytes

-4 bytes graças a @Christian Sievers (permutação total já formada)

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

Eu sei que é código de golfe, mas acho que @ Pietu1998 também está interessado em implementações eficientes.

Veja em ação em repl.it

Embora isso use mais bytes que a implementação de referência, comparando com n=5000000:

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

f é a função de índice reverso.

f primeiro obtém o próximo fatorial acima n , te o número inteiro cujo fatorial, xchamando h(n)e define g=range(x)os itens que compõem a permutação,o=g[:] e o detentor da permutação,r=[]

A seguir constrói a permutação no índice npor poping os índices da base de representação de fatorial npor sua vez a partir dos itens, oe anexando-os ar . A representação da base fatorial é encontrada por div e mod de ncom tonde té div'd por xex diminui para baixo 1.

Finalmente, ele encontra o índice da permutação reversa chamando icom a permutação reversa,[r.index(v)for v in g]

h é uma função de dupla finalidade para calcular um fatorial de um número inteiro não negativo ou calcular o próximo fatorial acima de um número inteiro não negativo e o número inteiro que faz esse fatorial.

Em seu estado padrão v=1e faz o último multiplicando vpor x(também inicialmente 1) e aumentandox até que nseja pelo menos tão grande, então ele retornav e x-1em uma tupla.

Para calcular n!uma chamada h(n,0)que multiplica x(inicialmente 1) por ne diminui natén é 0quando ela retorna x.

ifornece o índice lexicográfico de uma permutação, pdos itens [0,1,...n], somando os produtos do fatorial da base fatorial de cada índice h(len(p)-j-1,0)e quantos itens à direita do índice são menores que o valor nesse índice sum(k<p[j]for k in p[j+1:]).


Eu acho que você não precisa incluir um caso especial no último item ao construir a permutação. Não encontrei na minha solução GAP de 255 bytes.
Christian Sievers

Eu o adiciono separadamente no final porque, caso contrário, haveria um erro de divisão por zero t/=x.
Jonathan Allan

Demorei um pouco para ver: o loop já faz tudo, você pode substituir (r+o)por r.
Christian Sievers

Você está certo! Muito obrigado.
Jonathan Allan

1

Python 2, 130 129 bytes

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

Na verdade , 18 11 bytes

Essa resposta usa o algoritmo da resposta de Dennis 'Jelly, mas é indexada em 0. Sugestões de golfe são bem-vindas! Experimente online!

4╞r;)╨E╨♂#í

Ungolfing

      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.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.