Sistema factorádico de numeración
El sistema factorádico es un sistema numérico basado en factoriales en el que el n-ésimo dígito, empezando desde la derecha, debe ser multiplicado por n! Por ejemplo, el número "341010" en el sistema factorádico es 463 en el sistema decimal ya que
3×5! + 4×4! + 1×3! + 0×2! + 1×1! + 0×0! = 463
En este sistema numérico, el dígito de más a la derecha es siempre 0, el segundo 0 o 1, el tercero 0,1 o 2 y así sucesivamente.
Con los dígitos del 0 al 9 el mayor número que podemos codificar es el 10!-1 = 3628799. En cambio, si lo ampliamos con las letras A a Z podemos codificar hasta 36!-1 = 37199332678990121746799944815083519999999910.
Definir las funciones
factoradicoAdecimal :: String -> Integer decimalAfactoradico :: Integer -> String
tales que
- (factoradicoAdecimal cs) es el número decimal correspondiente al número factorádico cs. Por ejemplo,
λ> factoradicoAdecimal "341010" 463 λ> factoradicoAdecimal "2441000" 2022 λ> factoradicoAdecimal "A0000000000" 36288000 λ> map factoradicoAdecimal ["10","100","110","200","210","1000","1010","1100","1110","1200"] [1,2,3,4,5,6,7,8,9,10] λ> factoradicoAdecimal "3KXWVUTSRQPONMLKJIHGFEDCBA9876543210" 37199332678990121746799944815083519999999
- (decimalAfactoradico n) es el número factorádico correpondiente al número decimal n. Por ejemplo,
λ> decimalAfactoradico 463 "341010" λ> decimalAfactoradico 2022 "2441000" λ> decimalAfactoradico 36288000 "A0000000000" λ> map decimalAfactoradico [1..10] ["10","100","110","200","210","1000","1010","1100","1110","1200"] λ> decimalAfactoradico 37199332678990121746799944815083519999999 "3KXWVUTSRQPONMLKJIHGFEDCBA9876543210"
Comprobar con QuickCheck que, para cualquier entero positivo n,
factoradicoAdecimal (decimalAfactoradico n) == n
Nota: Escribir las soluciones en Haskell, en Python y en Common Lisp.
Soluciones
A continuación se muestran
- las soluciones en Haskell,
- las soluciones en Python y
- las soluciones en Common Lisp
1. Soluciones en Haskell
module Sistema_factoradico_de_numeracion where import Data.List (genericIndex, genericLength) import qualified Data.Map as M ((!), Map, fromList) import Test.Hspec (Spec, describe, hspec, it, shouldBe) import Test.QuickCheck -- 1ª definición de factoradicoAdecimal -- ==================================== factoradicoAdecimal1 :: String -> Integer factoradicoAdecimal1 cs = sum (zipWith (*) xs ys) where xs = map caracterAentero cs n = length cs ys = reverse (take n facts) -- (caracterAentero c) es la posición del carácter c en la lista de -- caracteres ['0', '1',..., '9', 'A', 'B',..., 'Z']. Por ejemplo, -- caracterAentero '0' == 0 -- caracterAentero '1' == 1 -- caracterAentero '9' == 9 -- caracterAentero 'A' == 10 -- caracterAentero 'B' == 11 -- caracterAentero 'Z' == 35 caracterAentero :: Char -> Integer caracterAentero c = head [n | (n,x) <- zip [0..] caracteres, x == c] -- caracteres es la lista de caracteres -- ['0', '1',..., '9', 'A', 'B',..., 'Z'] caracteres :: String caracteres = ['0'..'9'] ++ ['A'..'Z'] -- facts es la lista de los factoriales. Por ejemplo, -- λ> take 12 facts -- [1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800] facts :: [Integer] facts = scanl (*) 1 [1..] -- 2ª definición de factoradicoAdecimal -- ==================================== factoradicoAdecimal2 :: String -> Integer factoradicoAdecimal2 cs = sum (zipWith (*) xs ys) where xs = map caracterAentero2 cs n = length cs ys = reverse (take n facts) -- (caracterAentero2 c) es la posición del carácter c en la lista de -- caracteres ['0', '1',..., '9', 'A', 'B',..., 'Z']. Por ejemplo, -- caracterAentero2 '0' == 0 -- caracterAentero2 '1' == 1 -- caracterAentero2 '9' == 9 -- caracterAentero2 'A' == 10 -- caracterAentero2 'B' == 11 -- caracterAentero2 'Z' == 35 caracterAentero2 :: Char -> Integer caracterAentero2 c = caracteresEnteros M.! c -- caracteresEnteros es el diccionario cuyas claves son los caracteres y -- las claves son los números de 0 a 35. caracteresEnteros :: M.Map Char Integer caracteresEnteros = M.fromList (zip (['0'..'9'] ++ ['A'..'Z']) [0..]) -- 3ª definición de factoradicoAdecimal -- ==================================== factoradicoAdecimal3 :: String -> Integer factoradicoAdecimal3 cs = sum (zipWith (*) facts (reverse (map caracterAentero3 cs))) -- (caracterAentero3 c) es la posición del carácter c en la lista de -- caracteres ['0', '1',..., '9', 'A', 'B',..., 'Z']. Por ejemplo, -- caracterAentero3 '0' == 0 -- caracterAentero3 '1' == 1 -- caracterAentero3 '9' == 9 -- caracterAentero3 'A' == 10 -- caracterAentero3 'B' == 11 -- caracterAentero3 'Z' == 35 caracterAentero3 :: Char -> Integer caracterAentero3 c = genericLength (takeWhile (/= c) caracteres) -- 4ª definición de factoradicoAdecimal -- ==================================== factoradicoAdecimal4 :: String -> Integer factoradicoAdecimal4 = sum . zipWith (*) facts . reverse . map caracterAentero4 -- (caracterAentero4 c) es la posición del carácter c en la lista de -- caracteres ['0', '1',..., '9', 'A', 'B',..., 'Z']. Por ejemplo, -- caracterAentero4 '0' == 0 -- caracterAentero4 '1' == 1 -- caracterAentero4 '9' == 9 -- caracterAentero4 'A' == 10 -- caracterAentero4 'B' == 11 -- caracterAentero4 'Z' == 35 caracterAentero4 :: Char -> Integer caracterAentero4 = genericLength . flip takeWhile caracteres . (/=) -- 1ª definición de decimalAfactoradico -- ==================================== decimalAfactoradico1 :: Integer -> String decimalAfactoradico1 n = aux n (reverse (takeWhile (<=n) facts)) where aux 0 xs = ['0' | _ <- xs] aux m (x:xs) = enteroAcaracter (m `div` x) : aux (m `mod` x) xs -- (enteroAcaracter k) es el k-ésimo elemento de la lista -- ['0', '1',..., '9', 'A', 'B',..., 'Z']. . Por ejemplo, -- enteroAcaracter 0 == '0' -- enteroAcaracter 1 == '1' -- enteroAcaracter 9 == '9' -- enteroAcaracter 10 == 'A' -- enteroAcaracter 11 == 'B' -- enteroAcaracter 35 == 'Z' enteroAcaracter :: Integer -> Char enteroAcaracter k = caracteres `genericIndex` k -- 2ª definición de decimalAfactoradico -- ==================================== decimalAfactoradico2 :: Integer -> String decimalAfactoradico2 n = aux n (reverse (takeWhile (<=n) facts)) where aux 0 xs = ['0' | _ <- xs] aux m (x:xs) = enteroAcaracter2 (m `div` x) : aux (m `mod` x) xs -- (enteroAcaracter2 k) es el k-ésimo elemento de la lista -- ['0', '1',..., '9', 'A', 'B',..., 'Z']. . Por ejemplo, -- enteroAcaracter2 0 == '0' -- enteroAcaracter2 1 == '1' -- enteroAcaracter2 9 == '9' -- enteroAcaracter2 10 == 'A' -- enteroAcaracter2 11 == 'B' -- enteroAcaracter2 35 == 'Z' enteroAcaracter2 :: Integer -> Char enteroAcaracter2 k = enterosCaracteres M.! k -- enterosCaracteres es el diccionario cuyas claves son los número de 0 -- a 35 y las claves son los caracteres. enterosCaracteres :: M.Map Integer Char enterosCaracteres = M.fromList (zip [0..] caracteres) -- 3ª definición de decimalAfactoradico -- ==================================== decimalAfactoradico3 :: Integer -> String decimalAfactoradico3 n = aux "" 2 (n, 0) where aux s _ (0, 0) = s aux s m (d, r) = aux (enteroAcaracter3 r: s) (m + 1) (d `divMod` m) -- (enteroAcaracter3 k) es el k-ésimo elemento de la lista -- ['0', '1',..., '9', 'A', 'B',..., 'Z']. . Por ejemplo, -- enteroAcaracter3 0 == '0' -- enteroAcaracter3 1 == '1' -- enteroAcaracter3 9 == '9' -- enteroAcaracter3 10 == 'A' -- enteroAcaracter3 11 == 'B' -- enteroAcaracter3 35 == 'Z' enteroAcaracter3 :: Integer -> Char enteroAcaracter3 n = caracteres !! fromInteger n -- 4ª definición de decimalAfactoradico -- ==================================== decimalAfactoradico4 :: Integer -> String decimalAfactoradico4 = f "" 2 . (, 0) where f s _ (0, 0) = s f s n (d, r) = f (enteroAcaracter4 r: s) (n + 1) (d `divMod` n) -- (enteroAcaracter4 k) es el k-ésimo elemento de la lista -- ['0', '1',..., '9', 'A', 'B',..., 'Z']. . Por ejemplo, -- enteroAcaracter4 0 == '0' -- enteroAcaracter4 1 == '1' -- enteroAcaracter4 9 == '9' -- enteroAcaracter4 10 == 'A' -- enteroAcaracter4 11 == 'B' -- enteroAcaracter4 35 == 'Z' enteroAcaracter4 :: Integer -> Char enteroAcaracter4 = (caracteres `genericIndex`) -- Verificación -- ============ verifica :: IO () verifica = hspec spec specG1 :: (String -> Integer) -> Spec specG1 factoradicoAdecimal = do it "e1" $ factoradicoAdecimal "341010" `shouldBe` 463 it "e2" $ factoradicoAdecimal "2441000" `shouldBe` 2022 it "e3" $ factoradicoAdecimal "A0000000000" `shouldBe` 36288000 specG2 :: (Integer -> String) -> Spec specG2 decimalAfactoradico = do it "e1" $ decimalAfactoradico 463 `shouldBe` "341010" it "e2" $ decimalAfactoradico 2022 `shouldBe` "2441000" it "e3" $ decimalAfactoradico 36288000 `shouldBe` "A0000000000" spec :: Spec spec = do describe "def. 1" $ specG1 factoradicoAdecimal1 describe "def. 2" $ specG1 factoradicoAdecimal2 describe "def. 3" $ specG1 factoradicoAdecimal3 describe "def. 4" $ specG1 factoradicoAdecimal4 describe "def. 1" $ specG2 decimalAfactoradico1 describe "def. 2" $ specG2 decimalAfactoradico2 describe "def. 3" $ specG2 decimalAfactoradico3 describe "def. 4" $ specG2 decimalAfactoradico4 -- La verificación es -- λ> verifica -- -- 24 examples, 0 failures -- Propiedad de inverso -- ==================== prop_factoradico :: Integer -> Property prop_factoradico n = n >= 0 ==> factoradicoAdecimal1 (decimalAfactoradico1 n) == n && factoradicoAdecimal2 (decimalAfactoradico2 n) == n && factoradicoAdecimal3 (decimalAfactoradico3 n) == n && factoradicoAdecimal4 (decimalAfactoradico4 n) == n -- La comprobación es -- λ> quickCheck prop_factoradico -- +++ OK, passed 100 tests; 101 discarded. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> length (decimalAfactoradico1 (10^300000)) -- 68191 -- (2.46 secs, 9,088,634,744 bytes) -- λ> length (decimalAfactoradico2 (10^300000)) -- 68191 -- (2.36 secs, 9,088,634,800 bytes) -- λ> length (decimalAfactoradico3 (10^300000)) -- 68191 -- (2.18 secs, 4,490,856,416 bytes) -- λ> length (decimalAfactoradico4 (10^300000)) -- 68191 -- (1.98 secs, 4,490,311,536 bytes) -- -- λ> length (show (factoradicoAdecimal1 (show (10^50000)))) -- 213237 -- (0.93 secs, 2,654,156,680 bytes) -- λ> length (show (factoradicoAdecimal2 (show (10^50000)))) -- 213237 -- (0.51 secs, 2,633,367,168 bytes) -- λ> length (show (factoradicoAdecimal3 (show (10^50000)))) -- 213237 -- (0.93 secs, 2,635,792,192 bytes) -- λ> length (show (factoradicoAdecimal4 (show (10^50000)))) -- 213237 -- (0.43 secs, 2,636,996,848 bytes)
2. Soluciones en Python
from itertools import count, takewhile from math import factorial from typing import Iterator from hypothesis import given from hypothesis import strategies as st # 1ª definición de factoradicoAdecimal # ==================================== # caracterAentero(c) es la posición del carácter c en la lista de # caracteres ['0', '1',..., '9', 'A', 'B',..., 'Z']. Por ejemplo, # caracterAentero('0') == 0 # caracterAentero('1') == 1 # caracterAentero('9') == 9 # caracterAentero('A') == 10 # caracterAentero('B') == 11 # caracterAentero('Z') == 35 def caracterAentero(c: str) -> int: if c.isdigit(): return int(c) return ord(c) - ord('A') + 10 def factoradicoAdecimal1(cs: str) -> int: xs = map(caracterAentero, cs) n = len(cs) ys = reversed([factorial(k) for k in range(n)]) return sum((x * y for (x, y) in zip(xs, ys))) # 2ª definición de factoradicoAdecimal # ==================================== # caracteres es la cadena de los caracteres. caracteres: str = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' # caracteresEnteros es el diccionario cuyas claves son los caracteres y # los valores son los números de 0 a 35. caracteresEnteros: dict[str, int] = {c: i for i, c in enumerate(caracteres)} # caracterAentero2(c) es la posición del carácter c en la lista de # caracteres ['0', '1',..., '9', 'A', 'B',..., 'Z']. Por ejemplo, # caracterAentero2('0') == 0 # caracterAentero2('1') == 1 # caracterAentero2('9') == 9 # caracterAentero2('A') == 10 # caracterAentero2('B') == 11 # caracterAentero2('Z') == 35 def caracterAentero2(c: str) -> int: return caracteresEnteros[c] def factoradicoAdecimal2(cs: str) -> int: xs = map(caracterAentero2, cs) n = len(cs) ys = reversed([factorial(k) for k in range(n)]) return sum((x * y for (x, y) in zip(xs, ys))) # 3ª definición de factoradicoAdecimal # ==================================== # caracterAentero3(c) es la posición del carácter c en la lista de # caracteres ['0', '1',..., '9', 'A', 'B',..., 'Z']. Por ejemplo, # caracterAentero3('0') == 0 # caracterAentero3('1') == 1 # caracterAentero3('9') == 9 # caracterAentero3('A') == 10 # caracterAentero3('B') == 11 # caracterAentero3('Z') == 35 def caracterAentero3(c: str) -> int: return len(list(takewhile(lambda x: x != c, caracteres))) def factoradicoAdecimal3(cs: str) -> int: return sum(x * y for x, y in zip([factorial(k) for k in range(len(cs))], reversed(list(map(caracterAentero3, cs))))) # 1ª definición de decimalAfactoradico # ==================================== # enteroAcaracter(k) es el k-ésimo elemento de la lista # ['0', '1',..., '9', 'A', 'B',..., 'Z']. . Por ejemplo, # enteroAcaracter(0) == '0' # enteroAcaracter(1) == '1' # enteroAcaracter(9) == '9' # enteroAcaracter(10) == 'A' # enteroAcaracter(11) == 'B' # enteroAcaracter(35) == 'Z' def enteroAcaracter(k: int) -> str: return caracteres[k] # facts() es la lista de los factoriales. Por ejemplo, # >>> list(takewhile(lambda x : x < 900, facts())) # [1, 1, 2, 6, 24, 120, 720] def facts() -> Iterator[int]: return (factorial(n) for n in count()) def decimalAfactoradico1(n: int) -> str: def aux(m: int, xs: list[int]) -> str: if m == 0: return "0" * len(xs) y, *ys = xs print(m, y, m // y) return enteroAcaracter(m // y) + aux(m % y, ys) return aux(n, list(reversed(list(takewhile(lambda x : x <= n, facts()))))) # 2ª definición de decimalAfactoradico # ==================================== # enterosCaracteres es el diccionario cuyas claves son los número de 0 # a 35 y las claves son los caracteres. enterosCaracteres: dict[int, str] = dict(enumerate(caracteres)) # enteroAcaracter2(k) es el k-ésimo elemento de la lista # ['0', '1',..., '9', 'A', 'B',..., 'Z']. . Por ejemplo, # enteroAcaracter2(0) == '0' # enteroAcaracter2(1) == '1' # enteroAcaracter2(9) == '9' # enteroAcaracter2(10) == 'A' # enteroAcaracter2(11) == 'B' # enteroAcaracter2(35) == 'Z' def enteroAcaracter2(k: int) -> str: return enterosCaracteres[k] def decimalAfactoradico2(n: int) -> str: def aux(m: int, xs: list[int]) -> str: if m == 0: return "0" * len(xs) y, *ys = xs return enteroAcaracter2(m // y) + aux(m % y, ys) return aux(n, list(reversed(list(takewhile(lambda x : x <= n, facts()))))) # 3ª definición de decimalAfactoradico # ==================================== # enteroAcaracter3(k) es el k-ésimo elemento de la lista # ['0', '1',..., '9', 'A', 'B',..., 'Z']. . Por ejemplo, # enteroAcaracter3(0) == '0' # enteroAcaracter3(1) == '1' # enteroAcaracter3(9) == '9' # enteroAcaracter3(10) == 'A' # enteroAcaracter3(11) == 'B' # enteroAcaracter3(35) == 'Z' def enteroAcaracter3(n: int) -> str: return caracteres[n] def decimalAfactoradico3(n: int) -> str: def aux(m: int, xs: list[int]) -> str: if m == 0: return "0" * len(xs) y, *ys = xs return enteroAcaracter3(m // y) + aux(m % y, ys) return aux(n, list(reversed(list(takewhile(lambda x : x <= n, facts()))))) # Verificación # ============ def test_factoradico() -> None: for factoradicoAdecimal in [factoradicoAdecimal1, factoradicoAdecimal2, factoradicoAdecimal3]: assert factoradicoAdecimal("341010") == 463 assert factoradicoAdecimal("2441000") == 2022 assert factoradicoAdecimal("A0000000000") == 36288000 for decimalAfactoradico in [decimalAfactoradico1, decimalAfactoradico2, decimalAfactoradico3]: assert decimalAfactoradico(463) == "341010" assert decimalAfactoradico(2022) == "2441000" assert decimalAfactoradico(36288000) == "A0000000000" print("Verificado") # La verificación es # >>> test_factoradico() # Verificado # Propiedad de inverso # ==================== @given(st.integers(min_value=0, max_value=1000)) def test_factoradico_equiv(n: int) -> None: assert factoradicoAdecimal1(decimalAfactoradico1(n)) == n assert factoradicoAdecimal2(decimalAfactoradico3(n)) == n assert factoradicoAdecimal3(decimalAfactoradico3(n)) == n # La comprobación es # >>> test_factoradico_equiv() # >>>
3. Soluciones en Common Lisp
(ql:quickload "fiveam" :silent t) (defpackage :sistema-factoradico-de-numeracion (:use :cl :fiveam)) (in-package :sistema-factoradico-de-numeracion) ;;; Listas infinitas ;;; ================ ;;; En las soluciones se usarán las siguientes funciones para trabajar ;;; con listas infinitas (ver "Streams in Common Lisp" ;;; https://tinyurl.com/266685bz). ;;; (delay e) retrasa la evaluación de la función e. Por ejemplo, ;;; > (delay (+ 2 3)) ;;; #<FUNCTION (LAMBDA ()) {5363B46B}> (defmacro delay (e) `(lambda () ,e)) ;;; (force e) evalúa la expresión retrasada retrasada e. Por ejemplo, ;;; > (force (retrasa (+ 2 3))) ;;; 5 (defun force (e) (funcall e)) ;;; (s-cons x y) crea una lista perezosa cuyo primer elemento es x ;;; y el resto es la expresión y retrasada. Por ejemplo, ;;; > (setf ej-lp (s-cons 1 (s-cons 2 nil))) ;;; (1 . #<FUNCTION (LAMBDA ()) {5368A37B}>) (defmacro s-cons (x y) `(cons ,x (delay ,y))) ;;; (s-first s) es el primer elemento de la lista perezosa s. Por ;;; ejemplo, ;;; > (s-first (s-cons 1 (s-cons 2 nil))) ;;; 1 (defun s-first (s) (car s)) ;;; (s-rest s) es el resto de la lista perezosa s. Por ejmplo, ;;; > (s-rest (s-cons 1 (s-cons 2 nil))) ;;; (2 . #<FUNCTION (LAMBDA ()) {5363B00B}>) (defun s-rest (s) (force (cdr s))) ;;; (s-take n s) es la lista de los n primeros elementos de la lista ;;; infinita s. ;;; > (s-take 10 (factoriales)) ;;; (1 1 2 6 24) (defun s-take (n s) (if (zerop n) nil (cons (s-first s) (s-take (1- n) (s-rest s))))) ;;; (factoriales) es la lista de los factoriales. Por ejemplo, ;;; > (s-take 10 (factoriales)) ;;; (1 1 2 6 24 120 720 5040 40320 362880) (defun factoriales (&optional (n 0) (f 1)) (s-cons f (factoriales (1+ n) (* (1+ n) f)))) ;;; 1ª definición de factoradico-a-decimal ;;; ====================================== ;;; caracteres es la lista de caracteres (defparameter *caracteres* (concatenate 'string "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) ;;; (caracter-a-entero c) es la posición del carácter c en la lista de ;;; caracteres. Por ejemplo, ;;; (caracter-a-entero #\0) == 0 ;;; (caracter-a-entero #\1) == 1 ;;; (caracter-a-entero #\9) == 9 ;;; (caracter-a-entero #\A) == 10 ;;; (caracter-a-entero #\B) == 11 ;;; (caracter-a-entero #\Z) == 35 (defun caracter-a-entero (c) (position c *caracteres*)) (defun factoradico-a-decimal-1 (cs) (let* ((xs (map 'list #'caracter-a-entero cs)) (n (length cs)) (ys (reverse (s-take n (factoriales))))) (reduce #'+ (mapcar #'* xs ys)))) ;;; 2ª definición de factoradico-a-decimal ;;; ====================================== ;;; *caracteres-enteros* es la tabla que asigna a cada carácter su ;;; posición correspondiente, desde 0 hasta 35. Por ejemplo, ;;; > (gethash #\A *caracteres-enteros*) ;;; 10 (defparameter *caracteres-enteros* (let ((tabla (make-hash-table))) (loop for i from 0 to 35 for c across *caracteres* do (setf (gethash c tabla) i)) tabla)) ;;; (caracter-a-entero-2 c) es la posición del carácter c en la lista de ;;; caracteres. Por ejemplo, ;;; (caracter-a-entero-2 #\0) == 0 ;;; (caracter-a-entero-2 #\1) == 1 ;;; (caracter-a-entero-2 #\9) == 9 ;;; (caracter-a-entero-2 #\A) == 10 ;;; (caracter-a-entero-2 #\B) == 11 ;;; (caracter-a-entero-2 #\Z) == 35 (defun caracter-a-entero-2 (c) (gethash c *caracteres-enteros*)) (defun factoradico-a-decimal-2 (cs) (let* ((xs (map 'list #'caracter-a-entero-2 cs)) (n (length cs)) (ys (reverse (s-take n (factoriales))))) (reduce #'+ (mapcar #'* xs ys)))) ;;; 3ª definición de factoradico-a-decimal ;;; ====================================== ;;; (caracter-a-entero-3 c) es la posición del carácter c en la lista de ;;; caracteres. Por ejemplo, ;;; (caracter-a-entero-3 #\0) == 0 ;;; (caracter-a-entero-3 #\1) == 1 ;;; (caracter-a-entero-3 #\9) == 9 ;;; (caracter-a-entero-3 #\A) == 10 ;;; (caracter-a-entero-3 #\B) == 11 ;;; (caracter-a-entero-3 #\Z) == 35 (defun caracter-a-entero-3 (c) (loop for x across *caracteres* for i from 0 when (char= x c) return i)) (defun factoradico-a-decimal-3 (cs) (reduce #'+ (mapcar #'* (s-take (length cs) (factoriales)) (reverse (map 'list #'caracter-a-entero-3 cs))))) ;;; 1ª definición de decimalAfactoradico ;;; ==================================== ;;; (entero-a-caracter k) es el k-ésimo elemento de la lista ;;; de los caracteres. Por ejemplo, ;;; (entero-a-caracter 0) == #\0 ;;; (entero-a-caracter 1) == #\1 ;;; (entero-a-caracter 9) == #\9 ;;; (entero-a-caracter 10) == #\A ;;; (entero-a-caracter 11) == #\B ;;; (entero-a-caracter 35) == #\Z (defun entero-a-caracter (k) (char *caracteres* k)) ;;; (s-take-while p s) es la lista de los primeros elementos de la lista ;;; infinita s que verifica el predicado p. Por ejemplo, ;;; > (s-take-while (lambda (x) (< x 30)) (factoriales)) ;;; (0 1 1 2 3 5 8 13 21) (defun s-take-while (p s) (if (not (funcall p (s-first s))) nil (cons (s-first s) (s-take-while p (s-rest s))))) (defun decimal-a-factoradico-1 (n) (labels ((aux (m xs) (if (= m 0) (mapcar (constantly #\0) xs) (let ((x (first xs))) (cons (entero-a-caracter (truncate m x)) (aux (mod m x) (rest xs))))))) (coerce (aux n (reverse (s-take-while (lambda (x) (<= x n)) (factoriales)))) 'string))) ;;; 2ª definición de decimalAfactoradico ;;; ==================================== ;;; *enteros-caracteres* es la tabla cuyas claves son los número de 0 ;;; a 35 y las claves son los caracteres. Por ejemplo, ;;; > (gethash 10 *enteros-caracteres*) ;;; #\A (defparameter *enteros-caracteres* (let ((hash (make-hash-table))) (loop for i from 0 to 35 for c across *caracteres* do (setf (gethash i hash) c)) hash)) ;;; (entero-a-caracter-2 k) es el k-ésimo elemento de la lista ;;; ['0', '1',..., '9', 'A', 'B',..., 'Z']. . Por ejemplo, ;;; (entero-a-caracter-2 0) == #\0 ;;; (entero-a-caracter-2 1) == #\1 ;;; (entero-a-caracter-2 9) == #\9 ;;; (entero-a-caracter-2 10) == #\A ;;; (entero-a-caracter-2 11) == #\B ;;; (entero-a-caracter-2 35) == #\Z (defun entero-a-caracter-2 (k) (gethash k *enteros-caracteres*)) (defun decimal-a-factoradico-2 (n) (labels ((aux (m xs) (if (= m 0) (mapcar (constantly #\0) xs) (let ((x (first xs))) (cons (entero-a-caracter-2 (truncate m x)) (aux (mod m x) (rest xs))))))) (coerce (aux n (reverse (s-take-while (lambda (x) (<= x n)) (factoriales)))) 'string))) ;;; 3ª definición de decimalAfactoradico ;;; ==================================== ;;; (entero-a-caracter-3 k) es el k-ésimo elemento de la lista de los ;;; caracteres. Por ejemplo, ;;; (entero-a-caracter-3 0) == #\0 ;;; (entero-a-caracter-3 1) == #\1 ;;; (entero-a-caracter-3 9) == #\9 ;;; (entero-a-caracter-3 10) == #\A ;;; (entero-a-caracter-3 11) == #\B ;;; (entero-a-caracter-3 35) == #\Z (defun entero-a-caracter-3 (k) (char *caracteres* k)) (defun decimal-a-factoradico-3 (n) (labels ((aux (m xs) (if (= m 0) (mapcar (constantly #\0) xs) (let ((x (first xs))) (cons (entero-a-caracter-3 (truncate m x)) (aux (mod m x) (rest xs))))))) (coerce (aux n (reverse (s-take-while (lambda (x) (<= x n)) (factoriales)))) 'string))) ;;; Verificación ;;; ============ (test factoradico-a-decimal (mapc (lambda (factoradico-a-decimal) (is (= (funcall factoradico-a-decimal "341010") 463)) (is (= (funcall factoradico-a-decimal "2441000") 2022)) (is (= (funcall factoradico-a-decimal "A0000000000") 36288000))) '(factoradico-a-decimal-1 factoradico-a-decimal-2 factoradico-a-decimal-3))) (test decimal-a-factoradico (mapc (lambda (decimal-a-factoradico) (is (equal (funcall decimal-a-factoradico 463) "341010")) (is (equal (funcall decimal-a-factoradico 2022) "2441000")) (is (equal (funcall decimal-a-factoradico 36288000) "A0000000000"))) '(decimal-a-factoradico-1 decimal-a-factoradico-2 decimal-a-factoradico-3 ))) (defun verifica () (run 'factoradico-a-decimal) (run 'decimal-a-factoradico)) ;;; La verificación es ;;; (verifica) ;;; ;;; Running test FACTORADICO-A-DECIMAL ........ ;;; Propiedad de inverso ;;; ==================== ;;; La propiedad es (test factoradico (for-all ((n (gen-integer :min 0))) (is (= (factoradico-a-decimal-1 (decimal-a-factoradico-1 n)) n)) (is (= (factoradico-a-decimal-2 (decimal-a-factoradico-2 n)) n)) (is (= (factoradico-a-decimal-3 (decimal-a-factoradico-3 n)) n)))) (defun comprueba () (run 'factoradico)) ;;; La comprobación es ;;; > (comprueba) ;;; ;;; Running test SUMA-CADENAS-EQUIV ...