Introducción
Supongamos que se le entrega una permutación aleatoria de n
objetos. La permutación está sellada en una caja, por lo que no tienes idea de cuál es la n!
posible. Si logró aplicar la permutación a n
objetos distintos, podría deducir inmediatamente su identidad. Sin embargo, solo se le permite aplicar la permutación a n
vectores binarios de longitud , lo que significa que tendrá que aplicarla varias veces para reconocerla. Claramente, aplicarlo a los n
vectores con solo uno 1
hace el trabajo, pero si eres inteligente, puedes hacerlo con log(n)
aplicaciones. Sin embargo, el código para ese método será más largo ...
Este es un desafío experimental en el que su puntaje es una combinación de longitud de código y complejidad de consulta , lo que significa la cantidad de llamadas a un procedimiento auxiliar. La especificación es un poco larga, así que tengan paciencia conmigo.
La tarea
Su tarea es escribir una función con nombre (o el equivalente más cercano) f
que tome como entradas un número entero positivo n
y una permutación p
de los primeros n
números enteros, usando indexación basada en 0 o en 1. Su salida es la permutación p
. Sin embargo, no puede acceder a la permutación p
directamente . Lo único que puede hacer con él es aplicarlo a cualquier vector de n
bits. Para este propósito, utilizará una función auxiliar P
que toma una permutación p
y un vector de bits v
, y devuelve el vector permutado cuya p[i]
coordenada th contiene el bit v[i]
. Por ejemplo:
P([1,2,3,4,0], [1,1,0,0,0]) == [0,1,1,0,0]
Puede reemplazar "bits" con dos valores distintos, como 3
and -4
, or 'a'
y 'b'
, y no necesitan ser reparados, por lo que puede llamar P
con ambos [-4,3,3,-4]
y [2,2,2,1]
en la misma llamada a f
. La definición de P
no se cuenta para su puntaje.
Puntuación
La complejidad de la consulta de su solución en una entrada dada es la cantidad de llamadas que realiza a la función auxiliar P
. Para que esta medida no sea ambigua, su solución debe ser determinista. Puede usar números generados de forma seudoaleatoria, pero también debe arreglar una semilla inicial para el generador.
En este repositorio encontrará un archivo llamado permutations.txt
que contiene 505 permutaciones, 5 de cada longitud entre 50 y 150 inclusive, usando indexación basada en 0 (incremente cada número en el caso basado en 1). Cada permutación está en su propia línea, y sus números están separados por espacios. Su puntaje es el recuento de bytes de f
+ complejidad de consulta promedio en estas entradas . La puntuación más baja gana.
Reglas extra
Se prefiere el código con explicaciones, y las lagunas estándar no están permitidas. En particular, los bits individuales son indistinguibles (por lo que no puede dar un vector de Integer
objetos P
y comparar sus identidades), y la función P
siempre devuelve un nuevo vector en lugar de reorganizar su entrada. Puede cambiar libremente los nombres de f
y P
, y el orden en que toman sus argumentos.
Si es la primera persona en responder en su lenguaje de programación, le recomendamos encarecidamente que incluya un arnés de prueba, incluida una implementación de la función P
que también cuente la cantidad de veces que se llamó. Como ejemplo, aquí está el arnés para Python 3.
def f(n,p):
pass # Your submission goes here
num_calls = 0
def P(permutation, bit_vector):
global num_calls
num_calls += 1
permuted_vector = [0]*len(bit_vector)
for i in range(len(bit_vector)):
permuted_vector[permutation[i]] = bit_vector[i]
return permuted_vector
num_lines = 0
file_stream = open("permutations.txt")
for line in file_stream:
num_lines += 1
perm = [int(n) for n in line.split()]
guess = f(len(perm), perm)
if guess != perm:
print("Wrong output\n %s\n given for input\n %s"%(str(guess), str(perm)))
break
else:
print("Done. Average query complexity: %g"%(num_calls/num_lines,))
file_stream.close()
En algunos idiomas, es imposible escribir tal arnés. En particular, Haskell no permite que la función pura P
registre la cantidad de veces que se llama. Por este motivo, puede volver a implementar su solución de tal manera que también calcule su complejidad de consulta y la use en el arnés.
abaaabababaa
y-4 3 3 3 -4 3
sería un vector de bits.Respuestas:
J,
44.069322.0693 =3715 + 7.06931Si no podemos llamar
P
eni. n
, al menos podemos llamadaP
en cada bit dei. n
forma separada. El número de invocaciones deP
es>. 2 ^. n
(⌈log 2 n ⌉). Creo que esto es óptimo.Aquí hay una implementación de la función
P
que usa el vector de permutaciónp
y guarda el número de invocacionesPinv
.Aquí hay un arnés de prueba que recibe una permutación y devuelve el número de invocaciones de
p
:Y así es como puede usarlo en el archivo
permutations.txt
:Explicación
La breve explicación ya se proporcionó anteriormente, pero aquí hay una más detallada. Primero,
f
con espacios insertados y como una función explícita:Leer:
donde y es el parámetro formal de f. en J, los parámetros de a (función) se denominan x e y si el verbo es diádico (tiene dos parámetros) e y si es monádico (tiene un parámetro).
En lugar de invocar
P
eni. n
(es decir0 1 2 ... (n - 1)
), invocamosP
en cada posición de bit de los números eni. n
. Como todas las permutaciones permutan de la misma manera, podemos volver a ensamblar los bits permutados en números para obtener un vector de permutación:i. y
- enteros de0
ay - 1
.#: y
-y
representado en la base 2. Esto se extiende a los vectores de números de forma natural. Por ejemplo,#: i. 16
produce:#. y
-y
Interpretado como un número base 2. Notablemente, esto es lo contrario a#:
;y ~: #. #:
siempre tiene.|: y
-y
transpuesto.u&.v y
-u
debajov
, ahí esvinv u v y
dondevinv
está el inverso av
. Tenga en cuenta que|:
es su propio inverso.P y
- la funciónP
aplicada a cada vectory
por definición.fuente
Pyth 32 + 7.06931 = 37.06931
Encontré el siguiente algoritmo completamente independiente. Pero es más o menos lo mismo que la solución J muy corta de FUZxxl (por lo que yo entiendo).
Primero, la definición de la función
P
, que permuta una matriz de bits de acuerdo con una permutación desconocida.Y luego el código, que determina la permutación.
Esto define una función
g
, que toma dos argumentos. Puedes llamarlo porg5[4 2 1 3 0
. Aquí hay una demostración en línea . Nunca usé tantos (5) mapas anidados.Por cierto, no he hecho un arnés de prueba. La función tampoco
P
cuenta cuántas veces la llamo. Ya pasé mucho tiempo descubriendo el algoritmo. Pero si lees mi explicación, es bastante obvio que usaint(log2(n-1)) + 1
llamadas (= ceil(log2(n))
). Ysum(int(log2(n-1)) + 1 for n in range(50, 151)) / 101.0 = 7.069306930693069
.Explicación:
En realidad, me costó bastante encontrar este algoritmo. No me quedó claro en absoluto cómo lograrlo
log(n)
. Entonces comencé a hacer algunos experimentos con pequeñosn
.Primera nota: una matriz de bits recopila la misma información que la matriz de bits complementaria. Por lo tanto, todas las matrices de bits en mi solución tienen como máximo
n/2
bit activo.n = 3:
Como solo podemos usar una matriz de bits con 1 bit activo, la solución óptima depende de dos llamadas. Por ejemplo
P([1, 0, 0])
yP([0, 1, 0])
. Los resultados nos dicen el primer y segundo número de la permutación, indirectamente obtenemos el tercero.n = 4:
Aquí se pone un poco interesante. Ahora podemos usar dos tipos de matriz de bits. Aquellos con 1 bit activo y aquellos con 2 bits activos. Si usamos una matriz de bits con un bit activo, solo recopilamos información sobre un número de la permutación y volvemos a
n = 3
, lo que resulta en1 + 2 = 3
llamadas deP
. La parte interesante es que podemos hacer lo mismo con solo 2 llamadas, si utilizamos matrices de bits con 2 bits activos. Por ejemploP([1, 1, 0, 0])
yP([1, 0, 1, 0])
.Digamos que obtenemos los resultados
[1, 0, 0, 1]
y[0, 0, 1, 1]
. Vemos que el bit número 4 está activo en ambas matrices de salida. El único bit que estaba activo en ambas matrices de entrada era el bit número 1, por lo que claramente comienza la permutación4
. Ahora es fácil ver que el bit 2 se movió al bit 1 (primera salida) y el bit 3 se movió al bit 3 (segunda salida). Por lo tanto, la permutación debe ser[4, 1, 3, 2]
.n = 7:
Ahora algo más grande. Mostraré las llamadas de
P
inmediato. Son la única vez que se me ocurrió después de pensar un poco y experimentar. (Tenga en cuenta que estos no son los que uso en mi código).Si en las primeras dos matrices de salida (y no en la tercera) el bit 2 está activo, sabemos que la permutación mueve el bit 1 al bit 2, ya que el bit uno es el único bit que está activo en las dos primeras matrices de entrada.
Lo importante es que (interpretando las entradas como matriz) cada una de las columnas es única. Esto me recordó en los códigos de Hamming , donde se logra lo mismo. Simplemente toman los números del 1 al 7 y usan su representación de bits como columnas. Usaré los números del 0 al 6. Ahora, lo bueno es que podemos interpretar las salidas (nuevamente las columnas) como números nuevamente. Estos nos dicen el resultado de la permutación aplicada
[0, 1, 2, 3, 4, 5, 6]
.Entonces solo tenemos que rastrear los números. El bit 0 terminó en el bit 5, el bit 1 terminó en el bit 0, el bit 2 terminó en el bit 6, ... Así que la permutación fue
[5, 0, 6, 1, 3, 4, 2]
.Y el código para la función de permutación:
fuente
*sltQ]0
conm0sltQ
, podría tener 6 mapas anidados de la misma longitud.f
aunque se permiten otros nombres. La tarea cuenta para su puntaje.g
lugar de leer desde STDIN.Mathematica, 63 + 100 = 163
Estoy usando permutaciones basadas en uno, ya que así es como funciona la indexación en Mathematica.
Primero, el arnés de prueba. Esta es la función de consulta
p
(los nombres definidos por el usuario no deben estar en mayúsculas en Mathematica):Y preparación de entrada junto con el bucle de prueba:
Y finalmente, mi presentación real que usa el algoritmo ingenuo por ahora:
O con sangría:
fuente