Curioseando por la página Project Euler, he encontrado el problema nº 20 gracias a jAB. Dice lo siguiente:
Encuentra la suma de los dígitos del número 100! (Factorial de 100)
Así que me he dispuesto a hacer en Python dicho problema. La primera solución ha sido con el siguiente código fuente:
La función factorial evidente es:
def factorial(n):
"""Devuelve el factorial del argumento recibido"""
if n == 1:
return 1
else:
return n * factorial(n - 1)Una vez tenemos la manera de calcular un factorial, procedemos con la suma del mismo:
def sumadig(n):
"""Devuelve la suma de los dígitos del argumento recibido"""
nstr = str(n)
lista = []
for i in range(len(nstr)):
lista.append(nstr[i])
suma = 0
for i in lista:
suma += int(i)
return sumaSe invoca la llamada a dicha función así (en el main):
def main():
print sumadig(factorial(100))
if __name__ == '__main__':
main()Todo esto está muy bien y funciona. Pero... vamos a darle una vuelta de tuerca mediante una serie de aproximaciones para mejorarlo:
La función factorial está muy bien así, aunque seguramente alguno podría exigir un poco más. Tampoco se puede optimizar mucho, pero vamos a aprovechar para presentar una construcción curiosa del lenguaje que conocí usando Ruby y luego me enteré de que en Python también estaba disponible:
def factorial(n):
"""Devuelve el factorial del argumento recibido"""
return (1 if n == 0 else n * factorial(n - 1))En la función sumadig hay mucho margen de mejora. Básicamente lo que hacemos en la misma es:
Así que vamos por partes:
nstr = str(n)
lista = []
for i in range(len(nstr)):
lista.append(nstr[i])lista = list(str(n))
suma = 0
for i in lista:
suma += int(i)lista2 = map(int, lista) reduce (lamda x, y: x+y, lista2)
reduce(lambda x, y: x+y, map(int, list(str(n))))
return reduce(lambda x, y: x+y, map(int, list(str(n))))
En resumen, la función sumadig nos quedaría así:
def sumadig(n):
"""Devuelve la suma de los dígitos del argumento recibido"""
return reduce(lambda x, y: x+y, map(int, list(str(n))))Está bastante compacto el código fuente y a lo mejor resulta poco comprensible pero creo que es un buen ejercicio para comprender el funcionamiento de las funciones map y reduce.
Espero que os haya gustado el artículo.
Saludos.
Soy Diplomado en Informática por la E.U.I. de la Universidad Politécnica de Madrid, lo equivale ahora a Ingeniero Técnico en Informática de Sistemas. He estado trabajando en muchos proyectos en varias empresas y con entornos muy diferentes, desde aplicaciones bancarias a sistemas de emergencias, pasando por simuladores de vuelo. Me gusta aprender de muchos campos diferentes, aunque al final uno siempre tiene la sensación de ser aprendiz de todo y maestro de nada.
quintesse escribió
hace 1 años
anhelido escribió
hace 1 años
Pues efectivamente, nada mal y además se entiende muy bien aun sin saber Scala.
cort084 escribió
hace 1 años
La verdad es que es interesante la forma de resolver ese problema, me recuerda bastante a Haskell.
gimenete escribió
hace 1 años
Buen artículo!
Sugerencia: en Python se puede simplificar un poco más usando la función sum()
return sum(map(int, list(str(n))))
Pero bueno, como el artículo va de map-reduce, como ejemplo es mejor como está, usando reduce() :)
Venkman escribió
hace 1 años
Muy interesante como ejercicio para usar y explicar map y reduce.
Mi retorcida mente, sin embargo, está tentándome con una serie de temas perpendiculares sobre Python... Por ejemplo, ¿cómo podríamos mejorar ese factorial? Sin entrar en temas de rendimiento, usar una solución recursiva ya tiene la limitación del "maximum recursion depth exceeded" si en lugar de calcular 100! calculamos 1000! (Sí, sí, podemos aumentar ese límite, pero siempre está ahí como límite). Así que se me ocurre que podríamos hacerlo más funcional precisamente usando reduce...
factorial = lambda n:reduce(lambda a,b:a*(b+1),range(n),1) print sumadig(factorial(10000))
Así, en unas pruebas rápidas, no veo mucha diferencia de rendimiento. Pero a similar rendimiento, esta forma funcional no tiene problemas de recursión. Podemos fácilmente pedirle el factorial de 10000 mientras que a la forma original no he conseguido sacarle ni el de 5000 (incluso aumentando el límite de recursión).
anhelido escribió
hace 1 años
Si ya sabía yo que me ayudarías a aprender Python! :P
chemacortes escribió
hace 9 meses
Ahora mismo, en python se están abandonado tanto map como reduce. En su lugar se usan los "iteradores" que dan bastante más juego. Por ejemplo, para sumar los dígitos del factorial:
print sum(int(digito) for digito in str(factorial(10000)))
Una de las ventajas es que no hace falta convertir el número a lista, ya que las cadenas de caracteres son ya en sí mismas "secuencias" por las que se puede iterar.
© Copyright 2008-2009 debug_mode=ON | Aviso legal | Contacto | FAQ | ¿Quiénes somos? |
#1
Muy claro, me ha gustado, puedes escribir mas artículos como este jeje
Pero no podía dejar y intentar hacer lo mismo en Scala para ver como se quedaría:
def sumadig(n: Int) = n.toString.elements.map(.asDigit).reduceLeft( + _)
Tampoco esta nada mal!