Como dice el refrán: Divide y Vencerás... Es posible resolver un problema difícil, dividiéndolo en subproblemas más simples (en la medida de lo posible). La idea es resolver el problema principal, utilizando las soluciones de los subproblemas.
En el mundo de la informática, es muy frecuente que se resuelvan los problemas de esta forma, siendo uno de los paradigmas más importantes en el diseño de algoritmos. Un pequeño esquema podría ser:
Problema:
Subproblema 1
Subproblema 2
...
Subproblema N
Recordando los contenidos vistos en clases anteriores, podríamos relacionar cada subproblema a una función. De esta forma el esquema anterior quedaría más o menos así:
# Inicio de mi algoritmo (problema):
def funcion1(...):
Instrucciones
def funcion2(...):
Instrucciones
....
Instrucciones
funcion1(...)
Instrucciones
funcion2(...)
...
# Fin de mi algoritmo
En resumen, buscamos descomponer nuestro algoritmo en funciones, integrar estas funciones y finalmente obtener nuestra solución.
Ahora el problema está en la capacidad de encontrar los subproblemas... Pero generalmente son bastante simples de identificar :)
Si bien hay una definición mucho más completa de los tipos de variables Globales y Locales, para nosotros bastará diferenciarlas de la siguiente manera:
A continuación un pequeño ejemplo...
Ejemplo
La fuerza de atracción gravitacional entre dos planetas de masas $m_1$ y $m_2$ separados por una distancia de $r$ kilómetros está dada por la fórmula:
$$ F = \frac{G\,m_1\,m_2}{r^2}, $$donde $G = 6,67428\times 10^{−11}$ [m$^3$ kg$^{−1}$ s$^{−2}$] es la constante de gravitación universal.
Escriba un programa que pregunte las masas de los planetas y su distancia, y entregue la fuerza de atracción entre ellos.
Opción 1, utilizando $G$ como variable global.
G = 6.67428e-11 # Variable global. Puede ser utilizada en cualquier parte del código
def cgu(masa1, masa2, radio):
# La variable fuerza se comporta como variable local,
# solo se conoce dentro de la función cgu
fuerza = G * masa1 * masa2 / (radio ** 2)
return fuerza
m1 = float(input('m1: '))
m2 = float(input('m2: '))
r = float(input('Distancia: '))
print('La fuerza de atraccion es', cgu(m1, m2, r))
Opción 2, utilizando $G$ como variable local.
def cgu(masa1, masa2, radio):
# Tanto fuerza como G son variables locales que solo se conocen en cgu.
G = 6.67428e-11
fuerza = G * masa1 * masa2 / (radio ** 2)
return fuerza
m1 = float(input('m1: '))
m2 = float(input('m2: '))
r = float(input('Distancia: '))
print('La fuerza de atraccion es', cgu(m1, m2, r))
Como su nombre lo dice, hay problemas en los cuales es necesario utilizar una estructura repetitiva dentro de otra estructura repetiva. Sintaxis general:
ciclo:
ciclo:
ciclo:
...
Y en Python:
while condicion:
while condicion:
while condicion:
instrucciones
...
Pequeño Ejemplo
Escriba un programa que muestre todas las combinaciones de dos dados que entreguen un puntaje mayor que siete.
dado_1 = 1
while dado_1 <= 6:
dado_2 = 1
while dado_2 <= 6:
if dado_1 + dado_2 > 7:
print(dado_1, dado_2)
dado_2 += 1
dado_1 += 1
Y para que el puntaje sea mayor a 12?
Otro ejemplo
Imprimir (mostrar) todos los números primos en un rango particular recibido como entrada.
# Suponiendo que el rango es siempre correcto (no hay que verificar)
a = int(input("Ingrese a: "))
b = int(input("Ingrese b: "))
# Variable que utilizaremos para probar si es primo
n = a
while n <= b:
d = 2 # Variable para buscar divisores
n_divisores = 0 # Contador para numero de divisores
while d <= n/2:
if n % d == 0:
n_divisores += 1
d += 1
# Mostramos el numero si es primo
if n_divisores == 0:
print(n)
# Seguimos con el proximo numero
n += 1
Apliquemos los conceptos revisados en esta clase.
N = int(input("Ingrese N: "))
x = float(input("Ingrese x: "))
n = 0
exp = 0
while n <= N:
# Calculo de factorial
prod = 1
i = 1
while i <= n:
prod *= i
i += 1
# Actualizar acumulador
exp = exp + (x ** n) / prod
n += 1
print("Valor aproximacion: ", exp)
Resolvamos el mismo ejercicio pero descomponiendo el problema.
def factorial(n):
prod = 1
i = 1
while i <= n:
prod *= i
i += 1
return prod
def aproximacion(N, x):
n = 0
exp = 0
while n <= N:
exp += (x ** n) / factorial(n)
n += 1
return exp
N = int(input("Ingrese N: "))
x = float(input("Ingrese x: "))
exp_aprox = aproximacion(N, x)
print("Valor aproximacion: ", exp_aprox)
x = float(input("Ingrese x: "))
eps = float(input("Ingrese epsilon: "))
flag = True
n = 1
while flag:
aprox_1 = aproximacion(n, x)
aprox_2 = aproximacion(n+1, x)
if abs(aprox_1 - aprox_2) < eps:
flag = False
else:
n += 1
print("Aproximacion: ", aprox_2)
print("Numero de terminos: ", n)
Solo por curiosidad...
from math import exp
print(exp(x))
El ganador del juego es el primero que gane tres rondas.
Escriba un programa que pregunte a cada jugador cuál es su jugada, muestre cuál es el marcador después de cada ronda, y termine cuando uno de ellos haya ganado tres rondas. Los jugadores deben indicar su jugada escribiendo tijera, papel o piedra.
def cachipun(j1, j2):
gana = 0 # Para manejar el empate
if j1 == 'piedra':
if j2 == 'papel':
gana = 0
elif j2 == 'tijera':
gana = 1
elif j1 == 'papel':
if j2 == 'piedra':
gana = 1
elif j2 == 'tijera':
gana = 0
else:
if j2 == 'piedra':
gana = 0
elif j2 == 'papel':
gana = 1
return gana
p1 = 0
p2 = 0
while p1 < 3 and p2 < 3:
jug_1 = input("Jugador 1: ")
jug_2 = input("Jugador 2: ")
if jug_1 != jug_2:
res = cachipun(jug_1, jug_2)
p1 += res
p2 += (1 - res)
print("Marcador: ", p1, " - ", p2)
if p1 > p2:
print("Gana jugador 1")
else:
print("Gana jugador 2")
Calcule $\sqrt{2}$, sabiendo que $f(x)=x^2-2$, $f'(x)=2x$. El número máximo de iteraciones $N$ y $x_0$ son entradas del programa.
def f(x):
return x**2 - 2
def df(x):
return 2*x
def metodo_newton(x_0, N):
n = 1
x = x_0 # Valor inicial
while n <= N:
x = x - f(x) / df(x)
n += 1
return x
x_0 = float(input("Ingrese valor inicial: "))
N = int(input("Ingrese numero de iteraciones: "))
sol = metodo_newton(x_0, N)
print("La raiz de 2 es aproximadamente: ", sol)
Nuevamente por curiosidad...
from math import sqrt
print(sqrt(2))
print(abs(sol-sqrt(2)))