0% encontró este documento útil (0 votos)
3 vistas58 páginas

Funciones Métodos Numéricos

El documento describe diversas funciones y métodos numéricos en Python, incluyendo funciones de la biblioteca math y cómo definir funciones personalizadas. Se presentan ejemplos prácticos para calcular raíces cuadráticas, determinar números primos y realizar operaciones básicas, además de la importancia de la documentación mediante docstrings. También se abordan características de funciones en Python, como aquellas que no reciben argumentos y las que retornan múltiples valores.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
3 vistas58 páginas

Funciones Métodos Numéricos

El documento describe diversas funciones y métodos numéricos en Python, incluyendo funciones de la biblioteca math y cómo definir funciones personalizadas. Se presentan ejemplos prácticos para calcular raíces cuadráticas, determinar números primos y realizar operaciones básicas, además de la importancia de la documentación mediante docstrings. También se abordan características de funciones en Python, como aquellas que no reciben argumentos y las que retornan múltiples valores.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 58

Funciones_métodos_numéricos

September 5, 2024

0.0.1 Funciones de Python


Python incluye una gran cantidad de funciones que se agrupan en diversas bibliotecas. Algunas de
estas bibliotecas deben ser importadas antes de su uso. Por ejemplo, las bibliotecas math y random
se incluyen de manera predeterminada en Python, pero hay muchas otras bibliotecas que requieren
ser instaladas antes de utilizarlas.

Funciones en la Biblioteca math La biblioteca math proporciona varias funciones matemáticas


que son útiles en cálculos científicos. A continuación, se muestra una lista de algunas funciones
disponibles en la biblioteca math:

Función Nombre Ejemplo


Raíz cuadrada sqrt(x) sqrt(2.3) = 1.5166
Seno de x sin(x) sin(2.3) = 0.7457
Módulo y residuo de x, a divmod(x, a) divmod(7, 2) = (3, 1)
Seno hiperbólico de x sinh(x) sinh(2.3) = 4.9370
Logaritmo natural de x log(x) log(2.3) = 0.8329
Logaritmo decimal de x log10(x) log10(2.3) = 0.3617
Exponencial de x exp(x) exp(2.3) = 9.9742
Función exponencial 10**x 10**2.3 = 199.5262
Función techo ceil(x) ceil(2.3) = 3
Función piso floor(x) floor(2.3) = 2
Potencia pow(x, y) pow(2, 3) = 2**3 = 8

[ ]: import math

# Raíz cuadrada
print(math.sqrt(2.3)) # 1.5166

# Seno de x
print(math.sin(2.3)) # 0.7457

# Logaritmo natural de x
print(math.log(2.3)) # 0.8329

# Potencia
print(math.pow(2, 3)) # 8.0

1
1.51657508881031
0.7457052121767203
0.8329091229351039
8.0

Funciones Definidas por el Usuario En ocasiones, las funciones predefinidas no son suficientes
para nuestros propósitos. En esos casos, podemos definir nuestras propias funciones. Las funciones
personalizadas en Python se definen con la palabra clave def, seguida del nombre de la función y
los parámetros entre paréntesis.
Formato para definir una función:
def nombre_de_la_funcion(parametro_1, ..., parametro_n):
# INSTRUCCIONES
return variable
Puntos importantes al definir una función:
• La función debe comenzar con la palabra clave def.
• El nombre de la función sigue a la palabra clave def.
• Los parámetros de la función se colocan entre paréntesis y están separados por comas. Estos
también se llaman argumentos.
• Después de los paréntesis, se coloca dos puntos :.
• El cuerpo de la función contiene las instrucciones que forman la funcionalidad de la función,
con sangría.
• La función finaliza con la instrucción return seguida de la variable que deseamos devolver
como resultado.

0.0.2 Ejemplo de función:


Vamos a definir en Python la función $ f(x) = x^2 + 1 $, evaluarla en diferentes valores, calcular
$ f(f(2)) $ y $ f(f(f(3))) $, y verificar si $ f(3) = 5 ×2 $.

[ ]: import math

# Definición de la función f(x) = x^2 + 1


def f(x):
return x**2 + 1

[ ]: # Evaluación de la función en diferentes valores


evaluacion_4 = f(4)
evaluacion_pi = f(math.pi)
evaluacion_e = f(math.e)

print(f"f(4) = {evaluacion_4}")
print(f"f(pi) = {evaluacion_pi}")
print(f"f(e) = {evaluacion_e}")

f(4) = 17
f(pi) = 10.869604401089358

2
f(e) = 8.389056098930649

[ ]: # Cálculo de f(f(2))
f_f_2 = f(f(2))

# Cálculo de f(f(f(3)))
f_f_f_3 = f(f(f(3)))

print(f"f(f(2)) = {f_f_2}")
print(f"f(f(f(3))) = {f_f_f_3}")

f(f(2)) = 26
f(f(f(3))) = 10202

[ ]: # Verificación si f(3) = 5 * 2
es_igual = f(3) == 5 * 2

print(f"¿f(3) == 5 * 2? : \n {es_igual}")

¿f(3) == 5 * 2? :
True

0.0.3 Explicación del Código


1. Definición de la Función f(x):
• Se define la función $ f(x) = x^2 + 1 $ usando la palabra clave def.
2. Evaluaciones de la Función:
• La función se evalúa en $ x = 4 $, $ x = �$ (pi), y $ x = e $ (constante de Euler). Para
esto se utiliza la biblioteca math para obtener los valores de math.pi y math.e.
3. Cálculo de Composiciones de la Función:
• Se calcula $ f(f(2)) $, lo que significa que primero se evalúa $ f(2) $ y luego se utiliza ese
resultado como entrada para otra evaluación de $ f(x) $.
• Similarmente, se calcula $ f(f(f(3))) $, lo cual implica evaluar la función tres veces de
manera anidada.
4. Verificación de una Igualdad:
• Se verifica si $ f(3) = 5 ×2 $. Si esta igualdad es verdadera, la variable es_igual será
True; de lo contrario, será False.
5. Mostrar Resultados:
• Se imprimen todos los resultados de las evaluaciones y verificaciones.

0.0.4 Ejemplo de Función: Determinar Números Primos en Python


El siguiente ejemplo muestra cómo definir una función para determinar si un número es primo. El
algoritmo comprueba si el número es divisible por algún número menor que él. Si encuentra un
divisor distinto de 1 y del propio número, concluye que el número no es primo. Este subalgoritmo
se implementa de la siguiente manera:

[ ]: def primo(numero):
# Dado un número entero positivo, determina si es primo o no.

3
from math import sqrt
es_primo = True # Suponemos que es primo.
divisor = 2 # Comenzamos a probar desde el número 2.

# Recorremos desde 2 hasta la raíz cuadrada del número.


while divisor <= sqrt(numero) and es_primo:
if numero % divisor == 0: # Si encontramos un divisor exacto.
es_primo = False # No es primo.
else:
divisor += 1 # Seguimos buscando.

return es_primo

[ ]: # Ejemplo de uso
numero = int(input("Introduce un número: "))
if primo(numero):
print(f"{numero} es un número primo.")
else:
print(f"{numero} no es un número primo.")

Introduce un número: 23
23 es un número primo.
En este ejemplo, la función primo toma un número entero como argumento y determina si es primo.
Para optimizar el proceso, el algoritmo solo verifica los divisores hasta la raíz cuadrada del número
dado, ya que si un número no tiene divisores menores o iguales a su raíz cuadrada, entonces no
tendrá divisores mayores a ella.

0.0.5 Ejemplo: Solución de una Ecuación de Segundo Grado


Para resolver ecuaciones
√ de segundo grado de la forma 𝑎𝑥2 + 𝑏𝑥 + 𝑐 = 0, necesitamos calcular el
discriminante 𝑑 = 𝑏2 − 4𝑎𝑐.

[ ]: import numpy as np

def calcular_raices(a, b, c):


"""
Calcula las raíces de una ecuación cuadrática de la forma ax^2 + bx + c = 0.
Utiliza numpy para manejar los cálculos de discriminante y raíces.
"""
# Calculamos el discriminante
discriminante = b**2 - 4*a*c

# Verificamos el valor del discriminante para determinar el tipo de raíces


if discriminante > 0:
# Si el discriminante es positivo, hay dos raíces reales y diferentes
x1 = (-b + np.sqrt(discriminante)) / (2*a)
x2 = (-b - np.sqrt(discriminante)) / (2*a)

4
# Redondeamos las soluciones a dos decimales
x1 = round(x1, 2)
x2 = round(x2, 2)
print(f"La ecuación tiene dos raíces reales diferentes: x1 = {x1} y x2␣
↪= {x2}")

elif discriminante == 0:
# Si el discriminante es cero, hay una raíz real doble
x = -b / (2*a)
# Redondeamos la solución a dos decimales
x = round(x, 2)
print(f"La ecuación tiene una raíz real doble: x = {x}")
else:
# Si el discriminante es negativo, hay dos raíces complejas
parte_real = -b / (2*a)
parte_imaginaria = np.sqrt(-discriminante) / (2*a)
# Redondeamos las partes real e imaginaria a dos decimales
parte_real = round(parte_real, 2)
parte_imaginaria = round(parte_imaginaria, 2)
print(f"La ecuación tiene dos raíces complejas: x1 = {parte_real} +␣
↪{parte_imaginaria}i y x2 = {parte_real} - {parte_imaginaria}i")

# Programa principal
# Solicitamos al usuario que ingrese los coeficientes 'a', 'b' y 'c'
a = float(input("Ingresa el valor de a (coeficiente de x^2): "))
b = float(input("Ingresa el valor de b (coeficiente de x): "))
c = float(input("Ingresa el valor de c (término independiente): "))

# Calculamos y mostramos las raíces


calcular_raices(a, b, c)

Ingresa el valor de a (coeficiente de x^2): 2


Ingresa el valor de b (coeficiente de x): 2
Ingresa el valor de c (término independiente): 2
La ecuación tiene dos raíces complejas: x1 = -0.5 + 0.87i y x2 = -0.5 - 0.87i

0.0.6 Explicación:
1. Uso de numpy: El cálculo de la raíz cuadrada y otras operaciones matemáticas se realizan
con numpy, que es más robusto y eficiente para manejo de datos numéricos.
2. Función calcular_raices: Esta función reemplaza la función de discriminante original.
Calcula las raíces dependiendo del valor del discriminante (positivo, cero o negativo).
3. Estructura Condicional: Se añaden condicionales para determinar si las raíces son reales
diferentes, reales dobles o complejas.
4. Redondeo: Se usa round para redondear las soluciones a dos decimales para una salida más
legible.
Este código hace uso de numpy para manejar las raíces complejas y se mantiene fiel a la lógica del

5
cálculo cuadrático.

0.0.7 Nota:
un docstring, es un comentario explicativo que se coloca al principio de una función para describir
lo que hace. En Python, los docstrings se utilizan para documentar el propósito y funcionamiento
de una función, clase o módulo.
En el ejemplo anterior incluimos el docstring:
"""
Calcula las raíces de una ecuación cuadrática de la forma ax^2 + bx + c = 0.
Utiliza numpy para manejar los cálculos de discriminante y raíces.
"""

0.0.8 Importancia del Docstring:


• Documentación: Ayuda a otros desarrolladores (o a ti mismo en el futuro) a entender
rápidamente qué hace la función y cómo utilizarla.
• Ayuda Interactiva: En entornos interactivos como Jupyter Notebook (o Colab), cuando se
llama a help(funcion) o se coloca el cursor sobre el nombre de la función, este docstring se
muestra como parte de la ayuda.
• Buenas Prácticas: Es considerado una buena práctica en Python documentar cada función
con un docstring, especialmente en código que será reutilizado, compartido o mantenido por
diferentes personas.
Este docstring es especialmente útil en celdas de texto de Colab o Jupyter Notebook, donde la
documentación clara es crucial para entender cada parte del código, especialmente en entornos
educativos o de aprendizaje.

[ ]: help(calcular_raices)

Help on function calcular_raices in module __main__:

calcular_raices(a, b, c)
Calcula las raíces de una ecuación cuadrática de la forma ax^2 + bx + c = 0.
Utiliza numpy para manejar los cálculos de discriminante y raíces.

En Python, las funciones pueden comportarse de manera muy flexible, permitiendo una amplia
gama de funcionalidades. Dos de estas características son:
1. Funciones que no reciben argumentos
2. Funciones que retornan múltiples valores

0.0.9 1. Funciones que no reciben argumentos


Una función que no recibe argumentos es una función que no necesita ningún valor de entrada
para ejecutar su tarea. Este tipo de funciones suelen utilizarse cuando la operación que realiza la
función no depende de valores externos, sino de valores definidos dentro de la misma función o de
variables globales.

6
[ ]: def mostrar_mensaje():
"""
Esta función imprime un mensaje de bienvenida.
No recibe argumentos y no depende de entradas externas.
"""
print("¡Bienvenido a nuestro programa!")

# Llamada a la función
mostrar_mensaje()

¡Bienvenido a nuestro programa!


En este ejemplo, la función mostrar_mensaje() no requiere ningún argumento. Simplemente
ejecuta la instrucción de imprimir un mensaje cada vez que se llama.

Aplicaciones de funciones sin argumentos:


• Configuraciones iniciales: Inicializar variables o establecer configuraciones al inicio de un
programa.
• Mensajes estándar: Mostrar mensajes de bienvenida, instrucciones o advertencias.
• Tareas específicas autónomas: Realizar tareas que no dependen de entradas, como
mostrar la fecha y hora actual.

0.0.10 2. Funciones que retornan múltiples valores


En Python, las funciones que retornan múltiples valores permiten devolver más de un resul-
tado al llamarlas. Esto es muy útil cuando se necesitan obtener varios resultados de un cálculo o
proceso. Para devolver múltiples valores, simplemente se listan separados por comas en la sentencia
return.
Cuando se llaman, los valores devueltos se reciben como una tupla, la cual puede ser desempaque-
tada en variables individuales.

Ejemplo de función que retorna múltiples valores


[ ]: def operaciones_basicas(a, b):
"""
Esta función recibe dos números 'a' y 'b' y retorna su suma, resta,␣
↪multiplicación y división.

"""
suma = a + b
resta = a - b
multiplicacion = a * b
division = a / b if b != 0 else "Indefinida" # Evitar división por cero
return suma, resta, multiplicacion, division

[ ]: # Llamada a la función y desempaquetado de resultados


resultado_suma, resultado_resta, resultado_multiplicacion, resultado_division =␣
↪operaciones_basicas(10, 5)

7
# Mostrar los resultados
print(f"Suma: {resultado_suma}, Resta: {resultado_resta}, Multiplicación:␣
↪{resultado_multiplicacion}, División: {resultado_division}")

Suma: 15, Resta: 5, Multiplicación: 50, División: 2.0


En este ejemplo, la función operaciones_basicas devuelve cuatro valores: la suma, resta, multi-
plicación y división de dos números proporcionados. Los valores se retornan como una tupla que
se puede desempaquetar en variables separadas.

Aplicaciones de funciones que retornan múltiples valores:


• Cálculos matemáticos complejos: Donde se necesiten varios resultados derivados de un
conjunto de datos.
• Procesamiento de datos: Donde una función puede analizar datos y devolver varios as-
pectos de los resultados, como promedio, mediana, moda, etc.
• Manipulación de listas y estructuras: Para devolver elementos modificados junto con
algunos metadatos.

0.0.11 Conclusión
Las funciones sin argumentos y las que retornan múltiples valores son poderosas herramientas en
Python que facilitan el diseño de programas más organizados, flexibles y fáciles de entender. Estas
características permiten manejar operaciones de manera eficiente y reutilizar código de manera
efectiva.

0.0.12 Procedimientos en Python


Los procedimientos, al igual que las funciones, son subalgoritmos que realizan tareas específicas
dentro de una solución general. La diferencia clave entre procedimientos y funciones es que los
procedimientos no devuelven valores. Se usan directamente como una instrucción dentro del
algoritmo principal.

Formato General de un Procedimiento El formato general de un procedimiento en Python


es similar al de una función, pero no incluye la instrucción return. Un procedimiento se define de
la siguiente manera:
def nombre_procedimiento(parametro1, ..., parametroN):
# Variables de uso interno
# Instrucciones

Ejemplo: Lista de 20 Enteros Se desea elaborar un algoritmo que genere una lista de 20
enteros ordenados de manera vertical. Entre cada entero, se desea escribir una línea formada por
tres guiones bajos ___. Esto se puede lograr utilizando un procedimiento en Python que imprima
esta línea repetidamente.

Procedimiento para Dibujar la Línea


[ ]: def dibujar_linea():
"""

8
Procedimiento que dibuja una línea de tres guiones bajos.
"""
for i in range(3):
print("_", end=" ")
print() # Para salto de línea

Programa Principal para Mostrar la Lista de Enteros El programa principal utiliza el


procedimiento dibujar_linea para mostrar la lista de enteros del 1 al 20, con una línea de guiones
bajos después de cada número.

[ ]: # Algoritmo que despliega una lista de enteros del 1 al 20.


for k in range(1, 21):
print(k)
dibujar_linea()

1
_ _ _
2
_ _ _
3
_ _ _
4
_ _ _
5
_ _ _
6
_ _ _
7
_ _ _
8
_ _ _
9
_ _ _
10
_ _ _
11
_ _ _
12
_ _ _
13
_ _ _
14
_ _ _
15
_ _ _
16
_ _ _
17

9
_ _ _
18
_ _ _
19
_ _ _
20
_ _ _
Al ejecutar este programa, se mostrará una lista de enteros del 1 al 20, cada uno separado por una
línea de guiones bajos.

Ejemplo: Obtención del Módulo y Cociente de Dos Números Enteros El módulo de


dos números enteros a y b se define como el residuo de la división a/b. Para obtener el residuo, es
necesario efectuar la división entera, multiplicar el cociente por el divisor, y restar este resultado
del dividendo.

Procedimiento para Calcular el Módulo y Cociente A continuación, se presenta un pro-


cedimiento que calcula tanto el módulo como el cociente de dos números enteros:

[ ]: def calcular_modulo_y_cociente(a, b):


"""
Procedimiento que recibe dos números enteros 'a' y 'b' y
muestra el módulo (residuo) y el cociente de la división entera.
"""
residuo = a % b # Módulo de a y b
cociente = a // b # División entera de a y b
print(f"Residuo: {residuo}, Cociente: {cociente}")

#Programa principal

# Solicitar al usuario los números enteros


a = int(input("Introduce el primer número entero (a): "))
b = int(input("Introduce el segundo número entero (b): "))

# Llamada al procedimiento para mostrar el módulo y cociente


calcular_modulo_y_cociente(a, b)

Introduce el primer número entero (a): 56


Introduce el segundo número entero (b): 5
Residuo: 1, Cociente: 11

Uso del Procedimiento en el Programa Principal El programa principal solicita al usuario


dos números enteros y utiliza el procedimiento calcular_modulo_y_cociente para calcular y
mostrar el módulo y el cociente.

0.0.13 Conclusión
Los procedimientos en Python son útiles cuando necesitamos realizar tareas repetitivas dentro de
un programa, sin la necesidad de retornar un valor. Siguiendo la estructura básica presentada, se

10
pueden implementar diferentes tipos de procedimientos que faciliten la organización y claridad del
código.

0.0.14 Uso de la Función main() en Python


En muchos lenguajes de programación populares, se requiere que cada programa tenga una función
llamada main(). Esta función indica al computador dónde comenzar la ejecución. En Python, al
ser un lenguaje de secuencias de comandos (scripting), la ejecución comienza al inicio de un fichero,
y las declaraciones subsecuentes se ejecutan secuencialmente. Por tanto, no hay una necesidad
explícita de una función main().
A pesar de esto, muchos programadores prefieren adherirse a esta convención y escriben sus progra-
mas de manera que todas las declaraciones se contengan dentro de una función, a menudo llamada
main(). Después de definir todas las funciones necesarias, incluida la main(), se llama a esta última
para iniciar la ejecución.

Ejemplo del Uso de main() en Python En el siguiente ejemplo, un programa utiliza la función
main() para calcular el Índice de Masa Corporal (BMI). Aquí, asumimos que el código se guarda en
un archivo y que este archivo se ejecuta, por ejemplo, en una sesión de IDLE. La última declaración
es una llamada a la función main(), que gestiona las operaciones necesarias para el cálculo del BMI.

[ ]: def get_wh():
"""
Solicita al usuario el peso en kilogramos (kg) y la altura en metros (m).
Retorna el peso y la altura.
"""
weight = float(input("Ingrese el peso [kg]: "))
height = float(input("Ingrese la altura [m]: "))
return weight, height

def calc_bmi(weight, height):


"""
Calcula el BMI utilizando el peso (kg) y la altura (m) proporcionados.
"""
return weight / (height * height)

def show_bmi(bmi):
"""
Muestra el BMI calculado.
"""
print("Su índice de masa corporal (BMI) es:", round(bmi, 2))

def main():
"""
Función principal que llama a las otras funciones para obtener entrada,
calcular el BMI y mostrar el resultado.
"""
w, h = get_wh()

11
bmi = calc_bmi(w, h)
show_bmi(bmi)

main() # Inicia el cálculo.

Ingrese el peso [kg]: 68


Ingrese la altura [m]: 1.68
Su índice de masa corporal (BMI) es: 24.09
Al ejecutar este archivo, el flujo de ejecución comenzará con la función main(), que a su vez llamará
a las otras funciones definidas para solicitar entrada del usuario, calcular el BMI y mostrarlo.

0.0.15 Uso de if __name__ == "__main__" en Python


En Python, if __name__ == "__main__" es una construcción común que se utiliza para permitir
que un archivo actúe como un programa ejecutable o como un módulo importable sin necesidad de
modificar su código.

Explicación Cuando un archivo de Python se ejecuta, Python establece la variable especial


__name__. Si el archivo es ejecutado directamente, __name__ se establece a "__main__". Sin
embargo, si el archivo es importado como un módulo en otro script, __name__ toma el nombre del
archivo.
Por lo tanto, utilizando la condición if __name__ == "__main__":, puedes especificar una sección
de código que solo se ejecutará cuando el archivo se ejecute directamente y no cuando se importe.

0.0.16 Uso de if __name__ == "__main__" en Python


En Python, el condicional if __name__ == "__main__" es una construcción especial que nos per-
mite controlar si un bloque de código se ejecuta automáticamente cuando el script se ejecuta
directamente o no. Entender su funcionamiento es esencial para crear scripts modulares y reutiliz-
ables.

0.0.17 ¿Qué es __name__ en Python?


En Python, todas las funciones y clases tienen un atributo especial llamado __name__. El script
principal de Python, es decir, el que se ejecuta directamente, siempre tiene el nombre __main__.
Otros módulos importados en este script tendrán como nombre el nombre del módulo.

0.0.18 ¿Cómo funciona if __name__ == "__main__"?


Cuando un script es ejecutado directamente, __name__ se establece como __main__. Esto significa
que el bloque de código dentro de if __name__ == "__main__" se ejecutará solo cuando el script
sea ejecutado directamente. Sin embargo, si se importa como módulo en otro script, __name__ se
establecerá como el nombre del módulo y ese bloque de código no se ejecutará.

Ejemplo:
[ ]: # mi_modulo.py
def funcion_saludo():
print("¡Hola desde una función en mi_modulo.py!")

12
if __name__ == "__main__":
print("¡Este mensaje solo se muestra cuando mi_modulo.py es ejecutado␣
↪directamente!")

funcion_saludo()

¡Este mensaje solo se muestra cuando mi_modulo.py es ejecutado directamente!


¡Hola desde una función en mi_modulo.py!
En este ejemplo, si ejecutas mi_modulo.py directamente, verás ambos mensajes impresos. Si se
importa mi_modulo.py en otro archivo como un módulo, solo la función funcion_saludo() se
importará y el bloque bajo if __name__ == "__main__" no se ejecutará.

0.0.19 Ejemplo:
Volvamos a considerar el siguiente ejemplo: un programa utiliza la función main() para calcular el
Índice de Masa Corporal (BMI). Aquí, asumimos que el código se guarda en un archivo y que este
archivo se ejecuta, por ejemplo, en una sesión de IDLE. La última declaración es una llamada a la
función main(), que gestiona las operaciones necesarias para el cálculo del BMI.

[ ]: def get_wh():
"""
Solicita al usuario el peso en kilogramos (kg) y la altura en metros (m).
Retorna el peso y la altura.
"""
weight = float(input("Ingrese el peso [kg]: "))
height = float(input("Ingrese la altura [m]: "))
return weight, height

def calc_bmi(weight, height):


"""
Calcula el BMI utilizando el peso (kg) y la altura (m) proporcionados.
"""
return weight / (height * height)

def show_bmi(bmi):
"""
Muestra el BMI calculado.
"""
print("Su índice de masa corporal (BMI) es:", round(bmi, 2))

def main():
"""
Función principal que llama a las otras funciones para obtener entrada,
calcular el BMI y mostrar el resultado.
"""
w, h = get_wh()
bmi = calc_bmi(w, h)

13
show_bmi(bmi)

if __name__ == "__main__":
main() # Solo se ejecutará si el archivo es ejecutado directamente.

0.0.20 Beneficios de Usar if __name__ == "__main__":


1. Reutilización del Código: Permite que el código sea reutilizado importándolo como módulo
sin ejecutar el código de prueba o el bloque main.
2. Modularidad: Facilita la escritura de código modular y permite dividir programas en fun-
ciones reutilizables.
3. Organización: Ayuda a mantener el código de prueba o de ejecución separado de la lógica
principal de la aplicación, lo cual es útil para pruebas unitarias o scripts grandes.
4. Claridad: Hace más claro cuál es el punto de entrada de un programa.
Al utilizar estas construcciones en Python, se puede crear código más limpio, reutilizable y fácil de
entender.

0.0.21 Usando if __name__ == "__main__" para pruebas


Es muy común usar if __name__ == "__main__" para escribir pruebas simples o código de de-
mostración dentro del mismo archivo. Esto facilita la verificación de que las funciones funcionan
como se espera.

Ejemplo de Prueba:
[ ]: # mi_calculadora.py

def suma(a, b):


return a + b

def resta(a, b):


return a - b

if __name__ == "__main__":
print("Probando las funciones de mi_calculadora.py")
print("2 + 3 =", suma(2, 3))
print("5 - 3 =", resta(5, 3))

Probando las funciones de mi_calculadora.py


2 + 3 = 5
5 - 3 = 2
En este ejemplo, mi_calculadora.py puede ser importado en otro archivo sin ejecutar la parte
de prueba. Pero si mi_calculadora.py es ejecutado directamente, se verán los resultados de las
pruebas impresos.

14
0.0.22 Conclusión
El uso de if __name__ == "__main__" es una práctica esencial en Python para crear scripts
modulares, reutilizables y bien organizados. No solo facilita la depuración y las pruebas, sino que
también permite que los módulos sean utilizados por otros scripts sin necesidad de modificar el
código.

0.0.23 Parámetros Opcionales en Python


Python proporciona muchas funciones integradas. Una de las primeras funciones que introdujimos
antes fue la función incorporada print(). Esta función tiene algunas características interesantes
que aún no sabemos cómo incorporar en nuestras propias funciones: print() puede tomar un
número variable de parámetros y también acepta parámetros opcionales. Los parámetros opcionales
son sep y end, que especifican la cadena utilizada para separar argumentos y lo que debería aparecer
al final de la salida, respectivamente. Para demostrar esto, consideremos, el siguiente ejemplo.

0.0.24 Ejemplo

[ ]: # El separador por defecto es un espacio en blanco.


print("Hello", "World")
# Salida: Hello World

# Se establece explícitamente el separador a la cadena "--".


print("Hello", "World", sep="--")
# Salida: Hello--World

# Emite dos declaraciones de print separadas. (Múltiples declaraciones pueden


# aparecer en una línea si están separadas por punto y coma).
print("why", "Hello"); print("World!")
# Salida:
# why Hello
# World!

# Anula el separador predeterminado y el terminador de línea con los


# argumentos opcionales de sep y end.
print("why", "Hello", sep="--", end="^v^"); print("World!")
# Salida:
# why--Hello^v^World!

Hello World
Hello--World
why Hello
World!
why--Hello^v^World!
En la línea 2, print() se llama con dos argumentos. Dado que no se dan argumentos opcionales,
la salida subsiguiente tiene un espacio en blanco que separa los argumentos y la salida termina con
una nueva línea. En la línea 5, el argumento opcional sep se establece en “–” que aparece entre los
argumentos en la salida, como se muestra en la línea 6. En la línea 10, se dan dos declaraciones

15
print() (recuerde que múltiples declaraciones pueden aparecer en una sola línea si están separadas
por punto y coma). La salida de cada una de estas declaraciones termina con los caracteres de
nueva línea predeterminados como se muestra implícitamente en la salida subsiguiente en las líneas
11 y 12. La línea 15 vuelve a contener dos declaraciones print(), pero la primera declaración
print() utiliza los parámetros opcionales para establecer el separador y el terminador de línea en
las cadenas “–” y “v ”, respectivamente.

0.0.25 Definir Funciones con Parámetros Opcionales


Podemos crear funciones en Python usando una declaración def. En el encabezado, entre parén-
tesis, incluimos la lista de parámetros formales para la función. Python proporciona varios con-
structos diferentes para especificar cómo se manejan los parámetros de una función. De hecho,
podemos definir funciones propias que acepten cualquier número arbitrario de argumentos y em-
pleen parámetros opcionales. Explorar todas las construcciones diferentes puede ser una tarea larga
y complicada, y el uso de múltiples argumentos no es de interés actualmente. Sin embargo, crear
funciones con parámetros opcionales es simple y útil. Así que, consideremos cómo se define una
función con parámetros opcionales.
Definamos una función sin parámetros opcionales que eleva al cuadrado su argumento. La función
se define en las líneas 1 y 2, y luego, en la línea 3, se invoca con un argumento de 10.

[ ]: def square(x):
return x * x

square(10)
# Salida: 100

[ ]: 100

0.0.26 Funciones con Parámetros Opcionales


Para crear una función con uno o más parámetros opcionales, simplemente asignamos un valor
predeterminado al(los) parámetro(s) formal(es) correspondiente(s) en el encabezado de la definición
de la función. Un ejemplo ayuda a ilustrar esto.
Creamos una función llamada power() que eleva un número dado a algún exponente. El usuario
puede llamar a esta función con uno o dos argumentos. El segundo argumento es opcional y
corresponde al exponente. Si el exponente no se da explícitamente, se asume que es 2, es decir, la
función elevará al cuadrado el valor dado.
###Ejemplo:

[ ]: def power(x, exponent=2):


return x ** exponent

# Llamadas a la función power() con diferentes números de argumentos


power(10) # 10 al cuadrado, es decir, 10 ** 2.
# Salida: 100
power(3) # 3 al cuadrado, es decir, 3 ** 2.
# Salida: 9

16
power(3, 0.5) # Raíz cuadrada de 3, es decir, 3 ** 0.5.
# Salida: 1.7320508075688772
power(3, 4) # 3 al cubo
# Salida: 81

[ ]: 81

0.0.27 Función para Calcular el Valor de 𝑦 en una Línea Recta con Parámetros Op-
cionales
Consideremos otro ejemplo en el que una función calcula el valor de 𝑦 para una línea recta. Recorde-
mos que la ecuación general para una línea es:

𝑦 = 𝑚𝑥 + 𝑏

donde 𝑥 es la variable independiente, 𝑦 es la variable dependiente, 𝑚 es la pendiente y 𝑏 es la


intersección (es decir, el valor en el que la línea cruza el eje 𝑦). Escribamos una función llamada
line() que, en general, tiene tres argumentos correspondientes a 𝑥, 𝑚 y 𝑏. La pendiente y la
intersección serán parámetros opcionales, y la pendiente tendrá un valor predeterminado de 1 y la
intersección un valor predeterminado de 0.

[ ]: def line(x, m=1, b=0):


return m * x + b

# Llamadas a la función con diferentes números de argumentos


print(line(10)) # x=10 y valores por defecto m=1 y b=0.
# Salida: 10

print(line(10, 3)) # x=10, m=3, y valor por defecto b=0.


# Salida: 30

print(line(10, 3, 4)) # x=10, m=3, b=4.


# Salida: 34

print(line(10, b=4)) # x=10, b=4, y valor por defecto m=1.


# Salida: 14

print(line(10, m=7)) # x=10, m=7, y valor por defecto b=0.


# Salida: 70

10
30
34
14
70
En las líneas 4, 6 y 8, la función se llama con uno, dos y tres argumentos, respectivamente. En estas
tres llamadas, los argumentos no están nombrados; por lo tanto, la asignación a los parámetros

17
formales se basa únicamente en la posición. Cuando hay un solo argumento (real), este se asigna
al parámetro formal 𝑥, mientras que 𝑚 y 𝑏 toman los valores predeterminados de 1 y 0, respecti-
vamente. Cuando hay dos argumentos no nombrados (reales), como en la línea 6, se asignan a los
dos primeros parámetros formales, es decir, 𝑥 y 𝑚. Sin embargo, si uno de los argumentos está
nombrado, como en las líneas 10 y 12, la asignación de parámetros reales a parámetros formales ya
no está dictada por el orden.
En la línea 10, el primer argumento (10) se asigna a 𝑥. El segundo argumento dicta que el parámetro
formal 𝑏 se asigne con un valor de 4. Como no se ha especificado nada para el valor de 𝑚, se le
asigna el valor predeterminado.

0.0.28 Ejemplo: Generación del n-ésimo Número de Fibonacci con Valores Iniciales
Personalizados
En este ejemplo, se crea una función que permite al usuario calcular el n-ésimo número de Fibonacci
con valores iniciales específicos para 𝑎 y 𝑏. Además, si el usuario no proporciona estos valores
iniciales, la función utilizará los valores por defecto de 𝑎 = 1 y 𝑏 = 1.

Definición de la Función sin Valores Iniciales Predeterminados Primero, definimos la


función fibonacci sin establecer valores iniciales predeterminados:

[ ]: def fibonacci(N, a, b):


for i in range(N - 2):
a, b = b, a + b
return b

Esta función toma tres parámetros: - N: El índice del número de Fibonacci a calcular. - a: El
primer número de la secuencia de Fibonacci. - b: El segundo número de la secuencia de Fibonacci.
Ejemplo de Uso:

[ ]: # Calcular el 10º número de Fibonacci comenzando con 1 y 1


print(fibonacci(10, 1, 1)) # Salida: 55

55

Definición de la Función con Valores Iniciales Predeterminados A continuación, se re-


define la función fibonacci para incluir valores predeterminados para 𝑎 y 𝑏:

[ ]: def fibonacci(N, a = 1, b = 1):


for i in range(N - 2):
a, b = b, a + b
return b

Con esta versión de la función, si el usuario no proporciona los valores para 𝑎 o 𝑏, la función usará
𝑎 = 1 y 𝑏 = 1 por defecto.
Ejemplo de Uso:

[ ]: # Calcular el 10º número de Fibonacci usando los valores predeterminados a = 1␣


↪y b = 1

18
print(fibonacci(10)) # Salida: 55

# Calcular el 3º número de Fibonacci con b = 15 y usando el valor␣


↪predeterminado de a = 1

print(fibonacci(3, b = 15)) # Salida: 16

55
16

0.0.29 Recursividad y Funciones Recursivas en Python: Soluciones Eficientes y Ele-


gantes
La recursividad es una técnica en programación en la que una función se llama a sí misma para
resolver un problema. Este enfoque permite dividir problemas complejos en partes más pequeñas
y manejables. Python permite escribir funciones recursivas de manera elegante, pero es crucial
entender cómo funcionan para evitar errores como bucles infinitos o consumo excesivo de memoria.

¿Qué es la Recursividad? En términos simples, la recursividad ocurre cuando una función


se llama a sí misma. Cada llamada a la función resuelve una parte del problema hasta llegar a un
caso base, que es la condición que detiene las llamadas recursivas. Sin un caso base adecuado, la
recursividad puede llevar a un bucle infinito.

0.0.30 Ejemplo Clásico: Factorial de un Número


El factorial de un número entero positivo 𝑛 (representado como 𝑛!) es el producto de todos los
números enteros positivos menores o iguales a 𝑛. En términos recursivos:

𝑛! = 𝑛 × (𝑛 − 1)!

Con el caso base definido como:

0! = 1

Implementación Recursiva en Python


[ ]: def factorial(n):
"""
Calcula el factorial de un número n de forma recursiva.
"""
if n == 0:
return 1 # Caso base
else:
return n * factorial(n - 1) # Llamada recursiva

# Ejemplo de uso
print(factorial(5)) # Salida: 120

120

19
En el código anterior, la función factorial() se llama a sí misma con un valor decreciente de
𝑛 hasta que 𝑛 = 0. Este es un ejemplo típico de recursividad donde el problema se resuelve
reduciéndolo a instancias más pequeñas de sí mismo.

0.0.31 Ejemplo: Serie de Fibonacci


La Serie de Fibonacci es otra secuencia numérica que se presta bien para la recursividad. En
esta serie, cada número es la suma de los dos anteriores:

Fibonacci(𝑛) = Fibonacci(𝑛 − 1) + Fibonacci(𝑛 − 2)

Con los casos base:

Fibonacci(0) = 0, Fibonacci(1) = 1

Implementación Recursiva en Python


[ ]: def fibonacci(n):
"""
Calcula el n-ésimo término de la serie de Fibonacci de forma recursiva.
"""
if n <= 0:
return 0 # Caso base
elif n == 1:
return 1 # Caso base
else:
return fibonacci(n - 1) + fibonacci(n - 2) # Llamadas recursivas

# Ejemplo de uso
print(fibonacci(7)) # Salida: 13

13
Sin embargo, la solución recursiva para Fibonacci no es eficiente. La cantidad de llamadas recursivas
aumenta exponencialmente, resultando en un problema de rendimiento. Para mejorar esto, se
pueden usar técnicas como la memorización o la programación dinámica.

0.0.32 Implementación de Fibonacci con Bucle for


[ ]: def fibonacci_iterativo(n):
"""
Calcula el n-ésimo número de la secuencia de Fibonacci usando un enfoque␣
↪iterativo.

Args:
n (int): La posición del número en la secuencia de Fibonacci.

Returns:
int: El n-ésimo número de Fibonacci.

20
"""
if n == 0:
return 0
elif n == 1:
return 1

a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b

return b

# Ejemplo de uso
print(fibonacci_iterativo(13)) # Salida: 8

233

0.0.33 Comparación entre Métodos de Cálculo de Fibonacci


Ejemplo de recursividad con memorización:

0.0.34 Mejorando la Eficiencia con Memorización


La memorización es una técnica que almacena los resultados de las llamadas a funciones para
evitar calcularlos múltiples veces.

[ ]: def fibonacci_memorizacion(n, memo={}):


"""
Calcula el n-ésimo número de Fibonacci utilizando recursividad con␣
↪memorización.

Args:
n (int): La posición del número en la secuencia de Fibonacci.
memo (dict): Diccionario para almacenar los resultados calculados.

Returns:
int: El n-ésimo número de Fibonacci.
"""
if n in memo:
return memo[n]
if n <= 1:
return n

memo[n] = fibonacci_memorizacion(n - 1, memo) + fibonacci_memorizacion(n␣


↪- 2, memo)

return memo[n]

# Ejemplo de uso

21
print(fibonacci_memorizacion(6)) # Salida: 8

8
En Python, podemos usar la biblioteca functools con el decorador lru_cache para implementar
memorización fácilmente.

[ ]: from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci_mem(n):
"""
Calcula el n-ésimo término de la serie de Fibonacci de forma recursiva con␣
↪memorización.

"""
if n <= 0:
return 0
elif n == 1:
return 1
else:
return fibonacci_mem(n - 1) + fibonacci_mem(n - 2)

# Ejemplo de uso
print(fibonacci_mem(50)) # Salida: 12586269025

12586269025

0.0.35 ¿Cuál Método es Mejor?


1. Recursividad simple:
• Ventajas: Fácil de entender y escribir para problemas que naturalmente se descomponen
en subproblemas más pequeños.
• Desventajas: Tiene una complejidad exponencial 𝑂(2𝑛 ), lo que significa que es ex-
tremadamente ineficiente para grandes valores de 𝑛. Esto es porque recalcula muchas
veces los mismos valores de Fibonacci.
2. Recursividad con Memorización (Memoization):
• Ventajas: Resuelve el problema de recalcular los mismos valores al almacenar los re-
sultados en una estructura como un diccionario. Esto reduce la complejidad de tiempo
a 𝑂(𝑛).
• Desventajas: Usa más memoria para almacenar los resultados calculados, lo que puede
ser un problema en sistemas con memoria limitada.
3. Iteración (con bucle for):
• Ventajas: La implementación iterativa es simple, fácil de entender, y tiene una compleji-
dad de tiempo de 𝑂(𝑛). No requiere almacenamiento adicional como en la memorización.
• Desventajas: Para algunas personas, la recursividad puede parecer más natural para
problemas que tienen una estructura de “divide y vencerás”.
Conclusión: Para calcular números de Fibonacci, la implementación iterativa con un bu-
cle for es generalmente la mejor opción en términos de simplicidad y eficiencia. Sin embargo,

22
para problemas que pueden beneficiarse de la estructura recursiva y tienen muchos subproblemas
repetidos, la recursividad con memorización también es una excelente opción.
En este caso, la memorización evita que la función recursiva vuelva a calcular los mismos valores
múltiples veces, mejorando significativamente el rendimiento.

0.0.36 Recursividad en Problemas de División y Conquista


La recursividad es especialmente útil en algoritmos de división y conquista. Un ejemplo clásico
es el algoritmo de Búsqueda Binaria, que divide repetidamente un conjunto ordenado en mitades
hasta encontrar un elemento o determinar que no está presente.

Implementación de Búsqueda Binaria Recursiva


[ ]: def busqueda_binaria(arr, low, high, x):
"""
Implementa la búsqueda binaria de forma recursiva.
"""
if high >= low:
mid = (high + low) // 2

# Si el elemento está presente en el medio


if arr[mid] == x:
return mid

# Si el elemento está presente en el subarreglo izquierdo


elif arr[mid] > x:
return busqueda_binaria(arr, low, mid - 1, x)

# Si el elemento está presente en el subarreglo derecho


else:
return busqueda_binaria(arr, mid + 1, high, x)
else:
# El elemento no está presente en el array
return -1

# Ejemplo de uso
arr = [2, 3, 4, 10, 40]
x = 10
resultado = busqueda_binaria(arr, 0, len(arr)-1, x)

if resultado != -1:
print(f"Elemento encontrado en el índice {resultado}")
else:
print("Elemento no encontrado en el array")

Elemento encontrado en el índice 3

23
0.0.37 Conclusión
La recursividad permite soluciones limpias y elegantes para muchos problemas algorítmicos. Sin
embargo, debe usarse con precaución, asegurando siempre la presencia de casos base y considerando
técnicas de optimización como la memorización para evitar problemas de rendimiento.

0.1 Funciones Lambda en Python


En Python, las funciones lambda son pequeñas funciones anónimas definidas con la palabra
clave lambda en lugar de def. A diferencia de las funciones convencionales definidas con def, las
funciones lambda no requieren un nombre explícito y generalmente se usan para operaciones simples
y rápidas.

0.1.1 ¿Qué es una Función Lambda?


Una función lambda es una función anónima y compacta que puede tener cualquier número de
argumentos, pero solo puede tener una única expresión. La sintaxis básica para definir una función
lambda es la siguiente:
lambda argumentos: expresión
La función se puede asignar a una variable para que se pueda reutilizar como una función normal.
Por ejemplo:

0.1.2 Ejemplo: Funciones Anónimas (Funciones Lambda) en Python


En este ejemplo, aprenderemos cómo definir funciones anónimas en Python usando la palabra
clave lambda. Las funciones lambda, también conocidas como funciones anónimas, son funciones
pequeñas que no necesitan un nombre explícito. Son útiles cuando necesitas una función rápida y
simple que solo se usa una vez.

Definición Básica de una Función Lambda Podemos definir una función lambda que eleva
un número al cuadrado y le suma 4:

[ ]: lambda x: x**2 + 4

[ ]: <function __main__.<lambda>(x)>

Si queremos utilizar esta función inmediatamente, podemos hacerlo de la siguiente manera:

[ ]: # Evaluar la función lambda para x = 10


print((lambda x: x**2 + 4)(10)) # Salida: 104

104

Asignar una Función Lambda a una Variable Aunque las funciones lambda son anónimas,
se pueden asignar a una variable para usarlas más tarde:

[ ]: # Definir una función lambda que sume 3 a un número


f = lambda x: x + 3

24
# Usar la función lambda con el valor 1
print(f(1)) # Salida: 4

Funciones Lambda con Múltiples Argumentos Las funciones lambda también pueden acep-
tar múltiples argumentos:

[ ]: # Función lambda que suma dos números


print((lambda x, y: x + y)(2, 3)) # Salida: 5

# Función lambda que suma tres números


print((lambda x, y, z: x + y + z)(1, 2, 3)) # Salida: 6

5
6

Funciones Lambda con Valores por Defecto Al igual que las funciones tradicionales, las
funciones lambda pueden tener valores predeterminados para sus parámetros:

[ ]: # Función lambda con un valor predeterminado para z


print((lambda x, y, z = 3: x * y * z)(1, 2)) # Salida: 6

Uso de Funciones Lambda con Argumentos de Diferentes Tipos Puedes pasar diferentes
tipos de argumentos a una función lambda siempre y cuando la expresión dentro de la función sea
válida para esos tipos:

[ ]: # Función lambda que concatena cadenas


print((lambda x, y = ' prepared', z = ' pizza': x + y + z)('You')) # Salida:␣
↪'You prepared pizza'

You prepared pizza

Uso de Funciones Lambda con Argumentos Variables Las funciones lambda también
pueden manejar un número variable de argumentos usando *args o **kwargs:

[ ]: # Función lambda que invierte el orden de los argumentos


print((lambda *z: z[::-1])(1, 2, 3)) # Salida: (3, 2, 1)

(3, 2, 1)

[ ]: doblar = lambda x: 3 * x ** 2 +1
print(doblar(5)) # Salida: 76

76
Otra forma de evaluarla en 𝑥 = 5

25
[ ]: (lambda x: 3 * x ** 2 +1)(5)

[ ]: 76

Definición Básica de una Función Lambda Podemos definir una función lambda que eleva
un número al cuadrado y le suma 4:
lambda x: x**2 + 4
Si queremos utilizar esta función inmediatamente, podemos hacerlo de la siguiente manera:
# Evaluar la función lambda para x = 10
print((lambda x: x**2 + 4)(10)) # Salida: 104

Asignar una Función Lambda a una Variable Aunque las funciones lambda son anónimas,
se pueden asignar a una variable para usarlas más tarde:
# Definir una función lambda que sume 3 a un número
f = lambda x: x + 3

# Usar la función lambda con el valor 1


print(f(1)) # Salida: 4

Funciones Lambda con Múltiples Argumentos Las funciones lambda también pueden acep-
tar múltiples argumentos:
# Función lambda que suma dos números
print((lambda x, y: x + y)(2, 3)) # Salida: 5

# Función lambda que suma tres números


print((lambda x, y, z: x + y + z)(1, 2, 3)) # Salida: 6

Funciones Lambda con Valores por Defecto Al igual que las funciones tradicionales, las
funciones lambda pueden tener valores predeterminados para sus parámetros:
# Función lambda con un valor predeterminado para z
print((lambda x, y, z = 3: x * y * z)(1, 2)) # Salida: 6

Uso de Funciones Lambda con Argumentos de Diferentes Tipos Puedes pasar diferentes
tipos de argumentos a una función lambda siempre y cuando la expresión dentro de la función sea
válida para esos tipos:
# Función lambda que concatena cadenas
print((lambda x, y = ' prepared', z = ' pizza': x + y + z)('You')) # Salida: 'You prepared piz

Uso de Funciones Lambda con Argumentos Variables Las funciones lambda también
pueden manejar un número variable de argumentos usando *args o **kwargs:
# Función lambda que invierte el orden de los argumentos
print((lambda *z: z[::-1])(1, 2, 3)) # Salida: (3, 2, 1)

26
0.1.3 ¿Cuándo usar Funciones Lambda?
Las funciones lambda son más útiles cuando necesitas una función pequeña y desechable que se
usará solo una vez o en un contexto limitado. Algunas de las situaciones comunes donde las
funciones lambda son útiles incluyen:
1. Funciones anónimas como argumentos: A menudo se usan con funciones que esperan
otra función como argumento, como map(), filter() y sorted().
2. Operaciones simples: Cuando tienes operaciones que son tan simples que no justifican la
definición de una función completa con def.
3. Mejorar la legibilidad: Aunque pueden hacer el código más conciso, el uso excesivo de
funciones lambda puede afectar la legibilidad. Úsalas cuando realmente hagan el código más
claro y directo.

0.1.4 Ejemplos de Uso de Lambda en Python


1. Usando lambda con map() La función map() aplica una función a todos los elementos en
una lista u otro iterable. Aquí hay un ejemplo que usa lambda con map() para multiplicar cada
número en una lista por 2:

0.1.5 Ejemplo:

[ ]: numeros = [1, 2, 3, 4, 5]
doblados = list(map(lambda x: x * 2, numeros))
print(doblados) # Salida: [2, 4, 6, 8, 10]

[2, 4, 6, 8, 10]

0.1.6 Ejemplo
Uso de map con Funciones Lambda
[ ]: from numpy import sin

# Aplicar la función seno a los números del 1 al 9


x = map(sin, range(1, 10))
xlist = list(x)
print(xlist)

[0.8414709848078965, 0.9092974268256817, 0.1411200080598672,


-0.7568024953079282, -0.9589242746631385, -0.27941549819892586,
0.6569865987187891, 0.9893582466233818, 0.4121184852417566]

Ejemplo:
[ ]: from math import sin, cos, pi

# Aplicar la función seno a los números del 1 al 9


x = map(sin, range(1, 10))
xlist = list(x)
print(xlist)

27
#### Visualización Usando `matplotlib`
### Podemos combinar funciones lambda con `matplotlib` para crear␣
↪visualizaciones rápidamente:

import matplotlib.pyplot as plt


from math import sin, cos

# Crear listas de valores de seno y coseno


x = map(sin, range(1, 50))
xlist = list(x)
y = map(cos, range(1, 50))
ylist = list(y)

# Graficar los valores


plt.axis('equal')
plt.plot(xlist, ylist)
plt.show()

[0.8414709848078965, 0.9092974268256817, 0.1411200080598672,


-0.7568024953079282, -0.9589242746631385, -0.27941549819892586,
0.6569865987187891, 0.9893582466233818, 0.4121184852417566]

28
Ejemplo:
[ ]: import numpy as np

l = map(lambda x: x * np.sin(x * np.pi/2), range(10))


print(list(l))

[0.0, 1.0, 2.4492935982947064e-16, -3.0, -9.797174393178826e-16, 5.0,


2.204364238465236e-15, -7.0, -3.91886975727153e-15, 9.0]

2. Usando lambda con filter() La función filter() se utiliza para crear un nuevo iterable
que contiene solo los elementos que cumplen con una condición dada. Aquí hay un ejemplo que
utiliza lambda para filtrar números pares de una lista:

[ ]: numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


pares = list(filter(lambda x: x % 2 == 0, numeros))
print(pares) # Salida: [2, 4, 6, 8, 10]

[2, 4, 6, 8, 10]

3. Usando lambda con sorted() La función sorted() ordena una lista u otro iterable. Puedes
usar lambda como una función clave para especificar el criterio de ordenación. Aquí hay un ejemplo
que ordena una lista de tuplas por el segundo elemento:

[ ]: tuplas = [(1, 'uno'), (3, 'tres'), (2, 'dos')]


ordenado = sorted(tuplas, key=lambda x: x[1])
print(ordenado) # Salida: [(2, 'dos'), (1, 'uno'), (3, 'tres')]

[(2, 'dos'), (3, 'tres'), (1, 'uno')]

0.1.7 Diferencia entre lambda y def


Aunque tanto lambda como def se utilizan para definir funciones en Python, existen algunas difer-
encias clave:
• Definición de función def: Se usa para definir funciones con nombre, que pueden tener
múltiples líneas de código.

[ ]: def cuadrado(x):
return x * x

• Definición de función lambda: Se usa para definir funciones anónimas que son típicamente
de una sola línea.
lambda x: x * x

0.1.8 Limitaciones de las Funciones Lambda


Aunque las funciones lambda pueden ser muy útiles, tienen algunas limitaciones importantes:

29
1. Una sola expresión: Una función lambda solo puede contener una expresión. No puede
tener múltiples líneas de código o declaraciones de control de flujo como if o for de manera
explícita.
2. Sin nombre: Las funciones lambda son anónimas, lo que significa que no tienen un nombre
explícito. Aunque esto es útil para funciones desechables, puede hacer que el código sea más
difícil de depurar si se utilizan excesivamente.
3. Legibilidad: Aunque pueden hacer que el código sea más conciso, un uso excesivo de fun-
ciones lambda puede llevar a un código que es difícil de leer y mantener.
Para aplicar una función lambda a un array de NumPy, puedes simplemente pasar el array como
argumento de la función lambda, y NumPy se encargará de aplicar la operación a cada elemento
del array.

0.1.9 Uso de np.vectorize para Aplicar una Función Lambda a un Array de NumPy
Aunque las funciones lambda se pueden aplicar directamente a arrays de NumPy como mostramos
anteriormente, otra forma más eficiente es usar np.vectorize. Esta función “vectoriza” la op-
eración de modo que la función lambda puede ser aplicada a cada elemento del array de manera
más eficiente.

Ejemplo de Uso de np.vectorize con una Función Lambda


[ ]: import numpy as np

# Creamos un array de NumPy


arr = np.array([1, 2, 3, 4, 5])

# Vectorizamos la función lambda


funcion_vectorizada = np.vectorize(lambda x: x ** 2)

# Aplicamos la función vectorizada al array


squared_arr = funcion_vectorizada(arr)

print(squared_arr) # Salida: [ 1 4 9 16 25]

[ 1 4 9 16 25]

0.1.10 Llamada por valor y llamada por referencia


Python, el concepto de “llamado por valor” y “llamado por referencia” puede ser un poco confuso
al principio debido a la forma en que el lenguaje maneja las variables y los objetos. En términos
simples:
• Llamado por valor: La función recibe una copia del valor, y cualquier modificación dentro
de la función no afecta la variable original.
• Llamado por referencia: La función recibe una referencia al valor real, por lo que cualquier
modificación dentro de la función afectará la variable original.
En Python, todas las variables son referencias a objetos en memoria, por lo que el comportamiento
depende del tipo de objeto al que apunta la variable.

30
0.1.11 Llamado por Valor vs. Llamado por Referencia en Python
Python utiliza un modelo que a veces se describe como “llamado por asignación” o “llamado por
objeto-referencia”. La diferencia clave es cómo se comportan los objetos mutables (listas, dic-
cionarios, conjuntos, etc.) y los objetos inmutables (enteros, flotantes, cadenas, tuplas, etc.)
cuando se pasan a funciones.

1. Llamado por Valor Simulado (para objetos inmutables) Cuando se pasa un objeto
inmutable (por ejemplo, un entero o una cadena) a una función, Python en realidad está pasando
una referencia a ese objeto. Sin embargo, como los objetos inmutables no se pueden modificar,
parece que se están pasando “por valor”.

Ejemplo:
[ ]: def modificar_valor(x):
x = 10 # Intentamos cambiar el valor de x
print("Dentro de la función, x =", x)

# Variable original
a = 5
print("Antes de la función, a =", a)

# Llamamos a la función
modificar_valor(a)

# Después de la función
print("Después de la función, a =", a)

Antes de la función, a = 5
Dentro de la función, x = 10
Después de la función, a = 5
En este ejemplo, a sigue siendo 5 después de llamar a la función modificar_valor(). Esto se debe
a que x es una variable local dentro de la función que apunta a una copia del valor de a, no al
objeto original.

2. Llamado por Referencia Simulado (para objetos mutables) Cuando se pasa un objeto
mutable (por ejemplo, una lista o un diccionario) a una función, la función recibe una referencia
al mismo objeto. Por lo tanto, los cambios realizados en el objeto dentro de la función afectan al
objeto original.

Ejemplo:
[ ]: def modificar_lista(lista):
lista.append(4) # Modificamos la lista agregando un nuevo elemento
print("Dentro de la función, lista =", lista)

# Lista original
mi_lista = [1, 2, 3]
print("Antes de la función, mi_lista =", mi_lista)

31
# Llamamos a la función
modificar_lista(mi_lista)

# Después de la función
print("Después de la función, mi_lista =", mi_lista)

Antes de la función, mi_lista = [1, 2, 3]


Dentro de la función, lista = [1, 2, 3, 4]
Después de la función, mi_lista = [1, 2, 3, 4]
En este ejemplo, mi_lista cambia después de llamar a la función modificar_lista() porque la
lista es mutable, y la referencia a la misma lista se pasa a la función. Por lo tanto, cualquier
modificación dentro de la función afecta a la lista original.

0.1.12 Resumen de “Llamado por Valor” vs. “Llamado por Referencia”


1. Objetos Inmutables (llamado por valor simulado): Enteros, flotantes, cadenas, tuplas,
etc. Cuando se pasan a una función, parece que se pasan “por valor” porque cualquier
modificación dentro de la función no afecta al objeto original.
2. Objetos Mutables (llamado por referencia simulado): Listas, diccionarios, conjun-
tos, etc. Cuando se pasan a una función, parecen que se pasan “por referencia” porque las
modificaciones dentro de la función afectan al objeto original.

0.1.13 Notas Importantes:


• Python siempre pasa referencias a objetos, pero si el objeto es inmutable, cualquier cambio
genera un nuevo objeto, lo que se percibe como llamado por valor.
• Si quieres modificar un objeto inmutable dentro de una función y que el cambio se refleje
fuera de la función, debes devolver el nuevo objeto y asignarlo a la variable original fuera de
la función.

0.1.14 Ejemplo Adicional: Cómo Controlar el Comportamiento


Puedes controlar el comportamiento haciendo una copia de la lista antes de pasarla a una función:

[ ]: def modificar_lista(lista):
lista.append(4)
print("Dentro de la función, lista =", lista)

mi_lista = [1, 2, 3]
print("Antes de la función, mi_lista =", mi_lista)

# Llamamos a la función con una copia de la lista


modificar_lista(mi_lista.copy())

# Después de la función
print("Después de la función, mi_lista =", mi_lista)

32
Antes de la función, mi_lista = [1, 2, 3]
Dentro de la función, lista = [1, 2, 3, 4]
Después de la función, mi_lista = [1, 2, 3]
En este caso, mi_lista no se ve afectada porque pasamos una copia de la lista a la función.

0.1.15 Variables Locales y Globales en Python


En Python, las variables se pueden clasificar en dos tipos principales: locales y globales. Esta
clasificación depende del ámbito o alcance en el cual se definen y pueden ser accedidas dentro de
un programa. Vamos a explicar cada tipo en detalle y cómo se manejan en Python, utilizando
ejemplos para ilustrar su funcionamiento.

Variables Locales Las variables locales son aquellas que se definen dentro de una función y
solo son accesibles dentro de esa función. No se pueden usar fuera de su ámbito (scope). Esto
significa que cualquier variable creada dentro de una función es una variable local por defecto y es
destruida después de que la función ha terminado de ejecutarse.
Ejemplo de Variable Local:

[ ]: def funcion_ejemplo():
x = 5 # Variable local
print("Valor de x dentro de la función:", x)

# Programa principal
x = 10 # Variable global
print("Valor de x fuera de la función:", x)

funcion_ejemplo()
print("Valor de x fuera de la función después de llamar a la función:", x)

Valor de x fuera de la función: 10


Valor de x dentro de la función: 5
Valor de x fuera de la función después de llamar a la función: 10
En el ejemplo anterior, x se define tanto fuera como dentro de la función. Dentro de la función,
x es una variable local y no afecta a la x global. Al final del programa, el valor de x fuera de la
función sigue siendo el mismo.

Variables Globales Las variables globales son aquellas que se definen fuera de cualquier
función y están disponibles para cualquier parte del código, tanto dentro como fuera de las funciones.
Para hacer que una variable dentro de una función sea global, se debe usar la palabra clave global.
Ejemplo de Variable Global:

[ ]: x = 10 # Variable global

def funcion_ejemplo():
global x # Declaración de la variable como global
x = x + 5 # Modificación de la variable global

33
print("Valor de x dentro de la función:", x)

# Programa principal
print("Valor de x antes de la función:", x)
funcion_ejemplo()
print("Valor de x después de la función:", x)

Valor de x antes de la función: 10


Valor de x dentro de la función: 15
Valor de x después de la función: 15
En este caso, al utilizar la palabra clave global, x dentro de la función hace referencia a la x
definida fuera de la función. Cualquier modificación que se realice a x dentro de la función afectará
a la x global.

Diferencias Clave entre Variables Locales y Globales


1. Ámbito: Las variables locales solo son accesibles dentro de la función en la que se definen,
mientras que las variables globales son accesibles en todo el programa.
2. Modificación: Las variables locales pueden ser modificadas dentro de su función sin afectar
a las variables con el mismo nombre fuera de la función. Las variables globales, sin embargo,
pueden ser modificadas en cualquier parte del código utilizando la palabra clave global.
3. Uso de la palabra clave global: En Python, para modificar una variable global dentro de
una función, es necesario declarar esa variable como global.

Ejemplo Adicional: Listas y Paso por Referencia En Python, las listas son mutables y se
pasan por referencia, lo que significa que cualquier cambio realizado en una lista dentro de una
función afecta a la lista original fuera de la función.

[ ]: def modificar_lista(lista):
lista.append(4) # Modificación de la lista

# Programa principal
mi_lista = [1, 2, 3]
print("Lista antes de la función:", mi_lista)

modificar_lista(mi_lista)
print("Lista después de la función:", mi_lista)

Lista antes de la función: [1, 2, 3]


Lista después de la función: [1, 2, 3, 4]

0.1.16 Anotación de Tipos en Python (Type Hints)


Las anotaciones de tipos o type hints fueron introducidas en Python 3.5 y permiten especificar
los tipos de datos que se esperan como parámetros de una función y/o como valor de retorno de
la misma. Es importante notar que, aunque las anotaciones de tipos se utilizan principalmente en
funciones, también funcionan en la creación de variables.

34
Veamos un ejemplo donde se define una función para dividir una cadena de texto en dos partes,
utilizando una posición específica indicada en el parámetro:

[ ]: def ssplit(text: str, split_pos: int) -> tuple:


return text[:split_pos], text[split_pos:]

# Ejemplo de uso
resultado = ssplit('Always remember us this way', 15)
print(resultado) # Salida: ('Always remember', ' us this way')

('Always remember', ' us this way')


En el código anterior, añadimos los tipos después de cada parámetro utilizando el símbolo de dos
puntos : como separador. Para el tipo de retorno usamos la flecha ->.
Ahora bien, considera el siguiente caso que puede sorprender:

[ ]: resultado = ssplit([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 5)


print(resultado) # Salida: ([1, 2, 3, 4, 5], [6, 7, 8, 9, 10])

([1, 2, 3, 4, 5], [6, 7, 8, 9, 10])


En este caso, aunque el primer argumento es una lista en vez de una cadena de texto, no se genera
ningún error. Esto ocurre porque las anotaciones de tipos en Python son simplemente indicaciones
y no declaraciones estrictas de tipo. Sin embargo, existen herramientas como mypy que permiten
realizar comprobaciones estáticas de tipos en Python.

Valores por Defecto Así como en la definición habitual de funciones, cuando usamos anotaciones
de tipos también podemos indicar un valor por defecto para los parámetros.
Por ejemplo, podemos definir un valor por defecto en la función ssplit:

[ ]: def ssplit(text: str, split_pos: int = -1) -> tuple:


if split_pos == -1:
split_pos = len(text) // 2
return text[:split_pos], text[split_pos:]

# Ejemplo de uso
resultado = ssplit('Always remember us this way')
print(resultado) # Salida: ('Always rememb', 'er us this way')

('Always rememb', 'er us this way')


En este ejemplo, simplemente añadimos el valor por defecto después de indicar el tipo, utilizando
el signo igual =. Esto permite que el parámetro split_pos tome un valor predeterminado si no se
proporciona un argumento específico al llamar a la función.

Ventajas de las Anotaciones de Tipos Las anotaciones de tipos son una herramienta
poderosa que, cuando se usan de manera adecuada, pueden complementar la documentación de
nuestro código y aclarar ciertos aspectos que pueden ser confusos a primera vista. Su aplicación
depende de la necesidad detectada por el equipo de desarrollo y su uso puede mejorar significati-
vamente la legibilidad y mantenibilidad del código.

35
El siguiente código define una función llamada surface_area_of_cube que calcula el área de la
superficie de un cubo dado su longitud de arista. La función utiliza anotaciones de tipos para
especificar el tipo de datos esperado de los parámetros y el tipo de datos devuelto por la función.
A continuación, se explica cada parte del código y los tipos usados.

Definición de la Función
def surface_area_of_cube(edge_length: float) -> str:
1. Nombre de la función: surface_area_of_cube.
2. Parámetro de entrada:
• edge_length: float: La función espera un parámetro llamado edge_length de tipo
float. Esto significa que el valor pasado a este parámetro debe ser un número de punto
flotante (por ejemplo, 3.5 o 4.0). Este parámetro representa la longitud de la arista del
cubo.
3. Valor de retorno:
• -> str: La flecha -> seguida de str indica que la función devolverá un valor de tipo
str (cadena de texto). Es una anotación de tipo que especifica que el resultado de la
función será una cadena.

Cuerpo de la Función
return f"The surface area of the cube is {6 * edge_length ** 2}."
• Cálculo:
– 6 * edge_length ** 2: El área de la superficie de un cubo se calcula como 6 veces el
cuadrado de la longitud de su arista. El operador ** se utiliza para elevar edge_length
al cuadrado.
– Por ejemplo, si edge_length es 3.0, entonces 6 * 3.0 ** 2 resulta en 54.0.
• Formato de cadena:
– f"The surface area of the cube is {6 * edge_length ** 2}.": Utiliza una f-
string (cadena formateada) para combinar el texto con el cálculo del área de la su-
perficie. Las f-strings permiten insertar expresiones dentro de llaves {} y evaluar esas
expresiones en tiempo de ejecución.
– El resultado del cálculo 6 * edge_length ** 2 se inserta en la cadena dentro de las
llaves {}.

[ ]: def surface_area_of_cube(edge_length: float) -> str:


return f"The surface area of the cube is {6 * edge_length ** 2}."

surface_area_of_cube(3)

[ ]: 'The surface area of the cube is 54.'

Resumen de los Tipos Usados


• Entrada (edge_length: float): Especifica que el argumento debe ser un número de punto
flotante.
• Salida (-> str): Especifica que la función devuelve un str (cadena de texto) que contiene
el resultado del cálculo formateado.

36
Este uso de las anotaciones de tipos mejora la legibilidad del código y facilita la detección de
errores, ya que proporciona información adicional sobre lo que se espera como entrada y salida de
la función.

0.1.17 Funciones que Devuelven Varios Valores en Python


En Python, una función puede devolver varios valores utilizando tuplas. Las tuplas son estructuras
de datos que permiten agrupar varios valores en un solo objeto. Cuando una función devuelve varios
valores, en realidad está devolviendo una tupla que contiene esos valores. Esta característica es útil
cuando se necesita que una función devuelva más de un resultado.

¿Cómo Funcionan las Funciones que Devuelven Múltiples Valores? Cuando queremos
que una función devuelva varios valores, podemos separarlos con comas en la instrucción return.
Por defecto, Python empaqueta estos valores en una tupla, y podemos desempaquetar la tupla en
varias variables al llamar a la función.

Ejemplo 1: Retornar Múltiples Valores desde una Función En el siguiente ejemplo, creare-
mos una función llamada calcula_suma_y_producto que toma dos números como argumentos y
devuelve su suma y su producto.

[ ]: def calcula_suma_y_producto(a: int, b: int) -> tuple:


"""
Calcula la suma y el producto de dos números.
Args:
a (int): Primer número.
b (int): Segundo número.

Returns:
tuple: Una tupla que contiene la suma y el producto de los números.
"""
suma = a + b
producto = a * b
return suma, producto

# Ejemplo de uso
resultado_suma, resultado_producto = calcula_suma_y_producto(3, 5)
print(f"Suma: {resultado_suma}, Producto: {resultado_producto}")

Suma: 8, Producto: 15

Explicación del Ejemplo 1


• Definición de la función: calcula_suma_y_producto toma dos argumentos a y b de tipo
int.
• Cálculo:
– Calcula la suma de a y b y la almacena en la variable suma.
– Calcula el producto de a y b y lo almacena en la variable producto.
• Retorno: La función devuelve dos valores: suma y producto. Estos valores son empaqueta-
dos automáticamente en una tupla.

37
• Desempaquetado: Cuando llamamos a la función, la tupla devuelta se desempaqueta en
dos variables resultado_suma y resultado_producto.

Ejemplo 2: Devolver Varios Resultados Estadísticos Supongamos que queremos calcular


varios resultados estadísticos, como el mínimo, el máximo, el promedio y la suma de una lista de
números.

[ ]: from typing import List, Tuple

def estadisticas(lista: List[int]) -> Tuple[int, int, float, int]:


"""
Calcula estadísticas de una lista de números.

Args:
lista (List[int]): Lista de números enteros.

Returns:
Tuple[int, int, float, int]: Una tupla que contiene el mínimo, el máximo,
el promedio y la suma de la lista.
"""
minimo = min(lista)
maximo = max(lista)
promedio = sum(lista) / len(lista)
suma = sum(lista)
return minimo, maximo, promedio, suma

# Ejemplo de uso
numeros = [4, 7, 1, 12, 9, 3]
min_valor, max_valor, promedio_valor, suma_valor = estadisticas(numeros)
print(f"Mínimo: {min_valor}, Máximo: {max_valor}, Promedio: {promedio_valor:.
↪2f}, Suma: {suma_valor}")

Mínimo: 1, Máximo: 12, Promedio: 6.00, Suma: 36

0.1.18 Explicacion del algoritmo:


1. Importación del Módulo typing:
• typing es un módulo en Python que proporciona soporte para anotaciones de tipos
más avanzadas. Las anotaciones de tipos permiten especificar el tipo de datos que las
variables, parámetros de funciones y valores de retorno deben tener. Esto no afecta
la ejecución del código en sí, pero es extremadamente útil para la documentación y
mantenimiento del código, así como para herramientas de análisis estático como
mypy.
2. Uso de List y Tuple desde typing:
• List: Se utiliza para especificar que el parámetro lista debe ser de tipo list y que
contiene elementos de tipo int. En Python, una lista puede contener elementos de
cualquier tipo, pero al usar List[int], especificamos que la lista debe contener enteros.
Esto ayuda a evitar errores en tiempo de desarrollo.

38
• Tuple: De manera similar, Tuple[int, int, float, int] se utiliza para especificar
que la función estadisticas devuelve una tupla con cuatro elementos en el siguiente
orden: un int (mínimo), otro int (máximo), un float (promedio) y otro int (suma).
Esto aclara para cualquiera que lea el código o lo use en un proyecto, qué tipo de valores
se esperan y en qué orden.
3. Ventajas de Usar typing:
• Claridad y Mantenimiento: Facilita la comprensión del código al dejar claro qué
tipos de datos se esperan y qué se devuelve. Esto es crucial en proyectos grandes donde
muchas personas trabajan en el mismo código.
• Análisis Estático: Herramientas como mypy pueden analizar el código antes de eje-
cutarse para detectar errores relacionados con los tipos de datos, lo que ayuda a evitar
errores en tiempo de ejecución.
• Documentación Automática: Ayuda a generar documentación automáticamente me-
diante herramientas como Sphinx, lo que mejora la calidad de la documentación.
4. Cálculos Realizados por la Función:
• minimo: Calcula el mínimo de la lista usando la función incorporada min().
• maximo: Calcula el máximo de la lista usando la función incorporada max().
• promedio: Calcula el promedio de los elementos de la lista dividiendo la suma total
(sum(lista)) por el número de elementos (len(lista)).
• suma: Calcula la suma total de los elementos de la lista usando la función incorporada
sum().
5. Devolución de Múltiples Valores:
• Todos estos valores calculados se devuelven como una tupla. La tupla es una estructura
de datos que agrupa varios valores, y es perfecta para devolver múltiples resultados sin
necesidad de empaquetar/desempaquetar manualmente.

0.1.19 ¿Por Qué Escoger List y Tuple de typing?


• Listas (List): En Python, las listas son colecciones que pueden contener elementos de
diferentes tipos. Sin embargo, el uso de List de typing asegura que todos los elementos en
la lista sean de un tipo específico (en este caso, int). Esto previene errores en tiempo de
desarrollo cuando se trabaja con operaciones que asumen un tipo de dato uniforme.
• Tuplas (Tuple): Las tuplas son útiles para devolver múltiples valores porque son inmutables,
lo que significa que los datos devueltos por una función no se pueden cambiar accidentalmente.
Usar Tuple de typing no solo indica el número de elementos en la tupla, sino también el
tipo de cada uno de esos elementos, proporcionando documentación y verificación de tipos
adicionales.

0.1.20 Ejemplos de funciones:


0.1.21 Ejemplo:
En este ejemplo, vamos a definir una función en Python que calcula la suma de la serie finita:

𝑥 𝑥2 𝑥𝑛
1+ + +⋯+
1 2! 𝑛!
donde 𝑥 es un número real y 𝑛 es un número entero positivo dado por el usuario. Esta serie es
una forma truncada de la expansión de Taylor para la función exponencial 𝑒𝑥 . La función recibirá

39
como argumentos el valor de 𝑥 y el valor de 𝑛, y retornará el resultado de la suma de la serie hasta
el término 𝑛.

0.1.22 Código en Python

[ ]: import math

# Definición de la función para calcular la serie


def serie_exponencial(x, n):
"""
Calcula la suma de la serie 1 + x/1 + x^2/2! + ... + x^n/n!

Args:
x (float): El valor de x.
n (int): El número de términos en la serie.

Returns:
float: El resultado de la suma de la serie.
"""
suma = 1.0 # Empezamos la suma en 1 para incluir el primer término 1
for i in range(1, n + 1):
suma += (x ** i) / math.factorial(i)
return suma

# Ejemplo de uso
# Solicitamos al usuario que ingrese los valores de x y n
x = float(input("Ingrese el valor de x: "))
n = int(input("Ingrese el número de términos n: "))

# Llamamos a la función con los valores proporcionados por el usuario


resultado = serie_exponencial(x, n)

# Mostramos el resultado
print(f"La suma de la serie para x = {x} y n = {n} es: {resultado}")

Ingrese el valor de x: 4
Ingrese el número de términos n: 6
La suma de la serie para x = 4.0 y n = 6 es: 48.55555555555555

0.1.23 Explicación del Código


1. Importación del Módulo math: Importamos la biblioteca math para usar la función
math.factorial() que calcula el factorial de un número.
2. Definición de la Función serie_exponencial(x, n):
• La función toma dos argumentos: x (un número flotante) y n (un número entero).
• Inicializamos la variable suma con 1.0 para incluir el primer término de la serie.

40
• Utilizamos un bucle for que itera desde 1 hasta 𝑛. En cada iteración, se añade el término
𝑥𝑖
𝑖! a suma.

3. Entrada del Usuario:


• Se pide al usuario que ingrese el valor de 𝑥 y el número de términos 𝑛 que desea calcular
en la serie.
4. Llamada a la Función y Resultado:
• Se llama a la función serie_exponencial(x, n) con los valores proporcionados por el
usuario y se almacena el resultado en la variable resultado.
• Finalmente, se imprime el resultado de la serie calculada.

0.1.24 Uso del Operador * para Empaquetar y Desempaquetar Argumentos Posi-


cionales
En Python, el operador * se utiliza para empaquetar y desempaquetar argumentos posicionales.
Esto es útil cuando no sabemos de antemano cuántos argumentos va a recibir una función. A
continuación, explico cómo funciona cada caso:

1. Empaquetar Argumentos con * Cuando se utiliza * en los parámetros de una función,


permite que la función reciba un número variable de argumentos posicionales. Los argumentos
recibidos se empaquetan en una tupla.

[ ]: def suma(*args):
return sum(args)

# Ejemplo de uso
print(suma(1, 2, 3, 4)) # Salida: 10

10

[ ]: # Ejemplo de uso
print(suma(1, 2, 3, 4, 6, 8)) # Salida: 24

24
En este ejemplo, *args empaqueta todos los argumentos proporcionados en una tupla llamada
args.

2. Desempaquetar Argumentos con * El operador * también se puede usar al llamar a una


función para desempaquetar una lista o tupla en varios argumentos posicionales.

[ ]: def resta(a, b):


return a - b

# Desempaquetar argumentos desde una tupla


argumentos = (10, 3)
print(resta(*argumentos)) # Salida: 7

41
En este caso, *argumentos desempaqueta la tupla (10, 3) en dos argumentos posicionales para la
función resta.

0.1.25 Ejemplo 1: Función para Calcular la Norma de un Vector en ℝ𝑛


Vamos a definir una función que encuentre la norma (o magnitud) de un vector 𝑥 ∈ ℝ𝑛 . La norma
de un vector 𝑥 = (𝑥1 , 𝑥2 , … , 𝑥𝑛 ) se define como:

||𝑥|| = √𝑥21 + 𝑥22 + ⋯ + 𝑥2𝑛

Usaremos *args para permitir que la función reciba un número variable de componentes de un
vector.

Código en Python
[ ]: import math

def norma_vector(*args):
"""
Calcula la norma de un vector en R^n.

Args:
*args: Componentes del vector.

Returns:
float: La norma del vector.
"""
suma_cuadrados = sum(x**2 for x in args) # Calcula la suma de los␣
↪cuadrados de los componentes

return math.sqrt(suma_cuadrados)

# Ejemplo de uso
vector = (3, 4) # Un vector en R^2
print(f"La norma del vector {vector} es: {norma_vector(*vector)}") #␣
↪Desempaquetar la tupla vector

vector3D = (1, 2, 2) # Un vector en R^3


print(f"La norma del vector {vector3D} es: {norma_vector(*vector3D)}") #␣
↪Desempaquetar la tupla vector3D

La norma del vector (3, 4) es: 5.0


La norma del vector (1, 2, 2) es: 3.0

Explicación del Código


1. Importación del Módulo math: Importamos math para usar la función math.sqrt() que
calcula la raíz cuadrada.
2. Función norma_vector(*args):

42
• *args permite que la función reciba un número arbitrario de componentes del vector.
• Se calcula la suma de los cuadrados de los componentes con una expresión generadora:
sum(x**2 for x in args).
• Retornamos la raíz cuadrada de la suma usando math.sqrt().
3. Uso de la Función:
• Desempaquetamos las tuplas vector y vector3D cuando llamamos a la función
norma_vector.

0.1.26 Ejemplo 2:
Ahora, definiremos una función que calcule la siguiente expresión:

√𝑥 + √𝑥 + √𝑥 + ⋯ + √𝑥
1 2 3 𝑛

Esta función también utilizará *args para manejar un número variable de argumentos.

Código en Python
[ ]: import math

def nested_sqrt(*args):
"""
Calcula sqrt(x1 + sqrt(x2 + sqrt(x3 + ... + sqrt(xn)))).

Args:
*args: Una secuencia de números a los que se aplicará la raíz anidada.

Returns:
float: El resultado de la expresión.
"""
if not args: # Si no hay argumentos, retornamos 0
return 0
# Comenzamos desde el último número y aplicamos sqrt de manera anidada
resultado = args[-1]
for x in reversed(args[:-1]): # Recorremos los elementos de derecha a␣
↪izquierda

resultado = math.sqrt(x + resultado)


return resultado

# Ejemplo de uso
print(nested_sqrt(2, 3, 4)) # sqrt(2 + sqrt(3 + sqrt(4)))
print(nested_sqrt(5, 7, 11, 13)) # sqrt(5 + sqrt(7 + sqrt(11 + sqrt(13))))

2.1554004989942337
2.9068006025152773

43
Explicación del Código
1. Función nested_sqrt(*args):
• La función recibe un número arbitrario de argumentos gracias a *args.
• Se empieza desde el último número y se aplica la raíz cuadrada de manera anidada,
trabajando de derecha a izquierda.
• Usamos reversed() para iterar en orden inverso y aplicar la raíz cuadrada anidada.
2. Uso de la Función:
• Se llama a nested_sqrt con diferentes conjuntos de argumentos para calcular las raíces
anidadas.

0.1.27 Ejemplo: Definición de una Función con Suma Anidada Recursiva


En este ejemplo, vamos a definir una función matemática 𝑓(𝑐0 , 𝑐1 , … , 𝑐𝑚 ) con números enteros no
negativos 𝑐0 , 𝑐1 , … , 𝑐𝑚 , donde 𝑐𝑚 ≠ 0. La función se define de la siguiente manera:

1
𝑓(𝑐0 , 𝑐1 , … , 𝑐𝑚 ) = 𝑐0 + 1
𝑐1 + 𝑐2 +⋯+ 𝑐1
𝑚

Solución La solución implica comenzar con la última fracción, 𝑐1 , y trabajar de regreso hasta
𝑚
llegar a 𝑐0 . En cada paso, agregamos la fracción correspondiente. Este proceso puede capturarse
en el siguiente fragmento de código:
s = 0
for i in c[::-1]:
s = i + 1/s
Aquí, usualmente, en la primera iteración del bucle, s comenzaría con 0. Sin embargo, eso causaría
un error ya que estaríamos usando 1/𝑠 con 𝑠 = 0. Para evitar este error, definimos una declaración
if dentro del código para verificar si el valor de s es cero, y en ese caso ignoramos 1/𝑠.

0.1.28 Implementación en Python


[ ]: def f(*c):
"""
Calcula la función f(c_0, c_1, ..., c_m) de manera recursiva utilizando una␣
↪suma anidada.

Args:
*c: Una secuencia de números enteros no negativos, donde el último número␣
↪no debe ser cero.

Returns:
float: El resultado de la función recursiva.
"""
s = 0
# Recorremos los elementos de c de derecha a izquierda
for i in c[::-1]:

44
s = i + (1/s if s != 0 else 0) # Verificamos si s es cero para evitar␣
división por cero

return s
# Ejemplo de uso
print(f(1, 2, 3, 4)) # Salida: 1.4333333333333333

# Otro ejemplo con rango de números


print(f(*range(1, 100))) # Salida: 1.4331274267223117

1.4333333333333333
1.4331274267223117

Explicación del Código


1. Definición de la Función f(*c):
• *c permite que la función reciba un número variable de argumentos.
• La variable s se inicializa en 0.
• Utilizamos un bucle for para recorrer la lista de argumentos en orden inverso (c[::-1]),
desde el último elemento hasta el primero.
• Dentro del bucle, s se actualiza de acuerdo con la fórmula dada. Si s es distinto de 0,
calculamos 1/𝑠, de lo contrario, añadimos 0 para evitar una división por cero.
2. Ejemplo de Uso:
• Se llama a la función f con diferentes conjuntos de argumentos. En el primer ejemplo,
los argumentos son 1, 2, 3, 4, y en el segundo se usa el rango de números del 1 al 99.
3. Importancia del Control de Errores:
• Usamos un control de flujo (if s != 0) para evitar la división por cero, un error común
cuando se trabaja con fracciones anidadas.

0.1.29 Ejemplo: Comparación de Tiempos de Ejecución entre una Función Propia y


una Función de Python Usando la Librería time
En este ejemplo, crearemos un algoritmo que calcule el factorial de un número. Para ello, vamos
a definir nuestra propia función que calcula el factorial de manera recursiva, y compararemos su
tiempo de ejecución con la función math.factorial() que ya está implementada en la biblioteca
estándar de Python. Usaremos la librería time para medir el tiempo que toma ejecutar ambas
funciones.

Explicación del Algoritmo


1. Función Propia de Factorial (factorial_custom): Esta función calcula el factorial de
un número de manera recursiva. El factorial de un número 𝑛 se define como el producto de
todos los enteros positivos desde 1 hasta 𝑛. Por ejemplo, 5! = 5 × 4 × 3 × 2 × 1 = 120.
2. Función de la Biblioteca math (math.factorial): Esta es una función optimizada para
calcular el factorial de un número. Usaremos esta función para comparar su rendimiento con
nuestra implementación personalizada.
3. Medición del Tiempo de Ejecución con time.time(): Utilizaremos time.time() para
medir el tiempo de inicio y finalización de cada función y, así, calcular el tiempo total de
ejecución.

45
Código en Python
[ ]: import time
import math

# Función propia para calcular el factorial de manera recursiva


def factorial_custom(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial_custom(n - 1)

# Número para calcular el factorial


n = 20

# Medición del tiempo de ejecución para la función personalizada


start_time_custom = time.time()
factorial_result_custom = factorial_custom(n)
end_time_custom = time.time()
execution_time_custom = end_time_custom - start_time_custom

# Medición del tiempo de ejecución para la función de la biblioteca estándar


start_time_builtin = time.time()
factorial_result_builtin = math.factorial(n)
end_time_builtin = time.time()
execution_time_builtin = end_time_builtin - start_time_builtin

# Resultados
print(f"Factorial calculado con la función personalizada:␣
↪{factorial_result_custom}")

print(f"Tiempo de ejecución de la función personalizada:␣


↪{execution_time_custom} segundos")

print(f"Factorial calculado con math.factorial: {factorial_result_builtin}")


print(f"Tiempo de ejecución de la función math.factorial:␣
↪{execution_time_builtin} segundos")

# Comparación de tiempos
if execution_time_custom > execution_time_builtin:
print("La función math.factorial es más rápida.")
else:
print("La función personalizada es más rápida.")

Factorial calculado con la función personalizada: 2432902008176640000


Tiempo de ejecución de la función personalizada: 0.00011944770812988281 segundos
Factorial calculado con math.factorial: 2432902008176640000
Tiempo de ejecución de la función math.factorial: 8.106231689453125e-05 segundos
La función math.factorial es más rápida.

46
Explicación de los Resultados
• El código imprime el tiempo de ejecución de ambas funciones (la personalizada y la de la
biblioteca estándar).
• Al comparar los tiempos de ejecución, podemos ver cuál de las dos funciones es más eficiente
para calcular el factorial de un número dado.
• En general, la función math.factorial() de la biblioteca estándar será más rápida y eficiente
porque está optimizada a nivel de implementación en C, mientras que la función personalizada
es recursiva y tiene más sobrecarga de llamadas a función.
Este ejemplo ilustra cómo medir y comparar tiempos de ejecución en Python utilizando la librería
time.

0.1.30 Ejemplo:
El método de Newton-Raphson es un algoritmo numérico muy eficaz para encontrar aproximaciones
de las raíces (o ceros) de una función real. Este método utiliza la tangente de la curva de la función
para encontrar el punto en el cual corta al eje 𝑥, y usa este punto como nueva aproximación para
la raíz de la función.
El método comienza con un valor inicial 𝑥0 cercano a la raíz de la función 𝑓(𝑥). A continuación,
se utiliza la siguiente fórmula iterativa para encontrar una mejor aproximación 𝑥1 :

𝑓(𝑥0 )
𝑥1 = 𝑥0 −
𝑓 ′ (𝑥0 )

Este proceso se repite, utilizando 𝑥1 para encontrar 𝑥2 , 𝑥2 para encontrar 𝑥3 , y así sucesivamente.
De manera general, la fórmula iterativa es:

𝑓(𝑥𝑛 )
𝑥𝑛+1 = 𝑥𝑛 −
𝑓 ′ (𝑥𝑛 )

La serie 𝑥0 , 𝑥1 , 𝑥2 , … converge rápidamente hacia una raíz de 𝑓(𝑥) si se ha escogido un 𝑥0 adecuado.

#Ejemplo
Este código muestra cómo encontrar una raíz de la función $ f(x) = x^2 - 4 $ utilizando el método
de Newton-Raphson en Python. Además, se incluye una gráfica que muestra la función y la raíz
encontrada.

[ ]: import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from IPython.display import display

# Definir la función y su derivada


def f(x):
return x**2 - 4 # Función

47
def df(x):
return 2 * x # Derivada

# Parámetros iniciales
x0 = 9 # valor inicial
tolerance = 1e-7 # tolerancia
max_iter = 50 # número máximo de iteraciones

# Listas para almacenar el progreso


xs = []
xs1 = [x0]
errors = []

# Algoritmo de Newton-Raphson
for i in range(max_iter):
x1 = x0 - f(x0) / df(x0) # Nueva aproximación
error = abs(x1 - x0) # Error absoluto

xs.append(x1)
xs1.append(x1)
errors.append(error)

# Romper el ciclo si la tolerancia es alcanzada


if error < tolerance:
break

x0 = x1 # Actualizar x0 para la siguiente iteración

# Mostrar el valor aproximado de la raíz


print(f"El valor aproximado de la raíz es: {xs[-1]:.16f}")

# Crear una tabla de pandas para mostrar los resultados de las iteraciones
df_results = pd.DataFrame({
'Iteración': range(1, len(xs) + 1),
'Aproximación x': xs,
'Error Absoluto': errors
})

# Mostrar la tabla usando display


display(df_results)

# Crear una figura para la gráfica


x_vals = np.linspace(0, 10, 400)
y_vals = f(x_vals)

plt.figure(figsize=(10, 6))

48
# Graficar la función
plt.plot(x_vals, y_vals, label='f(x) = x^2 - 4', color='blue')

# Graficar las aproximaciones


for i in range(len(xs1)-1):
plt.plot([xs1[i], xs1[i]], [0, f(xs1[i])], color='red', linestyle='dotted')␣
↪ # Líneas verticales

plt.plot([xs1[i], xs1[i+1]], [f(xs1[i]), 0], color='green',␣


↪linestyle='dotted') # Líneas horizontales

# Marcar las aproximaciones obtenidas


plt.scatter(xs, [0]*len(xs), color='red', zorder=5, label='Aproximaciones de␣
↪Newton-Raphson')

# Configuración adicional de la gráfica


plt.axhline(0, color='black', linewidth=0.8)
plt.axvline(0, color='black', linewidth=0.8)
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Método de Newton-Raphson para f(x) = x^2 - 4')
plt.legend()
plt.grid(True)
plt.show()

El valor aproximado de la raíz es: 2.0000000000000000


Iteración Aproximación x Error Absoluto
0 1 4.722222 4.277778e+00
1 2 2.784641 1.937582e+00
2 3 2.110546 6.740947e-01
3 4 2.002895 1.076507e-01
4 5 2.000002 2.892983e-03
5 6 2.000000 2.092336e-06
6 7 2.000000 1.094680e-12

49
0.1.31 Ejemplo en Python
Para ilustrar cómo funciona el método de Newton-Raphson, vamos a encontrar una raíz de la
función $ f(x) = x^2 - 4 $ usando Python. Utilizaremos la función root_scalar de la biblioteca
SciPy para aplicar el método de Newton-Raphson.
Primero, instala las bibliotecas necesarias:
pip install scipy matplotlib
A continuación, el código para implementar el método:

[ ]: import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.misc import derivative
from IPython.display import display

# Definir la función
def f(x):
return x**2 - 4 # Función

# Parámetros iniciales
x0 = 9 # valor inicial
tolerance = 1e-7 # tolerancia
max_iter = 50 # número máximo de iteraciones

50
# Listas para almacenar el progreso
xs = [x0] # Lista de aproximaciones, incluyendo el valor inicial
errors = [] # Lista de errores absolutos

# Implementación manual del método de Newton-Raphson


for i in range(max_iter):
# Calcular la derivada de la función en el punto actual
df_x0 = derivative(f, xs[-1], dx=1e-6)

# Evitar la división por cero en caso de que la derivada sea cero


if df_x0 == 0:
print("Derivada cero, no se puede continuar con el método de␣
↪Newton-Raphson.")

break

# Calcular la siguiente aproximación


x1 = xs[-1] - f(xs[-1]) / df_x0
error = abs(x1 - xs[-1]) # Calcula el error absoluto

xs.append(x1) # Agrega la nueva aproximación


errors.append(error) # Agrega el error

# Romper el ciclo si el error es menor que la tolerancia


if error < tolerance:
break

# Mostrar el valor aproximado de la raíz


print(f"El valor aproximado de la raíz es: {xs[-1]:.16f}")

# Crear una tabla de pandas para mostrar los resultados de las iteraciones
df_results = pd.DataFrame({
'Iteración': range(1, len(xs)),
'Aproximación x': xs[1:], # Excluye el valor inicial x0 para mostrar las␣
↪iteraciones

'Error Absoluto': errors


})

# Mostrar la tabla usando display


display(df_results)

# Crear una figura para la gráfica


x_vals = np.linspace(0, 10, 400)
y_vals = f(x_vals)

plt.figure(figsize=(10, 6))

51
# Graficar la función
plt.plot(x_vals, y_vals, label='f(x) = x^2 - 4', color='blue')

# Graficar las aproximaciones


for i in range(len(xs)-1):
plt.plot([xs[i], xs[i]], [0, f(xs[i])], color='red', linestyle='dotted') #␣
↪Líneas verticales

plt.plot([xs[i], xs[i+1]], [f(xs[i]), 0], color='green',␣


↪linestyle='dotted') # Líneas horizontales

# Marcar las aproximaciones obtenidas


plt.scatter(xs[1:], [0]*(len(xs)-1), color='red', zorder=5,␣
↪label='Aproximaciones de Newton-Raphson')

# Configuración adicional de la gráfica


plt.axhline(0, color='black', linewidth=0.8)
plt.axvline(0, color='black', linewidth=0.8)
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Método de Newton-Raphson para f(x) = x^2 - 4 usando scipy')
plt.legend()
plt.grid(True)
plt.show()

El valor aproximado de la raíz es: 2.0000000000000000


<ipython-input-5-0e329e81af4a>:23: DeprecationWarning: scipy.misc.derivative is
deprecated in SciPy v1.10.0; and will be completely removed in SciPy v1.12.0.
You may consider using findiff: https://github.com/maroba/findiff or
numdifftools: https://github.com/pbrod/numdifftools
df_x0 = derivative(f, xs[-1], dx=1e-6)
Iteración Aproximación x Error Absoluto
0 1 4.722222 4.277778e+00
1 2 2.784641 1.937582e+00
2 3 2.110546 6.740947e-01
3 4 2.002895 1.076507e-01
4 5 2.000002 2.892983e-03
5 6 2.000000 2.092336e-06
6 7 2.000000 1.094680e-12

52
0.1.32 Ejemplo: Resolución de una Ecuación No Lineal Usando el Método de Newton
Supongamos que necesitamos resolver una ecuación no lineal compleja que aparece en el contexto
de la termodinámica. Esta ecuación está relacionada con el cálculo de la presión de vapor de
una sustancia dada mediante la ecuación de Clausius-Clapeyron. La ecuación de Clausius-
Clapeyron se puede expresar de la siguiente manera:

Δ𝐻𝑣𝑎𝑝 1
ln(𝑃 ) = − ( )+𝐶
𝑅 𝑇

Donde: - 𝑃 es la presión de vapor. - Δ𝐻𝑣𝑎𝑝 es el calor de vaporización. - 𝑅 es la constante universal


de los gases ideales. - 𝑇 es la temperatura en Kelvin. - 𝐶 es una constante de integración que
depende de las propiedades del fluido.
Ahora, supongamos que queremos encontrar la temperatura 𝑇 para la cual la presión de vapor
𝑃 alcanza un valor específico. La ecuación se puede reordenar para obtener una función que
necesitamos resolver numéricamente.

Planteamiento del Problema Dado: - Δ𝐻𝑣𝑎𝑝 = 40, 650 J/mol - 𝑅 = 8.314 J/(mol K) - 𝐶 =
18.3 - 𝑃 = 101325 Pa (presión atmosférica estándar)
Queremos encontrar la temperatura 𝑇 en Kelvin que satisfaga la ecuación. Reordenando la ecuación
de Clausius-Clapeyron, podemos escribir:

53
40, 650 1
ln(101325) + ( ) − 18.3 = 0
8.314 𝑇

Resolución del Problema Utilizando el Método de Newton Utilizaremos el método


de Newton-Raphson para resolver esta ecuación en Python utilizando la función newton de
scipy.optimize.

[ ]: from scipy.optimize import newton


import numpy as np

# Definir la función no lineal basada en la ecuación de Clausius-Clapeyron


def f(T):
R = 8.314 # Constante de los gases ideales en J/(mol K)
delta_H_vap = 40650 # Calor de vaporización en J/mol
C = 18.3 # Constante
P = 101325 # Presión en Pa

# Función a resolver
return np.log(P) + (delta_H_vap / R) * (1 / T) - C

# Definir la derivada de la función


def df(T):
R = 8.314 # Constante de los gases ideales en J/(mol K)
delta_H_vap = 40650 # Calor de vaporización en J/mol

# Derivada de la función
return - (delta_H_vap / R) * (1 / T**2)

# Parámetro inicial
T0 = 350 # Temperatura inicial en Kelvin

# Resolver la ecuación usando el método de Newton


T_solution = newton(f, T0, fprime=df, tol=1e-7, maxiter=50)

# Mostrar la solución
print(f"La temperatura para la cual la presión de vapor es 101325 Pa es␣
↪aproximadamente: {T_solution:.2f} K")

La temperatura para la cual la presión de vapor es 101325 Pa es aproximadamente:


721.79 K

0.1.33 Descripción del Código


1. Definición de la Función f(T): Se define la función no lineal que representa la ecuación
de Clausius-Clapeyron reordenada para encontrar la temperatura 𝑇 . Esta función depende
de constantes conocidas (Δ𝐻𝑣𝑎𝑝 , 𝑅, 𝐶, y 𝑃 ).
2. Definición de la Derivada df(T): Se define la derivada de la función 𝑓(𝑇 ) con respecto a

54
𝑇 . La derivada es necesaria para el método de Newton-Raphson, ya que este método utiliza
el gradiente de la función para aproximar la raíz.
3. Uso del Método de Newton-Raphson (scipy.optimize.newton): Se usa la función
newton de la biblioteca scipy.optimize para resolver la ecuación no lineal. Se le proporciona
la función f(T), su derivada df(T), una aproximación inicial T0, la tolerancia deseada tol,
y el número máximo de iteraciones maxiter.
4. Solución: El resultado es la temperatura 𝑇 en Kelvin para la cual la presión de vapor es
igual a 101325 Pa. Este es el valor de 𝑇 que satisface la ecuación planteada.

Ejemplo: Cálculo del Valor Acumulado de un Ahorro Basado en Pagos Periódicos


Descripción del Problema:
Queremos encontrar la tasa de interés mínima 𝑖 a la cual un ahorro alcanzará un valor objetivo 𝐴
después de un cierto número de años de depósitos periódicos compuestos mensualmente. El valor
acumulado de los ahorros se puede determinar mediante la ecuación de anualidad, dada por:

𝑃
𝐴= ((1 + 𝑖)𝑛 − 1)
𝑖

Donde: - 𝐴 es la cantidad en la cuenta al final del período. - 𝑃 es la cantidad depositada regular-


mente. - 𝑖 es la tasa de interés por período. - 𝑛 es el número de depósitos.
Datos del Problema: - 𝐴 = 750, 000 USD (valor objetivo al final de 20 años). - 𝑃 = 1500 USD
(monto depositado mensualmente). - 𝑛 = 20 × 12 = 240 depósitos (mensuales).
El objetivo es encontrar la tasa de interés mensual 𝑖 que hace que el valor acumulado sea de 750,000
USD.
Resolución del Problema:
Usamos el método de Newton para resolver la ecuación no lineal para 𝑖.

[ ]: from scipy.optimize import newton

# Definición de la función no lineal basada en la ecuación de anualidad


def f(i):
A = 750000 # Cantidad final deseada en USD
P = 1500 # Cantidad depositada mensualmente en USD
n = 240 # Número de depósitos
return (P / i) * ((1 + i)**n - 1) - A

# Definición de la derivada de la función


def df(i):
P = 1500
n = 240
return (P * (n * (1 + i)**(n - 1)) / i) - (P * ((1 + i)**n - 1) / i**2)

# Aproximación inicial para la tasa de interés


i0 = 0.01

55
# Resolver la ecuación usando el método de Newton
i_solution = newton(f, i0, fprime=df, tol=1e-7, maxiter=50)

# Convertir la tasa mensual a tasa anual y mostrar la solución


i_annual = i_solution * 12
print(f"La tasa de interés anual mínima requerida es aproximadamente: {i_annual:
↪.4%}")

La tasa de interés anual mínima requerida es aproximadamente: 6.6609%

Ejemplo: Cálculo del Monto Necesario para Pagar una Hipoteca Descripción del
Problema:
Queremos encontrar la tasa de interés máxima 𝑖 que un prestatario puede pagar para un prés-
tamo de hipoteca dado un valor máximo de pago mensual. La fórmula para el monto del dinero
requerido para pagar una hipoteca durante un período de tiempo fijo viene dada por la ecuación
de anualidad ordinaria:

𝑃
𝐴= (1 − (1 + 𝑖)−𝑛 )
𝑖

Donde: - 𝐴 es el monto de la hipoteca. - 𝑃 es el pago mensual. - 𝑖 es la tasa de interés por período.


- 𝑛 es el número total de pagos.
Datos del Problema: - 𝐴 = 135, 000 USD (monto de la hipoteca). - 𝑃 = 1000 USD (pago
mensual máximo). - 𝑛 = 30 × 12 = 360 pagos (mensuales).
El objetivo es encontrar la tasa de interés mensual máxima 𝑖 que hace que el pago mensual sea de
1000 USD.
Resolución del Problema:
Usamos el método de Newton para resolver la ecuación no lineal para 𝑖.

[ ]: # Definición de la función no lineal basada en la ecuación de anualidad␣


↪ordinaria

def f(i):
A = 135000 # Monto de la hipoteca en USD
P = 1000 # Pago mensual máximo en USD
n = 360 # Número de pagos
return (P / i) * (1 - (1 + i)**-n) - A

# Definición de la derivada de la función


def df(i):
P = 1000
n = 360
return (P * ((1 + i)**-n - 1) / i**2) + (P * n * (1 + i)**(-n - 1) / i)

# Aproximación inicial para la tasa de interés

56
i0 = 0.01

# Resolver la ecuación usando el método de Newton


i_solution = newton(f, i0, fprime=df, tol=1e-7, maxiter=50)

# Convertir la tasa mensual a tasa anual y mostrar la solución


i_annual = i_solution * 12
print(f"La tasa de interés anual máxima permitida es aproximadamente: {i_annual:
↪.4%}")

La tasa de interés anual máxima permitida es aproximadamente: 8.0999%

Ejemplo: Cálculo de la Probabilidad de Ganar un Juego Descripción del Problema:


Se quiere determinar, con una precisión de 10−3 , el valor mínimo de la probabilidad 𝑝 de que un
jugador A gane un juego de ráquetbol si las probabilidades de ganar son específicas y A necesita
ganar al menos la mitad de los partidos que juega.
La fórmula para la probabilidad 𝑃 de que A gane se puede expresar como:

21
1+𝑝 𝑝
𝑃 = ( )
2 1 − 𝑝 + 𝑝2

Objetivo:
Determinar el valor de 𝑝 tal que la probabilidad de que A gane sea mayor o igual a una cantidad
dada.
Resolución del Problema:
Usamos el método de Newton para encontrar el valor de 𝑝.

[ ]: from scipy.optimize import newton


import numpy as np

# Definición de la función basada en la probabilidad de ganar


def f(p):
# Supongamos que queremos P = 0.5
return ((1 + p) / 2) * (p / (1 - p + p**2))**21

# Definición de la derivada de la función


def df(p):
# Derivada corregida de la función, con manejo de posibles problemas␣
↪numéricos

try:
denominator = (1 - p + p**2)**22
if np.isclose(denominator, 0):
return np.inf # Manejo de divisiones por cero
numerator = p**20 * (-20 * p**3 - 22 * p**2 + 22 * p + 21)
return numerator / (2 * denominator)

57
except OverflowError:
return np.inf # Manejo de sobrecargas

# Aproximación inicial para p


p0 = 0.7 # Cambiar el valor inicial puede ayudar a la convergencia

# Resolver la ecuación usando el método de Newton


try:
p_solution = newton(f, p0, fprime=df, tol=1e-7, maxiter=100)
# Mostrar la solución
print(f"La probabilidad mínima requerida de que A gane es aproximadamente:␣
↪{p_solution:.4f}")

except RuntimeError as e:
print(f"Error: {e}")

Error: Failed to converge after 100 iterations, value is 0.0068267659428197245.

58

También podría gustarte