Madrid Manual PDF
Madrid Manual PDF
Madrid Manual PDF
Release 2010/2011
2. Bloque I: Introduccin 5
2.1. Tipos de datos en Sage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3. Control del flujo del programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.4. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.5. Un poco de programacin funcional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.6. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
I
5.2. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
5.3. Contar y enumerar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
5.4. Grafos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
5.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
5.6. Experimentos con numeros aleatorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.7. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
ndice 299
II
CAPTULO 1
1.1 Introduccin
Ya empezado el curso 07/08, en el departamento surgi la idea de incorporar a los, por entonces nacientes, estudios
de Grado de Matemticas, un asignatura dedicada a cualificar a nuestros estudiantes con una nueva habilidad: usar el
ordenador para resolver, o al menos ilustrar, los problemas.
La asignatura de laboratorio de matemticas pretende ensear a usar el ordenador como herramienta para aprender y
para experimentar en matemticas. Para ello usamos el programa libre Sage, que permite acceder a una vasta coleccin
de libreras matemticas usando el lenguaje python, una eleccin popular para aprender a programar. La asignatura se
imparte a lo largo de todo un ao, e intenta, an tmidamente, coordinarse con las otras asignaturas del curso para usar
cuando sea posible ejemplos extrados de aquellas.
En la Universidad Autnoma de Madrid, el laboratorio de matemticas se imparte a dos grupos de alumnos muy
distintos: alumnos de primero del grado en matemticas, que en su mayora no saben programar, y alumnos de segundo
del doble grado en matemticas e informtica, que aprendieron a programar en C en primer curso, y que seguirn
aprendiendo mucho ms sobre programacin. Estas notas corresponden al segundo grupo de alumnos, y por ello
contienen temas avanzados relacionados con la programacin que no son en absoluto imprescindibles para el objetivo
de la asignatura.
1.2 Crditos
1
Laboratorio de Matemticas, Release 2010/2011
1.3 Agradecimientos
Hristo Inouzhe prepar varios vdeos didcticos sobre el material de estas notas (puedes encontrar los links ms
abajo). La Universidad Autnoma de Madrid financi un proyecto de innovacin docente que nos permiti regalar
dvds live con Sage a nuestras alumnas. Luis Guijarro y Mara Calle probaron la parte de clculo II en las clases
de esta asignatura. Pablo Fernndez Gallardo nos prest material y comentarios para las secciones de combinatoria
y probabilidad. La Wikipedia (en ingls) nos ahorr muchas horas de bsqueda bibliogrfica (y nos prest algunas
imgenes).
Y la comunidad de Sage resolvi nuestras dudas y resolvi muy pero que muy rpido algunos problemas que encon-
tramos durante el curso.
Optimizacin con cython: el conjunto de Mandelbrot (bloque 2, Eficiencia en clculo cientfico), Pablo Angulo:
parte 1, parte 2
Mtodo de montecarlo vs fuerza bruta (bloque 4, Experimentos con numeros aleatorios), Hristo Inouzhe.
Coloracion y edicion de grafos con SAGE (bloque 4 Grafos), Hristo Inouzhe.
Puntos crticos por el metodo de lagrange (bloque 5 Clculo vectorial), Hristo Inouzhe.
Haz de conicas con SAGE (bloque 5 Curvas planas), Hristo Inouzhe.
Ajuste de Modelos utilizando SAGE (bloque 6, Regresin y ajuste de modelos), Hristo Inouzhe.
Este documento ha sido generado a partir de archivos rst (ReStructuredText), que a su vez fueron generados a partir
de archivos sws (hojas de trabajo de Sage). Los mismos archivos rst dieron lugar a documentacin en formatos html y
pdf (via latex).
Tanto los archivos rst como los archivos sws se distribuyen con la misma licencia que este documento. Puedes encontrar
todos esos documentos en la web oficial de la asignatura (a da de hoy, est en el sitio web de Pablo Angulo en la UAM).
1.6 Licencia
Este documento se distribuye con una licencia GFDL , cc-by-sa, a tu eleccin. Tambin se distribuye con la licencia
cc-by-nc usada normalmente en ocw, excepto algunas imgenes extradas de la wikipedia con una licencia incompati-
ble, que se listan ms abajo.
This work is licensed under a GFDL license, or a Creative Commons Attribution-Share Alike 3.0 License, at your
choice. It is also distributed under a cc-by-nc license, more common in ocw, with the exception of a few images taken
from wikipedia, which are listed below.
Sigue una lista de imgenes usadas en las notas y tomadas de la wikipedia. Todas las imgenes se distribuyen con una
licencia compatible con GFDL y cc-by-sa, pero una de ellas no tiene una licencia compatible con cc-by-nc.
Si quieres usar esas imgenes, puedes seguir el link de abajo para leer su licencia.
la tabla hash en b2s1:
Caminos en una malla en Bloque IV: Contar y enumerar
Caminos montonos en Bloque IV: Contar y enumerar
1.6. Licencia 3
Laboratorio de Matemticas, Release 2010/2011
Bloque I: Introduccin
Se presenta el entorno Sage, los tipos de datos y las estructuras de control de python, poniendo ejemplos con contenido
matemtico siempre que sea posible. En la ltima sesin se presentan formas de trabajar en python que no son posibles
en lenguajes como C.
En esta asignatura usaremos el programa Sage para resolver distintos problemas de matemticas con el ordenador. El
programa es libre, lo que nos permite copiarlo, modificarlo y redistribuirlo libremente. Sage consta de un buen nmero
de libreras para ejecutar clculos matemticos y para generar grficas. Para llamar a estas libreras se usa el lenguaje
de programacin python, para el que existen un buen nmero de recursos didcticos disponibles.
Python es un lenguaje de propsito general de muy alto nivel, que permite representar conceptos abstractos de forma
natural y, en general, hacer ms con menos cdigo. Buena parte de las libreras que componen Sage se pueden usar
directamente desde python, sin necesidad de acarrear todo el entorno de Sage.
Existen varias formas de interactuar con Sage: desde la consola, desde ciertos programas como TeXmacs o Cantor, y
desde el navegador de internet, como estamos haciendo ahora. Para ello, Sage crea un servidor web que escucha las
peticiones del cliente (un navegador), realiza los clculos que le pide el cliente, y le devuelve los resultados. En esta
asignatura slo usaremos el interfaz web.
Cuadros de texto
Los cuadros de texto como ste permiten incluir comentarios en una hoja de trabajo. Si haces doble clic sobre el
cuadro de texto puedes editar el contenido. Al entrar en modo de edicin, aparecen botones y desplegables para
cambiar algunos aspectos del estilo del texto. Lo ms importante para mantener un orden en la hoja de trabajo es el
primer desplegable, que permite elegir si se trata de un prrafo, un encabezado.
Cuadros de cdigo
Los cuadros de cdigo son rectangulares y su borde cambia de color al seleccionarlos. Dentro de los cuadros de cdigo
podemos escribir instrucciones que sern ejecutadas al pulsar el botn evaluate o teclear maysculas+Enter.
5
Laboratorio de Matemticas, Release 2010/2011
Puedes crear un nuevo cuadro de cdigo pulsando sobre la lnea azul que aparece al poner el cursor sobre un cuadro
de cdigo existente, o pulsando control+Enter dentro de un cuadro de cdigo. Puedes crear un nuevo bloque de texto
pulsando sobre la misma lnea azul, pero manteniendo pulsada la tecla de maysculas.
sage: print Hola, Mundo
Hola, Mundo
Si ponemos varias lneas de cdigo, se ejecutan una tras otra de arriba a abajo. El intrprete de instrucciones lee
las instrucciones, las interpreta y ejecuta lo que se le pide. Si al ejecutar las instrucciones se produce una salida, se
muestra debajo del cuadro de cdigo.
sage: #Los comentarios en python comienzan con #
sage: print Hola #El comando print muestra sus argumentos en la salida del programa
sage: print Mundo
Hola
Mundo
2.1.2 Operaciones
Podemos usar los bloques de comandos como una simple calculadora, escribiendo operaciones elementales y ob-
servando el resultado debajo del cuadro. Podemos introducir nmeros con decimales usando el punto decimal. Los
parntesis marcan qu comando se ejecuta antes, naturalmente.
sage: (1*2)+(3*4)+5
19
sage: 1*(2+3)*(4+5)
45
Reglas de precedencia
En ausencia de parntesis, Sage decide qu operaciones se ejecutan antes y cules despus usando unas reglas de
precedencia de operadores bastante estndar. En la siguiente tabla de operadores, cada uno se ejecutar antes que los
que estn por encima.
Los operadores con la misma precedencia se agrupan de izquierda a derecha, excepto la exponenciacin, que agrupa
de derecha a izquierda.
No necesitas conocer todos estos operadores por ahora:
operador Descripcin
or O booleano
and Y booleano
not x NO booleano
in , not in , is , is not , < , <= , > , >= , <> , != , == comparaciones, comprobacin de pertenencia y de tipo
| O bit a bit
& Y bit a bit
+,- Suma y resta
* , / , / / ,% multiplicacin, divisin, el resto
+ x , -x , ~ x positivos, negativos, NO bit a bit
^ exponenciacin
x [indice] , x [indice: indice] , x (arguments. ..) , ndices, rebanadas, llamada a funciones, referencia a
x.attribute atributos
( expresiones ...) , [expresiones ...] , {clave: dato ...} tuplas, listas, diccionarios, evaluacin de expresiones
Ejercicio. Intenta predecir el resultado de ejecutar las instrucciones de debajo antes de pulsar el botn de evaluar.
sage: 2*3^1+1*3^2
sage: 2*3^((1+1)*3)^2
sage: 1==2-1
Llamadas a funciones
Adems de las operaciones aritmticas, podemos usar las muchas funciones disponibles. La forma de usarlas es escribir
el nombre de la funcin, seguido del argumento, o argumentos, entre parntesis, y separados por comas.
sage: sin(pi/3)
1/2*sqrt(3)
sage: (1 + sin(pi/3))/2
1/4*sqrt(3) + 1/2
sage: max(2,3)
3
2.1.3 Variables
Por ejemplo,
numero = 1 + 2 + 3 + 4 + 5 + 6
largo = 2.56*20
angulo = pi/3
Para poder ver el valor de una variable podemos usar el comando print :
print numero
print largo, angulo
print largo * sin(angulo)
Una vez definida una variable, podemos hacer clculos con su valor:
masa = 3
aceleracion = 10
fuerza = masa\*aceleracion
print fuerza
sage: velocidad == 3
True
Nota : los nombres de las variables deben empezar por una letra o un guin bajo (_), pueden contener nmeros, y son
distintos si se usan maysculas o minsculas. Las variables numero y Numero son distintas .
sage: print Numero
Traceback (most recent call last):
...
NameError: name Numero is not defined
Usando el comando del , podemos liberar una variable, y a partir de ese punto el nombre de la variable deja de estar
definido.
del variable
sage: numero = 12
sage: print 2*numero
24
Al usar del solamente liberamos la referencia a un dato en la memoria, pero no el dato en s. Otras referencias a ese
dato siguen siendo vlidas, y no se corrompen por el uso de del . Por tanto, esta instruccin no tiene nada que ver
con las reservas de memoria en lenguajes de bajo nivel como C, que reservan y liberan espacio en la memoria.
Para liberar la memoria no usada, Python usa un colector de basura . Este colector de basura identifica los objetos a
los que no apunta ninguna referencia y los libera.
sage: lista1 = [1,2,3,4]
sage: lista2 = lista1
sage: del lista1
sage: print lista2
[1, 2, 3, 4]
En las variables podemos almacenar cualquier tipo de datos que resulte de evaluar una expresin. Ms adelante en el
curso guardaremos en las variables matrices, grficas e incluso objetos abstractos como espacios vectoriales. Por ahora
hemos usado los siguientes tipos de datos:
Booleanos: slo toman el valor True o False .
Enteros: cualquier nmero entero, positivo o negativo, de longitud arbitraria. Ej.: 1, 10, -30
Racionales Ej.: 1/2, -3/4
Nmeros de coma flotante: un nmero con unos cuantos dgitos decimales y un exponente, que representa un
nmero real de forma aproximada. Ej.: 1.25, -1.5e6
Expresiones simblicas:expresiones matemticas que representan nmeros reales de forma exacta. Ej.: pi/4
(/4), (1+sqrt(2))/2 ( 1+2 2 )
Las variables pueden almacenar referencias a datos de cualquier tipo: python usa tipado dinmico . Sin embargo,
todos los datos tienen necesariamente un tipo
sage: 2 >=3
False
sage: var1=2
sage: var2=3
sage: #La variable es_menor almacena un booleano
sage: es_menor = var1 < var2
sage: factorial(1000)
40238726007709377354370243392300398571937486421071463254379991042993851239862902059204420848696940480
sage: numero/factorial(1001)
1/1001
Al usar el ordenador para hacer matemticas es importante saber si los datos del ordenador representan los objetos
matemticos de forma exacta.
Es imposible almacenar en un ordenador con una cantidad finita de memoria todas las cifras decimales del nmero
pi . Una alternativa es almacenar slo unas cuantas cifras, y cometer por tanto un pequeo error. Para la mayora de
las aplicaciones es ms que suficiente usar 10 o 20 cifras decimales significativas . Con esto queremos decir que al
escribir el nmero en notacin exponencial, descartamos todas las cifras a partir de la nmero 10 o 20. Por ejemplo,
el nmero 1/ con diez dgitos significativos:
3,183098861 101
6, 62606 1034
Al hacer operaciones con nmeros que contienen errores, los errores se suman y multiplican, y pueden acabar estro-
peando un clculo. Otra alternativa es usar una variable simblica, y usar las reglas aritmticas sin hacer clculos con
decimales, exactamente igual que os contaban en el instituto:
3
2 32
34 = 21/32+2/3 321 = 21 3 = 3/2
3
4
Al estar orientado preferentemente al pblico matemtico, Sage prefiere usar expresiones simblicas exactas antes que
aproximaciones numricas. Como vimos antes, para obtener una representacin decimal de una expresin simblica,
podemos usar el comando n() (n de numrico).
sage: 1/pi
1/pi
sage: n(1/pi)
0.318309886183791
sage: a = sqrt(2)
sage: n(a)
1.41421356237310
Cada tipo de datos tiene sus propios mtodos: funciones que se aplican slo a datos de este tipo y que se llaman
escribiendo primero la variable que contiene el dato, despus un punto (.), y despus el mtodo:
variable.metodo()
Por ejemplo, podemos calcular la factorizacin de un nmero entero, pero no de un nmero real, o podramos intentar
simplificar una expresin simblica, pero no podemos simplificar un nmero entero.
sage: a = 12
sage: print a.factor() #Factorizacion del entero a
2^2 * 3
sage: b = 4.7
sage: print b.integer_part() #Parte entera del numero real b
4
sage: c = (3*sqrt(6)+sqrt(2))/sqrt(8)
sage: print c.full_simplify() #Intenta simplificar la expresion c
3/2*sqrt(3) + 1/2
[Tabulador]
sage: b.
Aunque Sage utiliza el lenguaje python, los tipos numricos en Sage no corresponden exactamente a los tipos num-
ricos de python, ya que los nmeros en Sage tienen ms funcionalidad. Por ejemplo, los enteros de Sage permiten
calcular su lista de divisores, su expresin binaria, etctera. Por defecto, los tipos numricos son enteros, racionales y
nmeros reales de Sage, no de python.
sage: a = 12 #Entero de SAGE
sage: #a = Integer(12) #Otra forma equivalente de definir un entero de SAGE
sage: b = int(12) #Entero de 32 bits de python
sage: d.exact_rational()
Traceback (most recent call last):
...
AttributeError: float object has no attribute exact_rational
sage: c.exact_rational()
5404319552844595/4503599627370496
Cadenas de caracteres
Las cadenas de caracteres son secuencias de caracteres (letras, nmeros, puntuacin...) que se manipulan de forma
conjunta y se pueden almacenar en una variable. Para introducir cadenas de caracteres en el cdigo, separamos el
texto entre comillas simples (cadena), comillas dobles (cadena), o triples comillas simples para cadenas ms largas
(cadena larga).
sage: print Hola
Hola
Para acceder al carcter que ocupa la posicin j-sima en una cadena de caracteres, usamos la notacin
cadena[j]
Ojo! Se empieza a contar desde el nmero 0, el ndice del primer carcter. El ndice del ltimo carcter es L-1, donde
L es la longitud de la cadena.
Tambin podemos acceder a una subcadena (o rango), desde el ndice j (inclusive) hasta el k (exclusive), con la
notacin
cadena[j:k]
Por ejemplo:
Tuplas
Las tuplas contienen unos cuantos elementos no necesariamente del mismo tipo. Basta con poner las variables entre
parntesis separadas por comas para formar una tupla.
tupla = (elemento1, elemento2)
Una vez creada, podemos acceder a sus elementos usando corchetes, igual que hacamos con las cadenas de caracteres.
sage: frutas = (pera,manzana,naranja)
sage: primos = (2,3,5,7,11,13)
sage: print frutas[0]
sage: print primos[0:3]
sage: primo = primos[2]
sage: fruta = frutas[2]
sage: print primo, fruta
pera
(2, 3, 5)
5 naranja
Las operaciones + y * actan sobre tuplas de la misma forma que sobre cadenas de caracteres.
sage: (1,2,a) + (3,4)
(1, 2, a, 3, 4)
Si queremos guardar cada elemento de la tupla en una variable distinta, podemos usar el acceso a los elementos usando
los corchetes:
frutas = (pera,manzana,naranja)
a=frutas[0]
b=frutas[1]
c=frutas[2]
lo que, al igual que el cdigo anterior, guarda en la variable a el primer elemento de la tupla, en la variable b el
segundo y en la variable c el tercero. Si intentamos desempaquetar una tupla de N elementos con ms, o con menos
de N variables, obtendremos un error.
sage: frutas = (pera,manzana,naranja)
sage: a,b,c = frutas
sage: print c + , + b + , + a
naranja,manzana,pera
Las tuplas son immutables , es decir, que no se puede quitar ni aadir elementos a una tupla una vez ha sido creada,
ni siquiera sustituir un elemento por otro.
Listas
Las listas se usan de modo similar a las tuplas, pero se pueden quitar y aadir elementos en cualquier momento.
Decimos que son contenedores dinmicos de datos .
La sintaxis para crear listas es igual a la de las tuplas, pero usando corchetes en vez de parntesis. Una vez creadas,
podemos acceder a sus elementos usando los corchetes, pero adems podemos asignar nuevos valores a posiciones
arbitrarias de la lista.
sage: lista_frutas = [pera,manzana,naranja]
sage: print lista_frutas
sage: lista_frutas[1] = fresa
sage: print lista_frutas
[pera, manzana, naranja]
[pera, fresa, naranja]
Tambin podemos eliminar elementos de la lista con el comando del y aadir elementos al final de la lista con el
comando append .
sage: print lista_frutas[1]
fresa
El comando srange permite crear listas de nmeros de SAGE (en python se usa la funcin range , que devuelve
enteros de python):
srange(j,k,d) : devuelve los nmeros entre j (inclusive) y k (exclusive), pero contando de d en d elemen-
tos. A pesar de que el uso ms extendido es con nmeros enteros, los nmeros j, k y d pueden ser enteros o
no.
Abreviaturas:
srange(k) : devuelve srange(0,k,1). Si, en particular, k es un natural, devuelve los naturales entre 0
(inclusive) y k (exclusive); y si k es negativo, devuelve una lista vaca.
srange(j,k) : devuelve la lista srange(j,k,1). Si, en particular, j y k son enteros, devuelve los enteros
entre j (inclusive) hasta el anterior a k.
sage: print srange(10)
sage: print srange(10,20)
sage: print srange(10,20,2)
sage: print srange(10,20,1.5)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[10, 12, 14, 16, 18]
[10.0000000000000, 11.5000000000000, 13.0000000000000, 14.5000000000000, 16.0000000000000, 17.5000000
Pertenencia
En cualquiera de las estructuras anteriores, podemos comprobar si un elemento est en la lista usando el operador in
, que devuelve True o False segn el elemento pertenezca o no al contenedor de datos.
sage: 10 in [1,2,3,4]
False
Las conversiones tupla -> lista, cadena -> lista, lista->tupla y cadena->tupla son triviales, usando las funciones tuple
y list :
sage: tupla_0 = (1,2,3)
sage: lista_0 = [a,b,c]
sage: cadena_0 = qwerty
sage: tuple(cadena_0)
(q, w, e, r, t, y)
sage: list(tupla_0)
[1, 2, 3]
Sin embargo, aunque existe la funcin str , el resultado no es el que deseamos normalmente:
sage: str(lista_0)
"[a, b, c]"
sage: str(tupla_0)
(1, 2, 3)
La funcin str intenta dar una representacin textual lo ms fidedigna posible del objeto que pasamos como argu-
mento.
La situacin siguiente es ms usual: tenemos una lista de cadenas de caracteres, y queremos unir esas cadenas, op-
cionalmente usando otra cadena como separador. Para ello usamos el mtodo join , que tienen todas las cadenas de
caracteres.
sage: , .join(lista_0)
a, b, c
sage: .join(lista_0)
abc
Para poder ver en pantalla los valores de las variables, hemos usado el comando print .
print var1
print var1, var2, ...
De esta forma podemos mostrar los valores de las variables. Tambin podemos escribir texto que contenga los valores
de estas variables usando el operador % y los cdigos de formato. Para usarlo, escribimos una cadena que contiene
cdigos de formato seguida del operador % y a continuacin la variable que queremos sustituir, o una tupla de variables
si hay ms de una.
lados = 8
print El nmero de lados es %d %lados
area = 17.5
print El rea es %f %area
print El rea del polgono de %d lados es %f %(lados, area)
nombre = Juan
print %s vino ayer a cenar %nombre
2.2 Ejercicios
2.2.1 1.
Guarda en variables el nmero de trminos de una progresin aritmtica, el primer elemento y el paso. Escribe cdigo
que devuelva la suma de todos los trminos, usando una frmula. Usa los cdigos de formato para escribir el resultado.
Nota: haz las asignaciones de variables en un cuadro de cdigo y los clculos en otro.
sage: primero=1
sage: paso=2
sage: nterminos=4
2.2.2 2.
donde A es el area, P el permetro y a la apotema, escribe un cdigo que calcule el rea y el permetro de un polgono
regular usando los valores de dos variables: lados (nmero de lados) y longitud (longitud del lado).
Usa los cdigos de formato para escribir el resultado con cuatro dgitos significativos.
Indicacin: deduce o busca en internet una frmula para obtener la apotema en funcin de los datos conocidos.
sage: lados = 10
sage: longitud= 2
Sage contiene funcionalidad para trabajar en una gran cantidad de reas de las matemticas. Las formas principales de
descubrir funcionalidad son:
1. Leer la ayuda, haciendo click en el link help en la cabecera de la pgina.
2. Escribir parte de una funcin o un mtodo, y pulsar el tabulador para ver los comandos que comien-
zan con los caracteres introducidos.
3. Escribir el nombre de una funcin o de un mtodo seguido de un carcter de interrogacin para leer
la ayuda contextual.
Usando el mtodo 2, busca una funcin que acepta un nmero y devuelve un booleano: True si el
nmero es un cuadrado perfecto (en ingls a square number ), y False si no lo es (pista: su nombre
comienza por is_ , pues se trata de una comprobacin). Usa el mtodo 3 para comprobar que tu
suposicin es correcta.
Busca otra funcin que devuelva True si el nmero es la potencia de un nmero primo (en ingls,
the power of a prime number ), y False si no lo es.
Busca un mtodo que tienen los nmeros racionales, que devuelva el denominador.
Busca un mtodo que tienen las cadenas de caracteres, que cuente el nmero de apariciones de un
carcter. Usa ese mtodo para contar el nmero de letras a en esta frase.
2.2. Ejercicios 17
Laboratorio de Matemticas, Release 2010/2011
Hemos visto en clase que las listas tienen un mtodo append que permite aadir un elemento a la
lista. Busca un mtodo que tienen las listas que permite aadir todos los elementos de una segunda
lista.
sage: is_prime?
<html>...</html>
Dada una cadena de caracteres, produce otra resultado de ordenar los caracteres de la primera. No debes alterar el
nmero de caracteres. Ejemplo:
En un lugar de cuyo nombre no quiero acordarme \-> Eaaabccddeeeegilmmnnnnoooooqrrrrruuuu
Pista: las cadenas de caracteres no tienen un mtodo para ordenarlas, pero las listas s la tienen.
2.2.5 5.
Debajo de este texto puedes ver dos cuadros de texto. Tu objetivo es escribir texto formateado con el operador %, de
tal forma que si ejecutas el primer bloque, el texto que aparezca en pantalla sea:
El nmero 10 es par
El lado mide 15.100
Juan tiene 10 manzanas
mientras que si ejecutas el segundo bloque, dando valores distintos a las variables, el resultado sea:
El nmero 7 es par
El lado mide 1250000.000
Alejandro tiene 7 manzanas
sage: nombre=Juan
sage: numero=10
sage: longitud=15.1
sage: nombre=Alejandro
sage: numero=7
sage: longitud=1.25e6
Alternativas lgicas
es bastante diferente de la otros lenguajes como C, pero la esencia es la misma: Si la primera condicin es cierta, se
ejecuta el primer bloque de instrucciones y se abandona el bloque de instrucciones. Si es falsa, el primer bloque de
instrucciones no se ejecuta , y se pasa a evaluar la segunda condicin. Si es cierta, se ejecuta el segundo bloque de
instrucciones y se abandona el bloque if...else . Finalmente, si todas las condiciones son falsas, se ejecuta el bloque de
instrucciones final, que va precedido de la palabra clave else .
La indentacin marca el principio y el final de cada bloque . Es decir, las lneas de los bloques de instrucciones
comienzan con cuatro espacios. Despus del bloque if...else, el programa puede tener ms lneas, que no estarn
indentadas.
Al final de cada condicin if, elif o else, escribimos dos puntos ( : ).
Tanto las instrucciones elif como la instruccin else son opcionales.
sage: numero = 6
sage: if numero %2==0:
... print %d es par %numero
6 es par
sage: numero = 6
sage: if numero %2==0:
... print %d es par %numero
sage: else:
... print %d es impar %numero
6 es par
Observa en este ejemplo el papel de la indentacin para marcar el principio y final de cada bloque:
sage: numero=-10
sage: if numero>0:
... print La raiz de %f es %f %(numero,numero^0.5)
sage: else:
... print La raiz de %f es %f *i %(numero,(-numero)^0.5)
sage: print El cuadrado de %f es %f %(numero, numero^2) #Esta instruccin no forma parte del blo
La raiz de -10.000000 es 3.162278*i
El cuadrado de -10.000000 es 100.000000
La condicin puede ser cualquier expresin que al evaluar devuelva un booleano, y no se pueden hacer asignaciones
. Tambin se puede poner un nmero como condicin (de cualquier tipo de datos), en cuyo caso 0 se interpreta como
False , y cualquier otra cosa como True, o un contenedor (lista, tupla, cadena...), en cuyo caso un contenedor vaco se
interpreta como False , y cualquier otra cosa como True.
sage: ls = [a]
sage: #ls = []
sage: if ls:
... print la lista no est vaca
la lista no est vaca
Bucle for
Utilizando la instruccin for podemos repetir una misma instruccin sobre cada elemento de una lista.
for elemento in lista:
instrucciones
El bloque de instrucciones se ejecutar una vez por cada elemento de la lista. La variable elemento tomar sucesiva-
mente el valor de cada elemento de la lista. A cada ejecucin del bloque de instrucciones lo llamamos una iteracin .
Veamos algunos ejemplos:
sage: suma = 0
sage: numeros = [1, 1, 3, 7, 13, 21, 31, 43]
sage: for k in numeros:
... suma = suma + k
sage: print suma
120
Bucles while
En el ejemplo de debajo, la variable i vale 0 antes de entrar en el bucle while. Despus, el valor de i aumenta en una
unidad en cada iteracin del bucle. Al cabo de 10 iteraciones, el valor de i es 10, y se abandona el bucle porque la
condicin i < 10 ya no se verifica.
sage: i = 0
sage: while i<10:
... i = i + 1
sage: print i
10
Si accidentalmente escribes un bucle infinito o simplemente quieres detener el clculo antes de que termine, puedes
usar la accin interrupt , que est en el men desplegable Action , al principio de la hoja de trabajo. Observa cmo al
evaluar el cdigo de abajo, aparece una lnea verde debajo y a la izquierda del bloque de cdigo. Esta lnea indica que
se est ejecutando el cdigo, y que todava no ha concludo. Al ejecutar interrupt , el cmputo se cancela, y la lnea
verde desaparece.
sage: x=0 #El valor de x no cambia al iterar
sage: while x<100:
... x = x*1.5
sage: print x
^CTraceback (most recent call last):
File "<stdin>", line 1, in <module>
File "_sage_input_11.py", line 10, in <module>
exec compile(uopen("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_
File "", line 1, in <module>
Podemos anidar bloques if...else, for y while de cualquier forma posible, cuidando de que la indentacin marque el
principio y el final de cada bloque.
Por ejemplo, calculamos la suma de los inversos de los primos menores que k, usando un acumulador:
X 1
p
p<k,p primo
sage: k=100
sage: suma = 0
sage: for j in range(k):
... if is_prime(j):
... suma += 1/j
sage: print La suma de los inversos de los primos menores que %d es %.3f %(k, suma)
La suma de los inversos de los primos menores que 100 es 1.803
En el siguiente ejemplo, tenemos dos bloques for, uno dentro de otro. Observamos que nada impide que el bucle
interior haga un nmero distinto de iteraciones cada vez.
sage: for j in srange(10):
... for k in srange(j):
... print k,
... print
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 3 4 5
0 1 2 3 4 5 6
0 1 2 3 4 5 6 7
0 1 2 3 4 5 6 7 8
Tablas de verdad
Para obtener la tabla de verdad de una frmula lgica, evaluamos la frmula en cada posible combinacin de valores
para p, q y r. Por ejemplo, la tabla de verdad de la frmula:
e (p q) (p r)
es:
p=True, q=True, r=True => e=True
p=True, q=True, r=False => e=True
p=True, q=False, r=True => e=False
p=True, q=False, r=False => e=True
p=False, q=True, r=True => e=False
Ejemplo : genera todas las combinaciones de valores para p, q y r anidando tres bucles for .
sage: for p in [True, False]:
... for q in [True, False]:
... for r in [True, False]:
... #Utilizamos el codigo %s para valores booleanos,
... #el mismo que usamos para cadenas de caracteres
... e = (p and q) or (p and not r)
... print p= %s, q= %s, r= %s => e= %s %(p,q,r,e)
p=True, q=True, r=True => e=True
p=True, q=True, r=False => e=True
p=True, q=False, r=True => e=False
p=True, q=False, r=False => e=True
p=False, q=True, r=True => e=False
p=False, q=True, r=False => e=False
p=False, q=False, r=True => e=False
p=False, q=False, r=False => e=False
En el siguiente ejemplo, buscamos un nmero N primo relativo a un nmero k dado y tal que k/3 N 2k/3.
Cualquier nmero con esa propiedad nos vale. Podemos empezar por k/3 y seguir hacia delante, o generar nmeros
aleatorios. Esta tcnica es bastante general y se puede usar en problemas de los que sabemos poco. Sin embargo, podra
llevar mucho tiempo a la computadora si los nmeros que buscamos son raros o, peor, si no hay ninguno.
En primer lugar, usamos un bucle for para iterar sobre todos los posibles casos, pero salimos del bucle usando la
instruccin break cuando encontramos un nmero que satisface la condicin.
sage: #Buscamos un numero que sea primo relativo a k
sage: #y que est entre k/3 y 2*k/3
sage: k=196
sage: tercio = ceil(k/3)
sage: dostercios = floor(2*k/3)
sage: for x in range(tercio, dostercios):
... if gcd(x, k) == 1: #Si x es primo relativo a k,
... #salimos del bucle for
... break #gcd es el maximo comun divisor
sage: print x
67
Hemos usado ya unas cuantas funciones definidas en Sage. Ahora aprenderemos a definir nuestras propias funciones.
La sintaxis para definir una funcin en python es:
def funcion(argumento1, argumento2, ...):
instrucciones
De nuevo, las instrucciones que componen el cuerpo de la funcin estn indentadas (comienzan con cuatro espacios),
y la primera lnea sin indentar marca el final de la declaracin de la funcin.
Por ejemplo, una funcin que acepta un nmero como argumento, y escribe por pantalla alguna informacin sobre el
nmero.
sage: def informacion(numero):
... print Informacin sobre %d %numero
... if is_prime(numero):
... print es primo
... if numero %2==0:
... print es par
... if is_power_of_two(numero):
... print es potencia de dos
Para llamar a la funcin, escribimos su nombre con los argumentos entre parntesis.
sage: informacion(2)
Informacin sobre 2
es primo
es par
es potencia de dos
Las funciones tienen la posibilidad de devolver un valor , usando la palabra clave return . El valor devuelto por la
funcin puede ser almacenado en una variable, o usado para hacer clculos, exactamente igual que para las funciones
definidas en Sage.
sage: def radio(x,y):
... return sqrt(x^2 + y^2)
sage: r1 = radio(1,1)
sage: r2 = radio(5,0)
sage: print r2-r1
-sqrt(2) + 5
Observa que cuando se ejecuta una instruccin return, se devuelve el valor correspondiente, y no se ejecuta ninguna
otra instruccin de la funcin . Observa la siguiente variante de nuestra primera funcin.
sage: def informacion_bis(numero):
... if is_prime(numero):
... return %d es primo %numero
... if numero %2==0:
... return %d es par %numero
... if is_power_of_two(numero):
... return %d es potencia de dos %numero
Documentacin
Es una buena prctica documentar el cdigo. Unas semanas despus de escribir el cdigo, no es tan fcil recordar qu
haca. Aparte de escribir comentarios cuando lo creas conveniente comenzando con el carcter # , las declaraciones de
funciones tienen una descripcin ( docstring ), que muchos programas utilizan para mostrar informacin de la funcin
en momentos apropiados. El lugar donde colocar la doctring es justo debajo de la definicin de la funcin, y tambin
indentado respecto de la funcin. La docstring es slo una cadena de caracteres , y por tanto se puede separar entre
comillas simples (cadena), comillas dobles (cadena), o triples comillas simples para cadenas ms largas (cadena
larga).
def funcion(argumento1, argumento2, ...):
docstring
instrucciones
Vamos a definir de nuevo las dos funciones anteriores colocando en su sitio las docstring de las funciones.
sage: def informacion(numero):
... Imprime en pantalla alguna informacion sobre un numero
...
... Imprime una linea que dice si es primo, otra que dice si
... es par...
...
... print Informacin sobre %d %numero
... if is_prime(numero):
... print es primo
... if numero %2==0:
... print es par
...
... #is_power_of_two devuelve True si el numero es
... #potencia de dos
... if is_power_of_two(numero):
... print es potencia de dos
...
sage: def radio(x,y):
... Devuelve la coordenada radial del punto con coordenadas cartesianas dadas
...
... Tiene dos argumentos
...
... return (x^2 + y^2)^.5
Una manera inmediata de acceder a las docstring de todas las funciones, incluso si no las hemos definido nosotras, es
escribir en un cuadro de cdigo el nombre de la funcin seguido de una interrogacin.
sage: informacion?
<html>...</html>
sage: radio?
<html>...</html>
sage: sin?
<html>...</html>
sage: is_prime?
<html>...</html>
sage: factorial?
<html>...</html>
Como regla general, siempre que, resolviendo un problema, podamos identificar una subtarea claramente delimitada,
debemos escribirla como funcin. Una tarea est bien delimitada cuando podemos describir a otra persona cules son
los datos de entrada (un nmero entero, dos nmeros reales, una cadena de caracteres y dos nmeros naturales ...),
cules son los datos de salida, y qu tiene que hacer la funcin, sin necesidad de explicarle cmo lo hemos hecho.
Veamos un ejemplo. Escribimos antes un cdigo que calculaba la suma de los inversos de los primos menores que k.
Identificamos una tarea bien delimitada: una funcin que recibe un nmero k y devuelve la suma de los inversos de
los primos que son menores que k. Escribimos ahora una funcin que realiza esta tarea usando el cdigo de la sesin
anterior: nos limitamos a copiar el cdigo que usamos antes, y lo colocamos dentro del cuerpo de una funcin.
sage: def sumaprimos(k):
... Suma los inversos de los primos menores que un numero dado
...
... suma = 0
... for j in srange(k):
... if is_prime(j):
... suma = suma + 1/j
... return suma
sage: print La suma de los inversos de los primos menores que 100 es %f %sumaprimos(100)
La suma de los inversos de los primos menores que 100 es 1.802817
Al conjunto formado por el nombre de la funcion, la descripcin de los datos de entrada y la descripcin de los datos
de salida, se le llama la signatura de la funcin. Al cdigo concreto que hemos escrito para realizar la tarea, se le
llama la implementacin . Distintas implementaciones de la misma tarea pueden conseguir el mismo resultado. Si no
se cambia la signatura, es posible cambiar la implementacin de la funcin sin necesidad de cambiar el cdigo que
usa la funcin.
Nos damos cuenta ahora de que hay una funcin, prime_range , que devuelve directamente una lista de nmeros
primos. Podemos utilizar esta funcin para mejorar la implementacin de sumaprimos .
sage: prime_range?
<html>...</html>
Aunque hemos cambiado la implementacin, la signatura es la misma, y podemos llamar a sumaprimos de la misma
forma que antes.
sage: print La suma de los primos menores que 100 es %f %sumaprimos(100)
La suma de los primos menores que 100 es 1.802817
El lenguaje python tiene una forma muy sofisticada de tratar con los errores en tiempo de ejecucin. Si algn fragmento
de cdigo produce un error, lanza una excepcin . Por ejemplo, si una funcin espera datos de cierto tipo y recibe
datos de otro tipo distinto, lanza un TypeError , o si realiza una divisin por cero lanza un ZeroDivisionError .
sage: range(a)
Traceback (most recent call last):
...
TypeError: range() integer end argument expected, got str.
sage: funcion(0)
Traceback (most recent call last):
...
ZeroDivisionError: Rational division by zero
Los errores se propagan desde la lnea que produjo el error a travs de la pila de ejecucin.
sage: def otra_funcion(a,b):
... return a + funcion(b)
sage: otra_funcion(1,0)
Traceback (most recent call last):
...
ZeroDivisionError: Rational division by zero
Es importante leer las descripciones de los errores por si nos dan una pista de cul puede ser el problema con una
funcin que no arroja un error.
Aunque no profundizaremos en el sistema de excepciones de python, si en algn momento sentimos la necesidad de
asegurarnos de que los datos de entrada de nuestras funciones verifican ciertas restricciones, utilizaremos la sintaxis
raise Exception .
Por ejemplo, si en la siguiente implementacin de la funcin factorial recibimos un nmero negativo corremos el
riesgo de entrar en un bucle infinito:
sage: def factorial(n):
... acumulador = 1
... k = n
... while k!=0:
... acumulador *= k
... k -= 1
... return acumulador
Para evitar esto, comprobamos que el nmero sea positivo y, de otro modo, lanzamos una excepcin genrica junto
con un mensaje de error:
sage: def factorial(n):
... if n <= 0:
... raise Exception, El argumento de factorial debe ser positivo
... acumulador = 1
... k = n
... while k!=0:
... acumulador *= k
... k -= 1
... return acumulador
sage: factorial(-3)
Traceback (most recent call last):
...
Exception: El argumento de factorial debe ser positivo
2.4 Ejercicios
2.4.1 1.
Guarda en variables los coeficientes de polinomio de segundo orden. Escribe un cdigo que muestra por pantalla
informacin sobre las races del polinomio (tiene dos races distintas, una doble, o ninguna).
2.4.2 2.
2.4.3 3.
Escribe un bucle while que sume los inversos de los nmeros naturales 1/k hasta que la suma sea mayor que 10.
Escribe el nmero k en el que la suma se detiene.
En este ejercicio, utilizamos un algoritmo que aproxima la raz cuadrada de un nmero real positivo a . El algoritmo
es iterativo: comienza con una aproximacin burda:
t0 = a
y despus en cada iteracin del algoritmo sustituimos t por otra aproximacin mejor:
1 a
t = t+
2 t
Este algoritmo para calcular races cuadradas era conocido por los babilonios, y a veces se le llama algoritmo de Hern
. La sucesin tn converge a la raiz de a.
2.4. Ejercicios 27
Laboratorio de Matemticas, Release 2010/2011
que acepte dos argumentos: un nmero real a y un error mximo , y devuelva una aproxi-
Escribe una funcin
macin b al nmero a con error menor que el error mximo :
|b2 a| <
2.4.5 5.
Busca un nmero natural k tal que |sin(k)| > 1 . No pruebes valores de demasiado pequeos: algo as como 0.01
es razonable. Para valores ms pequeos, k puede ser muy grande.
2.4.6 6.
Definimos una secuencia de nmeros por la siguiente regla: el nmero siguiente a n es n/2 si n es par, y 3n+1 si n es
impar.
Escribe una funcin que acepte como argumento un nmero k , y escriba por pantalla todos los nmeros de la
secuencia comenzando por k hasta y parando cuando se alcance por primera vez el nmero 1.
Escribe una funcin que acepte como argumento un nmero k , y devuelva el nmero de veces que se ha iterado
la regla anterior hasta llegar a 1 partiendo de k.
Aprovecha la funcin del apartado anterior para encontrar un nmero tal que la secuencia tarde al menos 50
pasos en alcanzar 1.
Nota: Se ha conjeturado, pero no se ha demostrado todava, que la secuencia siempre alcanza 1 eventualmente:
http://es.wikipedia.org/wiki/Conjetura_de_Collatz
http://xkcd.com/710/
Una funcin recursiva es aquella que contiene en el bloque de instrucciones que la definen una llamada a la propia
funcin. En matemticas tambin se usan definiciones recursivas y, tanto en un caso como en otro, hay que tener
cuidado para estar seguro de que la definicin es consistente:
1. La funcin debe poder calcularse directamente en uno o ms de los posibles casos de uso. Estos casos se llaman
casos base.
2. La llamada a la funcin con unos argumentos que no son casos base debe depender del valor de la funcin en
otros argumentos distintos, que deben estar ms cercanos a los casos base en algn sentido. Esta regla no es muy
precisa, pero no se puede dar una receta general, como veremos.
Como ejemplo, escribimos una implementacin recursiva de la funcin factorial, que se basa en la definicin recursiva:
0! = 1
n! = n (n 1)! si n > 0
Como vemos, la implementacin recursiva es una traslacin bastante literal de la definicin matemtica del factorial.
Recursin infinita
Al igual que un bucle while mal diseado puede repetirse infinitamente, una funcin recursiva mal diseada puede
repetirse indefinidamente.
sage: def fun_rec_inf(n):
... Funcion recursiva desastrosa
...
... return n+fun_rec_inf(n-1) #Nos hemos olvidado el caso base
sage: fun_rec_inf(10)
WARNING: Output truncated!
<html>...</html>
RuntimeError: maximum recursion depth exceeded
En realidad la ejecucin se detiene sola sin necesidad de que interrumpamos el clculo. En python , el lenguaje que
usa Sage, hay un lmite a la cantidad de veces que una funcin recursiva puede llamarse a s misma (en general est
fijado en 1000 llamadas recursivas).
En python, las funciones son ciudadanos de primera clase, lo que significa que podemos guardar funciones en variables
normales, guardarlas en listas, pasarlas como argumentos a otras funciones, etctera.
sage: funcion = factorial
sage: funcion(4)
24
Poder pasar funciones como argumentos a otras funciones permite abstraer algunos patrones usuales, y escribir cdigo
genrico que realiza las operaciones para funciones arbitrarias. De este modo la reutilizacin de cdigo aumenta
sensiblemente.
Como ejemplo, escribimos una funcin genrica que, partiendo de un valor inicial, itera una condicin hasta que una
cierta propiedad se verifique:
Usando esta funcin genrica, podemos calcular raices cuadradas con el algoritmo de Hern:
sage: a = 2.0
sage: epsilon = 1e-5
sage: def f(t):
... return (1/2)*( t + a/t)
...
sage: def p(t):
... return abs(t^2 - a) < epsilon
...
sage: print itera_hasta(f, p, 2)
1.41421568627451
El siguiente ejemplo es un clsico: tomamos una lista y la reducimos usando una funcin que toma dos elementos
y devuelve uno (como por ejemplo, la suma) . Para ello, aplicamos la funcion entre cada dos elementos de la lista. El
primer ejemplo con + no funciona, lo ponemos slo a modo de ejemplo.
lista : [ x1, x2, x3, x4, x5 ]
reduce(+, lista) : x1 + x2 + x3 + x4 + x5
reduce(f, lista) : f(f(f(f(x1, x2), x3), x4), x5)
Comentario
En este caso, el patrn es tan comn que python ya tiene su propia funcin reduce , similar a la que hemos definido
nosotras.
sage: reduce?
<html>...</html>
A continuacin aprenderemos otro estilo de programacin en el que visualizamos series de datos (por ejemplo, n-
meros) pasando por cajas que los transforman, los filtran o los acumulan de modo que podamos realizar el clculo
deseado partiendo de series de datos conocidas.
En python existe una sintaxis especial para generar nuevas listas a partir de otras, aplicando una transformacin a cada
elemento, y seleccionando aquellos elementos que verifican una propiedad.
La instruccin
[f(x) for x in lista_original]
genera una nueva lista, cuyos elementos son el resultado de ejecutar la funcin f sobre cada elemento de la
lista_original .
sage: lista_original = range(10)
sage: print lista_original
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Podemos transformar una lista con datos de un tipo cualquiera (enteros, booleanos, tuplas, cadenas de caracteres) en
una lista con datos de cualquier otro tipo.
sage: print lista_original
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sage: palabras=[En, un, lugar, de, la, Mancha, de, cuyo, nombre, no, quiero, a
sage: [palabra.upper() for palabra in palabras] #El metodo upper() pasa una cadena a mayusculas
[EN, UN, LUGAR, DE, LA, MANCHA, DE, CUYO, NOMBRE, NO, QUIERO, ACORDARME]
Con la instruccin
[x for x in lista if condicion(x)]
podemos seleccionar slo aquellos elementos de la lista que verifican una condicin. La condicin es cualquier expre-
sin o cualquier funcin que, para un valor x cualquiera, devuelva un valor booleano ( True o False ).
sage: [x for x in lista_original if is_prime(x)]
[2, 3, 5, 7]
Todo a la vez
Tambin podemos combinar ambas tcnicas, o incluso recorrer todas las combinaciones de elementos de dos listas
distintas:
[funcion(x) for x in lista if condicion(x)]
[funcion(x,y) for x in lista1 for y in lista2]
...
Las siguientes funciones encapsulan las tareas ms usuales que realizamos con listas:
len(lista) : devuelve la longitud de la lista
max(lista) : devuelve el mximo de la lista
min(lista) : devuelve el mnimo de la lista
sum(lista) : devuelve la suma de los elementos de la lista
all(lista) : si lista est compuesta de booleanos, devuelve True si todos son ciertos, y False en otro caso.
any(lista) : si lista est compuesta de booleanos, devuelve True si alguno es cierto, y False en otro caso.
sage: print len([2,4,1,3])
sage: print max([2,4,1,3])
sage: print min([2,4,1,3])
sage: print sum([2,4,1,3])
sage: print all([True, False, False])
sage: print any([True, False, False])
4
4
1
10
False
True
sage: k = 86
sage: divisores = k.divisors() #lista con los divisores de k
sage: print divisores
sage: print sum(divisores) #La suma de los divisores de k
sage: print max(divisores) #El mayor divisor de k
[1, 2, 43, 86]
132
86
Combinadas con las dos tcnicas de arriba, estas funciones permiten resolver problemas no triviales:
sage: k = 86
sage: divisores = k.divisors()
Ejercicio. Responde a las siguientes preguntas con las tcnicas usadas en esta sesin:
Cuntos divisores de k=21000 son cuadrados perfectos?
Hay algn nmero k entre 20 y 100 tal que 2^k +1 es primo?
Encuentra el tercer cuadrado perfecto n=k^2 tal que n+1 es primo.
Ejercicio : Escribe otra implementacin de la funcin sumaprimos de la sesin anterior usando estas ideas.
Quiz a estas alturas te hayas dado cuenta de que usar este enfoque indiscriminadamente puede llevar a realizar una
cantidad de clculos desproporcionada y a usar una gran cantidad de memoria, an cuando el problema no lo necesita.
Por ejemplo, para calcular el segundo primo entre 10000 y 100000 podemos hacer:
ls = srange(10000,100000)
primos = [t for t in ls if is_prime(t) ]
print primos[1]
pero esto nos obliga a guardar en memoria todos los nmeros naturales entre 10000 y 100000, y despus a calcular si
cada uno es primo, slo para quedarnos con el segundo.
sage: %time
sage: ls = srange(10000,100000)
sage: primos = [t for t in ls if is_prime(t) ]
sage: print primos[2]
10037
CPU time: 0.39 s, Wall time: 0.39 s
Sin embargo, es posible mantener esta sintaxis sin hacer clculos innecesarios. Para ello, usamos generadores , objetos
que generan los elementos de la lista uno por uno, segn se vayan solicitando desde otras partes del programa. A esta
tcnica se le denomina evaluacin perezosa , porque no se hacen clculos hasta que no son necesarios.
Por ejemplo, la funcin xsrange es la versin perezosa de srange (con la misma sintaxis). Mientras que
srange(100) inmediatamente calcula los enteros menores que 100 y los guarda en una lista, xsrange(100)
devuelve un generador, que no calcula los nmeros en ese momento:
sage: srange(100)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27
sage: xsrange(100)
<generator object generic_xsrange at 0x5dacf50>
Una vez creado el generador, podemos pedir los nmeros uno por uno, usando el mtodo next() , o recorrerlos
todos, usando un bucle for igual que si fuera una lista:
sage: xlista = xsrange(100)
sage: print xlista.next()
sage: print xlista.next()
0
1
sage: acumulador = 0
sage: for j in xsrange(101):
... acumulador += j
sage: print acumulador
5050
Podemos escribir nuestros propios generadores usando una notacin similar a la de transformaciones de listas, slo
que poniendo parntesis alrededor de nuestra expresin en vez de corchetes:
generador = (expresion for x in secuencia if condicion)
Ahora podemos modificar el programa original para calcular el segundo primo entre 10000 y 100000, usando una
cantidad de memoria y cpu equivalente a un programa tradicional:
sage: %time
sage: ls = xrange(10000,100000)
sage: primos = (t for t in ls if is_prime(t) )
sage: primos.next()
sage: print primos.next()
10009
CPU time: 0.01 s, Wall time: 0.01 s
sage: %time
sage: contador = 2
sage: for j in xsrange(10000,100000):
... if is_prime(j):
... contador -= 1
... if contador == 0:
... break
sage: print j
10009
CPU time: 0.00 s, Wall time: 0.00 s
con estas dos lneas, definimos dos generadores, pero no se realiza ningn clculo.
primos.next()
al llamar al mtodo next , ponemos a trabajar al generador primos , que debe rodar hasta devolver un nmero primo.
Para ello, prueba los nmeros de la lista ls uno por uno. Cuando encuentra uno que pasa el filtro is_prime(t) , lo
devuelve. En la primera llamada a next , primos devuelve el nmero 10007, el primer primo mayor que 10000.
print primos.next()
pedimos otro nmero ms a primos , que pide a su vez nmeros al generador ls continuando donde lo dej. Recorre
primero el nmero 10008 e inmediatemente despus encuentra 10009, que es primo. El programa concluye.
Por supuesto, es interesante combinar los generadores con el enfoque de flujos que tratamos hoy. Consideremos por
ejemplo la pregunta:
Hay algn nmero k entre 1000 y 10000 tal que 2*k +1 es primo?
Para comprobar la pregunta, podemos comprobar si el nmero 2*k+1 es primo para cada valor de k. Por supuesto, en
cuanto encontremos un valor de k para el que 2*k+1 es primo podemos parar, y responder a la pregunta con un s .
Un enfoque tradicional se aprovecha de esta propiedad:
hay_alguno = False
for k in srange(1000,10000):
if is_prime(2*k+1):
hay_alguno = True
break
print hay_alguno
mientras que si transformamos listas de forma naive, tendremos que comprobar si todos los nmeros son primos:
any([is_prime(2*k+1) for k in srange(1000,10000)])
Observa que, como el generador ya estaba entre parentsis, no necesitamos poner dobles parntesis.
sage: %time
sage: hay_alguno = False
sage: for k in srange(1000,10000):
... if is_prime(2*k+1):
... hay_alguno = True
... break
sage: print hay_alguno
True
CPU time: 0.01 s, Wall time: 0.00 s
sage: %time
sage: any([is_prime(2*k+1) for k in srange(1000,10000)])
True
CPU time: 0.05 s, Wall time: 0.05 s
sage: %time
sage: any(is_prime(2*k+1) for k in srange(1000,10000))
True
CPU time: 0.01 s, Wall time: 0.00 s
sage: %time
sage: any(is_prime(2*k+1) for k in xsrange(1000,10000))
True
CPU time: 0.00 s, Wall time: 0.00 s
Escribir generadores en una lnea a veces es poco prctico. Ms importante es que en los generadores de una lnea no
se guarda ninguna variable de estado entre cada par de iteraciones. Por ejemplo, podemos calcular los distintos bits de
un nmero entero usando la frmula para el bit j-simo: divide por 2j y toma el resto de dividir por 2:
sage: def bit_j_esimo(a, j):
... return (a//2^j) %2 # a//b calcula el cociente de la divisin entera
... #de dividir a entre b
sage: a = 19
sage: print list( bit_j_esimo(a,j) for j in range(0, int(log(a)/log(2)) + 1) )
sage: print sum( bit_j_esimo(a,j) for j in range(0, int(log(a)/log(2)) + 1) )
[1, 1, 0, 0, 1]
3
Sin embargo, es ms claro calcular los bits uno por uno haciendo una divisin por dos cada vez. La sintaxis para los
generadores es la misma que para funciones, slo que usando la instruccin
yield x
para devolver un valor. El objeto creado no es una funcin, sino un generador, que devuelve potencialmente varios
valores, segn los soliciten al generador. Cada vez que pedimos un elemento nuevo al generador, el flujo del programa
avanza dentro del cdigo de la funcin hasta que se encuentra con un yield, y entonces devuelve ese valor y se queda
congelado hasta que le vuelvan a pedir otro valor:
def bits(a):
while a:
yield a %2
a = a//2
sage: list(bits(19))
[1, 1, 0, 0, 1]
sage: g = bits(123)
sage: print g.next()
sage: print g.next()
sage: print g.next()
1
1
0
sage: g = bits(2)
sage: print g.next()
sage: print g.next()
sage: print g.next()
0
1
Traceback (most recent call last):
...
StopIteration
2.6 Ejercicios
2.6.1 1
Escribe una funcin recursiva que calcule la suma de los nmeros menores o iguales que un cierto nmero n (es decir,
1+2+...+n). Si llamamos sn a la suma de los naturales menores que n, usa la regla de induccin:
(
n + sn1 n 1
sn =
0 n=0
2.6.2 2.
Escribe una funcin recursiva que calcule la suma de las cifras decimales del nmero que se le pasa como argumento.
Pista: si llamamos s(n) a la suma de las cifras de n , usa la regla de induccin:
(
"ultima cifra" + s("todas las cifras menos la ltima") n 1
s(n) =
0 n=0
2.6.3 3.
x0 = 1
xn = x xn1 si n > 0
Escribe una funcin recursiva que calcule la potencia con un exponente entero y positivo.
2.6.4 4.
Usa la funcin reduce para escribir una funcin que multiplica los elementos de una lista.
2.6.5 5.
Escribe una funcin que acepta dos argumentos, ambos listas, y devuelve una tercera lista con los elementos
comunes a las dos listas que se pasaron como argumentos (es decir, la interseccin).
Usa la funcin reduce para escribir una funcin que acepta como nico argumento una lista cuyos elementos
son a su vez listas, y devuelve otra lista con la interseccin de todas ellas.
2.6.6 6.
Nota : Para este ejercicio y los que siguen, intenta usar las tcnicas de transformar y filtrar listas aprendidas en
la hoja del bloque I sesin III.
A partir de la lista de nmeros de abajo:
1. Consigue otra que contenga el resultado de evaluar la funcin x ex
2. Consigue otra que contenga el resto de dividir cada uno de ellos por 5
3. Consigue otra que contenga, en vez de cada nmero, una tupla con los restos de dividir el numero por 2, 3 y 5.
4. Consigue otra que contenga, en vez de cada nmero, una cadena de caracteres con tantos asteriscos como indica
el nmero.
5. Consigue otra que contenga slo aquellos que son mltiplos de 3
6. Consigue otra que contenga slo aquellos que son mltiplos de 3 de 5
sage: numeros = [1, 3, 7, 9, 11, 13, 18, 19, 21, 30, 31, 35]
2.6.7 7.
2.6.8 8.
Escribe cdigo que genere una lista cuyos elementos son cadenas de caracteres:
Dado un entero k, la lista contiene k cadenas, que dicen j elefantes se balanceaban, para cada j que va desde 1
hasta k. Por ejemplo, para k = 5:
[1 elefantes se balanceaban, 2 elefantes se balanceaban, 3 elefantes se balanceaban, 4 elefant
2.6.9 9.
Dado un entero k, la lista contiene k cadenas, que dicen j botellas contra la pared, para cada j que disminuye
desde k hasta 1. Por ejemplo, para k = 5:
[5 botellas contra la pared, 4 botellas contra la pared, 3 botellas contra la pared, 2 botella
2.6.10 10.
2.6. Ejercicios 39
Laboratorio de Matemticas, Release 2010/2011
Alguno es primo?
Para cada nmero k de la lista de abajo, k^2+2 es primo?
La suma de los nmeros de abajo, es un nmero primo?
Cul es el mayor nmero k de la lista de abajo tal que 2*k+1 es primo?
sage: numeros = [33, 39, 45, 57, 81, 99, 105, 111, 117]
2.6.11 11.
Ejercicio opcional (leer slo si has seguido la seccin opcional de la hoja b1s3).
Para generar los nmeros de Fibonacci, tendramos que escribir algo as como:
g = (fibonacci(k) for k in range(K))
Como el clculo del k-simo nmero de Fibonacci requiere ms clculos cuanto mayor es k (dependiendo de cmo
hagamos los clculos), esta opcin es poco eficiente.
Escribe un generador (de ms de una lnea) que devuelva los nmeros de Fibonacci de uno en uno, pero aprovechando
los clculos realizados para calcular el nmero anterior.
Se presentan dos estructuras de datos nuevas: conjuntos y diccionarios. Se resuelven algunos problemas de contenido
matemtico y se explica la complejidad de las operaciones ms usuales en python. Se presenta la filosofa consistente
en primero escribir cdigo claro y despus optimizar slo si es necesario y slo las partes crticas. Para ello se presentan
dos herramientas muy prcticas incorporadas en Sage: un profiler, para encontrar las partes del cdigo que consumen
ms tiempo de ejecucin, y cython, una herramienta que a menudo permite acelerar sensiblemente los clculos sin
perder ni tiempo ni claridad en el cdigo.
Como vimos, cualquier dato en SAGE tiene un tipo de datos . Los datos de ciertos tipos pueden ser modificados
despus de ser creados, mientras que otros no. En concreto, no es posible modificar los nmeros (enteros o de coma
flotante), los booleanos o las cadenas de caracteres, aunque podemos crear otros nmeros nuevos a partir de los
existentes. Las tuplas tambin son inmutables si los elementos que las componen lo son.
Las listas, sin embargo, son mutables, porque podemos aadir o borrar elementos, y modificar sus elementos.
La distincin es importante: ninguna instruccin, ninguna llamada a una funcin o un mtodo puede modificar un dato
inmutable, mientras que s pueden modificar un dato mutable (por ejemplo, pueden aadir o quitar elementos de una
lista, darle la vuelta u ordenar sus elementos).
sage: #Los elementos de una tupla no se pueden cambiar
sage: #y las tuplas no tienen metodos que permitan aadir
sage: #o quitar elementos
sage: tt = (1,2,3)
sage: tt[0] = 4
Traceback (most recent call last):
...
TypeError: tuple object does not support item assignment
sage: tt.append(4)
Traceback (most recent call last):
...
AttributeError: tuple object has no attribute append
41
Laboratorio de Matemticas, Release 2010/2011
3.1.2 Conjuntos
El conjunto ( set ) es una estructura de datos que permite almacenar datos sin repeticiones.
Las diferencias con una lista son:
Los elementos de un conjunto no se guardan ordenados y no se puede acceder a ellos con un ndice.
En un conjunto no hay elementos repetidos. Si aadimos un elemento que ya estaba en el conjunto, el conjunto
se queda como estaba.
Los elementos de un conjunto tienen que ser datos inmutables . Los tipos de datos inmutables tienen un nmero
hash que es la base del funcionamiento de los conjuntos.
Podemos crear un conjunto vaco usando el comando
conjunto = set()
o borrar elementos:
conjunto.remove(elemento)
Interseccin:
conjunto1 & conjunto2
Diferencia:
conjunto1 - conjunto2
sage: conjunto1.add(conjunto2)
Traceback (most recent call last):
...
TypeError: unhashable type: set
Los elementos del conjunto no estn ordenados, de modo que no tiene sentido extraer el elemento i-simo de la lista:
sage: conjunto1[2]
Traceback (most recent call last):
...
TypeError: set object does not support indexing
Sin embargo, podemos iterar los elementos del conjunto, aunque no podemos predecir el orden en que aparecern:
for elemento in conjunto:
operaciones...
Queremos calcular cuntos nmeros distintos obtenemos al hacer todas las posibles sumas a+b , donde a y b perte-
necen a una lista de nmeros. Es fcil almacenar las posibles sumas en una lista, pero entonces estaramos contando
algunos nmeros varias veces:
sumas = []
for k in lista:
for j in lista:
sumas.append(k+j)
Al usar un conjunto, cada posible suma queda en el conjunto final una sola vez .
sage: def sumas(ls):
... Devuelve la cantidad de sumas distintas que se pueden obtener
... sumando dos elementos de una lista
...
... sumas_posibles = set()
... for a in ls:
... for b in ls:
... sumas_posibles.add(a+b)
... return len(sumas_posibles)
De hecho, usar un conjunto es una forma sencilla de eliminar repeticiones en una lista de elementos:
3.1.3 Diccionarios
Un diccionario es una coleccin de pares clave-valor. Si una lista es una coleccin de objetos indexada por nmeros
enteros consecutivos, un diccionario permite como clave cualquier tipo de datos inmutable, y los valores pueden ser
totalmente arbitrarios.
Los diccionarios ( dict ) tienen una sintaxis especial en python usando las llaves {}.
diccionario = {clave1: valor1, clave2:valor2 ...}
Una vez definido el diccionario, podemos incluir nuevos elementos, borrar y modificar elementos existentes con una
sintaxis similar a la que usamos con listas:
diccionario[clave]=valor
del diccionario[otra_clave]
Si necesitamos slo las claves, o los valores podemos usar los mtodos:
diccionario.keys()
diccionario.values()
El operador in comprueba si un objeto es una clave del diccionario, pero no si es uno de los valores.
sage: print mano in diccionario
sage: print nariz in diccionario
sage: print 2 in diccionario
True
False
False
Para recorrer los elementos del diccionario, podemos usar un bucle for, que recorre las claves del diccionario, sin
seguir ningn orden en particular:
for clave in diccionario:
instrucciones...
A modo de ejemplo, calculamos el mnimo comn mltiplo de una lista de nmeros usando su factorizacin: el mnimo
comn mltiplo es el producto de todos los primos que aparecen en cualquiera de los nmeros de la lista con el mayor
de los exponentes.
sage: def mcm(ls):
... #Primera parte: recopilar los factores
... factores = {}
... for numero in ls:
... for par in list(factor(numero)):
... primo, exponente = par
... if primo not in factores:
... factores[primo] = exponente
... else:
... factores[primo] = max(factores[primo], exponente)
... #Segunda parte: multiplicarlos
... resultado = 1
... for primo in factores:
... resultado = resultado*primo^factores[primo]
... return resultado
Podemos construir un diccionario a partir de una lista de tuplas usando el constructor de diccionarios dict . Cada
tupla de la lista se incluir en el diccionario como un par (clave, valor).
sage: lista = [(a,1), (b,2), (c,3)]
sage: diccionario = dict(lista)
Si tenemos una lista con las claves y otra con los valores (ambas de igual longitud), podemos construir una lista de
tuplas que podemos pasar al constructor dict usando la funcin zip especialmente diseada para ese propsito:
sage: lista1 = [(2,3),(2,4),(2,5),(3,4),(3,5),(4,5)]
sage: lista2 = [6, 8, 10, 12, 15, 20]
sage: print lista1
sage: print lista2
sage: lista12 = zip(lista1, lista2)
Hash
Los objetos inmutables tienen un hash: un nmero entero que representa al objeto, de modo que dos objetos distintos
tengan distintos hash, al menos con probabilidad alta.
sage: print hash(0)
sage: print hash(1)
sage: print hash(2)
0
1
2
Por supuesto que puede haber dos datos distintos con el mismo hash, pero es muy improbable. Exceptuando a los
nmeros enteros, el hash tiene una apariencia bastante aleatoria.
sage: print hash(1107710717)
sage: print hash((0,2))
sage: #Aunque tengan el mismo hash, los datos siguen siendo distintos
sage: 1107710717 == (0,2)
1107710717
3713080549410493181
False
Tabla hash
http://es.wikipedia.org/wiki/Tabla_hash
Los conjuntos y los diccionarios se basan en una tabla de hash , una lista en la que guardamos los elementos usando
su hash (o ms bien parte del hash) como ndice, en vez de insertarlos por orden de llegada.
En python, el ndice usado para indexar un elemento en una tabla de tamao 2k son los k ltimos bits del hash del
elemento. Naturalmente, es posible que dos elementos distintos tengan nmeros hash cuyos k ltimos bits coincidan.
Cuando introducimos dos elementos en la tabla a los que corresponde el mismo ndice, decimos que ha ocurrido una
colisin . Sin entrar en detalles sobre cmo se gestionan las colisiones, digamos que cuantas menos colisiones ocurran
mejor . Un buen algoritmo de hash tiene dos propiedades bsicas: tarda poco tiempo, y produce pocas colisiones.
3.2 Ejercicios
3.2.1 1.
Dada una lista de nmeros enteros, construye un conjunto con los factores primos de todos los nmeros de la lista.
Indicacin: Usa list(k.factor()) para obtener los factores primos.
3.2.2 2. Histograma
Almacena en un diccionario los pares letra:frecuencia resultantes de hacer el anlisis de frecuencias de una cadena de
texto:
Crea una funcin que acepte como argumento una cadena de caracteres, y devuelva un diccionario cuyas claves
sean las palabras de la cadena, y los valores sean las frecuencias de aparicin de cada palabra.
Aprovecha el resultado de la funcin anterior para seleccionar las palabras que aparecen ms de 3 veces.
sage: cadena = Los pitnidos o pitones (Pythonidae) son una familia de serpientes constrictoras. O
La sucesin de Collatz, o del 3*n+1, que ya vimos, consiste en aplicar sucesivamente la siguiente regla:
Si un nmero es par, el siguiente nmero de la secuencia es n/2.
Si es impar, el siguiente nmero de la secuencia es 3n+1.
Se conjetura que una sucesin de Collatz siempre alcanza 1, independientemente del nmero en que comienza. El
codigo siguiente comprueba que las sucesiones que comienzan con cualquier nmero menor que M alcanzan 1 de una
forma poco eficiente:
sage: %time
sage: M = 500
sage: for k in range(2,M):
... j = k
... while j!=1:
... if j %2==0:
... j = j/2
... else:
... j = 3*j + 1
sage: #Si el calculo ha terminado, es que hemos verificado la conjetura
sage: print Verificada la conjetura para k<= %d %M
Con el mtodo anterior, calculamos la sucesin de Collatz completa partiendo de cada nmero, a pesar de que a
menudo la sucesin partiendo de un nmero j engancha con la sucesin partiendo de un nmero anterior. Por
ejemplo, la sucesin partiendo de 19 alcanza 11 despus de 6 iteraciones, y a partir de all obviamente coincide con la
sucesin partiendo de 11, que ya sabamos que acaba en 1. 11 : 34 17 52 26 13 40 20 10 5 16 8 4 2 1
19 : 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
Es fcil hacer una pequea mejora al programa: en vez de dar la comprobacin por buena cuando alcanzamos 1,
terminamos en cuanto alcanzamos un nmero menor que el nmero con el que comenzamos:
sage: %time
sage: M = 500
sage: for k in range(2,M):
... j = k
... while j>=k:
... if j %2==0:
... j = j/2
... else:
... j = 3*j + 1
3.2. Ejercicios 49
Laboratorio de Matemticas, Release 2010/2011
An podemos hacer una mejora ms. Observa las sucesiones que comienzan por 27 y 47:
27 : 82 41 124 62 31 94 47 142 71 214 107 322 161 484 242 121 364 182 91
274 137 412 206 103 310 155 466 233 700 350 175 526 263 790 395 1186 593
1780 890 445 1336 668 334 167 502 251 754 377 1132 566 283 850 425 1276
638 319 958 479 1438 719 2158 1079 3238 1619 4858 2429 7288 3644 1822
911 2734 1367 4102 2051 6154 3077 9232 4616 2308 1154 577 ...
47: 142 71 214 107 322 161
Observamos que el 47 ya apareci en la secuencia del 27, y por lo tanto ya no tenemos que hacer ms cuentas para
verificar el nmero 47. Sin embargo, el programa anterior perdi esta oportunidad de ahorrar tiempo.
Despus de tanto prembulo, tu objetivo es :
Calcular la sucesin de Collatz comenzando por 2,3,... hasta M.
Almacenar en un conjunto los valores de la sucesin que vas encontrando.
Si en algn momento de tu camino encuentras un valor conocido, abandona el clculo de la sucesin del nmero
actual y procede a calcular la sucesin que comienza por el nmero siguiente.
La conjetura de Goldbach afirma que todo nmero par se puede expresar como suma de dos nmeros pares. Confirma
la conjetura para todos los nmeros pares menores que una cota K siguiendo la estrategia siguiente:
Crea un conjunto con todos los nmeros pares menores que K
Crea un conjunto con todas las sumas de nmeros primos menores que K
Calcula la diferencia de los conjuntos para ver si todo par se puede expresar como suma de dos primos
Crea un diccionario cuyas claves sean los nmeros enteros entre 2 y 12, y su valor en el nmero k sea una lista
de tuplas con todas las posibles formas de sumar k usando dos nmeros enteros entre 1 y 6.
print dd[3]
[(1,2),(2,1)]
print dd[4]
[(1,3),(2,2),(3,1)]
Generaliza el resultado a dados con un rango mayor de valores, o a un nmero arbitrario de dados.
En esta seccin vamos a estudiar algunas formas de medir la eficiencia de nuestros programas en Sage, y a estudiar la
complejidad de las operaciones usuales en Sage.
Medir de forma precisa este tiempo no es una tarea trivial, y los resultados pueden variar sensiblemente de un ordenador
a otro. La cantidad de factores que pueden influir en el tiempo de ejecucin es muy larga:
algoritmo usado
sistema operativo
velocidad del procesador, nmero de procesadores y conjunto de instrucciones que entiende
cantidad de memoria RAM, y cach, y velocidad de cada una
coprocesador matemtico, GPU
...
Incluso en la misma mquina, el mismo algoritmo tarda algunas veces mucho ms tiempo en dar el resultado que otras,
debido a factores como el tiempo que consumen las otras aplicaciones que se estn ejecutando, o si hay suficiente
memoria RAM en el momento de ejecutar el programa.
Nuestro objetivo es comparar slo los algoritmos , intentando sacar conclusiones independientes de la mquina.
Un mismo algoritmo se puede llamar con distintos datos de entrada. Nuestro objetivo es estudiar el tiempo de ejecucin
como funcin del tamao de los datos de entrada . Para ello usamos dos tcnicas:
Medir tiempos de ejecucin de los programas con datos de entrada de distintos tamaos
Contar el nmero de operaciones que realiza el programa
Comenzamos por medir el tiempo de ejecucin de algunos algoritmos de forma emprica. Probando dos algoritmos
que calculan el mismo objeto con datos de distinto tamao nos haremos una idea de qu algoritmo es mejor para datos
grandes.
Para evitar que el resultado tenga en cuenta el efecto de los otros programas que se estn ejecutando en nuestro
ordenador en ese mismo momento, SAGE utiliza los conceptos de CPU time y Wall time , que son los tiempos que el
ordenador dedica exclusivamente a nuestro programa.
El CPU time es el tiempo de CPU que se ha dedicado a nuestro clculo, y el Wall time el tiempo de reloj entre el
comienzo y el final del clculo. Ambas mediciones son susceptibles a variaciones imprevisibles.
La forma ms sencilla de obtener los tiempos de ejecucin de un comando es anteponer la palabra time al comando.
sage: time is_prime(factorial(500)+1)
False
Time: CPU 0.09 s, Wall: 0.09 s
sage: #Para datos de mayor tamanyo, tarda mas tiempo (en general)
sage: time is_prime(factorial(1000)+1)
False
Time: CPU 0.72 s, Wall: 0.76 s
Para hacernos una idea del tiempo que tarda en terminar un programa en funcin del tamao de los datos de entrada,
vamos a hacer grficas: en el eje x , el tamao de los datos de entrada; en el eje y, el tiempo total.
El comando time no es lo bastante flexible, y necesitaremos las funciones cputime y walltime . cputime es
una suerte de taxmetro : es un contador que avanza segn hacemos clculos, y avanza tantos segundos como la CPU
dedica a Sage. walltime es un reloj convencional, pero medido en segundos desde el 1 de enero de 1970 (es el unix
clock). Para obtener el tiempo dedicado a nuestro programa, tomamos los tiempos antes y despus de la ejecucin, y
calculamos la diferencia.
sage: #cputime solo avanza cuando la cpu corre
sage: #(es un taximetro de la cpu)
sage: #Ejecuta esta funcion varias veces para ver como aumenta el tiempo
sage: #Si quieres, ejecuta comandos entre medias
sage: cputime()
2.0600000000000001
El siguiente cdigo guarda en una lista los cpu times empleados en ejecutar la funcin factorial con datos de distinto
tamao.
sage: numeros = [2^j for j in range(8,20)]
sage: tiempos = []
sage: for numero in numeros:
... tcpu0 = cputime()
... 11 = factorial(numero)
... tiempos.append(cputime()-tcpu0)
Una alternativa a medir el tiempo que tarda un programa que implementa un algoritmo es contar directamente el
nmero de operaciones que hace ese algoritmo cuando lo ejecutamos. Como este es un tema que estis estudiando a
fondo en otra asignatura, no entraremos en detalle, pero repasamos las nociones bsicas para fijar la terminologa:
Definicin. Dada una funcin g , diremos que otra funcin f es O(g) (y escribiremos f O(g) o incluso f = O(g)!),
si 0 < f (n) < c g(n) n n0 , para constantes positivas c, n0 .
Tambin diremos que f est dominada por g si f O(g). Por ejemplo,
n2 O(n3 )
p O(exp)
O, y
As como f (n) O(n) sirve para expresar una cota superior a la funcin f, y sirven para expresar una cota
inferior, y una cota ajustada, respectivamente:
Complejidad
Definicin . Decimos que un algoritmo tiene complejidad coste en O(f) (resp (f ), (f )) si su nmero de opera-
ciones (como funcin del tamao de los datos de entrada) es una funcin que pertenece a O(f) (resp (f ), (f )).
Nota: Un algoritmo termina en una cantidad fija de operaciones, independientemente de los datos de entrada, si y slo
si tiene complejidad en (1).
Aunque la notacin de complejidad es conveniente, no deja de ser una simplificacin: el nmero de operaciones no
depende slo del tamao de los datos de entrada.
El coste en el peor caso es el mximo nmero de operaciones para un dato de entrada que tenga tamao n.
El coste promedio es el promedio del nmero de operaciones sobre todos los posibles datos de entrada de tamao n.
Las listas nos permiten almacenar una cantidad arbitraria de valores de cualquier tipo. A lo largo del programa,
podemos aadir elementos a la lista, eliminarlos y acceder a cualquiera de los elementos.
Internamente, una lista es un espacio de direcciones de memoria consecutivas que contienen referencias a los objetos
almacenados en la lista.
Es muy importante que las direcciones sean consecutivas. De esta forma, podemos acceder al elemento j-simo en
poco tiempo: si la direccin de memoria del primer elemento de la lista es d , la direccin del elemento j-simo es d+j
.
Sin embargo, mantener las direcciones consecutivas tiene un precio. Si queremos aadir otro elemento al final (usando
append ), es necesario que la direccin de memoria al final de la lista est desocupada. Si no lo est, tenemos que
desplazar la lista a un nuevo emplazamiento en la memoria donde haya sitio para todos los elementos. Las listas de
python reservan parte del espacio detrs de la lista, de modo que no haya que recolocar la lista demasiadas veces.
Aadir un elemento en cualquier posicin distinta de la ltima obliga a desplazar los elementos posteriores de la lista
para hacer hueco al nuevo elemento.
Eliminar un elemento tambin puede ser una operacin costosa, porque despus de quitar un elemento, tenemos que
desplazar el resto de los elementos a la izquierda hasta tapar el hueco que deja el elemento que hemos sacado. Observa
que al eliminar un elemento cercano al final de la lista slo es necesario recolocar una pequea parte de la lista.
Comparamos los tiempos necesarios para eliminar todos los elementos de una lista, primero eliminando cada vez el
ltimo elemento, y despus eliminando el primer elemento de la lista.
sage: %time
sage: lista=range(20000)
sage: while len(lista)>0:
... del lista[-1]
CPU time: 0.03 s, Wall time: 0.03 s
sage: %time
sage: lista=range(20000)
sage: while len(lista)>0:
... del lista[0]
CPU time: 0.17 s, Wall time: 0.18 s
Ejercicio : Compara el tiempo necesario para aadir 20000 elementos al principio y al final de una lista. Para ello
tienes que encontrar un mtodo que te permita insertar un elemento al principio de la lista, usando la ayuda.
Aadir un elemento
Al aadir un elemento al final de la lista, pueden ocurrir dos cosas: que el espacio al final de la lista est disponible, o
que no lo est. Si est disponible, slo necesitamos O(1) operaciones. Si no lo est, tenemos que copiar la lista entera
a un lugar nuevo, lo que cuesta O(n). Por tanto, la complejidad de aadir un elemento al final de una lista en el pero
caso posible, es O(n).
Para evitar caer en el segundo caso con demasiada frecuencia, el intrprete de python reserva espacio extra despus de
cada lista. As, cuando nos vemos obligados a recolocar una lista de tamao n, le buscamos un espacio de tamao 2*n,
y colocamos los elementos de la lista al principio.
Sumemos el coste de aadir n elementos uno por uno a una lista:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
1+1 1 1+2 1 1+4 1 1 1 1+8 1 1 1 1 1 1 1 1+1
1+1 2+1 3+3 4+3 5+7 6+7 7+7 8+7 9+15 10+15 11+15 12+15 13+15 14+15 15+15 16+15 17+
Para aadir n elementos uno por uno, necesitamos hacer O(n) operaciones para aadir los elementos, y despus tene-
mos el coste de desplazar la lista a la nueva posicin. Este coste es igual a la longitud de la lista en el momento en que
se tiene que desplazar. La lista se desplaza cuando aadimos un elemento a una lista de longitud 2k , luego es igual a:
blog2 (n)c
X
2k = 2blog2 (n)c+1 1 2n
k=1
Decimos que aadir un elemento a una lista tiene coste amortizado O(1) , porque aadir n elementos siempre tiene
complejidad en O(n) .
Insertar un elemento en la posicin k-sima de una lista de n elementos obliga a desplazar los n-k ltimos elementos
para abrir hueco. Por ejemplo, crear una lista de n elementos insertando los elementos al principio tiene coste: 1 + 2 +
3 + + n = n(n 1)/2 = (n2 )
Quitar elementos
Quitar un elemento del final de la lista tiene coste O(1) (en el peor caso, no es necesario hablar de coste amortizado).
Quitar el elemento en la posicin k-sima de una lista de n elementos tiene coste (n k).
Acceder o actualizar elementos en posiciones arbitrarias no requiere recorrer la lista ni desplazar porciones de la lista,
y llevan tiempo O(1) .
Si una tabla est bastante llena de elementos, es necesario ampliarla. Al igual que en el caso de las listas, es tarea
del intrprete de python decidir el mejor momento para ampliar la tabla, y no necesitamos preocuparnos demasiado
porque ocurre con relativamente poca frecuencia.
Las bsquedas por el hash son relativamente rpidas, aunque no lo es tanto como el acceso directo al elemento i-simo
de una lista. Sin embargo, es mucho ms rpido aadir y quitar elementos de un conjunto o diccionario que de una
lista y es mucho ms rpido comprobar si un objeto ya est en la tabla.
En resumen, a pesar de su uso similar, las listas son muy distintas de los diccionarios, y el coste de las operaciones
elementales es distinto:
Acceder a un elemento de un diccionario (dic[clave]) es ms lento que a un elemento de una lista (lista[indice]).
Sin embargo, ambos son similares. A efectos prcticos, podemos asumir que el nmero de operaciones es inde-
pendiente del tamao de los datos de entrada en ambos casos.
Insertar o quitar un elemento del final de una lista es ms rpido que hacerlo de un diccionario. Sin embargo,
ambos son similares. A efectos prcticos, podemos asumir que el nmero de operaciones es independiente del
tamao de los datos de entrada en ambos casos.
Sin embargo, insertar o quitar un elemento cualquiera de una lista es mucho ms lento que hacerlo de un dic-
cionario. Insertar un elemento al principio de una lista obliga a recolocar la lista, lo que requiere un tiempo
proporcional al tamao de la lista (en otras palabras, O(n) ).
Comprobar si un valor est en una lista requiere recorrer toda la lista y es mucho ms lento que hacer la com-
probacin en un conjunto o diccionario. Como tenemos que comprobar si cada elemento de la lista es igual al
valor, el nmero de operaciones requiere un tiempo proporcional al tamao de la lista (en otras palabras, O(n) ).
sage: lista = [k^2 for k in range(10000)]
sage: conjunto=set(lista)
sage: %time
sage: for j in range(10000):
... b = (j in lista)
CPU time: 1.70 s, Wall time: 1.71 s
sage: %time
sage: for j in range(10000):
... b = (j in conjunto)
CPU time: 0.00 s, Wall time: 0.00 s
Ejercicio : comprueba empricamente las dems afirmaciones de arriba sobre la eficiencia de diccionarios y listas.
A modo de ejemplo, comparamos dos formas de construir una lista que tenga slo los elementos comunes a dos listas
dadas. La primera de ellos usa listas, la segunda sigue el mismo enfoque, pero con conjuntos, y la tercera usa la
interseccin de conjuntos (el operador & ).
sage: def interseca1(l1,l2):
... return [elemento for elemento in l1 if elemento in l2]
La segunda implementacin tiene que crear un conjunto con los elementos de la segunda lista, pero despus la opera-
cin de comprobacin de pertenencia (in) es ms eficiente.
La tercera implementacin tiene que crear conjuntos con los elementos de la cada lista, y convertir el resultado final a
un conjunto, pero a cambio podemos hacer directamente la interseccin de conjuntos, que es ms eficiente.
sage: numero = 1000
sage: #Tomamos los numeros aleatorios entre 1 y 10*numero para
sage: #que los numeros esten lo bastante espaciados, y no sea
sage: #probable que un elemento cualquiera de l1 este en l2
sage: l1 = [randint(1,10*numero) for k in range(numero)]
sage: l2 = [randint(1,10*numero) for k in range(numero)]
sage: time li = interseca1(l1, l2)
sage: time li = interseca2(l1, l2)
sage: time li = interseca3(l1, l2)
Time: CPU 0.02 s, Wall: 0.02 s
Time: CPU 0.00 s, Wall: 0.00 s
Time: CPU 0.00 s, Wall: 0.00 s
...
... #creamos el conjunto de numeros triangulares
... k=1
... triangulares = set()
... while k*(k+1)/2<cota:
... triangulares.add(k*(k+1)/2)
... k = k+1
... #creamos el conjunto de numeros pentagonales
... k=1
... pentagonales = set()
... while k*(3*k-1)/2<cota:
... pentagonales.add(k*(3*k-1)/2)
... k = k+1
... #creamos el conjunto de numeros hexagonales
... k=1
... hexagonales = set()
... while k*(2*k-1)<cota:
... hexagonales.add(k*(2*k-1))
... k = k+1
...
... return triangulares & pentagonales & hexagonales
sage: %time
sage: print busca_tph(1e6)
set([1, 40755])
CPU time: 0.02 s, Wall time: 0.02 s
sage: %time
sage: print busca_tph(1e8)
set([1, 40755])
CPU time: 0.13 s, Wall time: 0.13 s
Aumentando la cota, el tiempo de ejecucin aumenta, lgicamente. En vez de hacer grficas de tiempo de ejecucin,
vamos a reflexionar sobre el tiempo que debera tardar y la cantidad de memoria que necesita este enfoque para saber
hasta dnde podemos buscar.
Tanto los nmeros triangulares como los pentagonales y los hexagonales crecen de forma cuadrtica . Por lo tanto, si
fijamos una cota N, el tamao de cada uno de los conjuntos triangulares , pentagonales y hexagonales
ser menor que una constante por la raz cuadrada de N . Para construirlos, habremos dedicado a lo sumo O( N ).
Una vez construidos los conjuntos, la interseccin de dos conjuntos de tamao K1 y K2 se puede realizar en tiempo
O(min(K1, K2)), porque basta con comprobar si cada elemento del conjunto menor est en el otro conjunto.
De modo que el tiempo de ejecucin debera crecer como la raz de la cota, y el uso de la memoria tambin, por
motivos similares. Podemos permitirnos poner una cota de 1e10, y el tiempo de ejecucin slo debera aumentar unas
10 veces frente al tiempo que tard la llamada con cota = 1e8.
sage: %time
sage: print busca_tph(1e10)
set([1, 40755, 1533776805])
CPU time: 1.49 s, Wall time: 1.49 s
3.4 Ejercicios
3.4.1 1.
El resultado de las mediciones de %time, cputime y los dems es algo inestable. Al medir tiempos, es buena idea tomar
promedios. Escribe cdigo que almacene en una lista el tiempo promedio de ejecutar una cierta funcin N veces.
3.4.2 2.
Haz graficas que midan el tiempo de ejecucin en funcin de su nico argumento de la funcin es_primo definida
ms abajo. Asegrate de probar la funcin con todos los nmeros k en un rango [2,N]: puedes explicar la forma de
la grfica?
sage: def es_primo(k):
... return k>1 and all(k %j!=0 for j in range(2,k))
3.4.3 3.
Escribe cdigo que crea una lista aadiendo elementos uno por uno, primero aadiendo los elementos al final de la
lista y despus aadindolos al final de la lista. Cual debera ser la complejidad con cada enfoque?
Haz grficas del tiempo de ejecucin en funcin del tamao de la lista que construyes. Es consistente con la comple-
jidad predicha?
3.4.4 4.
Escribe cdigo que, partiendo de una lista de tamao N, elimina sus elementos uno por uno hasta que la lista quede
vaca. Resuelve el problema de dos formas: primero aadiendo los elementos al final de la lista y despus aadindolos
al final de la lista. Cual debera ser la complejidad con cada enfoque?
Haz grficas del tiempo de ejecucin en funcin del tamao de la lista. Es consistente con la complejidad predicha?
3.4.5 5.
Compara dos formas de calcular las formas de expresar un par como suma de dos primos. La nica diferencia est en
el uso de una lista o de un conjunto para almacenar los nmeros primos. Realiza grficas de tiempo de ejecucin en
funcin de n: es sensato pensar que el tiempo necesario para comprobar si un elemento pertenece a un conjunto no
depende del tamao del conjunto?
sage: def sumas1(n):
... if n %2!=0:
... #Si n es impar, no devolvemos nada
... return
... else:
... lista = []
... primos = prime_range(n)
... for k in primos:
... if n-k in primos:
... lista.append((k,n-k))
... return lista
3.4. Ejercicios 59
Laboratorio de Matemticas, Release 2010/2011
3.4.6 6.
Encuentra todos los nmeros primos menores que un milln que son de la forma k 2 + 1, con k un nmero triangular.
3.4.7 7.
Encuentra el menor nmero primo con k dgitos decimales distintos, para k=5,6,7. Resuelve el problema de las dos
formas siguientes:
Crea una lista de primos candidatos con prime_range, y busca entre ellos el primero con la propiedad pedida.
Este enfoque obliga a fijar una cota mxima y buscar primos menores que esa cota.
Genera los nmeros primos de uno en uno usando next_prime. En este enfoque no hay que fijar una cota a priori.
3.4.8 8.
Observa que ningn primo menor que 10000 puede tener menos de 5 cifras. Podemos por tanto comenzar la bsqueda
por este nmero. Apurando un poco ms, podemos comenzar a buscar primos por el menor nmero con cinco cifras
distintas, el 10234. Incorpora esta informacin y mide la mejora en tiempo de ejecucin.
En clculo cintifico, no es raro que ejecutemos un cdigo una sla vez. Obtenido el resultado, guardamos el cdigo
en el archivo y la salida del programa nos responde a una pregunta y nos ayuda a plantear la siguiente. Claramente, no
tiene sentido dedicar tiempo a optimizar al mximo un cdigo que probablemente nadie vuelva a usar.
Tenemos que encontrar un equilibrio entre el tiempo que dedicamos a optimizar el programa y el tiempo que podemos
esperar a que el cdigo sin optimizar termine. Claramente, nuestro tiempo vale ms que el tiempo del ordenador.
La optimizacin, frecuentemente, tambin va reida con otro aspecto muy importante del cdigo cientfico, la claridad
, necesaria para poder compartir nuestro cdigo con la comunidad internacional. Optimizaciones marginales que hacen
el cdigo ms difcil de seguir (y por tanto, ms propenso a esconder errores), no compensan.
PD: Lo anterior se aplica al cdigo exploratorio , que es el ms cotidiano en ciencia: un anlisis estadstico, una
conversin de formato, la solucin de una ecuacin concreta... pero no al cdigo que forma las libreras en las que se
basa el trabajo de mucha gente, porque ese cdigo se va a ejecutar muchas veces en muchos contextos distintos.
Hoy vamos a practicar una metodologa tpica de lenguajes dinmicos como python:
1. Escribe cdigo correcto, y claro.
2. Prubalo en suficientes casos.
3. Comprueba si es lo bastante rpido para el tamao de tus datos. Si lo es, dedcate a otra cosa.
4. Si el cdigo no es lo bastante rpido, identifica las partes del programa que ms influyen en la velocidad (una
conocida heurstica dice que el 90 % del tiempo se pasa ejecutando el 10 % del cdigo).
5. Piensa si tu algoritmo es mejorable y, si puedes, usa otro mejor.
6. Si tu cdigo no es lo bastante rpido y no conoces mejores algoritmos, reescribe las partes crticas del cdigo en
un lenguaje compilado como C, FORTRAN, o cython .
sage: %time
sage: print brun(1e4)
1.61689355743220
CPU time: 1.40 s, Wall time: 1.41 s
La serie converge muy despacio, y si queremos verificar el primer dgito decimal, tenemos que alcanzar al menos
K = 108 , lo que claramente nos obliga a optimizar el cdigo.
Aunque con este cdigo tan breve puede ser obvio, vamos a usar una herramienta de profile como ayuda a la meto-
dologa de la optimizacin progresiva. La herramienta nos ayuda a identificar las partes del programa ms lentas con
menos trabajo que si slo usamos timeit o similares.
sage: #importamos los modulos cProfile y pstats para ver las estadisticas
sage: #de cuanto tiempo se pasa en cada parte del codigo
sage: import cProfile, pstats
sage: #No necesitamos entender la siguiente linea:
sage: #tomalo como una version avanzada de timeit
sage: cProfile.runctx("brun(10000)", globals(), locals(), DATA + "Profile.prof")
sage: s = pstats.Stats(DATA + "Profile.prof")
sage: #Imprimimos las estadisticas, ordenadas por el tiempo total
sage: s.strip_dirs().sort_stats("time").print_stats()
Tue Feb 22 11:18:44 2011 /home/sageadm/nbfiles.sagenb/home/pang/214/data/Profile.prof
Vemos que la llamada a criba_gemelos es la que ocupa la mayor parte del tiempo. El algoritmo es mejorable: es
un algoritmo cuadrtico (para cada p de la lista recorremos la lista entera para buscar p+2), cuando para esta tarea
podemos usar un algoritmo lineal. Observamos que el primo p+2, si est en la lista, slo puede estar en un sitio:
inmediatamente a continuacin del primo p.
sage: #2: Selecciona los gemelos
sage: #Nos quedamos con el menor de cada par
sage: def criba_gemelos(ls):
... return [ls[j] for j in xrange(len(ls)-1) if ls[j+1]==ls[j]+2]
sage: %time
sage: print brun(1e4)
1.61689355743220
CPU time: 0.12 s, Wall time: 0.11 s
Como desgraciadamente el resultado sigue siendo insuficiente, volvemos a aplicar el profile para buscar el siguiente
fragmento de cdigo que necesita mejoras...
sage: import cProfile, pstats
sage: cProfile.runctx("brun(50000)", globals(), locals(), DATA + "Profile.prof")
sage: s = pstats.Stats(DATA + "Profile.prof")
sage: s.strip_dirs().sort_stats("time").print_stats()
Tue Feb 22 11:18:53 2011 /home/sageadm/nbfiles.sagenb/home/pang/214/data/Profile.prof
Vemos que ahora slo compensa dedicarle tiempo a la criba. Comenzamos por estudiar el algoritmo. Algunos de
vosotros presentasteis esta otra variante en la prctica, en la que para hallar los primos menores que N mantenemos
un array que comienza con todos los nmeros de 1 a N, y cuando descubrimos un nmero compuesto, lo tachamos
poniendo un cero en la posicin del array que ocupa.
sage: ##Variante
sage: def lista_primos2(n):
... aux = [True]*int(n)
... aux[0] = False
... aux[1] = False
...
... for i in xrange(2,floor(sqrt(n))+1):
... if aux[i]:
... for j in xrange(i*i,n,i):
... aux[j] = False
...
... ##Devolvemos los que no estn tachados
... return [k for k in xrange(n) if aux[k]]
Aunque podra parecer que ambos algoritmos hacen las mismas operaciones, observamos que:
Con el primer mtodo, intentamos dividir un nmero por todos los nmeros primos que son menores que su
menor factor primo.
Con el segundo mtodo, cada nmero se tacha varias veces, una por cada factor primo del nmero.
No necesitamos conocer ms detalles, por ahora es suficiente con entender que hacen dos cosas distintas. El segundo
mtodo resulta ser mucho ms eficiente: (detalles en la wikipedia )
sage: time a=lista_primos(50000)
sage: time b=lista_primos2(50000)
sage: a==b
Time: CPU 1.54 s, Wall: 1.54 s
Time: CPU 0.01 s, Wall: 0.01 s
True
sage: %time
sage: print brun(1e5)
1.67279958482774
CPU time: 0.05 s, Wall time: 0.05 s
sage: %time
sage: print brun(1e6)
1.71077693080422
CPU time: 0.39 s, Wall time: 0.39 s
El crecimiento aparenta ser casi lineal, y estimamos que podemos tener nuestra respuesta en un tiempo asumible, y
pasamos al siguiente problema.
3.5.2 Cython
Si estamos usando el mejor algoritmo, o no conocemos otro mejor, y aun as nuestro programa no es lo bastante rpido,
podemos compilar las partes crticas del programa.
El lenguaje cython se adapta perfectamente a esta tarea, ya que combina tipos de datos de C con sintaxis de python,
de modo que podemos alcanzar mayor velocidad sin tener que reescribir nuestros programas.
Comenzamos con un tpico ejemplo numrico, en el que calculamos una integral mediante una suma de Riemann.
sage: def f(x):
... return sin(x**2)
sage: def integral(a, b, N):
... dx = (b-a)/N
... s = 0
... for i in range(N):
... s += f(a+dx*i)
... return s * dx
Para compilar una funcin en cython, comenzamos el bloque de cdigo con %cython .
sage: %cython
sage: #Tenemos que importar la funcion seno
sage: from math import sin
sage: def f(x):
... return sin(x**2)
sage: def integral_cy1(a, b, N):
... dx = (b-a)/N
... s = 0
... for i in range(N):
... s += f(a+dx*i)
... return s * dx
Al ejecutar un bloque de cdigo que comienza por %cython , Sage compila el cdigo , y lo deja listo para llamarlo
ms adelante como una funcin normal definida en python.
sage: time integral_cy1(0.0, 1.0, 500000)
0.310267460252752
Time: CPU 2.79 s, Wall: 2.83 s
Como vemos, el cdigo apenas es un poco ms rpido que antes: no hemos indicado los tipos de los datos, y en esta
situacin es imposible hacerlo significativamente mejor que el intrprete de python.
En la siguiente versin le indicamos los tipos de los datos usando la palabra clave cdef : int o float .
sage: %cython
sage: from math import sin
sage: def f(double x):
... return sin(x**2)
...
sage: def integral_cy2(double a, double b, int N):
... cdef double dx = (b-a)/N
... cdef int i
... cdef double s = 0
... for i in range(N):
... s += f(a+dx*i)
... return s * dx
La estrategia anterior (compilar en cython indicando los tipos de los datos) se suele poder aplicar de forma bastante
mecnica y da resultados interesantes. En general, es posible optimizar el cdigo todava ms, pero hace falta cono-
cimiento ms especfico y por tanto ms difcil de aplicar en cada caso concreto. A modo de referencia, veamos el
siguiente cdigo:
sage: %cython
sage: #Usamos la funcion seno directamente de la libreria
sage: #"math.h" de C
sage: cdef extern from "math.h": # external library
... double sin(double)
sage: #Las funciones definidas con cdef solo son accesibles
sage: #desde codigo cython, pero son mas rapidas
sage: cdef double f(double x):
El conjunto de Mandelbrot es el subconjunto de los nmeros complejos formados por aquellos nmeros c tales que las
iteraciones de la regla z z 2 + c comenzando en z0 = 0 permanecen acotadas.
Como aproximacin, es habitual tomar un punto c del plano complejo e iterar la regla anterior hasta que se abandona
una bola de un cierto radio, o se alcanza un cierto nmero de iteraciones.
En la posicin (j,k) de un array NxN almacenamos el nmero de iteraciones h necesarias para que zh abandone
una bola de radio R cuando comenzamos a iterar z z 2 +c partiendo del punto c = (x0 +j l/N )+i(y0 +kl/N )
.
sage: def mandelbrot(x0, y0, side, N=200, L=50, R=float(3)):
... m=matrix(N,N)
... delta = side/N
... for j in range(N):
... for k in range(N):
... c = complex(x0+j*delta,y0+k*delta)
... z=0
... h=0
... while (h<L) and (abs(z)<R):
... z=z*z+c
... h+=1
... m[j,k]=h
... return m
Nuestro cdigo es una traslacin bastante literal de la definicin, y sin saber ninguna propiedad especial del conjunto
no parece haber forma de sustituir el algoritmo. Usamos el lenguaje cython.
sage: %cython
sage: def mandelbrot_cy_1(x0, y0, side, N=200, L=50, R=float(3)):
... m=matrix(N,N)
... delta = side/N
... for j in range(N):
... for k in range(N):
... c = complex(x0+j*delta,y0+k*delta)
... z=0
... h=0
... while (h<L) and (abs(z)<R):
... z=z*z+c
... h+=1
... m[j,k]=h
... return m
include "cdefs.pxi"
def mandelbrot_cy_1(x0, y0, side, N=200, L=50, R=float(3)):
m=matrix(N,N)
^
------------------------------------------------------------
/home/sageadm/.sage/temp/sageserver/23963/spyx/_home_sageadm_nbfiles_sagenb_home_pang_214_code_sage35
Traceback (most recent call last):
...
RuntimeError: Error converting /home/sageadm/nbfiles.sagenb/home/pang/214/code/sage35.spyx to C:
que nos indica que tenemos que importar el constructor de matrices ( matrix ) de la librera correspondiente. En-
contramos la ruta requerida para importar en la ayuda de matrix, ms concretamente en la primera lnea, que indica el
fichero en que se define matrix .
sage: matrix?
<html>...</html>
sage: %cython
sage: from sage.matrix.constructor import matrix
sage: def mandelbrot_cy_1(x0, y0, side, N=200, L=50, R=float(3)):
... m=matrix(N,N)
... delta = side/N
... for j in range(N):
... for k in range(N):
... c = complex(x0+j*delta,y0+k*delta)
... z=0
... h=0
... while (h<L) and (abs(z)<R):
... z=z*z+c
... h+=1
... m[j,k]=h
... return m
Al ejecutar un bloque de cdigo que comienza por %cython , Sage compila el cdigo , y lo deja listo para llamarlo
ms adelante como una funcin normal definida en python. Tambin genera un informe en html que nos permite
entender cmo de eficiente es el cdigo generado. Las lneas en amarillo son cdigo que no se ha podido optimizar,
y se ejecuta como si fuera cdigo dinmico en python, mientras que las lneas en blanco corresponden a las lneas
que se han podido optimizar, y se ejecutan como si fuera cdigo de C. En este primer informe, vemos que casi todas
las lneas estn en amarillo, porque no hemos indicado los tipos de los datos, y en esta situacin es imposible hacerlo
significativamente mejor que el intrprete de python.
En la siguiente versin le indicamos los tipos de los datos: int , float o double complex .
sage: %cython
sage: from sage.matrix.constructor import matrix
sage: def mandelbrot_cy2(float x0,float y0,float side,
... int N=200, int L=50, float R=3):
... returns an array NxN to be plotted with matrix_plot
...
... cdef double complex c, z
... cdef float delta
... cdef int h, j, k
... m=matrix(N,N)
... delta = side/N
... for j in range(N):
... for k in range(N):
... c = complex(x0+j*delta,y0+k*delta)
... z=0
... h=0
... while (h<L) and (abs(z)<R):
... z=z*z+c
... h+=1
... m[j,k]=h
... return m
Indicando los tipos de las variables hemos conseguido una mejora sustancial. Este es un buen momento para detenernos
(de hecho, el resto de la seccin no entra en el examen).
Observando el informe html sobre el cdigo generado, vemos que la condicin dentro del bucle while no se est
optimizando, y est dentro del bucle ms interior, luego es la parte del cdigo que ms se repite. El problema es
que estamos usando una funcin abs genrica. Podemos acelerar el clculo sustituyendo la llamada por operaciones
generales sobre nmeros reales (y eliminando la raz cuadrada implcita al calcular el valor absoluto):
sage: %cython
sage: from sage.matrix.constructor import matrix
sage: def mandelbrot_cy3(float x0,float y0,float side,
... int N=200, int L=50, float R=3):
... returns an array NxN to be plotted with matrix_plot
...
... cdef double complex c, z
... cdef float delta
... cdef int h, j, k
... m=matrix(N,N)
... delta = side/N
... for j in range(N):
... for k in range(N):
... c = complex(x0+j*delta,y0+k*delta)
... z=0
... h=0
... while (h<L and
... z.real**2 + z.imag**2 < R*R):
... z=z*z+c
... h+=1
... m[j,k]=h
... return m
Siguiendo la regla de optimizar slo la parte que ms repite, ponemos el ojo en la lnea:
c = complex(x0+j*delta,y0+k*delta)
que aparece en amarillo en el informe, y es interior a dos bucles for. Aunque a veces requeire un poco de ensayo y
error, una estrategia que suele funcionar es que las operaciones aritmticas se optimizan cuando declaramos los tipos.
Llamadas a funciones como complex no nos dan esas garantas, porque pueden implicar conversiones entre tipos de
datos.
sage: %cython
sage: from sage.matrix.constructor import matrix
sage: def mandelbrot_cy4(float x0,float y0,float side,
... int N=200, int L=50, float R=3):
... returns an array NxN to be plotted with matrix_plot
...
... cdef double complex c, z, I
... cdef float delta
... cdef int h, j, k
... m=matrix(N,N)
... I = complex(0,1)
... delta = side/N
... for j in range(N):
... for k in range(N):
... c = (x0+j*delta)+ I*(y0+k*delta)
... z=0
... h=0
... while (h<L and
... z.real**2 + z.imag**2 < R*R):
... z=z*z+c
... h+=1
... m[j,k]=h
... return m
La nica parte interior a los bucles que queda por optimizar es la asignacin:
m[j,k]=h
Para poder declarar arrays con tipos de datos tenemos dos opciones:
Usar punteros como en C:
...
cdef int\* m = <int\*> sage_malloc((sizeof int)\*N^2)
...
lo que conlleva peligros potenciales si calculamos mal los tamaos, y no es muy conveniente para arrays bidimensio-
nales.
Sustituir la matriz de Sage (matrix) por un array de la librera numpy , bien integrada en cython.
Al definir el tipo de la matriz m, usamos un tipo de datos terminado en _t, mientras que al llamar a una de las
funciones que construyen arrays (como zeros , ones o array ), pasamos el parmetro dtype, y no escribimos esa
terminacin. En este ejemplo usamos enteros positivos de 16 bits para los valores de m, asumiendo que no trabajaremos
con un L de ms de 216 .
cdef numpy.ndarray[numpy.uint16_t, ndim=2] m
m = numpy.zeros((N,N), dtype=numpy.uint16)
m[j,k]=h
sage: %cython
sage: import numpy
sage: cimport numpy #para declarar los tipos de los arrays
... #tb tenemos que usar cimport
sage: def mandelbrot_cy5(float x0,float y0,float side,
... int N=200, int L=50, float R=3):
... returns an array NxN to be plotted with matrix_plot
...
... cdef double complex c, z, I
... cdef float delta
... cdef int h, j, k
... cdef numpy.ndarray[numpy.uint16_t, ndim=2] m
... m = numpy.zeros((N,N), dtype=numpy.uint16)
... I = complex(0,1)
... delta = side/N
... for j in range(N):
... for k in range(N):
... c = (x0+j*delta)+ I*(y0+k*delta)
... z=0
... h=0
... while (h<L and
... z.real**2 + z.imag**2 < R*R):
... z=z*z+c
... h+=1
... m[j,k]=h
... return m
No tiene sentido continuar: hemos optimizado la parte interior a los bucles, y aunque an se puede hacer el cdigo
ms rpido, el precio a pagar no nos compensa.
Ms informacin en el manual de cython:
http://docs.cython.org/
sage: time m=mandelbrot_cy5(-0.59375, 0.46875, 0.046875,600,160)
sage: matrix_plot(m).show(figsize=(8,8))
Time: CPU 0.10 s, Wall: 0.11 s
3.6 Ejercicios
3.6.1 1.
La conjetura de Goldbach afirma que todo nmero par se puede expresar como suma de dos primos.
El cdigo de abajo pretende verificar la conjetura de Goldbach hasta un cierto nmero par K.
Tu objetivo es mejorar este cdigo siguiendo las siguientes directrices:
Identifica, de entre las tres funciones de abajo, la que ms tiempo de cmputo consume, y por tanto la que ms
necesita nuestra atencin. Justifica tu respuesta.
Usando la hiptesis (trivial de demostrar, por otro lado), de que la criba de Eratstenes tiene complejidad en
O(N^2) (observa que decimos O, luego hablamos slo de una cota superior), estima la complejidad del cdigo
presentado.
Mejora el cdigo de arriba modificando el algoritmo hasta que su complejidad sea O(N^2). Trabaja nicamente
en mejorar la funcin que has identificado antes.
sage: ##Criba
sage: def criba(ls):
... Se queda con los elementos irreducibles de una lista de enteros
... primos = []
... while ls:
... p = ls[0]
... primos.append(p)
... ls = [k for k in ls if k %p]
... return primos
sage: def lista_primos(K):
... genera los numeros primos menores que K
... return criba(range(2,K))
sage: def goldbach(N):
... for t in range(4,N,2):
... comprobado = False
... for x in lista_primos(N):
... for y in lista_primos(N):
... if t == x+y:
... comprobado = True
... if not comprobado:
... return False #t es un contraejemplo
... return True
3.6.2 2.
El objetivo de este ejercicio es calcular el tiempo de parada de la sucesin de Collatz comenzando por cada numero
menor que M. El cdigo de abajo calcula la longitud de la secuencia de Collatz partiendo de cada nmero entre 1 y M.
Tus objetivos son:
Modificar el algoritmo para recordar los valores ya calculados. Por ejemplo, si sabes que la longitud de la
sucesin que comienza por 3 es 8, entonces la longitud de la sucesin que empieza por 6 es 9(=8+1), ya que el
nmero siguiente a 6 es, y a partir de ese punto las sucesiones son iguales.
6->3->10->5->16->8->4->2->1
Compila el cdigo usando cython y declarando los tipos de las variables para acelerar el clculo.
sage: def collatz(k):
... if k %2:
... return 3*k+1
... else:
... return k/2
sage: def tiempos_collatz(M):
... tiempos = []
... for j in range(1,M):
... l = 1
... k = j
... while k!=1:
... k = collatz(k)
... l+=1
... tiempos.append(l)
... return tiempos
3.6. Ejercicios 73
Laboratorio de Matemticas, Release 2010/2011
3.6.3 3.
http://es.wikipedia.org/wiki/Juego_de_la_vida
El juego de la vida se desarrolla en una malla formada por cuadrados (clulas) que se extiende por el infinito
en todas las direcciones. Cada clula tiene 8 clulas vecinas, que son las que estn prximas a ella, incluyendo las
diagonales. Las clulas tienen dos estados: estn vivas o muertas (o encendidas y apagadas). El estado de
la malla evoluciona a lo largo de unidades de tiempo discretas (se podra decir que por turnos). El estado de todas
las clulas se tiene en cuenta para calcular el estado de las mismas al turno siguiente. Todas las clulas se actualizan
simultneamente.
Las transiciones dependen del nmero de clulas vecinas vivas:
Una clula muerta con exactamente 3 clulas vecinas vivas nace (al turno siguiente estar viva).
Una clula viva con 2 3 clulas vecinas vivas sigue viva, en otro caso muere o permanece muerta (por soledad
o superpoblacin).
El cdigo de debajo calcula la evolucin de una matriz NxN con unos para las clulas vivas y ceros para las muertas.
Tu objetivo es compilar el cdigo en cython indicando los tipos de las variables y haciendo alguna otra optimiza-
cin sencilla que se te ocurra para mejorar el cdigo. Anota las mejora obtenidas contra un ejemplo tipo de tamao
respetable para observar qu cambios son ms importantes.
sage: def cuenta_vecinos(m, j, k):
... Cuenta el nmero de vecinos vivos de la casilla (j,k)
... cuenta = 0
... for j0, k0 in [(j - 1,k - 1), (j - 1,k), (j - 1,k + 1), (j,k - 1), (j,k + 1), (j
sage: + 1,k - 1), (j + 1,k), (j + 1,k + 1)]:
... if 0<=j0<m.nrows() and 0<=k0<m.ncols():
... cuenta += m[j0,k0]
... return cuenta
sage: def paso(m):
... F = m.nrows()
... C = m.ncols()
... nueva = matrix(F,C)
... for j in range(F):
... for k in range(C):
... vecinos = cuenta_vecinos(m, j, k)
... if vecinos == 3 or (m[j,k] and vecinos == 2):
... nueva[j,k] = 1
... else:
... nueva[j,k] = 0
... return nueva
sage: def gameoflife(matriz, generaciones):
... for g in range(generaciones):
... matriz = paso(matriz)
... return matriz
sage: m=matrix(8,8)
sage: m[3,4]=1
sage: m[4,4]=1
sage: m[2,4]=1
sage: m[4,3]=1
sage: m[2,2]=1
sage: matrix_plot(m).show()
sage: m = gameoflife(m,1)
sage: matrix_plot(m).show()
sage: %time
sage: m=matrix(8,8)
sage: m[3,4]=1
sage: m[4,4]=1
sage: m[2,4]=1
sage: m[4,3]=1
sage: m[2,2]=1
sage: a = animate([matrix_plot(gameoflife(m,j)) for j in range(1,10)])
3.6. Ejercicios 75
Laboratorio de Matemticas, Release 2010/2011
La primera sesin consiste de ejercicios de aritmtica que se pueden resolver usando tcnicas bastante elementales.
Despus aprendemos a operar con elementos de anillos de enteros y anillos de polinomios en Sage. Finalmente,
aprendemos a hacer lgebra lineal sobre varios cuerpos de coeficientes y planteamos algunos problemas de formas
bilineales.
4.1 Aritmtica
En esta sesin vamos a trabajar con algunas funciones de Sage para trabajar con nmeros (algunas ya las hemos usado),
y a implementar algunos algoritmos clsicos de teora de nmeros.
Esta vez no vamos a separar la teora de los ejercicios . En realidad, no vamos a usar funciones de Sage ni carac-
tersticas de Sage que no hayamos visto antes. Debajo tienes varios temas de teora de nmeros, con una pequea
introduccin y un recordatorio de algunas funciones que pueden ser tiles, y de varios ejercicios de dos grupos. El
primer grupo consiste de ejercicios bsicos que deberas intentar resolver en esta misma sesin. El segundo grupo
consiste de ejercicios para resolver en casa . La siguiente sesin resolveremos buena parte de los ejercicios.
Aunque hemos escrito nuestra propia funcin para recuperar los dgitos de un nmero en una base B arbitraria (o casi),
tambin podemos usar el mtodo digits , que tienen los enteros de Sage ( Integer ).
sage: a = 17
sage: print a.digits(base=2)
sage: print a.digits(base=10)
sage: print a.digits(base=3)
[1, 0, 0, 0, 1]
[7, 1]
[2, 2, 1]
Ejercicio
77
Laboratorio de Matemticas, Release 2010/2011
1.
Escribe una funcin que devuelva True si un nmero es divisible por 9 usando el criterio que dice: un nmero es
divisible por 9 si y slo si la suma de sus dgitos es divisible por 9. Si la suma de los dgitos es mayor que 9, puedes
llamar a la funcin recursivamente para decidir si verifica el criterio. No debera usar el resto de la divisin ( % ) en
ningn momento.
2.
> [7,4,2]
3.
http://projecteuler.net/index.php?section=problems&id=36
Encuentra todos los nmeros menores que un milln que son palndromos tanto en base 2 como en base 10 .
Por ejemplo, 585 se escribe 1001001001 en binario. Ambas expresiones se leen igual al derecho que al revs.
Ejercicios
El teorema de Dirichlet dice que hay infinitos nmeros primos en una sucesin del tipo:
xj = a j + b
El teorema del nmero primo afirma que el siguiente lmite existe y es igual a 1:
(x)
lm
x x/ ln(x)
donde (x) es la cantidad de nmeros primos menores que x y ln(x) es el logaritmo neperiano.
(x)
Escribe un cdigo que evale la funcin x ln(x) para un nmero x cualquiera.
Encuentra un nmero x tal que el lmite diste de 1 menos de 0.1
1.
Halla la secuencia ms larga de nmeros consecutivos menores que un milln que no contiene ningn primo.
2.
La funcin de Euler se puede calcular utilizando la factorizacin del nmero. Si k = pe11 . . . pekk , tenemos:
e 1
Y e
(k) = (pj j pj j )
Utiliza el mtodo factor aplicado a nmeros enteros y la frmula de arriba para calcular el valor de la funcin de
Euler:
Compara el resultado con el obtenido usando la regla (k) es la cantidad de nmeros menores que k que son
primos relativos con k, o bien con alguna funcin de Sage que haga la misma tarea.
El algoritmo de euclides calcula el mximo comn divisor ( mcd ) de dos nmeros naturales n y m. El algoritmo se
basa en el siguiente principio: el mcd de dos nmeros n y m, con n<m, es tambin el mcd de n y m %n (el resto de
dividir m por n). De esta forma, hemos reducido el problema a otro con nmeros menores. Eventualmente, uno de los
dos nmeros ser 0, y entonces sabemos que mcd(0,m)=m.
4.1. Aritmtica 79
Laboratorio de Matemticas, Release 2010/2011
Ejercicios
1.
Escribe una funcin que calcule el mximo comn divisor de dos nmeros siguiendo el algoritmo de Euclides.
2.
Escribe una funcin que acepte una lista de nmeros como argumento y devuelva el mximo comn divisor de los
nmeros de la lista.
Escribe una funcin que calcule el mximo comn divisor de dos nmeros calculando la factorizacin de cada uno de
ellos y despus escogiendo los factores comunes con el menor exponente.
Una identidad de Bzout muestra explcitamente el mcd d de m y n como una combinacin lineal de m y n con
coeficientes enteros:
d=um+vn
La funcin xgcd de SAGE implementa el algoritmo extendido de Euclides, que devuelve una tupla con el mcd y los
coeficientes de una identidad de Bzout:
(d,u,v)=xgcd(m,n)
sage: m=15
sage: n=21
sage: (d,u,v)=xgcd(m,n)
sage: print %d=( %d)* %d+( %d)* %d %(d,u,m,v,n)
3=(3)*15+(-2)*21
donde d es el mximo divisor comn a todos los nmeros mj . De hecho, el proceso para encontrarlo se reduce al
anterior, usando induccin. Como ya sabemos resolver el caso N=2 , slo tenemos que aprender a reducir el caso de
N+1 nmeros al caso anterior.
Para unos nmeros m = m1 , . . . , mN +1 , comenzamos por encontrar una identidad de Bezout para los dos ltimos:
dN = v mN + w mN +1
Ejercicio
Escribe una funcin que acepta como argumento una lista de nmeros mj y devuelve una lista que contiene en pri-
mer lugar el mximo comn divisor de todos los elementos de la lista, seguido de los ceficientes uj que realizan la
identidad:
N
X
d= uj mj
j=1
Con la identidad de Bzout podemos hacer funcionar el teorema chino del resto:
Si los nmeros m y n son primos entre s, entonces para cualesquiera a<m , b<n existe un nico nmero c menor que
n*m tal que el resto de dividir c entre m es a y el resto de dividir c entre n es b .
En lenguaje de congruencias, escribimos x a(mod m) para decir que el resto de dividir a por m es el mismo que el
resto de dividir x por m . El teorema chino del resto se escribe entonces:
c = bmv + anu
1 = nu + mv
Ejercicio
4.1. Aritmtica 81
Laboratorio de Matemticas, Release 2010/2011
x ai (mod mi )i x c(mod m1 mN )
Sage tiene definidos un buen nmero de anillos, grupos y otras estructuras algebraicas.
Podemos operar con elementos que representan elementos de un anillo o un espacio vectorial, por ejemplo, adems de
con objetos que representan anillos, grupos, subgrupos, subespacios vectoriales y otras estructuras de nivel ms alto.
Muchos anillos comunes estn definidos en Sage:
ZZ : Z
Integers(m) : Zm ( Z/mZ)
QQ : Q
RR : R, representados por nmeros reales de doble precisin
CC : C, representados por nmeros reales de doble precisin
Adems, podemos usar otros constructores para definir anillos derivados, como el constructor de anillos de polinomios
PolynomialRing que veremos en detalle ms abajo.
sage: R1 = Integers(7)
sage: R2 = Integers(21)
sage: a = ZZ(3)
sage: b = R1(3)
sage: c = R2(3)
sage: print a, a.parent()
sage: print b, b.parent()
sage: print c, c.parent()
3 Integer Ring
3 Ring of integers modulo 7
3 Ring of integers modulo 21
En el caso de Zm , podemos trabajar de forma ms abstracta con elementos de Integers(m) bien a nivel ms
bajo usando nmeros enteros normales, pero tomando restos mdulo m. Estudiar los pasos necesarios en detalle es
ilustrativo.
Para hacer sumas y productos sobre clases de equivalencia, podemos usar la suma y el producto habituales de nmeros
enteros, y tomar el resto de dividir por m :
m=31
a=12
b=23
s=(a+b) %m
p=(a*b) %m
Potencia mdulo m
Aunque podemos calcular la clase de congruencia de ap (mod m) calculando el entero ap y luego tomando el resto
mdulo m , debemos tener en cuenta que ap puede ser un nmero muy grande, y el ordenador puede dedicar al clculo
demasiado tiempo y memoria. En este caso, compensa reducir el nmero mdulo m despus de cada producto:
sage: def potencia1(x, n, m):
... if n == 0:
... return 1
... elif n %2 == 0:
... y = potencia1(x, n/2, m)
... return (y*y) %m
... else:
... y = potencia1(x, (n-1)/2, m)
... return (x*y*y) %m
sage: potencia1(3,8,10)
1
El algoritmo correspondiente para Integers(m) es vlido para cualquier anillo con unidad (el 1 que aparece en el cdigo
se transformar en la unidad del anillo).
sage: def potencia(x, n):
... if n == 0:
... return 1
... elif n %2 == 0:
... y = potencia(x, n/2)
... return y*y
... else:
... y = potencia(x, (n-1)/2)
... return x*y*y
sage: a = ZZ(3)
sage: b = R1(3)
sage: c = R2(3)
sage: print potencia(a, 8)
sage: print potencia(b, 8)
sage: print potencia(c, 8)
6561
2
9
El inverso de a mdulo m, es decir, la clase b tal que a b 1(mod m), no se puede reducir a una operacin de
aritmtica usual. Una llamada a la funcin inverse_mod(a,m) devuelve el inverso de a mdulo m (este nmero se
calcula usando los coeficientes de una identidad de Bzout). La operacin equivalente en Integers(m) es 1/a .
sage: inverse_mod(3,7)
5
sage: R1 = Integers(7)
sage: b = R1(3)
sage: 1/b
5
La sintaxis para definir un anillo de polinomios con coeficientes en otro anillo R es:
Pols.<t> = PolynomialRing(R)
Pols.<t> = R[t]
donde hemos definido el anillo de polinomios Pols con coeficientes en el anillo R y la variable independiente t .
sage: #Definimos varios anillos de polinomios con coeficientes en distintos anillos
sage: PR1.<t> = PolynomialRing(ZZ)
sage: PR2.<x> = PolynomialRing(QQ)
sage: PR3.<y> = PolynomialRing(RR)
sage: PR4.<z> = PolynomialRing(CC)
sage: R1 = Integers(7)
sage: PR5.<u> = PolynomialRing(R1)
Una vez hemos definido el anillo, podemos usar la variable independiente para definir polinomios, operando con ella
como una variable ms.
sage: p=t^2-1
sage: q=x^2-1
sage: p.base_ring()
Integer Ring
sage: q.base_ring()
Rational Field
sage: q?
<html>...</html>
donde var es la variable independiente del polinomio y expresion es cualquier expresin en SAGE que d como
resultado un elemento del anillo.
sage: print p(t=2)
sage: print q(x=1+2)
sage: numero=0
sage: print q(x=numero)
sage: print [q(x=j) for j in range(-3,4)]
3
8
-1
[8, 3, 0, -1, 0, 3, 8]
Una raz de un polinomio p R[t] es un elemento x0 del anillo R tal que p(x0)=0 . El teorema fundamental del lgebra
afirma que un polinomio de grado n con coeficientes complejos tiene exactamente n races complejas. Sin embargo,
un polinomio con coeficientes enteros puede tener races que son nmeros racionales, reales algebraicos, o complejos.
El mtodo roots se puede llamar desde cualquier polinomio, y devuelve una lista de tuplas que contienen las races
del polinomio con sus multiplicidades. Atencin, si las races de un polinomio no estn en el anillo de sus coeficientes,
el mtodo roots no las devuelve, y tenemos que llamar a real_roots o complex_roots .
sage: p.roots()
[(1, 1), (-1, 1)]
sage: p2=x^2-2
sage: p2.roots()
[]
sage: p2.real_roots()
[-1.41421356237310, 1.41421356237310]
sage: p3=x^2+1
sage: p3.roots()
[]
sage: p3.real_roots()
[]
sage: p3.complex_roots()
[-1.00000000000000*I, 1.00000000000000*I]
sage: r=z^2+1
sage: r.roots()
[(-1.00000000000000*I, 1), (1.00000000000000*I, 1)]
El mtodo roots devuelve slo las races enteras, y real_roots devuelve todas las races reales, y adems lo
hace de forma numrica. Sin embargo, SAGE tiene una alternativa a nuestro mtodo raices: extender el polinomio a
un polinomio con coeficientes racionales, y entonces el mtodo roots devuelve las races racionales.
sage: s1 = 2*t-1
sage: print s1.roots()
[]
sage: s1.real_roots()
[0.500000000000000]
sage: r.roots()
[(5, 1), (2, 1)]
Si un polinomio con coeficientes en Z, Q R no tiene tantas races (contando multiplicidades) como su grado, no se
puede escribir como producto de factores lineales. Aun as, se puede escribir como producto de factores irreducibles
(es decir, factores que no se pueden expresar como producto de polinomios de menor grado):
Lista de coeficientes
list(polinomio)
devuelve una lista con los coeficientes de polinomio , ordenados de menor a mayor grado.
sage: s1=2*z^2-3*z+1
sage: print s1
2.00000000000000*z^2 - 3.00000000000000*z + 1.00000000000000
4.3 Ejercicios
4.3.1 1.
a(m) 1(mod m)
4.3. Ejercicios 87
Laboratorio de Matemticas, Release 2010/2011
Dado un nmero m , encuentra el menor nmero positivo k tal que ak 1(mod m) para todo a que sea primo
relativo con m . Llamemos (m) al nmero k as obtenido.
4.3.2 2.
Como vimos en el ejercicio anterior, (m) no siempre coincide con (m). Para cada una de las preguntas siguientes,
busca un contraejemplo, o verifica la propiedad para todos los nmeros menores que 1000.
Coinciden siempre para los nmeros m = pj cuando p es primo?
Coinciden siempre para los nmeros m = pj cuando p es primo impar ?
Coinciden siempre para los nmeros m = pj q k cuando p y q son primos impares?
Comprueba el criterio que aprendimos en secundaria sobre las races racionales de un polinomio con
coeficientes enteros:
Cualquier raz racional de un polinomio con coeficientes enteros tiene un numerador que divide al trmino indepen-
diente y un denominador que divide al coeficiente del trmino de mayor grado.
Escribe cdigo que busque las races de un polinomio con coeficientes enteros segn este criterio
Escribe cdigo que compruebe que el resultado coincide con el resultado de llamar al mtodo roots, para un
conjunto de polinomios que te parezca lo bastante significativo.
4.3.5 5.
En Sage podemos trabajar con distintos cuerpos para los coeficientes de los espacios vectoriales. Las elecciones ms
usuales son:
QQ (o RationalField() ) el cuerpo de los nmeros racionales.
QQbar (o AlgebraicField () ), representa la clausura algebraica Q del cuerpo de los nmeros racionales.
RDF (o RealDoubleField() ), nmeros reales de 64 bits.
CDF (o ComplexDoubleField() ), nmeros complejos de 64 bits.
SR , o expresiones simblicas. Cualquier expresin algebraica que contenga smbolos como pi, I, sqrt(2) perte-
nece a este anillo.
Los cuerpos Q y Q son computables, y sus implementaciones en Sage son exactas (en la documentacin de Sage:
exact field), es decir, los clculos no acumulan errores de redondeo. Sin embargo, los tres ltimos cuerpos no tienen
aritmtica exacta, debido a los errores de redondeo. Esto significa que en estos cuerpos no se pueden hacer clculos
como la multiplicidad de las races de un polinomio o la forma de Jordan de una matriz, que sean inestables ante la
aparicin de errores numricos.
sage: V1 = VectorSpace(QQ,3)
sage: V2 = VectorSpace(RDF,3) #Numeros reales de precision doble
sage: V3 = VectorSpace(CDF,4) #Numeros complejos de precision doble
sage: print V1
sage: print V2
sage: print V3
Vector space of dimension 3 over Rational Field
Vector space of dimension 3 over Real Double Field
Vector space of dimension 4 over Complex Double Field
Vectores
Los vectores pertenecen a un espacio vectorial concreto, aunque SAGE har las conversiones necesarias entre tipos de
datos si queremos hacer operaciones entre espacios vectoriales compatibles.
sage: #O tambien
sage: v1 = V1([1,1,1])
sage: v2 = V2([1,1,0])
sage: v3 = 2*v1+v2
sage: print v1 ,v1.parent()
sage: print v2 ,v2.parent()
sage: print v3 ,v3.parent()
(1, 1, 1) Vector space of dimension 3 over Rational Field
(1.0, 1.0, 0.0) Vector space of dimension 3 over Real Double Field
(3.0, 3.0, 2.0) Vector space of dimension 3 over Real Double Field
sage: #pero...
sage: v1 = V1([1,1])
Traceback (most recent call last):
...
TypeError: entries must be a list of length 3
Tambin podemos usar vector sin indicar el espacio vectorial, y entonces Sage escoger como espacio ambiente
el anillo de coeficientes ms pequeo que contenga a las entradas. Si los coeficientes son enteros, como Z no es un
cuerpo, en vez de espacio vectorial, Sage habla de modulo, pero no es necesario preocuparnos por esa distincin.
sage: v1 = vector([1,1,1])
sage: v2 = vector([1/2,1,1])
sage: v3 = vector([1.0,1,0])
sage: print v1 ,v1.parent()
sage: print v2 ,v2.parent()
sage: print v3 ,v3.parent()
(1, 1, 1) Ambient free module of rank 3 over the principal ideal domain Integer Ring
(1/2, 1, 1) Vector space of dimension 3 over Rational Field
(1.00000000000000, 1.00000000000000, 0.000000000000000) Vector space of dimension 3 over Real Field w
Subespacios vectoriales
Podemos definir fcilmente el subespacio engrendrado por un conjunto de vectores, y despus hacer operaciones como
interseccin o suma de subespacios, o comprobaciones como igualdad o inclusin de subespacios.
sage: v1 = vector([1,1,1])
sage: v2 = vector([1,1,0])
sage: v3 = vector([1,0,1])
sage: #Hay que fijarse en el cuerpo de coeficientes
sage: L1 = V1.subspace([v1,v2])
sage: L1_bis = V2.subspace([v1,v2])
sage: print L1
sage: print L1_bis
Vector space of degree 3 and dimension 2 over Rational Field
Basis matrix:
[1 1 0]
[0 0 1]
Vector space of degree 3 and dimension 2 over Real Double Field
Basis matrix:
[1.0 1.0 0.0]
[0.0 0.0 1.0]
El grado (degree) al que se refiere arriba, es la dimensin del espacio ambiente. Podemos recuperar este espacio
directamente con el mtodo ambient_vector_space .
sage: #Dimension
sage: print dim(L1)
2
sage: #Grado
sage: print L1.degree()
3
sage: L1.ambient_vector_space()
Vector space of dimension 3 over Rational Field
Muchos operadores actan sobre subespacios vectoriales, con los significados habituales. Cuando el smbolo usual
para la operacin no est en el teclado (como el operador de interseccin ), lo normal es usar un mtodo (como el
mtodo intersection ).
sage: #Pertenencia al subespacio
sage: print v3 in L1
sage: v4 = vector([4,4,3])
sage: print v4 in L1
False
True
Ejercicio resuelto
[1 1 1]
Suma
Vector space of degree 3 and dimension 3 over Rational Field
Basis matrix:
[1 0 0]
[0 1 0]
[0 0 1]
True
Como hemos visto, al definir subespacios mediante generadores, se construye una base escalonada del subes-
pacio a partir de los generadores. Para imponer la base del espacio o subespacio, usamos el comando
subspace_with_basis .
sage: v1 = vector([1,1,0])
sage: v2 = vector([1,0,1])
sage: L1 = V1.subspace([v1,v2])
sage: print L1
sage: print L1.basis()
sage: print L1.basis_matrix()
Vector space of degree 3 and dimension 2 over Rational Field
Basis matrix:
[ 1 0 1]
[ 0 1 -1]
[
(1, 0, 1),
(0, 1, -1)
]
[ 1 0 1]
[ 0 1 -1]
sage: L1 = V1.subspace([v1,v2])
sage: L2 = V1.subspace([v1,v1+v2])
sage: print L1.basis()
sage: print L2.basis()
[
(1, 0, 1),
(0, 1, -1)
]
[
(1, 0, 1),
(0, 1, -1)
]
sage: L3 = V1.subspace_with_basis([v1,v2])
sage: print L1
sage: print L3
sage: #A pesar de tener distintas bases, ambos subespacios se declaran iguales
sage: print L1 == L3
sage: print L3.basis_matrix()
Vector space of degree 3 and dimension 2 over Rational Field
Basis matrix:
[ 1 0 1]
[ 0 1 -1]
Vector space of degree 3 and dimension 2 over Rational Field
4.4.2 Matrices
Para crear una matriz manualmente en SAGE, llamamos a matrix con el anillo de coeficientes como primer argu-
mento. Despus introducimos los datos usando una de las dos formas siguientes:
Como una lista de listas, cada una de las cuales contiene una fila de la matriz:
#Matriz 2x4 con coeficientes en Q
M = matrix(QQ,[[0,1,0,0],[1,0,0,0]])
- Pasando el nmero de filas, el de columnas, y una sla lista con todos los elementos:
donde K1 es el nmero de filas y K2 el nmero de columnas y, en vez de pasar una lista con las filas, pasamos una
sla lista con K1xK2 elementos:
matrix(QQ, 2, 4, [1,2,3,4,5,6,7,8])
sage: M1 = matrix(QQ,[[1,2,3,4],[4,2,3,1]])
sage: M2 = matrix(QQ,3,3,[3,2,1, 1,2,3, 2,2,2])
sage: show(M1)
sage: show(M2)
1 2 3 4
4 2 3 1
3 2 1
1 2 3
2 2 2
Aparte de los clculos habituales con matrices, como determinante o rango, hay otros mtodos interesantes que co-
nectan con lo visto antes. Mencionamos tres, pero recuerda que puedes ver una lista completa escribiendo el nombre
de tu matriz seguido de un punto y pulsando el tabulador:
M.kernel() : Ncleo de una matriz, visto como un subespacio vectorial (pon atencin a la diferencia entre
el kernel por la izquierda M.left_kernel() o por la derecha M.right_kernel() ; el mtodo kernel
a secas se corresponde con left_kernel )
M.image() : Imagen de una matriz, como subespacio vectorial
M.echelon_form() : forma escalonada
sage: M1 = matrix(QQ,[[1,2,3,4],[4,2,3,1]])
sage: show(M1)
sage: print M1.kernel() #lo mismo que M1.left_kernel()
sage: print
sage: print M1.image()
sage: show( M1.echelon_form())
Vector space of degree 2 and dimension 0 over Rational Field
Basis matrix:
[]
1 2 3 4
4 2 3 1
1 0 0 1
3 5
0 1 2 2
3 2 1
5 2 1
4 2 1
2,0 0 2,0 2,0
1,0 3,0
2,0 3,0
5,0 1,0 8,0 3,0
5,0 1,0 4,0 1,0
La suma de matrices y productos por vectores se comporta de la manera previsible, lanzando errores si las dimen-
siones no casan, y cambiando los tipos de datos segn sea necesario para hacer operaciones cuyos operandos tienen
coeficientes en cuerpos distintos, pero compatibles.
sage: M4 = identity_matrix(QQ, 4)
sage: show(M4)
sage: show(3*M4 + M3)
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
5,0 0 2,0 2,0
1,0 6,0
2,0 3,0
5,0 1,0 11,0 3,0
5,0 1,0 4,0 4,0
2 1
0 1
2 2
0 1
Si tenemos un espacio dado por ecuaciones, basta poner los coeficientes de las ecuaciones en una matriz, y podemos
obtener el subespacio que define como el conjunto de vectores en los que todas las ecuaciones se anulan (es decir el
ncleo). Por ejemplo, escribimos las ecuaciones
x1 + 2x2 = 0
x1 + x3 = 0
en forma matricial:
1 2 0
1 0 1
Al haber elegido esta forma de escribir la matriz, el espacio que nos interesa es el kernel por la derecha (
right_kernel )
sage: M = matrix(QQ,2,3,[1,2,0, -1,0,1])
sage: M.right_kernel()
Vector space of degree 3 and dimension 1 over Rational Field
Basis matrix:
[ 1 -1/2 1]
Ejercicio resuelto
Ejercicio resuelto
Encuentra una base del subespacio de C4 dado por x1 + 2x2 x4 y una base de su interseccin con el subespacio
engendrado por [1, 1, 1, 1] y [1, 1, 0, 0].
sage: #Subespacio dado por x1 + 2*x2 -x4 en V(CDF,4)
sage: M = matrix(CDF,4,1,[1,2,0,-1])
sage: show(M)
sage: L2 = M.left_kernel()
sage: print L2
Vector space of degree 4 and dimension 3 over Complex Double Field
Basis matrix:
[1.0 0 0 1.0]
[ 0 1.0 0 2.0]
[ 0 0 1.0 0]
1,0
2,0
0
1,0
sage: L1.intersection(L2)
Vector space of degree 4 and dimension 1 over Complex Double Field
Basis matrix:
[1.0 1.0 3.0 3.0]
Como indicamos arriba, la forma de Jordan es inestable antes pequeos errores. Esto hace imposible calcular la forma
de Jordan usando aritmtica aproximada con errores de redondeo. La eleccin del cuerpo de coeficientes se vuelve por
tanto muy importante.
sage: M = matrix(QQ,3,3,[0,1,0,1,0,0,0,0,1])
sage: print M
sage: print M.eigenvalues()
sage: show( M.eigenvectors_left())
sage: print M.jordan_form()
[0 1 0]
[1 0 0]
[0 0 1]
[-1, 1, 1]
[-1| 0| 0]
[--+--+--]
[ 0| 1| 0]
[--+--+--]
[ 0| 0| 1]
8 6 3
1 8 3
4 10 19
1
7, 1, 1, , 1 , (14, [(1, 6, 3)] , 2)
2
[(1, [(0, 0, 1)] , 1) , (-1*I, [(1, 1*I, 0)] , 1) , (1*I, [(1, -1*I, 0)] , 1)]
Como vemos, si hay autovalores en C \ Q, SAGE no calcula la forma de Jordan. Una solucin es ampliar los coe-
ficientes a Q, el cuerpo de los nmeros algebraicos, que contiene todas las races de ecuaciones con coeficientes en
Q.
La forma de Jordan slo est definida en anillos de coeficientes exactos, como QQ, o QQbar, pero no tiene sentido en
anillos con precisin limitada, porque la forma de Jordan es numricamente inestable. Intentar calcular una forma de
Jordan en un cuerpo con errores numricos simplemente genera un error.
sage: M = matrix(CDF,3,3,[ 8, 6, 3, -1, 8, -3, 4, 10, 19])
sage: print M.jordan_form()
Traceback (most recent call last):
...
ValueError: Jordan normal form not implemented over inexact rings.
Sin embargo, aunque trabajemos con nmeros de como flotante, todava tiene sentido hablar de autovalores.
sage: M = matrix(CDF,3,3,[ 8, 6, 3, -1, 8, -3, 4, 10, 19])
sage: print M.eigenvalues()
[7.0 - 9.39502740013e-17*I, 14.0000000001 + 1.5006305546e-07*I, 13.9999999999 - 1.5006304995e-07*I]
Los autoespacios tambin son problemticos, y es mejor evitarlos si usamos aritmtica aproximada.
sage: M = matrix(QQ,2,2,[ 1, 1, 0, 1])
sage: print M.eigenspaces()
[
(1, Vector space of degree 2 and dimension 1 over Rational Field
User basis matrix:
[0 1])
]
4.5 Ejercicios
Buena parte de los siguientes ejercicios estn extrados de las hojas de problemas de lgebra Lineal.
4.5.1 1.
Decide si cada uno de los siguientes conjuntos es una base de R3 . Si lo es, encuentra las coordenadas en la nueva base de v = (1,
B1 = {(1, 1, 1), (0, 1, 1), (0, 0, 1)}, B2 = {(0, 1, 2), (1, 2, 3), (2, 3, 4)}
B3 = {(0, 1, 1), (1, 0, 1), (1, 1, 0)}, B4 = {(1, 1, 1), (1, 2, 3), (1, 2, 3)}
4.5. Ejercicios 99
Laboratorio de Matemticas, Release 2010/2011
4.5.2 2.
Escribe una funcin que acepte como argumento una lista de vectores, y te devuelva un subconjunto maximal de
vectores linealmente independientes extrados de la lista anterior. El mtodo consiste en recorrer la lista, y quedarte
slo con los vectores que son linealmente independientes con los vectores que has elegido antes.
Ejemplos:
{(1,0,0),(2,0,0),(1,1,0),(0,1,0),(2,1,0)} \-> {(1,0,0),(1,1,0)}
4.5.3 3.
Encuentra la inversa de la matriz siguiente usando el mtodo de Gauss-Jordan: aumenta la matriz con la matriz identi-
dad y usa operaciones de fila para reducir la matriz hasta la identidad:
2 1 1
A = 3 1 2
2 1 2
http://es.wikipedia.org/wiki/Eliminaci%C3%B3n_de_Gauss-Jordan#Encontrando_la_inversa_de_una_matriz
Usa para ello los mtodos: augment , add_multiple_of_row y otros similares que encuentres entre los mtodos
de las matrices. Compara el resultado con la matriz inversa obtenida de forma directa.
Cuando termines, escribe cdigo que resuelva este problema en general.
4.5.4 4.
Se consideran los subespacios V y W del espacio vectorial real R3 . Las ecuaciones paramtricas de V son
x1 = +
x2 = +
x3 = + + 2
, , R
4.5.5 5.
4 0 4 11 3 1 1 1 2 0 1 2
6 3 3 9 0 2 0 1 1 3 0 1
A1 =
A5 =
A9 =
5 0 5 7 1 1 1 0 2 2 4 6
1 0 1 5 0 0 0 2 1 1 1 1
Busca mtodos para calcular el polinomio caracterstico (characteristic polynomial) y el polinomio mnimo
(minimal polynomial) de estas matrices. Factoriza ambos polinomios y comprueba que el polinomio mnimo
divide al caracterstico.
Calcula los autoespacios asociados a cada autovalor calculando el ncleo de (A I)k . Compara el resultado
con los autoespacios calculados antes.
Los polinomios pk forman una base del espacio de polinomios, al igual que los monomios xk .
Comprueba que a pesar de tener coeficientes fraccionarios, todos los polinomios de arriba toman siempre valores
enteros cuando x es un entero (es decir, tras evaluar unos cuantos de esos polinomios en unos cuantos enteros x
slo se obtienen valores enteros).
Dado un polinomio cualquiera, expresado como una expresin simblica en la variable x, encuentra sus coefi-
cientes en la base formada por los pk .
El divisor fijo de un polinomio es el mximo comn divisor de los valores que toma, cuando x toma como valores
todos los nmeros enteros.
Genera unos cuantos polinomios aleatorios con divisor fijo mayor que 1. Define el nmero h del polinomio como
el mximo comn divisor de los coeficientes de p en la base pk . Conjetura una relacin entre el nmero h y el
divisor fijo de un polinomio arbitrario.
Continuamos la sesin de lgebra lineal con varias aplicaciones a formas bilineales y productos escalares. Vamos a
representar una forma bilineal por la matriz simtrica que la define. Por simplicidad, fijaremos el espacio vectorial y
la forma bilineal como variables globales mientras sea conveniente.
Cundo es una matriz simtrica definida positiva? Sabemos que una matriz simtrica es diagonalizable. La matriz es
definida positiva sii todos sus autovalores son positivos.
sage: def es_definida_positiva(M):
... if not M.is_symmetric():
... raise ValueError, "La matriz no es simetrica"
... return all(l>0 for l in M.eigenvalues())
1 0 1
0 2 0
1 0 3
1 0 1
0 2 0
1 0 3
Ejercicio
Escribe una funcin que haga el mismo papel, pero usando el criterio de Sylvester ( una matriz es definida
positiva sii los determinantes de sus menores principales son positivos).
Escribe una funcin anloga para matrices definidas negativas.
Usa el mtodo random_matrix para generar 100 matrices con las que comprobar que ambos mtodos dan el
mismo resultado. (observacin: random_matrix genera matrices no necesariamente simtricas, pero hay un
truco cannico para obtener una matriz simtrica a partir de una matriz arbitraria).
Como sabemos de las clases de lgebra lineal, es fcil trabajar con un producto escalar distinto del habitual reempla-
zando los productos escalares por los productos escalares con la matriz B.
sage: #globales
sage: V = VectorSpace(QQ,3)
sage: B = matrix(QQ,3,3,[1,0,1, 0,2,0, 1,0,3])
sage: W = V.subspace(list(random_matrix(QQ,2,3)))
sage: L = complemento_ortogonal(W)
sage: print son_ortogonales(L,W) #Deberia ser True
sage: W = V.subspace(list(random_matrix(QQ,1,3)))
sage: L = complemento_ortogonal(W)
sage: print son_ortogonales(L,W) #Deberia ser True
Ejercicio en clase
El mtodo de Gram-Schmidt permite obtener una base ortonormal a partir de una base arbitraria.
Comenzamos con una lista vaca que contendr en todo momento un sistema ortonormal. Para cada vector de la base
original, repetimos los pasos siguientes:
Resta a ese vector su componente en cada uno de los vectores del sistema ortonormal.
Divide el resultado por su norma para obtener un vector unitario.
Ade el resultado al sistema ortonormal.
sage: def gram_schmidt(base):
... base_gm = []
... for u in base:
... w = u - sum((u*B*v)*v for v in base_gm)
... base_gm.append( w/sqrt(w*B*w) )
... return base_gm
Ejercicio en clase
Verifica que el resultado es una base ortonormal, para 100 bases generadas aleatoriamente usando random_matrix
.
Ejercicio
Escribe una funcin que acepte como argumentos un vector y un subespacio, y devuelva la proyeccin ortogonal
del vector en el subespacio (usando el producto escalar B definido globalmente).
Reescribe el mtodo de Gram-Schmidt usando esta funcin.
Una matriz simtrica definida positiva representa un producto escalar. Como hemos visto, podemos encontrar una base
ortonormal para ese producto escalar.
Si tenemos dos productos escalares, podemos encontrar una base que es ortonormal para el primer producto escalar y
ortogonal para el segundo. El procedimiento es el siguiente:
Encuentra una base ortonormal para el primer producto escalar.
En esa base, el primer producto escalar tiene como matriz la identidad, y el segundo tiene una matriz B, que es
simtrica.
Gracias al teorema espectral, es posible encontrar una matriz ortonormal tal que B es diagonal. En otras palabras,
existe una base ortonormal para el primer producto escalar en la que la matriz del segundo producto escalar es
diagonal. Es decir, que esa base tambin es ortogonal para el segundo producto escalar.
Ejercicio
Encuentra una base ortonormal para el producto escalar B1 y ortogonal para el producto escalar B2.
sage: B1 = matrix(RDF,3,3,[1,0,1, 0,2,0, 1,0,3])
sage: B2 = matrix(RDF,3,3,[3,-1,1, -1,2,0, 1,0,1])
Aprendemos tcnicas para construir familias de objetos combinatorios, usando recursin de forma explcita y tambin
construyendo rboles de objetos combinatorios. Repasamos las construcciones usuales, ya implementadas en Sage,
y las herramientas para resolver problemas de teora de grafos. Como alternativa cuando la enumeracin explcita es
inviable, usamos el mtodo de Monte Carlo.
5.1 Combinatoria
Algunos problemas de combinatoria requieren gran cantidad de clculos y se impone una solucin con el ordenador.
Muchos otros problemas se pueden resolver con papel y lpiz, pero requieren tiempo e ingenio. Sin embargo, escribir
programas de ordenador que resuelven los problemas por fuerza bruta suele ser bastante fcil. El problema estriba
en que la cantidad de casos a examinar a menudo se dispara, y un programa poco fino slo podr resolver los casos
ms sencillos. A veces, so es suficiente para darnos una idea, pero en general es ms recomendable aprender a hacer
programas ms eficientes.
En este bloque escribiremos programas que exploran conjuntos de posibilidades muy amplios. Es muy importante
comenzar siempre por aplicar nuestros programas con valores muy pequeos, medir bien los tiempos, y slo lanzar
los programas con los valores que nos interesan cuando pensemos que el programa podr manejarlo.
5.1.1 Cmo iterar sobre todos los posibles valores de muchas variables
Comenzamos un truco muy prctico que puede servir como alternativa a anidar muchos bucles for. Supn que tenemos
K variables que pueden tomar el valor 0 1, y queremos recorrer todas las posibles combinaciones de valores para
cada variable.
Si por ejemplo, K=10, la cantidad total de posibilidades es 210 = 1024, algo perfectamente asumible. Anidar 10 bucles
es muy poco prctico, y obliga a hacer cambios sustanciales si aumentamos el nmero de variables:
for k1 in [0,1]:
for k2 in [0,1]:
for k3 in [0,1]:
for k4 in [0,1]:
for k5 in [0,1]:
...
En su lugar, podemos usar el siguiente truco: cuando un nmero k recorre los nmeros desde 0 hasta 2k 1, sus bits
recorren todas las posibles combinaciones de 0 y 1 para K variables. Podemos usar, por ejemplo, el mtodo digits()
105
Laboratorio de Matemticas, Release 2010/2011
pasando los argumento base=2 y padto=K , para que extraiga los dgitos binarios (bits) y devuelva siempre listas
de longitud K.
Este mtodo lo tienen todos los enteros de SAGE (cuidado: esto nos obliga a usar srange en vez de range , para
tener enteros de SAGE en vez de enteros de la mquina).
Con este truco, no necesitamos saber el nmero de variables al escribir el cdigo.
sage: #Truco para iterar K variables que pueden tomar el valor 0 o 1
sage: #K no es conocido a priori
sage: K=4
sage: for k in srange(2^K):
... vars = k.digits(base=2,padto=K)
... print vars
[0, 0, 0, 0]
[1, 0, 0, 0]
[0, 1, 0, 0]
[1, 1, 0, 0]
[0, 0, 1, 0]
[1, 0, 1, 0]
[0, 1, 1, 0]
[1, 1, 1, 0]
[0, 0, 0, 1]
[1, 0, 0, 1]
[0, 1, 0, 1]
[1, 1, 0, 1]
[0, 0, 1, 1]
[1, 0, 1, 1]
[0, 1, 1, 1]
[1, 1, 1, 1]
La tcnica anterior permite iterar sobre todas las sublistas de una lista dada. Dada una lista con k elementos, usamos k
variables que pueden tomar los valores 0 y 1. Si la variable xj vale 1, incluimos el elemento j-simo en la sublista y, si
vale 0, no lo incluimos.
Ejercicio: usa la tcnica anterior para mostrar todas las sublistas de una lista dada.
sage: lista = [a,b,c,d]
Tambin podemos usar la funcin powerset , definida en SAGE que hace la misma tarea.
sage: for sublista in powerset(lista):
... print sublista
[]
[a]
[b]
[a, b]
[c]
[a, c]
[b, c]
[a, b, c]
[d]
[a, d]
[b, d]
[a, b, d]
[c, d]
[a, c, d]
[b, c, d]
[a, b, c, d]
Cuntos conjuntos de nmeros enteros entre 2 y K tienen mximo comn divisor igual a 1?
sage: K = 7
sage: numeros = range(2,K+1)
sage: mcd_es_1 = 0
sage: for subconjunto in powerset(numeros):
... if gcd(subconjunto)==1:
... mcd_es_1 += 1
sage: print mcd_es_1
52
Consideramos el ratio entre el nmero de conjuntos de nmeros enteros entre 2 y K cuyo mximo comn divisor es
igual a 1 frente al total de subconjuntos de enteros entre 2 y K: existe el lmite de esta sucesin?
sage: probs = []
sage: for K in range(3,15):
... numeros = range(2,K+1)
... mcd_es_1 = 0
... for subconjunto in Subsets(numeros):
... if gcd(subconjunto)==1:
... mcd_es_1 += 1
... total = 2^(K-1)
... probs.append((K,mcd_es_1/total))
sage: point(probs).show(ymin=0,ymax=1)
Observacin : la funcin powerset devuelve un objeto similar a un generador. Recordamos que al construir un
generador el cdigo no se ejecuta, y no se almacenan en memoria los resultados, sino que slo se van construyendo
segn son necesarios, por ejemplo al iterar powerset en un bucle for. Sin embargo, difiere de un generador en que no
se agota al iterarlo. Cada vez que recorremos el objeto, se crea un generador nuevo.
Los objetos combinatorios suelen admitir una definicin recursiva que permite construir unos a partir de otros. Si
representamos los objetos primordiales (una lista vaca, un rbol con un slo nodo ...) como las races de un rbol, y
utilizamos la definicin recursiva para construir las sucesivas generaciones de nodos, podemos generar los objetos de
forma bastante limpia, recorriendo el rbol que comienza en cada nodo base (sea en recorrido o en profundidad).
Lo importante de este enfoque es concretar la definicin recursiva en una funcin que acepta un nodo como argumento
y devuelve la lista con sus hijos, y separar esta parte de la definicin del recorrido del grafo. Para esta segunda parte
podemos usar por ejemplo la funcin SearchForest definida en Sage, que hace un recorrido en profundidad de
un bosque, que no es otra cosa que una unin disjunta de rboles. Tambin podemos escribir nuestro propio cdigo
genrico para esta tarea.
Como ejemplo, usamos esta idea para construir las particiones de un nmero k: todas las formas de sumar k con
enteros positivos menores que k. Escribimos cada particin de k como una lista de enteros que suma k, y para evitar
repeticiones las listamos slo una vez en orden decreciente. Podemos construir todas las particiones parciales de k,
que son listas decrecientes cuya suma es a lo sumo k. Los hijos de una particin parcial L son las particiones parciales
que comienzan por L y tienen un elemento ms que L. Lo dibujamos como un grafo:
sage: total = 5
sage: def hijos(ls):
... Los hijos de una lista ls son las listas que comienzan por ls
... y tienen un elemento ms que ls
...
... suma = sum(ls)
... minimo = min(ls) if ls else total
... return [ls + [j] for j in range(1, min(total - suma, minimo) + 1)]
sage: hijos([2])
[[2, 1], [2, 2]]
Antes de poder reusar este cdigo, observamos que la funcin hijos usa la variable total definida en el mbito principal.
Para pasar este cdigo a funcin, podemos simplemente anidar la funcin hijos dentro de la nueva funcin para que
Ya hemos encontrado otros ejemplos de este patrn, como por ejemplo generar todos los polinomios de cierto grado
con coeficientes en un cierto rango. Repetimos el ejercicio con esta visin, declarando que los hijos del polinomio p
son los polinomios px+j, para j en el rango de coeficientes en el que trabajamos.
sage: m = 3
sage: R.<x> = PolynomialRing(Integers(m))
sage: pols1 = [R(j) for j in srange(m)]
sage: grado_max = 3
sage: def children(p):
... if p.degree()>=grado_max or p == 0:
... return []
... else:
... return [p*x+j for j in srange(m)]
sage: S = SearchForest(pols1, children)
sage: list(S)
[0, 1, x, x^2, x^3, x^3 + 1, x^3 + 2, x^2 + 1, x^3 + x, x^3 + x + 1, x^3 + x + 2, x^2 + 2, x^3 + 2*x,
En Sage estn definidos muchos objetos combinatorios tiles, que podemos usar y combinar de varias formas. Todos
tienen una estructura uniforme que permite usarlos de varias formas. Todas estos objetos combinatorios son perezosos,
e incluso podemos definir objetos infinitos. En general usan un mtodo para enumerar todos los objetos y otro distinto
para contarlos , que es importante porque en muchos casos se puede contar el nmero de objetos en muy poco tiempo
aplicando frmulas, mientras que enumerarlos todos podra llevar mucho tiempo. En general, usaremos los siguientes
mtodos:
cardinality() para obtener el nmero de elementos
list() para obtener la lista de todos los elementos
random_element() para obtener un elemento aleatorio
Iteraciones del tipo for objeto in Clase para recorrer los objetos sin necesidad de cargarlos todos en
memoria.
Conjuntos y subconjuntos
sage: S2 = Subsets(Subsets(S))
sage: S2.random_element()
{{3}, {4}, {1, 2, 3}, {1, 4}, {}, {2, 3, 4}, {1, 2, 4}, {3, 4}, {1, 3, 4}, {2, 3}, {1, 3}, {2}}
sage: #Por si acaso no te creias que para calcular el cardinal no hace falta
sage: #calcular todos los elementos
sage: S3 = Subsets(Subsets(Subsets(S)))
sage: S3.cardinality()
20035299304068464649790723515602557504478254755697514192650169737108940595563114530895061308809333481
Combinaciones
Dada una lista de tamao k, las combinaciones de j elementos de la lista son todas las posibles sublistas con j elementos
extrados de la lista. En SAGE, podemos recorrer las combinaciones con un generador, de forma similar a la que
usamos para powerset:
for c in Combinations(lista,j):
...haz algo con c...
sage: K=5
sage: for c in Combinations(range(2,K+1),2):
... print c
[2, 3]
[2, 4]
[2, 5]
[3, 4]
[3, 5]
[4, 5]
Ejemplo: Cul es la probabilidad de que dos nmeros elegidos al azar entre 2 y K sean primos entre s?
Usando la frmula:
casos favorables
probabilidad =
casos posibles
slo tenemos que extraer todos los posibles pares de nmeros entre 2 y K, y contar cuntas parejas estn formadas por
dos nmeros primos entre s.
sage: K=5
sage: primos_relativos = 0
sage: for c in Combinations(range(2,K+1),2):
... if gcd(c)==1:
... primos_relativos += 1
sage: print primos_relativos
5
sage: binomial(10,2)
45
... primos_relativos += 1
... total = binomial(K-1,2)
... probs.append((K,primos_relativos/total))
sage: print probs
[(3, 1), (4, 2/3), (5, 5/6), (6, 3/5), (7, 11/15), (8, 2/3), (9, 19/28), (10, 11/18), (11, 31/45), (1
sage: point(probs).show(ymin=0,ymax=1)
Ms
sage: Partitions(5).list()
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
sage: WeightedIntegerVectors(33,[2,5]).list()
[[4, 5], [9, 3], [14, 1]]
5.2 Ejercicios
5.2.1 1.
Escribe cdigo para recorrer todos los posibles valores de un conjunto de variables x1,x2,...,xK, donde x1 toma
los valores 0,1,...,B1-1; x2 toma los valores 0,1,...,B2-1;... y xK toma los valores 0,1,...,BK-1. Los valores B1,...,
BK estn almacenados en una lista.
Utiliza el cdigo anterior para contar el nmero de posibilidades para las que la suma de los valores de las
variables es mayor o igual que un nmero M.
5.2.2 2.
Consideramos matrices 3x3 cuyas entradas son 0 1. El total de matrices de este tipo es 29 = 512. Cuenta el nmero
de estas matrices cuyo determinante es 1 mdulo 2 (es decir, el determinante es impar).
Equivalentemente, cuenta el nmero de matrices 3x3 con coeficientes en Z2 cuyo determinante es no nulo.
5.2.3 3.
Encuentra todas las formas de colocar 8 reinas en un tablero de ajedrez de forma que ninguna reina amenace a otra.
Plantea el problema como un rbol de soluciones parciales partiendo de un tablero vaco y agregando una sla reina
cada vez. Usa SearchForest para recorrer el grafo.
Nota: el enfoque anterior, tomado de forma literal, da lugar a un algoritmo muy ineficiente. Es buena idea asumir de
partida que cada reina tiene que ir en una columna distinta y anotar slo la fila que ocupa la reina i-sima.
5.2.4 4.
Escribe una funcin genrica que recibe una lista de nodos raices y una funcin que construye los hijos y
devuelva todos los nodos del rbol engendrado (al igual que hicimos con SearchForest), pero realizando una
bsqueda en anchura .
Modifica el cdigo anterior para que devuelva slo las hojas (nodos que no tienen hijos).
Aplica esta funcin a algn ejemplo de la hoja.
5.2.5 5.
Escribe cdigo para recorrer todas las posibles listas que constan de 3 elementos extrados de una lista dada, en todas
las ordenaciones posibles . Usa tres estrategias:
Usa la funcin de Sage que extrae listas de tres elementos, y despus usa la funcin de Sage que recorre las
permutaciones de las listas de 3 elementos.
Usa primero la funcin de Sage que recorre las permutaciones de la lista dada, y despus extrae listas de tres
elementos que respetan el orden dado.
Escribe todas las elecciones de sublistas como un rbol cuyo nodo raz es una sublista vaca, y tal que en cada
eleccin eliges un nuevo elemento de la lista. Nota: recuerda lo que significa un elemento mutable en python!
5.2.6 6. Monedas
Nota. Los siguientes problemas de probabilidad se pueden resolver a mano, pero ahora puedes escribir cdigo que
utiliza la frmula:
casos favorables
casos posibles
para calcular la probabilidad contando uno por uno los casos favorables.
Calcula la probabilidad de que, al lanzar 10 monedas (equilibradas), obtengas al menos 4 caras.
Calcula la probabilidad de que, al lanzar 10 monedas (equilibradas), obtengas el mismo nmero de caras con las
5 primeras monedas que con las 5 siguientes.
Calcula la probabilidad de que, al lanzar 15 monedas (equilibradas), las primeras 10 monedas contengan al
menos el doble de caras que las 5 monedas siguientes.
Indicacin: Usa k variables 0/1 para representar un posible lanzamiento de monedas (1:cara, 0:cruz). Para cada apar-
tado, escribe una funcin que acepte como argumento una lista con los valores de las variables y devuelva True si es
un caso favorable y False si no lo es.
5.2.7 7. Cartas
Nos planteamos ahora resolver problemas de cartas. El primer paso es representar las cartas de la baraja. Usaremos la
baraja espaola de 40 cartas para agilizar los clculos.
Por ejemplo, podemos representar cada carta por una tupla que contiene su nmero (de 1 a 10, donde 8, 9 y 10 son las
figuras) y su palo (por ejemplo, usando la baraja espaola: o (oros), c (copas), e (espadas), b (bastos) ).
Crea una lista con las 40 cartas de la baraja espaola, representadas de la forma descrita arriba. Ejemplos: (4,c)
4 de copas; (10,b) 10 de bastos (rey de bastos).
Para calcular una probabilidad con la frmula
casos favorables
casos posibles
necesitamos, por un lado, recorrer todas las posibles extracciones de cinco cartas de una baraja y, por otro lado,
discriminar si una mano de cinco cartas es un caso favorable o no.
Escribe una funcin que acepte como argumento una lista con 5 cartas y devuelva True si esa mano es una
escalera y False si no lo es.
Lo mismo de antes, para un pker.
Calcula la probabilidad de obtener un pker, o una escalera, en la primera mano.
Indicacin : si tu cdigo tarda ms de unos pocos segundos, ser mejor que uses una baraja ms pequea hasta estar
seguro
de que todo funciona. Ten en cuenta que las posibles extracciones de cinco cartas de una baraja espaola son
40
5 = 658008. Usa una baraja con menos palos y menos nmeros, y haz que tu cdigo imprima todos los casos
favorables.
Si lo prefieres, calcula la probabilidad de algunas jugadas de mus. Como las manos slo tienen 4 cartas, hay
muchas menos posibilidades.
En esta sesin vamos a enumerar varias familias de objetos combinatorios, igual que hicimos en la primera sesin,
pero ahora adems de enumerar objetos los contaremos sin enumerarlos, para hacer ms patente la relacin entre una
construccin recursiva y las frmulas recursivas para el nmero de elementos que son habituales en combinatoria. El
primer ejemplo est resuelto, los dos siguientes son ejercicios que resolveremos en clase, y los dos ltimos son una
prctica a entregar.
Estudiamos las formas de descomponer un nmero n como suma de exactamente k enteros positivos. Listamos los
nmeros de cada particin de mayor a menor, para evitar repeticiones. Observamos que las particiones pertenecen a
dos clases:
Las que terminan en 1.
Las que tienen todos sus elementos mayores o iguales a 2.
Esta idea nos permite construir las particiones recursivamente:
Generamos las particiones de n-1 en k-1 partes iguales, y aadimos un 1 al final para dar cuenta del primer grupo
de particiones.
Generamos las particiones de n-k en k partes iguales, y sumamos 1 a cada elemento para dar cuenta del segundo
grupo de particiones.
sage: def particiones_k(total, partes):
... if partes == total:
... return [[1]*total]
... if partes == 1:
... return [[total]]
... if not(0 < partes < total):
... return []
... ls1 = [p+[1] for p in particiones_k(total-1, partes-1)]
... ls2 = [[parte+1 for parte in p] for p in particiones_k(total-partes, partes)]
... return ls1 + ls2
sage: particiones_k(8,3)
[[6, 1, 1], [5, 2, 1], [4, 3, 1], [4, 2, 2], [3, 3, 2]]
Si slo queremos contar el nmero de particiones de n con k elementos (en adelante pk (n)), las reglas de recursin
anteriores se traducen en la siguiente ecuacin de recurrencia:
pk (n) = pk1 (n 1) + pk (n k)
y:
pk (n) = 0 si k 0, k > n, n 0
sage: n_particiones_k(8,3)
5
Obviamente, se tarda menos tiempo en contar las posibilidades que en construirlas todas explcitamente.
sage: time n_particiones_k(60,10)
62740
Time: CPU 0.10 s, Wall: 0.10 s
En el ejemplo anterior hemos transportado la definicin recursiva directamente a nuestro cdigo, pero vimos en el
bloque II que este enfoque nos puede traer problemas, al usar una definicin recursiva para calcular los nmeros de
fibonacci:
def fibo(n):
if n<2:
return 1
return fibo(n-1) + fibo(n-2)
En este ejemplo paradigmtico, estimamos que para calcular el nmero de fibonacci n-simo se hacan aproximada-
mente tantas llamadas recursivas a fibo como el nmero de fibonacci n-simo (ms exactamente, el nmero I(n)
de llamadas a fibo para calcular fibo(n) es: I(n-1)+I(n-2)+1). Usando la tcnica del profiler de la sesin b3s3,
medimos exactamente el nmero de llamadas para confirmar este hecho:
sage: def fibo(n):
... if n<2:
... return 1
... return fibo(n-1) + fibo(n-2)
Observamos la columna ncalls que indica el nmero de llamadas a una funcin. Comprueba para unos pocos valores
que se satisface la frmula de recurrencia mencionada.
sage: #importamos los modulos cProfile y pstats para ver las estadisticas
sage: #de cuanto tiempo se pasa en cada parte del codigo
sage: import cProfile, pstats
sage: #No necesitamos entender la siguiente linea:
En nuestro ejemplo anterior, ocurre algo similar, porque al llamar a particiones_k(n,k) llamamos recursi-
vamente a particiones_k(n-k,k) y a particiones_k(n-1,k-1) , lo que eventualmente implica que
llamaremos varias veces a particiones_k(n,k) con los mismos argumentos.
A modo de confirmacin, el sentido comn nos dice que para calcular n_particiones_k(n,k) , necesita-
remos como mucho n*k llamadas recursivas a n_particiones_k , lo que corresponde a llamar a la funcin
con todos los posibles valores menores que n y con los menores que k. Sin embargo, el nmero de llamadas a
n_particiones_k(40,10) es 9111, mucho mayor que 400.
sage: #importamos los modulos cProfile y pstats para ver las estadisticas
sage: #de cuanto tiempo se pasa en cada parte del codigo
sage: import cProfile, pstats
sage: #No necesitamos entender la siguiente linea:
sage: #tomalo como una version avanzada de timeit
sage: cProfile.runctx("n_particiones_k(40,10)", globals(), locals(), DATA + "Profile.prof")
sage: s = pstats.Stats(DATA + "Profile.prof")
sage: #Imprimimos las estadisticas, ordenadas por el tiempo total
sage: s.strip_dirs().sort_stats("time").print_stats()
Mon Feb 21 22:57:30 2011 /home/sageadm/nbfiles.sagenb/home/pang/295/data/Profile.prof
En otros lenguajes como Haskell , las definiciones anteriores no provocan esta cascada de llamadas recursivas . Esto
se debe a que Haskell es un lenguaje funcional puro, lo que implica entre otras cosas que todas las llamadas a una
funcin con los mismos argumentos producen siempre el mismo resultado. Gracias a este conocimiento, el compilador
no necesita ejecutar una funcin la segunda vez que la llaman con unos argumentos dados, porque ya ha calculado el
valor una vez.
En python, sto no es necesariamente cierto (piensa por ejemplo en las funciones que generan nmeros aleato-
rios), pero podemos crear una funcin que guarde los valores de las llamadas en memoria, usando el decorador
@cached_function , que altera una funcin para que almacene los valores de cada llamada, y as evitar calcular
dos veces el valor de la funcin con los mismos argumentos.
sage: @cached_function
sage: def n_particiones_k_cached(total, partes):
... if partes == total:
... return 1
... if partes == 1:
... return 1
... if not(0 < partes < total):
... return 0
... n1 = n_particiones_k_cached(total-1, partes-1)
... n2 = n_particiones_k_cached(total-partes, partes)
... return n1 + n2
Verificamos que el nmero de llamadas recursivas desciende drsticamente (en este caso, a 248).
sage: #importamos los modulos cProfile y pstats para ver las estadisticas
sage: #de cuanto tiempo se pasa en cada parte del codigo
sage: import cProfile, pstats
sage: #No necesitamos entender la siguiente linea:
sage: #tomalo como una version avanzada de timeit
sage: cProfile.runctx("n_particiones_k_cached(40,10)", globals(), locals(), DATA + "Profile.prof")
sage: s = pstats.Stats(DATA + "Profile.prof")
sage: #Imprimimos las estadisticas, ordenadas por el tiempo total
sage: s.strip_dirs().sort_stats("time").print_stats()
Mon Feb 21 22:57:31 2011 /home/sageadm/nbfiles.sagenb/home/pang/295/data/Profile.prof
Cuestin : Vuelve a ejecutar el cdigo de arriba, sin modificarlo. Explica el resultado. Usa la accin Restart Works-
heet, y ejectalo de nuevo (ejecuta antes el cuadro que define n_particiones_k_cached ). Explica el resultado.
5.3.3 Ejercicios
Estudiamos las palabras formadas por 0 y 1 tales que no hay dos unos consecutivos. Hay dos palabras de longitud 1
con esta propiedad:0 y 1, tres de longitud 2: 00, 01, 10, 5 de longitud 3: 000, 001, 010, 100, 101. Las
palabras se pueden generar de forma recursiva siguiendo la regla siguiente:
Las palabras que terminan en 0 se pueden extender con un 0 o con un 1
Las palabras que terminan en 1 slo se pueden extender con un 0.
Tambin puedes seguir la siguiente regla equivalente para generar todas las palabras de longitud k:
Aade un 0 a todas las palabras de longitud k-1.
Aade un 01 a todas las palabras de longitud k-2.
Ejercicio
Escribe cdigo que genere todas las palabras de este tipo hasta longitud k.
Escribe (y justifica) una ecuacin de recursin para el nmero total de palabras de este tipo: te resulta familiar?
Construye los caminos colocando las palabras en un rbol y usando SearchForest. Los hijos de una palabra
que termina en 0 son la misma palabra seguida de 0 y de 1, mientras que el nico hijo de una palabra que
termina en 1 es la misma palabra seguida de 0. En este caso el bosque tiene dos nodos races, el 0 y el 1.
El tringulo de Pascal registra el nmero de caminos desde el vrtice superior hasta un vrtice cualquiera, tales que en
cada paso descendemos de una fila a la inmediatamente inferior mediante una de las aristas descendientes: bien a la
izquierda bien a la derecha.
Codificamos los caminos como secuencias con las letras I y D. Por ejemplo el camino que est arriba a la izquierda del
diagrama corresponde a la secuencia IDDD, mientras que el camino de abajo a la derecha corresponde a DDDI.
La siguiente recursin permite encontrar todos los caminos que llevan del vrtice superior al vrtice en la fila n y la
columna k:
Encuentra todos los caminos que llevan a la posicin (n-1, k-1) y adeles una D.
Encuentra todos los caminos que llevan a la posicin (n-1, k) y adeles una I.
Ejercicio :
Aplica la recursin anterior para calcular todos los
caminos que llevan del vrtice superior al vrtice en la fila n
y la columna k. Deberas obtener exactamente nk tales caminos.
Escribe (y justifica) una ecuacin de recursin para el nmero total de caminos basada en la recursin anterior:
te resulta familiar?
Construye los caminos colocando los caminos parciales en un rbol y usando SearchForest. Los caminos par-
ciales son caminos desde el vrtice superior hasta otro vrtice que son susceptibles de ser continuados hasta el
vrtice (n,k) siguiendo las reglas del juego.
5.3.4 Entrega b4
Los dos ejercicios que siguen constituyen la entrega del bloque IV, que por supuesto es opcional. Vale un total de 7
puntos.
Hay dos formas de agrupar tres letras (por ejemplo, para realizar una operacin matemtica asociativa sobre varios
smbolos, pero sin alterar el orden de las letras)
(a(bc)), ((ab)c)
La siguiente recursin permite encontrar todas las formas. Partimos de una secuencia de letras, abcd:
Partimos la secuencia en todos los posibles puntos: [a, bcd], [ab, cd], [abc,d]
Agrupamos cada parte recursivamente de todas las formas posibles. Por ejemplo, bcd se puede agrupar como
(b(cd)) y como ((bc)d)
Unimos cada forma de agrupar las letras de la izquierda con cada forma de agrupar las letras de la derecha, y
encerramos el resultado entre parntesis. Ej, a, ((bc)d) -> (a((bc)d)) (las letras sueltas no necesitan parn-
tesis)
Ejercicio
Aplica la recursin anterior para construir todas las formas de agrupar n letras.
Escribe (y justifica) una ecuacin de recursin para el nmero total de posibilidades. Cuenta el nmero de
posibilidades G(n) para n desde 1 hasta 10.
Caminos montonos
Consideramos ahora los caminos montonos , en la forma siguiente: todos los caminos que unen el vrtice (0,0) con
el vrtice (n,n) avanzando hacia la derecha (D) y hacia arriba (A), tales que en todo momento hemos avanzado al
menos tantos pasos hacia la derecha como hemos avanzado hacia arriba.
Sea C(n) el nmero total de caminos que llegan al vrtice (n,n) partiendo del vrtice (0,0). Llamaremos a estos caminos
diagonales porque terminan en la misma diagonal en la que empiezan.
Cmo podemos encontrar una frmula de recursin para C(n)? Observamos que todos los caminos siguen la siguiente
estructura: comienzan con un movimiento a la derecha, luego sigue un camino diagonal (posiblemente vaco), luego
viene un movimiento hacia arriba que compensa el primer movimiento a la derecha, y luego viene otro camino diagonal
(posiblemente vaco). En el siguiente diagrama, los puntos rojo y verde marcan el principio y el fin del primer camino
diagonal.
Ejercicio
Aplica la recursin anterior para construir todos los caminos . Codifica los caminos como secuencias con las
letras D y A, que indican en qu momentos debemos movernos a la derecha o hacia arriba. Por ejemplo, el
diagrama de abajo a la derecha corresponde al camino DADADADA, y el de arriba a la izquierda al camino
DDDDAAAA .
Escribe (y justifica) una ecuacin de recursin para el nmero total de caminos de este tipo. Calcula C(n) para n
desde 1 hasta 10.
Sustituye la letra D por el carcter ( y la letra A por el carcter ). Describe el resultado.
Los ejemplos estn extrados del captulo 6 del libro de texto de la asignatura de matemtica discreta, que puedes
consultar como referencia:
http://www.uam.es/personal_pdi/ciencias/gallardo/cap6-MD-2010-2011.pdf
del libro de Sage en francs:
http://sagebook.gforge.inria.fr/
y de la wikipedia:
http://en.wikipedia.org/wiki/Catalan_number
5.3.5 Entrega b4
Los dos ejercicios que siguen constituyen la entrega del bloque IV, que por supuesto es opcional. Vale un total de 7
puntos.
5.4 Grafos
En esta leccin vamos a estudiar las posibilidades que ofrece SAGE para trabajar con grafos .
Un grafo consiste de un conjunto de vrtices y otro conjunto de aristas que unen algunos de los vrtices. En un grafo
no dirigido las aristas no tienen direccin, mientras que en los grafos dirigidos debemos distinguir entre la arista que
une el vrtice v1 con el v2 de la arista que une el vrtice v2 con el v1.
Existen constructores para muchos de los grafos no dirigidos ms famosos, dentro del mdulo graphs . Por ejemplo:
Grafo completo: el que tiene todas las aristas que unen cada par de vrtices
Grafo cclico: una circuito simple
Grafo del n-cubo
...
sage: g1 = graphs.CompleteGraph(5)
sage: show(g1.plot())
sage: g2 = graphs.CycleGraph(4)
sage: show(plot(g2))
sage: g3 = graphs.CubeGraph(3)
sage: show(plot(g3))
El dibujo anterior confunde dos de los vrtices. Leemos la documentacin de Graph.plot para encontrar la forma de
mejorar el dibujo
sage: g3.plot?
<html>...</html>
Como de costumbre, usando el tabulador accedemos a una lista completa con todos los grafos disponibles.
sage: graphs.
Traceback (most recent call last):
...
SyntaxError: invalid syntax
Tambin podemos introducir un grafo usando la matriz de adyacencia : una matriz KxK (donde K es el nmero de
vrtices), que tiene un 1 en la posicin i,j si y slo si hay una arista entre los vrtices i y j . Para grafos no dirigidos, la
matriz debe ser simtrica.
Nota : En la documentacin de Graph puedes encontrar otras formas de introducir un grafo (por ejemplo, mediante
un diccionario asigna a cada vertice la lista de sus vecinos).
sage: M = matrix([[0,1,0,0],[1,0,0,0],[0,0,0,1],[0,0,1,0]])
sage: show(M)
sage: g4 = Graph(M)
sage: show(g4, layout=circular)
0 1 0 0
1 0 0 0
0 0 0 1
0 0 1 0
Podemos construir del mismo modo un grafo dirigido, con una matriz no necesariamente simtrica, usando DiGraph
.
sage: M = matrix(ZZ,[[0,0,0,0],[1,0,0,0],[0,0,0,1],[0,0,1,0]])
sage: show(M)
sage: g4 = DiGraph(M)
sage: show(g4, layout=circular)
0 0 0 0
1 0 0 0
0 0 0 1
0 0 1 0
sage: g6 = g4*3
sage: show(g6,layout=circular)
Podemos aadir y quitar vrtices y aristas a grafos existentes usando add_vertex , add_edge , delete_vertex
, delete_edge .
sage: g7 = 2*g2
sage: show(g7,layout=spring)
sage: g7.add_edge(0,4)
sage: g7.add_edge(1,5)
sage: g7.add_edge(2,6)
sage: g7.add_edge(3,7)
sage: show(g7,layout=spring)
Ejercicio
Modifica el grafo g6 aadiendo un vrtice y uniendo todos los otros vrtices al nuevo vrtice.
Podemos verificar un buen nmero de propiedades de un grafo usando los mtodos adecuados. Por ejemplo:
is_connected : comprueba si el grafo es conexo
is_planar : comprueba si el grafo es plano . Un grafo es plano si se pueden dibujar los vrtices y las aristas
en un plano sin que las aristas se intersequen.
is_eulerian : comprueba si el grafo tiene un circuito euleriano . Un circuito en un grafo es una sucesin
de aristas adyacentes que comienza y termina en el mismo vrtice. Un circuito euleriano es un circuito que pasa
exactamente una vez por cada arista.
is_tree : comprueba si el grafo es un rbol . Un rbol es un grafo conexo que no tiene ningn circuito
cerrado .
sage: print g1.is_connected()
sage: print g1.is_planar()
sage: print g1.is_eulerian()
sage: print g1.is_tree()
sage: show(g1.plot())
True
False
True
False
Criterio de Euler
Segn el criterio de Euler, un grafo tiene un circuito euleriano si y slo todos los vrtices tienen grado par.
Ejercicio
Dos grafos son isomorfos si y slo si existe una biyeccin del conjunto de vrtices del primero en el conjunto de
vrtices del segundo tal que dos vrtices estn unidos en el primer grafo si y slo si los vrtices correspondientes estn
unidos en el segundo.
En dos grafos isomorfos, los vrtices pueden tener nombres distintos y estar colocados en distintas posiciones, pero
todas las relaciones de incidencia y todas las propiedades de grafos como conexin, planaridad etcetera son idnticas.
sage: print g7.is_isomorphic(g3)
sage: print g7 == g3
sage: print g3.vertices()
Pregunta: cul de los grafos que definimos antes es isomorfo al siguiente grafo?
sage: M = matrix(ZZ,[[0,1,1,0],[1,0,0,1],[1,0,0,1],[0,1,1,0]])
sage: show(M)
sage: g9=Graph(M)
sage: show(g9,layout=circular)
0 1 1 0
1 0 0 1
1 0 0 1
0 1 1 0
La funcin graphs genera un grafo de cada clase de isomorfismo de grafos con un cierto nmero de vrtices.
sage: for g in graphs(4):
... if g.is_connected():
... print g.adjacency_matrix()
... print
[0 1 1 1]
[1 0 0 0]
[1 0 0 0]
[1 0 0 0]
[0 1 1 0]
[1 0 0 0]
[1 0 0 1]
[0 0 1 0]
[0 1 1 0]
[1 0 1 0]
[1 1 0 1]
[0 0 1 0]
[0 1 1 0]
[1 0 0 1]
[1 0 0 1]
[0 1 1 0]
[0 1 1 0]
[1 0 1 1]
[1 1 0 1]
[0 1 1 0]
[0 1 1 1]
[1 0 1 1]
[1 1 0 1]
[1 1 1 0]
sage: L = list(graphs(4))
Podemos generar todos los grafos con un cierto nmero de vrtices y contar el nmero de ellos que verifican una cierta
propiedad.
sage: L = [g for g in graphs(4) if g.is_connected()]
sage: print len(L)
sage: graphs_list.show_graphs(L)
6
5.5 Ejercicios
5.5.1 1.
Encuentra un subgrafo inducidos del grafo graphs.CubeGraph(4) que sea isomorfo al grafo
graphs.CubeGraph(3)
Cuntos posibles grafos existen con un conjunto de vrtices dado de tamao k? Cuntos digrafos (grafos dirigidos)?
Cuntos posibles grafos no isomorfos existen con exactamente k vrtices, para k<=7?
Cuntos posibles digrafos conexos no isomorfos existen con 4 vrtices ?
Cuntos grafos hay con 5 vrtices que no sean planos? Dibjalos todos:
Referencia: http://es.wikipedia.org/wiki/Grafo_plano
Una coloracin de un grafo es una asignacin de un color a cada vrtice del grafo de modo que dos vrtices adyacentes
tienen asignados colores distintos. Las coloraciones ms interesantes son las que tienen menor nmero de colores. Un
conocido teorema afirma que todos los grafos planos se pueden colorear con a lo sumo 4 colores.
Explora la ayuda de los mtodos coloring y plot de los grafos para conseguir dibujar grafos junto con una
coloracin que tiene el mnimo nmero de colores.
Encuentra grafos con k vrtices que no se puedan colorear con menos de j colores, para j<k y k hasta 6.
Cul es el nmero cromtico del grafo graphs.LollipopGraph(a,b) ?
Cul es el nmero cromtico del grafo graphs.BarbellGraph(a,b) ? Cul es su nmero de vrtices?
Dada una lista de nmeros S, construye el grafo de divisores de S, cuyos vrtices son los nmeros de S, y que tiene
una arista desde j hasta k si y slo si j es divisor de k j es divisor de k.
5.5.6 6.
Calcula el nmero de aristas del grafo de divisores para las listas S de la forma range(2,k), para distintos valores de k
y dibjalas en una grfica.
5.5.7 7.
En la primera sesin de este bloque aprendimos a recorrer exhaustivamente un conjunto de posibilidades para contar
las que satisfacan una propiedad, y as calcular probabilidades como el nmero de casos favorables dividido por el
nmero de casos posibles. En algunos casos el nmero de posibilidades es enorme, y el resultado, realmente mucho
ms preciso de lo que necesitbamos.
Una alternativa consiste en calcular slo unos cuantos casos extrados aleatoriamente, y hacer una estadstica de los
casos favorables entre los posibles. A esta estrategia se le llama en general el mtodo de Monte Carlo, por su uso del
azar. Aunque el resultado slo es aproximado, es fcil controlar el tiempo de cmputo que destinamos a aproximar la
solucin.
Por supuesto, en muchos casos es posible hacer el clculo exacto mediante razonamiento puro y, an en los casos en
que no es as, el razonamiento puede servir para reducir el nmero de posibilidades a explorar, o hacer ms eficiente
un mtodo de Monte Carlo.
Como ejemplo, nos planteamos este problema: partiendo de una masa para hacer galletas que contiene un total de P
pasas, hacemos G galletas. Asumiendo que cada pasa terminar en una de las G galletas con la misma probabilidad y
de forma independiente a la galleta de destino de las otras pasas, calcula la probabilidad de que cada galleta tenga al
menos k pasas.
Para distribuir las pasas de todas las formas posibles, contamos cada posible lista de P valores
[G1 , G2 , . . . , GP ]
donde el primer valor indica la galleta en que termina la primera pasa, y as sucesivamente. Como cada pasa puede
terminar en una de las G galletas posibles, el nmero total de posibilidades es GP .
sage: def pasas(G,P,k):
... Calcula cuantas formas de repartir P pasas entre G galletas
... dejan al menos k pasas en cada galleta
...
... favorables = 0
... #Cada pasa termina en una galleta distinta, total G^P posibilidades
... for j in srange(G^P):
... #lista que indica en que galleta esta cada pasa
... donde_esta_la_pasa = j.digits(base=G,padto=P)
... #G galletas, que comienzan sin pasas
... pasas_en_cada_galleta = [0]*G
... #Contamos el numero de pasas en cada galleta
... for g in donde_esta_la_pasa:
... pasas_en_cada_galleta[g] += 1
... if min(pasas_en_cada_galleta)>=k:
... favorables += 1
... return favorables/G^P
sage: %time
sage: pasas(4,6,1)
195/512
CPU time: 0.17 s, Wall time: 0.19 s
sage: %time
sage: pasas(3,7,1)
602/729
CPU time: 0.09 s, Wall time: 0.10 s
sage: %time
sage: pasas(4,7,1)
525/1024
CPU time: 0.66 s, Wall time: 0.67 s
Como vemos, este enfoque requiere mucho tiempo incluso para cantidades pequeas de pasas y de galletas. No en
vano hay que iterar el bucle principal G^P veces!!. Como el problema no tiene mucha gracia si el nmero de pasas
no es al menos igual al nmero de galletas, estamos hablando de crecimiento exponencial. Aunque se pueden usar
varios trucos para reducir el tiempo de cmputo, sigue siendo un crecimiento muy rpido.
Usando el mtodo de Monte Carlo, decidimos exactamente el nmero de veces que iteramos el bucle.
Nota: este problema se puede resolver de forma exacta, pero no es trivial.
Nota: la lista de galletas de cada pasa contiene informacin redundante para este problema, en el que slo nos interesa
el total de pasas en cada galleta. Por ejemplo, podemos usar IntegerVectors para recorrer las listas de enteros
de longitud G que suman P (el nmero de pasas en cada galleta), pero distintas formas de recorrer el conjunto pueden
responder a distintos modelos de probabilidad.
sage: def pasas_mc(G,P,k,T):
... Calcula cuantas formas de repartir P pasas entre G galletas
... dejan al menos k pasas en cada galleta, usando un mtodo de
... Monte Carlo con muestra de tamao T
...
... favorables = 0
... for h in xrange(T):
... #lista que indica en que galleta esta cada pasa
... #Usamos randint que devuelve un entero escogido
... #aleatoriamente en un rango
... donde_esta_la_pasa = [randint(0,G-1) for j in range(P)]
... #G galletas, que comienzan sin pasas
... pasas_en_cada_galleta = [0]*G
... #Contamos el numero de pasas en cada galleta
... for g in donde_esta_la_pasa:
... pasas_en_cada_galleta[g] += 1
... if min(pasas_en_cada_galleta)>=k:
... favorables += 1
... return favorables/T
5.6.2 Simulaciones
El mtodo de Monte Carlo permite abordar problemas que no se pueden resolver por fuerza bruta contando todas las
posibilidades.
En el siguiente ejemplo sencillo, lanzamos monedas al aire (monedas equilibradas, lanzamientos independientes) hasta
que acumulamos un cierto nmero de caras, cuntos lanzamientos necesitamos en promedio para obtener al menos k
caras?
Podemos simular este experimento usando llamadas a randint(0,1) para simular lanzamientos de monedas. Si
el nmero devuelto es 0, lo tomamos como una cruz, si es un 1, como una cara. Repetimos el experimento hasta que
acumulemos k caras, y tomamos nota del nmero de lanzamientos que hemos necesitado.
sage: def geometrica_mc(k,T):
... Nmero medio de lanzamientos hasta obtener k caras, por
... el mtodo de Monte Carlo
...
... lanzamientos = 0
... for j in range(T):
... caras = 0
... while caras<k:
... lanzamientos += 1
... caras += randint(0,1)
...
... promedio = lanzamientos/T
... return promedio
sage: m = geometrica_mc(4,10000)
sage: print m, m.n()
79953/10000 7.99530000000000
En este caso, obtenemos la confirmacin no de la fuerza bruta, sino del razonamiento, porque el nmero esperado de
lanzamientos es claramente 2k (hecho que sabris demostrar cuando estudiis la binomial negativa en probabilidad).
Cuntas matrices cuadradas cuyas entradas sean 0 1 son invertibles en Z2 ? Podemos dar a este enunciado una
interpretacin probabilista: cul es la probabilidad de que una matriz KxK en Z2 cuyas entradas han sido escogidas
de forma aleatoria e independiente entre 0 y 1 sea invertible?
Es fcil generar una matriz cuadrada cuyas entradas son enteros escogidos aleatoriamente entre 0 y 1. Para calcular
su determinante en Z2 , calculamos su determinante habitual y tomamos el resto de dividir por 2. Alternativamente,
podramos usar el cuerpo Integers(2) y su mtodo random_element() .
Recordamos una forma cmoda de construir matrices en Sage:
M = matrix(ZZ, K1, K2, lista)
donde K1 es el nmero de filas y K2 el nmero de columnas y, en vez de pasar una lista con las filas, pasamos una
sla lista con K1xK2 elementos.
Tambin podemos usar random_matrix , pero es un mtodo menos general.
sage: K=6
sage: #Generar una matriz con entradas 0 o 1 escogidas de forma aleatoria
sage: lista = [randint(0,1) for j in range(K*K)]
sage: M = matrix(ZZ,K,K, lista)
sage: print M, det(M) %2
[0 0 1 0 0 1]
[0 0 1 0 1 1]
[1 1 1 1 1 0]
[1 0 1 0 0 1]
[1 0 1 0 1 1]
[0 1 0 1 1 0] 0
Ejercicio
Calcula la probabilidad por fuerza bruta contando los casos favorables y por el mtodo de Monte Carlo.
En esta sesin hemos usado los generadores de nmeros aleatorios del ordenador, llamando a las funcioens randint y
random . Nuestros resultados se basan en que los nmeros generados sean lo bastante aleatorios. Vamos a comprobar
esta afirmacin haciendo estadsticas de los nmeros obtenidos.
Comenzamos con randint , generando nmeros enteros aleatorios, guardando la frecuencia con que aparece cada
resultado, y mostrando las frecuencias en un grfico de barras.
sage: L = 10
sage: T = 1000
sage: frecuencias = [0]*L
sage: for j in range(T):
... k = randint(0,L-1)
... frecuencias[k] += 1
sage: #Mediante ymin=0, indicamos que muestre las graficas
sage: #partiendo desde el eje de las x
sage: #(prueba a quitarlo y veras que pasa)
sage: bar_chart(frecuencias,width=1).show(ymin=0)
Para comprobar random, nos limitamos a separar los nmeros entre 0 y 1 en L subintervalos distintos, y luego
comprobar que en cada caja tenemos aproximadamente 1/L nmeros. No tenemos teora suficiente para hacer tests
ms sofisticados.
sage: L = 10
sage: T = 1000
sage: frecuencias = [0]*L
sage: for j in range(T):
... a = random()
... k = floor(a*L)
... frecuencias[k] += 1
sage: #Mediante ymin=0, indicamos que muestre las graficas
sage: #partiendo desde el eje de las x
sage: #(prueba a quitarlo y veras que pasa)
sage: bar_chart(frecuencias,width=1).show(ymin=0)
En realidad, los nmeros generados con randint y random son pseudo-aleatorios: son aleatorios a efectos prcticos
pero estn generados de manera casi determinista: primero se escoge una semilla , y a partir de ah se usa una funcin
de apariencia aleatoria para obtener el siguiente nmero aleatorio a partir del anterior.
Ms detalles (en ingls) en:
http://docs.python.org/library/random.html
Si fijamos la semilla, todos los clculos posteriores con numeros aleatorios generan los mismos nmeros, aunque
lo ejecutemos en distintos momentos o en distintos ordenadores. Esto es importante para reproducir exactamente un
clculo con nmeros aleatorios.
sage: #Fijamos la semilla para generar nmeros aleatorios
sage: set_random_seed(123)
sage: print random()
sage: print random()
sage: print random()
sage: print randint(1,10)
sage: print randint(1,10)
sage: print randint(1,10)
0.220001316661
0.778780038062
0.0648345056353
8
2
1
5.7 Ejercicios
5.7.1 1.
el rea del crculo unidad dividido entre el rea del cuadrado unidad.
Utiliza esta idea para aproximar el nmero
5.7.2 2.
Estima mediante el mtodo de Monte Carlo las probabilidades de diversos eventos relacionados con lanzamientos de
monedas que calculaste en el ejercicio de la seccin 1.
5.7.3 3.
Los paquetes de cereales ACME traen seis tipos distintos de regalos. Fulanito est dispuesto a comprar cajas de
cereales hasta tenerlos todos.
Asumiendo que la probabilidad de encontrar cada tipo de regalo en una caja de cereales es la misma, e independiente
de las otras cajas de cereales, estima mediante el mtodo de Monte Carlo el nmero de cajas que tendr que comprar
Fulanito hasta tener al menos un regalo de cada tipo.
5.7.4 4. Cumpleaos
Estima mediante el mtodo de Monte Carlo la probabilidad de que en un grupo de 30 personas, al menos dos de ellas
cumplan aos el mismo da.
Para ello, asume que cada persona del grupo tiene un cumpleaos elegido aleatoriamente entre 1 y 365, y que el
cumpleaos de cada uno es independiente del cumpleaos de los dems.
Nota: es posible resolver este problema por razonamiento, pero no por fuerza bruta. El nmero de posibles fechas de
cumpleaos para las 30 personas es 36530 7 1076 .
Generamos un grafo aleatorio de la forma siguiente: partimos de un conjunto de k vrtices y, con probabilidad p ,
ponemos una arista entre cada dos vrtices, haciendo cada eleccin de forma independiente. Nos preguntamos cul es
la probabilidad de obtener un grafo con una cierta propiedad.
Calcula la probabilidad de obtener un grafo plano cuando k=6, p=0.6 y cuando k=12, p=0.3.
Calcula la probabilidad de obtener un grafo conexo cuando k=6, p=0.5.
Indicacin : construye la matriz de incidencia de forma aleatoria, con las entradas 0 y 1. Ten cuidado, porque la matriz
debe ser simtrica. Si decides que entre los vrtices j y k debe haber una arista, tienes que poner unos en las entradas
M[j,k] y M[k,j] de la matriz de incidencia. Alternativamente, busca cdigo en Sage que genera estos grafos.
Una introduccin al clculo simblico. En primer lugar se estudian las expresiones simblicas, y las ecuaciones, y
el comando solve. Despus se aplica lo aprendido al clculo en una y varias variables. La ltima sesin es una
introduccin al estudio de las curvas en el plano.
Una variable simblica es un objeto en python que representa una incgnita que puede tomar cualquier valor. Por
tanto, una operacin que involucra una o ms variables simblicas no devuelve un valor numrico, sino una expresin
simblica que involucra nmeros, operaciones, funciones y variables simblicas. Si en algn momento sustituimos las
variables simblicas por nmeros (o elementos de un anillo), podremos realizar las operaciones y obtener un nmero
(o un elemento de un anillo).
Esta forma de trabajar se corresponde con los criterios de no hacer las operaciones hasta el final, dejar las opera-
ciones indicadas y cancelar trminos antes de evaluar que aprendistis en secundaria.
sage: #Declaramos las variables a b c
sage: var(a b c)
sage: #Definimos varias expresiones simbolicas
sage: s = a + b + c
sage: s2 = a^2 + b^2 + c^2
sage: s3 = a^3 + b^3 + c^3
sage: s4 = a + b + c + 2*(a + b + c)
sage: p = a*b*c
sage: #Las imprimimos como cdigo
sage: print s
sage: print s2
sage: print s3
sage: print s4
sage: print p
sage: #Las mostramos en el formato matemtico usual
sage: show(s2)
sage: show(s3)
a + b + c
a^2 + b^2 + c^2
a^3 + b^3 + c^3
145
Laboratorio de Matemticas, Release 2010/2011
a2 + b2 + c2
a3 + b3 + c3
1 3 1 1 1 1
(a + b + c) + a3 + b3 + c3 (a + b + c) a2 + b2 + c2
6 3 3 3 2
..index:: simplifciar, expand, factor, simplify_rational, simplify_trig, simplify_exp
Simplificar expresiones
Sin embargo, pese a que no es posible evaluar una expresin hasta el final, observamos que al crear la expresin
se han realizado de oficio algunas simplificaciones triviales. En ejemplos como el de arriba, nos puede interesar
simplificar la expresin todava ms, pero es necesario decir qu queremos exactamente. Existen varias estrategias
para intentar simplificar una expresin, y cada estrategia puede tener ms o menos xito dependiendo del tipo de
expresin simblica. Algunas estrategias dan lugar a una expresin ms sencilla en algunos casos pero no en otros, y
con expresiones complicada pueden consumir bastante tiempo de proceso.
Para la expresin anterior, como tenemos un polinomio, es buena idea expandirla en monomios que se puedan compa-
rar unos con otros, usando el mtodo expand .
sage: show(ex.expand())
abc
A menudo nos interesa lo contrario: factorizar la expresin usando factor .
sage: p = a^3 + a^2*b + a^2*c + a*b^2 + a*c^2 + b^3 + b^2*c + b*c^2 + c^3
sage: show(p)
sage: show(p.factor())
(a + b + c) a2 + b2 + c2
Si observas con el tabulador los mtodos de las expresiones regulares, vers que hay mtodos especficos para expre-
siones con funciones trigonomtricas, exponenciales, con radicales o fracciones (es decir, con funciones racionales).
sage: p = sin(3*a)
sage: show(p.expand_trig())
3 2
sin (a) + 3 sin (a) cos (a)
2
2 cos (a) + 1
Funciones simblicas
A medio camino entre las expresiones simblicas y las funciones de python, las funciones simblicas son expresiones
simblicas en las que se fija el orden de las variables de entrada. Esto elimina la ambigedad sobre qu variables se
deben sustituir por qu argumentos y permite usarlas en sitios donde se espera una funcin de python.
sage: f(a,b,c) = a + 2*b + 3*c
sage: f(1,2,1)
8
En algunos aspectos las expresiones simblicas pueden parecer similares a los polinomios que vimos en el bloque III,
lo que puede provocar confusin. Es importante saber si tenemos entre manos una expresin simblica que representa
un polinomio como sta:
var(x)
p = x^2 + 1
Aunque ambos tienen mtodos de propsito similar como coefficients o roots , el comportamiento es distinto,
a veces de manera obvia y a veces de manera ms sutil. Por ejemplo, las races o los coeficientes de una expresin
simblica son a su vez expresiones simblicas, mientras que en el caso de un polinomio son miembros del anillo de
coeficientes.
sage: var(x)
sage: p = x^2 + 3
sage: print p.coefficients()
sage: print p.roots()
sage: termino_indi = p.coefficients()[0][0]
sage: print termino_indi, parent(termino_indi)
[[3, 0], [1, 2]]
[(-I*sqrt(3), 1), (I*sqrt(3), 1)]
3 Symbolic Ring
El mtodo polynomial permite construir un polinomio con coeficientes en un anillo dado a partir de una expresin
simblica. Para hacer lo contrario, podemos sustituir la variable del anillo de polinomios por una variable simblica,
o convertir explcitamente al tipo de datos SR (el anillo de expresiones simblicas, o Symbolic Ring).
sage: p.polynomial(ZZ).roots()
[]
sage: q0 = q(t=x); q0
x^2 + 3.0
sage: q1 = SR(q); q1
t^2 + 3.0
Si usamos el operador de igualdad (==) o algn operador de comparacin (<, <=, >, >=, !=) sobre dos expresiones sim-
blicas, obtenemos una relacin simblica. Al igual que las expresiones simblicas, las operaciones en una relacin
estn indicadas pero no se pueden ejecutar hasta que todas las variables han tomado un valor concreto.
Para evaluar la relacin y obtener un booleano, usamos el operador de conversin a booleano bool(relacion) .
Algunas relaciones tienen un valor definido, incluso aunque dependan de variables simblicas: son siempre ciertas o
siempre falsas. Muchas otras tendrn un valor indeterminado: dependiendo de los valores de las variables. En estos
casos, el valor booleano de la relacin es False , siguiendo una convencin usual en otros programas de clculo
simblico. Tambin obtenemos False si evaluamos una expresin demasiado compleja para el motor simblico.
Tomamos buena nota de la asimetra en los resultados:
True : el programa puede demostrar que la relacin se verifica siempre, para cualquier valor de las variables.
False : el programa no puede demostrar que la relacin se verifica siempre, para cualquier valor de las varia-
bles. Eso no significa que nunca se verifique, ni siquiera que sea ambigua (cierta para algunos valores de las
variables y falsa para otros).
sage: var(a b)
sage: r1 = (a<a+1)
sage: r2 = (a<a-1)
sage: r3 = (a<b)
sage: r4 = (b<a)
sage: r5 = a > 0
Para distinguir entre relaciones que se puede demostrar que son falsas y relaciones ambiguas (o demasiado complicadas
para el software), podemos usar la negacin de las relaciones. Vemos por ejemplo, que se puede demostrar que r2 es
siempre falsa porque su negacin se resuelve a True .
sage: print r2.negation(), bool(r2.negation())
sage: print r3, bool(r3)
sage: print r3.negation(), bool(r3.negation())
a >= a - 1 True
a < b False
a >= b False
Por supuesto concretando los valores de algunas variables, una relacin ambigua se puede concretar, y resultar cierta
(o falsa).
sage: print bool(r2(a=1, b=2)), bool(r3(a=1, b=2))
sage: print bool(r2(a=3, b=2)), bool(r3(a=3, b=2))
sage: print bool(r5(a=e^b))
False True
False False
True
Aparte de preguntar por su valor de verdad, podemos simplificar y operar con las relaciones de forma similar a las
formas de operar con una expresin cualquiera:
sage: r1 = (a<a+1)
sage: print r1.subtract_from_both_sides(a)
0 < 1
sage: print r3
sage: print r4
sage: r6 = r3 + r4
sage: print r6
sage: r6 = r6.subtract_from_both_sides(r6.right_hand_side())
sage: print r6
a < b
b < a
a + b < a + b
0 < 0
El comando solve permite resolver ecuaciones (al menos lo intenta): para resolver una ecuacin llamamos a solve
con la ecuacin como primer argumento y la variable a despejar como segundo argumento, y recibimos una lista con
las soluciones.
sage: x = var(x)
sage: solve(x^2 + 3*x + 2 == 0, x)
[x == -2, x == -1]
Si en vez de pasar una igualdad pasamos una expresin r , calcula las soluciones de r==0 .
sage: #Las soluciones de esta ecuacion cubica son numeros complejos
sage: solve(x^3 - 3*x + 5, x)
[x == -1/2*(1/2*sqrt(21) - 5/2)^(1/3)*(I*sqrt(3) + 1) - 1/2*(-I*sqrt(3) + 1)/(1/2*sqrt(21) - 5/2)^(1/
A veces solve no puede resolver la ecuacin, en cuyo caso puede que la simplifique un poco, o ni siquiera so.
sage: solve(x^5 + 4*x^3 == x^3 + 2, x)
[0 == x^5 + 3*x^3 - 2]
Ante una ecuacin arbitraria de quinto grado, podemos intentar encontrar las soluciones de forma numrica (volvere-
mos a hablar de este tema).
sage: soluciones = solve(x^5 + 4*x^3 == x^3 + 2, x)
sage: s = soluciones[0]
sage: print s
sage: show(plot(s, x, 0, 1)) #dibujamos s entre 0 y 1
sage: print s.find_root(0,1) #buscamos una raiz (numerica) entre 0 y 1
0 == x^5 + 3*x^3 - 2
0.816997207347
solve trabaja con expresiones con muchas variables simblicas, e intenta despejar la variable que le pasamos como
argumento y expresarla como funcin de las otras
sage: var(x y)
sage: for expresion in solve(x^4 + y^2*x^2 + 2, x ):
... show(expresion)
sage: for expresion in solve(x^4 + y^2*x^2 + 2, y ):
... show(expresion)
1
qp
x= y4 8 y2 2
2
1
qp
x= y4 8 y2 2
2
qp
1
x= I y4 8 + y2 2
2
qp
1
x= I y4 8 + y2 2
2
x4 2
y=
x
x4 2
y=
x
Sistemas de ecuaciones
Tambin podemos usar solve para resolver sistemas de ecuaciones (lineales o no). Como primer argumento pasamos
una lista de ecuaciones y despus sigue la lista de variables que queremos despejar.
Aplicar solve a sistemas lineales de ecuaciones es totalmente equivalente a resolver el sistema mediante matrices ,
aunque las distintas posibilidades (incompatible, compatible determinado...) se muestran de forma distinta.
sage: var(x1 x2 x3)
(x1, x2, x3)
]
(1/2, -1/2, 0)
Vector space of degree 3 and dimension 1 over Rational Field
Basis matrix:
[ 1 -3 -2]
sage: #Incompatible
sage: #solve devuelve silenciosamente una lista de soluciones vacia
sage: eq1 = 2*x1 + x3 == 1
sage: eq2 = x1 + x2 - x3 == 0
sage: eq3 = x1 + x2 - x3 == 1
sage: print solve([eq1,eq2,eq3],x1,x2,x3)
sage: #M\v, o M.solve_right(v) arrojan un error
sage: M = matrix(QQ,3,3,[2,0,1, 1,1,-1, 1,1,-1])
sage: v = vector(QQ,[1,0,1])
sage: M\v
[
]
Traceback (most recent call last):
...
ValueError: matrix equation has no solutions
El comando asume informa a Sage de que cierta identidad no trivial que involucra algunas variables simblicas se
verifica. Esto es importante porque permite hacer ciertas simplificaciones extra. Por ejemplo:
sage: var("x")
sage: s = sqrt(x^2)
sage: simplify(s)
sqrt(x^2)
sage: assume(x>0)
sage: simplify(s)
x
El comando dual de assume es forget , que olvida una identidad que anteriormente habamos asumido como cierta
(la sintaxis es idntica).
forget() sin argumentos olvida todas las identidades.
assumptions() muestra todas las identidades activas.
sage: assumptions()
[x > 0]
sage: forget(x>0)
sage: simplify(s)
sqrt(x^2)
sage: assumptions()
[]
Tambin podemos asumir otro tipo de informacin, como por ejemplo que una variable representa un nmero entero.
sage: var(n)
sage: assume(n, integer)
sage: var(k t)
sage: assume(k, integer)
sage: simplify(sin(t+2*k*pi))
sin(t)
6.1.5 Ejemplos
Si usamos el anillo de expresiones simblicas ( SR ) para los coeficientes de las matrices, podemos trabajar con
parmetros.
sage: #Ejemplo de una matriz dependiendo de un parmetro
sage: var(a)
sage: M = matrix(SR,3,3, [1,2,3, 1,-3,1, 2,1,a])
sage: M
[ 1 2 3]
[ 1 -3 1]
[ 2 1 a]
a21
1 a1
1 a2 a22
1 a3 a23
sage: var(a)
sage: M = matrix(SR,3,3, [1,2,3, 1,-3,1, 2,1,a])
sage: M
[ 1 2 3]
[ 1 -3 1]
[ 2 1 a]
Solucin 1: ponemos la matriz en forma escalonada mediante operacinoes de fila y vemos que el rango depende de si
a es 24/5 o no.
sage: M.add_multiple_of_row(1,0,-1)
sage: M
[ 1 2 3]
[ 0 -5 -2]
[ 2 1 a]
sage: M.add_multiple_of_row(2,0,-2)
sage: M
[ 1 2 3]
[ 0 -5 -2]
[ 0 -3 a - 6]
sage: M.add_multiple_of_row(2,1,-3/5)
sage: M
[ 1 2 3]
[ 0 -5 -2]
[ 0 0 a - 24/5]
Sin embargo, la forma escalonada produce un resultado inesperado: como las expresiones simblicas no nulas son
invertibles, la matriz es equivalente a la identidad!
sage: M.echelon_form()
[1 0 0]
[0 1 0]
[0 0 1]
Ejercicio resuelto
Encuentra la recta afn que pasa por el punto (1, 0, 1) y corta a las rectas L1 y L2 , sabiendo que L1 pasa por (1, 0, 1)
y (2, 1, 0), y que L2 pasa por (3, 1, 1) y (1, 2, 1).
sage: p1 = vector([1,0,1])
sage: p2 = vector([2,1,0])
sage: p3 = vector([3,1,-1])
sage: p4 = vector([1,2,-1])
sage: p5 = vector([1,0,-1])
sage: var(r s t)
sage: #Calculamos un punto generico de L1, y otro de L2
sage: pgL1 = (1-r)*p1+r*p2
sage: pgL2 = (1-s)*p3+s*p4
sage: #Un punto generico de la recta que pasa por pgL1 y pgL2
sage: pgL = (1-t)*pgL1 + t*pgL2
sage: pgL
(-(t - 1)*(r + 1) - (2*s - 3)*t, -(t - 1)*r + (s + 1)*t, (t - 1)*(r - 1) - t)
Si vas a evaluar muchas veces una expresin simblica, puedes compilarla usando el comando fast_float ,
que devuelve una funcin normal de python correspondiente a la expresin simblica. Esta funcin ya no se puede
manipular de forma simblica, pero se puede evaluar usando mucho menos tiempo.
sage: var(a b c)
sage: f(a,b,c) = a^3 + sin(b + 3*log(c))
sage: timeit(f(1,2,1))
sage: ff = fast_float(f)
sage: timeit(ff(1,2,1))
625 loops, best of 3: 248 s per loop
625 loops, best of 3: 13 s per loop
sage: var(a b c)
sage: s = a^3 + sin(b + 3*log(c))
sage: timeit(s(a=1, b=2, c=1))
sage: fs = fast_float(s, a, b, c)
sage: timeit(fs(1,2,1))
625 loops, best of 3: 109 s per loop
625 loops, best of 3: 13 s per loop
6.2 Ejercicios
6.2.1 1.
Queremos encontrar todos los tringulos equilateros con vrtices en tres rectas paralelas dadas.
Plantea el problema usando variables simblicas de la forma ms directa posible, usando la defini-
cin de tringulo equiltero: los tres lados deben ser iguales, y observa como Sage falla miserable-
mente.
Mastica un poco el problema, usando algunas de las sugerencias siguientes:
Evita las raices cuadradas que tienen varias soluciones.
Fija la primera recta al eje de las x.
6.2.2 2.
Calcula el centro y el radio de la circunferencia que pasa por tres puntos del plano, de coordenadas (x1,y1),
(x2,y2) y (x3,y3).
Intenta entender la frmula obtenida para el radio del crculo, en funcin de conceptos geomtricos intrnsecos
como reas, distancias y ngulos.
6.2.3 3.
Un teorema de lgebra afirma que todo polinomio simtrico p(x1 , . . . , xn ) se puede expresar en funcin de los poli-
nomios simtricos elementales:
e0 (x1 , . . . , xn ) = 1,
e1 (x1 , . . . , xn ) = P1 + x2 + + xn ,
x
e2 (x1 , . . . , xn ) = i<j xi xj ,
en (x1 , . . . , xn ) = x1 x2 xn ,
ek (x1 , . . . , xn ) = 0, for k > n.
Por ejemplo:
Busca a mano una forma de expresar el polinomio x21 + x22 + x23 + x24 en funcin de los polinomios simtricos
elementales en cuatro variables.
Busca a mano una forma de expresar el polinomio x31 + x32 en funcin de los (tres) polinomios simtricos
elementales en dos variables.
Busca a mano una forma de expresar el polinomio 61 x31 12 x21 x2 1
2 x1 x22 1
6 x32 + x1 + x2 en funcin de
los (tres) polinomios simtricos elementales en dos variables.
Busca a mano una forma de expresar el polinomio de Taylor de grado k de sin(x1 + x2 ) en funcin de los (tres)
polinomios simtricos elementales en dos variables (indicacin: busca un mtodo para calcular el polinomio de
Taylor)
El cdigo siguiente:
N = 5
var(,.join([x %d %j for j in range(1, N+1)]))
N = 5
vs = var(,.join([x %d %j for j in range(1, N+1)]))
p = 1
for v in vs:
p = p\*v
print p
Pn
Escribe una funcin que, dados dos nmeros n y k , construya el polinomio pk (x1 , . . . , xn ) = i=1 xki =
xk1 + + xkn
Escribe una funcin que, dado un nmero k , construya el polinomio simtrico en k variables: ek :
e0 (x1 , . . . , xn ) = 1,
e1 (x1 , . . . , xn ) = P1 + x2 + + xn ,
x
e2 (x1 , . . . , xn ) = i<j xi xj ,
en (x1 , . . . , xn ) = x1 x2 xn ,
ek (x1 , . . . , xn ) = 0, for k>n
Escribe cdigo que compruebe las identidades de Newton , para k desde 2 hasta 5 ( ms informacin ):
k
X
kek (x1 , . . . , xn ) = (1)i1 eki (x1 , . . . , xn )pi (x1 , . . . , xn )
i=1
sage: N = 5
sage: var(,.join([x %d %j for j in range(1, N+1)]))
(x1, x2, x3, x4, x5)
sage: N = 5
sage: vs = var(,.join([x %d %j for j in range(1, N+1)]))
sage: p = 1
sage: for v in vs:
... p = p*v
sage: print p
x1*x2*x3*x4*x5
6.2.5 5.
12 1n1
1 1 ...
1 2 22 ... 2n1
3n1
V =
1 3 32 ...
.. .. .. .. ..
. . . . .
1 n n2 ... nn1
Demuestra que la matriz de van der Monde es invertible si los nmeros 1 , . . . , n son distintos, para n=2,3 y
4. (Indicacin: factoriza el determinante)
6.3.1 Lmites
El mtodo limit permite calcular lmites de funciones. Para calcular el lmite de f en el punto x0 .
f.limit(x=x0)
Cuando la funcin est definida y es continua en el punto, el valor del lmite es igual al lmite de la funcin:
sage: var(x)
sage: f1=x^2
sage: f2=(x+1)*sin(x)
sage: f3=f2/f1
sage: f4=sqrt(f3)/(x^2+1)
Se puede calcular el lmite en un punto de la recta, o tambin en (al que podemos referirnos por \+Infinity
o por oo ).
sage: f3.limit(x=0)
+Infinity
sage: f3.limit(x=+oo)
0
1
x sin (x)
Usando el argumento adicional dir , podemos especificar si se trata de un lmite lateral, por arriba (above) o por
abajo (below):
sage: f=1/x
sage: print f.limit(x=0,dir=above)
sage: print f.limit(x=0,dir=below)
+Infinity
-Infinity
Lmites de sucesiones
Tambin podemos calcular lmites de sucesiones de la misma forma. Al fin y al cabo, si la expresin simblica admite
valores reales, el lmite de la funcin que define en infinito es lo mismo que el lmite de la sucesin de naturales
definido por la misma expresin.
sage: var(k)
sage: limit(k^2/(k+1)^2, k = oo)
1
El comando sum permite calcular la suma de una serie infinita, pero usando una sintaxis distinta de la usada hasta
ahora:
sum(expresion, variable, limite_inferior, limite_superior)
Algunos lmites, o sumas, no se pueden resolver sin hiptesis adicionales, y es necesario usar assume .
sage: var(k a)
sage: sum(k^a, a, 1, oo)
Is abs(k)-1 positive, negative, or zero?
Traceback (most recent call last):
...
TypeError: Computation failed since Maxima requested additional constraints (try the command assume(
sage: var(k a)
sage: forget()
sage: assume(abs(k)<1)
sage: sum(k^a, a, 0, oo)
-1/(k - 1)
Observamos que informar a Sage de que -1<k<1 en vez de |k|<1, aunque en principio es equivalente (si entendemos
que al escribir desigualdades ya no hablamos de nmeros complejos sino reales), no funciona.
Sirva este ejemplo para recordar que los motores de clculo simblico hacen lo que pueden, y no siempre dan el
resultado esperado. A veces necesitan que le introduzcamos la informacin de una forma particular, y fallan si la
introducimos de una forma equivalente. Es por ello que normalmente usamos las herramientas de clculo simblico
de forma interactiva.
sage: var(k a)
sage: forget()
sage: assume(k,real)
sage: assume(-1<k<1)
sage: sum(k^a, a, 0, oo)
Is abs(k)-1 positive, negative, or zero?
Traceback (most recent call last):
...
TypeError: Computation failed since Maxima requested additional constraints (try the command assume(
6.3.2 Derivadas
El mtodo derivative permite calcular derivadas de funciones simblicas. Las derivadas se obtienen siguiendo
metdicamente las reglas de derivacin y no suponen ningn problema al ordenador:
sage: sin(x).derivative(x)
cos(x)
sage: f2.derivative(x)
(x + 1)*cos(x) + sin(x)
Si usamos funciones de una sla variable, podemos omitir la variable por la que estamos derivando.
sage: f2.derivative()
(x + 1)*cos(x) + sin(x)
6.3.3 Integrales
Las integrales son ms complicadas de tratar mediante clculo simblico que las derivadas, ya que no existe ningn
mtodo que pueda calcular la integral de cualquier funcin. En realidad, hay muchas funciones elementales (cons-
trudas a partir funciones trigonomtricas, exponenciales y algebraicas mediante sumas, productos y composicin de
funciones) cuyas integrales, aunque estn bien definidas, no se pueden expresar en trminos de estas mismas funciones.
2
Los ejemplos f (x) = ex y f (x) = sin(x)x son bien conocidos.
Aunque en teora existe un algoritmo (el algoritmo de Risch) capaz de decidir si la integral de una funcin elemental es
otra funcin elemental, dificultades prcticas imposibilitan llevarlo a la prctica, y el resultado es que incluso en casos
en los que integramos una funcin cuya integral es una funcin elemental, nuestro algoritmo de integracin simblica
puede no darse cuenta.
sage: sin(x).integral()
__main__:1: DeprecationWarning: Variable of integration should be specified explicitly.
-cos(x)
sage: f2=sin(x)
sage: f2.integral(x)
-cos(x)
sage: f5=1/sqrt(1-x^2)
sage: f5.show()
1
x2 + 1
sage: f5.integral(x)
arcsin(x)
Las funciones racionales (cociente de un polinomio por otro polinomio) se pueden integrar de forma exacta, siempre
que sea posible descomponer los denominadores en fracciones simples (el algoritmo para integrar funciones racionales
es el mismo que estudisteis en el Bachillerato).
sage: f6=(x^3+4*x^2-x-2)/(x^2+8*x+1)
!
1 2 59 x 15 + 4
4 x + 15 log x2 + 8 x + 1
x 15 log
2 15 x+ 15 + 4
Sin embargo, algunas integrales no se pueden expresar en trminos de funciones elementales y slo se pueden dejar
indicadas.
sage: f7=sin(x)/x
sage: f7i=f7.integral(x)
Z
sin (x)
dx
x
Algunas funciones tienen integrales que se pueden expresar en trminos de funciones elementales, pero el ordenador
no es capaz de encontrarlas y desiste.
sage: f=(x^10*sin(x^3)*cos(x)).derivative()
sage: print f
3*x^12*cos(x^3)*cos(x) - x^10*sin(x^3)*sin(x) + 10*x^9*sin(x^3)*cos(x)
sage: #Observa que el resultado contiene integrales; esto quiere decir que no ha completado la integr
sage: #de la funcion f y lo ha dejado indicado
sage: g=f.integrate()
sage: print g
__main__:4: DeprecationWarning: Variable of integration should be specified explicitly.
1/4*x^10*sin(x^3 - x) + 1/4*x^10*sin(x^3 + x) + 1/4*x^10*cos(x^3 - x) - 1/4*x^10*cos(x^3 + x) + 1/4*i
sage: sage.calculus.calculus.integral?
<html>...</html>
Las integrales definidas son tambin complicadas de calcular de forma exacta. Sin embargo, una integral definida es
un nmero real, y en las aplicaciones es suficiente con aproximar ese nmero con suficiente precisin.
Si insistimos en hacer el clculo exacto, podemos usar el mismo mtodo integral , pero pasando adems dos
argumentos: el extremo inferior y superior del intervalo.
sage: sin(x).integral(0,pi)
2
Sin embargo, todas las integrales definidas se pueden calcular de forma aproximada con la funcin
numerical_integral , que acepta como argumentos la funcin a integrar y los extremos del intervalo, pero
devuelve una tupla formada por el valor aproximado de la integral y una cota del error cometido.
sage: numerical_integral(f7,0,1)
(0.94608307036718298, 1.0503632079297087e-14)
sage: numerical_integral?
<html>...</html>
Las funciones de una variable simblica tambin se pueden integrar con el mtodo nintegral, que adems del valor
aproximado de la integral y una estimacin del error devuelve dos parmetros adicionales.
sage: f7.nintegral(x,0,1)
(0.94608307036718309, 1.050363207929709e-14, 21, 0)
Ejercicio
Lee la ayuda del mtodo nintegral , y realiza llamadas a nintegral tales que:
el ltimo valor devuelto por nintegral sea positivo
el tercer valor devuelto por nintegral sea mayor que 100
sage: f7.nintegral?
<html>...</html>
6.3.5 Grficas
sage: line2d(puntos)
Adems de los argumentos obligatorios, le podemos pasar argumentos opcionales. Los siguientes parmetros son
bastante tiles:
plot(f,xmin,xmax) muestra la funcin entre los puntos xmin y xmax .
point2d(puntos, pointsize=20) permite especificar el tamao de los puntos
line2d(puntos, thickness= 2) permite especificar el grueso de la lnea
plot(f, color=(1,0,0)) permite especificar el color como una tupla de 3 valores: los tonos de rojo,
verde y azul.
point2d(puntos, rgbcolor=(1,0,0) ) y line2d(puntos, rgbcolor=(1,0,0) ) permiten
especificar el color como una tupla de 3 valores: los tonos de rojo, verde y azul.
sage: plot(f2,-5,5,color=(1,0,0))
Las grficas tienen un tipo de datos especial, con sus propios mtodos.
sage: grafica1?
<html>...</html>
sage: grafica1.
Traceback (most recent call last):
...
SyntaxError: invalid syntax
Podemos combinar muchas grficas en una sla almacenando las grficas en una lista, y luego usando la funcin sum
.
sage: lista = [plot(sin(n*x), (0, pi), color=(0,n/3,1-n/3)) for n in [1..4]]
sage: grafica = sum(lista)
sage: grafica.show()
Algunos problemas tpicos del clculo no se prestan tan bien al clculo simblico como los lmites y las derivadas. Un
ejemplo tpico es encontrar los puntos donde una funcin toma el valor 0. Podemos intentar resolver la ecuacin f=0
usando el comando solve , al que pasamos como argumento la variable simblica para la que queremos resolver.
f = x^2 - 3
f.solve(x)
Aunque el resultado es adecuado en muchos casos particulares importantes, francamente pocas ecuaciones que poda-
mos definir combinando ms de una funcin elemental tienen races que se puedan escribir de forma exacta como una
expresin simblica. Adems, tenemos el problema de la multiplicidad de races y extremos de las funciones.
La solucin que nos ofrece SAGE es buscar las races de forma numrica en un intervalo dado. Una llamada al mtodo
find_root :
f.find_root(a,b)
nos devuelve una raz de la funcin f en el intervalo [a,b] , y lanza un error si la funcin no tiene ceros en ese
intervalo.
sage: f6 = x^2 - 3
sage: f6.solve(x)
[x == -sqrt(3), x == sqrt(3)]
sage: f7=2*x+sin(x)-2
sage: f7.plot(0,10)
sage: f7.find_root(0,2)
0.68403665667799907
sage: sin(x).find_root(1,2)
Traceback (most recent call last):
...
RuntimeError: f appears to have no zero on the interval
sage: plot(sin(x),1,2)
Si la funcin tiene ms de una raz en el intervalo, find_root devuelve una cualquiera de las raices.
sage: sin(x).find_root(1,30)
21.991148575128552
devuelve una tupla con dos valores: el minimo de la funcin en el intervalo y el punto donde se alcanza.
sage: f8 = x^2 - 3*sin(x)
sage: plot(f8,-4,4)
En realidad, el valor devuelto por find_minimum_on_interval es uno cualquiera de los mnimos locales de la
funcin. En la documentacin de la funcin leemos que usa el mtodo de Brent : una combinacin del mtodo de
biseccin, el mtodo de la secante y el mtodo de interpolacin cuadrtica:
Todos estos mtodos pueden detenerse en un extremo local, y el algoritmo de Brent tambin. En general, el problema
de encontrar mximos globales no es trivial, aunque para funciones de una variable de las que se conoce alguna
propiedad extra no es difcil encontrar procedimientos para encontrar los extremos.
sage: f8.find_minimum_on_interval?
<html>...</html>
sage: f9=sin(x/10)+sin(x)
sage: plot(f9,0,50)
sage: f9.find_maximum_on_interval(10,20)
(1.987809504903955, 14.152658043591082)
sage: f9.find_maximum_on_interval(0,50)
(1.8920281240458212, 20.37533953333153)
Usando la funcin Piecewise podemos construir funciones definidas a trozos. Para ello, pasamos a la funcin
Piecewise una lista de tuplas, donde cada tupla contiene un intervalo y una funcin. El resultado es la funcin definida
a trozos que vale en cada intervalo la funcin que hemos pasado.
f = Piecewise( [(I1, f1), (I2,f2), ...] )
Pasamos los intervalos como tuplas con los extremos del intervalo.
::
(
x + sin (x) on (1, 0)
x2 on (0, 1)
sage: plot(f)
sage: fp = f.derivative()
sage: show(fp)
sage: plot(fp)
__main__:2: DeprecationWarning: Substitution using function-call syntax and unnamed arguments is depr
(
x 7 cos (x) 1 on (1, 0)
x 7 2 x on (0, 1)
6.4 Ejercicios
6.4.1 1.
Estudia la funcin:
(1 x)x sin (10 x)
(10 x 6)
en el intervalo [0,1]:
La funcin no est definida cuando x=0.6. Indica cmo puedes extender la funcin para que quede definida en
ese punto, siendo continua si es posible.
Encuentra todos sus mximos y mnimos locales y globales en ese intervalo.
Dibuja la funcin en el intervalo [0,1], con los extremos locales indicados en un color y los extremos absolutos
indicados en un color distinto.
6.4.2 2.
Estudia la funcin g:
(x 1)x cos 61 + 4 x
(3 x 1)
en el intervalo [0,1]
Calcula la segunda derivada de g.
Dibuja la segunda derivada de g en el intervalo [0,1], y encuentra todos los ceros de la segunda derivada.
Dibuja la funcin en el intervalo [0,1], con los puntos encontrados antes marcados en rojo. Es decir, deben
aparecer, dibujados sobre la grfica de f, los puntos donde se anula la segunda derivada.
6.4.3 3.
Define en un cuadro de comandos una funcin f en la variable simblica x y un valor x0 . A continuacin, escribe
cdigo para:
Calcular la funcin de la variable simblica x que representa la recta tangente a f en el punto x0 , a partir de
los datos anteriores.
Dibujar la funcin cerca del punto, el punto (x0,f(x0)) en otro color, y la recta tangente a la funcin en (x0,f(x0))
en otro color.
sage: f(x) = sin(x)
sage: x0 = 0
6.4.4 4.
Escoge una funcin f(x) cuyo lmite cuando x exista, y crea una grfica que muestre la funcin y la asntota,
en un rango aceptable de valores de x .
6.4.5 5.
Escoge una funcin con una asntota oblicua, encuentra una funcin simblica que represente esa asntota y dibuja en
la misma grfica la funcin y la asntota.
6.4.6 6.
Escoge una funcin convexa en un intervalo [a,b] y escribe cdigo que, dado un nmero K, genere una grfica que
muestre la funcin en el intervalo, y K rectas tangentes a la funcin en puntos (x,f(x)) para puntos x equiespaciados en
el intervalo [a,b] .
Repite el ejercicio para una funcin con un punto de inflexin.
En esta hoja vamos a estudiar problemas de clculo diferencial e integral en varias dimensiones espaciales. En general,
manejaremos funciones de varias variables simblicas.
Comencemos por representar grficamente funciones de dos variables reales.
sage: var(x,y)
(x, y)
sage: f1 = sin((x+y)^2)
sage: f1.show()
2
sin (x + y)
El comando plot3d genera una superficie cuya altura sobre el punto (x,y) es proporcional al valor de una funcin f
de dos variables simblicas. La sintaxis es:
plot3d(f,(x,x1,x2),(y,y1,y2))
sage: f2 = x*y*exp(x-y)
sage: f2.show()
sage: p = plot3d(f2,(x,-2,0.5),(y,-0.5,2))
sage: p.show(viewer=tachyon)
xye(xy)
Curvas de nivel
Dibujamos la curva dada por f=0. Si pasamos a implicit_plot el argumento adicional contours, el dibujo
contiene tantas curvas de nivel como le indiquemos:
implicit_plot(f,(x,x1,x2),(y,y1,y2), contours= 10)
contour_plot es muy similar al anterior, pues muestra de distinto color la regin entre dos curvas de nivel conse-
cutivas (es decir, la preimagen de un intervalo).
sage: contour_plot(x^2+2*y^2,(x,-2,2),(y,-2,2)).show(aspect_ratio=1)
Plano tangente
Una funcin de dos variables simblicas se puede derivar respecto de cada una de ellas de forma independiente. De
esta forma podemos aplicar la frmula del plano tangente en un punto:
f f
z = f (x0 , y0 ) + (x0 , y0 )(x x0 ) + (x0 , y0 )(y y0 )
x y
Las grficas de curvas de nivel no permiten apreciar bien el plano tangente, pero podemos representar el plano junto a
la funcin con plot3d .
sage: f4=x*log(x^2+y^2)
sage: f4.show()
sage: x0 = 1
sage: y0 = 1
sage: plano = (x - x0)*f4.derivative(x,1).subs(x=x0, y=y0) + (y - y0)*f4.derivative(y,1).subs(x=x0, y
sage: show(plano)
sage: p = (plot3d(f4,(x,-1.5,1.5),(y,-1.5,1.5)) +
... plot3d(plano,(x,-1.5,1.5),(y,-1.5,1.5),color=red) +
... point3d( (x0,y0,f4(x=x0,y=y0)), color=green, pointsize=30) )
sage: p.show(viewer=tachyon)
x log x2 + y 2
Polinomio de Taylor
La frmula del plano tangente es el caso particular de la frmula del polinomio de Taylor de la funcin en un punto:
k
X 1 f (a) X
f (x) = (x a) + R (x)(x a)
! x
||=0 ||=k+1
sage: f5 = x*exp(x + y)
sage: f5.show()
sage: #pol5 = f5.taylor((x,0),(y,0),2)
sage: pol5 = polinomio_taylor(f5,(x,0),(y,0),2)
sage: pol5.show()
xe(x+y)
x2 + xy + x
sage: p1=plot3d(f5,(x,-1,1),(y,-1,1));
sage: p2=plot3d(pol5,(x,-1,1),(y,-1,1),color=red);
sage: show(p1+p2,viewer=tachyon)
Derivadas direccionales
Ejercicio
Calcula la derivada direccional de la funcin f6 = sin (xy) + cos (xy) en el punto (1, /2) y la direccin (1, 1).
Investiga la ayuda del comando arrow, y dibuja el gradiente de f6 en un punto. Observa el efecto al cambiar la
funcin, y el punto.
sage: f6=sin(x*y)+cos(x*y)
sage: f6.show()
Campo gradiente
Usando el comando plot_vector_field , podemos dibujar el campo gradiente . El campo de vectores gradiente
de f es la asignacin del vector gradiente de f a cada punto del plano. Recordemos de las clases de teora que el
gradiente es perpendicular a las curvas de nivel. Es una buena ocasin para dibujar simultneamente las curvas de
nivel y el campo gradiente de una funcin.
sage: f7=sin(x*y)+cos(x*y)
sage: f7.show()
sage: plot_vector_field(f7.gradient(),(x,-2,2),(y,-2,2)).show(aspect_ratio=1)
Regiones
Otro comando interesante es el comando region_plot, que sirve para dibujar regiones definidas por desigualdades.
Se puede llamar de varias formas para representar regiones definidas por varias ecuaciones (ver la ayuda para ms
informacin). En esta sesin lo usaremos para visualizar regiones de integracin.
sage: #Una elipse
sage: region_plot((x-1)^2 + 2*y^2 < 1,(x,-2,2),(y,-2,2)).show(aspect_ratio=1)
Ejercicio
(f ) = 0
El sistema es trivial de plantear usando el mtodo gradient visto antes, y podemos intentar resolver el sistema llamando
a solve :
solve(f.gradient(),f.variables())
Por supuesto, con funciones ms complicadas, este tipo de sistemas de ecuaciones no lineales es casi imposible de
resolver.
sage: #Esta funcion parece tener un minimo cerca de (-0.3,-0.5)
sage: var(x y)
sage: f = x^2+ y^4 + sin(x+y)
sage: contour_plot(f,(x,-1,1),(y,-1,1),plot_points=300).show()
sage: solve(f.gradient(),[x,y])
[2*x + cos(x + y), 4*y^3 + cos(x + y)]
El paquete scipy.optimize contiene varios mtodos relacionados con problemas de optimizacin. El mtodo
scipy.optimize.fmin busca mnimos de una funcin de varias variables partiendo de un punto inicial. Para
llamar al mtodo, es necesario definir una funcin de python que acepta como argumento una lista con los valores de
las variables y devuelve el valor de la funcin en el punto. El resultado es el mnimo propuesto, y una estimacin del
error cometido. Adems, nos imprime un resumen de la ejecucin del algoritmo numrico.
sage: from scipy.optimize import fmin
sage: def f_0(xs):
... x,y = xs
... return x^2 + y^4 + x + y
sage: fmin(f_0,(0,0))
Optimization terminated successfully.
Current function value: -0.722470
Iterations: 58
Function evaluations: 113
array([-0.500004 , -0.62998934])
6.5.3 Integrales
La integracin simblica sobre regiones cuadradas no supone ningn quebradero de cabeza (siempre que el sistema
sea capaz de encontrar las integrales del integrando). Para calcular la integral doble, integramos primero respecto de
una variable y despus respecto de la otra.
Ejemplo:
ZZ
x2 ey dx dy, Q = [1, 1] [0, log 2]
Q
sage: f = x^2*e^y
sage: f.integral(x,-1,1).integral(y,0,log(2))
2/3
Para regiones ms generales, tenemos que operar igual que en clase de Clculo, descomponiendo la regin en subre-
giones del tipo:
{x1 x x2 , g(x) y h(x)}
Ejercicio resuelto
Representa el conjunto de los valores f (x, y) sobre Q = [0, 1] [0, 1] y calcula el volumen del slido as obtenido.
si x2 y 2x2 ,
x+y
f (x, y) =
0 en el resto.
Cuando sea imposible evaluar la integral de una funcin sobre una regin bsica del plano de forma exacta, podemos
hacerlo de forma numrica con scipy.integrate.dblquad
sage: #Es necesario importar la funcion dblquad
sage: #del paquete scipy.integrate al que pertenece
sage: from scipy.integrate import dblquad
sage: dblquad?
<html>...</html>
Calculamos la misma integral que calculamos antes de forma simblica usando dblquad.
sage: def g(x,y):
... return x+y
sage: def gfun(x):
... return x^2
sage: def hfun(x):
... return 2*x^2
sage: dblquad(g,0,1,gfun,hfun)
(0.54999999999999993, 6.1062266354383602e-15)
Cambios de coordenadas
sage: f = x^2+y^2
sage: print f.integral(y,-sqrt(1-x^2),sqrt(1-x^2)).integral(x,-1,1)
CODE:
sage47 : integrate(sage43,sage44,sage45,sage46)$
Maxima ERROR:
El cdigo anterior produce un error. Hurgando un poco en la traza del error, leemos:
TypeError: Error executing code in Maxima
CODE:
sage9 : integrate(sage5,sage6,sage7,sage8)
Maxima ERROR:
defint: lower limit of integration must be real; found \-sqrt(1\-x^2)
Recordemos que SAGE usa otras piezas de software con ms tradicin siempre que puede. En este caso, le pasa la tarea
de realizar la integral a la librera Maxima . Esta librera arroja un error bastante ilustrativo: el lmite de integracin
debe ser real, pero sqrt(1-x^2) puede ser imaginario. Entender la causa del error no siempre garantiza que se pueda
superar el problema, pero en este caso la solucin es informar a Sage de que puede asumir que x est entre -1 y 1.
En cualquier caso, nada nos garantiza el xito al intentar una integral de forma simblica, as que deberamos estar
preparadas para hacer la integral con dblquad si todo lo dems falla.
sage: assume(1-x^2>0)
sage: f = x^2+y^2
sage: print f.integral(y,-sqrt(1-x^2),sqrt(1-x^2)).integral(x,-1,1)
1/2*pi
6.6 Ejercicios
6.6.1 1.
Hallar el vector gradiente, en cada punto en el que exista, de las siguientes funciones escalares
f (x, y) = ex cos y
f (x, y, z) = log (x2 + 2 y 2 3 z 2 )
1
f (x, y) = xy sin x2 +y 2 si (x, y) 6= (0, 0) y f (0, 0) = 0.
6.6.2 2.
1
Calcular la distancia mnima entre los puntos de la grfica de f (x, y) = 4xy y el punto (0, 0, 0).
6.6.3 3.
6.6.4 4.
Hallar los puntos crticos y determinar cules son los mximos locales, mnimos locales o puntos silla:
6.6.5 5.
Representa el conjunto de los valores f (x, y) sobre Q = [0, 1] [0, 1] y calcula el volumen del slido as obtenido.
1 (x + y) si x + y 1,
f (x, y) =
0 en otro caso.
6.6.6 6.
Investiga la ayuda de region_plot con el objetivo de dibujar la unin de dos regiones. Es posible que la calidad
del dibujo empeore, pero la ayuda tambin explica cmo mejorar la precisin del dibujo.
6.6.7 7.
En los siguientes apartados, se supone que la integral de una funcin positiva f sobre la regin se reduce a la integral
iterada que se da. En cada caso, se pide determinar y dibujar la regin e invertir el orden de integracin.
R 2 R 2y R4R2
0 y2
f (x, y) dx dy. 1
x
f (x, y) dy dx ,
R e R log x R R sin x
1 0
f (x, y) dy dx. 0 sin x/2
f (x, y) dy dx.
Indicacin: usa region_plot para dibujar la regin. Despus de hacer el cambio en el orden de integracin, dibuja
de nuevo la regin para comprobar que obtienes el mismo resultado.
6.6.8 8.
Hallar el valor de las siguientes integrales, determinando y dibujando en cada caso el recinto de integracin
RRR
Q
(2x + 3y + z) dx dy dz, con Q = [1, 2] [1, 1] [0, 1].
RRR 2
T
x cos z dx dy dz, siendo T la regin limitada por los planos z = 0, z = , y = 0, y = 1, x = 0, x+y = 1.
x y 2 z 3 dx dy dz, siendo el slido limitado por la superficie z = x y y los planos y = x, x = 1 y z = 0.
RRR
Veamos cmo realizar integrales de lnea como las que aparecen en el teorema de Green. Este teorema se suele escribir
as:
I ZZ
M L
(L dx + M dy) = dx dy
C D x y
Con frecuencia, sta es una manera prctica de evaluar integrales planas sobre regiones que no admiten una descom-
posicin sencilla en regiones simples. Por ejemplo, podemos calcular reas:
I ZZ
x dy = 1 dx dy = |D|
C D
Calcula el rea de un tringulo con este mtodo. Comprueba el resultado calculando el rea de tringulos cuya
rea puedas calcular a mano.
Calcula el rea de un polgono de n lados.
x2 y2
Demuestra que el rea de la elipse es a2 + b2 1 es A = a b
Indicacin: Recuerda que una parametrizacin de la elipse es t (a cos(), b sin()).
Pensaremos en una curva plana como la imagen por una funcin continua : I R2 , con I un intervalo de nmeros
reales. Si tomamos t como variable independiente (t R), describiremos la imagen como el conjunto de puntos
= (I) = (x(t), y(t)) : t I
o simplemente
: x(t) , y(t)
Cuando el dominio de valores para el parmetro t es un intervalo, digamos [a, b], la curva tiene como extremos los
puntos: (x(a), y(a)) y (x(b), y(b)).
Ejemplos
La curva : (t, t), para t (0, 1) , es el segmento de recta que une los puntos (0, 0) y (1, 1). En general, si P y
Q son dos puntos, un segmento rectilneo que une P con Q es: : (1 t) P + t Q, considerando t (0, 1).
Antes de continuar, vamos a utilizar Sage para dibujar este ejemplo. Necesitamos que nuestro cdigo sepa interpre-
tar la expresin (1 t) P + t Q como lo haramos nosotros, a saber: si P = (x0 , y0 ) y Q = (x1 , y1 ), el resultado
queda:
(1 t) (x0 , y0 ) + t (x1 , y1 ) = (1 t)x0 , (1 t)y0 + tx1 , ty1
= x0 + t(x1 x0 ) , y0 + t(y1 y0 )
Esta manipulacin (afn), no es ms que tratar a los puntos P y Q como vectores (los vectores OP y OQ, cuando se ha
fijado un punto como origen del plano afn). En el siguiente cdigo se consigue este propsito. Utilizamos la funcin
parametric_plot de Sage (leer la ayuda antes de seguir). Obsrvese en qu manera se explicita el intervalo para
la variable t.
sage: parametric_plot?
<html>...</html>
(t + 1, 3 t 1)
Ms ejemplos
Si tenemos una funcin (continua) real de variable real, f : R R, la grfica es una curva en el
plano, {(x, f (x)) : x R}, que podemos presentar paramtricamente (tomando x = t). Estos son
casos particulares de curvas, en las que no hay ms de un punto en cada vertical (por qu?).
Una parbola: (t, t2 )
Una exponencial: (t, et )
Curvas trigonomtricas: (t, sin(t)), (t, cos(t)), . . .
Funciones racionales: t, t21t
+3t , t 6= 3, 0. En los puntos en que una funcin tal no es continua, la
grfica no es fiel (comprobarlo).
Polinomios, ...
sage: parametric_plot((t,(1-t)/(t^2+3*t)), (t,-5,2))
Y los ms interesantes
La mayora del lenguaje de curvas parametrizadas viene del estudio del movimiento de una partcula. En esos casos, el
parmetro de la curva suele ser el tiempo (qu buena eleccin de letra t!), y cada punto de la curva marca la posicin de
la partcula en cada momento. As el segmento rectilneo entre dos puntos P y Q descrito por : (1 t) P + t Q, t
[0, 1] sera la trayectoria descrita por una partcula que viaja en lnea recta, partiendo de P y llegando a Q en una unidad
de tiempo (t [0, 1]).
Este enfoque nos surte de una cantidad tremenda de ejemplos, histricamente estudiados por grandes fsicos y mate-
mticos, de los que a continuacin exponemos una brevsima lista (para ver ms visitar, por ejemplo: mathcurve.com
, epsilones.com ):
Circunferencia (centrada en el origen y de radio ): cos t, sin t , t [0, 2)
Bruja de Agnesi: 2at, 2a/(1 + t2 ) (a es el radio de la circunferencia base ver construccin ms abajo)
Astroide: (a cos3 (t), a sin3 (t)), t [0, 2)
3at 3at2
El folium de Descartes: 1+t 3 , 1+t3 , t (1, 1). Nota: el cambio t por 1/t, cambia x e y.
???: sin(3t), cos(4t) + 4 cos t , t [0, 2)
sage: # Dibujamos el folium de Descartes pegando dos ramas simtricas respecto al eje (y=x).
sage: rama1=parametric_plot((t/(1+t^3),t^2/(1+t^3)), (t,-.5,1))
sage: rama2=parametric_plot((t^2/(1+t^3),t/(1+t^3)), (t,-.5,1), color=(0,1,0))
sage: show(rama1+rama2, aspect_ratio=1)
para cada punto de la circunferencia, X, tomamos el punto de corte de la recta OX con la recta `: X 0 = OX `;
completamos un tringulo rectngulo con X, X 0 y un tercer punto, P (X), en la recta, `(X), paralela a ` por el
punto X: el ngulo recto sobre el vrtice nuevo, P (X).
La curva de Agnesi es la descrita por los puntos P (X).
sage: var(a t s x y)
(a, t, s, x, y)
sage: solve((2*a*t*s)^2+(2*a*s-a)^2-a^2,s)
[s == (1/(t^2 + 1)), s == 0]
Una manera alternativa de indicar la posicin de cada punto es mediante sus coordenadas polares (, ), siendo la
distancia del punto al origen, y el ngulo que forma el vector que une el origen con el punto y el semieje horizontal
positivo, es decir: si P = (x, y)
p y
= x2 + y 2 , = arctan .
x
Obsrvese que la funcin arctan nos devuelve el valor correcto del ngulo salvo, quizs, un error de (pues tan() =
tan( )). El cambio inverso, de polares a cartesianas, es ms preciso:
x = cos , y = sin .
Es claro que 0 as como que basta tomar ngulos en el intervalo [0, 2), por ejemplo.
Para dar la ecuacin en polares de una curva, se suele expresar el radio como una funcin del ngulo, pero se representa
en el plano cartesiano. As, si dibujamos la curva : = cos2 () , [0, 2), estaremos dibujando los puntos del
plano (x, y), con x = cos = cos2 () cos = cos3 , e y = cos2 sin , variando , en este caso, entre 0 y 2.
La funcin de Sage que utilizamos en este caso es polar_plot (revisar su ayuda en lnea), que es equivalente a
utilizar el valor polar=True en un plot() .
sage: polar_plot?
<html>...</html>
sage: var(theta)
sage: polar_plot(cos(theta)^2, (theta,0,2*pi))
Ejemplos
Recta de ecuacin Ax + By = 1 (no pasa por el origen). Si p = 1/ A2 + B 2 y es el ngulo definido por
p
cos = Ap, sin = Bp, entonces: = es su ecuacin en polares.
cos( )
Recta que pasa por el origen: en este caso la descripcin es ms sencilla, pues todos los puntos de la recta (al
unirse al origen) forman un ngulo constante con el eje de abcisas. As, si dicho ngulo es 0 la ecuacin es
= 0 . Ahora bien, no podemos indicarle a Sage cmo dibujarlas utilizando polar_plot() (ocurre como en la
representacin de rectas verticales, x = constante, en coordenadas cartesianas).
Como vemos, las rectas no son muy amigables para representar en polares. Sigamos con ejemplos ms enriquecedores.
Circunferencia (con centro el origen): = constante, [0, 2). Y si el ngulo lo variamos menos, consegui-
mos cualquier arco de circunferencia.
Circunferencia que pasa por el origen: = A cos + B sin , [0, 2). Nota: el centro es el punto de
coordenadas cartesianas (A/2, B/2).
Cnica con un foco en el origen: la ecuacin de la cnica, dado un foco, necesita conocer la recta directriz y la
excentricidad e. Si tal recta tiene ecuacin cartesiana x cos + y sin = q, la cnica es el lugar de puntos M
tales que kOM k = ekM Hk, siendo H la proyeccin ortogonal de M sobre la directriz. En tal caso, la ecuacin
polar de la cnica queda:
p
= , p = eq .
1 + e cos( )
La recta de ecuacin = es eje de simetra. Cuando e = 1 la cnica es una parbola, su eje de simetra
y (p/2, ) su vrtice. Cuando e 6= 1 la cnica es una cnica con centro, el eje focal y sus vrtices se calculan
sustituyendo por y + .
ltimos ejemplos
Por ltimo una lista de curvas famosas con sus ecuaciones polares:
Rosas: = a cos(n), [0, 2). Ejercicio : averiguar el nmero de ptalos para cada valor natural
de n. Nota: n puede ser cualquier real positivo.
La cardiode: = a(1 + cos ), [0, 2).
2
La cisoide: = a sin
cos , [0, 2).
La estrofoide: = a cos(2)
cos , [0, 2).
Aunque no han dejado de usarse, hasta aqu no hemos pedido a Sage que represente una curva cuyas ecuaciones le
vienen dadas en funcin de sus coordenadas cartesianas. Lo hasta ahora habitual es encontrarnos con que la coordenada
y es una funcin de la coordenada x: y = f (x). Las herramientas del clculo diferencial nos ayudan a esbozar este
tipo de grficas. En este apartado vamos a adoptar un punto de vista ms general, dando una ecuacin de la curva en
la que ambas variables puedan aparecer en la expresin sin que sea necesario despejar una en funcin de la otra.
Ecuaciones implcitas
La ecuacin Ax + By = C, con A, B y C nmeros reales, nos sirve para describir todos los
puntos del plano cuyas coordenadas, (x, y), la verifican. Es fcil comprobar que todos estos puntos
quedan alineados, es decir describen una lnea recta. De la ecuacin dada decimos que es su ecuacin
cartesiana o implcita , y basta, para calcularla con dar dos puntos de la recta, o un punto y una
direccin (vector director).
Una ecuacin implcita de la recta que pasa por los puntos de coordenadas (x0 , y0 ), (x1 , y1 ) surge
de desarrollar la siguiente igualdad:
!
x x0 x1 x0
det = 0.
y y0 y1 y0
Si conocemos que la recta pasa por el punto de coordenadas (x0 , y0 ) y la direccin de la recta es la del vector
v = (vx , vy ), una ecuacin implcita la obtenemos al desarrollar:
!
x x0 vx
det = 0.
y y0 vy
Ejercicio
Encuentra la ecuacin de la recta que pasa por dos puntos usando lgebra lineal.
Ms ecuaciones implcitas
Por cinco puntos distintos del plano pasa una nica cnica . Este sencillo principio es una manera escueta de
afirmar que con fijar cinco puntos distintos (en posicin general), somos capaces de encontrar (determinar los
coeficientes de) una ecuacin implcita de la forma ax2 + by 2 + cxy + dx + ey + f = 0, y que cualquier otra
es el resultado de multiplicar todos los coeficientes por un factor no nulo. Aunque no entraremos en el concepto
general de puntos en posicin general, basta decir que en este caso quiere decir que los puntos son distintos y
que no hay tres que estn alineados.
Este problema, dadas las coordenadas de los cinco puntos, se puede resolver con clculo matricial. Si Pi = (xi , yi ),
para i = 1, . . . , 5, son cinco puntos distintos, la ecuacin buscada, de la forma ax2 + by 2 + cxy + dx + ey + f = 0,
x21 a + y12 b + x1 y1 c + x1 d + y1 e + 1 f =0
x22 a + y22 b + x2 y2 c + x2 d + y2 e + 1 f =0
x23 a + y32 b + x3 y3 c + x3 d + y3 e + 1 f =0
x24 a + y42 b + x4 y4 c + x4 d + y4 e + 1 f =0
x25 a + y52 b + x5 y5 c + x5 d + y5 e + 1 f =0
que son lineales en sus incgnitas (los coeficientes a, b, . . . , f ). Este es un sistema lineal compatible, y si los puntos
estn en posicin general su matriz de coeficientes es de rango 5. Si (a0 , b0 , c0 , d0 , e0 , f0 ) es una solucin (no trivial),
cualquier otra es de la forma (a0 , b0 , c0 , d0 , e0 , f0 ). Pero es obvio que todas estas soluciones nos dan ecuaciones
para una misma curva (por qu?).
Encuentra la cnica que pasa por cinco puntos en posicin general:
ax2 + by 2 + cxy + dx + ey + f = 0
sage: var(x y)
sage: coefs = matrix(QQ,5,6)
sage: for j in range(5):
... x0, y0 = puntos[j]
... coefs[j,0] = x0^2 #Primera columna (coef. de a)
... coefs[j,1] = y0^2 #Segunda columna (coef. de b)
... coefs[j,2] = x0*y0 #Tercera columna (coef. de c)
... coefs[j,3] = x0 #Cuarta columna (coef. de d)
... coefs[j,4] = y0 #Quinta columna (coef. de e)
... coefs[j,5] = 1 #Sexta columna (coef. de f)
sage: show(coefs)
0 0 0 0 0 1
0 1 0 0 1 1
1 9 3 1 3 1
4 1 2 2 1 1
1 0 0 1 0 1
(0, 0, 0, 0, 0, 0)
sage: vk = coefs.right_kernel().basis()[0]
sage: a,b,c,d,e,f = vk
sage: curva = a*x^2 + b*y^2 + c*x*y + d*x + e*y + f
sage: show(curva)
1 2 1
x2 xy + y x y
2 2
sage: implicit_plot(curva,(x,-1,4),(y,-1,5))+point2d(puntos,color=(1,0,0),pointsize=30)
Ejercicio
Encuentra 5 puntos tal que la cnica que pasa por ellos sea una hiprbola.
Si fijamos 4 puntos del plano en posicin general, encontramos muchas cnicas que pasan por todos los puntos. En
este caso, los coeficientes de la ecuacin de la cnica que pasa por los 4 puntos deben satisfacer 4 ecuaciones:
x21 a + y12 b + x1 y1 c + x1 d + y1 e + 1 f =0
x22 a + y22 b + x2 y2 c + x2 d + y2 e + 1 f =0
x23 a + y32 b + x3 y3 c + x3 d + y3 e + 1 f =0
x24 a + y42 b + x4 y4 c + x4 d + y4 e + 1 f =0
sage: var(x y)
sage: coefs = matrix(QQ,4,6)
sage: for j in range(4):
... x0, y0 = puntos[j]
... coefs[j,0] = x0^2
... coefs[j,1] = y0^2
... coefs[j,2] = x0*y0
... coefs[j,3] = x0
... coefs[j,4] = y0
... coefs[j,5] = 1
sage: show(coefs)
0 0 0 0 0 1
0 1 0 0 1 1
1 0 0 1 0 1
4 4 4 2 2 1
sage: K = coefs.right_kernel()
sage: v1 = K.basis()[0]
sage: v2 = K.basis()[1]
sage: show(K.basis())
1 1
1, 0, , 1, 0, 0 , 0, 1, , 0, 1, 0
2 2
Cualquier vector no nulo de K da lugar a una cnica que pasa por los 4 puntos. No todas son equivalentes, aunque
por supuesto dos vectores proporcionales dan lugar a la misma curva. Al conjunto de cnicas que pasa por los cuatro
puntos lo llamamos el haz de cnicas por los 4 puntos.
sage: c1 = 1
sage: c2 = -3
sage: a,b,c,d,e,f = c1*v1 + c2*v2
sage: curva = a*x^2 + b*y^2 + c*x*y + d*x + e*y + f
sage: grafica = point2d(puntos,color=(1,0,0),pointsize=30) + implicit_plot(curva,(x,-1,4),(y,-1,4))
sage: grafica.show()
Ejercicio
6.8 Ejercicios
6.8.1 1.
Busca las ecuaciones (paramtricas, implcitas o las que ms te convengan) de las siguientes curvas y dibjalas:
Lemniscata de Bernouilli
Tractriz
Cicloide
6.8.2 2.
Compara las distintas formas de representar la misma curva usando parametric_plot , polar_plot e
implicit_plot para comprobar que se obtiene el mismo resultado:
Las ecuaciones implcitas de la recta Ax + By = 1 y la forma polar vista en clase.
Las ecuaciones implcitas de la circunferencia (x x0 )2 + (y y0 )2 = r2 que pasa por el origan y la forma
polar vista en clase.
Busca las ecuaciones implcitas de la curva de Agnesi y comprueba que corresponde a la misma curva que la
parametrizacin vista en clase.
Idem para el folium de Descartes.
Si denotamos por x1 , x2 , . . . , xn las coordenadas cartesianas en Rn , una ecuacin implcita o cartesiana del hiper-
plano determinado por n puntos, (x1,1 , x1,2 , . . . , x1,n ), . . . , (xn,1 , xn,1 , . . . , xn,n ), en posicin general , se obtiene al
desarrollar:
x1 x1,1 x1 x2,1 x1 xn,1
x2 x1,2 x2 x2,2 x2 xn,2
det = 0.
.. . .
. .
xn x1,n xn x2,n xn xn,n
6.8.4 4.
Encuentra 5 puntos tal que la cnica que pasa por ellos sea una hiprbola, una parbola, y el producto de 2 rectas (no
importa que no estn en posicin general).
El comando animate permite convertir una lista de grficas en una animacin cuyos fotogramas son las grficas de
la lista.
Investiga la ayuda de animate, y encuentra el cdigo que muestra la grfica del seno desplazndose a lo largo
del eje x.
Crea una animacin de un punto movindose desde un punto A a otro punto B.
Crea una animacin de un punto movindose a lo largo de una circunferencia de centro (0,0) y radio 1.
6.8.6 6.
Combina lo aprendido sobre el comando animate con lo visto en clase de teora para crear una animacin del haz
de cnicas que pasa por 4 puntos.
6.8.7 7.
Dada una curva : I R2 dada por (t) = (x(t), y(t)), el vector tangente a la curva en (t) es el vector con
origen en (t) y direccin 0 (t) = (x0 (t), y 0 (t)). Investiga el mtodo arrow y dibuja el vector tangente en varias de
las curvas definidas en coordenadas paramtricas en la clase de teora.
6.8.8 8.
Combina lo aprendido sobre el comando animate con el ejercicio anterior para hacer animaciones donde la curva
est fija y el vector tangente se mueve a lo largo de la curva.
Aplicaciones variadas. La primera sesin ensea el concepto de ajuste de modelos y su materializacin en Sage, y se
aplica a algunos ejemplos de datos cientficos y de matemticas. La sesin de criptografa se puede seguir immedia-
tamente despus de estudiar el bloque de aritmtica, y la sesin de malabares slo usa lo visto sobre grafos. Las dos
ltimas sesiones son breves y poco sistemticas.
7.1 Qu es TeX?
TeX es el sistema de composicin de textos creado por Donald Knuth. TeX, y su extensin LaTeX, desarrollada princi-
palmente por Leslie Lamport, son el sistema ms comn de escribir textos matemticos, pues permite incluir frmulas
matemticas de calidad profesional. El sistema es libre y multiplataforma, y es usado por muchsimos cientficos de
diversas disciplinas. Existen extensiones que permiten incluir grficas, notacin msical, cdigo de SAGE, y muchas
otras cosas.
Al ser un sistema tan extendido, muchos programas utilizan TeX para escribir las matemticas, como Sage. Cuando
editas un bloque de texto, puedes incluir cdigos de TeX. Al guardar los cambios, los cdigos de TeX se convierten en
frmulas matemticas. Por mantener la compatibilidad con los tutoriales de TeX, diremos que el texto original, con sus
cdigos especiales, es el cdigo fuente , y que el resultado final, con las frmulas matemticas, es el texto compilado
.
Aunque aprender el sistema lleva mucho tiempo, las ideas fundamentales de TeX son sencillas
Cuando queremos incluir una frmula matemtica dentro de en una lnea de texto, la rodeamos con carcteres
de $, como por ejemplo: $\sin(\pi)=1$, que se convierte en sin() = 1 al compilar el cdigo. Esta forma
de incluir frmulas se llama en ingls modo inline .
Cuando queremos incluir una frmula en su propia lnea, la rodeamos con dos caracteres de $. Por ejemplo:
$$e^{\pi i}=-1$$
ei = 1
209
Laboratorio de Matemticas, Release 2010/2011
Aparte de estas dos formas de incluir frmulas matemticas, TeX pone mucho enfsis en la estructura del documento,
separndolo en captulos, secciones, subsecciones... y dentro de ellas teoremas, corolarios, comentarios... Esta parte de
TeX no se usa en SAGE, que usa el html para dar estructura al documento. Ambos sistemas, TeX y html, dan mucha
importancia a la estructura del documento, y ofrecen al autor la posibilidad de indicar el tipo de contenido en vez de
limitarse a colocarlo en la posicin correcta con el tamao de letra deseado.
Lo mejor de SAGE es que podemos obtener fcilmente el cdigo latex que muestra una expresin cualquiera usando
el comando latex , que acepta como nico argumento un objeto de SAGE, y devuelve su expresin en LaTeX.
sage: var(x)
sage: f=sqrt(x)
sage: print f
sage: show(f)
sage: print latex(f)
sqrt(x)
\sqrt{x}
x
Podemos usar el cdigo latex de arriba en nuestros cuadros de texto: x.
Veamos ms ejemplos:
sage: print latex(pi)
sage: print latex(1/2)
\pi
\frac{1}{2}
El primer ejemplo muestra la forma de introducir las letras griegas: $\alpha$, $\beta$, $\gamma$ se convierten
en: , , .
El segundo ejemplo muestra la forma de escribir fracciones \frac{ numerador }{ denominador }.
Por supuesto, todas los cdigos se pueden combinar de cualquier forma:
sage: a=1/sqrt(2*pi)
sage: print a
sage: show(a)
sage: print latex(a)
1/2*sqrt(2)/sqrt(pi)
\frac{1}{2} \, \frac{\sqrt{2}}{\sqrt{\pi}}
1 2
2
La lista completa de smbolos es enorme, por supuesto, e incluye flechas (\rightarrow: ), operadores (\div:
), desigualdades (\neq: 6=) ...
Bsquedas en google arrojan listas bastante completas:
http://omega.albany.edu:8008/Symbols.html
http://stdout.org/~winston/latex/
El siguiente ejemplo usa polinomios.
sage: R1.<t>=PolynomialRing(QQ)
sage: p=t^3-1
sage: print latex(p)
sage: print latex(p/(p+2))
t^{3} - 1
\frac{t^{3} - 1}{t^{3} + 1}
El nico comando nuevo es el ^{ superndice }, para exponentes. El comando equivalente para subndices es _{
subndice }. Por ejemplo:
$$x_{1}^{2}+x_{2}^{2}+\dots+x_{n}^{2} $$
se convierte en:
x21 + x22 + + x2n
En modo display, y para algunos operadores como el sumatorio (sum) o el lmite (limit), los subndices se muestran
debajo del operador:
$$\sum_{j=1}^{N}x_i$$
se convierte en :
N
X
xi
j=1
sin x2
El cdigo de esta funcin incluye dos nuevos comandos: \left y \right. Estos comandos sirven para cuadrar el
parntesis izquierdo con el derecho, y asegurarse de que son lo bastante grandes para delimitar el contenido.
Siempre que aparece un comando left, debe aparecer despus un comando right.
En este caso, despus de cada uno hemos usado partensis, pero podemos poner corchetes o barras verticales:
$$\left[(a+b)^{2}+(a-b)^{2}\right]$$
sage: f = sin(1/x)
sage: show(f)
sage: print latex(f)
\sin\left(\frac{1}{x}\right)
1
sin
x
7.1.4 Matrices
sage: show(A)
1 2 3
4 5 6
El cdigo {rrr} indica que la matriz tiene tres columnas, todas alineadas a la derecha (r:right, l:left, c:center). Los
caracteres & delimitan las columnas, y los caracteres \\ delimitan las filas.
7.1.5 Ejercicios
1.
copia, pega y modifica el cdigo anterior, para escribir una matriz 2x4 y otra 4x2.
2.
Intenta escribir el cdigo que genera el determinante de abajo (no vale mirar):
1 1 12
d = 1 2 22
1 3 32
3.
Las frmulas de la Wikipedia tambin estn escritas en LaTeX. Puedes obtener el cdigo LaTeX que genera una
frmula haciendo click derecho sobre la frmula y eligiendo propiedades, y copiando el texto alternativo.
Copia en esta hoja la frmula del determinante 3x3 y n x n de la pgina:
http://en.wikipedia.org/wiki/Determinant
4.
A veces necesitamos slo uno de los dos parntesis, corchetes o llaves. Incluso en este caso, escribimos un operador
\left y otro \right para delimitar la regin, slo que donde no queremos que ponga un parntesis, escribimos un
punto. Ejemplo:
$$\alpha\left| \frac{1}{2}\right. + \omega\left| \frac{1}{2}\right.$$
1 1
+
2 2
Intenta escribir el cdigo que genera la definicin de abajo (no vale mirar):
2
x si x < 0
f (x) =
x3 si x 0
En esta sesin vamos a intentar representar distribuciones de probabilidad discretas y continuas y realizar con ellas
varias operaciones comunes, como calcular medias y varianzas, hacer extracciones aleatorias segn una distribucin
dada o dibujar las funciones de masa, densidad y distribucin. Al final, trabajaremos un poco con variables aleatorias
bidimensionales.
Representamos la funcin de masa mediante un diccionario en el que las claves son los puntos del espacio muestral y el
valor asociado a cada clave es la probabilidad de ese punto. Un diccionario representa una distribucin de probabilidad
si sus valores son nmeros (reales, racionales, incluso expresiones simblicas) que suman 1.
sage: #Ejemplos:
...
sage: #Bernouilli de prob p=1/3
sage: p = 1/3
sage: f_bernouilli = {0:p, 1:1-p}
sage: #Binomial con prob p=1/3 y k=10 ensayos independientes
sage: k = 10
sage: p = 1/3
sage: f_binomial = dict((j, p^j*(1-p)^(k-j)*binomial(k,j)) for j in range(k+1))
Asumiendo que el espacio muestral est contenido en R, podemos dibujar la distribucin por ejemplo as:
sage: #dibujar una distribucion discreta con soporte finito
sage: def dibuja_f(f, *args, **kargs):
... Dibuja una funcion de masa con soporte finito, dada como diccionario
...
... Acepta los argumentos adicionales tipicos de graficas en Sage,
... como color, etc
...
... p = (sum([line2d([(x, 0), (x, f[x])], *args, **kargs) for x in f])
... + point2d(f.items(), pointsize=30, *args, **kargs))
...
... #Imponemos rango [0,1] para el eje que muestra las probabilidades
... p.ymin(0)
... p.ymax(1)
... p.axes_labels([$x$,$p$])
... return p
sage: show(dibuja_f(f_bernouilli))
sage: show(dibuja_f(f_binomial, color = (1,0,0)))
De nuevo asumiendo que el espacio muestral est contenido en R, calculamos la esperanza y la varianza de una
N
X
2 = V ar[X] = (xi )2 f (xi )
i=1
Como el cdigo anterior es genrico, nada nos impide usar variables simblicas, y hacer clculos con parmetros
libres.
sage: var(p)
sage: #Bernouilli
sage: #funcion de masa
sage: f = {0:1-p, 1:p}
sage: media_f(f), varianza_f(f)
(p, (p - 1)^2*p - (p - 1)*p^2)
sage: varianza_f(f).factor()
-(p - 1)*p
Funcin de distribucin
Para trabajar con la funcin de distribucin, necesitamos ordenar los puntos del espacio muestral. Guardamos en una
lista los puntos que tienen probabilidad positiva y en otra lista (del mismo tamao) la prob de cada punto.
sage: pares = f_binomial.items()
sage: pares.sort()
sage: valores = [x for x,p in pares]
sage: probs = [p for x,p in pares]
sage: cum_probs = []
sage: suma = 0
sage: for p in probs:
... suma += p
... cum_probs.append(suma)
sage: print valores
sage: print probs
sage: print cum_probs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1024/59049, 5120/59049, 1280/6561, 5120/19683, 4480/19683, 896/6561, 1120/19683, 320/19683, 20/6561,
[1024/59049, 2048/19683, 5888/19683, 11008/19683, 15488/19683, 18176/19683, 2144/2187, 19616/19683, 1
Ejercicio - debate : Cmo podemos extraer un nmero en el soporte de nuestra funcin de masa respetando las
probabilidades requeridas? Es decir, si tenemos:
f = {0:1/2, 1:1/3, 2:1/6}
valores : [0, 1, 2]
cum_probs : [1/2, 5/6, 1]
queremos una funcin que devuelva 0 con probabilidad 1/2, 1 con probabilidad 1/3, y 2 con probabilidad 1/6.
Un diccionario no puede contener una cantidad infinita de valores. Para trabajar con distribuciones con soporte infinito
podemos usar funciones de python, o expresiones simblicas. Optamos por la segunda opcin para tener al menos la
posibilidad de hacer algunos clculos de forma exacta, aunque no siempre sea posible.
sage: #Geometrica
sage: var(k)
sage: p = 0.1
sage: f_geometrica = (1-p)^k*p
sage: #Probabilidad de que X<=5
sage: print sum(f_geometrica, k, 0, 5).n()
sage: #Poisson de parametro landa = 2
sage: landa = 2
sage: f_poisson = e^(-landa)*landa^k/factorial(k)
sage: #Probabilidad de que X>=3
sage: print sum(f_poisson, k, 3, oo).n()
0.468559000000000
0.323323583816937
Para dibujar una distribucion discreta con soporte finito, nos conformamos con mostrar unos cuantos puntos que
concentran la mayora de la masa:
sage: def aproxima_df(f, porcentaje_masa = 0.95):
... Aproxima una distribucion de probabilidad discreta dada por una
... expresion simbolica por una funcion de masa con soporte finito
...
... d = {}
... masa_total = 0
... j = 0
... while masa_total < porcentaje_masa:
... d[j] = f(k = j)
... masa_total += f(k = j)
... j += 1
... return d
sage: def dibuja_d(f, porcentaje_masa = 0.95, *args, **kargs):
... d = aproxima_df(f, porcentaje_masa)
... return dibuja_f(d, *args, **kargs)
sage: dibuja_d(f_geometrica)
sage: dibuja_d(f_poisson)
Para extraer un entero con una distribucin de probabilidad prescrita, generamos un nmero aleatorio t entre 0 y 1, y
tomamos el menor k tal que la probabilidad acumulada P(X<=k) es mayor que t.
sage: #extraccion aleatoria
sage: def extraccion_aleatoria_d(f):
... t = random()
... j = 0
... prob = f(k=j)
... while prob < t:
... j += 1
... prob += f(k=j)
... return j
sage: extraccion_aleatoria_d(f_geometrica)
1
Las distribuciones continuas se pueden manejar de forma similar a las discretas con soporte infinito, pero cambiando
sumas por integrales. Por ejemplo, la normal en una variable:
1 1 x 2
f (x) = e 2 ( )
2
Extracciones aleatorias
Para hacer extracciones aleatorias de una distribucin continua no podemos seguir un procedimiento tan naive como
hasta ahora. Tenemos que transformar nuestro nmero aleatorio, elegido de forma uniforme entre 0 y 1, en un n-
mero real (potencialmente entre e ) que siga una distribucin dada X. Por si no lo habis visto en clase de
probabilidad, repasamos el procedimiento habitual brevemente:
Queremos generar nmeros x de tal modo que, para cualquier conjunto A R, la probabilidad de devolver un
nmero x A es exactamente P (X A).
Comenzamos por elegir un nmero aleatorio t [0, 1] (es decir, segn una distribucin uniforme), pero devol-
vemos el nmero G(t), para una cierta funcin G que tenemos que determinar.
Para cualquier conjunto A R, queremos que {t [0, 1] : G(t) A} = G1 (A) tenga medida P (X A).
De este modo, la probabilidad de devolver un nmero x = G(t) A es exactamente P (t G1 (A)) =
|G1 (A)| = P (X A).
La inversa de la funcin de distribucin G = F 1 cumple exactamente esta propiedad. Lo comprobamos slo
para intervalos. Si A=[x,y]:
P (X [x, y]) = F (y) F (x) = P (U [F (x), F (y)]) = P (U F ([x, y])) = P (U G1 ([x, y]))
sage: extraccion_aleatoria_c(F_normal)
0.6299529113987602
Histograma
Para comparar una muestra aleatoria (una cantidad finita de puntos) con una distribucin continua, tenemos que agrupar
los datos extrados en intervalos:
sage: from collections import defaultdict
sage: T = 400
sage: #Dividimos [-K,K] en N partes iguales
sage: K = 3
sage: N = 20
sage: frecuencias = defaultdict(int)
sage: for j in range(T):
... a = extraccion_aleatoria_c(F_normal)
... #TODO: explica las dos lineas siguientes
... k = floor(a*N/(2*K))*(2*K/N)
... frecuencias[k] += 1/(T*2*K/N)
sage: dibuja_f(frecuencias) + plot(f_normal, x, m-3*s, m+3*s, color=(1,0,0))
Las distribuciones en ms de una dimensin se manejan de forma similar, pero con ms variables simblicas. Por
ejemplo, estudiamos la distribucin normal en k dimensiones:
1
fX (x) = exp 12 (x )0 1 (x ) ,
(2)k/2 ||1/2
sage: #plot3d(f,(x1,-3,3),(x2,-3,3)).show(viewer=tachyon)
sage: p = contour_plot(f, (x1, m1-3*sqrt(v1), m1+3*sqrt(v1)), (x2, m2-3*sqrt(v2), m2+3*sqrt(v2)))
sage: p.show(aspect_ratio=1)
Resolvemos un tpico ejercicio de probabilidad condicionada con dos variables normales: X=N(m=0.2,s=0.3) e
Y=N(0.5, 0.6) son dos variables aleatorias que siguen una distribucin normal, con cov(X,Y)=0.1.
Si sabemos que para un individuo (aka elemento del espacio muestral), Y=1.3, cual es la prob de que X sea mayor
que 0.10?
sage: var(x1 x2)
sage: m1 = 0.2
sage: m2 = 0.5
sage: v1 = 0.3
sage: v12 = 0.3
sage: v2 = 0.6
sage: S = matrix(RDF, [[v1,v12],[v12,v2]])
sage: vs = vector([x1,x2])
sage: ms = vector([m1,m2])
sage: f(x1,x2) = (1/(2*pi))*(1/sqrt(det(S)))*exp(-(1/2)*(vs-ms)*(~S)*(vs-ms))
sage: p.show(aspect_ratio=1)
7.3 Ejercicios
7.3.1 1.
Simula el siguiente experimento: lanza monedas cargadas que dan cara con probabilidad p y cruz con probabi-
lidad 1-p hasta obtener la primera cara, y anota el nmero de cruces que has obtenido. Dibuja un histograma de
1000 extracciones aleatorias siguiendo este esquema, para un valor de p que t elijas.
Compara el resultado con el histograma de 1000 extracciones de una distribucin geomtrica con probabilidad
p.
El cuantil de orden p o p-cuantil, para 0 p 1, de una distribucin de probabilidad es el valor F 1(p), donde F
es la funcin de distribucin. La funcin cuantil es la funcin Q(p) que a cada p le asocia su p-cuantil. En principio,
esta definicin se aplica a distribuciones continuas, pero se puede extender a distribuciones de probabilidad finitas y
discretas, si tomamos:
Q(p) = F 1 (p) = nf {x R : p F (x)}
Escribe funciones que devuelvan el p-cuantil de una distribucin de probabilidad (en versiones finita, discreta y conti-
nua). Razona sobre la relacin entre estas funciones y las extracciones aleatorias que vimos en teora.
7.3.3 3. QQ plots
Una forma grfica bastante comn de visualizar si dos distribuciones de probabilidad son similares es el QQ-plot . La
letra Q se refiere a los cuantiles ( quantiles en ingls). Si tenemos dos distribuciones f y g:
1. Tomamos un entero k y calculamos la lista de los k-1 cuantiles de orden 1/k, 2/k, ... (k-1)/k, de f (sean
[p1 , ..., pk1 ]) y la lista correspondiente con los cuantiles de g (sean [q1 , ..., qk1 ]).
2. Dibujamos para cada j entre 1 y k-1 todos los puntos (pj , qj ).
3. Si las distribuciones son iguales, los puntos dibujados estn todos en la diagonal. Si son la misma distribucin,
pero desplazadas una distancia d (por ejemplo, si las funciones de densidad satisfacen g(x)=f(x-d)), los puntos
estarn en la recta y=x+d.
Dibuja qqplots que comparen los siguientes pares de distribuciones:
Una binomial con n lanzamientos y p=1/2 con una normal de media np y varianza np(1-p), para varios valores
de n.
Una poisson de parmetro con una normal de media y varianza , para un valor pequeo de y otro mayor.
Cmo podemos estimar la distancia entre dos distribuciones de probabilidad? Queremos que por ejemplo la distancia
entre la distribucin que toma el valor x con probabilidad 1 y la que toma el valor y con probabilidad 1 sea |x-y|, y que
la distancia entre una distribucin y ella misma sea 0. sto ltimo descarta por ejemplo tomar el valor esperado de la
distancia entre las dos variables aleatorias.
La idea del qqplot se puede llevar un poco ms lejos: tomar como distancia entre las distribuciones el promedio de
las distancias entre los cuantiles del mismo orden. Para mayor nmero de cuantiles, tenemos una medida ms fina.
Estudia si esta medida verifica las condiciones de arriba y si merece que la llamemos una distancia.
7.3.5 5.
El anlisis de regresin consiste en encontrar un modelo que relaciona los valores medidos de un conjunto de variables.
Los valores medidos en el mundo real nunca se ajustan de forma perfecta a un modelo, debido en primer lugar a
errores de medida, pero tambin a que cualquier modelo matemtico es una simplificacin del mundo real, y si tuviera
en cuenta todos los factores que influyen en un conjunto de variables, sera inmanejable.
Por tanto, no tiene sentido aspirar a encontrar un modelo que prediga exactamente los valores medidos, y debemos
admitir que el modelo cometer un cierto error.
Un modelo til encuentra una relacin funcional sencilla en conjuntos de pocas variables. Se trata de explicar una
variable que tiene importancia para nosotras, en funcin de otro conjunto de variables mejor conocidas o ms fciles
de medir. El anlisis de regresin (ms exactamente, el anlisis de regresin paramtrico ) permite encontrar un
modelo explicativo en tres etapas:
1. Nuestro conocimiento del tema en cuestin nos permite escribir un modelo que afirma que la variable X es una
funcin de las variables Y1 , . . . , Yk . La variable X recibe el nombre de variable dependiente y las variables
Y1 , . . . , Yk se llaman variables independientes . La forma exacta de la funcin no est fijada a priori, sino que
depende de unos pocos parmetros libres.
2. Tomamos una muestra . Es decir, medimos todas las variables en un subconjunto de todos los casos posibles
(unos cuantos individuos de la poblacin, unos cuantos momentos de tiempo, una cuantas muestras preparadas
en el laboratorio...)
3. Ajustamos el modelo , eligiendo aquellos valores de los parmetros tales que la distancia entre los valores
medidos de la variable X y los valores predichos aplicando el modelo minimizan el error cometido.
Tratamos de predecir la temperatura a la que hierve el agua ( T ), conocida la presin atmosfrica ( P ) en el lugar y
momento en que hacemos el experimento.
Para ello, contamos con un conjunto de mediciones de ambas variables, con la temperatura en grados Fahrenheit y
la presin en pulgadas de mercurio (sea lo que sea, es una unidad de medidad de presin). Por ejemplo, en un cierto
punto de los Alpes, un cierto da, el barmetro marcaba 20.79 pulgadas de mercurio, y el agua hirvi a 194.5 grados
Fahrenheit. Las mediciones se realizaron en el mismo lugar geogrfico, pero en das distintos, con distintas condiciones
atmosfricas y quiz incluso por personas distintas. En estas condiciones, es imposible que ningn modelo prediga
con exactitud el valor de T en funcin de P, pero esperamos que lo haga con un margen de error moderado.
T P
194.5 20.79
194.3 20.79
197.9 22.4
198.4 22.67
199.4 23.15
199.9 23.35
200.9 23.89
201.1 23.99
201.4 24.02
201.3 24.01
203.6 25.14
204.6 26.57
209.5 28.49
208.6 27.76
210.7 29.04
211.9 29.88
212.2 30.06
Referencia: http://www.sci.usq.edu.au/staff/dunn/Datasets/Books/Hand/Hand-R/alps-R.html
Comenzamos por dibujar los datos:
sage: datos = [(20.79,194.50),(20.79,194.30),(22.40,197.90),(22.67,198.40),
... (23.15,199.40),(23.35,199.90),(23.89,200.90),(23.99,201.10),
... (24.02,201.40),(24.01,201.30),(25.14,203.60),(26.57,204.60),
... (28.49,209.50),(27.76,208.60),(29.04,210.70),(29.88,211.90),
... (30.06,212.20)]
sage: puntos.show(axes_labels=(Presion,Temperatura))
Los datos parecen estar dispuestos sobre una recta, de modo que intentamos un modelo lineal, de la forma:
T =a+bP
A priori, no conocemos los valores de a y b . Para unos valores fijos de a y b , cometeremos un error en la medicin
j-sima que ser exactamente: |Tj (a + bPj )|. Vemos que no existe un criterio unvoco para encontrar a y b , dado
que no existe ninguna recta que pase por todos los pares (Tj , Pj ) . Para cualquier eleccin de **a* y b , nuestro
modelo cometer un error al estimar alguno de los puntos medidos.
Podemos escoger los valores de a y b para los que el error mximo cometido es menor, o aquellos para los que el error
medio cometido es menor, o segn otros muchos criterios. Es bastante habitual en estadstica buscar los valores de a y
b que hacen mnimo el error cuadrtico total :
X
E= (Tj (a + bPj ))2
j
La funcin find_fit de SAGE permite ajustar un modelo cualquiera de tal forma que se minimice el error cuadr-
tico. La funcin acepta dos argumentos: los datos y el modelo.
Los datos deben ser una lista con todas las mediciones. Cada elemento de la lista es una lista o tupla con los
valores de las variables en una medicin. La ltima variable es la variable dependiente.
El modelo es una funcin simblica de varias variables que representan las variables independientes y de otras
variables que representan los parmetros del modelo.
Aparte, acepta otros valores opcionales como parameters y variables para indicar a find_fit cul es la
variable dependiente y cules son los parmetros del modelo (ms informacin en la documentacin de find_fit ).
Tj (a + bPj )
sage: var(T P a b)
(T, P, a, b)
Usar el modelo
T = 1,9017835338620657 P + 155,29648321028
Ahora podemos utilizarlo para predecir la temperatura a la que hervir el agua a una cierta presin.Por ejemplo,
esperamos que si un da la presin es de 24.5 mmHg, el agua hervir a:
sage: modelo_ajustado(P=24.5)
201.890179796966
No es difcil demostrar que los parmetros que minimizan el error cuadrtico en un modelo lineal son nicos. Sin
embargo, cuando usamos modelos no lineales, puede haber varios mnimos de la funcin de error cuadrtico total. En
general, la minimizacin es ms complicada, y puede que el algoritmo no consiga encontrar los valores ptimos de a
y b sin ayuda. Esta vez no vamos a usar datos reales, sino datos generados usando nmeros aleatorios. Se trata de ver
qu tal ajustamos un modelo en un caso en el que conocemos cmo fueron generados los datos.
Obtenemos los datos usando la frmula 0,8 + 1,5x + 1,2 sin(2x 0,2), y sumando un nmero aleatorio extrado segn
una distribucin normal de media 0 y varianza 0.1:
a2 x + a3 sin (a4 x a5 ) + a1
La minimizacin del error se ha quedado atascada por el camino. La forma ms sencilla de ayudar al algoritmo es darle
un punto de partida mejor . Para ello, pasamos a find_fit un argumento adicional: initial_guess . Tambin
le pasamos los argumentos parameters y variables para que sepa cules de las variable simblicas del modelo
son los parmetros.
sage: #Ejercicio: prueba con distintos valores iniciales
sage: #para tratar de conseguir un mejor ajuste
sage: parametros = find_fit(datos, modelo, initial_guess=[0,0,1.0,2,0],
... parameters=[a1,a2,a3,a4,a5], variables=[x], solution_dict=True)
sage: print parametros
{a3: 1.2403261721812324, a2: 1.5056455458784646, a1: 0.78272474105136614, a5: 0.23540896442811415, a4
X: 0 0, 5 1 2 3 5 9 15
Y : 100 42 14 7, 5 0, 4 0, 11 0, 05 0, 002
Ejercicio
Ajustar una recta (Y = a + bX) y una exponencial (Y = aebx ) a los datos: qu ajuste es mejor?
La forma clsica de ajustar un modelo exponencial entre dos variables X e Y es reducirlo a un ajuste lineal entre las
variables X y V=log(Y).
Y = aebX V = c + dX
para c=log(a), d=b. La razn para usar este enfoque es que el ajuste lineal se puede realizar fcilmente con una
calculadora, mientras que el ajuste de un modelo no lineal requiere de una computadora.
Si dibujamos las variables X y V juntas vemos que parece sensato que estn relacionadas por un modelo lineal.
sage: datosXV = [(d[0],log(d[1])) for d in datos]
sage: show(point(datosXV))
Ejercicio
Ajusta un modelo lineal con X como variable independiente y V como variable dependiente. Deduce un modelo para
escribir Y en funcin de X, y dibjalo junto a los datos.
Observamos que las dos curvas exponenciales no se parecen gran cosa. La razn es que no es lo mismo minimizar el
error cuadrtico:
X
(Yj aebXj )2
j
7.5 Ejercicios
7.5.1 1.
Usando el anlisis de regresin, encuentra un modelo simplificado para la funcin que a un nmero k le asigna el
k-simo nmero primo.
Genera una serie de datos de longitud K con pares de datos (k, p), donde p es el primo k-simo.
Ajusta una curva del tipo P = a k ln(b k), con un parmetro libre a.
Referencia: teorema del numero primo.
7.5.2 2.
La sucesin de Collatz, o del 3*n+1, que ya vimos, consiste en aplicar sucesivamente la siguiente regla:
Si un nmero es par, el siguiente nmero de la secuencia es n/2.
Si es impar, el siguiente nmero de la secuencia es 3n+1.
El siguiente argumento heurstico muestra que la sucesin de Collatz debera converger a velocidad exponencial:
partiendo de un nmero impar, lo multiplicamos por 3 (ignoramos el 1 que sumamos), y lo dividimos por la mayor
potencia de 2 que lo divide hasta obtener otro nmero impar. Cul es la mayor potencia de 2 que divide a un nmero
par? Bueno, depende del nmero, pero en promedio nos encontramos con un mltiplo de 4 la mitad de las veces (la
otra mitad de las veces es de la forma 4n+2), con un mltiplo de 8 la cuarta parte de las veces, etctera. Es decir, es
seguro que podremos quitar el primer factor 2, pero cada factor sucesivo lo quitaremos con probabilidad 1/2^k. Dividir
por 2 con probabilidad 1/2 viene a ser como dividir por 21/2 . En resumen, hemos multiplicado nuestro nmero por:
3 3
=
2 21/2 21/4 21/8 ... 22
Siguiendo esta heurstica,el j-simo punto de la sucesin de Collatz que comienza en k ser k(3/4)j , y el tiempo que
log(k)
tarda la sucesin en alcanzar 1 (llammoslo T(k)) ser aproximadamente log(4/3) .
Ajusta una curva T(k) = a log(k) a datos obtenidos calculando la sucesin para distintos valores de k.
Ante los datos siguientes, un investigador decide hacer un ajuste lineal. El objetivo es predecir qu pasar cuando x
valga 10.
x: 0, 1, 2, 3, 4, 5
y: 1.03, 3.19, 5.10, 7.20, 9.10, 10.87
Ajusta los datos a un modelo lineal y usa el modelo para predecir el valor de y cuando x=10.
sage: datos = [[0,1.03], [1,3.19], [2,5.1], [3,7.2], [4,9.1], [5,10.87]]
Con los mismos datos de arriba, otro investigador decide usar como modelo un polinomio de grado 4 (con cinco grados
de libertad) para conseguir un mejor ajuste. Realiza el ajuste, y predice un nuevo valor cuando x valga 10.
Como vers, el resultado obtenido es bastante menos razonable que el obtenido con el modelo lineal ms sencillo. Si
dibujas el nuevo modelo ajustado a los datos en un intervalo ms grande vers por qu: los trminos polinmicos son
muy sensibles a los errores, y aunque la curva se ajusta mejor a los datos conocidos, ajusta peor los datos nuevos. No
es buena idea introducir trminos innecesarios en un modelo.
http://es.wikipedia.org/wiki/Sobreajuste
Uniendo las tcnicas del bloque IV con el anlisis de regresin podemos predecir el tiempo que tardar un algoritmo
en ejecutarse.
La funcin calculo de abajo tiene complejidad cuadrtica. Tu misin es estimar el tiempo que tardar en ejecutarse
cuando la llamemos con el valor K=10000, usando como informacin los tiempos que tard al ejecutarla con valores
menores.
Mide los tiempos de ejecucin para los valores 1000, 2000, 3000, 4000 y 5000.
Asume que el tiempo se cie al modelo: T=a*K^2.
7.5.5 5. Alometra
La siguiente serie de datos est extrada de un artculo de biologa en el que se estudia el dimetro del ojo de un animal
(D) como funcin de su peso (P). Usamos slo los datos de los primates.
datos =[[0.3300, 12.544],
[4.1850, 17.278],
[2.9000, 17.979],
[0.2000, 12.938],
[167.5000, 22.500],
[72.3416, 24.521],
[9.2500, 17.599],
[6.0000, 19.176],
[51.5000, 19.000],
[19.5100, 19.750],
[0.1150, 8.070]]
.
Dibuja los datos. Intenta pensar qu tipo de curva podra ajustar esos datos: bastar un modelo lineal?
Ajusta un modelo del tipo D = a*P^b, con dos parmetros a y b .
Define dos nuevas variables: U = log(P) y V=log(D). Transforma los datos de las variables (P,D) en datos para
las variables (U,V).
Ajusta un modelo lineal en el que U sea la variable independiente y V la variable dependiente.
Deduce a partir de ese modelo un modelo para P y D. Dibuja ese modelo. En este problema, el resultado es
bastante similar al obtenido con el mtodo anterior.
El objeto de la criptografa es poder comunicarse con otras personas encriptando nuestros mensajes de tal modo que
si alguien encuentra el mensaje codificado no sepa lo que dice, pero que si el mensaje llega a su destino, esa persona
pueda recuperar el mensaje original. Por ejemplo, podemos cambiar todos los usos de la palabra tanque por la palabra
paloma , para que quien encuentr el mensaje piense que hablamos de otra cosa. Hoy vamos a estudiar algunos mtodos
matemticos para encriptar mensajes basados en la teora de nmeros.
Para poder aplicar estos mtodos criptogrficos, tenemos que codificar la informacin que queremos mandar como
una secuencia de nmeros enteros. Por ejemplo, podemos hacer corresponder un nmero a cada posible letra. Para
no preocuparnos de la codificacin de carateres, usaremos un alfabeto simplificado, y el cdigo de cada letra ser la
posicin que ocupa en el alfabeto.
sage: #nuestro alfabeto son el espacio en blanco y las mayusculas sin acentos
sage: alfabeto= ABCDEFGHIJKLMNOPQRSTUVWXYZ
sage: L=len(alfabeto)
sage: def codifica_letra(letra):
... return alfabeto.index(letra)
sage: def decodifica_letra(n):
... return alfabeto[n]
sage: codifica_letra(A)
1
sage: decodifica_letra(1)
A
Ejercicio : Usa la funcin ord para implementar codifica_letra con menor complejidad.
Y ahora para una cadena del tirn
sage: def codifica_cadena(texto):
... return [codifica_letra(c) for c in texto]
...
sage: def decodifica_cadena(lista):
... return .join([decodifica_letra(j) for j in lista])
Para ejecutar el cifrado de Csar de clave k , sustituimos cada letra del mensaje original por la letra que est k posiciones
a la derecha (dando la vuelta al alfabeto si nos pasamos de largo). Por ejemplo, si k=3 :
CENA -> FHPD
Para implementar este mtodo criptogrfico, tomamos una cadena de caracteres, la codificamos en una secuencia de
nmeros, le sumamos k a cada cdigo, tomamos el resto de dividir por la longitud del alfabeto y despus decodificamos
los nmeros en letras para volver a tener una cadena de caracteres.
sage: L=len(alfabeto)
sage: def cesar(texto, k):
... Cesar...
...
... numeros = codifica_cadena(texto)
... encriptado = [(j+k) %L for j in numeros]
... texto_encriptado = decodifica_cadena(encriptado)
... return texto_encriptado
En el sistema RSA, un nmero menor que N (que puede representar texto, o cualquier otra cosa), se encripta elevndolo
a un exponente mdulo N . La operacin de desencriptado tambin usa la misma operacin, pero con un exponente
distinto. Aunque podramos usar la misma funcin para las tareas de encriptar y desencriptar, preferimos usar dos
funciones distintas por claridad.
Encriptado
Cada nmero de la secuencia se eleva al exponente e mdulo N . Por tanto, para encriptar se necesita el par formado
por N y e , que llamaremos la clave pblica.
x xe (mod N )
Desencriptado
Cada nmero de la secuencia se eleva al exponente d mdulo N . Para desencriptar se necesita el par formado por N y
d , que llamaremos la clave privada.
y y d (mod N )
Por ejemplo, usamos de forma naive el mtodo RSA para encriptar el cdigo de cada letra del mensaje.
sage: texto = HOLA MUNDO
sage: p=29; q=31;
sage: N=p*q
sage: e=13
sage: d=517
sage: clave_publica=(N,e)
sage: print texto
sage: numeros = codifica_cadena(texto)
sage: encriptado = encripta_RSA(numeros, N, e)
sage: print encriptado
HOLA MUNDO
[47, 27, 389, 1, 0, 879, 301, 524, 312, 27]
Anlisis de frecuencias
Si usamos una misma clave de cifrado para un texto lo bastante largo, eventualmente encontramos repeticiones. En los
dos cifrados anteriores, una misma letra se codifica siempre a la misma letra. Estudiando las frecuencias de aparicin
de cada carcter, podemos averiguar cosas sobre la clave de cifrado. Por ejemplo, si en un texto en castellano encriptado
con el cifrado de Csar al letra ms repetida es la D, probablemente la clave sea 3, que se la que lleva la A (la letra
ms frecuente en la mayora de textos en castellanos) en la D. En el cifrado RSA letra a letra, descifrar el texto es
slo ligeramente ms difcil.
Veremos ms sobre este punto en el ejercicio a entregar.
Para tener esperanzas de conseguir un cifrado resistente al anlisis de frecuencias, tenemos que agrupar los cdigos de
varias letras en un slo nmero ms grande.
La tarea de codificacin consta por tanto de dos partes:
Sustituir cada carcter por su cdigo numrico, de la misma forma que hicimos antes.
Agrupar un bloque de varios nmeros en un slo nmero ms grande. El sistema es similar al usado cuando nos
daban una lista con los dgitos de un nmero y tenamos que recuperar el nmero. A una secuencia {n1 , . . . , nb }
de nmeros entre 0 y L-1 le hacemos corresponder un nmero entre 0 y Lb 1:
X
nj Lbj1
j
Las operaciones inversas son similares, ahora tenemos que recuperar el texto a partir de la secuencia de nmeros:
1. Convertir los nmeros entre 0 y Lb 1 en bloques de b nmeros entre 0 y L .
2. Poner los nmeros de cada bloque unos a continuacin de otros.
3. Sustituir los nmeros por los caracteres con esos cdigos.
4. Convertir la lista de caracteres en una cadena de caracteres usando join .
sage: def decodifica_letra(n):
... return alfabeto[n]
sage: def numero2bloque(n,b):
... bloque=[]
... for i in range(b):
... bloque.append(n %L)
... n=n//L
... bloque.reverse()
... return bloque
sage: def decodifica_mensaje(secuencia,b):
... Convierte una secuencia de numeros en una secuencia de letras
...
... bloques=[numero2bloque(numero,b) for numero in secuencia]
... numeros=[]
... for b in bloques:
... numeros.extend(b) #extiende numeros con los numeros del bloque b
... letras=[decodifica_letra(k) for k in numeros]
... mensaje=.join(letras)
... return mensaje
Uniendo los pasos de codificar un texto y encriptarlo, componemos estas dos funciones que trabajan directamente con
una cadena de caracteres y una clave RSA.
sage: def encripta_mensaje(mensaje, clave_publica):
... Encripta una cadena de texto siguiendo el metodo RSA
... clave_publica es la tupla formada por N y e
...
... N,e = clave_publica
Para que las operaciones de encriptado y desencriptado sean inversas una de la otra, se tiene que verificar, para cual-
quier x :
xde = x(modN )
Los nmeros siguientes tienen esta propiedad, as que al desencriptar deberamos recuperar el mensaje original. El
nmero N es el producto de dos nmeros primos p y q .
sage: p=29; q=31;
sage: N=p*q
sage: e=13
sage: d=517
sage: clave_publica=(N,e)
sage: mensaje_encriptado=encripta_mensaje(mensaje, clave_publica)
sage: print mensaje_encriptado
[193, 90, 470, 378, 449, 252, 66, 474, 0]
sage: clave_privada=(N,d)
sage: desencripta_mensaje(mensaje_encriptado,clave_privada)
CITA EN EL PATIO
Veamos ahora cmo encontrar pares de clave pblica y privada arbitrariamente grandes. Necesitamos nmeros N , e y
d tales que
xde = x(mod N )
para cualquier 1 < x < N , pero adems no debe ser fcil encontrar d a partir de e :
Tomamos N igual al producto de dos primos muy grandes.
Buscamos e que sea primo con (N ) = (p 1)(q 1).
Gracias al paso anterior, existe el nmero d , inverso de e mdulo (N ).
Gracias al teorema de Euler : xde = x(mod N ) para cualquier x.
Comprobamos que todo ha salido bien encriptando un mensaje y luego desencriptndolo. Usamos un alfabeto ms
grande para poder encriptar un texto ms largo.
sage: #La u delante de la cadena indica que contiene caracteres internacionales
sage: #codificados en el estandar unicode
sage: alfabeto = u abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$ %&\()*+,-./:;<=>?@[\\]
sage: L=len(alfabeto)
SIPDIS
La seguridad del sistema RSA se basa en que calcular la clave privada en funcin de la clave pblica requiere mucho
tiempo de cmputo. Pero si conocemos la factorizacin de N, conocemos el nmero (N ), y podemos calcular el
exponente de desencriptado.
El pilar del sistema es que factorizar nmeros grandes lleva mucho ms tiempo de cmputo que encontrar nmeros
primos grandes. Si dedicamos un poco ms de tiempo a buscar nmeros primos un poco ms grandes, el tiempo
necesario para factorizar el producto de los nmeros aumenta en una proporcin mucho mayor.
sage: %time
sage: tamanyo = 1e10
sage: K=randint(tamanyo,2*tamanyo)
sage: p=next_prime(K)
sage: K=randint(tamanyo,2*tamanyo)
sage: q=next_prime(K)
sage: N=p*q
sage: print p,q,N
11434278431 11245017313 128578658918257475903
CPU time: 0.00 s, Wall time: 0.00 s
sage: %time
sage: factor(N)
11245017313 * 11434278431
CPU time: 0.01 s, Wall time: 0.10 s
sage: %time
sage: tamanyo = 1e20
sage: K=randint(tamanyo,2*tamanyo)
sage: p=next_prime(K)
sage: K=randint(tamanyo,2*tamanyo)
sage: q=next_prime(K)
sage: N=p*q
sage: print p,q,N
167630755805843746343 163492503135211998401 27406371869144865602492332625985565597543
CPU time: 0.00 s, Wall time: 0.00 s
sage: %time
sage: factor(N)
163492503135211998401 * 167630755805843746343
CPU time: 0.24 s, Wall time: 0.59 s
sage: %time
sage: tamanyo = 1e30
sage: K=randint(tamanyo,2*tamanyo)
sage: p=next_prime(K)
sage: K=randint(tamanyo,2*tamanyo)
sage: q=next_prime(K)
sage: N=p*q
sage: print p,q,N
1775360778775552738367426329231 1551261724291569068929331402687 2754049222922986839254015328851480115
CPU time: 0.03 s, Wall time: 0.02 s
sage: %time
sage: factor(N)
1551261724291569068929331402687 * 1775360778775552738367426329231
CPU time: 17.48 s, Wall time: 17.93 s
7.7 Ejercicios
sage: print L
7.7.1 1.
Implementa funciones para cifrar y descifrar textos usando como clave un entero 0 < k < L, segn la regla:
Cambia el carcter de ndice j por el carcter de ndice j*k
Nota: Qu nmeros k son aceptables como claves de cifrado?
7.7.2 2.
Escribe una funcin que reciba como argumento una cadena de texto cuyos caracteres estn todos en alfabeto , y
que devuelva una lista de longitud N que contenga las frecuencias de aparicin de cada carcter (el nmero total de
veces que cada carcter aparece en el texto).
7.7.3 3.
Sabiendo que el texto de abajo ha sido encriptado con un cifrado de Csar (y que est escrito en latn), encuentra el
texto original usando el anlisis de frecuencias.
sage: texto=#SJUBOOJDVNAOPOANJOVTABFNVMBUJPOFAWPDJTaARVBFAJMMJAJVDVOEJPSATVQQFUFCBUaARVBNANFUVAOFA
7.7.4 4.
El cifrado de Vigenre es otro cifrado clsico fcil de implementar. Este mtodo no es muy distinto del cifrado de
Csar. Veamos cmo funciona cuando el alfabeto son el espacio en blanco y las 26 letras maysculas.
La clave es una palabra, por ejemplo: COSA. Para encriptar un texto como por ejemplo: EN UN LUGAR DE LA
MANCHA, seguimos estos pasos:
Convertimos la clave y la cadena de texto a encriptar en nmeros.
COSA \-> [3, 15, 19, 1]
EN UN LUGAR DE LA MANCHA \-> [5, 14, 0, 21, 14, 0, 12, 21, 7, 1, 18, 0, 4, 5, 0, 12, 1, 0, 13, 1,
Repitiendo la clave tantas veces como sea necesario, sumamos los nmeros del texto a encriptar con los nmeros
de la clave:
[3, 15, 19, 1, 3, 15, 19, 1, 3, 15, ...
[3+5, 15+14, 19+0, +21, 3+14, 15+0, 19+12, 1+21, 3+7, 15+1,...
[8, 2, 19, 22, 17, 15, 4, 22, 10, 16, ... (mod 27)
Implementa una funcin que acepte como argumentos el texto a encriptar y la clave de encriptado, y devuelva el texto
cifrado, y otra que sirva para desencriptar textos cifrados.
7.7.5 5.
El artculo de la Wikipedia en ingls tiene un buen anlisis del cifrado de Vigenre y las posibles formas de descifrar
textos sin tener la clave:
http://en.wikipedia.org/wiki/Vigenere_cipher#Cryptanalysis
Sabiendo que la clave del siguiente cifrado tena longitud 5, desencripta este texto en ingls:
sage: alfabeto = u abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$ %&()*+,-./:;<=>?@[\\]^
7.7.6 6.
Sabiendo que el texto original empezaba por The machine, descifra el siguiente texto cifrado (en ingls). Averigua
tambin la clave para seguir usndola en el futuro.
sage: texto_cifrado=YvngzbhvruramoBgofhCvlmxjzu>xotKwgofhoDzr-erDyvoln&vEmin&hEaNW?gNmqwnkmdtrniEf
7.7.7 7.
Alice enva un mensaje a Bob encriptado mediante RSA, pero Eve lo intercepta. El mensaje es:
[70, 641, 216, 390, 291, 757, 711]
Finalmente, consigue que Alice desencripte el mensaje producto, y obtiene la cadena de cdigos:
[307, 723, 333, 157, 462, 184, 657]
Explica cmo puede Eve recuperar el mensaje original, sin necesidad de calcular la clave privada de Alice.
Nota: aunque parezca complicado conseguir que Alice desencripte un mensaje arbitrario, ste es precisamente el
mtodo que se usa para firmar mensajes mediante RSA. Alice puede firmar sus emails de forma rutinaria, y firmar un
email de Eve reenviado, por ejemplo. Es por este motivo que se desaconseja usar la misma clave para firmar mensajes
y para encriptarlos:
http://en.wikipedia.org/wiki/Rsa#Signing_messages
http://en.wikipedia.org/wiki/Rsa#Attacks_against_plain_RSA
7.7.8 8.
Estudiamos un sistema criptogrfico que funciona de la forma siguiente (la clave de encriptado es k ):
Desplazamos el primer carcter de la cadena que vamos a encriptar k posiciones para obtener el primer carcter
de la cadena encriptada. En otras palabras, le sumamos k al cdigo del primer carcter, al igual que hacamos
con el cifrado de Csar.
El segundo carcter de la cadena encriptada es aquel cuyo cdigo es la suma de los cdigos de los dos primeros
caracteres de la cadena original.
El carcter n-simo de la cadena encriptada es aquel cuyo cdigo es la suma de los cdigos de los caracteres
n-simo y (n-1)-simo de la cadena original.
Se te pide:
1. Escribir una funcin que encripta, segn este esquema
2. Escribir una funcin que desencripta, segn este esquema
Hiptesis:
Estado
Un instante est caracterizado por los tiempos esperados de cada de cada bola. Un estado slo es vlido si cada bola
en el aire caer en un instante futuro distinto. Representamos un estado mediante una secuencia de bola * y no bola
_. En la posicin i-sima ponemos una bola si se espera que una bola caiga dentro de i instantes y no-bola si ninguna
bola caer dentro de i instantes. Por ejemplo **__* quiere decir que hay tres bolas en el aire y que caern dentro
de 1, 2 y 5 instantes.
Nota: Al estado en el que las bolas caern en los instantes sucesivos inmediatos, lo denominamos estado fundamental
( ***_ , ***__ , lo que toque).
Altura
La fuerza y la precisin del malabarista limitan el nmero mximo de instantes en el que futuro al que puede enviar
una bola. A este nmero lo llamamos altura .
Hay una cantidad finita de estados posibles con k bolas y una altura h. Concretamente, cada una de las k bolas caer
en un instante entre 1 y h que es distinto para cada bola, luego se trata de elegir k nmeros entre 1 y h, y la cantidad de
formas de hacer esta eleccin es hk
sage: bolas = 3
sage: altura = 4
sage: estados = [ .join((* if j in ss else _) for j in range(altura))
... for ss in Subsets(range(altura), bolas) ]
sage: estados
[***_, **_*, *_**, _***]
Transiciones
A qu estados podemos cambiar partiendo de un estado dado? En el instante siguiente todas las bolas estn un
instante ms prximas a caer en la mano del malabarista, luego pasamos por ejemplo de _*_** a *_**_ , pero
si tenamos una bola a punto de caer, el malabarista la recibe y la lanza de forma que caiga dentro de un cierto nmero
de instantes de tiempo, y puede elegir cualquier momento futuro en el que no est prevista la cada de ninguna otra
bola. Por ejemplo, del estado *_**_ podemos pasar a **__, _** * _ o _**_ * .
Junto a cada posible transicin, guardamos la altura a la que lanzamos la bola (usando un 0 cuando no hacemos nada).
sage: def opciones(estado):
... pasa = estado[1:] + _
... if estado[0]==_: return {pasa:0}
... opciones = {}
... for j,s in enumerate(pasa):
... if s==_:
... opciones[pasa[:j] + *+ pasa[j+1:]] = j + 1
... return opciones
sage: transiciones = dict((estado,opciones(estado)) for estado in estados)
sage: transiciones
{***_: {***_: 3, **_*: 4}, _***: {***_: 0}, *_**: {***_: 1, _***: 4}, **_*: {***_
Dibujamos el grafo
sage: g = grafo_malabar(3,4)
sage: g.show(edge_labels=True, layout=circular, vertex_size=300, figsize=8)
Tambin dibujaremos los diagramas con graphviz, porque las etiquetas se ven ms claras (graphviz debe estar instala-
do).
sage: attach(DATA + graphviz.sage)
sage: graphviz(grafo_malabar(3,4))
sage: graphviz(grafo_malabar(2,4))
sage: graphviz(grafo_malabar(3,5))
En pocas palabras, los grafos permiten representar situaciones muy diversas de forma muy uniforme.
Requerimientos del espectculo de lo ms variopinto se pueden traducir en restricciones sobre el grafo de posibles
malabares.
Ejemplo prctico
Queremos estar a distancia menor o igual que 4 del estado ___***, por ejemplo porque ese estado nos da tiempo
para hacer un truco que hemos preparado, o para saludar a un viandante que ha echado una moneda en el plato.
sage: g = grafo_malabar(3,5)
sage: ds = g.distance_all_pairs()
sage: v0 = __***
sage: new_labels = {}
sage: for v in g:
... new_labels[v] = (v, ds[v][v0])
sage: g.relabel(new_labels)
sage: graphviz(g)
sage: g = grafo_malabar(3,5)
sage: ds = g.distance_all_pairs()
sage: v0 = __***
sage: vs0 = [v for v in g if ds[v][v0]<=4]
sage: sg = g.subgraph(vs0)
sage: graphviz(sg)
Ahora podemos dejar una pelota en la cabeza por tiempo indefinido. Representamos por un 8 una cabeza con una
pelota encima, y por una o una cabeza sin pelota encima.
Por ejemplo, para tres pelotas y altura 4, los posibles estados tienen dos pelotas en el aire y una en la cabeza, o tres
pelotas en el aire y ninguna en la cabeza.
sage: bolas = 3
sage: altura = 4
sage: estados_con_cabeza = ([(o + .join((* if j in ss else _)
... for j in range(altura)))
... for ss in Subsets(range(altura), bolas) ] +
... [(8 + .join((* if j in ss else _)
... for j in range(altura)))
... for ss in Subsets(range(altura), bolas-1) ])
sage: estados_con_cabeza
[o***_, o**_*, o*_**, o_***, 8**__, 8*_*_, 8*__*, 8_**_, 8_*_*, 8__**]
sage: g = grafo_malabar_con_cabeza(3, 4)
sage: graphviz(g)
Y mucho ms
Y bien, qu hacemos una vez tenemos un grafo? Para empezar, nos podemos preguntar si el grafo tiene algunas
propiedades tpicas de la teora de grafos que pueden ser interesantes:
El grafo es fuertemente conexo ? Es decir, se puede pasar de un estado a cualquier otro?
El grafo de malabares es euleriano ? Es decir, existe un circuito que pasa por cada arista exactamente una
vez ?
El grafo de malabares es hamiltoniano ? Es decir, existe un circuito que pasa por cada vrtice exactamente
una vez ?
sage: for j in range(2,5):
... for k in range(j+1,j+4):
... print el grafo malabar con %d bolas y altura %d\
... %ses fuertemente conexo %(j,k,
... if grafo_malabar(j,k).is_strongly_connected() else no )
el grafo malabar con 2 bolas y altura 3 es fuertemente conexo
el grafo malabar con 2 bolas y altura 4 es fuertemente conexo
el grafo malabar con 2 bolas y altura 5 es fuertemente conexo
el grafo malabar con 3 bolas y altura 4 es fuertemente conexo
el grafo malabar con 3 bolas y altura 5 es fuertemente conexo
el grafo malabar con 3 bolas y altura 6 es fuertemente conexo
el grafo malabar con 4 bolas y altura 5 es fuertemente conexo
el grafo malabar con 4 bolas y altura 6 es fuertemente conexo
el grafo malabar con 4 bolas y altura 7 es fuertemente conexo
Podemos movernos libremente sobre el grafo, sin necesidad de seguir uno de los patrones de siteswap como 441 o
531. Creamos a continuacin una animacin que cada segundo muestra el estado en que nos encontramos dentro del
grafo y el nmero asociado a la arista que seguimos para llegar al siguiente estado (que es la altura a la que tenemos
que lanzar la bola).
sage: def camino_aleatorio(g, estado_inicial, longitud=10):
... vs = g.vertices()
... vs.remove(estado_inicial)
... ps = [g.plot(edge_labels=True, layout=circular,
... vertex_size = 400,
... vertex_colors={red:[estado_inicial],
... green:vs})+
... text(..., (0,0), fontsize=200)]
... v0 = estado_inicial
... for j in xrange(longitud):
... v0,v1,altura = choice(g.outgoing_edges(v0))
... if v1 != v0:
... vs.remove(v1)
... vs.append(v0)
... ps.append(g.plot(edge_labels=True, layout=circular,
... vertex_size = 400,
... vertex_colors={red:[v1],
... green:vs}) +
... text(altura, (0,0), fontsize=200))
... v0 = v1
... return animate(ps, axes = False)
sage: g = grafo_malabar(3,5)
sage: #Partimos del estado fundamental
sage: a = camino_aleatorio(g, ***__, longitud = 10)
sage: a.show(delay = 200)
Si cada vez que lanzamos una bola escogemos la altura entre las posibilidades viables con igual probabilidad , cunto
tiempo pasaremos en cada estado?
Para empezar a entender el problema, trabajamos con 3 bolas y altura 4, y nos planteamos qu pasara si repetimos el
experimento 1000 veces, partiendo por ejemplo del estado fundamental ***_. En el primer lanzamiento, podemos
elegir entre hacer un lanzamiento a altura 3 4, de modo que 500 veces estaremos en el estado ***_ y otras 500 en
el estado **_*. Si hacemos dos lanzamientos, tenemos dos decisiones, lo que da lugar a 4 caminos, y seguiremos
cada uno de los cuatro en 250 ocasiones. Sin embargo, dos de esos caminos llevan al estado fundamental, luego en
500 ocasiones nos encontramos en ***_, en 250 en **_* y en otras 250 en *_**.
Por supuesto, lo importante no es el nmero excato, sino la proporcin sobre el nmero de lanzamientos (es decir, 1/2
de las veces nos encontramos en ***_, 1/4 de las veces en **_* y el otro 1/4 de las veces en *_**.). La
manera habitual de registrar el experimento anterior en matemticas es guardar estas proporciones, o probabilidades
, en un vector donde guardamos las probabilidades en el orden correspodiente a [***_, _*, *_, _***]. En
el ejemplo anterior, decimosque estamos en el estado [1/2, 1/4, 1/4, 0]. El estado inicial era el estado determinista
[1,0,0,0], y el estado en el primer instante despus de empezar era [1/2, 1/2, 0, 0].
Calculamos el vector de probabilidades hasta despus de 10 instantes.
sage: g = grafo_malabar(3,4)
sage: print g.vertices()
sage: print Partiendo de un estado inicial [1,0,...0])
sage: M = matrix([row/sum(row) for row in g.adjacency_matrix().rows()])
sage: estado = vector( [1]+[0]*(len(g.vertices())-1) )
sage: n3 = lambda x: x.n(digits = 3)
sage: for k in range(10):
... print k, :, map(n3, estado)
... estado = estado*M
[***_, **_*, *_**, _***]
Partiendo de un estado inicial [1,0,...0])
0 : [1.00, 0.000, 0.000, 0.000]
1 : [0.500, 0.500, 0.000, 0.000]
2 : [0.500, 0.250, 0.250, 0.000]
3 : [0.500, 0.250, 0.125, 0.125]
4 : [0.562, 0.250, 0.125, 0.0625]
5 : [0.531, 0.281, 0.125, 0.0625]
6 : [0.531, 0.266, 0.141, 0.0625]
7 : [0.531, 0.266, 0.133, 0.0703]
8 : [0.535, 0.266, 0.133, 0.0664]
9 : [0.533, 0.268, 0.133, 0.0664]
Observa que hemos usado un truco para calcular las probabilidades en un estado a partir de las probabilidades en el
anterior: hemos construido una matriz con las probabilidades de transicin a partir de cada estado. Para obtener las
probabilidades en un estado a partir de las probabilidades en el anterior, multiplicamos el vector de probabilidades por
la matriz de probabilidades de transicin.
Esta forma de calcular las transiciones se basa en el teorema de la probabilidad total . La probabilidad de estar en el
estado I en tiempo k+1 es igual a la suma para cada estado J del producto de la probabilidad del estado J en tiempo k
por la probabilidad de transicin de J a I:
X
Pk+1 (I) = Pk (J)Pkk+1 (I|J)
J
Pk+1 = Pk M
La matriz de probabilidades de transicin se obtiene de forma sencilla a partir de la matriz de adyacencia del grafo,
que tiene un 1 en la posicin (i,j) si hay una flecha del vrtice i-simo al j-simo, y 0 en otro caso.
sage: show(g.adjacency_matrix())
sage: M = matrix([row/sum(row) for row in g.adjacency_matrix().rows()])
sage: show(M)
1 1 0 0
1 0 1 0
1 0 0 1
1 0 0 0
1 1
2 2 0 0
1 1
2 0 2 0
1 1
2 0 0 2
1 0 0 0
Otra cosa curiosa que observamos al calcular las probabilidades de transicin es que parecen acercarse al vector
[0.533, 0.267, 0.133, 0.0667], y que una vez se llega a ese vector las probabilidades ya no cambian. Para ms inri,
comprobamos que se llega al mismo vector independientemente del estado inicial .
sage: print Partiendo de un estado inicial [0,...0,1]
sage: estado_inicial = vector( [0]*(len(g.vertices())-1) + [1] )
sage: print map(n3, estado_inicial*M)
sage: print map(n3, estado_inicial*M^2)
sage: print map(n3, estado_inicial*M^5)
sage: print map(n3, estado_inicial*M^10)
sage: print map(n3, estado_inicial*M^20)
Partiendo de un estado inicial [0,...0,1]
[1.00, 0.000, 0.000, 0.000]
[0.500, 0.500, 0.000, 0.000]
[0.562, 0.250, 0.125, 0.0625]
[0.533, 0.268, 0.133, 0.0664]
[0.533, 0.267, 0.133, 0.0667]
0,500 0,500 0,000 0,000
0,500 0,000 0,500 0,000
0,500 0,000 0,000 0,500
1,00 0,000 0,000 0,000
0,500 0,250 0,250 0,000
0,500 0,250 0,000 0,250
0,750 0,250 0,000 0,000
0,500 0,500 0,000 0,000
0,531 0,281 0,125 0,0625
0,531 0,250 0,156 0,0625
0,531 0,250 0,125 0,0938
0,562 0,250 0,125 0,0625
0,533 0,267 0,134 0,0664
0,533 0,267 0,133 0,0674
0,534 0,267 0,133 0,0664
0,533 0,268 0,133 0,0664
0,533 0,267 0,133 0,0667
0,533 0,267 0,133 0,0667
0,533 0,267 0,133 0,0667
0,533 0,267 0,133 0,0667
La teora de cadenas de Markov nos dice que se alcanza una distribucin de probabilidad estable, que es nica e
independiente del punto de partida porque el grafo es fuertemente conexo. El vector con las probabilidades es un
autovector por la izquierda de autovalor 1. Como el grafo es fuertemente conexo y tiene una arista que une un vrtice
consigo mismo, la cadena es irreducible y este autovector es nico salvo escala. Escogemos el vector tal que la suma
de sus componentes es uno.
sage: #print M.eigenvectors_left()[0][1]
sage: distribucion_estable = (M - 1).left_kernel().basis()[0]
sage: distribucion_estable /= sum(distribucion_estable)
sage: print g.vertices()
sage: print distribucion_estable
sage: print [n3(p) for p in distribucion_estable]
[***_, **_*, *_**, _***]
(8/15, 4/15, 2/15, 1/15)
[0.533, 0.267, 0.133, 0.0667]
Como vemos, la probabilidad de estar en cada nodo al cabo de un nmero grande de iteraciones no depende del estado
inicial. Esta probabilidad se puede usar por tanto para medir la importancia de cada nodo. Por ejemplo, Google afirma
que su algoritmo original para asignar una importancia a cada pgina web estaba basado en esta idea.
Referencia: Pablo Fernndez Gallardo: el secreto de google.
Aunque podemos movernos indefinidamente por el grafo sin repetirnos, antes o despus pasaremos dos veces por el
mismo nodo y en ese momento habremos completado un circuito .
Circuitos primos
Si un circuito pasa dos veces por el mismo sitio, es composicin de circuitos elementales o primos, en los que no se
pasa dos veces por el mismo vrtice.
Los caminos en el grafo son composicin de estos caminos elementales. En cierto modo, es equivalente a cmo el
desarrollo decimal de un nmero puede no ser peridico (por ejemplo, el desarollo de ), pero todos los nmeros se
pueden desarrollar con los mismo dgitos.
Algoritmo de Johnson
La teora de circuitos cerrados en un grafo no dirigido es preciosa y tiene una descripcin matemtica muy elegante,
que en ltima instancia se debe a se puede definir una suma de caminos. Sin embargo, para grafos dirigidos no se puede
definir la suma de caminos y las matemticas no dan una visin tan clara del conjunto de circuitos. Afortunadamente,
existen algoritmos eficientes para encontrar todos los circuitos elementales en un grafo dirigido.
Nota: si listamos la altura a la que debemos lanzar cada pelota (la etiqueta de cada arista), recuperamos la notacin
siteswap .
Todos los posibles trucos con 3 bolas y altura a lo sumo 4: listamos los circuitos del grafo malabar con 3 bolas y altura
mxima 4, en notacin siteswap.
sage: attach(DATA + circuits.py)
sage: g = grafo_malabar(3,4)
sage: for ls in circuits(g):
... aristas = [(ls[j],ls[j+1])
... for j in range(len(ls)-1)]
... sg = g.subgraph(edges = aristas)
... print [g.edge_label(v1,v2) for v1,v2 in aristas]
... graphviz(sg)
[3]
[4, 2]
[4, 4, 1]
[4, 4, 4, 0]
sage: g = grafo_malabar(3,4)
sage: g.show(edge_labels=True, layout=circular, vertex_size=300, figsize=8)
sage: graphs_list.show_graphs([g.subgraph(edges = [(ls[j],ls[j+1])
... for j in range(len(ls)-1)])
... for ls in circuits(g)])
4,1,3,o4,4,4,0,o0
4,1,3,o4,4,1,o0
4,1,3,o4,2,o0
4,1,3,0
4,1,3,o3,o0
4,4,0,o4,o0,4,o4,4,0,o0
4,4,0,o4,o0,4,o4,1,o0
4,4,0,o4,o0,4,o2,o0
4,4,0,o4,o0,1
4,4,0,o4,4,4,0,o0
4,4,0,o4,4,1,o0
4,4,0,o4,2,o0
4,4,0,0
4,4,0,o3,4,o0,1
4,4,0,o3,o0
4,4,o4,0,4,o0,4,o4,o0,0
4,4,o4,0,4,o0,1
4,4,o4,0,4,o0,3,0
4,4,o4,0,4,4,o0,0
4,4,o4,0,o0
4,4,o1,4,o0,4,o4,o0,0
4,4,o1,4,o0,1
4,4,o1,4,o0,3,0
4,4,o1,4,4,o0,0
4,4,o1,o0
4,2,o4,o0,4,o4,4,0,o0
4,2,o4,o0,4,o4,1,o0
4,2,o4,o0,4,o2,o0
4,2,o4,o0,1
4,2,o4,4,4,0,o0
4,2,o4,4,1,o0
4,2,o4,2,o0
4,2,0
4,2,o3,4,o0,1
4,2,o3,o0
4,0
4,o4,o0,o4,o0
4,o4,o0,o3,4,o0
4,o4,4,0,4,o0
4,o4,1,4,o0
4,o2,4,o0
3,o4,o0
3,o3,4,o0
o4,4,o0
o3,4,4,o0
4,4,4,0
4,4,1
4,2
3
7.8.7 Ejercicios
Escribe el grafo de malabares con dos pelotas de un color y una tercera de otro color y altura mxima 4. Si la
tercera pelota es una manzana, identifica los estados en los que puedes pegarle un mordisco.
Pepito est aprendiendo a hacer malabares con cuchillos. Se ve capaz de lanzar y recibir los cuchillos a altura
5 pero no quiere lanzar dos cuchillos seguidos tan alto por si se hace un lo al recogerlos. Modifica el grafo de
tres objetos y altura 5 para evitar lanzar dos objetos a altura 5 seguidos.
Dos malabaristas pueden hacer malabares juntos en modo sncrono o canon. Cada malabarista puede pasarle cada
pelota al otro o lanzarla para recogerla ella misma.
En modo sncrono, en cada instante, ambos malabaristas actan a la vez (cada uno puede hacer un lanzamiento distinto,
por supuesto). En modo canon, los tiempos de uno y otro malabarista no estn sincronzados, sino que tienen un retraso
de medio tiempo). Por ejemplo, con dos malabaristas, la secuencia es Malabarista 1 mano I, Malabarista 2 mano I,
Malabarista 1 mano D, Malabarista 2 mano D.
Tienes ms abajo la construccin del grafo de n malabaristas en modo sncrono.
Genera el grafo de dos malabaristas y un total de 5 bolas trabajando en modo canon, donde cada malabarista
puede enviarse la pelota a s mismo, para recogerla dentro de a lo sumo tres instantes, o al otro malabarista, con
la misma limitacin.
Cuestiones
Grafo de varios malabaristas que tienen todos la misma altura como cota y con una cantidad dada de bolas en total.
Observa que si en un instante varios malabaristas deben lanzar una bola, slo anotamos los destinos de esas bolas, sin
distinguir quin hace qu lanzamiento. Es decir, que no distinguimos si cada malabarista se lanza a s mismo o si cada
uno le lanza al otro. Representamos cada estado mediante la unin de las listas de espera de todos los malabaristas, y
cada arista mediante pares que dicen el malabarista que debe recibir y la altura del lanzamiento.
sage: def opcionesm(estado):
... estado1 = tuple(malabarista[1:] + _ for malabarista in estado)
... bolas = sum(1 if malabarista[0]==* else 0
... for malabarista in estado)
... if not bolas:
... return {estado1:0}
... huecos = [(i, j)
... for i,malabarista in enumerate(estado1)
... for j,s in enumerate(malabarista)
... if s==_]
... opciones = {}
... for objetivos in Combinations(huecos, bolas):
... nuevo_estado = list(estado1)
... for i, j in objetivos:
... cadena = nuevo_estado[i]
... nuevo_estado[i] = cadena[:j] + *+ cadena[j+1:]
... opciones[tuple(nuevo_estado)] = ,.join( %d %d %(i+1,j+1)
... for i, j in objetivos)
... return opciones
sage: def grafo_malabarm(bolas, altura, malabaristas):
... Crea el grafo de malabares para varios malabaristas
...
... Acepta como argumento el numero de bolas y la lista de alturas
... mximas de cada malabarista
... total = altura*malabaristas
... cadenas = [ .join((* if j in ss else _) for j in range(total))
... for ss in Subsets(range(total), bolas) ]
... parte =lambda c: tuple(c[j:j+altura]
... for j in range(0, total, altura))
... estadosm = [parte(c) for c in cadenas]
... transiciones = dict((estado,opcionesm(estado)) for estado in estadosm)
... return DiGraph(transiciones)
...
22,23;13;23;23;12;22
22,23;13;23;23;12;23;12,23;0
22,23;13;23;23;12;23;11,21
22,23;13;23;23;11
22,23;13;23;21
22,23;13;22;22
22,23;13;22;23;12,23;0
22,23;13;22;23;11,23;11
22,23;13;22;23;11,21
13,22;23;23;12;22
13,22;23;23;12;23;21,23;11
13,22;23;23;12;23;12,23;0
13,22;23;23;12;23;11,21
13,22;23;23;11
13,22;23;21
13,22;22;22
13,22;22;23;21,23;11
13,22;22;23;12,23;0
13,22;22;23;11,23;11
13,22;22;23;11,21
12,22;22
12,22;23;21,23;11
12,22;23;21,23;13;23;23;11
12,22;23;21,23;13;23;21
12,22;23;13,21;23;23;11
12,22;23;13,21;23;21
12,22;23;12,23;0
12,22;23;11,23;11
12,22;23;11,21
11,22
11,23;21,23;11
11,23;21,23;12;22
11,23;21,23;13;23;23;12;22
11,23;21,23;13;23;23;11
11,23;21,23;13;23;21
11,23;21,23;13;22;22
11,23;13,21;23;23;12;22
11,23;13,21;23;23;11
11,23;13,21;23;21
11,23;13,21;22;22
11,23;12,23;0
11,23;11,23;12;22
11,23;11,23;11
11,23;12,21;22
11,23;11,21
11,23;12;23
21,23;12;23
21,23;13;23;23;12;23
21,23;13;22;23
13,21;23;23;12;23
13,21;22;23
12,21;23
23
7.8.9 Crditos
Esta presentacin usa el programa Sage para hacer los clculos sobre grafos y la librera graphviz para dibujar los
grafos.
Muchas gracias a Daniel Snchez , y a Nicolas M. Thiry y Florent Hivert por sus comentarios (y a Clara, por supues-
to).
7.8.10 Licencia
En este apndice vamos a aprender el efecto que tienen los link farms o mutual admiration societies en el ndice
de google. Por supuesto hay otros mtodos de link spam ms clsicos, pero estudiamos ste mtodo en concreto
aprovechando lo que aprendimos sobre cadenas de Markov al hablar de grafos.
El PageRank es un algoritmo abstracto que permite dar un valor numrico ( ranking ) a cada nodo de un grafo que
mide de alguna forma su conectividad . Cuantos ms links tiene una pgina web, mayor es su ranking.
Podemos interpretar este ranking de la siguiente forma: cada pgina web es un nodo de un grafo dirigido, que tiene
una arista desde la pgina A hasta la pgina B sii en A hay un link que apunta a B. Comenzamos un camino aleatorio
en un nodo aleatorio del grafo, escogido con igual probabilidad. Con probabilidad d (el damping factor ), saltamos
a uno de sus enlaces, con igual probabilidad. Con probabilidad 1-d, comenzamos desde cero. Hemos construido una
cadena de Markov , que a largo plazo tiene una distribucin de probabilidad estable que tomamos como ranking de
cada nodo: el ranking es la proporcin del tiempo que pasamos en ese nodo en el camino aleatorio descrito arriba.
Para ms informacin podis leer:
http://www.uam.es/personal_pdi/ciencias/gallardo/google.htm
sage: def ranking(g, damping = 0.85):
... #M es la matriz estocastica asociada al grafo
... M = matrix(RDF, [row/sum(row) for row in g.adjacency_matrix().rows()])
... r = M.nrows()
... #T tiene en cuenta el factor de "damping"
... #o la probabilidad de abandonar el camino actual
... #y volver a comenzar desde un nodo aleatorio
... T = damping*M + matrix(RDF, r, [(1 - damping)/r]*(r^2))
...
... #Una aproximacion practica al ranking: tomamos una fila
... #cualquiera de una potencia grande de la matriz
... distribucion_estable = (T^64).rows()[0]
... return dict(zip(g.vertices(),distribucion_estable))
Partimos de un grafo dirigido bastante arbitrario, calculamos el ranking de google de cada nodo, usando el damping
por defecto de 0.15.
sage: edges = [(0, 4), (0, 6), (1, 6), (2, 1), (2, 3), (2, 5),
... (2, 6), (3, 1), (3, 2), (3, 5), (3, 6), (4, 5),
... (5, 0), (5, 2), (6, 0), (6, 1), (6, 7), (7, 1),
... (7, 5)]
sage: g=DiGraph(edges)
sage: show(g, layout=circular)
sage: d = ranking(g)
sage: print [(p, v.n(digits = 3)) for p,v in d.items()]
[(0, 0.152), (1, 0.152), (2, 0.0926), (3, 0.0384), (4, 0.0835), (5, 0.154), (6, 0.240), (7, 0.0868)]
En un primer intento de inflar el ranking del nodo 0, creamos dos nodos, 20 y 21 que apuntan a 0. La tctica apenas es
efectiva.
sage: g=DiGraph(edges)
sage: g.add_vertices([20, 21])
sage: g.add_edges([(20,0), (21,0)])
sage: g.show(layout=circular)
sage: d = ranking(g)
sage: print [(p, v.n(digits = 3)) for p,v in d.items()]
[(0, 0.168), (1, 0.139), (2, 0.0848), (3, 0.0330), (4, 0.0866), (5, 0.148), (6, 0.230), (7, 0.0802),
7.9. Inflar el PageRank controlando los links que salen de tu pgina 289
Laboratorio de Matemticas, Release 2010/2011
Sin embargo, si desde el vrtice 0 tb apuntamos a 20 y 21, la cosa cambia. Ahora los caminos aleatorios que alcanzan
0 se quedan ms tiempo orbitando no muy lejos de este nodo.
sage: g=DiGraph(edges)
sage: g.add_vertices([20, 21])
sage: g.add_edges([(0,20), (20,0), (0,21), (21,0)])
sage: g.show(layout=circular)
sage: d = ranking(g)
sage: print [(p, v.n(digits = 3)) for p,v in d.items()]
[(0, 0.224), (1, 0.117), (2, 0.0717), (3, 0.0302), (4, 0.0625), (5, 0.118), (6, 0.184), (7, 0.0671),
Siguiendo esta misma idea, eliminamos los links legtimos del nodo 0. Ahora todos los caminos aleatorios que llegan
a 0 pasan su tiempo en circuitos muy cortos que contienen a 0. Los links del nodo 0 a los nodos 4 y 6 permitan que
los caminos aleatorios escapasen de la rbita del 0, pero ahora la nica manera de salir es que intervenga el factor de
damping .
sage: g=DiGraph(edges)
sage: g.add_vertices([20, 21])
sage: g.add_edges([(0,20), (20,0), (0,21), (21,0)])
sage: g.delete_edges([(0,4), (0,6)])
sage: g.show(layout=circular)
sage: d = ranking(g)
sage: print [(p, v.n(digits = 3)) for p,v in d.items()]
[(0, 0.333), (1, 0.0738), (2, 0.0459), (3, 0.0248), (4, 0.0150), (5, 0.0603), (6, 0.0928), (7, 0.0413
Esta tctica aumenta el ranking de cualquier nodo, pero si era un nodo muy poco conectado tampoco hace milagros.
En este caso usamos slo un nodo extra, el 20, que es ms efectivo, y el ranking pasa de 0.04 a 0.16.
sage: g=DiGraph(edges)
sage: g.add_vertices([20])
sage: g.add_edges([(3,20), (20,3)])
sage: g.delete_edges([(3,2), (3,1), (3,5), (3,6)])
sage: g.show(layout=circular)
sage: d = ranking(g)
sage: print [(p, v.n(digits = 3)) for p,v in d.items()]
[(0, 0.111), (1, 0.105), (2, 0.0642), (3, 0.160), (4, 0.0640), (5, 0.112), (6, 0.167), (7, 0.0639), (
7.9. Inflar el PageRank controlando los links que salen de tu pgina 291
Laboratorio de Matemticas, Release 2010/2011
Si la pgina falsa tiene un link autntico que aleja el camino aleatorio de nuestro nodo, la tcnica se vuelve mucho
menos efectiva.
sage: g=DiGraph(edges)
sage: g.add_vertices([20])
sage: g.add_edges([(3,20), (20,3), (20,0)])
sage: g.delete_edges([(3,2), (3,1), (3,5), (3,6)])
sage: g.show(layout=circular)
sage: d = ranking(g)
sage: print [(p, v.n(digits = 3)) for p,v in d.items()]
[(0, 0.163), (1, 0.123), (2, 0.0753), (3, 0.0622), (4, 0.0861), (5, 0.138), (6, 0.207), (7, 0.0753),
sage: plot_rule([0,0,0,1,1,0,0,1])
Un nmero que identifica una regla en la notacin de Wolfram, se convierte mediante num2rule en una regla que
pueda entender cellular .
El dibujo que realizamos es un matrix_plot , que pone el pxel (i,j) en blanco si la entrada (i,j) de la matriz es 1, y
lo pone en negro si la entrada de la matriz es un 0.
La regla por defecto es la Regla 110 , uno de los automtas celulares ms famosos.
sage: def num2rule(number):
... if not (0 <= number <= 255):
... raise Exception(Invalid rule number)
... binary_digits = number.digits(base=2)
... return binary_digits + [0]*(8-len(binary_digits))
sage: @interact
sage: def _( N=input_box(label=Number of iterations,default=100),
... rule_number=input_box(label=Rule number,default=110),
... size = slider(1, 11, step_size=1, default=6 ) ):
... rule = num2rule(rule_number)
... M = cellular(rule, N)
... regla_plot = plot_rule(rule)
... plot_M = matrix_plot(M, axes=False)
... plot_M.show( figsize=[size,size])
http://en.wikipedia.org/wiki/Rule_184
Algunas reglas son modelos de fenmenos del mundo real. En concreto, la regla 184 puede servir como modelo del
trfico en una lnea de celdas por donde se mueven los vehculos. Una celda est activa si y slo si contiene un vehculo.
Cada vehculo avanza a la derecha si no hay otro vehculo que se lo impida.
sage: rule184 = num2rule(184)
sage: plot_rule(rule184)
En el ejemplo de debajo, modelizamos dos carreteras que confluyen en una tercera, pasando por un semforo que se
abre alternativamente a una u otra carretera.
sage: l1=30
sage: l2=30
sage: l3=30
sage: turnos=150
sage: #Las tres carreteras
sage: c1 = zero_matrix(ZZ,turnos,l1)
sage: c2 = zero_matrix(ZZ,turnos,l2)
sage: c3 = zero_matrix(ZZ,turnos,l3)
sage: densidad1=0.8
sage: densidad2=0.3
sage: semaforo={1:10,2:10}
sage: abierto=1 #semaforo abierto para c1 0 c2
sage: resto=5
sage: for j in range(turnos):
... #actualizamos el semaforo: restamos un turno y cambiamos al llegar a cero
... resto -= 1
... if resto<=0:
... #cambiamos el semaforo
... abierto=2 if abierto==1 else 1
... #reiniciamos el numero de turnos
... resto = semaforo[abierto]
...
... #actualizamos la carretera 1
... #el primer punto es un 1 o un 0 con probabilidad = densidad1
... c1[j, 0] = 1 if random()<densidad1 else 0
... #el resto, excepto el ltimo, se actualizan con la regla 184
... for k in range(1,l1-1):
... numi = 4*c1[j-1,k-1] + 2*c1[j-1,k] + c1[j-1,k+1]
... c1[j,k] = rule184[numi]
... #actualizamos la carretera 2
... #el primer punto es un 1 o un 0 con probabilidad = densidad2
... c2[j, 0] = 1 if random()<densidad2 else 0
... #el resto, excepto el ltimo, se actualizan con la regla 184
... for k in range(1,l2-1):
... numi = 4*c2[j-1,k-1] + 2*c2[j-1,k] + c2[j-1,k+1]
... c2[j,k] = rule184[numi]
... #actualizamos cerca del semaforo
... if abierto==1:
... numi = 4*c1[j-1,l1-2] + 2*c1[j-1,l1-1] + c3[j-1,0]
... c1[j, l1-1] = rule184[numi]
... numi = 4*c2[j-1,l1-2] + 2*c2[j-1,l1-1] + 1
... c2[j, l1-1] = rule184[numi]
...
... numi = 4*c1[j-1,l1-1] + 2*c3[j-1,0] + c3[j-1,1]
... c3[j, 0] = rule184[numi]
... else:
... numi = 4*c1[j-1,l1-2] + 2*c1[j-1,l1-1] + 1
... c1[j, l1-1] = rule184[numi]
... numi = 4*c2[j-1,l1-2] + 2*c2[j-1,l1-1] + c3[j-1,0]
... c2[j, l1-1] = rule184[numi]
...
... numi = 4*c2[j-1,l1-1] + 2*c3[j-1,0] + c3[j-1,1]
... c3[j, 0] = rule184[numi]
... #actualizamos la carretera 3
... #el resto, excepto el ltimo, se actualizan con la regla 184
... for k in range(1,l3-1):
... numi = 4*c3[j-1,k-1] + 2*c3[j-1,k] + c3[j-1,k+1]
... c3[j,k] = rule184[numi]
... #el ultimo se queda a cero, queriendo decir que sale del sistema
sage: plots=[]
sage: for j in range(turnos):
... c=max(l1,l2)
... M=matrix(ZZ,3,c+l3)
... M[0,(c-l1):c]=c1[j,:]
... M[2,(c-l2):c]=c2[j,:]
... M[1,c:]=c3[j,:]
...
... plots.append(matrix_plot(M, axes=False))
sage: a.show()
Exploramos ahora las reglas con tres colores (negro: 0, gris: 1 y blanco: 2) que tienen la siguiente propiedad adicional:
el estado de la celda depende slo de la suma de los colores de las celdas vecinas.
sage: %cython
sage: from numpy import zeros
sage: def cellular2(rule, int N):
... Returns a matrix showing the evolution of the totalistic 3-color automaton
...
... rule: determines how a cells value is updated, depending on its neighbors
... N: number of iterations
...
... cdef int j,k,numi
... M=zeros( (N,2*N+1), dtype=int)
... M[0,N]=1
...
... for j in range(1,N):
... for k in range(N-j,N+j+1):
... #color contiene la suma de los tonos de las celdas vecinas
... color = M[j-1,k-1] + M[j-1,k] + M[j-1,k+1]
... M[j,k]=rule[ color ]
... return M
genindex
search
A degree, 90
add_edge, 130 del, 8
add_vertex, 130 delete_edge, 130
ajuste de un modelo, 227 delete_vertex, 130
all, 33 derivative, 159
ambient_vector_space, 90 dict, 45
any, 33 digits, 77
argumentos de una funcin, 22 digraphs, 123
arrow, 181 distribucin binomial, 213
assume, 152 distribucin de bernouilli, 213
assumptions, 152 distribucin de Poisson, 216
distribucin geomtrica, 216
B distribucin normal, 219
basis, 92 docstring, 24
basis_matrix, 92
binomial negativa, 139 E
booleano, 9 echelon_form, 93
break, 22 eigenspaces, 97
eigenvalues, 97
C elif, 18
cadena de caracteres, 12 else, 18
cardinality, 110 estado fundamental en malabares, 249
CartesianProduct, 112 excepcin, 26
CDF, 89 Exception, 26
colector de basura, 8 expresin simblica, 9, 145
coma flotante, 9 extraccin aleatoria de una distribucin, 216, 221
Combinations, 111
CompleteGraph, 123 F
complex_roots, 85 factor, 78
contour_plot, 174 fast_float, 155
coordinates, 92 find_fit, 228
coste amortizado, 55 find_maximum_on_interval, 166
cputime, 51 find_minimum_on_interval, 166
cuadro de cdigo, 5 find_root, 149, 166
cuadro de texto, 5 float, 11
fmin, 188
D for, 19
dblquad, 189 forget, 152
299
Laboratorio de Matemticas, Release 2010/2011
300 ndice
Laboratorio de Matemticas, Release 2010/2011
return, 22 Z
roots, 85 zip, 46
S
scipy.integrate, 189
scipy.optimize, 188
semilla de nmeros aleatorios, 141
servidor web, 5
Set, 110
set, 42
set_random_seed, 141
solve, 149
SR, 89
srange, 14
subcadena, 12
Subsets, 110
subspace, 90
subspace_with_basis, 92
subtract_from_both_sides, 148
sum, 33
suma de series (sum), 158
T
Tabla de verdad, 21
taylor, 180
time, 51
timeit, 51
tipado dinmico, 9
tipo de dato, 9
tupla, 13
V
var, 145
variable dependiente, 227
variable independiente, 227
variable simblica, 145
variables, 7
vector, 89
VectorSpace, 89
W
walltime, 51
WeightedIntegerVectors, 112
while, 20
X
xgcd, 80
xrange, 34
xsrange, 34
Y
yield, 36
ndice 301