Ejercicios: Integración y Aplicación de Conceptos en Python para Física¶
Introducción¶
En esta serie de ejercicios, integraremos conceptos fundamentales de programación en Python para resolver problemas de física. Trabajaremos con funciones que manipulan listas, módulos estándar y propios, implementaciones recursivas, y aplicaciones a problemas físicos realistas. Estos ejercicios están diseñados para fortalecer tus habilidades de programación y tu capacidad de modelar fenómenos físicos mediante código.
Parte I: Funciones que Operan sobre Listas¶
Ejercicio 1: Análisis de Datos de Movimiento¶
Escribe una función analizar_trayectoria que reciba una lista de tuplas donde cada tupla contiene la posición (x, y) de un objeto en movimiento en diferentes instantes de tiempo, igualmente espaciados. La función debe tener un parámetro adicional intervalo_tiempo que indique el intervalo de tiempo en segundos entre cada medición (por defecto 1 segundo). La función debe calcular y retornar:
La distancia total recorrida.
El desplazamiento neto (distancia en línea recta desde el punto inicial al final).
La velocidad media (magnitud del desplazamiento dividido por el tiempo total).
Una lista con las velocidades instantáneas entre puntos consecutivos.
Los puntos donde la velocidad instantánea cambia de dirección (si existen).
La distancia entre dos puntos consecutivos se calcula como:
Ejemplo de uso:
trayectoria = [(0, 0), (1, 1), (2, 0), (3, 2), (4, 1)]
# Usando el valor por defecto (intervalo de 1 segundo)
resultados = analizar_trayectoria(trayectoria)
print(resultados)
# Especificando un intervalo diferente (0.5 segundos entre mediciones)
resultados_detallados = analizar_trayectoria(trayectoria, intervalo_tiempo=0.5)
print(resultados_detallados)
# Debería mostrar la distancia total, desplazamiento, velocidad media,
# lista de velocidades instantáneas y puntos de cambio de dirección
Ejercicio 2: Procesamiento de Datos Experimentales¶
Implementa una función filtrar_outliers que tome una lista de mediciones experimentales y elimine valores atípicos (outliers). La función debe utilizar el método de la desviación absoluta de la mediana (MAD):
Cualquier valor que se desvíe más de n veces el MAD de la mediana se considera un outlier, donde n es un parámetro ajustable (típicamente entre 2 y 3).
La función debe retornar una nueva lista sin los outliers y un informe en forma de diccionario que incluya:
'num_outliers': Número de outliers eliminados'outliers': Lista con los valores de los outliers'media_antes': Media aritmética antes de la filtración'desviacion_antes': Desviación estándar antes de la filtración'media_despues': Media aritmética después de la filtración'desviacion_despues': Desviación estándar después de la filtración
Ejemplo:
mediciones = [10.1, 10.2, 10.1, 10.3, 10.2, 15.6, 10.0, 10.1, -2.5, 10.3]
# 15.6 y -2.5 son outliers
datos_filtrados, informe = filtrar_outliers(mediciones, n=2.5)
print(datos_filtrados) # Lista sin outliers
# El informe es un diccionario con la información del proceso
print(informe['num_outliers']) # Debería mostrar 2
print(informe['outliers']) # Debería mostrar [15.6, -2.5]
print(informe['media_antes']) # Media antes de filtrar
print(informe['desviacion_antes']) # Desviación estándar antes de filtrar
print(informe['media_despues']) # Media después de filtrar
print(informe['desviacion_despues']) # Desviación estándar después de filtrar
Ejercicio 3: Búsqueda Binaria para Valores de Energía¶
Implementa una función busqueda_binaria que encuentre la posición de un valor en una lista ordenada de energías (en electronvoltios, eV). Si el valor exacto no existe, la función debe retornar la posición donde debería insertarse para mantener el orden.
La función debe incluir un parámetro opcional para especificar una tolerancia, permitiendo encontrar valores dentro de un rango cercano al solicitado.
Nota sobre búsqueda binaria: La búsqueda binaria es un algoritmo eficiente para encontrar un elemento en una lista ordenada. Funciona dividiendo repetidamente a la mitad el segmento de la lista que podría contener el elemento, hasta reducir las ubicaciones posibles a solo una. A diferencia de la búsqueda lineal (que revisa cada elemento), la búsqueda binaria tiene una complejidad de O(log n), haciéndola mucho más rápida para listas grandes.
Ejemplo:
energias = [1.2, 2.4, 3.1, 4.5, 5.6, 6.7, 8.2, 9.5]
posicion = busqueda_binaria(energias, 4.5) # Debería retornar 3
posicion_aprox = busqueda_binaria(energias, 4.6, tolerancia=0.2) # Debería retornar 3
# Ejemplo donde el valor no existe y se retorna la posición donde debería insertarse
posicion_insercion = busqueda_binaria(energias, 7.0) # Debería retornar 6
print(f"El valor 7.0 debería insertarse en la posición {posicion_insercion}")
energias_nueva = energias.copy()
energias_nueva.insert(posicion_insercion, 7.0)
print(f"Lista actualizada: {energias_nueva}") # [1.2, 2.4, 3.1, 4.5, 5.6, 6.7, 7.0, 8.2, 9.5]
# Ejercicio 4: Algoritmo de Ordenamiento para Partículas
Ejercicio 4: Algoritmo de Ordenamiento para Partículas¶
Escribe una función ordenar_particulas que ordene una lista de partículas representadas como diccionarios. Cada partícula tiene propiedades como masa, carga y energía.
La función debe permitir especificar la propiedad por la cual ordenar y el orden (ascendente o descendente). Implementa el algoritmo de ordenamiento por selección (selection sort) desde cero, sin usar las funciones de ordenamiento integradas de Python.
Ejemplo:
particulas = [
{'nombre': 'Electrón', 'masa': 9.1e-31, 'carga': -1.6e-19, 'energia': 5.2},
{'nombre': 'Protón', 'masa': 1.67e-27, 'carga': 1.6e-19, 'energia': 7.8},
{'nombre': 'Neutrón', 'masa': 1.67e-27, 'carga': 0, 'energia': 2.1},
{'nombre': 'Muón', 'masa': 1.88e-28, 'carga': -1.6e-19, 'energia': 12.3}
]
# Ordenar por masa (ascendente)
ordenadas_por_masa = ordenar_particulas(particulas, 'masa')
# Ordenar por energía (descendente)
ordenadas_por_energia = ordenar_particulas(particulas, 'energia', ascendente=False)
El algoritmo de ordenamiento por selección (Selection Sort) es un método simple que opera directamente sobre la lista original, realizando un ordenamiento «in-place» (en el lugar):
Se trabaja con una única lista, donde en cada momento hay una parte ordenada (al inicio) y una parte no ordenada (al final).
Inicialmente, toda la lista se considera no ordenada.
En cada iteración:
Se recorre la porción no ordenada para encontrar el elemento más pequeño.
Este elemento mínimo se intercambia con el primer elemento de la porción no ordenada.
La frontera entre la porción ordenada y no ordenada avanza una posición.
El proceso continúa hasta que toda la lista queda ordenada.
A continuación se muestra un diagrama que ilustra el funcionamiento del algoritmo de ordenamiento por selección con una lista de 5 números:
![digraph selection_sort {
// Configuración general
graph [rankdir=TB, nodesep=0.3, ranksep=0.3];
node [shape=plaintext, fontname="Helvetica", fontsize=11];
edge [fontname="Helvetica", fontsize=9];
// Estado inicial
initial [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Estado Inicial</TD></TR>
<TR><TD>8</TD><TD>3</TD><TD>5</TD><TD>1</TD><TD>7</TD></TR>
</TABLE>
>];
// Iteración 1
it1 [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Iteración 1: Buscar mínimo</TD></TR>
<TR><TD>8</TD><TD>3</TD><TD>5</TD><TD BGCOLOR="lightpink">1</TD><TD>7</TD></TR>
<TR><TD COLSPAN="5">Mínimo encontrado: 1</TD></TR>
</TABLE>
>];
it1_result [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Después del intercambio</TD></TR>
<TR><TD BGCOLOR="lightgreen">1</TD><TD>3</TD><TD>5</TD><TD>8</TD><TD>7</TD></TR>
<TR><TD BGCOLOR="lightgreen">Ordenado</TD><TD COLSPAN="4">Desordenado</TD></TR>
</TABLE>
>];
// Iteración 2
it2 [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Iteración 2: Buscar mínimo</TD></TR>
<TR><TD BGCOLOR="lightgreen">1</TD><TD BGCOLOR="lightpink">3</TD><TD>5</TD><TD>8</TD><TD>7</TD></TR>
<TR><TD BGCOLOR="lightgreen">Ordenado</TD><TD COLSPAN="4">Mínimo: 3</TD></TR>
</TABLE>
>];
it2_result [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Después del intercambio</TD></TR>
<TR><TD BGCOLOR="lightgreen">1</TD><TD BGCOLOR="lightgreen">3</TD><TD>5</TD><TD>8</TD><TD>7</TD></TR>
<TR><TD COLSPAN="2" BGCOLOR="lightgreen">Ordenado</TD><TD COLSPAN="3">Desordenado</TD></TR>
</TABLE>
>];
// Iteración 3
it3 [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Iteración 3: Buscar mínimo</TD></TR>
<TR><TD BGCOLOR="lightgreen">1</TD><TD BGCOLOR="lightgreen">3</TD><TD BGCOLOR="lightpink">5</TD><TD>8</TD><TD>7</TD></TR>
<TR><TD COLSPAN="2" BGCOLOR="lightgreen">Ordenado</TD><TD COLSPAN="3">Mínimo: 5</TD></TR>
</TABLE>
>];
it3_result [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Después del intercambio</TD></TR>
<TR><TD BGCOLOR="lightgreen">1</TD><TD BGCOLOR="lightgreen">3</TD><TD BGCOLOR="lightgreen">5</TD><TD>8</TD><TD>7</TD></TR>
<TR><TD COLSPAN="3" BGCOLOR="lightgreen">Ordenado</TD><TD COLSPAN="2">Desordenado</TD></TR>
</TABLE>
>];
// Iteración 4
it4 [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Iteración 4: Buscar mínimo</TD></TR>
<TR><TD BGCOLOR="lightgreen">1</TD><TD BGCOLOR="lightgreen">3</TD><TD BGCOLOR="lightgreen">5</TD><TD>8</TD><TD BGCOLOR="lightpink">7</TD></TR>
<TR><TD COLSPAN="3" BGCOLOR="lightgreen">Ordenado</TD><TD COLSPAN="2">Mínimo: 7</TD></TR>
</TABLE>
>];
it4_result [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Después del intercambio</TD></TR>
<TR><TD BGCOLOR="lightgreen">1</TD><TD BGCOLOR="lightgreen">3</TD><TD BGCOLOR="lightgreen">5</TD><TD BGCOLOR="lightgreen">7</TD><TD>8</TD></TR>
<TR><TD COLSPAN="4" BGCOLOR="lightgreen">Ordenado</TD><TD>Desordenado</TD></TR>
</TABLE>
>];
// Resultado final
final [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="5">Lista Ordenada</TD></TR>
<TR><TD BGCOLOR="lightgreen">1</TD><TD BGCOLOR="lightgreen">3</TD><TD BGCOLOR="lightgreen">5</TD><TD BGCOLOR="lightgreen">7</TD><TD BGCOLOR="lightgreen">8</TD></TR>
</TABLE>
>];
// Conexiones
initial -> it1 -> it1_result -> it2 -> it2_result -> it3 -> it3_result -> it4 -> it4_result -> final;
}](../../_images/graphviz-dab0b1d24eccef1715e306508c55132272da1353.png)
Ordenamiento por selección de una lista de 5 números.¶
Parte II: Módulos y Organización de Código¶
Ejercicio 5: Creación de un Módulo de Mecánica¶
Crea un módulo llamado mecanica.py que contenga funciones para cálculos relacionados con mecánica clásica:
calcular_trabajo(fuerza, desplazamiento, angulo=0): Calcula el trabajo realizado por una fuerza constante sobre un objeto.\[W = F \cdot d \cdot \cos(\theta)\]energia_cinetica(masa, velocidad): Calcula la energía cinética de un objeto.\[E_k = \frac{1}{2}mv^2\]energia_potencial_gravitatoria(masa, altura, g=9.8): Calcula la energía potencial gravitatoria.\[E_p = mgh\]conservacion_energia(energia_inicial, energia_final, tolerancia=1e-6): Verifica si se conserva la energía dentro de una tolerancia.
Luego, crea un script principal simulacion_energia.py que importe y use estas funciones para simular un péndulo simple, mostrando cómo las energías cinética y potencial varían durante la oscilación y verificando la conservación de la energía total.
El script debe:
Simular el movimiento del péndulo para varios periodos
Calcular y las energías cinética, potencial y total a lo largo del tiempo
Verificar que la energía total se conserva
Ejercicio 6: Análisis Físico con Módulos Estándar¶
Escribe un programa pendulo.py que analice datos de oscilaciones de un péndulo utilizando módulos estándar de Python como math, random, statistics y sys.
El programa debe:
Generar datos sintéticos de un péndulo:
Crear una función simular_pendulo(longitud, tiempo_max, dt, ruido) que:
Simule la posición angular θ(t) = θ₀·cos(ωt) donde ω = √(g/L)
Añada ruido aleatorio a las posiciones
Devuelva dos listas: tiempos y posiciones angulares
Determinar el periodo experimental:
Implementar una función calcular_periodo(tiempos, posiciones) que:
Detecte los cruces por cero con pendiente positiva en la señal
Calcule el tiempo entre cruces consecutivos
Devuelva la lista de periodos medidos
Analizar los resultados:
Calcular estadísticas básicas del periodo (media, mediana, desviación estándar)
Estimar el valor de g usando la fórmula T = 2π√(L/g)
Mostrar los resultados en un formato legible
El programa debe funcionar con valores preestablecidos (sin necesidad de argumentos de línea de comando) pero permitir modificarlos fácilmente en el código.
Ejemplo de código base para empezar:
import math
import random
import statistics
def simular_pendulo(longitud=1.0, amplitud=0.1, tiempo_max=10.0, dt=0.01, ruido=0.005):
"""
Simula el movimiento de un péndulo y devuelve los datos con ruido.
Args:
longitud: Longitud del péndulo en metros
amplitud: Amplitud inicial en radianes
tiempo_max: Tiempo máximo de simulación en segundos
dt: Paso temporal en segundos
ruido: Magnitud del ruido aleatorio en radianes
Returns:
Tupla con (lista_tiempos, lista_posiciones)
"""
# Tu código aquí: implementar la simulación
pass
def calcular_periodo(tiempos, posiciones):
"""
Calcula los periodos observados a partir de los datos de posición.
Args:
tiempos: Lista de tiempos
posiciones: Lista de posiciones angulares
Returns:
Lista con los periodos medidos
"""
# Tu código aquí: implementar detección de periodos
pass
def main():
# Parámetros de simulación
longitud = 1.0 # metros
amplitud = 0.1 # radianes
g_teorico = 9.8 # m/s²
# Simular el péndulo
tiempos, posiciones = simular_pendulo(longitud=longitud, amplitud=amplitud)
# Calcular periodos
periodos = calcular_periodo(tiempos, posiciones)
# Analizar resultados
periodo_medio = statistics.mean(periodos)
periodo_std = statistics.stdev(periodos)
periodo_mediana = statistics.median(periodos)
# Calcular g experimental
# Tu código aquí
# Imprimir resultados
print("=== ANÁLISIS DE PÉNDULO SIMPLE ===")
print(f"Longitud: {longitud} m")
print(f"Periodo medio: {periodo_medio:.4f} ± {periodo_std:.4f} s")
print(f"Valor de g calculado: {g_calculado:.3f} ± {g_incertidumbre:.3f} m/s²")
if __name__ == "__main__":
main()
Parte III: Recursividad¶
Ejercicio 7: Integración Numérica Recursiva¶
Implementa una función recursiva integrar_recursiva que calcule la integral definida de una función utilizando el método de Simpson recursivo adaptativo.
El método de Simpson recursivo divide el intervalo en dos mitades y aplica la regla:
Para estimar el error, se debe:
Calcular la integral con la fórmula de Simpson para el intervalo completo [a,b], llamémosla S(a,b)
Dividir el intervalo en dos mitades [a,m] y [m,b] donde m=(a+b)/2
Calcular la integral para cada mitad: S(a,m) y S(m,b)
Comparar S(a,b) con la suma S(a,m) + S(m,b)
Estimar el error como:
La recursividad se aplica si el error estimado es mayor que una tolerancia especificada. En ese caso, se divide el intervalo en dos y se aplica la integración a cada mitad, sumando los resultados. Si el error es menor que la tolerancia, se acepta el valor S(a,m) + S(m,b) como aproximación válida.
Tu implementación debe:
Aceptar la función a integrar, los límites de integración y la tolerancia
Aplicar recursivamente el método de Simpson
Utilizar la estimación de error como criterio de parada
Opcionalmente, incluir un parámetro para limitar la profundidad máxima de recursión
Utiliza esta función para calcular:
La integral de la función seno entre 0 y π (cuyo resultado exacto es 2)
La integral de la función \(e^{-x^2}\) entre -1 y 1 (relacionada con la función error)
Ejemplo:
import math
def integrar_recursiva(f, a, b, tolerancia=1e-6, profundidad_max=30):
# Implementa el método de Simpson recursivo con estimación de error
# ...
# Calcular la integral de sin(x) de 0 a π
resultado = integrar_recursiva(math.sin, 0, math.pi, tolerancia=1e-6)
print(f"Integral de sin(x) de 0 a π: {resultado}") # Debería ser cercano a 2
# Calcular la integral de e^(-x²) de -1 a 1
def gaussiana(x):
return math.exp(-x**2)
resultado_gaussiana = integrar_recursiva(gaussiana, -1, 1, tolerancia=1e-8)
print(f"Integral de e^(-x²) de -1 a 1: {resultado_gaussiana}")
Parte IV: Problemas de Aplicación¶
Ejercicio 8: Difusión de Calor en una Varilla¶
Implementa una simulación numérica de la ecuación de difusión de calor en una dimensión:
donde \(T\) es la temperatura, \(t\) es el tiempo, \(x\) es la posición, y \(\alpha\) es la difusividad térmica.
Para resolver numéricamente esta ecuación, usaremos el método de diferencias finitas:
La ecuación de actualización es:
Donde:
\(i\) representa el índice de la posición espacial en la varilla (0, 1, 2, …, N)
\(n\) representa el índice del paso temporal (0, 1, 2, …, M)
\(T_i^n\) es la temperatura en la posición \(i\) y en el tiempo \(n\)
Condiciones del problema:
Condición inicial: Temperatura de 100°C en el centro de la varilla y 25°C en el resto
Condiciones de contorno: Los extremos de la varilla se mantienen fijos a 25°C
Difusividad térmica: \(\alpha = 0.01\)
Longitud de la varilla: 1 metro
Tiempo de simulación: 1 segundo
Tu programa debe:
Crear una malla espacial para la varilla (representada por una lista de temperaturas)
Inicializar la temperatura según la condición inicial dada
Para cada paso temporal:
Calcular la distribución de temperatura en el tiempo n+1 a partir de la distribución actual (tiempo n)
Actualizar la distribución actual con los nuevos valores calculados
Repetir el proceso para el número de pasos temporales deseado
Mostrar la distribución de temperatura para diferentes instantes de tiempo durante la simulación
Tiempo |
Posición x₀ |
Posición x₁ |
Posición x₂ |
Posición x₃ |
Posición x₄ |
Descripción |
|---|---|---|---|---|---|---|
t₀ (inicial) |
25°C |
25°C |
100°C |
25°C |
25°C |
Condición inicial: calor en el centro |
t₁ (ciclo 1) |
25°C |
26.2°C |
97.6°C |
26.2°C |
25°C |
El calor comienza a difundirse |
t₂ (ciclo 2) |
25°C |
27.3°C |
95.3°C |
27.3°C |
25°C |
La temperatura continúa equilibrándose |
Nota
Los extremos (x₀ y x₄) permanecen a temperatura constante de 25°C (condiciones de borde).
Para estos cálculos, se usó α = 0.01, Δx = 0.25 m y Δt = 0.1 s.
Fórmula de actualización:
Ejemplo de cálculo para el primer ciclo:
Para calcular la temperatura en la posición x₂ en el tiempo t₁:
En cada ciclo, todas las temperaturas interiores (excepto las de los extremos) se actualizan simultáneamente según esta fórmula.
Ejercicio 9: Oscilador Armónico Amortiguado¶
Implementa una simulación de un oscilador armónico amortiguado, como un sistema masa-resorte con fricción. La ecuación diferencial que gobierna el sistema es:
donde \(m\) es la masa, \(b\) es el coeficiente de amortiguamiento, \(k\) es la constante del resorte, y \(F(t)\) es una fuerza externa que puede variar con el tiempo.
Para resolver numéricamente esta ecuación, sigue estos pasos:
Transformación a ecuaciones de primer orden: Introduce las variables \(x\) (posición) y \(v\) (velocidad), para convertir la ecuación de segundo orden en un sistema de dos ecuaciones de primer orden:
\[\begin{split}\frac{dx}{dt} &= v \\ \frac{dv}{dt} &= \frac{F(t) - bv - kx}{m}\end{split}\]Método de integración numérica: Utiliza el método de Euler simple para avanzar la solución en pasos de tiempo pequeños:
Para cada paso de tiempo de tamaño \(\Delta t\):
\[\begin{split}x_{n+1} &= x_n + v_n \cdot \Delta t \\ v_{n+1} &= v_n + \frac{F(t_n) - bv_n - kx_n}{m} \cdot \Delta t\end{split}\]Fuerza externa: Implementa al menos dos tipos diferentes de fuerza externa: - Fuerza constante: \(F(t) = F_0\) - Fuerza sinusoidal: \(F(t) = F_0 \sin(\omega t)\)
Visualización: Calcula y grafica:
La posición \(x(t)\) en función del tiempo
La velocidad \(v(t)\) en función del tiempo
La energía total: \(E(t) = \frac{1}{2}mv^2 + \frac{1}{2}kx^2\)
Análisis de regímenes: Experimenta con diferentes valores de los parámetros para identificar:
Régimen subamortiguado (oscilatorio): \(b^2 < 4mk\)
Régimen críticamente amortiguado: \(b^2 = 4mk\)
Régimen sobreamortiguado: \(b^2 > 4mk\)
Para cada régimen, muestra un gráfico representativo y explica las diferencias observadas.
Sugerencias prácticas:
Utiliza un paso de tiempo pequeño (p. ej., \(\Delta t = 0.01\) segundos) para mantener la estabilidad numérica
Simula el sistema durante un tiempo suficientemente largo para observar el comportamiento completo
Puedes elegir valores iniciales como \(x(0) = 1\) (posición desplazada) y \(v(0) = 0\) (velocidad inicial nula)
Conclusión¶
Estos ejercicios están diseñados para integrar múltiples conceptos de programación en Python aplicados a problemas físicos relevantes. Abarcan desde manipulación básica de datos hasta simulaciones complejas, promoviendo un enfoque organizado y modular en la escritura de código.
Recuerda que la clave para resolver estos problemas no es solo obtener el resultado correcto, sino desarrollar soluciones que sean: - Claras y bien documentadas - Eficientes en términos de recursos computacionales - Modeladas correctamente según los principios físicos - Organizadas de manera modular y reutilizable
Al trabajar en estos ejercicios, desarrollarás habilidades valiosas para la modelación computacional en física y otras ciencias.