Índice de permutación inversa

17

Introducción

¡Las permutaciones lexicográficas de una lista con n elementos pueden numerarse de 0 a n ! - 1. Por ejemplo, los 3! = 6 permutaciones de (1,2,3)serían (1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2), (3,2,1).

Cuando se aplica una permutación a una lista, sus elementos se ordenan en el mismo orden que los números de la permutación. Por ejemplo, aplicando la permutación (2,3,1)a los l = (a,b,c)rendimientos (l[2],l[3],l[1]) = (b,c,a).

La inversa de una permutación se define como la permutación que invierte esta operación, es decir, aplicar una permutación y luego su inversa (o viceversa) no modifica la matriz. Por ejemplo, el inverso de (2,3,1)es (3,1,2), ya que aplica eso a los (b,c,a)rendimientos (a,b,c).

Además, la inversa de una permutación aplicada a la permutación misma produce los enteros 1 ... n . Por ejemplo, aplicando (3,1,2)a (2,3,1)rendimientos (1,2,3).

Ahora definimos la función revind ( x ) como el índice de la permutación inversa de la permutación con el índice x . (Esto es A056019 , si está interesado).

Dado que una permutación con índice i solo modifica los últimos k elementos de la lista iff 0 ≤ i < k !, Podemos agregar cualquier número de elementos al comienzo de la lista sin afectar revind ( i ). Por lo tanto, la longitud de la lista no afecta el resultado.

Desafío

Su tarea es implementar revind ( x ). Escribirás un programa o función completa que tome un solo entero x no negativo como entrada / argumento y produzca / devuelva el resultado como un solo entero no negativo.

La entrada y la salida pueden estar indexadas a 0 o indexadas a 1, pero esto debe ser coherente entre ellas.

Las incorporaciones que generan permutaciones por índice, devuelven el índice de una permutación o encuentran la permutación inversa están prohibidas. (Se permiten las construcciones que generan todas las permutaciones o la próxima permutación).

Aplican reglas estándar de .

Ejemplos

Los siguientes ejemplos están indexados a 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

Implementación de referencia (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]
PurkkaKoodari
fuente
1
Tuve que buscar la definición de permutación inversa para comprender este desafío. Encuentro su ejemplo con (a,b,c)extremadamente poco claro. Incluya una explicación adecuada de lo que es una permutación inversa.
Fatalize
@Fatalize Esto es un poco difícil de explicar simplemente. ¿Mejor ahora?
PurkkaKoodari
Jelly tiene el átomo (grado superior) que clasifica los índices de una matriz por sus valores correspondientes. Esto sucede para invertir una permutación de 1, ..., n , pero no funciona para otras permutaciones. ¿Está prohibido incorporarlo?
Dennis
@ Dennis Pregunta difícil. Técnicamente, encuentra el inverso de cualquier permutación después de que se haya aplicado a cualquier lista estrictamente creciente. Por lo tanto, voy a decir que no está permitido. (Si alguien no está de acuerdo estrictamente, no dude en comentar puedo cambiar esto si la comunidad así lo desee..)
PurkkaKoodari

Respuestas:

5

Jalea , 6 bytes

ịŒ!⁺iR

I / O usa indexación basada en 1. Muy lento y hambriento de memoria.

Verificación

¡Mientras la entrada no exceda de 8! = 40320 , es suficiente considerar todas las permutaciones de la matriz [1, ..., 8] . Para el último caso de prueba, las permutaciones de [1, ..., 9] son suficientes.

Con un código ligeramente modificado que solo considera las permutaciones de los primeros 8 o 9 enteros positivos, ¡puede probarlo en línea! o verificar todos los casos de prueba restantes .

Cómo 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.

Enfoque alternativo, 6 bytes (no válido)

Œ!Ụ€Ụi

Es igual de largo y usa el átomo de graduación prohibido , pero es (posiblemente) más idiomático.

Al anteponer 8 (o 9 para el último caso de prueba), en realidad podemos probarlo en línea.

Cómo 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.
Dennis
fuente
6

Mathematica, 74 bytes

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

Utiliza 1-indexación. Muy ineficiente (usa ~ 11GB de memoria cuando la entrada es 11)

Explicación

j=Range@#

Genere una lista del 1 al N. Almacénelo en j.

i=Permutations[...]

Encuentra todas las permutaciones de j. Almacene eso en i.

k=Position

Almacene la Positionfunción en k. (para reducir el conteo de bytes cuando se usa Positionnuevamente)

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

Encuentre la permutación inversa de la enésima permutación.

Max@k[i,...]

Encuentre el k( Position) de la permutación inversa en i(todas las permutaciones)

Usando incorporados, 46 43 bytes

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

1 indexado.

JungHwan Min
fuente
2
"Las construcciones que ... encuentran la permutación inversa están prohibidas"
Greg Martin
@ GregMartin, ah, de alguna manera me perdí esa parte y solo vi la parte de "devolver el índice de una permutación". Tonto ... El nuevo código no tiene ese problema.
JungHwan Min
Sí, estoy de acuerdo en que fue fácil pasarlo por alto. 74 bytes, ¡todavía bastante impresionante!
Greg Martin
5

MATL , 15 bytes

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

La entrada y la salida están basadas en 1.

Similar a la respuesta CJam de @ MartinEnder , pero encuentra la permutación inversa al componer todas las permutaciones posibles con la especificada por la entrada, y ver cuál se ha convertido en la permutación de identidad.

Se queda sin memoria en el compilador en línea para entrada 10 .

Pruébalo en línea!

Explicación

:      % 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
Luis Mendo
fuente
5

Pyth, 12 bytes

xJ.phQxL@JQh

Banco de pruebas

0 indexado.

Explicación:

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.
isaacg
fuente
5

05AB1E , 14 13 bytes

Muy memoria ineficiente. Ahora aún más memoria ineficiente (pero 1 byte más corto).
Rango basado en 0.
Utiliza la codificación CP-1252 .

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

Pruébalo en línea! o como un conjunto de pruebas modificado

Explicación

ƒ               # 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
Emigna
fuente
4

CJam , 16 bytes

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

Los índices están basados ​​en 0.

Pruébalo en línea!

No soy mucho más ineficiente que esto ... se queda sin memoria con la configuración predeterminada de Java para entradas mayores que 8 (pero funciona en principio para entradas arbitrarias dado un número suficiente de universos de tiempo y memoria).

Explicación

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.
Martin Ender
fuente
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. Las nuevas líneas no cuentan, no son necesarias. Realmente no tengo que asignar la función final a un nombre, pero ...

hes una función curry que toma una lista de permutaciones y un índice en esa lista y devuelve el índice de la permutación inversa. Sin restricciones, solo lo haría Position(l,l[n]^-1).fllama a esa función con las permutaciones ordenadas de un grupo simétrico lo suficientemente grande y el dadon .

Podría escribir SymmetricGroup(n), luego la función podría calcularse para valores de hasta 9. Como ya existen soluciones mucho más pequeñas, prefiero poder hacer esto:

gap> f(100001);
303017

¡Una solución indexada 0 realmente eficiente que funciona para argumentos por debajo de 99! (y se puede hacer que funcione para argumentos por debajo de 999! a costa de un byte) es 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;

Después de eliminar espacios en blanco, esto tiene 255 bytes.

Christian Sievers
fuente
¡Buen trabajo! Esperaba obtener algunas soluciones eficientes también.
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>

0 indexado. Funciona convirtiendo el índice en una permutación, invirtiéndolo y luego volviendo a convertirlo en un índice. Editar: ahorró alrededor del 25% al finvertir y revertir la permutación, luego gconvertir la permutación revertida a un índice. Ahorró otros 10 bytes combinando las dos llamadas recursivas en una sola función. Sin golf:

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;
}
Neil
fuente
1
@JonathanAllan Lo siento, pensé que había visto un último ahorro de 9 bytes, pero no pude probarlo a fondo. He vuelto a mi versión anterior.
Neil
Implementación muy rápida ahora.
Jonathan Allan el
1
@JonathanAllan Resulta ser aún más elegante si puedo finvertir la permutación en lugar de g...
Neil
3

J 55 50 bytes

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

Basado en el ensayo J sobre el índice de permutación .

Este código solo requiere memoria en el orden de n pero usa más tiempo ya que clasifica los ntiempos de la lista y lo buscan cada índice.

Usando el incorporado /: que es capaz de encontrar el grado de una lista y el inverso de una permutación, hay una solución de 42 bytes que es más eficiente.

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

Esta versión solo requiere 44 segundos para calcular el último caso de prueba en comparación con la otra que requiere 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
millas
fuente
+1 para una eficiencia de memoria que los idiomas de golf no pueden tocar.
Urna de pulpo mágico
2

Gelatina , 14 13 9 bytes

-4 bytes gracias a @Dennis (que jugó más usando el rápido en su respuesta )

Œ!ịịŒ!$iR

Otra implementación muy lenta.
La indexación basada en 1 se utiliza aquí, por lo que los resultados esperados son:

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

No tiene sentido incluso poner un enlace IDE en línea, ya que TIO mata a una entrada de 10 . Resultados locales (¡el último es muy lento y requiere una tonelada de memoria!):

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

¿Cómo?

Œ!ịịŒ!$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: no es necesario ordenar las permutaciones ya que estamos usando el mismo orden para encontrar la permutación y es inversa.

Jonathan Allan
fuente
No puedo probarlo desde mi teléfono, pero ¿no podrías deshacerte del enlace 2 y hacer el principal ÇịịÇ$iR?
Dennis
En realidad, el Rantes Œ!está implícito, por lo que Œ!ịịŒ!$iRdebería hacer el trabajo.
Dennis
Sí, esta fue una entrada muy apresurada antes de conocer 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

Basado en 0. Lento y memoria hambriento pero corto en bytes.


No usar funciones de permutación; Memoria y tiempo eficiente.289 285 bytes

-4 bytes gracias a @Christian Sievers (permutación completa ya 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])

Sé que es código de golf, pero creo que @ Pietu1998 también está interesado en implementaciones eficientes.

Véalo en acción en repl.it

Si bien esto usa más bytes que la implementación de referencia en comparación para n=5000000:

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

f es la función de índice inverso.

fprimero obtiene el siguiente factorial anterior n, ty el entero cuyo factorial es, xal llamar h(n)y establece g=range(x), los elementos que formarán la permutación o=g[:], y el titular de la permutación,r=[]

A continuación se construye la permutación en el índice npor el poping los índices de la base de la representación factorial de na su vez de los artículos, oy añadiéndolos a r. La representación base factorial se encuentra por div y mod de ncon tdonde tse divide por xyx disminuye hasta 1.

Finalmente encuentra el índice de la permutación inversa llamando icon la permutación inversa,[r.index(v)for v in g]

h es una función de doble propósito para calcular un factorial de un entero no negativo o calcular tanto el siguiente factorial sobre un entero no negativo como el entero que hace ese factorial.

En su estado predeterminado v=1y lo hace multiplicando vpor x(también inicialmente 1) e incrementando xhasta que nsea ​​al menos tan grande, luego devuelvev yx-1 una tupla.

Para calcular n!una llamada h(n,0)que se multiplica x(inicialmente 1) por ny disminuye nhasta que nes 0cuando regresa x.

iproporciona el índice lexicográfico de una permutación, p, de los artículos [0,1,...n]mediante la suma de los productos de la factorial de la base factorial de cada índice, h(len(p)-j-1,0)y el número de elementos a la derecha del índice es menor que el valor de ese índice, sum(k<p[j]for k in p[j+1:]).

Jonathan Allan
fuente
Creo que no es necesario poner un caso especial en el último elemento al construir la permutación. No lo hice en mi solución GAP de 255 bytes.
Christian Sievers
Lo agrego por separado al final porque de lo contrario habría un error de división por cero cuando lo haga t/=x.
Jonathan Allan
Me tomó un tiempo ver: el ciclo ya lo hace todo, puede reemplazarlo (r+o)por r.
Christian Sievers
Uh, tienes razon! Muchas gracias.
Jonathan Allan el
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
Dennis
fuente
1

En realidad , 18 11 bytes

Esta respuesta usa el algoritmo en la respuesta de Dennis 'Jelly pero está indexada en 0. Sugerencias de golf bienvenidas! Pruébalo en línea!

4╞r;)╨E╨♂#í

No golfista

      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.
Sherlock9
fuente