Está en la página 1de 61

Notas de Octave

Dra. Adriana Lara

2 de abril de 2019
Índice general

1. Introducción 2
1.1. ¿Qué es Octave? . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2. Lo que debes aprender de Octave para este curso es: . . . . . . . 2
1.3. Acerca de estas notas: . . . . . . . . . . . . . . . . . . . . . . . . 2

2. Usando Octave 4
2.1. Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2. Acceso a elementos . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3. Matrices especiales . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4. Funciones predefinidas para trabajar con matrices y vectores . . 14

3. Archivos Script 23
3.1. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.2. Guardar y cargar datos . . . . . . . . . . . . . . . . . . . . . . . 25

4. Programación en Octave 28
4.1. Operadores relacionales y lógicos . . . . . . . . . . . . . . . . . . 28
4.2. Operadores Lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.3. Sentencias condicionales . . . . . . . . . . . . . . . . . . . . . . . 32
4.4. Bucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

5. Funciones 39
5.1. Cómo definir una función . . . . . . . . . . . . . . . . . . . . . . 39
5.2. Subfunciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.3. Manejadores de Funciones. . . . . . . . . . . . . . . . . . . . . . . 43
5.4. Comando feval. . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.5. Formas de llamar a una función. . . . . . . . . . . . . . . . . . . 44
5.6. Directorios. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

6. Gráficas 46
6.1. El comando plot . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.2. El comando contour. . . . . . . . . . . . . . . . . . . . . . . . . 55

7. Temas Avanzados 57
7.1. Aumentar la calidad del código escrito en Octave . . . . . . . . . 57
7.2. Vectorizar, la clave para aumentar la velocidad . . . . . . . . . . 57
7.3. El truco más importante de la programación en Octave . . . . . 58

1
Capı́tulo 1

Introducción

1.1. ¿Qué es Octave?


Octave es un software para cálculo numérico, que permite ejecutar algo-
ritmos de manera muy práctica sin necesidad de declarar variables, manejar
estructuras de datos complejas, apuntadores, etc.
Permite manipular datos y graficarlos. Octave es un software, en cierto senti-
do, compatible con MatlabTM en cuanto a su naturaleza y sintaxis. La principal
diferencia radica en la licencia de uso puesto que MatlabTM es un software co-
mercial con desarrolladores de la compañia y actualizaciones regulares; mientras
que Octave es desarrollado principalmente para el uso académico y mantenido
por profesores y desarrolladores entusiastas bajo un esquema de licencia libre
(GLP).
En este curso usaremos Octave pues es muy sencillo de aprender y ejecutar,
es de naturaleza numérica, nos permite trabajar con matrices de manera nativa
y tiene licencia de uso libre.
Octave contiene muchas de las caracterı́sticas de Matlab y para los propósitos
de nuestro curso son suficientes.

1.2. Lo que debes aprender de Octave para este


curso es:
1. Hacer operaciones con vectores y matrices
2. Procesar datos en un archivo de texto (lectura/escritura)
3. Graficación de datos y creación de figuras
4. Creación y uso de scripts y funciones
5. Escritura de programas sencillos

1.3. Acerca de estas notas:


Estas notas cubren aspectos básicos para empezar en Octave. Es necesario
que complementes la información con recursos en lı́nea (e.g., foros, páginas de

2
ayuda , manuales) para lograr dominar el lenguaje y optimizar tu código poco a
poco. Octave cuenta con recursos de programación sofisticados que vale la pena
aprender.

3
Capı́tulo 2

Usando Octave

Octave se encuentra disponible para sistemas UNIX (Linux) y MS Windows.


También se conoce como Octave al lenguaje de alto nivel en el que se codifica.
Se trata de un lenguaje interpretado que posee muchas similitudes con Matlab,
con el fin de hacerlo compatible (*).
Cuando se inicia Octave, se tiene una leyenda descriptiva seguida de la lı́nea
de comandos (prompt).

$ octave
GNU Octave, version 3.2.4
Copyright (C) 2009 John W. Eaton and others.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. For details, type ‘warranty’.
Octave was configured for "x86_64-pc-linux-gnu".
Additional information about Octave is available at
http://www.octave.org.
For more information, visit http://www.octave.org/help-wanted. html
Report bugs to <bug@octave.org> (but first, please read...
For information about changes from previous versions, type ‘news’.

octave:1>

2.1. Matrices
La estructura de datos básica en Octave es la matriz, de esta forma, los
escalares son vistos como matrices de tamaño 1x1.

octave:1> 2

ans = 2

octave:2> 235

ans = 235

4
Los vectores son matrices de tamaño nx1 o 1xn y se introducen de la siguiente
forma:

octave:3> a=[1 2 3 4 5 6 7 8 9]
a =
1 2 3 4 5 6 7 8 9
octave:4> b=[1;2;3;4;5;6;7;8;9]
b =
1
2
3
4
5
6
7
8
9
octave:5> d=[1 2 3;4 5 6]
d =
1 2 3
4 5 6
octave:38> a=[1 2 3]
a =
1 2 3

Se pueden efectuar las operaciones tı́picas sobre matrices, por ejemplo:

Si se quiere obtener la transpuesta de una matriz:

octave:6> d’
ans =

1 4
2 5
3 6

Asignar el contenido de una matriz a otra:

octave:7> c=d’
c =

1 4
2 5
3 6

Concatenación de matrices

octave:5> d=[1 2 3;4 5 6]


d =

5
1 2 3
4 5 6

octave:38> a=[1 2 3]
a =
1 2 3

octave:39> s=[d;a]
s =
1 2 3
4 5 6
1 2 3
Suma de matrices y multiplicación por un escalar.
octave:13> c=a+b’
c =

2 4 6 8 10 12 14 16 18

octave:14> c=2*c
c =

4 8 12 16 20 24 28 32 36
Existen casos en el que, sin embargo, las operaciones no son las usuales
entre matrices y es cuando una de las matrices es de orden 1x1, es decir, un
escalar. En el caso de la suma, cuando se suma una matriz a un escalar, a
cada elemento de la matriz se le suma dicho escalar, en la suma normal, las
dimensiones de la matriz deben de coincidir para poder realizar esta suma. En
el caso de multiplicación, cuando una variable es un escalar, entonces se realiza
la multiplicación usual entre un escalar y una matriz, es decir, el escalar por cada
entrada, ignorándose la convención de que el número de columnas de la primer
matriz debe de coincidir con el número de renglones de la segunda matriz. Es
posible hacer otras operaciones útiles como multiplicación entrada por entrada,
asegurándose desde luego que las matrices a operar sean de la misma dimensión
agregando ’.’ antes del operador de multiplicación ’*’.
octave:1> a = [1 2 3]
a =

1 2 3

octave:2> b = [4 5 6]
b =

4 5 6

octave:3> a.*b
ans =

4 10 18

6
El operador . es también compatible con los operadores de división / y ex-
ponenciación ^, la sintaxis es igual que en el caso de la multiplicación. En el
caso de la exponenciación el exponente es un escalar y cada entrada de la matriz
es elevada a dicho exponente.

Para desplegar el valor de una variable, simplemente se teclea el nombre de


la variable, por ejemplo:

octave:15> c
c =

4 8 12 16 20 24 28 32 36

Si no se desea desplegar el valor de las expresiones que se teclean, se pone


un punto y coma al final:

octave:16> a=[1 2 3; 1 2 3; 1 2 3];


octave:17>

Existen operaciones especiales que nos permiten resolver sistemas de ecua-


ciones de forma eficiente. Para resolver sistemas de ecuaciones de la forma

Tx = a

se utiliza el operador \ haciendo equivalente escribir

T \ x

con T −1 a. (T −1 indica la inversa de la matriz T )

octave:7> T = [4 5 6;1 8 9; 3 5 -1]


T =

4 5 6
1 8 9
3 5 -1
octave:9> a = [1;2;3]
a =

1
2
3
octave:10> T\a
ans =

-0.043011
0.569892
-0.279570
octave:11> inv(T)*a
ans =

7
-0.043011
0.569892
-0.279570

Sin embargo, usar el operador diagonal es computacionalmente más eficiente


que calcular la inversa de T y multiplicar con a debido a que al usar el operador
diagonal se realiza eliminación gaussiana para resolver el sistema de ecuaciones,
con la consecuente mejora numérica, mientras que en la otra forma se calcula la
inversa y luego se realiza un producto de matrices.

Si el sistema lo escribimos en la forma

xt T = at

se puede usar el operador / para realizar la eliminación gaussiana.

octave:12> T = T’
T =

4 1 3
5 8 5
6 9 -1

octave:13> a=a’
a =

1 2 3

octave:14> a/T
ans =

-0.043011 0.569892 -0.279570

Obteniéndose el mismo resultado que en el caso anterior, salvo que ahora las
soluciones están en un vector renglón.

2.2. Acceso a elementos


NOTA: Los ı́ndices de vectores y matrices comienzan en 1, no en cero como
en otros lenguajes. La última posición de un arreglo utiliza el sı́mbolo especial
end.

Para tener acceso a una o varias de las entradas de una matriz, se utilizan
ı́ndices y paréntesis. por ejemplo

Para acceder a un elemento de la matriz.

8
octave:20> a
a =

5 8 6
1 7 3
8 2 4
octave:21> a(3,2)
ans = 2
octave:22> a(3,end)
ans = 4

Acceder a varios elementos, en este caso se obtiene una submatriz de la


matriz original, en el ejemplo se desea acceder a los elementos en los renglones
1 y 2 que están en la columna 3 en la matriz a.

octave:23> a([1,2],3)
ans =

6
3

En el siguiente ejemplo deseamos ver los elementos de los renglones 1 y 2


que están en las columnas 2 y 3 de la matriz a.

octave:24> a([1,2],[2,3])
ans =

8 6
7 3

En un vector (matriz de 1xn o nx1) basta con indicar un solo ı́ndice, aunque
tampoco es incorrecto escribir ambos ı́ndices.

octave:1> v = [1 2 3]
v =

1 2 3

octave:2> v(1) = 5
v =

5 2 3

octave:3> v(1,end) = 200


v =

5 2 200

La expresión x1 : x2 devuelve un vector renglón (fila) de enteros entre x1 y


x2 .

9
octave:1> v = 3:6
v =

3 4 5 6

Esta forma es particularmente útil cuando se desea acceder a varios elemen-


tos consecutivos de una matriz o vector.

octave:6> v1 = [1 2 3 4 5 6]
v1 =

1 2 3 4 5 6

octave:7> v1(2:5) = 0.5


v1 =

1.00000 0.50000 0.50000 0.50000 0.50000 6.00000

Usando una sintaxis similar podemos crear vectores donde cada elemento
corresponde a una sucesión aritmética, por ejemplo:

octave:11> x = 0:0.2:1
x =

0.00000 0.20000 0.40000 0.60000 0.80000 1.00000

En el caso anterior se comienza con 0, y el siguiente elemento del vector es el


elemento anterior más 0.2, la sucesión termina cuando se ha rebasado el número
1. En este caso el lḿite superior fue alcanzado pero podrı́a no darse el caso, por
ejemplo:

octave:12> x = 1:0.3:2
x =

1.0000 1.3000 1.6000 1.9000

También se pueden dar incrementos negativos.

octave:13> 0:-0.2:-1
ans =

0.00000 -0.20000 -0.40000 -0.60000 -0.80000 -1.00000

Sin embargo, si deseamos dividir el intervalo en un número conocido de


puntos igualmente espaciados usamos la función linspace(·, ·, ·) que toma 3
argumentos, el primero y segundo correspondientes al primer y último elemento
del vector, el último parámetro corresponde al número de elementos deseados
en el vector, por ejemplo:

octave:14> linspace(1,2,5)
ans =

1.0000 1.2500 1.5000 1.7500 2.0000

10
Ejercicio 2.1 Hacer que se muestren los elementos de la matriz x en orden
inverso por renglón y por columna, es decir, en una sola orden queremos que
tome la matriz:

octave:32> x = [1 2 3;4 5 6;7 8 9]


x =

1 2 3
4 5 6
7 8 9

y nos devuelva como respuesta:

ans =

9 8 7
6 5 4
3 2 1

Otro operador útil es el operador : este operador es usado para indicar que
se desea acceder a todos los elementos de un renglón o columna de una matriz.

octave:17> a = [1 2 3;4 5 6;7 8 9]


a =

1 2 3
4 5 6
7 8 9

octave:18> a(1,:)
ans =

1 2 3

octave:19> a(:,2)
ans =

2
5
8

Algunas veces es deseable eliminar por alguna razón el contenido de un


renglón o columna de una matriz, o simplemente un elemento de un vector, esto
se puede hacer usando la siguiente sintaxis:

octave:20> a = [1 2 3;4 5 6;7 8 9]


a =

11
1 2 3
4 5 6
7 8 9

octave:21> a(2,:) = []
a =

1 2 3
7 8 9

octave:22> a(:,1) = []
a =

2 3
8 9
Cabe notar que a diferencia de los lenguajes de bajo nivel como C, en Octave
no es necesario reservar memoria declarando las variables o su tipo, Octave reali-
za todo el proceso de asignación y liberación de memoria, como en cada ejemplo
donde hemos creado o redefinido los tamaños de las matrices.

Hasta ahora se han creado variables de forma intuitiva, las reglas para nom-
brar las variables son como en casi cualquier lenguaje de programación:

1. Deben de comenzar con una letra.


2. Pueden contener números.
3. Hay sensibilidad a letras mayúsculas y minúsculas.
4. No es posible asignarle como nombre de variable una palabra reservada
por el sistema, por ejemplo Sin, Cos, sqrt, entre otras.
Existen algunas variables predefinidas por el sistema, por ejemplo:

ans Esta variable contiene el resultado de la última sentencia que no ha sido


asignada a una variable.
pi Es el valor del número π, no es lo mismo que teclear PI.
eps Representa la diferencia más pequeña entre dos números. Es igual a 2−52 ,
que es aproximadamente 2,2204x10−16 .
i Se define como la raı́z cuadrada de −1.
j Es equivalente a i.
NaN Es la abreviatura de Not a Number. Se utiliza cuando Octave no puede
encontrar un valor numérico de la expresión calculada, por ejemplo en la
operación 0/0.
inf Representa el infinito.

12
2.3. Matrices especiales
Algunas matrices son muy frecuentemente usadas, las más usadas son las
siguientes 3: zeros(m,n) que nos crea una matriz de m renglones y n columnas
con elementos iguales a cero, ones(m,n) que nos crea una matriz de m renglones
y n columnas con elementos iguales a uno, y la función eye(n) que crea una
matriz identidad de nxn.

octave:135> A = zeros(3,2)
A =

0 0
0 0
0 0

octave:136> B = ones(4,5)
B =

1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

octave:137> C = eye(3)
C =

Diagonal Matrix

1 0 0
0 1 0
0 0 1

Aunque existen otras matrices especiales, por ejemplo magic(n) que genera
una matriz cuyos elementos forman un cubo mágico de tamaño nxn, pascal(n)
que forma una matriz que vista desde la esquina superior izquierda y en direc-
ción a la esquina inferior derecha contiene los elementos del triángulo de Pascal,
entre otras. Aunque pareciera que son en cierta forma funciones inútiles, y lo
cierto es que rara vez se van a llegar a usar, un uso por ejemplo de la matriz de
pascal es en la generación de números aleatorios de Faure.

Si v es un vector, el comando diag(v) devuelve una matriz con los elementos


de v en la diagonal. Mientras que diag(M) devuelve un vector con los elementos
de la diagonal de M.

octave:35> v = [5 30 20]
v =

13
5 30 20

octave:41> M = diag(v)
M =

Diagonal Matrix

5 0 0
0 30 0
0 0 20

octave:42> M(3,1) = 40
M =

5 0 0
0 30 0
40 0 20

octave:43> M(1:2,1:2) = [1 2;3 4]


M =

1 2 0
3 4 0
40 0 20

octave:44> v = diag(M)
v =

1
4
20

2.4. Funciones predefinidas para trabajar con ma-


trices y vectores
Algunas veces no se tiene control sobre las caracterı́sticas de las variables
usadas, por ejemplo si estas fueron extraı́das de un archivo de texto plano (.txt
por ejemplo), hoja de cálculo, etc. Y sin embargo es necesario conocer algunos
de sus datos, por ejemplo, tipo de dato, número de elementos. Para esto utili-
zamos las funciones:

who Muestra una lista de todas las variables almacenadas en memoria.

whos Muestra una lista de todas las variables en memoria y su tamaño junto
con su clase y longitud.

octave:114> x = [1 2 3 4];
octave:115> y=[1:5];

14
octave:116> z = [eye(3) ones(3,1) zeros(3,2)];
octave:117> whos
Variables in the current scope:

Attr Name Size Bytes Class

==== ==== ==== ===== =====

x 1x4 32 double

y 1x5 40 double

z 3x6 144 double

Total is 27 elements using 216 bytes

octave:118> who
Variables in the current scope:

x y z

Sin embargo, si sólo se desea conocer información de una variable en especı́fico


se pueden utilizar los comandos:

1. length(A): Devuelve el número de elementos del vector A, en el caso de


una matriz, devuelve el máximo entre el número de renglones y el número
de columnas.
2. size(A): Devuelve un vector fila [m, n] que representa el número de ren-
glones y número de columnas de la matriz A respectivamente.

octave:127> x = [1 2 3 4];
octave:128> z = [eye(3) ones(3,1) zeros(3,2)];
octave:129> length(x)
ans = 4
octave:130> size(z)
ans =

3 6

Es importante tener en cuenta las dimensiones de las matrices en todo mo-


mento, ası́ como saber que se trata de una matriz o un vector. Para ello con-
sidérese el siguiente ejemplo:

octave:131> x = magic(4)
x =

15
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1

octave:132> x(10) = 100


x =

16 2 3 13
5 11 100 8
9 7 6 12
4 14 15 1

Aunque se trata de una matriz y no se ha especificado apropiadamente el


elemento con el cual se trabajará, Octave no ha enviado un mensaje de error.
Cuando solo se envı́a un valor como entrada para localizar un elemento en una
matriz se hace una correspondencia entre el par ordenado que determina ca-
da elemento en la matriz con una enumeración de los elementos de la misma,
dicha enumeración comienza en el elemento x(1, 1) y se va moviendo primero
en vertical y luego en horizontal. De esta forma el elemento correspondiente al
x(2) es x(2, 1), y en particular el elemento x(10) para el ejemplo corresponde
con el elemento x(2, 3). Usando esta correspondencia es que trabaja la siguiente
función:

reshape(A,m,n): Convierte una matriz A de dimensiones rxs en una matriz


de dimensiones mxn, se debe de cumplir que el producto de r, s sea igual al
producto de m, n. Es decir, redimensiona la matriz por otra que contiene el
mismo número de elementos pero diferentes dimensiones.

octave:133> A = [5 1 6;8 0 2]
A =

5 1 6
8 0 2

octave:134> reshape(A,3,2)
ans =

5 0
8 6
1 2

En general Octave posee un gran número de funciones que trabajan con


arreglos y matrices y, de momento, solo se mostrarán algunas de ellas.

sum(A)

16
Si A es un vector, calcula la suma de todos sus elementos.

octave:2> A = 1:6
A =

1 2 3 4 5 6

octave:3> sum(A)
ans = 21

Si A es una matriz entonces devuelve la suma de los elementos por columna.

octave:4> A = magic(5)
A =

17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9

octave:5> sum(A)
ans =

65 65 65 65 65

Si se necesita la suma de los elementos por renglón se puede usar la trans-


puesta de la matriz y aplicar la función sum(·), pero la función sum(·) también
puede trabajar con un segundo parámetro para lograr el mismo resultado. La
sintaxis es la siguiente:

octave:6> sum(A,2)
ans =

65
65
65
65
65

sort(A)

La función sort(A) organiza los elementos de un vector en orden creciente


como opción por default.

octave:10> x = randperm(8)
x =

17
4 3 6 5 7 2 1 8

octave:11> sort(x)
ans =

1 2 3 4 5 6 7 8

Pero si lo que se desea es obtener los elementos ordenados en forma decre-


ciente se puede usar la palabra ’descend’ como segundo argumento.

octave:12> sort(x,’descend’)
ans =

8 7 6 5 4 3 2 1

La función sort(A) puede devolver dos valores de salida, cuando se hace una
asignación a una variable(o a ninguna como en el caso de los ejemplos) sólo se
devuelve un valor que es el vector ordenado, el segundo valor de salida de esta
función es una lista del orden en que se encontraba cada entrada en el vector
original. La sintaxis es:

octave:13> [y,indices] = sort(x)


y =
1 2 3 4 5 6 7 8
indices =
7 6 2 1 4 3 5 8

Que indicarı́a que el 1 estaba en la posición 7 en el vector x, el 2 en la


posición 6 y ası́ sucesivamente. Si la función sort(A) es aplicada a una matriz
sucede un comportamiento similar a la función sum(A) y realiza el ordenado por
columna o por renglón agregando 2 como segundo parámetro.

octave:14> A = magic(5)
A =

17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9

octave:15> sort(A)
ans =

4 5 1 2 3
10 6 7 8 9
11 12 13 14 15

18
17 18 19 20 16
23 24 25 21 22

octave:16> sort(A,2)
ans =

1 8 15 17 24
5 7 14 16 23
4 6 13 20 22
3 10 12 19 21
2 9 11 18 25

octave:17> [y,indice] = sort(A)


y =

4 5 1 2 3
10 6 7 8 9
11 12 13 14 15
17 18 19 20 16
23 24 25 21 22
indice =

3 2 1 5 4
4 3 2 1 5
5 4 3 2 1
1 5 4 3 2
2 1 5 4 3

Ejercicio 2.2 La matriz X contiene por renglón vectores en R2 y el vector


Y las evaluaciones de esos vectores en cierta función F : R2 :→ R, deseamos
ordenar los renglones de X en orden decreciente con respecto a sus evaluaciones
en la función F . Es decir tomar:

X =

0.92398 0.90550
0.85098 0.44459
0.40337 0.51532
0.16965 0.63489
0.63003 0.63630
Y =

1.2199
1.5620
1.7899
1.7908
1.6123

Y devolver:

X2 =

19
0.16965 0.63489
0.40337 0.51532
0.63003 0.63630
0.85098 0.44459
0.92398 0.90550
Y2 =

1.7908
1.7899
1.6123
1.5620
1.2199

Usando la función sort(A) y sus parámetros de salida. La solución está


dada en dos renglones.

mean(A)
Calcula el promedio de los valores del vector A, si A es una matriz entonces
calculará el promedio por columna, obteniéndose un vector renglń con cada pro-
medio. Al igual que las funciones sort(A) acepta un 2 como segundo parámetro
con lo que calcula el promedio por renglón en vez de por columna.

octave:6> X = magic(5)
X =

17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9

octave:7> mean(X)
ans =

13 13 13 13 13

octave:8> mean(X,2)
ans =

13
13
13
13
13

octave:9> mean(X(1,:))
ans = 13

20
Ası́ como las anteriores existen muchas funciones, explicar cada una de ellas
serı́a algo tardado, Octave nos puede dar información detallada de las funciones
siempre que conozcamos el nombre de la misma, basta con teclear en la terminal
la palabra help seguida de un espacio y el nombre de la función de la cual
queremos información.

octave:2> help max


’max’ is a function from the file
/usr/lib/i386-linux-gnu/octave/
3.6.4/oct/i686-pc-linux-gnu/max.oct

-- Loadable Function: max (X)

-- Loadable Function: max (X, Y)

-- Loadable Function: max (X, [], DIM)

-- Loadable Function: max (X, Y, DIM)

-- Loadable Function: [W, IW] = max (X)

For a vector argument, return the maximum value. For a matrix


argument, return the maximum value from each column,

as a row vector, or over the dimension DIM if defined, in


which case Y should be set to the empty matrix (it’s ignored

otherwise). For two matrices (or a matrix and scalar), return


the pair-wise maximum. Thus,

max (max (X))

returns the
largest element of the matrix X, and

max (2:5, pi)


=> 3.1416 3.1416 4.0000 5.0000

compares each element of the range


’2:5’ with ’pi’, and returns
a row vector of the maximum values.

For complex arguments, the magnitude of the elements are used


for comparison.

If called with one input and two output


arguments, ’max’ also
returns the first index of the maximum value(s).

21
Thus,

[x, ix] = max ([1, 3, 5, 2, 5]) => x = 5


ix = 3

See also: min, cummax, cummin

Additional help for built-in functions and operators is


available in the online version of
the manual. Use the command
’doc <topic>’ to search the manual index.

Help and information about Octave is also available


on the WWW
at http://www.octave.org and via the help@octave.org
mailing list.

Ejercicio 2.3 Una variable X U (a, b) (Uniforme en el intervalo (a, b)) puede
ser generada usando la siguiente fórmula: X = (b − a) ∗ U + a, donde U es
una variable aleatoria uniforme en el intervalo (0, 1). Búsquese usando help
el funcionamiento de la función rand y genérese un vector con 100000 valores
de esta variable aleatoria, use la función hist para graficar un histograma y
comprobar que se sigue una distribución uniforme.

Otras funciones que son de utilidad en el área de optimización y en simulación


son: min, det, rand, randn.

22
Capı́tulo 3

Archivos Script

El uso de Octave es muy similar al de una calculadora en donde damos or-


den por orden lo que se debe realizar, esto puede ser muy tedioso si la cantidad
de instrucciones que hemos de ingresar es relativamente grande, además de que
si cometimos algún error al escribir alguna variable, entrada de matriz, orden,
etc. generalmente tendrı́amos que comenzar desde el principio, lo cual es poco
práctico.

Para evitar este problema se usan los Archivos script, que tratan de emular
lo que se hace en lenguajes de programación como C o Pascal en donde se
tiene que escribir el código, luego compilarlo y finalmente ejecutar el programa.
Algunas caracterı́sticas de los scripts son:

Un script es una lista de comandos de Octave, también denominada pro-


grama.
Cuando se ejecuta un archivo script Octave ejecuta los comandos en el
orden en que han sido escritos como si se teclearan directamente en la
terminal.
Cuando un comando tiene una orden que produce una salida (por ejemplo
una asignación u operación), y esta no está terminada en punto y coma, se
visualizará el resultado de la misma forma que si se tecleara en la terminal.
La utilización de Archivos es conveniente ya que pueden ser editados y
ejecutarse tantas veces como se desee.
Los scripts pueden ser creados usando cualquier editor de texto plano
(gedit, block de notas, Emacs, Kate, etc.). Asimismo el texto puede ser
copiado y pegado en la terminal para ser ejecutado.
Los Archivos script también se denominan Archivos M ya que la extensión
.m es la que se debe utilizar para ser reconocidos por Octave.

Como se puede ver, editar un script es muy sencillo, solo hace falta tener
un editor de texto y guardar el archivo con la extensión .m para ser ejecutado.
No se necesita de algún tipo de sintaxis adicional, en el script los comandos se
escriben tal y como se escrbirı́an en la terminal.

23
Para poder ejecutar un script se debe de asegurar primero que el script
tiene extensión .m y segundo que nos encontramos en la carpeta que contiene el
script. para movernos a la carpeta que contiene al script solo hace falta conocer
la direcciń de esta y movernos al directorio usando el comando cd.

octave:15> cd /home/sltkbcu/Documents
octave:16>

3.1. Variables
Las variables globales son variables que pueden ser reconocidas en cualquier
parte, incluidos los Archivos script. Su utilidad es apreciada principalmente en
el manejo de funciones (se hablará más adelante de ellas). Las variables globa-
les, a diferencia de las variables comunes que hemos manejado, deben de ser
declaradas en el ambiente en el que van a ser utilizadas, y se debe de asegurarse
que estas ya tienen un valor cuando son utilizadas, para declarar variables glo-
bales se escribe global seguido de espacio y el nombre de cada variable global
separada por espacio.

octave:18> global x y z w

En el caso de un script, si las variables globales son declaradas o no carece de


importancia ya que estas instrucciones son ejecutadas como si fueran ejecutados
los comandos directamente sobre la terminal, sin embargo, en el uso de funcio-
nes es necesario declararlas dentro y fuera de ella para poder ser manipuladas.

Cuando el script usará variables, se debe de asegurar que se les ha dado


un valor previamente y/o se les asignará un valor a medida que se ejecutan los
comandos, no es posible usarse una variable si esta no tiene asignado un valor.

Para ejecutarse un script solo hay que teclear su nombre en la lı́nea de


comandos, por ejemplo si el script tiene nombre ejemplo.m entonces el script
se llamará desde la lı́nea de comandos como:

octave:19> ejemplo
a =

1 2 3 4 5 6 7 8 9 10

Cuando se trabaja con un script grande o se resuelven varios problemas es


posible que se tenga guardada una cantidad relativamente grande de variables
en la memoria, es una práctica común en cualquier lenguaje de programación ir
desechando estas variables de tal forma que la memoria quede libre para poder
usarse en cualquier momento. No nos gustarı́a que una aplicación falle a medio
proceso por falta de memoria. Para realizar eso se usa el comando clear. Esto
borra todas las variables con las que se esté trabajando, si se desea eliminar

24
alguna variable en particular mientras que se sigue trabajando con el resto, se
usa el mismo comando, se deja un espacio y luego la/las variables a eliminar
seguidas de espacio.

octave:20> clear
octave:21> x = 1;
octave:22> y=4
y = 4
octave:23> z=[1,2,5]
z =

1 2 5
octave:24> clear x y
octave:25> x
error: ’x’ undefined near line 25 column 1
octave:25> z
z =

1 2 5
octave:26>

3.2. Guardar y cargar datos


Cuando se ejecuta un programa, se desea obtener uno o más resultados, serı́a
poco práctico escribir estos resultados a mano, en especial si se trata de una
matriz de 1000x1000 por dar un ejemplo. Octave permite guardar estos datos
en diversos formatos, ası́ como cargar desde un archivo de tal forma que no se
tengan que ingresar los datos manualmente.

Los comandos save y load permiten guardar y cargar datos en diversos


formatos.
save El comando save sirve para guardar los valores de las variables con las que
se esté trabajando actualmente, usa la sintaxis save opciones archivo x1 x2
· · · . Se escribe primero la palabra save seguida de espacio, en seguida la o las
opciones que se van a utilizar, si se usa más de una opción entonces cada opción
se separará por un espacio, luego de un espacio se escribe entre comillas simples
el nombre del archivo en el que se guardarán las variables y finalmente la/las
variables que se guardarán, cada una de ellas separadas por un espacio.

octave:31> x = 2;
octave:32> y = [1,2,3];
octave:33> z = [1 2 ; 4 5];
octave:34> save-ascii’archivo.txt’ x y z

Es necesario escribir save, las opciones pueden o no ser escritas, en caso


de no indicarse alguna opción la salida por default es un archivo de texto que

25
contiene información detallada sobre cada variable guardada, el nombre del ar-
chivo debe de proveerse, si el archivo no existe lo crea y si existe lo reemplaza.
Las variables a guardarse pueden o no especificarse, si no se especifica alguna
variable entonces se guardan todas las variables con las que se esté trabajando.

Las opciones de la función save son:


Opción Descripción
-ascii Guarda los datos en formato de texto.
-binary Guarda los datos en formato binario.
-float-binary Guarda los datos en formato binario de precisión sen-
cilla. Se debe de usar este formato solo si se sabe que
los datos a guardar pueden ser guardados usando es-
ta precisión.
-mat-binary Guarda los datos en formato binario de MATLAB. Es
el formato por default en el que se guardan los datos
en MATLAB.
La función load sirve para cargar datos de un archivo de texto plano, la
sintaxis a utilizar es en este caso:

octave:14> y = load(’X.txt’)
y =

6.60100 13.02966 10.32290 13.79483 1.65054


2.28473 12.80102 9.98589 14.11730 8.75711
6.21307 9.02257 2.29923 3.47374 7.99365
12.75629 9.01940 10.45939 12.85129 12.75086
3.60502 6.85801 0.16206 9.78344 0.91882
9.39162 2.60399 7.36981 7.87528 9.53574
1.74349 12.97184 4.47034 12.15191 9.58456
7.70008 1.27695 11.98621 2.05689 5.45029
2.47802 0.57247 3.42081 12.68678 3.27684
12.01603 9.17642 12.15015 10.33811 4.43015

octave:15> load(’X.txt’)
octave:16> X
X =

6.60100 13.02966 10.32290 13.79483 1.65054


2.28473 12.80102 9.98589 14.11730 8.75711
6.21307 9.02257 2.29923 3.47374 7.99365
12.75629 9.01940 10.45939 12.85129 12.75086
3.60502 6.85801 0.16206 9.78344 0.91882
9.39162 2.60399 7.36981 7.87528 9.53574
1.74349 12.97184 4.47034 12.15191 9.58456
7.70008 1.27695 11.98621 2.05689 5.45029
2.47802 0.57247 3.42081 12.68678 3.27684
12.01603 9.17642 12.15015 10.33811 4.43015

26
Como se ve, existen dos formas de cargar el archivo, la primera es asignárselo
directamente a una variable (y en el caso del ejemplo). La segunda es solo usar
el comando load con lo que Octave creará una variable con el mismo nombre
del archivo (sin extensión) y guardará el archivo ahı́. Cuando se usa el comando
save para guardar varias variables, es conveniente guardar las variables en ar-
chivos por separado, si las matrices son de tamaños distintos provocará un error
al usar load además de que sólo se puede usar load para asignar valor a una sola
variable, en el caso de un archivo .txt. Por el contrario si se carga un archivo
guardado como .mat no hace falta asignarse a una variable en particular, por
default se asigna cada variable en el archivo a una variable con el mismo nombre
que tenı́a al ser guardada, en este caso es posible recuperar más de una variable
desde un solo archivo. Ambos formatos son útiles, si se guardan en un archivo
.txt se puede usar otro programa para usar estos datos, por ejemplo gnuplot
para graficarlos, si por el contrario sólo se desea guardar los datos para usarse en
una sesión posterior entonces quizá sea más conveniente guardarlos como .mat.

27
Capı́tulo 4

Programación en Octave

Hasta ahora se ha hablado sobre el ambiente de Octave, la naturaleza de


las variables y la utilidad de los scripts para organizar las instrucciones de una
forma clara y susceptible a cambios. Por sı́ solo lo que se ha visto que no es
suficiente para resolver un problema.

4.1. Operadores relacionales y lógicos


Un operador relacional es aquel que compara dos números y devuelve como
respuesta falso o verdadero. Si el resultado es falso se devuelve o y si es verda-
dero se devuelve 1. Los operadores relacionales disponibles en Octave son:

< Menor a
> Mayor a
== Igual a
<= Menor o igual a
>= Mayor o igual a
∼= Distinto a

Los operadores relacionales usualmente son aplicables solamente a escalares,


pero en Octave es posible aplicar el operador a vectores y matrices siempre
que coincida el tamaño. En este último caso se devolverá una matriz del mismo
tamaño que las matrices comparadas y que contendrá el resultado de la compa-
ración entre cada par de elementos correspondientes.

octave:27> 1<3
ans = 1

octave:28> 5>0
ans = 1

octave:29> x = rand(5,1)
x =

28
0.265078
0.730114
0.969672
0.212333
0.050660

octave:30> y = rand(5,1)
y =

0.36415
0.42134
0.24123
0.47921
0.85776

octave:31> x<=y
ans =

1
0
0
1
1

También es posible comparar una matriz con un escalar, en este caso se com-
para cada elemento de la matriz con dicho escalar.

octave:32> M = rand(4)
M =

0.492392 0.671352 0.587814 0.400250


0.997428 0.880732 0.252315 0.638045
0.053192 0.755610 0.633398 0.196543
0.458146 0.403724 0.964102 0.215812

octave:33> M>0.5
ans =

0 1 1 0
1 1 0 1
0 1 1 0
0 0 1 0

octave:34> whos ans


Variables in the current scope:

Attr Name Size Bytes Class

29
==== ==== ==== ===== =====
ans 4x4 16 logical

Total is 16 elements using 16 bytes

Aunque el resultado es un vector/matriz lógico/a, es posible operar con estos


valores como si se trataran de números en punto flotante, de esta forma, si se
quiere calcular la suma de los elementos de la matriz ans en el ejemplo anterior
no habrı́a problemaalguno:

octave:35> sum(sum(ans))
ans = 8

4.2. Operadores Lógicos


Los operadores lógicos son usados en la lógica proposicional para aceptar
o rechazar una sucesión de proposiciones relacionadas entre sı́. Los operadores
lógicos que son posibles de usar en Octave son:

& Y Se utiliza para comprobar que dos sen-


tencias son afirmativas, si al menos al-
guna es falsa entonces se devuelve 0, y
1 en caso contrario.
| O Se utiliza para comprobar si de dos sen-
tencias al menos una es afirmativa, se
devuelve 1 si al menos una es afirmati-
va y 0 si ambas son falsas.
∼ negación Se utiliza para negar una sentencia, de-
vuelve 0 si la sentencia es verdadera y
1 si es falsa.

Los operadores lógicos funcionan con números, cualquier número distinto


a 0 (falso) se considera 1 (verdadero).

Los operadores lógicos como los operadores relacionales se pueden utilizar


en expresiones y operaciones matemáticas.

Los operadores lógicos al igual que los relacionales se pueden ocupar con
vectores y matrices siempre que se haga la comparación entre matrices del
mismo tamaño o una matriz y un escalar.

30
octave:39> x1 = 1;
octave:40> x2 = 0;
octave:41> x1&x2
ans = 0

octave:42> x1|x2
ans = 1

octave:43> ~x2
ans = 1

octave:44> y = [0 1 0;1 2 3;-1,0,4]


y =

0 1 0
1 2 3
-1 0 4

octave:45> y&x1
ans =

0 1 0
1 1 1
1 0 1

octave:46> y|x2
ans =

0 1 0
1 1 1
1 0 1

octave:47> ~y
ans =

1 0 1
0 0 0
0 1 0

octave:48> y2 = [0 0 1;2 3 7;1 1 1]


y2 =

0 0 1
2 3 7
1 1 1

octave:49> y&y2
ans =

0 0 0

31
1 1 1
1 0 1

Ya que los operadores lógicos y relacionales se pueden emplear en operaciones


matemáticas, estos tienen precedencia con respecto a las operaciones usuales.
Los órdenes de precedencia son los siguientes:

Orden de Precedencia Operación


1(la mayor) Paréntesis, si hay paréntesis anidados el
más interno tiene mayor precedencia.
2 Exponenciación
3 Operación lógica de negación (∼)
4 Multiplicación y división
5 Suma y resta
6 Operadores relacionales
7 Operación Lógica Y (&)
8(la menor) Operación lógica O ( | )

Si dos o más operadores tienen la misma precedencia entonces la operación


se efectuará de izquierda a derecha.

4.3. Sentencias condicionales


Una sentencia condicional en Octave es una instrucción que permite tomar
decisiones sobre si se ejecuta un grupo de comandos si se cumple una condición
o, por el contrario, omitirlos. En una sentencia condicional se evalúa una expre-
sión condicional y en caso de ser verdadera el grupo de comandos se ejecutará.

Estructura if-endif Esta estructura consta de 3 partes, la sentencia if que


evalúa una expresión condicional, una serie de instrucciones que serán ejecutadas
si la expresión condicional resulta verdadera, la sentencia endif que indica el
fin de los comandos afectados dentro de la sentencia if.

octave:56> x=0;
octave:57> y=1;
octave:58> z=4;
octave:59> if x==0
> y=2;
> z=0;
> x=1;
> endif
octave:60> x
x = 1
octave:61> y
y = 2
octave:62> z
z = 0

32
octave:63> if x==0
> x=100;
> y=0;
> endif
octave:64> x
x = 1
octave:65> y
y = 2
octave:66> z
z = 0

Un valor distinto de cero funciona como verdadero.

Estructura if-else-endif Esta estructura es usada para ejecutar uno entre


dos grupos de comandos en función de su evaluación lógica. la sintaxis es como
sigue:

octave:6> x=1;
octave:7> y=2;
octave:8> opcion=2;
octave:9> if opcion==1
> x=0;
> y=0;
> else
> x=10;
> y=10;
> endif
octave:10> x
x = 10
octave:11> y
y = 10

Si se cumple la sentencia lógica entonces se ejecuta el primer grupo de co-


mandos y en caso contrario se ejecuta el segundo grupo. También es posible
“anidar” de tal forma que se ejecute un grupo de comandos entre varios dispo-
nibles, para eso se usa la sentencia elseif seguida de una expresión condicional.

octave:14> x=0;
octave:15> y=1;
octave:16> opcion=3;
octave:17> if opcion==0
> x=10;
> elseif opcion==1
> y=10;
> elseif opcion==2
> x=x*y;

33
> elseif opcion==3
> y=x/y;
> else
> x=2;
> y=2;
> end

octave:18> x
x = 0

octave:19> y
y = 0

En este tipo de estructura, cuando se cumple una expresión condicional se


ejecutará el grupo de datos correspondiente y luego de eso no se seguirán eva-
luando las demás expresiones condicionales, es decir son ignoradas.

Estructura switch-case La función de la estructura switch-case es muy


similar al de la estructura if-elseif-else-end, también es utilizada para eje-
cutar un grupo de comandos pero no se verifica una expresión condicional sino el
valor de una variable (que en algún sentido es evaluar una expresión condicional
de manera indirecta), la sintaxis es la siguiente:

octave:19> x = 2;
octave:20> opcion = mod(x,2)
opcion = 0
octave:21> switch(opcion)
> case 0
> fprintf(’x es un número par\n’)
> case 1
> fprintf(’x es un número impar\n’)
> otherwise
> fprintf(’Ocurrio un error\n’)
> end
x es un número par

La estructura switch-case tiene las siguientes reglas:

Si hay más de una coincidencia solo se atenderá a la primera que aparezca.

Si no se encuentran coincidencias entonces se ejecuta el grupo de comandos


que siguen a la palabra otherwise (esta es opcional y no es necesario
usarse).
Si no hay coincidencias y la palabra otherwise no se encuentra entonces
no se ejecuta ningún grupo de comandos.

34
Una sentencia case puede tener más de un valor con el cual establecer
coincidencias. Para declararlos solo es necesario ponerlos a continuación
entre llaves y separados por comas { valor1, valor2,...}.

Para los que estén familiarizados con C, la estructura switch-case funciona


de la misma forma salvo que en C es necesario terminar cada grupo de comandos
con la sentencia break; ya que de no hacer esto se siguen buscando coincidencias
de tal forma que se puede ejecutar más de un grupo de comandos.

4.4. Bucles
Los bucles o sentencias repetitivas nos sirven para realizar un mismo grupo
de comandos en forma consecutiva. Cada una de estas repeticiones se denomina
paso o iteración. Existen dos tipos de bucles que se pueden usar en Octave,
estos son los bucles tipo for-endfor y los bucles tipo while-endwhile.
Estructura for-endfor La sintaxis para este tipo de bucle es la siguiente:

octave:1> for i=1:5


> x(i) = i;
> endfor
x
x =

1 2 3 4 5

octave:9> y = ones(1,10);
octave:10> for x = 1:2:10
> y(x) = 0;
> end
octave:11> y

y =

0 1 0 1 0 1 0 1 0 1

Se inicia con la palabra for seguida de espacio y un contador igualado a un


vector con los valores que tomará el contador en cada paso del bucle. Enseguida
un conjunto de instrucciones que se realizará en cada paso y finalmente la pala-
bra endfor para indicar el fin del bucle. En los ejemplos anteriores se muestra
que los valores que debe de tomar el contador no deben de ser necesariamente
consecutivos, además, estos valores no tienen restricciones en cuanto a que no
hace falta que tomen valores enteros (como suele usarse en C), ni siquiera deben
de ser valores distintos.

octave:1> a = ones(1,5)
a =

1 1 1 1 1

35
octave:2> for i = a
> x(a) = a;
> end
octave:3> x
x = 1

octave:15> a = zeros(1,5);
octave:16> cont = 1

cont = 1

octave:17> for i = linspace(1,2,5)


> a(cont) = i;
> cont = cont + 1;
> end
octave:18> a

a =

1.00 1.25 1.50 1.75 2.00

Si la variable contador tiene asignado la matriz nula [] entonces el ciclo no


se ejecutará.

Estructura while-endwhile
Estos bucles constan de las siguientes partes: la palabra while seguida de
espacio y una expresión condicional, una serie de instrucciones, y finalmente la
palabra endwhile para indicar el fin del bucle.

octave:24> x = 0;
octave:25> i = 1;
octave:26> while x<20
> x = x + i;
> i = i + 1;
> endwhile
octave:27> x
x = 21

Cuando se entra al bucle tipo while-endwhile se empieza por verificar la


expresión lógica, si esta es verdadera entonces se ejecutan las instrucciones den-
tro del bucle, si esta no se cumple entonces se ignora el bucle y se continua con
las siguientes instrucciones. En C existe el tipo de bucle do-while que asegura
que siempre se ejecuta al menos una vez el grupo de instrucciones, en Octave
este tipo de bucle no existe, hay que tener en cuenta este detalle.

36
Cuando se trabaja con bucles del tipo while hay que tener en cuenta que
este continuará ejecutándose hasta que la expresión lógica sea falsa, es algo
común cometer errores y que la expresión lógica siempre sea verdadera, en ese
caso se puede cancelar la ejecución del bucle mediante la combinación de teclas
control+C. Esto, sin embargo, detiene la ejecución de todo el código restante
por lo que se debe de ocupar en caso de que sepamos que se ha cometido un
error o no se desee continuar con el resto de cálculos por algún motivo.

Existen dos herramientas que nos ayuda al control de los bucles, la primera
es el comando break, este comando forza la terminación del bucle, en el caso
del bucle for-endfor ignora el resto de iteraciones y en el caso del bucle tipo
while-endwhile no se vuelve a evaluar la expresión lógica. La otra herramienta
es el comando continue al aparecer este comando se provoca que el grupo de
instrucciones en la iteración actual sea ignorada y se comienza con las instruc-
ciones de la siguiente iteración.

Estructura do-until Este bucle consta de iniciar el código con la palabra do, se-
guido de un espacio donde se colocarán todas las instrucciones que se ejecutarán
hasta que se cumpla la condición que coloquemos en la palabra until

octave:28> x = 0;
octave:29> i = 1;
octave:30> do
> x = x + i;
> i = i + 1;
> until x>20
octave:31> x
x = 21

Cuando se entra al bucle do-until primero se ejecutan las instrucciones


que hemos indicado debajo de la palabra do, finalmente se verifica la condición
lógica señalada después de la palabra until, si se satisface ya no se vuelve a
entrar en el bucle, de lo contrario se volverán a ejecutar las instrucciones dadas.

Ejercicio 4.1 Recordemos que una expresión lógica, en Octave, puede ser tam-
bién un vector. ¿Qué sucede si en una expresión condicional se usa un vector?.
Por ejemplo, ¿Qué devuelve el siguiente código?.

octave:47> x = [1 2 3 4];
octave:48> y = [4 3 2 1];
octave:49> a = 1;
octave:50> if x<y
> a = 0;
> endif

¿Y si el código es el siguiente?

octave:56> x = [0 0 0 0];
octave:57> y = [1 2 3 4];

37
octave:58> a = 1;
octave:59> if x<y
> a = 0;
> endif

38
Capı́tulo 5

Funciones

En la sección 1 se usaron algunas funciones que trabajan sobre matrices,


que devuelven la suma de sus elementos, que devuelven su tamaño, etc. Aunque
en Octave existen un sinnúmero de funciones predefinidas para hacer gran can-
tidad de tareas, siempre será necesario poder crear nuestras propias funciones
para realizar tareas personalizadas o rutinas que no se encuentran programadas
en Octave, las funciones sirven para esto.

5.1. Cómo definir una función


Una función es un conjunto de instrucciones que, al igual que los scripts,
son guardadas en un archivo .m, la cual es una forma de definir funciones en
Octave. Una función puede requerir de algunas variables de entrada (argumen-
tos) o no, y devuelven una o más variables de salida. Para manejar una función,
al igual que con los scripts, es necesario que la función se encuentre guardada en
el directorio donde se está trabajando actualmente. Debe de tener la siguiente
forma:

function [y] = paraboloide(x),


y = sum(x.^2);
endfunction

Se inicia con la palabra function seguida de espacio y el nombre de la va-


riable de salida, el operador de asignación y entre paréntesis los argumentos que
usará la función, la función podrı́a no necesitar de parámetro alguno, en ese
caso se deja vacı́o el interior de los paréntesis. Una función en Octave puede
no devolver valor alguno, una o más variables. Los siguientes son ejemplos de
funciones que no devuelven valores y que devuelven más de uno.

function [x,y] = funcion1(z),

39
x = z.^2;
y = z + x - 1;
endfunction

function funcion2()
fprintf(’Esta funcion no devuelve nada\n’)

endfunction

Ejecutar una función en Octave se realiza como en cualquier otro lenguaje,


tan solo se escribe el nombre de la función y entre paréntesis los valores de
entrada que requiere.

octave:79> z = 1:5;
octave:80> funcion1(z)
ans =

1 4 9 16 25

octave:81> funcion2
Esta funcion no devuelve nada

Como se puede observar en el ejemplo, la función funcion1 que queremos


que devuelva dos valores de salida solo devuelve uno, sólo es posible visualizar
la segunda variable de salida si esta es asignada a una variable, por ejemplo,
para poder obtener los dos valores de salida de la función podrı́amos hacer algo
como:

octave:82> [x,y] = funcion1(z)


x =

1 4 9 16 25

y =

1 5 11 19 29

Mientras que si lo que deseamos es solo el segundo valor que devuelve la


función podemos hacer:

octave:83> [~,y] = funcion1(z)


y =

1 5 11 19 29

Usando el operador ∼ no hace falta reservar memoria para una variable que
no vamos a ocupar. También es posible que una función haga un llamado a otra
función, como es el caso de la primer función que planteamos (paraboloide) que
usa la función sum, podemos usar una función siempre que esta esté definida
en el mismo directorio de trabajo, y se llamará de la forma usual. Algo que
es muy útil es que una función usada dentro de otra función sea una variable,

40
por ejemplo, si deseamos calcular la derivada en un punto, nos gustarı́a que
esta función sirva para cualquier función, o lo que es lo mismo, que la función
”derivada.acepte como argumento de entrada otra función. En C esto se hace
mediante un puntero a una función, en Octave esto se realiza mediante la ex-
presión:

octave:84> A = @funcion2
A = @funcion2
octave:85> A
A = @funcion2
octave:86> A()
Esta funcion no devuelve nada
El operador @ nos permite asignar la función a una variable para poder
enviarla como argumento a una función. Volviendo al ejemplo de la derivada de
una función, la función tendrı́a una estructura como la siguiente:
function [y] = derivada(f,x)
....
....
....
endfunction
Donde f es la función de la cual queremos obtener la derivada, x el punto
en el que hay que evaluarse y y la variable de salida con el valor de f 0 (x).

Una cosa importante y que no ha sido mencionada es que para poder ejecu-
tar una función desde un archivo .m (podrı́a definirse la función en la terminal
sin necesidad de un archivo que la contenga) es necesario que el archivo .m tenga
el mismo nombre de la función, es decir, en nuestros ejemplos los archivos de-
berı́an de llamarse paraboliode.m, funcion1.m, funcion2.m, derivada.m.
En general las funciones tienen muchas similitudes con los scripts, por ejemplo,
que son una serie de comandos y que estas pueden guardarse en un archivo .m
para poder ejecutarse después, sin embargo, las funciones tienen diferencias con
respecto a los scripts.
Una función puede requerir o no de argumentos de entrada. Un script no
requiere de argumentos de entrada para poder ejecutarse.
En una función todos los valores usados son eliminados (salvo por los
valores de salida y variables globales) una vez acabadas las instrucciones
de esta. En un script las variables usadas pueden seguirse usando y si no se
desea seguir usándolas requiere de eliminarse (usando clear por ejemplo).
Cuando se ejecuta una función o script, al igual que con los bucles, se puede
detener la ejecución de estos usando la combinación de teclas control+C.
Para terminar esta sección se muestra a manera de ejemplo un programa
que permite calcular el gradiente de una función usando diferencias finitas:

41
function [G] = grad(F,X,h), %F es el nombre de la
%función, X es el punto en el que se
%evaluará el gradiente, h es el tama~no del
%incremento en cada
% variable
dim = length(X); %Aquı́ se calcula el número de
%variables de la función
% En I se guardan vectores que representan los
%incrementosen cada variable
I = h*eye(dim);
%G es la inicialización del vector que se
%devolverá(el gradiente de
%F en X)
G = zeros(1,dim);
%En el siguiente bucle se calcula el gradiente para
%cada variable i, i=1,..,dim
for i = 1:dim
G(i) = (F(X+I(i,:))-f(X-I(i,:)))/(2*h);
endfor
endfunction

Como nota del programa anterior, X debe ser un vector renglón para que
las dimensiones de las sumas de matrices sean compatibles.

Ejercicio 5.1 Usando como base el programa anterior, elabórese un programa


que permita calcular la hessiana de una función F en el punto X con un incre-
mento h.

Otra forma de definir una función es la que se denomina Funciones Anónimas


y se hace directamente en la ventana de comandos, como en el siguiente ejemplo:

octave:36> f1 = @(x) x . ^ 2 - 2;
octave:37> f2 = @(t, x) - t . / x;

La sintaxis para definir funciones en la ventana de comandos es la siguiente:

nombrefuncion = @(numero de argumentos) funcion;

Notemos que la función f1 es una de un argumento y f2 es de dos.

Es importante resaltar que si se quieren definir funciones sencillas que sóla-


mente se usarán una vez es recomandable utlizar este método, de lo contrario
es mejor guardarlas en un arhivo script.

Ahora definimos una función con más de un parámetro, usando las funciones
anónimas.
function [x]=producto(f1,f2,a,b)
x=f1(a,b)*f2(a,b); %Regreso el producto de la función f1 con f2
% evaluada en (a,b)
endfunction

42
Ahora en la ventana de comandos, definimos f1 y f2 como en el siguiente
ejemplo y llamamos a la función producto con parámetros f1,f2,a,b donde (a,b)
es el punto donde se evaluarán las funciones:

octave:36> f1 = @(x,y) x^2-y;


octave:37> f2 = @(x, y) sqrt(x-y);
octave:38> [x]=producto(f1,f2,4,2)
x = 19.799

5.2. Subfunciones.
Un archivo .m donde hemos programado una función puede contener fun-
ciones secundarias llamadas subfunciones. Estas funciones secundarias sólo son
visibles para las otras funciones en el mismo archivo de la función. Por ejemplo,
un archivo f.m que contenga

function f ()
printf ("in f, calling g\n");
g ()
endfunction
function g ()
printf ("in g, calling h\n");
h ()
endfunction
function h ()
printf ("in h\n")
endfunction

Define a una función principal f y dos subfunciones. Las subfunciones h y g


sólo pueden llamadas desde la función principal f o desde las otras subfunciones,
pero no desde afuera del archivo f.m

5.3. Manejadores de Funciones.


Un manejador de función es un apuntador a otra función y está definido con
la sintaxis:

@nombre de la función

Por ejemplo;

f = @sin

Crea un manejador de función llamado f que nos remite a la función seno.


Los manejadores de funciones son usados para llamar indirectamente a otras
funciones o pasar una función como argumento de otra.
Consideremos como ejemplo la función quad, la cual realiza integración numérica
basada en la cuadratura de Gauss y la función del ejemplo anterior, la integra-
ción se hara de 0 a π:

43
f = @sin
quad (f, 0, pi)
ans =
2

Como otro ejemplo, podemos usar la función feval (que se tratará en la si-
guiente subsección) para llamar a una función usando manejadores de funciones
0 simplemente escribir el nombre de la función del manejador de función seguido
de un argumento entre paréntesis.

f = @sin
feval (f, pi/4)
ans =
0.70711
f(pi/4)
ans =
0.70711

5.4. Comando feval.


La función feval nos permite llamar a una función desde un string que con-
tiene su nombre. ES útil cuando escribimos una función que necesita llamar
funciones sumnistradas por el usuario.
Como ejemplos tenemos:

f = @exp
feval (f, 1)
ans =
2.71183

feval (acos, -1)


ans =
3.1416

5.5. Formas de llamar a una función.


cuando trabajamos con funciones (ya sea con las que cuenta Octave o las
que nosotros mismos programamos), hay ocasiones en las que necesitamos que
los argumentos de estas sean otras funciones que hemos programado, es por eso
que veremos dos formas de cómo llamarlas cuando lo necesitemos.
1.Como apuntadores a una función: Para esto necesitamos tener programada
previamente una función guardada en un script y luego cuando la necesitemos
en un argumento de cualquier otra función, la llamaremos con

@nombre de la función

Por ejemplo, si programamos la función:

44
function [y] = prueba(x),
y = cos(x) + exp(x+1);
end

Y queremos usar la evalución de dicha función en 10 como argumento de la


función cos, entonces la llamarı́amos ası́:

cos(@prueba(10))

ans = 59,873.347

Teniendo simepre cuidado de que nos encontramos en la carpeta donde se


encuentra el archivo prueba.m.

5.6. Directorios.
Cuando trabajamos con funciones programadas por nosotros mismos y la
llamamos, Octave busca una lista de en directorios el archivo en el que se haya
declarado dicha función. Esta lista de directorios es conocido como Loadpath.
Por default, el load path contiene una lista de diretorios distribuidos con Octave
más el directorio en que se esté trabajando.
Es posible agregar o remover directorios del load path usando los comandos
addpath y rmpath. Como ejemplo, el siguiente código agrega ‘ /Octave’ al load
path.

addpath ("~/Octave")

Después de esto, el directorio -/Octave”sera parte de los directorios en los que


Octave buscará funciones.

45
Capı́tulo 6

Gráficas

Además de los resultados numéricos, mostrar gráficas que muestren dichos


resultados siempre es más ilustrativo. En Octave se pueden realizar gr”aficas en
2D y 3D usando los comandos plot y plot3 respectivamente, en esta sección
se mostrará algunas de las caracterı́sticas de estas funciones.

6.1. El comando plot


El comando plot sirve para graficar en 2D, es una función que se comporta
de forma distinta dependiendo del número de parámetros y el tipo que sea in-
gresado.

Si se ingresa un solo parámetro y este es un vector, entonces el vector lo to-


mará como una serie y se graficará una correspondencia de esta con los números
naturales, por ejemplo el código:

octave:6> x = rand(1,100);
octave:7> plot(x)
octave:8> print(’-djpg’,’1.jpg’)

Produce:

46
Figura 6.1: Gráfica de una serie

El comando usado luego del comando plot, print, se usa para guardar la
imagen graficada en un archivo, en el caso anterior se usÓ el argumento ’-djpg’
para guardar la imagen en el archivo ’ej1.jpg’, puede guardarse la imagen en
distintos tipos de archivo, por ejemplo .eps, PS, PNG, PDF, TIFF,.

Si el argumento es una matriz entonces cada columna la considerará como


una serie de tiempo distinta y las graficará todas ellas en la misma gráfica y en
distinto color. Por ejemplo el código:

octave:14> x = rand(20,5);
octave:15> plot(x)
octave:16> print(’-djpg’,’2.jpg’)

Muestra:

47
Figura 6.2: Gráfica de varias series

En general plot la forma más común de usar plot es usando dos argumen-
tos, estos argumentos son vectores que contendrán las abscisas y las ordenadas
respectivamente, hay que tener en cuenta que deben de tener el mismo tamaño.
En el código siguiente se grafica la función f (x) = cos(x) en el intervalo [1, 4].

octave:21> x = 1:0.01:4;
octave:22> y = cos(x);
octave:23> plot(x,y)
octave:24> print(’-djpg’,’3.jpg’)

48
Figura 6.3: Gráfica de f (x) = cos(x)

En realidad lo que hace Octave es hacer una correspondencia entre los va-
lores de x y los de y y graficar punto por punto, al no especificarse más acerca
de lo que se quiere, por default, Octave unirá esos puntos por una lı́nea, si sólo
necesitáramos ver los puntos se necesita agregar más argumentos, por ejemplo:

octave:52> x = 1:0.5:4;
octave:53> y = x.^2;
octave:54> plot(x,y,’marker’,’*’,’color’,
’b’,’marker size’,20,’linestyle’,’none’)
octave:55> print(’-djpg’,’4.jpg’)

Que muestra la siguiente figura:

49
Figura 6.4: Gráfica discreta

Las opciones que acepta plot son:

1. color. Esta opción cambia el color del gráfico, puede usarse con valores
predeterminados como b para azul, r para rojo, etc. O puede usarse un
vector con porcentajes de los colores primarios azul, rojo y verde, por
ejemplo [0, 4, 0,5, 0].
2. marker. Esta opción determina el tipo de marcador que usarán los pun-
tos en el gráfico, puede usar como valores . para usar un punto, ∗ para
asterisco, o para circulo, s para cuadrado, d para diamante y muchos más.

3. linestyle. Determina el tipo de lı́nea con el que se unirán los puntos, como
valores puede tomar: − para una lı́nea recta, −− lı́nea punteada, none
para no poner lı́nea etc.
4. linewidth Se usa para cambiar el grosor de la lı́nea dibujada entre puntos.

5. markersize. Determina el tamaño del marcador, su argumento es un núme-


ro real.
6. markeredgecolor. Indica el color del contorno de la figura usada como
marcador, puede usar los colores predeterminados por letras o un vector
de 3 componentes como la opción color.

50
7. markerfacecolor. Se usa para indicar el color del relleno de la figura usada
como marcador, usa las mismas opciones que color.

La sintaxis para un formato muy especı́fico puede ser muy larga, pero si
no hace falta especificar mucho se puede usar un formato más corto como el
siguiente:

octave:83> x = linspace(-1,3,10);
octave:84> y = x.^2;
octave:85> plot(x,y,’k.’)
octave:86> print(’-djpg’,’5.jpg’)
octave:87> plot(x,y,’m’)
octave:88> print(’-djpg’,’6.jpg’)

Que produce las figuras:

Figura 6.5: Gráfica simple (solo puntos)

51
Figura 6.6: Gráfica simple (puntos unidos)

Antes pudimos graficar en una sola gráfica varias series de tiempo, también
es posible graficar varias gráficas en un mismo gráfico, para eso usamos los
comandos hold on y hold off. Para usarse sólo se debe de colocar entre estos
las instrucciones plot.

octave:1> x = linspace(0,pi,100);
octave:2> hold on
octave:3> plot(x,cos(x),’ro’)
octave:4> plot(x,sin(x),’bp’)
octave:5> plot(x,x.^2,’mx’)
octave:6> plot(x,exp(x),’k^’)
octave:7> hold off
octave:8> print(’-djpg’,’7.jpg’)

El código anterior produce:

52
Figura 6.7: Gráficas usando hold on y hold off

Además de las figuras es posible agregar texto a las mismas, por ejemplo
incluir un tı́tulo a las gráficas o nombrar los ejes, etc.

Para darle un tı́tulo a la imagen se usa el comando title, para darle nom-
bre a los ejes se usa xlabel y ylabel, para poner una texto que asocie los
marcadores con alguna caracterı́stica se usa legend, todas estas funciones(salvo
legend) usan como parámetro una cadena de texto (hay que tener cuidado con
los acentos) que indica lo que debe de escribirse como etiqueta, el tamaño del
texto se puede ajustar con la opción fontsize y un tamaño de fuente, el valor
por defecto es 10.

octave:27> x = linspace(0,pi,100);
octave:28> hold on
octave:29> xlabel(’Abscisas’,’fontsize’,20)
octave:30> ylabel(’Ordenadas’,’fontsize’,20)
octave:31> title(’Ejemplo de texto en
graficas’,’fontsize’,20)
octave:32> plot(x,cos(x),’ro’)
octave:33> plot(x,sin(x),’b.’)
octave:34> legend(’Grafica de cos(x)’,’Grafica de
sen(x)’)
octave:35> hold off
octave:36> print(’-djpg’,’8.jpg’)

53
Figura 6.8: Gráficas con etiquetas

También es algo común que queramos graficar varias funciones o conjuntos


de datos en distintas gráficas pero vistas en la misma figura, para ello usamos
el comando subplot. El comando subplot(m,n,p) tiene 3 argumentos, los dos
primeros definen una partición de la figura, la parte en m renglones y n colum-
nas, p es la posición en la que se colocará el gráfico. Para comprender mejor
esto veamos el siguiente ejemplo:

octave:38> x = linspace(0,pi,100);
octave:39> subplot(2,2,1)
octave:40> plot(x,cos(x),’yo’)
octave:41> subplot(2,2,2)
octave:42> plot(x,sin(x),’cd’)
octave:43> subplot(2,2,3)
octave:44> plot(x,x.^2,’color’,rand(1,3),’marker’,’h
’,’linestyle’,’none’)
octave:45> subplot(2,2,4)
octave:46> plot(x,exp(x),’bx’)
octave:47> print(’-djpg’,’9.jpg’)

54
Figura 6.9: Gráficas con etiquetas

También se debe de notar que cada subgráfica es independiente de las demás,


por lo que se pueden graficar más de una función en cada una de ellas, insertar
tı́tulos, legendas, y en general, todo lo que se ha visto hasta ahora.

6.2. El comando contour.


Crea una gráfica en dos dimensiones.
Grafica curvas de nivel de la matriz z, usando la matriz de contorno c calulada
por contourc con los mismos argumentos. Por ejemplo, el siguiente código:

x = 0:2;
y = x;
z = x’*y;
contourc(x, y, z, 2:3)

Genera:

2.0000 2.0000 1.0000 3.0000 1.5000 2.0000


2.0000 1.0000 2.0000 2.0000 2.0000 1.5000

La apariencia de las curvas de nivel pueden ser definidas con un estilo de


linea linestyle de la misma manera que plot.
Como ejemplo, el código;

55
colormap ("default");
[x, y, z] = peaks ();
contour (x, y, z);
title ({"contour() plot (isolines of constant Z)"; "Z = peaks()"});

Genera la figura:

Figura 6.10: Gráficas de curvas de nivel

Donde, [x, y, z] = peaks () grafica una función con muchos máximos y


mı́nimos. Dicha función tiene la forma,

z = 3*(1-x)^2*exp(-x^2 - (y+1)^2) ...


- 10*(x/5 - x^3 - y^5)*exp(-x^2-y^2) ...
- 1/3*exp(-(x+1)^2 - y^2)

56
Capı́tulo 7

Temas Avanzados

Cuando se programa se está interesado en que nuestro código nos devuelva el


resultado que deseamos, la mayorı́a de las veces esto es suficiente, sin embargo,
cuando los problemas aumentan en dificultad, resolver el problema ya no es sufi-
ciente, además de resolverlo necesitamos que lo haga lo más rápido posible. Esta
sección se centra en mencionar algunos detalles que están más bien relacionados
a la programaci”on en general y c”alculo num”erico.

7.1. Aumentar la calidad del código escrito en


Octave
El uso de scripts y funciones ayuda a tener ordenado lo que se desea progra-
mar, siempre es conveniente crear módulos sobre los cuales si ocurre un error
sea fácil identificar la sección de donde proviene dicho error. Es por esto que un
buen hábito es el de usar estas herramientas. En general, Octave es un lenguaje
interpretado, de alto nivel y por tanto difı́cilmente podr”a compararse en tiem-
po de ejecución a programas compilados en C, y sin embargo, existen algunas
técnicas de programación que pueden ayudarnos a que esta diferencia no sea
tan significativa.

7.2. Vectorizar, la clave para aumentar la velo-


cidad
El usuario de Octave debe de acostumbrarse a que todo son matrices. Cuan-
do se crearon los ordenadores y empezaron a surgir los lenguajes de programa-
ción casi todos los procesos eran escalares. Todo se realizaba mediante operacio-
nes lógicas que operaban en bloques pequeños de memoria. Cuando los equipos
se volvieron más potentes se pensó en formas más eficientes de realizar cálculos
y esto dió paso al concepto de vectorización.

Una operación escalar se realiza elemento a elemento. Cuando se quieren


sumar dos matrices o vectores se cada elemento con su correspondiente y se
guarda en un tercer vector. En una operación vectorial se hace esta operación

57
pero para un bloque grande de memoria, de tal forma que todas las sumas se
realizan a la vez.

Octave es un software que está basado esta ideologı́a y está optimizado


para hacer las cosas en dicha forma, pero si se le pide hacer las cosas elemento a
elemento (por ejemplo en un bucle) también lo hará aunque no está optimizado
para trabajar de esta forma. Entonces, hay que pensar siempre en la forma en
que se hagan los cálculos por bloques de memoria en vez de elemento a elemento
usando por ejemplo submatrices.

7.3. El truco más importante de la programa-


ción en Octave
El truco más importante en Octave para que los scripts tengan una velocidad
aceptable es evitar los bucles contador. Esta es la estructura más lenta en este
lenguaje. A manera de ejemplo considere el siguiente código en el que se suman
dos matrices usando dos métodos distintos (el ejemplo fue obtenido de [?]).
El primer método consiste en sumarlas elemento a elemento usando un ciclo
anidado en otro, en el segundo método simplemente se suman, para medir la
eficiencia de cada método usamos la pareja tic y toc para medir el tiempo de
ejecución de cada método.

a = rand(66); %matriz de 66 x 66
b = rand(66);
tic
for i = 1:66

for j = 1:66

c(i,j) = a(i,j)+b(i,j);

endfor

endfor

toc
Elapsed time is 0.05702 seconds.

tic
c = a+b;

toc
Elapsed time is 3.501e-05 seconds.

Como se observa que la diferencia es muy grande, usualmente del orden


de 100 veces más rápido (en el ejemplo 1628 veces para ser exactos). Cuando
pensamos en resolver una ecuación diferencial parcial, esta diferencia puede con-
vertir un proceso que tardarı́a un dı́a en uno que tardarı́a apenas unos minutos.

58
Podrı́amos decir que fue un ejemplo amañado ya que el segundo método (el
más rápido) es más intuitivo de hacer y en realidad no es algo que harı́amos de
forma premeditada. Supongamos ahora el siguiente ejemplo, tenemos un vector
y una matriz, supongamos que deseamos multiplicar cada renglón de la matriz
por un elemento del vector. Nuevamente se pensará en realizar por dos métodos
diferentes, en el primero se formará un bucle y se tomará cada elemento del
vector y se multiplicará por cada renglón de la matriz y se guardará en otra
matriz, en el segundo método la secuencia de números del vector se convertirán
en una matriz para poder luego multiplicar la matriz.
a = 1:66;
b = rand(66);
tic
for i = 1:66

c(i,:) = a(i)*b(i,:);

endfor

toc

Elapsed time is 0.002794 seconds.


a = 1:66;
b = rand(66);

tic
c = a’*ones(1,66).*b;

toc

Elapsed time is 0.000108 seconds.

Ahora podemos notar que el segundo método es aproximadamente 25 veces


más rápido que el primero. A diferencia de nuestro ejemplo anterior, aquı́ se
veı́a perfectamente justificado el uso del bucle, y sin embargo al darle la vuelta
al problema (aun cuando pareciera que estamos realizando más operaciones) el
tiempo resulta menor.

Lo que hace que estos bucles sean lentos no es exactamente el bucle en sı́,
tampoco la ausencia de vectorización, en los ejemplos anteriores el principal
problema fue en la asignación de memoria. Cuando se sumaron las matrices en
el primer ejemplo al final tenı́amos una matriz en donde se estaba guardando
la suma, recordemos que las variables no se declaran y se crean en el instante
en el que se les asigna un valor, cuando se inicia el bloque, esta matriz se está
igualando a un escalar (1x1), en los siguientes pasos del ciclo la matriz está
cambiando de tamaño hasta que al final terminamos con una matriz de 66x66,
precisamente porque no lo vemos es que pasa desapercibido, Octave realiza la
reserva de memoria para la variable, y cuando esta cambia de tamaño realiza
la reserva de memoria para una variable más grande, copia los valores de la

59
variable anterior en la nueva variable, asigna el nuevo valor calculado y libera
la memoria de la variable vieja. Es por lo que los ciclos se vuelven tan lentos.
En el segundo caso la reserva de memoria se realiza sólo una vez. Además de
evitar los ciclos, una práctica común es reservar memoria para las variables,
por ejemplo creando una matriz de ceros del tamaño que necesitamos. Este
comportamiento está ligado al funcionamiento de los arrays en C; un buen texto
para comprenderlo mejor es [?] donde encontraremos un capı́tulo inicial sobre
qué es verdaderamente un array y qué relación tiene con un puntero.

60

También podría gustarte