Esta semana he publicado en Exercitium las soluciones de los siguientes problemas:
- 1. El tipo abstracto de datos de los polinomios
- 2. Transformaciones entre las representaciones dispersa y densa de polinomios
- 3. Transformaciones entre polinomios y listas dispersas
- 4. Coeficiente del término de grado k de un polinomio
- 5. Transformaciones entre polinomios y listas densas
A continuación se muestran las soluciones.
1. El tipo abstracto de datos de los polinomios
1. El tipo abstracto de datos de los polinomios
Un polinomio es una expresión matemática compuesta por una suma de términos, donde cada término es el producto de un coeficiente y una variable elevada a una potencia. Por ejemplo, el polinomio 3x^2+2x-1 tiene un término de segundo grado (3x^2), un término de primer grado (2x) y un término constante (-1).
Las operaciones que definen al tipo abstracto de datos (TAD) de los polinomios (cuyos coeficientes son del tipo a) son las siguientes:
polCero :: Polinomio a esPolCero :: Polinomio a -> Bool consPol :: (Num a, Eq a) => Int -> a -> Polinomio a -> Polinomio a grado :: Polinomio a -> Int coefLider :: Num a => Polinomio a -> a restoPol :: (Num a, Eq a) => Polinomio a -> Polinomio a |
tales que
- polCero es el polinomio cero.
- (esPolCero p) se verifica si p es el polinomio cero.
- (consPol n b p) es el polinomio bx^n+p
- (grado p) es el grado del polinomio p.
- (coefLider p) es el coeficiente líder del polinomio p.
- (restoPol p) es el resto del polinomio p.
Por ejemplo, el polinomio
3*x^4 + -5*x^2 + 3 |
se representa por
consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero)) |
Las operaciones tienen que verificar las siguientes propiedades:
- esPolCero polCero
- n > grado p && b /= 0 ==> not (esPolCero (consPol n b p))
- consPol (grado p) (coefLider p) (restoPol p) == p
- n > grado p && b /= 0 ==> grado (consPol n b p) == n
- n > grado p && b /= 0 ==> coefLider (consPol n b p) == b
- n > grado p && b /= 0 ==> restoPol (consPol n b p) == p
2. Los polinomios en Haskell
2.1. El tipo abstracto de datos de los polinomios en Haskell
El TAD de los polinomios se encuentra en el módulo Polinomio.hs cuyo contenido es el siguiente:
module TAD.Polinomio ( Polinomio, polCero, -- Polinomio a esPolCero, -- Polinomio a -> Bool consPol, -- (Num a, Eq a) => Int -> a -> Polinomio a -> Polinomio a grado, -- Polinomio a -> Int coefLider, -- Num a => Polinomio a -> a restoPol -- (Num a, Eq a) => Polinomio a -> Polinomio a ) where import TAD.PolRepTDA -- import TAD.PolRepDensa -- import TAD.PolRepDispersa |
Para usar el TAD hay que usar una implementación concreta. En principio, consideraremos las siguientes:
- mediante tipo de dato algebraico,
- mediante listas densas y
- mediante listas dispersas.
Hay que elegir la que se desee utilizar, descomentándola y comentando las otras.
2.2. Implementación de los polinomios mediante tipos de datos algebraicos
Representamos un polinomio mediante los constructores ConsPol y
PolCero. Por ejemplo, el polinomio
6x^4 -5x^2 + 4x -7 |
se representa por
ConsPol 4 6 (ConsPol 2 (-5) (ConsPol 1 4 (ConsPol 0 (-7) PolCero))) |
La implementación se encuentra en el módulo PolRepTDA.hs cuyo contenido es el siguiente:
{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module TAD.PolRepTDA ( Polinomio, polCero, -- Polinomio a esPolCero, -- Polinomio a -> Bool consPol, -- (Num a, Eq a)) => Int -> a -> Polinomio a -> Polinomio a grado, -- Polinomio a -> Int coefLider, -- Num t => Polinomio t -> t restoPol -- Polinomio t -> Polinomio t ) where import Test.QuickCheck -- Polinomio como tipo de dato algebra data Polinomio a = PolCero | ConsPol Int a (Polinomio a) deriving Eq -- (escribePol p) es la cadena correspondiente al polinomio p. Por -- ejemplo, -- λ> escribePol (consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero))) -- "3*x^4 + -5*x^2 + 3" escribePol :: (Num a, Show a, Eq a) => Polinomio a -> String escribePol PolCero = "0" escribePol (ConsPol 0 b PolCero) = show b escribePol (ConsPol 0 b p) = concat [show b, " + ", escribePol p] escribePol (ConsPol 1 b PolCero) = show b ++ "*x" escribePol (ConsPol 1 b p) = concat [show b, "*x + ", escribePol p] escribePol (ConsPol n 1 PolCero) = "x^" ++ show n escribePol (ConsPol n b PolCero) = concat [show b, "*x^", show n] escribePol (ConsPol n 1 p) = concat ["x^", show n, " + ", escribePol p] escribePol (ConsPol n b p) = concat [show b, "*x^", show n, " + ", escribePol p] -- Procedimiento de escritura de polinomios. instance (Num a, Show a, Eq a) => Show (Polinomio a) where show = escribePol -- Ejemplos de polinomios con coeficientes enteros: ejPol1, ejPol2, ejPol3 :: Polinomio Int ejPol1 = consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero)) ejPol2 = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero)) ejPol3 = consPol 4 6 (consPol 1 2 polCero) -- Comprobación de escritura: -- > ejPol1 -- 3*x^4 + -5*x^2 + 3 -- > ejPol2 -- x^5 + 5*x^2 + 4*x -- > ejPol3 -- 6*x^4 + 2*x -- polCero es el polinomio cero. Por ejemplo, -- > polCero -- 0 polCero :: Polinomio a polCero = PolCero -- (esPolCero p) se verifica si p es el polinomio cero. Por ejemplo, -- esPolCero polCero == True -- esPolCero ejPol1 == False esPolCero :: Polinomio a -> Bool esPolCero PolCero = True esPolCero _ = False -- (consPol n b p) es el polinomio bx^n+p. Por ejemplo, -- ejPol2 == x^5 + 5*x^2 + 4*x -- consPol 3 0 ejPol2 == x^5 + 5*x^2 + 4*x -- consPol 3 2 polCero == 2*x^3 -- consPol 6 7 ejPol2 == 7*x^6 + x^5 + 5*x^2 + 4*x -- consPol 4 7 ejPol2 == x^5 + 7*x^4 + 5*x^2 + 4*x -- consPol 5 7 ejPol2 == 8*x^5 + 5*x^2 + 4*x consPol :: (Num a, Eq a) => Int -> a -> Polinomio a -> Polinomio a consPol _ 0 p = p consPol n b PolCero = ConsPol n b PolCero consPol n b (ConsPol m c p) | n > m = ConsPol n b (ConsPol m c p) | n < m = ConsPol m c (consPol n b p) | b+c == 0 = p | otherwise = ConsPol n (b+c) p -- (grado p) es el grado del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- grado ejPol3 == 4 grado :: Polinomio a -> Int grado PolCero = 0 grado (ConsPol n _ _) = n -- (coefLider p) es el coeficiente líder del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- coefLider ejPol3 == 6 coefLider :: Num t => Polinomio t -> t coefLider PolCero = 0 coefLider (ConsPol _ b _) = b -- (restoPol p) es el resto del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- restoPol ejPol3 == 2*x -- ejPol2 == x^5 + 5*x^2 + 4*x -- restoPol ejPol2 == 5*x^2 + 4*x restoPol :: Polinomio t -> Polinomio t restoPol PolCero = PolCero restoPol (ConsPol _ _ p) = p -- Generador de polinomios -- -- ======================= -- genPolinomio es un generador de polinomios. Por ejemplo, -- λ> sample (genPol 1) -- 7*x^9 + 9*x^8 + 10*x^7 + -14*x^5 + -15*x^2 + -10 -- -4*x^8 + 2*x -- -8*x^9 + 4*x^8 + 2*x^6 + 4*x^5 + -6*x^4 + 5*x^2 + -8*x -- -9*x^9 + x^5 + -7 -- 8*x^10 + -9*x^7 + 7*x^6 + 9*x^5 + 10*x^3 + -1*x^2 -- 7*x^10 + 5*x^9 + -5 -- -8*x^10 + -7 -- -5*x -- 5*x^10 + 4*x^4 + -3 -- 3*x^3 + -4 -- 10*x genPol :: (Num a, Arbitrary a, Eq a) => Int -> Gen (Polinomio a) genPol 0 = return polCero genPol _ = do n <- choose (0,10) b <- arbitrary p <- genPol (div n 2) return (consPol n b p) instance (Num a, Arbitrary a, Eq a) => Arbitrary (Polinomio a) where arbitrary = sized genPol -- Propiedades de los polinomios -- ============================= -- polCero es el polinomio cero. prop_polCero_es_cero :: Bool prop_polCero_es_cero = esPolCero polCero -- Si n es mayor que el grado de p y b no es cero, entonces -- (consPol n b p) es un polinomio distinto del cero. prop_consPol_no_cero :: Int -> Int -> Polinomio Int -> Property prop_consPol_no_cero n b p = n > grado p && b /= 0 ==> not (esPolCero (consPol n b p)) -- (consPol (grado p) (coefLider p) (restoPol p)) es igual a p. prop_consPol :: Polinomio Int -> Bool prop_consPol p = consPol (grado p) (coefLider p) (restoPol p) == p -- Si n es mayor que el grado de p y b no es cero, entonces -- el grado de (consPol n b p) es n. prop_grado :: Int -> Int -> Polinomio Int -> Property prop_grado n b p = n > grado p && b /= 0 ==> grado (consPol n b p) == n -- Si n es mayor que el grado de p y b no es cero, entonces -- el coeficiente líder de (consPol n b p) es b. prop_coefLider :: Int -> Int -> Polinomio Int -> Property prop_coefLider n b p = n > grado p && b /= 0 ==> coefLider (consPol n b p) == b -- Si n es mayor que el grado de p y b no es cero, entonces -- el resto de (consPol n b p) es p. prop_restoPol :: Int -> Int -> Polinomio Int -> Property prop_restoPol n b p = n > grado p && b /= 0 ==> restoPol (consPol n b p) == p -- Verificación -- ============ return [] verificaPol :: IO Bool verificaPol = $quickCheckAll -- La verificación es -- λ> verificaPol -- === prop_polCero_es_cero from PolPropiedades.hs:53 === -- +++ OK, passed 1 test. -- -- === prop_consPol_no_cero from PolPropiedades.hs:63 === -- +++ OK, passed 100 tests; 251 discarded. -- -- === prop_consPol from PolPropiedades.hs:73 === -- +++ OK, passed 100 tests. -- -- === prop_grado from PolPropiedades.hs:83 === -- +++ OK, passed 100 tests; 321 discarded. -- -- === prop_coefLider from PolPropiedades.hs:94 === -- +++ OK, passed 100 tests; 340 discarded. -- -- === prop_restoPol from PolPropiedades.hs:105 === -- +++ OK, passed 100 tests; 268 discarded. -- -- True |
2.3. Implementación de polinomios mediante listas densas
Representaremos un polinomio por la lista de sus coeficientes ordenados en orden decreciente según el grado. Por ejemplo, el polinomio
6x^4 -5x^2 + 4x -7 |
se representa por
[6,0,-2,4,-7] |
En la representación se supone que, si la lista no es vacía, su primer elemento es distinto de cero.
La implementación se encuentra en el módulo PolRepDensa.hs cuyo contenido es el siguiente:
{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module TAD.PolRepDensa ( Polinomio, polCero, -- Polinomio a esPolCero, -- Polinomio a -> Bool consPol, -- (Num a, Eq a) => Int -> a -> Polinomio a -> Polinomio a grado, -- Polinomio a -> Int coefLider, -- Num a => Polinomio a -> a restoPol -- (Num a, Eq a) => Polinomio a -> Polinomio a ) where import Test.QuickCheck newtype Polinomio a = Pol [a] deriving Eq -- (escribePol p) es la cadena correspondiente al polinomio p. Por -- ejemplo, -- λ> escribePol (consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero))) -- "3*x^4 + -5*x^2 + 3" escribePol :: (Num a, Show a, Eq a) => Polinomio a -> String escribePol pol | esPolCero pol = "0" | n == 0 && esPolCero p = show a | n == 0 = concat [show a, " + ", escribePol p] | n == 1 && esPolCero p = show a ++ "*x" | n == 1 = concat [show a, "*x + ", escribePol p] | a == 1 && esPolCero p = "x^" ++ show n | esPolCero p = concat [show a, "*x^", show n] | a == 1 = concat ["x^", show n, " + ", escribePol p] | otherwise = concat [show a, "*x^", show n, " + ", escribePol p] where n = grado pol a = coefLider pol p = restoPol pol -- Procedimiento de escritura de polinomios. instance (Num a, Show a, Eq a) => Show (Polinomio a) where show = escribePol -- Ejemplos de polinomios con coeficientes enteros: ejPol1, ejPol2, ejPol3 :: Polinomio Int ejPol1 = consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero)) ejPol2 = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero)) ejPol3 = consPol 4 6 (consPol 1 2 polCero) -- Comprobación de escritura: -- > ejPol1 -- 3*x^4 + -5*x^2 + 3 -- > ejPol2 -- x^5 + 5*x^2 + 4*x -- > ejPol3 -- 6*x^4 + 2*x -- polCero es el polinomio cero. Por ejemplo, -- λ> polCero -- 0 polCero :: Polinomio a polCero = Pol [] -- (esPolCero p) se verifica si p es el polinomio cero. Por ejemplo, -- esPolCero polCero == True -- esPolCero ejPol1 == False esPolCero :: Polinomio a -> Bool esPolCero (Pol []) = True esPolCero _ = False -- (consPol n b p) es el polinomio bx^n+p. Por ejemplo, -- ejPol2 == x^5 + 5*x^2 + 4*x -- consPol 3 0 ejPol2 == x^5 + 5*x^2 + 4*x -- consPol 3 2 polCero == 2*x^3 -- consPol 6 7 ejPol2 == 7*x^6 + x^5 + 5*x^2 + 4*x -- consPol 4 7 ejPol2 == x^5 + 7*x^4 + 5*x^2 + 4*x -- consPol 5 7 ejPol2 == 8*x^5 + 5*x^2 + 4*x consPol :: (Num a, Eq a) => Int -> a -> Polinomio a -> Polinomio a consPol _ 0 p = p consPol n b p@(Pol xs) | esPolCero p = Pol (b : replicate n 0) | n > m = Pol (b : replicate (n-m-1) 0 ++ xs) | n < m = consPol m c (consPol n b (restoPol p)) | b+c == 0 = Pol (dropWhile (==0) (tail xs)) | otherwise = Pol ((b+c):tail xs) where c = coefLider p m = grado p -- (grado p) es el grado del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- grado ejPol3 == 4 grado :: Polinomio a -> Int grado (Pol []) = 0 grado (Pol xs) = length xs - 1 -- (coefLider p) es el coeficiente líder del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- coefLider ejPol3 == 6 coefLider :: Num t => Polinomio t -> t coefLider (Pol []) = 0 coefLider (Pol (a:_)) = a -- (restoPol p) es el resto del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- restoPol ejPol3 == 2*x -- ejPol2 == x^5 + 5*x^2 + 4*x -- restoPol ejPol2 == 5*x^2 + 4*x restoPol :: (Num t, Eq t) => Polinomio t -> Polinomio t restoPol (Pol []) = polCero restoPol (Pol [_]) = polCero restoPol (Pol (_:b:as)) | b == 0 = Pol (dropWhile (==0) as) | otherwise = Pol (b:as) -- Generador de polinomios -- ======================= -- genPolinomio es un generador de polinomios. Por ejemplo, -- λ> sample (genPol 1) -- 7*x^9 + 9*x^8 + 10*x^7 + -14*x^5 + -15*x^2 + -10 -- -4*x^8 + 2*x -- -8*x^9 + 4*x^8 + 2*x^6 + 4*x^5 + -6*x^4 + 5*x^2 + -8*x -- -9*x^9 + x^5 + -7 -- 8*x^10 + -9*x^7 + 7*x^6 + 9*x^5 + 10*x^3 + -1*x^2 -- 7*x^10 + 5*x^9 + -5 -- -8*x^10 + -7 -- -5*x -- 5*x^10 + 4*x^4 + -3 -- 3*x^3 + -4 -- 10*x genPol :: (Num a, Arbitrary a, Eq a) => Int -> Gen (Polinomio a) genPol 0 = return polCero genPol _ = do n <- choose (0,10) b <- arbitrary p <- genPol (div n 2) return (consPol n b p) instance (Num a, Arbitrary a, Eq a) => Arbitrary (Polinomio a) where arbitrary = sized genPol -- Propiedades de los polinomios -- ============================= -- polCero es el polinomio cero. prop_polCero_es_cero :: Bool prop_polCero_es_cero = esPolCero polCero -- Si n es mayor que el grado de p y b no es cero, entonces -- (consPol n b p) es un polinomio distinto del cero. prop_consPol_no_cero :: Int -> Int -> Polinomio Int -> Property prop_consPol_no_cero n b p = n > grado p && b /= 0 ==> not (esPolCero (consPol n b p)) -- (consPol (grado p) (coefLider p) (restoPol p)) es igual a p. prop_consPol :: Polinomio Int -> Bool prop_consPol p = consPol (grado p) (coefLider p) (restoPol p) == p -- Si n es mayor que el grado de p y b no es cero, entonces -- el grado de (consPol n b p) es n. prop_grado :: Int -> Int -> Polinomio Int -> Property prop_grado n b p = n > grado p && b /= 0 ==> grado (consPol n b p) == n -- Si n es mayor que el grado de p y b no es cero, entonces -- el coeficiente líder de (consPol n b p) es b. prop_coefLider :: Int -> Int -> Polinomio Int -> Property prop_coefLider n b p = n > grado p && b /= 0 ==> coefLider (consPol n b p) == b -- Si n es mayor que el grado de p y b no es cero, entonces -- el resto de (consPol n b p) es p. prop_restoPol :: Int -> Int -> Polinomio Int -> Property prop_restoPol n b p = n > grado p && b /= 0 ==> restoPol (consPol n b p) == p -- Verificación -- ============ return [] verificaPol :: IO Bool verificaPol = $quickCheckAll -- La verificación es -- λ> verificaPol -- === prop_polCero_es_cero from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDensa.hs:157 === -- +++ OK, passed 1 test. -- -- === prop_consPol_no_cero from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDensa.hs:163 === -- +++ OK, passed 100 tests; 274 discarded. -- -- === prop_consPol from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDensa.hs:169 === -- +++ OK, passed 100 tests. -- -- === prop_grado from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDensa.hs:175 === -- +++ OK, passed 100 tests; 297 discarded. -- -- === prop_coefLider from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDensa.hs:182 === -- +++ OK, passed 100 tests; 248 discarded. -- -- === prop_restoPol from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDensa.hs:189 === -- +++ OK, passed 100 tests; 322 discarded. -- -- True |
2.4. Implementación de polinomios mediante listas dispersas
Representaremos un polinomio mediante una lista de pares (grado,coef),
ordenados en orden decreciente según el grado. Por ejemplo, el polinomio
6x^4 -5x^2 + 4x -7 |
se representa por
[(4,6),(2,-5),(1,4),(0,-7)] |
En la representación se supone que los primeros elementos de los pares forman una sucesión estrictamente decreciente y que los segundos elementos son distintos de cero.
La implementación se encuentra en el módulo PolRepDispersa.hs cuyo contenido es el siguiente:
{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module TAD.PolRepDispersa ( Polinomio, polCero, -- Polinomio a esPolCero, -- Num a => Polinomio a -> Bool consPol, -- Num a => Int -> a -> Polinomio a -> Polinomio a grado, -- Polinomio a -> Int coefLider, -- Num a => Polinomio a -> a restoPol -- Polinomio a -> Polinomio a ) where import Test.QuickCheck newtype Polinomio a = Pol [(Int,a)] deriving Eq -- (escribePol p) es la cadena correspondiente al polinomio p. Por -- ejemplo, -- λ> escribePol (consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero))) -- "3*x^4 + -5*x^2 + 3" escribePol :: (Num a, Show a, Eq a) => Polinomio a -> String escribePol pol | esPolCero pol = "0" | n == 0 && esPolCero p = show a | n == 0 = concat [show a, " + ", escribePol p] | n == 1 && esPolCero p = show a ++ "*x" | n == 1 = concat [show a, "*x + ", escribePol p] | a == 1 && esPolCero p = "x^" ++ show n | esPolCero p = concat [show a, "*x^", show n] | a == 1 = concat ["x^", show n, " + ", escribePol p] | otherwise = concat [show a, "*x^", show n, " + ", escribePol p] where n = grado pol a = coefLider pol p = restoPol pol -- Procedimiento de escritura de polinomios. instance (Num a, Show a, Eq a) => Show (Polinomio a) where show = escribePol -- Ejemplos de polinomios con coeficientes enteros: ejPol1, ejPol2, ejPol3 :: Polinomio Int ejPol1 = consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero)) ejPol2 = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero)) ejPol3 = consPol 4 6 (consPol 1 2 polCero) -- Comprobación de escritura: -- > ejPol1 -- 3*x^4 + -5*x^2 + 3 -- > ejPol2 -- x^5 + 5*x^2 + 4*x -- > ejPol3 -- 6*x^4 + 2*x -- polCero es el polinomio cero. Por ejemplo, -- λ> polCero -- 0 polCero :: Num a => Polinomio a polCero = Pol [] -- (esPolCero p) se verifica si p es el polinomio cero. Por ejemplo, -- esPolCero polCero == True -- esPolCero ejPol1 == False esPolCero :: Num a => Polinomio a -> Bool esPolCero (Pol []) = True esPolCero _ = False -- (consPol n b p) es el polinomio bx^n+p. Por ejemplo, -- ejPol2 == x^5 + 5*x^2 + 4*x -- consPol 3 0 ejPol2 == x^5 + 5*x^2 + 4*x -- consPol 3 2 polCero == 2*x^3 -- consPol 6 7 ejPol2 == 7*x^6 + x^5 + 5*x^2 + 4*x -- consPol 4 7 ejPol2 == x^5 + 7*x^4 + 5*x^2 + 4*x -- consPol 5 7 ejPol2 == 8*x^5 + 5*x^2 + 4*x consPol :: (Num a, Eq a) => Int -> a -> Polinomio a -> Polinomio a consPol _ 0 p = p consPol n b p@(Pol xs) | esPolCero p = Pol [(n,b)] | n > m = Pol ((n,b):xs) | n < m = consPol m c (consPol n b (Pol (tail xs))) | b+c == 0 = Pol (tail xs) | otherwise = Pol ((n,b+c) : tail xs) where c = coefLider p m = grado p -- (grado p) es el grado del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- grado ejPol3 == 4 grado :: Polinomio a -> Int grado (Pol []) = 0 grado (Pol ((n,_):_)) = n -- (coefLider p) es el coeficiente líder del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- coefLider ejPol3 == 6 coefLider :: Num t => Polinomio t -> t coefLider (Pol []) = 0 coefLider (Pol ((_,b):_)) = b -- (restoPol p) es el resto del polinomio p. Por ejemplo, -- ejPol3 == 6*x^4 + 2*x -- restoPol ejPol3 == 2*x -- ejPol2 == x^5 + 5*x^2 + 4*x -- restoPol ejPol2 == 5*x^2 + 4*x restoPol :: Num t => Polinomio t -> Polinomio t restoPol (Pol []) = polCero restoPol (Pol [_]) = polCero restoPol (Pol (_:xs)) = Pol xs -- Generador de polinomios -- -- ======================= -- genPolinomio es un generador de polinomios. Por ejemplo, -- λ> sample (genPol 1) -- 7*x^9 + 9*x^8 + 10*x^7 + -14*x^5 + -15*x^2 + -10 -- -4*x^8 + 2*x -- -8*x^9 + 4*x^8 + 2*x^6 + 4*x^5 + -6*x^4 + 5*x^2 + -8*x -- -9*x^9 + x^5 + -7 -- 8*x^10 + -9*x^7 + 7*x^6 + 9*x^5 + 10*x^3 + -1*x^2 -- 7*x^10 + 5*x^9 + -5 -- -8*x^10 + -7 -- -5*x -- 5*x^10 + 4*x^4 + -3 -- 3*x^3 + -4 -- 10*x genPol :: (Num a, Arbitrary a, Eq a) => Int -> Gen (Polinomio a) genPol 0 = return polCero genPol _ = do n <- choose (0,10) b <- arbitrary p <- genPol (div n 2) return (consPol n b p) instance (Num a, Arbitrary a, Eq a) => Arbitrary (Polinomio a) where arbitrary = sized genPol -- Propiedades de los polinomios -- ============================= -- polCero es el polinomio cero. prop_polCero_es_cero :: Bool prop_polCero_es_cero = esPolCero polCero -- Si n es mayor que el grado de p y b no es cero, entonces -- (consPol n b p) es un polinomio distinto del cero. prop_consPol_no_cero :: Int -> Int -> Polinomio Int -> Property prop_consPol_no_cero n b p = n > grado p && b /= 0 ==> not (esPolCero (consPol n b p)) -- (consPol (grado p) (coefLider p) (restoPol p)) es igual a p. prop_consPol :: Polinomio Int -> Bool prop_consPol p = consPol (grado p) (coefLider p) (restoPol p) == p -- Si n es mayor que el grado de p y b no es cero, entonces -- el grado de (consPol n b p) es n. prop_grado :: Int -> Int -> Polinomio Int -> Property prop_grado n b p = n > grado p && b /= 0 ==> grado (consPol n b p) == n -- Si n es mayor que el grado de p y b no es cero, entonces -- el coeficiente líder de (consPol n b p) es b. prop_coefLider :: Int -> Int -> Polinomio Int -> Property prop_coefLider n b p = n > grado p && b /= 0 ==> coefLider (consPol n b p) == b -- Si n es mayor que el grado de p y b no es cero, entonces -- el resto de (consPol n b p) es p. prop_restoPol :: Int -> Int -> Polinomio Int -> Property prop_restoPol n b p = n > grado p && b /= 0 ==> restoPol (consPol n b p) == p -- Verificación -- ============ return [] verificaPol :: IO Bool verificaPol = $quickCheckAll -- La verificación es -- λ> verificaPol -- === prop_polCero_es_cero from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDispersa.hs:156 === -- +++ OK, passed 1 test. -- -- === prop_consPol_no_cero from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDispersa.hs:162 === -- +++ OK, passed 100 tests; 264 discarded. -- -- === prop_consPol from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDispersa.hs:168 === -- +++ OK, passed 100 tests. -- -- === prop_grado from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDispersa.hs:174 === -- +++ OK, passed 100 tests; 266 discarded. -- -- === prop_coefLider from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDispersa.hs:181 === -- +++ OK, passed 100 tests; 251 discarded. -- -- === prop_restoPol from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/TAD/PolRepDispersa.hs:188 === -- +++ OK, passed 100 tests; 254 discarded. -- -- True |
3. Los polinomios en Python
3.1. El tipo abstracto de los polinomios en Python
La implementación se encuentra en el módulo Polinomio.py cuyo contenido es el siguiente:
__all__ = [ 'Polinomio', 'polCero', 'esPolCero', 'consPol', 'grado', 'coefLider', 'restoPol', 'polinomioAleatorio' ] from src.TAD.PolRepDensa import (Polinomio, coefLider, consPol, esPolCero, grado, polCero, polinomioAleatorio, restoPol) # from src.TAD.PolRepDispersa import (Polinomio, polCero, esPolCero, # consPol, grado, coefLider, # restoPol, polinomioAleatorio) |
Para usar el TAD hay que usar una implementación concreta. En principio, consideraremos las siguientes:
- mediante listas densas y
- mediante listas dispersas.
3.2. Implementación de los polinomios mediante listas densas
Representaremos un polinomio por la lista de sus coeficientes ordenados en orden decreciente según el grado. Por ejemplo, el polinomio
6x^4 -5x^2 + 4x -7 |
se representa por
[6,0,-2,4,-7] |
En la representación se supone que, si la lista no es vacía, su primer elemento es distinto de cero.
Se define la clase Polinomio con los siguientes métodos:
- esPolCero() se verifica si es el polinomio cero.
- consPol(n, b) es el polinomio obtenido añadiendo el térmiono bx^n
- grado() es el grado del polinomio.
- coefLider() es el coeficiente líder del polinomio.
- restoPol() es el resto del polinomio.
Por ejemplo,
>>> Polinomio() 0 >>> ejPol1 = Polinomio().consPol(0,3).consPol(2,-5).consPol(4,3) >>> ejPol1 3*x^4 + -5*x^2 + 3 >>> ejPol2 = Polinomio().consPol(1,4).consPol(2,5).consPol(5,1) >>> ejPol2 x^5 + 5*x^2 + 4*x >>> ejPol3 = Polinomio().consPol(1,2).consPol(4,6) >>> ejPol3 6*x^4 + 2*x >>> Polinomio().esPolCero() True >>> ejPol1.esPolCero() False >>> ejPol2 x^5 + 5*x^2 + 4*x >>> ejPol2.consPol(3,0) x^5 + 5*x^2 + 4*x >>> Polinomio().consPol(3,2) 2*x^3 >>> ejPol2.consPol(6,7) 7*x^6 + x^5 + 5*x^2 + 4*x >>> ejPol2.consPol(4,7) x^5 + 7*x^4 + 5*x^2 + 4*x >>> ejPol2.consPol(5,7) 8*x^5 + 5*x^2 + 4*x >>> ejPol3 6*x^4 + 2*x >>> ejPol3.grado() 4 >>> ejPol3.restoPol() 2*x >>> ejPol2 x^5 + 5*x^2 + 4*x >>> ejPol2.restoPol() 5*x^2 + 4*x |
Además se definen las correspondientes funciones. Por ejemplo,
>>> polCero() 0 >>> ejPol1a = consPol(4,3,consPol(2,-5,consPol(0,3,polCero()))) >>> ejPol1a 3*x^4 + -5*x^2 + 3 >>> ejPol2a = consPol(5,1,consPol(2,5,consPol(1,4,polCero()))) >>> ejPol2a x^5 + 5*x^2 + 4*x >>> ejPol3a = consPol(4,6,consPol(1,2,polCero())) >>> ejPol3a 6*x^4 + 2*x >>> esPolCero(polCero()) True >>> esPolCero(ejPol1a) False >>> ejPol2a x^5 + 5*x^2 + 4*x >>> consPol(3,9,ejPol2a) x^5 + 9*x^3 + 5*x^2 + 4*x >>> consPol(3,2,polCero()) 2*x^3 >>> consPol(6,7,ejPol2a) 7*x^6 + x^5 + 5*x^2 + 4*x >>> consPol(4,7,ejPol2a) x^5 + 7*x^4 + 5*x^2 + 4*x >>> consPol(5,7,ejPol2a) 8*x^5 + 5*x^2 + 4*x >>> ejPol3a 6*x^4 + 2*x >>> grado(ejPol3a) 4 >>> restoPol(ejPol3a) 2*x >>> ejPol2a x^5 + 5*x^2 + 4*x >>> restoPol(ejPol2a) 5*x^2 + 4*x |
Finalmente, se define un generador aleatorio de polinomios y se comprueba que los polinomios cumplen las propiedades de su especificación.
La implementación se encuentra en el módulo PolRepDensa.py en el que se define la clase Conj con los siguientes métodos:
from __future__ import annotations __all__ = [ 'Polinomio', 'polCero', 'esPolCero', 'consPol', 'grado', 'coefLider', 'restoPol', 'polinomioAleatorio' ] from dataclasses import dataclass, field from itertools import dropwhile from typing import Generic, TypeVar from hypothesis import assume, given from hypothesis import strategies as st A = TypeVar('A', int, float, complex) # Clase de los polinomios mediante listas densas # ============================================== @dataclass class Polinomio(Generic[A]): _coeficientes: list[A] = field(default_factory=list) def esPolCero(self) -> bool: return not self._coeficientes def grado(self) -> int: if self.esPolCero(): return 0 return len(self._coeficientes) - 1 def coefLider(self) -> A: if self.esPolCero(): return 0 return self._coeficientes[0] def restoPol(self) -> Polinomio[A]: xs = self._coeficientes if len(xs) <= 1: return Polinomio([]) if xs[1] == 0: return Polinomio(list(dropwhile(lambda x: x == 0, xs[2:]))) return Polinomio(xs[1:]) def consPol(self, n: int, b: A) -> Polinomio[A]: m = self.grado() c = self.coefLider() xs = self._coeficientes if self.esPolCero(): return Polinomio([b] + ([0] * n)) if n > m: return Polinomio([b] + ([0] * (n-m-1)) + xs) if n < m: return self.restoPol().consPol(n, b).consPol(m, c) if b + c == 0: return Polinomio(list(dropwhile(lambda x: x == 0, xs[1:]))) return Polinomio([b + c] + xs[1:]) def __repr__(self) -> str: n = self.grado() a = self.coefLider() p = self.restoPol() if self.esPolCero(): return "0" if n == 0 and p.esPolCero(): return str(a) if n == 0: return str(a) + " + " + str(p) if n == 1 and p.esPolCero(): return str(a) + "*x" if n == 1: return str(a) + "*x + " + str(p) if a == 1 and p.esPolCero(): return "x^" + str(n) if p.esPolCero(): return str(a) + "*x^" + str(n) if a == 1: return "x^" + str(n) + " + " + str(p) return str(a) + "*x^" + str(n) + " + " + str(p) # Funciones del tipo polinomio # ============================ def polCero() -> Polinomio[A]: return Polinomio([]) def esPolCero(p: Polinomio[A]) -> bool: return p.esPolCero() def grado(p: Polinomio[A]) -> int: return p.grado() def coefLider(p: Polinomio[A]) -> A: return p.coefLider() def restoPol(p: Polinomio[A]) -> Polinomio[A]: return p.restoPol() def consPol(n: int, b: A, p: Polinomio[A]) -> Polinomio[A]: return p.consPol(n, b) # Generador de polinomios # ======================= # normal(xs) es la lista obtenida eliminando los ceros iniciales de # xs. Por ejmplo, # >>> normal([0,0,5,0]) # [5, 0] # >>> normal([0,0,0,0]) # [] def normal(xs: list[A]) -> list[A]: return list(dropwhile(lambda x: x == 0, xs)) # polinomioAleatorio() genera polinomios aleatorios. Por ejemplo, # >>> polinomioAleatorio().example() # 9*x^6 + -7*x^5 + 7*x^3 + x^2 + 7 # >>> polinomioAleatorio().example() # -3*x^7 + 8*x^6 + 2*x^5 + x^4 + -1*x^3 + -6*x^2 + 8*x + -6 # >>> polinomioAleatorio().example() # x^2 + 7*x + -1 def polinomioAleatorio() -> st.SearchStrategy[Polinomio[int]]: return st.lists(st.integers(min_value=-9, max_value=9), max_size=10)\ .map(lambda xs: normal(xs))\ .map(Polinomio) # Comprobación de las propiedades de los polinomios # ================================================= # Las propiedades son def test_esPolCero1() -> None: assert esPolCero(polCero()) @given(p=polinomioAleatorio(), n=st.integers(min_value=0, max_value=10), b=st.integers()) def test_esPolCero2(p: Polinomio[int], n: int, b: int) -> None: assume(n > grado(p) and b != 0) assert not esPolCero(consPol(n, b, p)) @given(p=polinomioAleatorio()) def test_consPol(p: Polinomio[int]) -> None: assume(not esPolCero(p)) assert consPol(grado(p), coefLider(p), restoPol(p)) == p @given(p=polinomioAleatorio(), n=st.integers(min_value=0, max_value=10), b=st.integers()) def test_grado(p: Polinomio[int], n: int, b: int) -> None: assume(n > grado(p) and b != 0) assert grado(consPol(n, b, p)) == n @given(p=polinomioAleatorio(), n=st.integers(min_value=0, max_value=10), b=st.integers()) def test_coefLider(p: Polinomio[int], n: int, b: int) -> None: assume(n > grado(p) and b != 0) assert coefLider(consPol(n, b, p)) == b @given(p=polinomioAleatorio(), n=st.integers(min_value=0, max_value=10), b=st.integers()) def test_restoPol(p: Polinomio[int], n: int, b: int) -> None: assume(n > grado(p) and b != 0) assert restoPol(consPol(n, b, p)) == p # La comprobación es # > poetry run pytest -v PolRepDensa.py # # PolRepDensa.py::test_esPolCero1 PASSED # PolRepDensa.py::test_esPolCero2 PASSED # PolRepDensa.py::test_consPol PASSED # PolRepDensa.py::test_grado PASSED # PolRepDensa.py::test_coefLider PASSED # PolRepDensa.py::test_restoPol PASSED # # === 6 passed in 1.64s === |
3.3. Implementación de los polinomios mediante listas dispersas
Representaremos un polinomio mediante una lista de pares (grado,coef), ordenados en orden decreciente según el grado. Por ejemplo, el polinomio
6x^4 -5x^2 + 4x -7 |
se representa por
[(4,6),(2,-5),(1,4),(0,-7)] |
En la representación se supone que los primeros elementos de los pares forman una sucesión estrictamente decreciente y que los segundos elementos son distintos de cero.
La implementación se encuentra en el módulo PolRepDispersa.py cuyo contenido es
from __future__ import annotations __all__ = [ 'Polinomio', 'polCero', 'esPolCero', 'consPol', 'grado', 'coefLider', 'restoPol', 'polinomioAleatorio' ] from dataclasses import dataclass, field from typing import Generic, TypeVar from hypothesis import assume, given from hypothesis import strategies as st A = TypeVar('A', int, float, complex) # Clase de los polinomios mediante listas densas # ============================================== @dataclass class Polinomio(Generic[A]): _terminos: list[tuple[int, A]] = field(default_factory=list) def esPolCero(self) -> bool: return not self._terminos def grado(self) -> int: if self.esPolCero(): return 0 return self._terminos[0][0] def coefLider(self) -> A: if self.esPolCero(): return 0 return self._terminos[0][1] def restoPol(self) -> Polinomio[A]: xs = self._terminos if len(xs) <= 1: return Polinomio([]) return Polinomio(xs[1:]) def consPol(self, n: int, b: A) -> Polinomio[A]: m = self.grado() c = self.coefLider() xs = self._terminos if b == 0: return self if self.esPolCero(): return Polinomio([(n, b)]) if n > m: return Polinomio([(n, b)] + xs) if n < m: return Polinomio(xs[1:]).consPol(n, b).consPol(m, c) if b + c == 0: return Polinomio(xs[1:]) return Polinomio([(n, b + c)] + xs[1:]) def __repr__(self) -> str: n = self.grado() a = self.coefLider() p = self.restoPol() if self.esPolCero(): return "0" if n == 0 and p.esPolCero(): return str(a) if n == 0: return str(a) + " + " + str(p) if n == 1 and p.esPolCero(): return str(a) + "*x" if n == 1: return str(a) + "*x + " + str(p) if a == 1 and p.esPolCero(): return "x^" + str(n) if p.esPolCero(): return str(a) + "*x^" + str(n) if a == 1: return "x^" + str(n) + " + " + str(p) return str(a) + "*x^" + str(n) + " + " + str(p) # Funciones del tipo polinomio # ============================ def polCero() -> Polinomio[A]: return Polinomio([]) def esPolCero(p: Polinomio[A]) -> bool: return p.esPolCero() def grado(p: Polinomio[A]) -> int: return p.grado() def coefLider(p: Polinomio[A]) -> A: return p.coefLider() def restoPol(p: Polinomio[A]) -> Polinomio[A]: return p.restoPol() def consPol(n: int, b: A, p: Polinomio[A]) -> Polinomio[A]: return p.consPol(n, b) # Generador de polinomios # ======================= # normal(ps) es la representación dispersa de un polinomio. def normal(ps: list[tuple[int, A]]) -> list[tuple[int, A]]: xs = sorted(list({p[0] for p in ps}), reverse=True) ys = [p[1] for p in ps] return [(x, y) for (x, y) in zip(xs, ys) if y != 0] # polinomioAleatorio() genera polinomios aleatorios. Por ejemplo, # >>> polinomioAleatorio().example() # -4*x^8 + -5*x^7 + -4*x^6 + -4*x^5 + -8*x^3 # >>> polinomioAleatorio().example() # -7*x^9 + -8*x^6 + -8*x^3 + 2*x^2 + -1*x + 4 def polinomioAleatorio() -> st.SearchStrategy[Polinomio[int]]: return st.lists(st.tuples(st.integers(min_value=0, max_value=9), st.integers(min_value=-9, max_value=9)))\ .map(lambda ps: normal(ps))\ .map(Polinomio) # Comprobación de las propiedades de los polinomios # ================================================= # Las propiedades son def test_esPolCero1() -> None: assert esPolCero(polCero()) @given(p=polinomioAleatorio(), n=st.integers(min_value=0, max_value=10), b=st.integers()) def test_esPolCero2(p: Polinomio[int], n: int, b: int) -> None: assume(n > grado(p) and b != 0) assert not esPolCero(consPol(n, b, p)) @given(p=polinomioAleatorio()) def test_consPol(p: Polinomio[int]) -> None: assume(not esPolCero(p)) assert consPol(grado(p), coefLider(p), restoPol(p)) == p @given(p=polinomioAleatorio(), n=st.integers(min_value=0, max_value=10), b=st.integers()) def test_grado(p: Polinomio[int], n: int, b: int) -> None: assume(n > grado(p) and b != 0) assert grado(consPol(n, b, p)) == n @given(p=polinomioAleatorio(), n=st.integers(min_value=0, max_value=10), b=st.integers()) def test_coefLider(p: Polinomio[int], n: int, b: int) -> None: assume(n > grado(p) and b != 0) assert coefLider(consPol(n, b, p)) == b @given(p=polinomioAleatorio(), n=st.integers(min_value=0, max_value=10), b=st.integers()) def test_restoPol(p: Polinomio[int], n: int, b: int) -> None: assume(n > grado(p) and b != 0) assert restoPol(consPol(n, b, p)) == p # La comprobación es # > poetry run pytest -v PolRepDispersa.py # # PolRepDispersa.py::test_esPolCero1 PASSED # PolRepDispersa.py::test_esPolCero2 PASSED # PolRepDispersa.py::test_consPol PASSED # PolRepDispersa.py::test_grado PASSED # PolRepDispersa.py::test_coefLider PASSED # PolRepDispersa.py::test_restoPol PASSED # # === 6 passed in 1.74s === |
2. Transformaciones entre las representaciones dispersa y densa de polinomios
Definir las funciones
densaAdispersa :: (Num a, Eq a) => [a] -> [(Int,a)] dispersaAdensa :: (Num a, Eq a) => [(Int,a)] -> [a] |
tales que
densaAdispersa xs
es la representación dispersa del polinomio cuya representación densa esxs
. Por ejemplo,
λ> densaAdispersa [9,0,0,5,0,4,7] [(6,9),(3,5),(1,4),(0,7)] |
dispersaAdensa ps
es la representación densa del polinomio cuya representación dispersa esps
. Por ejemplo,
λ> dispersaAdensa [(6,9),(3,5),(1,4),(0,7)] [9,0,0,5,0,4,7] |
Comprobar con QuickCheck que las funciones densaAdispersa y dispersaAdensa son inversas.
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import Data.List (nub, sort) import Test.QuickCheck -- 1ª definición de densaAdispersa -- =============================== densaAdispersa :: (Num a, Eq a) => [a] -> [(Int,a)] densaAdispersa xs = [(m,a) | (m,a) <- zip [n-1,n-2..] xs, a /= 0] where n = length xs -- 2ª definición de densaAdispersa -- =============================== densaAdispersa2 :: (Num a, Eq a) => [a] -> [(Int,a)] densaAdispersa2 xs = reverse (aux (reverse xs) 0) where aux [] _ = [] aux (0:ys) n = aux ys (n+1) aux (y:ys) n = (n,y) : aux ys (n+1) -- Comprobación de equivalencia de densaAdispersa -- ============================================== -- La propiedad es prop_densaAdispersa :: [Int] -> Bool prop_densaAdispersa xs = densaAdispersa xs == densaAdispersa2 xs -- La comprobación es -- λ> quickCheck prop_densaAdispersa -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> densaAdispersa (5 : replicate (10^7) 0) -- [(10000000,5)] -- (4.54 secs, 3,280,572,504 bytes) -- λ> densaAdispersa2 (5 : replicate (10^7) 0) -- [(10000000,5)] -- (7.35 secs, 3,696,968,576 bytes) -- 1ª definición de dispersaAdensa -- =============================== dispersaAdensa :: (Num a, Eq a) => [(Int,a)] -> [a] dispersaAdensa [] = [] dispersaAdensa [(n,a)] = a : replicate n 0 dispersaAdensa ((n,a):(m,b):ps) = a : replicate (n-m-1) 0 ++ dispersaAdensa ((m,b):ps) -- 2ª definición de dispersaAdensa -- =============================== dispersaAdensa2 :: (Num a, Eq a) => [(Int,a)] -> [a] dispersaAdensa2 [] = [] dispersaAdensa2 ps@((n,_):_) = [coeficiente ps m | m <- [n,n-1..0]] -- (coeficiente ps n) es el coeficiente del término de grado n en el -- polinomio cuya representación densa es ps. Por ejemplo, -- coeficiente [(6,9),(3,5),(1,4),(0,7)] 3 == 5 -- coeficiente [(6,9),(3,5),(1,4),(0,7)] 4 == 0 coeficiente :: (Num a, Eq a) => [(Int,a)] -> Int -> a coeficiente [] _ = 0 coeficiente ((m,a):ps) n | n > m = 0 | n == m = a | otherwise = coeficiente ps n -- Comprobación de equivalencia de dispersaAdensa -- ============================================== -- Tipo de las representaciones dispersas de polinomios. newtype Dispersa = Dis [(Int,Int)] deriving Show -- dispersaArbitraria es un generador de representaciones dispersas de -- polinomios. Por ejemplo, -- λ> sample dispersaArbitraria -- Dis [] -- Dis [] -- Dis [(3,-2),(2,0),(0,3)] -- Dis [(6,1),(4,-2),(3,4),(2,-4)] -- Dis [] -- Dis [(5,-7)] -- Dis [(12,5),(11,-8),(10,3),(8,-10),(7,-5),(4,12),(3,6),(2,-8),(1,11)] -- Dis [(7,-2),(2,-8)] -- Dis [(14,-15)] -- Dis [(17,5),(16,1),(15,-1),(14,10),(13,5),(12,-15),(9,12),(6,14)] -- Dis [(19,17),(12,7),(8,-3),(7,13),(5,-2),(4,7)] dispersaArbitraria :: Gen Dispersa dispersaArbitraria = do (xs, ys) <- arbitrary let xs' = nub (reverse (sort (map abs xs))) ys' = filter (/= 0) ys return (Dis (zip xs' ys')) -- Dispersa está contenida en Arbitrary instance Arbitrary Dispersa where arbitrary = dispersaArbitraria -- La propiedad es prop_dispersaAdensa :: Dispersa -> Bool prop_dispersaAdensa (Dis xs) = dispersaAdensa xs == dispersaAdensa2 xs -- La comprobación es -- λ> quickCheck prop_dispersaAdensa -- +++ OK, passed 100 tests. -- Comparación de eficiencia de dispersaAdensa -- =========================================== -- La comparación es -- λ> length (dispersaAdensa [(10^7,5)]) -- 10000001 -- (0.11 secs, 560,566,848 bytes) -- λ> length (dispersaAdensa2 [(10^7,5)]) -- 10000001 -- (2.51 secs, 2,160,567,112 bytes) -- Propiedad -- ========= -- Tipo de las representaciones densas de polinomios. newtype Densa = Den [Int] deriving Show -- densaArbitraria es un generador de representaciones dispersas de -- polinomios. Por ejemplo, -- λ> sample densaArbitraria -- Den [] -- Den [] -- Den [] -- Den [-6,6,5,-3] -- Den [] -- Den [8,-7,-10,8,-10,-4,10,6,10] -- Den [-6,2,11,-4,-9,-5,9,2,2,9] -- Den [-6,9,-2] -- Den [-1,-7,15,1,5,-2,13,16,8,7,2,16,-2,16,-7,4] -- Den [8,13,-4,-2,-10,3,5,-4,-6,13,-9,-12,8,11,9,-18,12,10] -- Den [-1,-2,11,17,-7,13,-12,-19,16,-10,-18,-19,1,-4,-17,10,1,10] densaArbitraria :: Gen Densa densaArbitraria = do ys <- arbitrary let ys' = dropWhile (== 0) ys return (Den ys') -- Dispersa está contenida en Arbitrary instance Arbitrary Densa where arbitrary = densaArbitraria -- La primera propiedad es prop_dispersaAdensa_densaAdispersa :: Densa -> Bool prop_dispersaAdensa_densaAdispersa (Den xs) = dispersaAdensa (densaAdispersa xs) == xs -- La comprobación es -- λ> quickCheck prop_dispersaAdensa_densaAdispersa -- +++ OK, passed 100 tests. -- La segunda propiedad es prop_densaAdispersa_dispersaAdensa :: Dispersa -> Bool prop_densaAdispersa_dispersaAdensa (Dis ps) = densaAdispersa (dispersaAdensa ps) == ps -- La comprobación es -- λ> quickCheck prop_densaAdispersa_dispersaAdensa -- +++ OK, passed 100 tests. |
from itertools import dropwhile from typing import TypeVar from hypothesis import given from hypothesis import strategies as st A = TypeVar('A', int, float, complex) # 1ª definición de densaAdispersa # =============================== def densaAdispersa(xs: list[A]) -> list[tuple[int, A]]: n = len(xs) return [(m, a) for (m, a) in zip(range(n-1, -1, -1), xs) if a != 0] # 2ª definición de densaAdispersa # =============================== def densaAdispersa2(xs: list[A]) -> list[tuple[int, A]]: def aux(xs: list[A], n: int) -> list[tuple[int, A]]: if not xs: return [] if xs[0] == 0: return aux(xs[1:], n + 1) return [(n, xs[0])] + aux(xs[1:], n + 1) return list(reversed(aux(list(reversed(xs)), 0))) # 3ª definición de densaAdispersa # =============================== def densaAdispersa3(xs: list[A]) -> list[tuple[int, A]]: r = [] n = len(xs) - 1 for x in xs: if x != 0: r.append((n, x)) n -= 1 return r # Comprobación de equivalencia de densaAdispersa # ============================================== # normalDensa(ps) es la representación dispersa de un polinomio. def normalDensa(xs: list[A]) -> list[A]: return list(dropwhile(lambda x: x == 0, xs)) # densaAleatoria() genera representaciones densas de polinomios # aleatorios. Por ejemplo, # >>> densaAleatoria().example() # [-5, 9, -6, -5, 7, -5, -1, 9] # >>> densaAleatoria().example() # [-4, 9, -3, -3, -5, 0, 6, -8, 8, 6, 0, -9] # >>> densaAleatoria().example() # [-3, -1, 2, 0, -9] def densaAleatoria() -> st.SearchStrategy[list[int]]: return st.lists(st.integers(min_value=-9, max_value=9))\ .map(normalDensa) # La propiedad es @given(xs=densaAleatoria()) def test_densaADispersa(xs: list[int]) -> None: r = densaAdispersa(xs) assert densaAdispersa2(xs) == r assert densaAdispersa3(xs) == r # 1ª definición de dispersaAdensa # =============================== def dispersaAdensa(ps: list[tuple[int, A]]) -> list[A]: if not ps: return [] if len(ps) == 1: return [ps[0][1]] + [0] * ps[0][0] (n, a) = ps[0] (m, _) = ps[1] return [a] + [0] * (n-m-1) + dispersaAdensa(ps[1:]) # 2ª definición de dispersaAdensa # =============================== # coeficiente(ps, n) es el coeficiente del término de grado n en el # polinomio cuya representación densa es ps. Por ejemplo, # coeficiente([(6, 9), (3, 5), (1, 4), (0, 7)], 3) == 5 # coeficiente([(6, 9), (3, 5), (1, 4), (0, 7)], 4) == 0 def coeficiente(ps: list[tuple[int, A]], n: int) -> A: if not ps: return 0 (m, a) = ps[0] if n > m: return 0 if n == m: return a return coeficiente(ps[1:], n) def dispersaAdensa2(ps: list[tuple[int, A]]) -> list[A]: if not ps: return [] n = ps[0][0] return [coeficiente(ps, m) for m in range(n, -1, -1)] # 3ª definición de dispersaAdensa # =============================== def dispersaAdensa3(ps: list[tuple[int, A]]) -> list[A]: if not ps: return [] n = ps[0][0] r: list[A] = [0] * (n + 1) for (m, a) in ps: r[n-m] = a return r # Comprobación de equivalencia de dispersaAdensa # ============================================== # normalDispersa(ps) es la representación dispersa de un polinomio. def normalDispersa(ps: list[tuple[int, A]]) -> list[tuple[int, A]]: xs = sorted(list({p[0] for p in ps}), reverse=True) ys = [p[1] for p in ps] return [(x, y) for (x, y) in zip(xs, ys) if y != 0] # dispersaAleatoria() genera representaciones densas de polinomios # aleatorios. Por ejemplo, # >>> dispersaAleatoria().example() # [(5, -6), (2, -1), (0, 2)] # >>> dispersaAleatoria().example() # [(6, -7)] # >>> dispersaAleatoria().example() # [(7, 2), (4, 9), (3, 3), (0, -2)] def dispersaAleatoria() -> st.SearchStrategy[list[tuple[int, int]]]: return st.lists(st.tuples(st.integers(min_value=0, max_value=9), st.integers(min_value=-9, max_value=9)))\ .map(normalDispersa) # La propiedad es @given(ps=dispersaAleatoria()) def test_dispersaAdensa(ps: list[tuple[int, int]]) -> None: r = dispersaAdensa(ps) assert dispersaAdensa2(ps) == r assert dispersaAdensa3(ps) == r # Propiedad # ========= # La primera propiedad es @given(xs=densaAleatoria()) def test_dispersaAdensa_densaAdispersa(xs: list[int]) -> None: assert dispersaAdensa(densaAdispersa(xs)) == xs # La segunda propiedad es @given(ps=dispersaAleatoria()) def test_densaAdispersa_dispersaAdensa(ps: list[tuple[int, int]]) -> None: assert densaAdispersa(dispersaAdensa(ps)) == ps # La comprobación es # > poetry run pytest -v Polinomios_Transformaciones_dispersa_y_densa.py # test_densaADispersa PASSED # test_dispersaAdensa PASSED # test_dispersaAdensa_densaAdispersa PASSED # test_densaAdispersa_dispersaAdensa PASSED |
3. Transformaciones entre polinomios y listas dispersas
Utilizando el tipo abstracto de datos de los polinomios definir las funciones
dispersaApolinomio :: (Num a, Eq a) => [(Int,a)] -> Polinomio a polinomioAdispersa :: (Num a, Eq a) => Polinomio a -> [(Int,a)] |
tales que
dispersaApolinomio ps
es el polinomiocuya representación dispersa esps
. Por ejemplo,
λ> dispersaApolinomio [(6,9),(3,5),(1,4),(0,7)] 9*x^6 + 5*x^3 + 4*x + 7 |
polinomioAdispersa p
es la representación dispersa del polinomiop
. Por ejemplo,
λ> ejPol = consPol 6 9 (consPol 3 5 (consPol 1 4 (consPol 0 7 polCero))) λ> ejPol 9*x^6 + 5*x^3 + 4*x + 7 λ> polinomioAdispersa ejPol [(6,9),(3,5),(1,4),(0,7)] |
Comprobar con QuickCheck que ambas funciones son inversas.
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import TAD.Polinomio (Polinomio, polCero, esPolCero, consPol, grado, coefLider, restoPol) import Data.List (sort, nub) import Test.QuickCheck -- 1ª definición de dispersaApolinomio -- =================================== dispersaApolinomio :: (Num a, Eq a) => [(Int,a)] -> Polinomio a dispersaApolinomio [] = polCero dispersaApolinomio ((n,a):ps) = consPol n a (dispersaApolinomio ps) -- 2ª definición de dispersaApolinomio -- =================================== dispersaApolinomio2 :: (Num a, Eq a) => [(Int,a)] -> Polinomio a dispersaApolinomio2 = foldr (\(x,y) -> consPol x y) polCero -- 3ª definición de dispersaApolinomio -- =================================== dispersaApolinomio3 :: (Num a, Eq a) => [(Int,a)] -> Polinomio a dispersaApolinomio3 = foldr (uncurry consPol) polCero -- Comprobación de equivalencia -- ============================ -- Tipo de las representaciones dispersas de polinomios. newtype Dispersa = Dis [(Int,Int)] deriving Show -- dispersaArbitraria es un generador de representaciones dispersas de -- polinomios. Por ejemplo, -- λ> sample dispersaArbitraria -- Dis [] -- Dis [] -- Dis [(3,-2),(2,0),(0,3)] -- Dis [(6,1),(4,-2),(3,4),(2,-4)] -- Dis [] -- Dis [(5,-7)] -- Dis [(12,5),(11,-8),(10,3),(8,-10),(7,-5),(4,12),(3,6),(2,-8),(1,11)] -- Dis [(7,-2),(2,-8)] -- Dis [(14,-15)] -- Dis [(17,5),(16,1),(15,-1),(14,10),(13,5),(12,-15),(9,12),(6,14)] -- Dis [(19,17),(12,7),(8,-3),(7,13),(5,-2),(4,7)] dispersaArbitraria :: Gen Dispersa dispersaArbitraria = do (xs, ys) <- arbitrary let xs' = nub (reverse (sort (map abs xs))) ys' = filter (/= 0) ys return (Dis (zip xs' ys')) -- Dispersa está contenida en Arbitrary instance Arbitrary Dispersa where arbitrary = dispersaArbitraria -- La propiedad es prop_dispersaApolinomio :: Dispersa -> Bool prop_dispersaApolinomio (Dis ps) = all (== dispersaApolinomio ps) [dispersaApolinomio2 ps, dispersaApolinomio3 ps] -- Definición de polinomioAdispersa -- ================================ polinomioAdispersa :: (Num a, Eq a) => Polinomio a -> [(Int,a)] polinomioAdispersa p | esPolCero p = [] | otherwise = (grado p, coefLider p) : polinomioAdispersa (restoPol p) -- Propiedad de ser inversas -- ========================= -- La primera propiedad es prop_polinomioAdispersa_dispersaApolinomio :: Dispersa -> Bool prop_polinomioAdispersa_dispersaApolinomio (Dis ps) = polinomioAdispersa (dispersaApolinomio ps) == ps -- La comprobación es -- λ> quickCheck prop_polinomioAdispersa_dispersaApolinomio -- +++ OK, passed 100 tests. -- La segunda propiedad es prop_dispersaApolinomio_polinomioAdispersa :: Polinomio Int -> Bool prop_dispersaApolinomio_polinomioAdispersa p = dispersaApolinomio (polinomioAdispersa p) == p -- La comprobación es -- λ> quickCheck prop_dispersaApolinomio_polinomioAdispersa -- +++ OK, passed 100 tests. |
from typing import TypeVar from hypothesis import given from src.Polinomios_Transformaciones_dispersa_y_densa import dispersaAleatoria from src.TAD.Polinomio import (Polinomio, coefLider, consPol, esPolCero, grado, polCero, polinomioAleatorio, restoPol) A = TypeVar('A', int, float, complex) # 1ª definición de dispersaApolinomio # =================================== def dispersaApolinomio(ps: list[tuple[int, A]]) -> Polinomio[A]: if not ps: return polCero() (n, a) = ps[0] return consPol(n, a, dispersaApolinomio(ps[1:])) # 2ª definición de dispersaApolinomio # =================================== def dispersaApolinomio2(ps: list[tuple[int, A]]) -> Polinomio[A]: r: Polinomio[A] = polCero() for (n, a) in reversed(ps): r = consPol(n, a, r) return r # Comprobación de equivalencia # ============================ # La propiedad es @given(ps=dispersaAleatoria()) def test_dispersaApolinomio(ps: list[tuple[int, int]]) -> None: assert dispersaApolinomio(ps) == dispersaApolinomio2(ps) # El generador dispersaAleatoria está definido en el ejercicio # "Transformaciones entre las representaciones dispersa y densa" que se # encuentra en https://bit.ly/402UpuT # Definición de polinomioAdispersa # ================================ def polinomioAdispersa(p: Polinomio[A]) -> list[tuple[int, A]]: if esPolCero(p): return [] return [(grado(p), coefLider(p))] + polinomioAdispersa(restoPol(p)) # Propiedad de ser inversas # ========================= # La primera propiedad es @given(ps=dispersaAleatoria()) def test_polinomioAdispersa_dispersaApolinomio(ps: list[tuple[int, int]]) -> None: assert polinomioAdispersa(dispersaApolinomio(ps)) == ps # La segunda propiedad es @given(p=polinomioAleatorio()) def test_dispersaApolinomio_polinomioAdispersa(p: Polinomio[int]) -> None: assert dispersaApolinomio(polinomioAdispersa(p)) == p # La comprobación es # > poetry run pytest -v Polinomios_Transformaciones_polinomios_dispersas.py # test_dispersaApolinomio PASSED # test_polinomioAdispersa_dispersaApolinomio PASSED # test_dispersaApolinomio_polinomioAdispersa PASSED |
4. Coeficiente del término de grado k de un polinomio
Utilizando el tipo abstracto de datos de los polinomios definir la función
coeficiente :: (Num a, Eq a) => Int -> Polinomio a -> a |
tal que coeficiente k p
es el coeficiente del término de grado k
del polinomio p
. Por ejemplo,
λ> ejPol = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero)) λ> ejPol x^5 + 5*x^2 + 4*x λ> coeficiente 2 ejPol 5 λ> coeficiente 3 ejPol 0 |
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import TAD.Polinomio (Polinomio, coefLider, grado, restoPol, consPol, polCero) coeficiente :: (Num a, Eq a) => Int -> Polinomio a -> a coeficiente k p | k == n = coefLider p | k > grado (restoPol p) = 0 | otherwise = coeficiente k (restoPol p) where n = grado p |
from typing import TypeVar from src.TAD.Polinomio import (Polinomio, coefLider, consPol, grado, polCero, restoPol) A = TypeVar('A', int, float, complex) def coeficiente(k: int, p: Polinomio[A]) -> A: if k == grado(p): return coefLider(p) if k > grado(restoPol(p)): return 0 return coeficiente(k, restoPol(p)) |
5. Transformaciones entre polinomios y listas densas
Utilizando el tipo abstracto de datos de los polinomios definir las funciones
densaApolinomio :: (Num a, Eq a) => [a] -> Polinomio a polinomioAdensa :: (Num a, Eq a) => Polinomio a -> [a] |
tales que
densaApolinomio xs
es el polinomio cuya representación densa esxs
. Por ejemplo,
λ> densaApolinomio [9,0,0,5,0,4,7] 9*x^6 + 5*x^3 + 4*x + 7 |
polinomioAdensa p
es la representación densa del polinomiop
. Por ejemplo,
λ> ejPol = consPol 6 9 (consPol 3 5 (consPol 1 4 (consPol 0 7 polCero))) λ> ejPol 9*x^6 + 5*x^3 + 4*x + 7 λ> polinomioAdensa ejPol [9,0,0,5,0,4,7] |
Comprobar con QuickCheck que ambas funciones son inversas.
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
import TAD.Polinomio (Polinomio, polCero, esPolCero, consPol, grado, coefLider, restoPol) import Polinomios_Transformaciones_dispersa_y_densa (densaAdispersa, dispersaAdensa) import Polinomios_Transformaciones_polinomios_dispersas (dispersaApolinomio, polinomioAdispersa) import Polinomios_Coeficiente (coeficiente) import Data.List (sort, nub) import Test.QuickCheck -- 1ª definición de densaApolinomio -- ================================ densaApolinomio :: (Num a, Eq a) => [a] -> Polinomio a densaApolinomio [] = polCero densaApolinomio (x:xs) = consPol (length xs) x (densaApolinomio xs) -- 2ª definición de densaApolinomio -- ================================ densaApolinomio2 :: (Num a, Eq a) => [a] -> Polinomio a densaApolinomio2 = dispersaApolinomio . densaAdispersa -- La función densaAdispersa está definida en el ejercicio -- "Transformaciones entre las representaciones dispersa y densa" que se -- encuentra en https://bit.ly/3GTyIqe -- La función dispersaApolinomio se encuentra en el ejercicio -- "Transformaciones entre polinomios y listas dispersas" que se -- encuentra en https://bit.ly/41GgQaB -- Comprobación de equivalencia de densaApolinomio -- =============================================== -- La propiedad es prop_densaApolinomio :: [Int] -> Bool prop_densaApolinomio xs = densaApolinomio xs == densaApolinomio2 xs -- La comprobación es -- λ> quickCheck prop_densaApolinomio -- +++ OK, passed 100 tests. -- 1ª definición de polinomioAdensa -- ================================ polinomioAdensa :: (Num a, Eq a) => Polinomio a -> [a] polinomioAdensa p | esPolCero p = [] | otherwise = [coeficiente k p | k <- [n,n-1..0]] where n = grado p -- La función coeficiente está definida en el ejercicio -- "Coeficiente del término de grado k" que se encuentra en -- https://bit.ly/413l3oQ -- 2ª definición de polinomioAdensa -- ================================ polinomioAdensa2 :: (Num a, Eq a) => Polinomio a -> [a] polinomioAdensa2 = dispersaAdensa . polinomioAdispersa -- La función dispersaAdensa está definida en el ejercicio -- "Transformaciones entre las representaciones dispersa y densa" que se -- encuentra en https://bit.ly/3GTyIqe -- La función polinomioAdispersa se encuentra en el ejercicio -- "Transformaciones entre polinomios y listas dispersas" que se -- encuentra en https://bit.ly/41GgQaB -- Comprobación de equivalencia de polinomioAdensa -- =============================================== -- La propiedad es prop_polinomioAdensa :: Polinomio Int -> Bool prop_polinomioAdensa p = polinomioAdensa p == polinomioAdensa2 p -- La comprobación es -- λ> quickCheck prop_polinomioAdensa -- +++ OK, passed 100 tests. -- Propiedades de inversa -- ====================== -- La primera propiedad es prop_polinomioAdensa_densaApolinomio :: [Int] -> Bool prop_polinomioAdensa_densaApolinomio xs = polinomioAdensa (densaApolinomio xs') == xs' where xs' = dropWhile (== 0) xs -- La comprobación es -- λ> quickCheck prop_polinomioAdensa_densaApolinomio -- +++ OK, passed 100 tests. -- La segunda propiedad es prop_densaApolinomio_polinomioAdensa :: Polinomio Int -> Bool prop_densaApolinomio_polinomioAdensa p = densaApolinomio (polinomioAdensa p) == p -- La comprobación es -- λ> quickCheck prop_densaApolinomio_polinomioAdensa -- +++ OK, passed 100 tests. |
from typing import TypeVar from hypothesis import given from src.Polinomios_Coeficiente import coeficiente from src.Polinomios_Transformaciones_dispersa_y_densa import (densaAdispersa, densaAleatoria, dispersaAdensa) from src.Polinomios_Transformaciones_polinomios_dispersas import ( dispersaApolinomio, polinomioAdispersa) from src.TAD.Polinomio import (Polinomio, coefLider, consPol, esPolCero, grado, polCero, polinomioAleatorio, restoPol) A = TypeVar('A', int, float, complex) # 1ª definición de densaApolinomio # ================================ def densaApolinomio(xs: list[A]) -> Polinomio[A]: if not xs: return polCero() return consPol(len(xs[1:]), xs[0], densaApolinomio(xs[1:])) # 2ª definición de densaApolinomio # ================================ def densaApolinomio2(xs: list[A]) -> Polinomio[A]: return dispersaApolinomio(densaAdispersa(xs)) # La función densaAdispersa está definida en el ejercicio # "Transformaciones entre las representaciones dispersa y densa" que se # encuentra en https://bit.ly/3GTyIqe # La función dispersaApolinomio se encuentra en el ejercicio # "Transformaciones entre polinomios y listas dispersas" que se # encuentra en https://bit.ly/41GgQaB # Comprobación de equivalencia de densaApolinomio # =============================================== # La propiedad es @given(xs=densaAleatoria()) def test_densaApolinomio(xs: list[int]) -> None: assert densaApolinomio(xs) == densaApolinomio2(xs) # La función densaAleatoria está definida en el ejercicio # "Transformaciones entre las representaciones dispersa y densa" que se # encuentra en https://bit.ly/3GTyIqe # 1ª definición de polinomioAdensa # ================================ def polinomioAdensa(p: Polinomio[A]) -> list[A]: if esPolCero(p): return [] n = grado(p) return [coeficiente(k, p) for k in range(n, -1, -1)] # La función coeficiente está definida en el ejercicio # "Coeficiente del término de grado k" que se encuentra en # https://bit.ly/413l3oQ # 2ª definición de polinomioAdensa # ================================ def polinomioAdensa2(p: Polinomio[A]) -> list[A]: return dispersaAdensa(polinomioAdispersa(p)) # La función dispersaAdensa está definida en el ejercicio # "Transformaciones entre las representaciones dispersa y densa" que se # encuentra en https://bit.ly/3GTyIqe # La función polinomioAdispersa se encuentra en el ejercicio # "Transformaciones entre polinomios y listas dispersas" que se # encuentra en https://bit.ly/41GgQaB # Comprobación de equivalencia de polinomioAdensa # =============================================== # La propiedad es @given(p=polinomioAleatorio()) def test_polinomioAdensa(p: Polinomio[int]) -> None: assert polinomioAdensa(p) == polinomioAdensa2(p) # Propiedades de inversa # ====================== # La primera propiedad es @given(xs=densaAleatoria()) def test_polinomioAdensa_densaApolinomio(xs: list[int]) -> None: assert polinomioAdensa(densaApolinomio(xs)) == xs # La segunda propiedad es @given(p=polinomioAleatorio()) def test_densaApolinomio_polinomioAdensa(p: Polinomio[int]) -> None: assert densaApolinomio(polinomioAdensa(p)) == p # La comprobación es # > poetry run pytest -v Polinomios_Transformaciones_polinomios_densas.py # test_densaApolinomio PASSED # test_polinomioAdensa PASSED # test_polinomioAdensa_densaApolinomio PASSED # test_densaApolinomio_polinomioAdensa PASSED |