Ir al contenido principal

Sucesión infinita de todas las palabras


El conjunto de todas las palabras se puede ordenar como en los diccionarios:

  a,   b, ...,   z,   A,   B, ...,   Z,
 aa,  ab, ...,  az,  aA,  aB, ...,  aZ,
 ba,  bb, ...,  bz,  bA,  bB, ...,  bZ,
...
 za,  zb, ...,  zz,  zA,  zB, ...,  zZ,
aaa, aab, ..., aaz, aaA, aaB, ..., aaZ,
baa, bab, ..., baz, baA, baB, ..., baZ,
...

Definir las funciones

palabras :: [String]
posicion :: String -> Integer

tales que

  • palabras es la lista ordenada de todas las palabras. Por ejemplo,
λ> take 10 (drop 100 palabras)
["aW","aX","aY","aZ","ba","bb","bc","bd","be","bf"]
λ> take 10 (drop 2750 palabras)
["ZU","ZV","ZW","ZX","ZY","ZZ","aaa","aab","aac","aad"]
λ> palabras !! (10^6)
"gePO"
  • (posicion n) es la palabra que ocupa la posición n en la lista ordenada de todas las palabras. Por ejemplo,
posicion "c"                    ==  2
posicion "ab"                   ==  53
posicion "ba"                   ==  104
posicion "eva"                  ==  14664
posicion "adan"                 ==  151489
posicion "HoyEsMartes"          ==  4957940944437977046
posicion "EnUnLugarDeLaMancha"  ==  241779893912461058861484239910864

Comprobar con QuickCheck que para todo entero positivo n se verifica que

posicion (palabras `genericIndex` n) == n

Soluciones

import Data.List (genericIndex, genericLength)
import Test.QuickCheck

-- 1ª definición de palabras
-- =========================

palabras1 :: [String]
palabras1 = concatMap palabrasDeLongitud [1..]

-- (palabrasDeLongitud n) es la lista ordenada de palabras longitud
-- n. Por ejemplo,
--    take 3 (palabrasDeLongitud 2)  ==  ["aa","ab","ac"]
--    take 3 (palabrasDeLongitud 3)  ==  ["aaa","aab","aac"]
palabrasDeLongitud :: Integer -> [String]
palabrasDeLongitud 1 = [[x]  | x <- letras]
palabrasDeLongitud n = [x:ys | x <- letras,
                               ys <- palabrasDeLongitud (n-1)]

-- letras es la lista ordenada de las letras.
letras :: [Char]
letras = ['a'..'z'] ++ ['A'..'Z']

-- 2ª definición de palabras
-- =========================

palabras2 :: [String]
palabras2 = concat (iterate f elementales)
    where f = concatMap (\x -> map (x++) elementales)

-- elementales es la lista de las palabras de una letra.
elementales :: [String]
elementales = map (:[]) letras

-- 3ª definición de palabras
-- =========================

palabras3 :: [String]
palabras3 = tail $ map reverse aux
    where aux = "" : [c:cs | cs <- aux, c <- letras]

-- Comparación
-- ===========

--    λ> palabras1 !! (10^6)
--    "gePO"
--    (0.87 secs, 480,927,648 bytes)
--    λ> palabras2 !! (10^6)
--    "gePO"
--    (0.11 secs, 146,879,840 bytes)
--    λ> palabras3 !! (10^6)
--    "gePO"
--    (0.25 secs, 203,584,608 bytes)

-- 1ª definición de posicion
-- =========================

posicion1 :: String -> Integer
posicion1 cs = genericLength (takeWhile (/=cs) palabras1)

-- 1ª definición de posicion
-- =========================

posicion2 :: String -> Integer
posicion2 ""     = -1
posicion2 (c:cs) = (1 + posicionLetra c) * 52^(length cs) + posicion2 cs

posicionLetra :: Char -> Integer
posicionLetra c = genericLength (takeWhile (/=c) letras)

-- La propiedad es
prop_palabras :: (Positive Integer) -> Bool
prop_palabras (Positive n) =
    posicion2 (palabras3 `genericIndex` n) == n

-- La comprobación es
--    λ> quickCheck prop_palabras
--    +++ OK, passed 100 tests.