2011-11-30 25 views
7

quiero definir una funciónHaskell: invertir una permutación en el tiempo lineal, utilizando solo lista

invert :: [Int] -> [Int] 

que asume que su entrada es una permutación de [0..(n-1)], y devuelve su inversa. ¿Se puede definir utilizando solo listas y tuplas (sin matrices) para que se ejecute en tiempo lineal?

Esto es principalmente por interés académico; en código real podría usar Array o STArray o similar.

+0

¿Puede explicar qué es un invertido de una permutación ¿lista? – Tarrasch

+0

Tarrasch, mira aquí: http://mathworld.wolfram.com/InversoPermutación.html – Kenji

+1

Me gusta esta pregunta, pero creo que no es posible hacerlo en O (n). Un método sencillo requiere clasificación. Si Haskell enumera O (1) indexado y anexado, entonces sería posible. Muchas operaciones en 'Data.List' son O (n), por lo que estamos muy limitados en términos de eficiencia. – Kenji

Respuesta

2

No estoy seguro sobre el tiempo lineal, solo una nota para principiantes.

λ> (\x -> map snd $ sort $ zip x [1..(length x)]) [3,8,5,10,9,4,6,1,7,2] 
[8,10,1,6,3,7,9,2,5,4] 
+0

Eso calificaría si puede proporcionar un algoritmo de clasificación 'O (n)' ... – leftaroundabout

+0

Gracias, pero esto utiliza una clasificación de comparación, por lo que será Ω (n log n). – Prateek

+7

Por cierto, puede omitir 'length x', simplemente zip con' [1 ..] '. – leftaroundabout

1

Me parece que no se puede hacer esto en tiempo lineal. Para una implementación con una complejidad de tiempo O (n) necesitará crear la lista de resultados fuera de servicio, lo que no se puede hacer directamente con cons-lists, supongo.

+1

Me inclino a creer eso también. Pero entonces, la implementación recursiva "obvia" de "reversa" es el tiempo cuadrático, y también hay una implementación de tiempo lineal fácil. Considere 'minout :: [Int] -> Int' que toma una lista de enteros distintos no negativos, y devuelve el entero no negativo más pequeño * no * presente en la entrada. Esto también se puede hacer en tiempo lineal, con cierta astucia. Entonces me pregunto ... – Prateek

+0

@prateek: No estoy seguro de si entiendo tu punto. Por supuesto, 'minout' tiene una implementación obvia de tiempo lineal; Incluso diría que eso no requiere mucha inteligencia: 'minout = foldr (máximo succ) 0'. Pero, ¿cómo se relaciona esto con 'invertir'?La diferencia entre "invertir" e "invertir" es que el primero puede construir su resultado en orden, mientras que el segundo -creo que yo creo- no puede. –

+0

Mi punto (algo débil) era simplemente que las cosas que parecen difíciles de hacer con listas en tiempo lineal podrían ser posibles, así que tal vez haya una forma inteligente de 'invertir' en la que no he pensado. No sé cómo se podría probar un resultado negativo aquí. (Por cierto, la función que usted dio no computa 'minout' como lo definí - calcula 1 + el elemento máximo de la lista:' minout [0,2,4] 'debe ser' 1') – Prateek

2

Por lo tanto, esto no utiliza "solo listas". Pero parecía encajar tan bien.

import qualified Data.Vector as V 

invert :: [Int] -> [Int] 
invert list = V.toList $ vec V.// assocs 
    where vec = V.fromList list -- better ideas for initializing vec? 
     assocs = zip (map pred list) [1..] 

Ver the Vector package, que afirma que // es O (n). Bueno, dice O (n + m), pero en este caso, n = m.

Lo cargué en ghci y obtuve la misma respuesta que dmitry. :)

+2

Solo piense en vectores como tuplas realmente grandes. ;) –

+0

[Una implementación alternativa que utiliza la inicialización monádica] (http://stackoverflow.com/a/6737456/98117). (Nota: esto usa indexación basada en cero) – hammar

0

Si las permutaciones se representan como listas de ciclos disjuntos, entonces

invert :: [[Int]] -> [[Int]] 
invert = map reverse 

carreras en tiempo lineal. No estoy seguro si hay una forma lineal de convertir entre las diferentes representaciones.

1

Como no parece ser una respuesta positiva a la pregunta real, voy a añadir mi tramposo a la lista de los no-soluciones:

invert :: [Int] -> [Int] 
invert lst = take lng $ map (fromIntegral.(`mod`h)) $ iterate (`div`h) 
       $ sum $ zipWith (\k x->k*h^x) [0..] lst 
    where h::Integer 
     h = fromIntegral lng 
     lng = length lst 
+0

Me pregunto en qué punto 'k * h^x' se convierte en un factor de desaceleración significativo. –

+0

¡Muy pronto, desafortunadamente! Como dije, no es realmente una solución; de hecho, la exponenciación lo hace mucho más lento que _O_ (_n_ log _n_), más parecido a _O_ (_n_ ² log _n_). – leftaroundabout

+0

Sería _O_ (_n_) en alguna computadora cuántica hipotética con exponenciación de precisión arbitraria nativa, aunque ... – leftaroundabout

Cuestiones relacionadas