import Test.QuickCheck
data Arbol = H
| N Integer Arbol Arbol
deriving (Eq, Show)
data Movimiento = I | D deriving (Show, Eq)
type Posicion = [Movimiento]
-- 1ª solución
-- ===========
posicionDeElemento :: Integer -> Posicion
posicionDeElemento n =
head (posiciones n (arbolBinarioCompleto n))
-- (arbolBinarioCompleto n) es el árbol binario completo con n
-- nodos. Por ejemplo,
-- λ> arbolBinarioCompleto 4
-- N 1 (N 2 (N 4 H H) H) (N 3 H H)
-- λ> pPrint (arbolBinarioCompleto 9)
-- N 1
-- (N 2
-- (N 4
-- (N 8 H H)
-- (N 9 H H))
-- (N 5 H H))
-- (N 3
-- (N 6 H H)
-- (N 7 H H))
arbolBinarioCompleto :: Integer -> Arbol
arbolBinarioCompleto n = aux 1
where aux i | i <= n = N i (aux (2*i)) (aux (2*i+1))
| otherwise = H
-- (posiciones n a) es la lista de las posiciones del elemento n
-- en el árbol a. Por ejemplo,
-- posiciones 9 (arbolBinarioCompleto 9) == [[I,I,D]]
posiciones :: Integer -> Arbol -> [Posicion]
posiciones n a = aux n a [[]]
where aux _ H _ = []
aux n (N x i d) cs | x == n = cs ++ ps
| otherwise = ps
where ps = map (I:) (aux n i cs) ++
map (D:) (aux n d cs)
-- 2ª solución
-- ===========
posicionDeElemento2 :: Integer -> Posicion
posicionDeElemento2 1 = []
posicionDeElemento2 n
| even n = posicionDeElemento2 (n `div` 2) ++ [I]
| otherwise = posicionDeElemento2 (n `div` 2) ++ [D]
-- 3ª solución
-- ===========
posicionDeElemento3 :: Integer -> Posicion
posicionDeElemento3 = reverse . aux
where aux 1 = []
aux n | even n = I : aux (n `div` 2)
| otherwise = D : aux (n `div` 2)
-- 4ª solución
-- ===========
posicionDeElemento4 :: Integer -> Posicion
posicionDeElemento4 n =
[f x | x <- tail (reverse (binario n))]
where f 0 = I
f 1 = D
-- (binario n) es la lista de los dígitos de la representación binaria
-- de n. Por ejemplo,
-- binario 11 == [1,1,0,1]
binario :: Integer -> [Integer]
binario n
| n < 2 = [n]
| otherwise = n `mod` 2 : binario (n `div` 2)
-- Equivalencia
-- ============
-- La propiedad es
prop_posicionDeElemento_equiv :: Positive Integer -> Bool
prop_posicionDeElemento_equiv (Positive n) =
posicionDeElemento n == posicionDeElemento2 n &&
posicionDeElemento n == posicionDeElemento3 n &&
posicionDeElemento n == posicionDeElemento4 n
-- La comprobación es
-- λ> quickCheck prop_posicionDeElemento_equiv
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- λ> posicionDeElemento (10^7)
-- [I,I,D,D,I,I,I,D,I,I,D,I,D,D,I,D,I,I,I,I,I,I,I]
-- (5.72 secs, 3,274,535,328 bytes)
-- λ> posicionDeElemento2 (10^7)
-- [I,I,D,D,I,I,I,D,I,I,D,I,D,D,I,D,I,I,I,I,I,I,I]
-- (0.01 secs, 189,560 bytes)
-- λ> posicionDeElemento3 (10^7)
-- [I,I,D,D,I,I,I,D,I,I,D,I,D,D,I,D,I,I,I,I,I,I,I]
-- (0.01 secs, 180,728 bytes)
-- λ> posicionDeElemento4 (10^7)
-- [I,I,D,D,I,I,I,D,I,I,D,I,D,D,I,D,I,I,I,I,I,I,I]
-- (0.01 secs, 184,224 bytes)
--
-- λ> length (posicionDeElemento2 (10^4000))
-- 13287
-- (2.80 secs, 7,672,011,280 bytes)
-- λ> length (posicionDeElemento3 (10^4000))
-- 13287
-- (0.03 secs, 19,828,744 bytes)
-- λ> length (posicionDeElemento4 (10^4000))
-- 13287
-- (0.03 secs, 18,231,536 bytes)
--
-- λ> length (posicionDeElemento3 (10^50000))
-- 166096
-- (1.34 secs, 1,832,738,136 bytes)
-- λ> length (posicionDeElemento4 (10^50000))
-- 166096
-- (1.70 secs, 1,812,806,080 bytes)