Ejercicios bucles y condicionales

Vamos a afianzar los conocimientos de Python que acabamos de adquirir haciendo algunos ejercicios, y así retener las peculiaridades de la sintaxis y aclarar algunos detalles a tener en cuenta cuando se trabaja en modo interactivo.

Ejercicio 1: Sumatorio

Vamos a escribir ahora una función que sume los n primeros números naturales. Observa que podemos escribir una cadena de documentación (docstring) justo debajo de la definición de la función para explicar lo que hace.


In [1]:
def sumatorio(num):
    """Suma los `num` primeros números.

    Ejemplos
    --------
    >>> sumatorio(4)
    10

    """
    suma = 0
    for nn in range(1, num + 1):
        suma = nn + suma
    return suma

Lo que hemos hecho ha sido inicializar el valor de la suma a 0 e ir acumulando en ella los num primeros números naturales.


In [2]:
sumatorio(4)


Out[2]:
10

In [3]:
help(sumatorio)


Help on function sumatorio in module __main__:

sumatorio(num)
    Suma los `num` primeros números.
    
    Ejemplos
    --------
    >>> sumatorio(4)
    10

Observa lo que sucede si no inicializamos la suma:

In [4]:
def sumatorio_mal(num):
    for nn in range(1, num + 1):
        suma = nn + suma
    return suma

In [5]:
sumatorio_mal(4)


---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-5-9882aa9a55e2> in <module>()
----> 1 sumatorio_mal(4)

<ipython-input-4-d575a2b0e54e> in sumatorio_mal(num)
      1 def sumatorio_mal(num):
      2     for nn in range(1, num + 1):
----> 3         suma = nn + suma
      4     return suma

UnboundLocalError: local variable 'suma' referenced before assignment

Para comprobar el resultado correcto, nada como acudir a la función sum de Python, que suma los elementos que le pasemos:


In [6]:
list(range(1, 4 + 1))


Out[6]:
[1, 2, 3, 4]

In [7]:
sum(range(1, 4 + 1))


Out[7]:
10

Ejercicio 2: Sumatorio con cota superior

Ahora nuestra función es un poco más rara: tiene que sumar números naturales consecutivos y no pasarse de un determinado límite. Además, queremos el valor de la suma.


In [8]:
def suma_tope(tope):
    """Suma números naturales consecutivos hasta un tope.

    """
    suma = 0
    nn = 1
    while suma + nn <= tope:
        suma = suma + nn
        nn += 1
    return suma

In [9]:
suma_tope(9)


Out[9]:
6

In [10]:
suma_tope(9) == 1 + 2 + 3


Out[10]:
True

In [11]:
suma_tope(10) == 1 + 2 + 3 + 4


Out[11]:
True

La palabra clave assert recibe una expresión verdadera o falsa, y falla si es falsa. Si es verdadera no hace nada, con lo cual es perfecto para hacer comprobaciones a mitad del código que no estorben mucho.


In [12]:
assert suma_tope(11) == 1 + 2 + 3 + 4

In [13]:
assert suma_tope(10 + 5) == 1 + 2 + 3 + 4


---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-13-7e20f9ee8a4a> in <module>()
----> 1 assert suma_tope(10 + 5) == 1 + 2 + 3 + 4

AssertionError: 

Ejercicio 3: Normativa de exámenes

La normativa de exámenes es: "si un examen dura más de 3 horas, entonces debe tener un descanso". Los argumentos de la función son el tiempo en horas y un valor True o False que indica si hay descanso o no.


In [14]:
def cumple_normativa(tiempo, descanso):
    """Comprueba si un examen cumple la normativa de la UPM.

    """
    if tiempo <= 3:
        return True
    else:
        #if descanso:
        #    return True
        #else:
        #    return False
        return descanso  # ¡Equivalente!

In [15]:
cumple_normativa(2, False)


Out[15]:
True

In [16]:
if not cumple_normativa(5, descanso=False):
    print("¡Habla con DA!")


¡Habla con DA!

Ejercicio 4

Hallar $x = \sqrt{S}$.

  1. $\displaystyle \tilde{x} \leftarrow \frac{S}{2}$.
  2. $\displaystyle \tilde{x} \leftarrow \frac{1}{2}\left(\tilde{x} + \frac{S}{\tilde{x}}\right)$.
  3. Repetir (2) hasta que se alcance un límite de iteraciones o un criterio de convergencia.

http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method


In [17]:
def raiz(S):
    x = S / 2
    while True:
        temp = x
        x = (x + S / x) / 2
        if temp == x:
            return x

Aquí estoy usando un truco de la aritmética en punto flotante: como la convergencia se alcanza rápidamente, llega un momento en que el error es menor que la precisión de la máquina y el valor no cambia de un paso a otro.


In [18]:
raiz(10)


Out[18]:
3.162277660168379
Se deja como ejercicio implementar otras condiciones de convergencia: error relativo por debajo de un umbral o número máximo de iteraciones.

In [19]:
import math
math.sqrt(10)


Out[19]:
3.1622776601683795

In [20]:
raiz(10) ** 2


Out[20]:
9.999999999999998

In [21]:
math.sqrt(10) ** 2


Out[21]:
10.000000000000002

Ahora tienes curiosidad, ¿verdad? :) http://puntoflotante.org/

Ejercicio 5

Secuencia de Fibonacci: $F_n = F_{n - 1} + F_{n - 2}$, con $F_0 = 0$ y $F_1 = 1$.

$$0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...$$

Con iteración:


In [22]:
def fib(n):
    a, b = 0, 1
    for i in range(n):
        a, b = b, a + b  # Bendita asignación múltiple
    return a

In [23]:
fib(0), fib(3), fib(10)


Out[23]:
(0, 2, 55)

Con recursión:


In [24]:
def fib_recursivo(n):
    if n == 0:
        res = 0
    elif n == 1:
        res = 1
    else:
        res = fib_recursivo(n - 1) + fib_recursivo(n - 2)
    return res

Imprimir una lista con los $n$ primeros:


In [25]:
def n_primeros(n):
    F = fib_recursivo
    lista = []
    for ii in range(n):
        lista.append(F(ii))
    return lista

In [26]:
n_primeros(10)


Out[26]:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Ejericio 6 Ley d'Hondt

Implementar el sistema de reparto de escaños d'Hondt. Dicho sistema se basa en ir repartiendo escaños consecutivamente al partido con el máximo coeficiente, $c_i = \frac{V_i}{s_i + 1}$, donde $V_i$ es el número total de votos obtenido por del partido $i$, mientras que $s_i$ es el número de escaños asignados dicho partido (0 al comenzar el reparto).

Veamos por ejemplo el caso expuesto en Wikipedia:

Partido Partido A Partido B Partido C Partido D Partido E
Votos 340000 280000 160000 60000 15000

Todavía no hay ningún escaño asignado, así que los votos de cada partido se dividen por 1:

Partido Partido A Partido B Partido C Partido D Partido E
Votos 340000 280000 160000 60000 15000
Escaño 1 340000 280000 160000 60000 15000

Y por tanto el partido A recibe el primer escaño. Para repartir el segundo escaño se vuelven a dividr por 1 los votos de cada partido, salvo el partido A que se divide por 2, pues ya tiene un escaño:

Partido Partido A Partido B Partido C Partido D Partido E
Votos 340000 280000 160000 60000 15000
Escaño 1 340000 280000 160000 60000 15000
Escaño 2 170000 280000 160000 60000 15000

Así pues, el segundo escaño va para el partido B. Si se reparten 7 escaños como en el ejemplo de Wikpedia, la tabla final quedaría como sigue:

Partido Partido A Partido B Partido C Partido D Partido E
Votos 340000 280000 160000 60000 15000
Escaño 1 340000 280000 160000 60000 15000
Escaño 2 170000 280000 160000 60000 15000
Escaño 3 170000 140000 160000 60000 15000
Escaño 4 113333 140000 160000 60000 15000
Escaño 5 113333 140000 80000 60000 15000
Escaño 6 170000 93333 80000 60000 15000
Escaño 7 85000 93333 80000 60000 15000

Así que los partidos A y B obtendrían 3 escaños, mientras que el partido C obtendría 1 único escaño, quedando el resto de partidos fuera del proceso.


In [27]:
def hondt(votos, n):
    s = [0] * len(votos)
    for i in range(n):
        c = [v[j] / (s[j] + 1) for j in range(len(s))]
        s[c.index(max(c))] += 1
    return s

In [28]:
v = [340000, 280000, 160000, 60000, 15000]
n = 7
hondt(v, n)


Out[28]:
[3, 3, 1, 0, 0]

En esta clase hemos visto cómo crear funciones que encapsulen tareas de nuestro programa y las hemos aplicado para respondernos ciertas preguntas sencillas.

Referencias



¡Síguenos en Twitter!



Este notebook ha sido realizado por: Juan Luis Cano, Jose Luis Cercós y Álex Sáez



<span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">Curso AeroPython</span> por <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Juan Luis Cano Rodriguez y Alejandro Sáez Mollejo</span> se distribuye bajo una Licencia Creative Commons Atribución 4.0 Internacional.