Fracciones que son cuadrados perfectos
Nitesh Singh ha planteado en Mathematics Competitions el siguiente problema:
¿Cuántos números enteros n existen tales que n/(1450−n) es un cuadrado perfecto?
Vamos a generalizarlo y resolverlo con Haskell. La generalización es
Para cada número natural x, calcular los números enteros n tales que n/(x−n) es un cuadrado perfecto.
Escribiremos dos formas de resolverlo y compararemos su eficiencia.
Este ejercicio sirve para ilustrar cómo el prepocesamiento matemático puede ayudar a mejorar la eficiencia de los programas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
-- --------------------------------------------------------------------- -- § 1ª solución -- -- --------------------------------------------------------------------- -- Puesto que n/(x-n) es un cuadrado perfecto, se tiene que 0 ≤ n < x. -- (soluciones1 x) es la lista de los números enteros n tales que -- n/(x−n) es un cuadrado perfecto. Por ejemplo, -- soluciones1 1450 == [0,725,1160,1305,1421,1440,1445] soluciones1 :: Integer -> [Integer] soluciones1 x = [n | n <- [0..x-1], n `rem` (x - n) == 0, esCuadrado (n `div` (x - n))] -- (esCuadrado x) se verifica si x es un cuadrado perfecto. Por ejemplo, -- esCuadrado 9 == True -- esCuadrado 10 == False esCuadrado :: Integer -> Bool esCuadrado x = x == y^2 where y = floor (sqrt (fromIntegral x)) -- --------------------------------------------------------------------- -- § 2ª solución -- -- --------------------------------------------------------------------- -- Puesto que n/(x-n) es un cuadrado perfecto, existe un k tal que -- n/(x-n) = k^2 -- Luego, -- n = x * k^2/(k^2 + 1). -- Además, como n < x se tiene que k < sqrt(x). -- (soluciones2 x) es la lista de los números enteros n tales que -- n/(x−n) es un cuadrado perfecto. Por ejemplo, -- soluciones2 1450 == [0,725,1160,1305,1421,1440,1445] soluciones2 :: Integer -> [Integer] soluciones2 x = [x * k^2 `div` (k^2 + 1) | k <- [0..c-1], x * k^2 `rem` (k^2 + 1) == 0] where c = ceiling (sqrt (fromIntegral x)) -- --------------------------------------------------------------------- -- § Equivalencia de las soluciones -- -- --------------------------------------------------------------------- -- (prop_equivalencia m) se verifica si para todos los números x entre 0 -- y m, son iguales (soluciones1 x) y (soluciones2 x). Por ejemplo, -- ghci> prop_equivalencia 2000 -- True prop_equivalencia :: Integer -> Bool prop_equivalencia m = and [soluciones1 x == soluciones2 x | x <- [0..m]] -- --------------------------------------------------------------------- -- § Comparación de la eficiencia -- -- --------------------------------------------------------------------- -- La segunda definición es más eficiente como se comprueba en el -- siguiente ejemplo: -- ghci> length (soluciones1 10000000) -- 5 -- (34.39 secs, 1360001588 bytes) -- ghci> length (soluciones2 10000000) -- 5 -- (0.05 secs, 3098372 bytes) |