En esta asignatura usaremos el programa Sage para resolver distintos problemas de matemáticas con el ordenador. El programa es libre, lo que nos permite copiarlo, modificarlo y redistribuirlo libremente. Sage consta de un buen número de librerías para ejecutar cálculos matemáticos y para generar gráficas. Para llamar a estas librerías se usa el lenguaje de programación python, para el que existen un buen número de recursos didácticos disponibles.
Python es un lenguaje de propósito general de muy alto nivel, que permite representar conceptos abstractos de forma natural y, en general, hacer más con menos código. Buena parte de las librerías 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 cálculos que le pide el cliente, y le devuelve los resultados. En esta asignatura sólo usaremos el interfaz web.
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 edición, aparecen botones y desplegables para cambiar algunos aspectos del estilo del texto. Lo más importante para mantener un orden en la hoja de trabajo es el primer desplegable, que permite elegir si se trata de un párrafo, un encabezado.
Los cuadros de código son rectangulares y su borde cambia de color al seleccionarlos. Dentro de los cuadros de código podemos escribir instrucciones que serán ejecutadas al pulsar el botón evaluate o teclear mayúsculas+Enter.
Puedes crear un nuevo cuadro de código pulsando sobre la línea azul que aparece al poner el cursor sobre un cuadro de código existente, o pulsando control+Enter dentro de un cuadro de código. Puedes crear un nuevo bloque de texto pulsando sobre la misma línea azul, pero manteniendo pulsada la tecla de mayúsculas.
sage: print 'Hola, Mundo'
Hola, Mundo
Si ponemos varias líneas de código, se ejecutan una tras otra de arriba a abajo. El intérprete 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 código.
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
Podemos usar los bloques de comandos como una simple calculadora, escribiendo operaciones elementales y observando el resultado debajo del cuadro. Podemos introducir números con decimales usando el punto decimal. Los paréntesis marcan qué comando se ejecuta antes, naturalmente.
sage: (1*2)+(3*4)+5
19
sage: 1*(2+3)*(4+5)
45
sage: #Un numero racional
sage: 1/2
1/2
sage: #Un número de coma flotante
sage: 1+1.0
2.00000000000000
En ausencia de paréntesis, Sage decide qué operaciones se ejecutan antes y cuáles después usando unas reglas de precedencia de operadores bastante estándar. En la siguiente tabla de operadores, cada uno se ejecutará antes que los que están por encima.
Los operadores con la misma precedencia se agrupan de izquierda a derecha, excepto la exponenciación, que agrupa de derecha a izquierda.
No necesitas conocer todos estos operadores por ahora:
operador | Descripción |
or | O booleano |
and | Y booleano |
not x | NO booleano |
in , not in , is , is not , < , <= , > , >= , <> , != , == | comparaciones, comprobación de pertenencia y de tipo |
| | O bit a bit |
& | Y bit a bit |
+ , - | Suma y resta |
* , / , / / , % | multiplicación, división, el resto |
+ x , -x , ~ x | positivos, negativos, NO bit a bit |
^ | exponenciación |
x [indice] , x [indice: indice] , x (arguments. ..) , x.attribute | Índices, rebanadas, llamada a funciones, referencia a atributos |
( expresiones ...) , [expresiones ...] , {clave: dato ...} | tuplas, listas, diccionarios, evaluación de expresiones |
Ejercicio. Intenta predecir el resultado de ejecutar las instrucciones de debajo antes de pulsar el botón de evaluar.
sage: 2*3^1+1*3^2
sage: 2*3^((1+1)*3)^2
sage: True or True and False
sage: 1==2-1
sage: 2 < 3 and not 1 == 2
Además de las operaciones aritméticas, podemos usar las muchas funciones disponibles. La forma de usarlas es escribir el nombre de la función, seguido del argumento, o argumentos, entre paréntesis, 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
Para guardar un valor dentro de una variable, usamos la sintaxis:
variable = expresion
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 cálculos con su valor:
masa = 3
aceleracion = 10
fuerza = masa\*aceleracion
print fuerza
sage: numero = 1+2+3+4+5+6
sage: largo = 2.56*20
sage: angulo = pi/3
sage: print numero
sage: print largo, angulo
sage: print largo * sin(angulo)
21
51.2000000000000 1/3*pi
25.6000000000000*sqrt(3)
Nota: no confundas la asignación ( = ) con el operador de comparación de igualdad ( == ).
sage: velocidad = 3
sage: velocidad == 3
True
Nota : los nombres de las variables deben empezar por una letra o un guión bajo (_), pueden contener números, y son distintos si se usan mayúsculas o minúsculas. 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
sage: del numero
sage: print 3*numero
Traceback (most recent call last):
...
NameError: name 'numero' is not defined
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 válidas, y no se corrompen por el uso de del . Por tanto, esta instrucción 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 expresión. Más adelante en el curso guardaremos en las variables matrices, gráficas e incluso objetos abstractos como espacios vectoriales. Por ahora hemos usado los siguientes tipos de datos:
- Booleanos: sólo toman el valor True o False .
- Enteros: cualquier número entero, positivo o negativo, de longitud arbitraria. Ej.: 1, 10, -30
- Racionales Ej.: 1/2, -3/4
- Números de coma flotante: un número con unos cuantos dígitos decimales y un exponente, que representa un número real de forma aproximada. Ej.: 1.25, -1.5e6
- Expresiones simbólicas: expresiones matemáticas que representan números reales de forma exacta. Ej.: pi/4 (), (1+sqrt(2))/2 ()
Las variables pueden almacenar referencias a datos de cualquier tipo: python usa tipado dinámico . 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: print es_menor
True
sage: factorial(1000)
402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
sage: numero = factorial(1000)
sage: numero/factorial(1001)
1/1001
Al usar el ordenador para hacer matemáticas es importante saber si los datos del ordenador representan los objetos matemáticos de forma exacta.
Es imposible almacenar en un ordenador con una cantidad finita de memoria todas las cifras decimales del número pi . Una alternativa es almacenar sólo unas cuantas cifras, y cometer por tanto un pequeño error. Para la mayoría de las aplicaciones es más que suficiente usar 10 o 20 cifras decimales significativas . Con esto queremos decir que al escribir el número en notación exponencial, descartamos todas las cifras a partir de la número 10 o 20. Por ejemplo, el número con diez dígitos significativos:
y la constante de Planck con seis dígitos significativos:
Al hacer operaciones con números que contienen errores, los errores se suman y multiplican, y pueden acabar estropeando un cálculo. Otra alternativa es usar una variable simbólica, y usar las reglas aritméticas sin hacer cálculos con decimales, exactamente igual que os contaban en el instituto:
Al estar orientado preferentemente al público matemático, Sage prefiere usar expresiones simbólicas exactas antes que aproximaciones numéricas. Como vimos antes, para obtener una representación decimal de una expresión simbólica, podemos usar el comando n() (n de numérico).
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 métodos: funciones que se aplican sólo a datos de este tipo y que se llaman escribiendo primero la variable que contiene el dato, después un punto (.), y después el método:
variable.metodo()
Por ejemplo, podemos calcular la factorización de un número entero, pero no de un número real, o podríamos intentar simplificar una expresión simbólica, pero no podemos simplificar un número 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
sage: print b.factor()
Traceback (most recent call last):
...
AttributeError: 'sage.rings.real_mpfr.RealLiteral' object has no attribute 'factor'
sage: print a.full_simplify()
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'full_simplify'
El tabulador permite conocer la lista completa de métodos aplicables a un dato:
- Escribe el nombre de una variable seguido de un punto, y pulsa [Tabulador] para ver todos los métodos específicos al tipo de datos de la variable.
- Escribe el nombre de una variable seguido de un punto y algunos caracteres, y pulsa [Tabulador] para ver sólo los métodos que comienzan con esos caracteres.
sage: a.
sage: b.
Aunque Sage utiliza el lenguaje python, los tipos numéricos en Sage no corresponden exactamente a los tipos numéricos de python, ya que los números en Sage tienen más funcionalidad. Por ejemplo, los enteros de Sage permiten calcular su lista de divisores, su expresión binaria, etcétera. Por defecto, los tipos numéricos son enteros, racionales y números 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: print a.divisors(), a.digits(base = 2)
[1, 2, 3, 4, 6, 12] [0, 0, 1, 1]
sage: print b.divisors()
Traceback (most recent call last):
...
AttributeError: 'int' object has no attribute 'divisors'
sage: c = 1.2 #Real de SAGE (de precision limitada)
sage: c = RealNumber(1.2) #Otra forma equivalente de definir un real de SAGE
sage: d = float(1.2) #Real de 64 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
Las cadenas de caracteres son secuencias de caracteres (letras, números, puntuación...) que se manipulan de forma conjunta y se pueden almacenar en una variable. Para introducir cadenas de caracteres en el código, separamos el texto entre comillas simples (‘cadena’), comillas dobles (“cadena”), o triples comillas simples para cadenas más largas (‘’‘cadena larga’‘’).
sage: print 'Hola'
Hola
Se pueden usar algunas operaciones con cadenas de caracteres:
- La suma (+) de cadenas devuelve una cadena resultado de poner la primera cadena después de la segunda
- El producto (*) de una cadena por un número natural repite la cadena tantas veces como indica el número.
sage: print 'Hola' + 'tu'
sage: print 'Hola'*2
Holatu
HolaHola
sage: cadena1 = 'Hola'
sage: cadena2 = ' a todas' #presta atencion al espacio en blanco al principio
sage: cadena3 = cadena1 + cadena2
sage: cadena4 = cadena1 + '.'*3 + cadena2
sage: print cadena1
sage: print cadena2
sage: print cadena3
sage: print cadena4
Hola
a todas
Hola a todas
Hola... a todas
Podemos obtener la longitud de una cadena con el comando len .
sage: print len(cadena1), len(cadena2), len(cadena3), len(cadena4)
4 8 12 15
Para acceder al carácter que ocupa la posición j-ésima en una cadena de caracteres, usamos la notación
cadena[j]
¡Ojo! Se empieza a contar desde el número 0, el índice del primer carácter. El índice del último carácter es L-1, donde L es la longitud de la cadena.
También podemos acceder a una subcadena (o rango), desde el índice j (inclusive) hasta el k (exclusive), con la notación
cadena[j:k]
Por ejemplo:
cadena = 'Si miras al abismo...'
print cadena[0], cadena[1], cadena[2], cadena[3]
print cadena[3:8]
sage: cadena = 'Si miras al abismo...'
sage: print cadena[0], cadena[1], cadena[2], cadena[3]
sage: print cadena[3:10]
S i m
miras a
Las tuplas contienen unos cuantos elementos no necesariamente del mismo tipo. Basta con poner las variables entre paréntesis separadas por comas para formar una tupla.
tupla = (elemento1, elemento2)
Una vez creada, podemos acceder a sus elementos usando corchetes, igual que hacíamos 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 * actúan sobre tuplas de la misma forma que sobre cadenas de caracteres.
sage: (1,2,'a') + (3,4)
(1, 2, 'a', 3, 4)
sage: (1, 'a')*3
(1, 'a', 1, 'a', 1, 'a')
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]
o también podemos desempaquetar la tupla con una sóla instrucción:
a,b,c = frutas
lo que, al igual que el código 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 más, 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
sage: a,b = frutas
Traceback (most recent call last):
...
ValueError: too many values to unpack
sage: a,b,c,d = frutas
Traceback (most recent call last):
...
ValueError: need more than 3 values to unpack
Las tuplas son immutables , es decir, que no se puede quitar ni añadir elementos a una tupla una vez ha sido creada, ni siquiera sustituir un elemento por otro.
Las listas se usan de modo similar a las tuplas, pero se pueden quitar y añadir elementos en cualquier momento. Decimos que son contenedores dinámicos de datos .
La sintaxis para crear listas es igual a la de las tuplas, pero usando corchetes en vez de paréntesis. Una vez creadas, podemos acceder a sus elementos usando los corchetes, pero además 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']
También podemos eliminar elementos de la lista con el comando del y añadir elementos al final de la lista con el comando append .
sage: print lista_frutas[1]
fresa
sage: lista_frutas = ['pera','manzana','naranja']
sage: print lista_frutas
sage: lista_frutas.append('fresa')
sage: print lista_frutas
sage: del lista_frutas[1]
sage: print lista_frutas
['pera', 'manzana', 'naranja']
['pera', 'manzana', 'naranja', 'fresa']
['pera', 'naranja', 'fresa']
sage: print lista_frutas
sage: lista_frutas.insert(2,'otra fruta')
sage: print lista_frutas
['pera', 'naranja', 'fresa']
['pera', 'naranja', 'otra fruta', 'fresa']
El comando srange permite crear listas de números de SAGE (en python se usa la función range , que devuelve enteros de python):
- srange(j,k,d) : devuelve los números entre j (inclusive) y k (exclusive), pero contando de d en d elementos. A pesar de que el uso más extendido es con números enteros, los números 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 vacía.
- 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.5000000000000, 19.0000000000000]
En cualquiera de las estructuras anteriores, podemos comprobar si un elemento está en la lista usando el operador in , que devuelve True o False según el elemento pertenezca o no al contenedor de datos.
sage: 10 in [1,2,3,4]
False
sage: 'n' in 'En un lugar de la Mancha'
True
sage: 133 in srange(0,1000,7)
True
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 función str , el resultado no es el que deseamos normalmente:
sage: str(lista_0)
"['a', 'b', 'c']"
sage: str(tupla_0)
'(1, 2, 3)'
La función str intenta dar una representación textual lo más fidedigna posible del objeto que pasamos como argumento.
La situación siguiente es más usual: tenemos una lista de cadenas de caracteres, y queremos unir esas cadenas, opcionalmente usando otra cadena como separador. Para ello usamos el método 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. También podemos escribir texto que contenga los valores de estas variables usando el operador % y los códigos de formato. Para usarlo, escribimos una cadena que contiene códigos de formato seguida del operador % y a continuación la variable que queremos sustituir, o una tupla de variables si hay más de una.
lados = 8
print 'El número de lados es %d'%lados
area = 17.5
print 'El área es %f'%area
print 'El área del polígono de %d lados es %f'%(lados, area)
nombre = 'Juan'
print '%s vino ayer a cenar'%nombre
Los códigos más usuales son:
- %d : número entero
- %f : número de coma flotante, con decimales
- %.3f : número de coma flotante, con 3 decimales exactamente
- %s : cadena de caracteres (o cualquier dato que no sea entero ni de coma flotante)
sage: lados = 8
sage: print 'El número de lados es %d'%lados
sage: area = 17.5
sage: print 'El área es %.3f'%area
sage: print 'El área del polígono de %d lados es %f'%(lados, area)
sage: nombre = 'Juan'
sage: print '%s vino ayer a cenar'%nombre
El número de lados es 8
El área es 17.500
El área del polígono de 8 lados es 17.500000
Juan vino ayer a cenar