<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Introducción a Jupyter
Expresiones aritmeticas y algebraicas
Empezaremos esta práctica con algo de conocimientos previos de programación. Se que muchos de ustedes no han tenido la oportunidad de utilizar Python como lenguaje de programación y mucho menos Jupyter como ambiente de desarrollo para computo cientifico, asi que el primer objetivo de esta práctica será acostumbrarnos a la sintaxis del lenguaje y a las funciones que hacen especial a Jupyter.
Primero tratemos de evaluar una expresión aritmetica. Para correr el código en la siguiente celda, tan solo tienes que hacer clic en cualquier punto de ella y presionar las teclas Shift + Return.
Step1:  Sin embargo no existen funciones trigonométricas cargadas por default. Para esto tenemos que importarlas de la libreria math
Step2:  Variables
Las variables pueden ser utilizadas en cualquier momento, sin necesidad de declararlas, tan solo usalas!
Step3:  Ejercicio
Ejecuta el siguiente calculo y guardalo en una variable
Step4:  Ejecuta la prueba de abajo para saber si has creado el codigo correcto
Step5:  Listas
Las listas son una manera de guardar varios datos en un mismo arreglo. Podemos tener por ejemplo
Step6:  Pero si intentamos multiplicar estos datos por un numero, no tendrá el comportamiento esperado.
Step7:  Funciones
Podemos definir funciones propias de la siguiente manera
Step8:  Esta linea de codigo es equivalente a definir una función matemática de la siguiente manera
Step9:  Esta notación que introducimos es muy util para funciones matemáticas, pero esto nos obliga a pensar en las definiciones de una manera funcional, lo cual no siempre es la solución (sobre todo en un lenguaje con un paradigma de programación orientado a objetos).
Esta función tambien puede ser escrita de la siguiente manera
Step10:  Con los mismos resultados
Step11:  Ejercicio
Define una función que convierta grados Celsius a grados Farenheit, de acuerdo a la siguiente formula
Step12:  Y para probar trata de convertir algunos datos
Step13:  Ciclos de control
Cuando queremos ejecutar código varias veces tenemos varias opciones, vamos a explorar rapidamente el ciclo for.
python
    for paso in pasos
Step14:  ó agregarlo en una lista nueva
Step15:  y aun muchas cosas mas, pero por ahora es momento de empezar con la práctica.
Ejercicio
Crea una lista C con los enteros positivos de un solo digito, es decir
Step16:  Ejecuta las pruebas de abajo
Step17:  Método de bisección
Para obtener una raiz real de un polinomio $f(x) = x^3 + 2 x^2 + 10 x - 20$ por el metodo de bisección, tenemos que primero definir dos puntos, uno que evaluado en el polinomio nos de positivo, y otro que nos de negativo. Propondremos $x_1 = 1$ y $x_2 = 2$, y los evaluaremos para asegurarnos de que cumplan lo que acabamos de pedir.
Step18:  Una vez que tenemos dos puntos de los que sabemos que definen el intervalo donde se encuetra una raiz, podemos empezar a iterar para descubrir el punto medio.
$$x_m = \frac{x_1 + x_2}{2}$$
Si hacemos esto ingenuamente y lo evaluamos en la función, podremos iterar manualmente
Step19:  Y de aqui podemos notar que el resultado que nos dio esto es positivo, es decir que la raiz tiene que estar entre $x_1$ y $x_M$. Por lo que para nuestra siguiente iteración usaremos el nuevo intervalo $x_1 = 1$ y $x_2 = 2.875$, es decir que ahora asignaremos el valor de $x_M$ a $x_2$.
Step20:  Y podriamos seguir haciendo esto hasta que tengamos la exactitud que queremos, pero esa no seria una manera muy inteligente de hacerlo (tenemos una maquina a la que le gusta hacer tareas repetitivas y no la aprovechamos?).
En vez de eso, notemos que la formula no cambia absolutamente en nada, por lo que la podemos hacer una funcion y olvidarnos de ella.
Step21:  Si volvemos a ejecutar el codigo que teniamos, sustituyendo esta función, obtendremos exactamente el mismo resultado
Step22:  Y ahora lo que tenemos que hacer es poner una condicion para que $x_M$ se intercambie con $x_1$ ó $x_2$ dependiendo del signo.
Step23:  Si, yo se que parece raro, pero si lo revisas con calma te daras cuenta que funciona.
Ya casi llegamos, tan solo tenemos que ir guardando cada una de las aproximaciones en un arreglo, y calcularemos el numero de aproximaciones necesarias para llegar a la precisión requerida. Tomemos en cuenta $\varepsilon = 0.001$. La formula para el numero de aproximaciones necesarias es
Step24:  Es decir, $n = 10$.
<ASSISTANT_TASK:>
Python Code:
2 + 3
2*3
2**3
sin(pi)
Explanation: Introducción a Jupyter
Expresiones aritmeticas y algebraicas
Empezaremos esta práctica con algo de conocimientos previos de programación. Se que muchos de ustedes no han tenido la oportunidad de utilizar Python como lenguaje de programación y mucho menos Jupyter como ambiente de desarrollo para computo cientifico, asi que el primer objetivo de esta práctica será acostumbrarnos a la sintaxis del lenguaje y a las funciones que hacen especial a Jupyter.
Primero tratemos de evaluar una expresión aritmetica. Para correr el código en la siguiente celda, tan solo tienes que hacer clic en cualquier punto de ella y presionar las teclas Shift + Return.
End of explanation
from math import sin, pi
sin(pi)
Explanation: Sin embargo no existen funciones trigonométricas cargadas por default. Para esto tenemos que importarlas de la libreria math:
End of explanation
a = 10
a
Explanation: Variables
Las variables pueden ser utilizadas en cualquier momento, sin necesidad de declararlas, tan solo usalas!
End of explanation
c = 
Explanation: Ejercicio
Ejecuta el siguiente calculo y guardalo en una variable:
$$
c = \pi *10^2
$$
Nota: Una vez que hayas concluido el calculo y guardado el valor en una variable, puedes desplegar el valor de cualquier variable al ejecutar en una celda el nombre de la variable
End of explanation
from pruebas_1 import prueba_1_1
prueba_1_1(_, c)
Explanation: Ejecuta la prueba de abajo para saber si has creado el codigo correcto
End of explanation
A = [2, 4, 8, 10]
A
Explanation: Listas
Las listas son una manera de guardar varios datos en un mismo arreglo. Podemos tener por ejemplo:
End of explanation
A*2
Explanation: Pero si intentamos multiplicar estos datos por un numero, no tendrá el comportamiento esperado.
End of explanation
f = lambda x: x**2 + 1
Explanation: Funciones
Podemos definir funciones propias de la siguiente manera:
End of explanation
f(2)
Explanation: Esta linea de codigo es equivalente a definir una función matemática de la siguiente manera:
$$
f(x) = x^2 + 1
$$
Por lo que si la evaluamos con $x = 2$, obviamente obtendremos como resultado $5$.
End of explanation
def g(x):
    y = x**2 + 1
    return y
Explanation: Esta notación que introducimos es muy util para funciones matemáticas, pero esto nos obliga a pensar en las definiciones de una manera funcional, lo cual no siempre es la solución (sobre todo en un lenguaje con un paradigma de programación orientado a objetos).
Esta función tambien puede ser escrita de la siguiente manera:
End of explanation
g(2)
Explanation: Con los mismos resultados:
End of explanation
def cel_a_faren(grados_cel):
    
    grados_faren = # Escribe el codigo para hacer el calculo aqui
    
    return grados_faren
Explanation: Ejercicio
Define una función que convierta grados Celsius a grados Farenheit, de acuerdo a la siguiente formula:
$$
F = \frac{9}{5} C + 32
$$
End of explanation
cel_a_faren(10)
cel_a_faren(50)
from pruebas_1 import prueba_1_2
prueba_1_2(cel_a_faren)
Explanation: Y para probar trata de convertir algunos datos:
End of explanation
for dato in A:
    print dato*2
Explanation: Ciclos de control
Cuando queremos ejecutar código varias veces tenemos varias opciones, vamos a explorar rapidamente el ciclo for.
python
    for paso in pasos:
        ...
        codigo_a_ejecutar(paso)
        ...
En este caso el codigo se ejecutará tantas veces sean necesarias para usar todos los elementos que hay en pasos.
Por ejemplo, pordemos ejecutar la multiplicacion por 2 en cada uno de los datos:
End of explanation
B = []
for dato in A:
    B.append(dato*2)
    
B
Explanation: ó agregarlo en una lista nueva:
End of explanation
C = [] # Escribe el codigo para declarar el primer arreglo adentro de los corchetes
C
D = []
# Escribe el codigo de tu ciclo for aqui
D
Explanation: y aun muchas cosas mas, pero por ahora es momento de empezar con la práctica.
Ejercicio
Crea una lista C con los enteros positivos de un solo digito, es decir: $\left{ x \in \mathbb{Z} \mid 0 \leq x < 10\right}$
Crea una segunda lista D con los cuadrados de cada elemento de C
End of explanation
from pruebas_1 import prueba_1_3
prueba_1_3(C, D)
Explanation: Ejecuta las pruebas de abajo
End of explanation
f = lambda x: x**3 + 2*x**2 + 10*x - 20
f(1.0)
f(2.0)
Explanation: Método de bisección
Para obtener una raiz real de un polinomio $f(x) = x^3 + 2 x^2 + 10 x - 20$ por el metodo de bisección, tenemos que primero definir dos puntos, uno que evaluado en el polinomio nos de positivo, y otro que nos de negativo. Propondremos $x_1 = 1$ y $x_2 = 2$, y los evaluaremos para asegurarnos de que cumplan lo que acabamos de pedir.
End of explanation
x_1, x_2 = 1.0, 2.0
xm1 = (x_1 + x_2)/2.0
f(xm1)
Explanation: Una vez que tenemos dos puntos de los que sabemos que definen el intervalo donde se encuetra una raiz, podemos empezar a iterar para descubrir el punto medio.
$$x_m = \frac{x_1 + x_2}{2}$$
Si hacemos esto ingenuamente y lo evaluamos en la función, podremos iterar manualmente:
End of explanation
x_1, x_2 = x_1, xm1
xm2 = (x_1 + x_2)/2.0
f(xm2)
Explanation: Y de aqui podemos notar que el resultado que nos dio esto es positivo, es decir que la raiz tiene que estar entre $x_1$ y $x_M$. Por lo que para nuestra siguiente iteración usaremos el nuevo intervalo $x_1 = 1$ y $x_2 = 2.875$, es decir que ahora asignaremos el valor de $x_M$ a $x_2$.
End of explanation
def biseccion(x1, x2):
    return (x1 + x2)/2.0
Explanation: Y podriamos seguir haciendo esto hasta que tengamos la exactitud que queremos, pero esa no seria una manera muy inteligente de hacerlo (tenemos una maquina a la que le gusta hacer tareas repetitivas y no la aprovechamos?).
En vez de eso, notemos que la formula no cambia absolutamente en nada, por lo que la podemos hacer una funcion y olvidarnos de ella.
End of explanation
x_1, x_2 = x_1, xm1
xm2 = biseccion(x_1, x_2)
f(xm2)
Explanation: Si volvemos a ejecutar el codigo que teniamos, sustituyendo esta función, obtendremos exactamente el mismo resultado:
End of explanation
x_1, x_2 = 1.0, 2.0
xm1 = biseccion(x_1, x_2)
f(xm1)
if x_2*xm1 > 0:
    x_2 = xm1
else:
    x_1 = xm1
    
xm2 = biseccion(x_1, x_2)
f(xm2)
if x_2*xm2 > 0:
    x_2 = xm2
else:
    x_1 = xm2
    
xm3 = biseccion(x_1, x_2)
f(xm3)
Explanation: Y ahora lo que tenemos que hacer es poner una condicion para que $x_M$ se intercambie con $x_1$ ó $x_2$ dependiendo del signo.
End of explanation
n = (log(1) - log(0.001))/(log(2))
n
Explanation: Si, yo se que parece raro, pero si lo revisas con calma te daras cuenta que funciona.
Ya casi llegamos, tan solo tenemos que ir guardando cada una de las aproximaciones en un arreglo, y calcularemos el numero de aproximaciones necesarias para llegar a la precisión requerida. Tomemos en cuenta $\varepsilon = 0.001$. La formula para el numero de aproximaciones necesarias es:
$$n = \frac{\ln{a} - \ln{\varepsilon}}{\ln{2}}$$
donde $a$ es el tamaño del intervalo original.
End of explanation
def metodo_biseccion(funcion, x1, x2, n):
    xs = []
    for i in range(n):
        xs.append(biseccion(x1, x2))
        if funcion(x2)*funcion(xs[-1]) > 0:
            x2 = xs[-1]
        else:
            x1 = xs[-1]
    return xs[-1]
metodo_biseccion(f, 1.0, 2.0, 10)
Explanation: Es decir, $n = 10$.
End of explanation
<END_TASK>