P. 1
notas-metnu

notas-metnu

|Views: 101|Likes:
Publicado porRubi Poot

More info:

Published by: Rubi Poot on Sep 04, 2011
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

08/24/2012

pdf

text

original

Sections

  • 1.1. Introducci´on
  • 1.2. ¿Qu´e es un algoritmo? Medidas de eficiencia
  • 1.3. Complejidad. Complejidad computacional
  • 1.4. Complejidad agebr´aica y anal´ıtica
  • 1.5. Precisi´on num´erica
  • 2.1.1. Representaci´on binaria de la informaci´on
  • 2.2. Memoria, dispositivos de entrada/salida, uni-
  • 2.3. Lenguaje de m´aquina, lenguajes de ensambla-
  • 3.1. Nociones sobre trabajo en el Sistema Opera-
  • 3.2. Compilado, ejecuci´on y depuraci´on de progra-
  • 3.3.1. Nombres de variables
  • 3.3.2. Tipo y tama˜no de datos
  • 3.4. Un ejemplo. El primer programa en C
  • 3.5.1. Funciones m´as usuales
  • 3.6. Ambito de definici´on y validez de las varia-
  • 3.7. Precedencia y orden de evaluaci´on entre ope-
  • 4.1. Declaraciones. Tipos de datos escalares: ente-
  • 4.2. Conversiones de tipo
  • 4.3.1. Errores de redondeo en computaci´on
  • 4.3.2. Error de cancelaci´on
  • 5.2. Funciones: anatom´ıa y uso
  • 5.3. Convenciones sobre el paso de argumentos
  • 5.4. Clases de almacenamiento
  • 5.5. Recursi´on
  • 5.6. Funciones definidas en stdio y math
  • 6.1. Introducci´on
  • 6.2.1. M´etodo de bisecci´on
  • 6.2.2. M´etodo de Newton-Raphson
  • 6.2.3. M´etodo de la secante
  • 6.2.4. Regula Falsi
  • 6.3.1. Convergencia del m´etodo de bisecci´on
  • 6.3.2. Convergencia del m´etodo de Newton-Raphson
  • 6.3.3. Convergencia del m´etodo de la secante
  • 6.3.4. Convergencia del m´etodo de regula falsi
  • 6.4. Teor´ıa general de los m´etodos iterativos. Ite-
  • 6.5.1. M´etodo de Newton-Raphson
  • 6.5.2. M´etodo de Gauss-Seidel
  • 6.6. Ejercicios de aplicaci´on
  • 7.1. An´alisis te´orico: orden de complejidad ´optimo
  • 7.2.1. M´etodo de comparaci´on (selecci´on)
  • 7.2.2. M´etodo de inserci´on
  • 7.2.3. M´etodo de intercambio (burbuja)
  • 7.3.1. Heapsort
  • 7.4. Quicksort
  • 8.1. Direccionamiento indirecto. Punteros: varia-
  • 8.2.1. Punteros y arrays
  • 8.3. Algebra de punteros
  • 8.4. Punteros a caracteres
  • 8.5. Ejemplos de utilizaci´on de punteros
  • 9.1. Tipos de datos definidos por el usuario: es-
  • 9.2. Operaciones con estructuras
  • 9.3. Punteros a estructuras y estructuras ligadas
  • 10.1.1. Sistemas triangulares
  • 10.2. Eliminaci´on gaussiana
  • 10.3. Estabilidad num´erica y su mejora. Pivotado
  • 10.4. La variante de Gauss-Jordan
  • 11.1. La descomposici´on LU
  • 11.2. Escenas compactas en la eliminaci´on gaus-

Computaci´ on y M´etodos Num´ericos

P56, libre elecci´ on
Notas de clase y tareas
M. Araceli Gar´ın
Prof. Titular de Econom´ıa Aplicada
Dpto. Econom´ıa Aplicada III
Fac. CC. Econ´omicas y Empresariales
Bilbao
´
Indice general
1. Nociones b´ asicas sobre algoritmos 1
1.1. Introducci´ on . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. ¿Qu´e es un algoritmo? Medidas de eficiencia . . . . . . . . . . 2
1.3. Complejidad. Complejidad computacional . . . . . . . . . . . 5
1.4. Complejidad agebr´ aica y anal´ıtica . . . . . . . . . . . . . . . 6
1.5. Precisi´on num´erica . . . . . . . . . . . . . . . . . . . . . . . . 7
1.6. An´alisis de Algoritmos. L´ımites de complejidad . . . . . . . . 10
2. Introducci´on al lenguaje de programaci´on C (I) 13
2.1. Nociones sobre trabajo en el Sistema Operativo UNIX. El
editor vi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.2. Compilado, ejecuci´on y depuraci´ on de programas en C . . . . 20
2.3. Variables escalares, subindicadas (arrays) y constantes . . . . 21
2.3.1. Nombres de variables . . . . . . . . . . . . . . . . . . . 22
2.3.2. Tipo y tama˜ no de datos . . . . . . . . . . . . . . . . . 23
2.4. Un ejemplo. El primer programa en C . . . . . . . . . . . . . 26
2.5. Operadores y funciones standard m´ as usuales . . . . . . . . . 30
2.5.1. Funciones m´as usuales . . . . . . . . . . . . . . . . . . 31
2.6. Ambito de definici´ on y validez de las variables. Variables ex-
ternas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.7. Precedencia y orden de evaluaci´ on entre operadores . . . . . . 39
3. Introducci´on al lenguaje de programaci´on C (II) 41
3.1. Declaraciones. Tipos de datos escalares: enteros, car´acter, de
coma flotante y enumeraci´ on . . . . . . . . . . . . . . . . . . 41
3.2. Conversiones de tipo . . . . . . . . . . . . . . . . . . . . . . . 45
3.3. Nociones sobre representaci´on de datos e implicaciones sobre
la precisi´ on num´erica . . . . . . . . . . . . . . . . . . . . . . . 48
3.3.1. Errores de redondeo en computaci´ on . . . . . . . . . . 51
3.3.2. Error de cancelaci´ on . . . . . . . . . . . . . . . . . . . 52
4. Introducci´on al lenguaje de programaci´on C (III) 55
4.1. Sentencias de control de flujo: for, if , do, while, switch. Ejem-
plos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
i
ii
´
Indice general
4.2. Funciones: anatom´ıa y uso . . . . . . . . . . . . . . . . . . . . 64
4.3. Convenciones sobre el paso de argumentos . . . . . . . . . . . 65
4.4. Clases de almacenamiento . . . . . . . . . . . . . . . . . . . . 68
4.5. Recursi´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.6. Funciones definidas en stdio y math . . . . . . . . . . . . . . 72
5. Algoritmos iterativos 79
5.1. Introducci´ on . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.2. Resoluci´on de ecuaciones . . . . . . . . . . . . . . . . . . . . . 81
5.2.1. M´etodo de bisecci´ on . . . . . . . . . . . . . . . . . . . 81
5.2.2. M´etodo de Newton-Raphson . . . . . . . . . . . . . . 83
5.2.3. M´etodo de la secante . . . . . . . . . . . . . . . . . . . 85
5.2.4. Regula Falsi . . . . . . . . . . . . . . . . . . . . . . . . 87
5.3. Comparaci´ on de los distintos ´ordenes de convergencia . . . . 88
5.3.1. Convergencia del m´etodo de bisecci´ on . . . . . . . . . 88
5.3.2. Convergencia del m´etodo de Newton-Raphson . . . . . 90
5.3.3. Convergencia del m´etodo de la secante . . . . . . . . . 93
5.3.4. Convergencia del m´etodo de regula falsi . . . . . . . . 95
5.4. Teor´ıa general de los m´etodos iterativos. Iteraci´on del punto
fijo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.5. Generalizaci´ on a sistemas de ecuaciones . . . . . . . . . . . . 99
5.5.1. M´etodo de Newton-Raphson . . . . . . . . . . . . . . 101
5.5.2. M´etodo de Gauss-Seidel . . . . . . . . . . . . . . . . . 102
5.6. Ejercicios de aplicaci´on . . . . . . . . . . . . . . . . . . . . . . 103
6. M´etodos de ordenaci´on 105
6.1. An´ alisis te´orico: orden de complejidad ´ optimo alcanzable para
algoritmos internos . . . . . . . . . . . . . . . . . . . . . . . . 105
6.2. M´etodos simples de complejidad O(n
2
): comparaci´ on, inser-
ci´on, burbuja . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
6.2.1. M´etodo de comparaci´on (selecci´on) . . . . . . . . . . . 108
6.2.2. M´etodo de inserci´on . . . . . . . . . . . . . . . . . . . 110
6.2.3. M´etodo de intercambio (burbuja) . . . . . . . . . . . . 111
6.3. M´etodos de complejidad O(nlogn): Quicksort y Heapsort . . . 113
6.3.1. Heapsort . . . . . . . . . . . . . . . . . . . . . . . . . 113
6.4. Quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
7. El lenguaje de programaci´ on C (IV) 125
7.1. Direccionamiento indirecto. Punteros: variables que contienen
la ubicaci´ on en memoria de otras variables . . . . . . . . . . . 125
7.2. Punteros y funciones: paso de argumentos de referencia ha-
ciendo uso de punteros . . . . . . . . . . . . . . . . . . . . . . 128
7.2.1. Punteros y arrays . . . . . . . . . . . . . . . . . . . . . 131
7.3. Algebra de punteros . . . . . . . . . . . . . . . . . . . . . . . 134
´
Indice general 1
7.4. Punteros a caracteres . . . . . . . . . . . . . . . . . . . . . . . 137
7.5. Ejemplos de utilizaci´ on de punteros . . . . . . . . . . . . . . . 139
8. El lenguaje de programaci´ on C (V) 149
8.1. Tipos de datos definidos por el usuario: estructuras . . . . . . 149
8.2. Operaciones con estructuras . . . . . . . . . . . . . . . . . . . 151
8.3. Punteros a estructuras y estructuras ligadas. Arboles binarios.
Ejemplos de uso . . . . . . . . . . . . . . . . . . . . . . . . . 158
9. Soluci´on de sistemas de ecuaciones lineales 167
9.1. M´etodos directos para la resoluci´ on de sistemas de ecuaciones
lineales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
9.1.1. Sistemas triangulares . . . . . . . . . . . . . . . . . . . 168
9.2. Eliminaci´ on gaussiana . . . . . . . . . . . . . . . . . . . . . . 169
9.3. Estabilidad num´erica y su mejora. Pivotado total y parcial.
Complejidad algor´ıtmica . . . . . . . . . . . . . . . . . . . . . 172
9.4. La variante de Gauss-Jordan . . . . . . . . . . . . . . . . . . 176
10.Soluci´on de sistemas de ecuaciones lineales sin inversi´on de
la matriz de coeficientes 179
10.1. La descomposici´on LU . . . . . . . . . . . . . . . . . . . . . . 179
10.2. Escenas compactas en la eliminaci´ on gaussiana: m´etodos de
Doolittle, Crout y Choleski . . . . . . . . . . . . . . . . . . . 184
11.Simulaci´on (I). Generaci´ on de n´ umeros aleatorios 189
11.1. N´ umeros aleatorios y M´etodo de Montecarlo. Integraci´ on num´eri-
ca . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
11.2. Otras aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . 195
11.3. Generaci´on de observaciones pseudoaleatorias uniformes . . . 195
11.4. Generadores multiplicativos: elecci´on del multiplicador, m´ odu-
lo y semilla . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
12.Simulaci´on (II). Generaci´on de variables no uniformes 205
12.1. M´etodo de inversi´ on. Ejemplos . . . . . . . . . . . . . . . . . 205
12.2. M´etodo de rechazo. Ejemplos . . . . . . . . . . . . . . . . . . 207
12.3. M´etodo de transformaci´ on. Ejemplos . . . . . . . . . . . . . . 210
12.4. M´etodos espec´ıficos . . . . . . . . . . . . . . . . . . . . . . . . 211
2
´
Indice general
Cap´ıtulo 1
Nociones b´asicas sobre algoritmos
1.1. Introducci´ on
Es evidente que las matem´aticas son utilizadas de una forma u otra en
la mayor´ıa de las ´ areas de la ciencia y la industria.
Durante el siglo XX avanzados m´etodos y modelos matem´aticos han sido
aplicados en distintas ´ areas como la medicina, la econom´ıa o las ciencias so-
ciales. A menudo, las aplicaciones generan problemas matem´aticos que por
su complejidad no pueden ser resueltos de manera exacta. Uno entonces, se
restringe a casos especiales o modelos demasiado simplificados que puedan
ser analizados. En la mayoria de los casos, se reduce el problema a un pro-
blema lineal, por ejemplo, una ecuaci´ on diferencial, la funci´ on objetivo en un
problema de optimizaci´on, etc. Tal aproximaci´on puede ser muy efectiva y
explicar conceptos y puntos de vista que al menos cualitativamente pueden
ser aplicados al problema inicial. Sin embargo, a veces, tal aproximaci´on no
es suficiente o incluso puede inducir a error.
La alternativa que nosotros plateamos aqui, es la de tratar un problema
menos simplificado, utilizando el potencial del c´ alculo num´erico. La cantidad
de trabajo depender´ a del grado de precisi´on demandada.
Con la utilizaci´ on del ordenador, desarrollado en los ´ ultimos cincuenta
a˜ nos, las posibilidades de utilizar m´etodos num´ericos han crecido enorme-
mente.
Desarrollar un m´etodo num´erico, significa en la mayor´ıa de los casos, la
aplicaci´ on de un peque˜ no n´ umero de ideas generales y relativamente simples.
Dichas ideas han de ser combinadas de forma intuitiva con un conocimiento
3
4 1 Nociones b´ asicas sobre algoritmos
del problema que puede ser obtenido de distintas formas fundamentalmente
con m´etodos del an´ alisis matem´atico.
A lo largo de la asignatura ilustratremos la utilizaci´on de las ideas ge-
nerales que se esconden detr´as de los m´etodos num´ericos que resuelven los
problemas m´as habituales. Normalmente dichos problemas aparecen como
subproblemas o detalles computacionales de problemas m´ as grandes y com-
plejos.
En este cap´ıtulo comenzamos presentando conceptos y terminolog´ıa b´ asi-
ca en el desarrollo de posteriores cap´ıtulos.
1.2. ¿Qu´e es un algoritmo? Medidas de eficiencia
Por problema num´erico entenderemos una clara e inambigaa descrip-
ci´ on de una relaci´ on funcional entre datos (inputs) -es decir, variables in-
dependientes en el problema- y resultados (outputs). Datos y resultados, que
constituyen un conjunto finito de cantidades reales. La relaci´ on funcional
puede, por su parte, estar impl´ıcita o expl´ıcitamente representada.
Por algoritmo entenderemos un procedimiento formado por un con-
junto finito de reglas que especifican una secuencia finita de operaciones,
mediante las cuales se obtiene la soluci´on de un problema, o bien, de una
clase espec´ıfica de problemas.
Analizaremos algunos aspectos de esta ´ ultima definici´ on:
1. Cada paso de un algoritmo debe estar definido de manera precisa y sin
ambig¨ uedad. Las acciones a llevar a cabo deben estar rigurosamente
especificadas en cada paso.
2. El algoritmo debe llegar a la soluci´ on del problema en un n´ umero
finito de pasos. La restricci´on general de necesitar un n´ umero finito
de pasos, en ocasiones no es suficiente, ya que dicho n´ umero aunque
finito puede llegar a ser muy elevado. Un algoritmo ´ util, debe requerir
no s´ olo un n´ umero finito, sino adem´ as un n´ umero razonable de pasos
hasta obtener la soluci´ on del problema.
3. Cada algoritmo posee cero o m´as datos y proporciona uno o m´ as resul-
tados. Los datos (inputs) pueden ser definidos como cantidades, que
le son dados al algoritmo inicialmente, antes de ser ejecutado, y los
resultados (outputs), tambi´en definidos como cantidades, tienen algu-
1.2 ¿Qu´e es un algoritmo? Medidas de eficiencia 5
na relaci´ on con los datos y son proporcionados cuando se completa la
ejecuci´on del algoritmo.
4. Es preferible que un algoritmo sea aplicable en la resoluci´ on de cual-
quier miembro de una clase de problemas y no ´ unicamente en un
problema aislado. Esta propiedad de generalidad es, aunque no nece-
saria, siempre deseable en un algoritmo.
5. Finalmente, aclarar que quiz´ a la noci´ on de algoritmo es muy general.
A lo largo de la asignatura nos dedicaremos al estudio de algoritmos
dise˜ nados para ser ejecutados en un ordenador. Un algoritmo de este
tipo, debe ser implementado en un lenguaje de programaci´ on y a la
larga constituir un programa o conjunto de programas.
Como hemos mencionado, para un problema num´erico dado se pueden
considerar distintos algoritmos.
Ejemplo 1.1 Determinar la ra´ız real m´ as grande de la ecuaci´on:
x
3
+a
2
x
2
+a
1
x +a
0
= 0
con coeficientes reales a
0
, a
1
, a
2
es un problema num´erico. El vector de datos
es (a
0
, a
1
, a
2
). El resultado es el valor de la ra´ız buscada x, que est´ a impl´ıci-
tamente definida en funci´on de los datos. Existen distintos algoritmos para
la resoluci´ on de este problema, basados en el m´etodo de Newton Rapson, el
de bisecci´ on, el de la secante, etc.
Ejemplo 1.2 El problema de resolver la ecuaci´ on diferencial
d
2
x
d
2
y
= x
2
+y
2
con condiciones de contorno y(0) = 0, y(5) = 1, no es, de acuerdo con la
definici´ on dada arriba un problema num´erico. En este problema los datos
(input) se reducen a una funci´ on y, que no puede ser especificada simple-
mente por un conjunto finito de n´ umeros. El problema es entonces un pro-
blema matem´ atico, que puede ser aproximado por un problema num´erico, en
el sentido de que el resultado ser´ a un conjunto de valores que aproximar´an
a la funci´ on y(x), para x = h, 2h, 3h, ....
Medidas de eficiencia
Es muy f´ acil inventar algoritmos. En la pr´ actica, sin embargo, uno no
quiere s´ olo algoritmos, quiere buenos algoritmos. El objetivo entonces, es
inventar buenos algoritmos y probar que dichos algoritmos son buenos. La
6 1 Nociones b´ asicas sobre algoritmos
“bondad” de un algoritmo puede ser valorada de acuerdo con varios crite-
rios. Uno de los m´ as importantes es el tiempo que tarda en ejecutarse. Hay
tambi´en, distintos aspectos dentro del criterio que controla el tiempo. Uno
de ellos, es el tiempo de ejecuci´on requerido por distintos algoritmos para la
resoluci´on de un mismo problema en un ordenador concreto. Esta medida
emp´ırica, est´a fuertemente relacionada con el programa y con la m´ aquina
utilizada en la implementaci´ on del algoritmo. Un cambio en un programa
puede no representar un cambio significativo en el algoritmo correspondiente
y, sin embargo, afectar a su rapidez de ejecuci´on. Adem´ as, si dos programas
son comparados, primero en una m´aquina y luego en otra, ambas compara-
ciones pueden generar diferentes conclusiones.
As´ı, mientras la comparaci´ on de programas corriendo en ordenadores
es una importante fuente de informaci´ on, los resultados estar´an siempre
afectados tanto por las caracter´ısticas de la m´aquina como por la destreza
y habilidad del programador.
Una alternativa ´ util a esta medida emp´ırica, es la realizaci´ on de un an´ ali-
sis matem´atico de la dificultad intr´ınseca de la resoluci´on computacional del
problema. Juiciosamente utilizado, tal an´alisis proporciona una importante
medida de evaluaci´ on del costo de ejecuci´on de un algoritmo.
El tiempo de proceso de un algoritmo es una funci´ on del tama˜ no del pro-
blema computacional a resolver. Sin embargo, suponiendo que tenemos un
programa de ordenador que eventualmente acaba, la resoluci´ on de un proble-
ma s´olo requiere de suficiente tiempo y suficiente memoria para almacenar
informaci´ on.
De mayor inter´es son los algoritmos que pueden ser aplicados a una
colecci´on de problemas de un cierto tipo. Para estos algoritmos, el tiempo y
el espacio de memoria requeridos por un programa, variar´ a con el ejemplo
particular a ser resuelto. Veamos por ejemplo la siguiente clase de problemas.
1. Encontrar el mayor elemento en una secuencia de n n´ umeros enteros.
2. Resolver un conjunto de ecuaciones lineales Ax = b, donde A es una
matriz real nxn y b es un vector real de longitud n.
3. Ordenar en orden decreciente un vector de n n´ umeros enteros distintos.
4. Evaluar un polinomio de grado n, P
n
(x) = b −
n

k=0
a
k
x
k
en x = x
0
.
En cada uno de estos problemas, el par´ ametro n proporciona una medida
del tama˜ no del problema, en el sentido de que, o bien el tiempo requerido
1.3 Complejidad. Complejidad computacional 7
para resolver cada problema o el espacio de memoria requerido por el algo-
ritmo, o ambos a la vez, crecen con n.
1.3. Complejidad. Complejidad computacional
Para medir el coste de ejecuci´on de un programa, definiremos una funci´ on
de complejidad (o costo) F, donde F(n) es una medida del tiempo requerido
para ejecutar el algoritmo sobre un problema de tama˜ no n, o una medida del
espacio de memoria requerido para tal ejecuci´ on. De esta forma, hablaremos
de la complejidad en cuanto a tiempo o de la complejidad en cuanto a
memoria del algoritmo. Tambi´en podemos referirnos a cualquiera de las dos,
simplemente como funci´on de complejidad (o costo) del algoritmo.
En general, el costo de obtenci´ on de una soluci´ on crece de acuerdo con
el incremento del tama˜ no del problema, n. Si el valor n es muy peque˜ no,
incluso un algoritmo ineficiente tardar´ a poco en ejecutarse, de manera que
la elecci´on de un algoritmo para un problema peque˜ no no es cr´ıtico (a menos
que el problema sea resuelto muchas veces). En la mayor´ıa de los casos, sin
embargo, al incrementar n, se llega a una situaci´on, en la que el algoritmo
no puede ejecutarse en un periodo razonable de tiempo. Este punto se ilus-
tra en la siguiente tabla, donde puede verse c´ omo crecen las funciones de
complejidad de determinados algoritmos, al crecer n.
F(n) n = 10 n = 10
2
n = 10
3
n = 10
4
log
2
n 3.3 6.6 10 13.3
n 10 10
2
10
3
10
4
nlog
2
n 0,33 10 0,7 10
2
10
4
1,3 10
5
n
2
10
2
10
4
10
6
10
8
n
3
10
3
10
6
10
9
10
12
2
n
1024 1,3 10
30
> 10
100
> 10
100
n! 3 10
6
> 10
100
> 10
100
> 10
100
n: tama˜ no del problema
F(n): funci´ on de complejidad temporal
En la pr´actica es importante controlar el comportamiento del algorit-
mo para valores grandes de n, es decir, el comportamiento asint´ otico de la
funci´ on de complejidad.
8 1 Nociones b´ asicas sobre algoritmos
La complejidad asint´ otica es el crecimiento en el l´ımite de la funci´ on
de complejidad cuando crece el tama˜ no del problema n. De esta forma, la
funci´ on asint´ otica (temporal o espacial) determina el tama˜ no m´ aximo del
problema a ser resuelto por el algoritmo.
En estos t´erminos diremos que la complejidad del algoritmo es O(nlogn)
y leeremos “de orden n logaritmo (en base dos) de n”, si el tiempo de proceso
del algoritmo para un ejemplo de tama˜ no n es proporcional a nlogn, o en
cualquier caso no supera esta cantidad. A lo largo de diferentes cap´ıtulos,
hablaremos en los siguientes t´erminos, todos ellos para decir lo mismo:
1. La complejidad temporal del algoritmo es de orden nlogn, que puede
ser expresado como O(nlogn).
2. El algoritmo es ejecutado en un tiempo O(nlogn).
3. La cantidad de trabajo requerida por el algoritmo es proporcional a
nlogn, o es O(nlogn).
1.4. Complejidad agebr´aica y anal´ıtica
Entenderemos por algoritmo num´erico, un algoritmo que resuelve un
problema en el que el contenido num´erico es esencial. Hay muchos ejemplos
de algoritmos num´ericos entre los que se encuentran los siguientes:
1. El c´ alculo de las raices de una ecuaci´ on no lineal, o bien de un sistema
de ecuaciones no lineales.
2. La evaluaci´on de un polinomio en un valor dado, etc.
Los algoritmos no num´ericos, resuelven problemas y la soluci´on de los
mismos, aunque en ocasiones se representa por un n´ umero (o conjunto de
n´ umeros), es de naturaleza no num´erica. Dos importantes ejemplos de pro-
blemas no num´ericos son, la ordenaci´ on de un conjunto de elementos des-
ordenados (si es un conjunto de n´ umeros, en orden creciente o decreciente,
mientras que si es un conjuntos de letras o palabras, en orden alfab´etico) y la
b´ usqueda de un elemento en un conjunto. En los Temas 5 y 6 del programa
analizaremos algoritmos para resolver estos problemas.
En el an´ alisis de la complejidad computacional de los algoritmos num´eri-
cos es conveniente distinguir entre la medida de complejidad algebr´ aica y
1.5 Precisi´ on num´erica 9
anal´ıtica, donde ambas representan ramas de la propia complejidad com-
putacional. En el caso de los algoritmos no num´ericos, la complejidad es
´ unicamente abgebr´ aica.
Los objetivos de la complejidad algebr´ aica son m´ ultiples. Por una parte
intenta estimar el n´ umero de operaciones aritm´eticas requeridas por un algo-
ritmo. Adem´ as intenta estimar el n´ umero m´ınimo de operaciones necesario
para resolver un problema dado. Tambi´en intenta describir un algoritmo o
algoritmos que resuelvan un problema en un n´ umero m´ınimo de operaciones.
La evaluaci´on de un polinomio en uno o varios valores dados y la resoluci´ on
de ecuaciones o sistemas de ecuaciones lineales mediante m´etodos directos
son ejemplos de problemas que pueden ser estudiados en t´erminos de su
complejidad algebr´ aica. Un aspecto importante de los algoritmos que son
utilizados para resolver estos problemas, es el n´ umero finito de pasos que
son necesarios hasta la obtenci´ on de la soluci´on del problema. Esto implica,
evidentemente, que el n´ umero de operaciones aritm´eticas necesarias tambi´en
es finito.
Por otra parte, la complejidad anal´ıtica resuelve la cuesti´on de cu´ anta
computaci´ on ha de ser desarrollada hasta obtener un resultado con un deter-
minado grado de precisi´ on y se centra m´as en los procesos computacionales
que en cierto sentido nunca terminan. Los procesos iterativos son un evi-
dente ejemplo. En este caso, el proceso es interrumpido en un punto y, si
el valor actual del resultado satisface el problema con un margen de error
acotado, dicho resultado se toma como soluci´ on del problema, en otro caso
la computaci´ on contin´ ua hasta obtener un resultado satisfactorio. En este
caso, uno estima el n´ umero de operaciones aritm´eticas por paso en cada ite-
raci´on y un “buen” algoritmo en t´erminos de su complejidad computacional
es definido como aqu´el que requiere el menor n´ umero total de operaciones
aritm´eticas para alcanzar un resultado, con un nivel de precisi´ on prefijado.
1.5. Precisi´ on num´erica
En el an´ alisis de algoritmos que resuelven problemas num´ericos, la pre-
cisi´on de los resultados obtenidos es otro importante criterio a la hora de
distinguir entre un buen y un mal algoritmo. La necesidad de examinar la
precisi´on de los c´alculos matem´aticos viene del hecho de considerar que un
ordenador es una m´ aquina finita, que es capaz de representar los n´ umeros
´ unicamente en un n´ umero finito de posiciones. La mayor´ıa de los n´ umeros,
incluso si son enteros, si son muy grandes para ser representados en el or-
denador, se redondean y s´ olo una aproximaci´ on finita a dicho n´ umero es
almacenada en el ordenador. Esto implica que si no se tiene cuidado, algu-
10 1 Nociones b´ asicas sobre algoritmos
nos algoritmos num´ericos implementados en un ordenador pueden producir
aproximaciones al verdadero valor de la soluci´ on, bastante imprecisas.
Aunque todav´ıa no hemos introducido el concepto de error de redondeo
(lo haremos en el Cap´ıtulo 3), con el siguiente ejemplo vamos a ver c´omo los
errores de redondeo pueden destrozar el resultado de un c´ alculo si se elige
un mal algoritmo.
Ejemplo 1.3 Vamos a analizar el problema de computar para n =
0, ..., 8, la integral:
y
n
=
_
1
0
x
n
x + 5
dx
Vamos a utilizar un procedimiento iterativo basado en una relaci´ on de
recurrencia. En general las relaciones de recurrencia son ubicuas en es-
tad´ıstica computacional y probabilidad. En la mayor´ıa de los casos surgen
de considerar el primero y el ´ ultimo de una cadena de eventos. Este ejemplo
ser´a el primero en ilustrar este principio.
En nuestro caso la relaci´ on de recurrencia viene dada por:
y
n
=
1
n
−5y
n−1
y se sigue directamente de la identidad:
y
n
=
_
1
0
x
n−1
(x + 5 −5)
x + 5
dx =
_
1
0
x
n−1
dx −5
_
1
0
x
n−1
x + 5
dx =
1
n
−5y
n−1
En teor´ıa esta relaci´on de recurrencia permitir´ a crear un proceso iterativo
y computar y
n
a partir del valor inicial y
0
=
_
1
0
dx
x+5
= [ln(x + 5)]
1
0
= ln6 −
ln5 = 0,182.
y
1
= 1 −5y
0
≈ 0,09
y
2
=
1
2
−5y
1
≈ 0,05
y
3
=
1
3
−5y
2
≈ 0,083 extra˜ no que y
3
> y
2
y
4
=
1
4
−5y
3
≈ −0,165 absurdo!!
La raz´on del absurdo resulta ser un error de redondeo en y
0
cuya mag-
nitud puede ser como mucho de 5 10
−4
. Dicho valor es multiplicado por -5
1.5 Precisi´ on num´erica 11
en el c´ alculo de y
1
, que entonces tiene un error de −5. Dicho error produce
un error en y
2
de 25, etc. De esta forma el error en y
4
es 625 que puede
alcanzar el valor 625 5 10
−4
= 0,3125.
Si se comienza en y
0
utilizando m´as decimales, lo que sucede es que se
retrasa la aparici´ on del absurdo.
Este algoritmo es un ejemplo de un fen´ omeno denominado inestabili-
dad num´erica. Podremos evitar dicha inestabilidad num´erica eligiendo un
algoritmo m´ as adecuado.
Otra forma de explicar dicha inestabilidad num´erica es utilizando cono-
cimientos de an´alisis matem´atico. En este caso sabemos que, para n mode-
radamente grande, la mayor´ıa de la masa de la integral se agrupa en torno
a x = 1. Por lo tanto, una buena aproximaci´ on de y
n−1
ser´ıa:
y
n−1

1
1 + 5
_
1
0
x
n−1
dx =
1
6n
Es decir:
y
n

1
n

5
6n
=
1
n
(1 −
5
6
)
Por lo tanto perdemos precisi´on porque en cada iteraci´on restamos dos
n´ umeros del mismo signo y parecida magnitud.
Una soluci´ on al problema en este caso, puede ser utilizar la f´ ormula de
recurrencia en la otra direcci´ on.
y
n−1
=
1
5n

y
n
5
Pero necesitamos un valor inicial. Podemos ver directamente de la de-
finici´on que y
n
decrece cuando n crece. Se puede suponer entonces que y
n
decrece suavemente cuando n es grande. De manera que y
10
≈ y
9
, de donde:
y
9
+ 5y
9

1
10
, y
9

1
60
≈ 0,017
De este modo:
y
8
=
1
45

y
9
5
≈ 0,019
12 1 Nociones b´ asicas sobre algoritmos
y
7
=
1
40

y
8
5
≈ 0,021
y
6
≈ 0,025
y
5
≈ 0,028
y
4
≈ 0,034
y
3
≈ 0,043
y
2
≈ 0,058
y
1
≈ 0,088
y
0
≈ 0,182 Correcto!!
Este mismo algoritmo hacia atr´ as puede elegir como valor inicial y
10
= 0.
La aproximaci´on inicial es distinta pero el error se divide por 5 en cada
iteraci´ on y a partir de y
7
se obtienen los mismos resultados.
Aunque en este caso la soluci´on ha sido elegir una f´ ormula de recursi´ on
hacia atr´as, esto no supone siempre una mejor alternativa.
En adelante utilizaremos el t´ermino m´etodo num´erico para denotar
un procedimiento ´ util en algunos casos para aproximar un problema ma-
tem´atico con un problema num´erico, y en otros para resolver un problema
num´erico. Especificar la f´ormula de recursi´ on que permite resolver una su-
cesi´on de integrales, es proporcionar un m´etodo num´erico. As´ı, un m´etodo
num´erico es m´as general que un algoritmo y hace menos ´enfasis en detalles
computacionales.
1.6. An´alisis de Algoritmos. L´ımites de compleji-
dad
Es conveniente distinguir entre el an´ alisis de un algoritmo en particular
y el an´ alisis de una clase de algoritmos. En el an´ alisis de un algoritmo en
concreto deber´ıamos probablemente investigar sus caracter´ısticas m´as im-
portantes tales como su complejidad temporal, cu´ antas veces es conveniente
ejecutar cada parte del algoritmo y la complejidad espacial, ´esto es, cu´anta
memoria va a ser necesaria. En el an´alisis de una clase de algoritmos, por
otra parte, estudiaremos la clase de algoritmos que resuelven un problema
particular e intentaremos seleccionar el “mejor” algoritmo de la clase capaz
de hacerlo. Si tal algoritmo no se encuentra, entonces intentaremos estable-
cer l´ımites de la complejidad de los algoritmos de dicha clase.
1.6 An´alisis de Algoritmos. L´ımites de complejidad 13
An´ alisis del primer tipo han sido realizados desde los comienzos de la pro-
gramaci´on en ordenador. An´ alisis del segundo tipo, comienzan a realizarse,
salvo raras excepciones, mucho tiempo despu´es. Es f´acil ver que an´ alisis del
segundo tipo son bastante m´as potentes y ´ utiles ya que permiten estudiar
varios algoritmos simult´aneamente.
L´ımites de Complejidad
El an´ alisis de la complejidad est´ a relacionado entre otras cosas con la
obtenci´ on de l´ımites superiores e inferiores de la ejecuci´on de un algoritmo o
una clase de algoritmos que resuelve una clase de problemas. La existencia
de l´ımites de la complejidad de algunos algoritmos nos puede servir como
base para la clasificaci´ on de algunos problemas.
Hay algunos problemas para los que los l´ımites de complejidad de su
resoluci´on pueden ser determinados utilizando teoremas apropiados, sin la
necesidad de construir el algoritmo para el problema. En algunos de estos
casos las demostraciones de los teoremas no son constructivas en el sentido
de que no indican la forma de construir un algoritmo para la resoluci´ on del
problema y ni siquiera nos dicen si es posible su construcci´on. Para otros pro-
blemas ha sido posible acotar su complejidad, pero no se conoce algoritmo
capaz de alcanzar dicha cota. Un ejemplo de este caso es la multiplicaci´ on
de matrices, donde una cota superior m´ınima de O(n
2
) es f´acilmente visua-
lizada y sin embargo el m´as r´apido de todos los m´etodos conocidos tiene
una complejidad de O(n
2,496
). Este intervalo sugiere que la cota O(n
2
) no
es alcanzada en la pr´actica.
Otros problemas son tales que se conoce la cota inferior de su com-
plejidad, se pueden construir algoritmos que satisfacen dichas cotas y sin
embargo dichos algoritmos son num´ericamente inestables. M´etodos r´apidos
de multiplicaci´ on de matrices pertencen a esta categor´ıa.
Finalmente, hay problemas para los cuales los l´ımites inferiores de com-
plejidad son conocidos y los algoritmos que alcanzan dichas cotas pueden
construirse y adem´ as son num´ericamente estables.
14 1 Nociones b´ asicas sobre algoritmos
Cap´ıtulo 2
Nociones sobre arquitectura de
ordenadores
2.1. Introducci´ on
Las aplicaciones de un ordenador caen, en su mayor´ıa, dentro de los
grupos:
1. C´alculo num´erico.
2. Almacenamiento, recuperaci´on procesamiento y an´alisis de datos o in-
formaci´on.
El primer punto ha sido la causa por la que se invent´ o y desarroll´ o el
ordenador. Sin embargo el segundo punto es el que ha experimentado un
mayor desarrollo en los ´ ultimos a˜ nos y ya domina en todos los campos de la
tecnolog´ıa inform´ atica. Estamos sin duda en la era de la gesti´ on inform´ atica
de la informaci´ on. Facturaci´ on y control de grandes compa˜ n´ıas, edici´ on y
composici´on editorial de las p´ aginas en los peri´odicos, b´ usqueda de s´ıntomas
en grandes bases de datos que ayudan al diagn´ ostico de enfermedades, no
son m´as que algunos ejemplos en los que interviene un ordenador.
El ´ unico l´ımite existente proviene del n´ umero de personas expertas en
un determinado campo, que posean la habilidad y conocimientos necesarios
para aplicar la inform´ atica a ese campo.
15
16 2 Nociones sobre arquitectura de ordenadores
2.1.1. Representaci´ on binaria de la informaci´ on
Las operaciones que realiza internamente un ordenador no son, en prin-
cipio, demasiado complicadas. Desde hace siglos se sabe que un conmutador
al tener dos estados (abierto y cerrado) puede representar los n´ umeros 0 y
1. Si disponemos de muchos conmutadores, podremos representar grandes
cantidades de ceros y unos. Por otra parte podremos representar los n´ umeros
en base 2 en lugar de utilizar la base de numeraci´ on decimal como hacemos
normalmente. Los n´ umeros expresados en base dos solamente utilizan los
d´ıgitos 0 y 1, as´ı que podemos representar un determinado n´ umero por me-
dio del estado de una serie de conmutadores. Por ejemplo, el 372 se puede
representar en base decimal
372 = 3(10
2
) + 7(10) + 2(10
0
)
pero tambi´en podemos escribirlo como
372 = 1(2
8
) + 0(2
7
) + 1(2
6
) + 1(2
5
) + 1(2
4
) + 0(2
3
) + 1(2
2
) + 0(2
1
) + 0(2
0
) =
= 1(256) + 0(128) + 1(64) + 1(32) + 1(16) + 0(8) + 1(4) + 0(2) + 0(1)
o bien
(372)
2
= (101110100)
2
es decir necesitaremos nueve posiciones para representar el n´ umero 372.
Adem´as de n´ umeros podemos representar otros tipos de datos en c´ odigo
binario, por ejemplo, cada una de las letras del abecedario. Podemos con-
vertir de forma aproximada, la informaci´ on contenida en una imagen, en
una serie de puntos cuyos valores de posici´ on color e intensidad podremos
codificar y procesar o transmitir a trav´es de l´ıneas telef´ onicas.
2.2. Memoria, dispositivos de entrada/salida, uni-
dad central de proceso o CPU
En sus m´as remotos or´ıgenes cada uno de los conmutadores que consti-
tu´ıan un ordenador era un dispositivo electromec´ anico conocido como rel´e.
El resultado fu´e una m´aquina monstruosa en tama˜ no, ruido y consumo. El
siguiente paso fue sustituir los ruidosos y poco fiables rel´es por calurosas y,
tambi´en, poco fiables v´ alvulas de vacio, que, por lo menos eran m´ as r´apidas.
A continuaci´ on se introdujo el transistor, lo que supuso un gran avance (ya
estamos en los a˜ nos sesenta). Pero lo que ha hecho posible la revoluci´ on
inform´ atica ha sido la aparici´ on de los circuitos integrados.
2.2 Memoria, dispositivos de entrada/salida, unidad central de proceso o CPU17
Existe un circuito b´ asico formado por dos transistores llamado flip-flop
o biestable. Este circuito posee dos estados de funcionamiento estable. Si
asignamos el valor 1 a uno de estos estados y el 0 al otro, de nuevo tendremos
nuestro sistema de representaci´on de n´ umeros binarios. El estado en el que
se encuentra el circuito puede medirse (lectura) o alterarse (escritura).
Gracias a la microelectr´onica se pueden colocar varias decenas de mi-
les de circuitos dentro de un chip. Las t´ecnicas que permiten un grado de
empaquetamiento tan elevado de denominan tecnolog´ıa VLSI (Very Large
Scale of Integration). El tama˜ no resultante no suele superar el cent´ımetro
cuadrado, lo que da idea del grado de miniaturizaci´ on del proceso. Estos
avances tecnol´ogicos han hecho posible que los ordenadores actuales sean
mucho m´as r´apidos, fiables, y baratos que sus antecesores.
La memoria principal de un ordenador consiste en varios millones de cir-
cuitos elementales biestables capaces de almacenar un cero o un uno. Cada
unidad de informaci´ on binaria es un bit. Sin embargo, no suele accederse
de forma individual a un bit, sino que suelen agruparse de ocho en ocho,
formando un byte. La unidad de informaci´ on que se transfiere a o desde la
memoria principal recibe el nombre de palabra. En los ordenadores perso-
nales la longitud de una palabra suele ser de 8, 16 ´ o 32 bits, mientras que
en los grandes puede oscilar entre 12 y 64, o m´ as, bits. Los detalles sobre
la construcci´ on y organizaci´ on de la memoria principar de un odenador no
suelen tener demasiada importancia a la hora de escribir un programa. Los
dos ´ unicos puntos importantes a considerar son los siguientes:
1. Hay dos valores asociados con cada elemento de memoria: Su contenido
que ser´a su dato o instrucci´ on en c´ odigo binario y la direcci´ on de esa
posici´ on de memoria, que nos permite acceder a ella.
2. El tama˜ no de la palabra de cualquier ordenador es finito. Esto signi-
fica que dispondremos solamente de un n´ umero finito de d´ıgitos para
representar las cantidades durante su c´ alculo. As´ı los n´ umeros cuya
representaci´on binaria sea ilimitada, ser´an truncados al guardarse en
una direcci´ on de memoria. Nos estamos refiriendo a los n´ umeros cuya
representaci´on binaria (no decimal) tiene infinitos decimales. Por ejem-
plo 0.1, tiene una representaci´ on peri´ odica en base 2. La longitud de la
memoria ocupada por una dato nos limitar´ a la precisi´ on de los c´alculos
num´ericos y es una caracter´ıstica importante de cada ordenador.
El tiempo de acceso (tiempo necesario para leer informaci´on almacena-
da en una direcci´ on determinada) es un par´ ametro de importancia crucial
en el funcionamiento de un ordenador. Una suma se suele realizar en me-
nos tiempo del necesario en buscar los sumandos en memoria. El tiempo
18 2 Nociones sobre arquitectura de ordenadores
de acceso en una memoria de gran tama˜ no es del orden de 5 10
−7
segun-
dos. Este tiempo aunque es extraordinariamente peque˜ no, puede reducirse
colocando los datos e instrucciones m´as recientes en otra memoria m´as pe-
que˜ na y r´ apida, es la memoria cache. La idea es que los t´erminos utilizados
´ ultimamente tienen grandes probabilidades de ser necesarios de nuevo. La
memoria principal recibe el nombre de memoria de acceso aleatorio (random
access memory, o RAM). Con este t´ermino se indica que el tiempo de acceso
a una determinada posici´ on de memoria es pr´acticamente independiente de
su direcci´ on.
En una RAM la informaci´ on no se guarda de forma secuencial, aunque
puede ser c´ omodo imaginar las direcciones ordenadas consecutivamente.
La memoria principal no es el ´ unico sitio en el que se almacena la in-
formaci´on; existen otros lugares donde a cambio de unos tiempos de acceso
mayores se pueden conservar grandes cantidades de informaci´ on. Estos dis-
positivos reciben el nombre de memoria secundaria o memoria masiva, y
suelen consistir en cintas magn´eticas, discos fijos, diskketes, cd’s, etc. Los
datos de la memoria secundaria no son accesibles directamente, sino que
deber´ an pasar a la memoria principal.
La secci´on del ordenador que realiza las operaciones con los datos, si-
guiendo las instrucciones almacenadas en la memoria principal, es la unidad
central de proceso (central processing unit, o CPU). La CPU realiza dos
tipos de tareas:
1. Control y observaci´ on del sistema completo. Este consta de todos los
dispositivos de entrada y salida de informaci´ on del ordenador junto
con el tr´ afico de informaci´ on interna al ordenador.
2. Ejecuci´on de las instrucciones recibidas desde la memoria principal.
La CPU consta de dos secciones que realizan cada una de estas funciones.
La unidad de control se encarga de la realizaci´ on de las tareas de control,
mientras que la unidad de proceso de datos e instrucciones ser´ a la respon-
sable de ejecutar las ´ordenes elementales que forman el programa. Estas
´ordenes consisten principalmente en operaciones aritm´eticas y de compa-
raci´on entre dos datos. Esta segunda secci´ on recibe el nombre de unidad
aritm´etico-l´ogica (arithmetic-logic unit, o ALU).
La unidad de control es la responsable de la b´ usqueda de la siguiente
instrucci´ on y del c´ alculo de las direcciones de los datos, as´ı como del alma-
cenamiento de ambos en registro de acceso inmediato. Las instrucciones se
traen de la memoria principal en el orden indicado por el programa. Si hay
2.3 Lenguaje de m´ aquina, lenguajes de ensamblado, lenguajes de alto nivel19
que realizar alguna operaci´ on con los datos, ´estos se transfieren a la ALU
junto con la orden de sumarlos, restarlos, compararlos, etc. La unidad de
control, se encarga, una vez realizada la operaci´ on, de colocar el resultado
en la memoria principal.
Recuerda que todos los elementos procesados en la CPU, datos e instruc-
ciones, est´an escritos en c´odigo binario. Esta es la ´ unica forma de comunica-
ci´on que puede entender el ordenador. Los programas escritos de esta forma
reciben el nombre de programas en lenguaje m´aquina o microprogramas.
Dos de los errores m´as comunes son el de realizar una serie de opera-
ciones con datos que no se han introducido en la memoria principal y el
de ejecutar un programa que carece de las instrucciones para la impresi´ on
de los resultados. Pr´ acticamente todos los programas constan de tres fases:
introducci´ on de los datos, procesamiento e impresi´ on de los resultados. Los
dispositivos de entrada m´ as comunes son el teclado y los discos magn´eticos.
Los resultados de un programa pueden presentarse en una pantalla o en una
impresora o bien guardarse en un disco. Los dispositivos m´ as com´ unmen-
te usados, especialmente en los ordenadores personales, son el teclado y la
pantalla.
Todos los componentes f´ısicos tales como la CPU, las memorias principal
y secundaria y los dispositivos de entrada E/S forman parte del llamado
hardware del ordenador. El conjunto de programas forman el software.
Nota 2.1. La met´afora del enano computador Lee atentamente este
art´ıculo que te servir´ a para comprender mejor lo descrito en esta secci´on.
2.3. Lenguaje de m´aquina, lenguajes de ensambla-
do, lenguajes de alto nivel
Como se ha comentado anteriormente, la definibilidad de un algoritmo
exige especificar de modo riguroso y sin ambig¨ uedades las acciones a realizar.
Para ello se han creado los lenguajes de programaci´ on definidos formalmente
para especificar algoritmos. La transcripci´on del algoritmo en sentencias de
un lenguaje de programaci´ on se denomina “codificaci´ on” y constituye el
“programa” inform´ atico. Un lenguaje de programaci´ on est´a formado por un
conjunto de c´ odigos que permiten describir los algoritmos y sus datos, de
manera que, por una parte, se puedan ejecutar por un ordenador; y por otra,
su descripci´ on sea comprensible por el usuario.
El lenguaje de programaci´ on es un medio de comunicaci´ on entre el hom-
20 2 Nociones sobre arquitectura de ordenadores
bre y el ordenador. Para describir al ordenador un m´etodo para resolver
un problema, es necesario formalizarlo adecuadamente para que lo pueda
entender.
Dado que la circuiter´ıa del ordenador s´ olo acepta, e interpreta ceros y
unos, el lenguaje binario es la base del “lenguaje m´ aquina”. Como la palabra
lo dice, el “lenguaje m´ aquina” es particular de cada ordenador en concreto
y aunque su base sea binaria existen entre unos y otros grandes diferencias.
Interesa precisar dos hechos:
1. Un ordenador s´ olo entiende y acepta su propio lenguaje de m´ aquina.
2. La utilizaci´ on de un lenguaje m´ as evolucionado y por lo tanto los pro-
gramas escritos en ese lenguaje, exigir´a la existencia de un “traductor”
(escrito en el propio lenguaje m´ aquina”) para que convierta estos pro-
gramas al lenguaje m´ aquina.
L´ogicamente el lenguaje m´aquina es muy inc´ omodo de utilizar. Por una
parte hay que conocer los c´ odigos de todas las operaciones, y por otra, hay
que disponer de las direcciones que los datos toman en la memoria central.
Todo ello oblig´ o a las empresas inform´aticas a desarrollar una forma de
programaci´ on m´ as sencilla y plantearon distintas formas de automatizarla.
Un primer paso fue utilizar bases num´ericas superiores a dos para la
representaci´on de las instrucciones, en la entrada y la salida del ordenador:
octal, decimal, hexadecimal, etc.
Pero la verdadera evoluci´ on la marcaron dos aspectos:
a) La simbolizaci´on.
b) La utilizaci´ on de lenguajes intermedios.
Al igual que se ha descrito en el art´ıculo del enano computador, en el
ordenador real se numeran las direcciones de memoria. Ello presenta una
gran inconveniente en la programaci´ on, por el hecho de que el programador
debe conocer en todo momento la ubicaci´ on de sus datos en la memoria
central.
Los lenguajes “simb´olicos” obvian este inconveniente al permitir reem-
plazar los c´odigos de operaci´on y las direcciones num´ericas de memoria por
s´ımbolos que representan esos c´odigos y direcciones.
2.3 Lenguaje de m´ aquina, lenguajes de ensamblado, lenguajes de alto nivel21
Los nombres simb´olicos de las direcciones de memoria exigen la observan-
cia de determinadas reglas de formaci´on, por ejemplo: limitaci´on del n´ umero
de caracteres, obligaci´on de que aparezca un car´ acter en particular, etc.
Las instrucciones escritas de manera simb´olica deben ser transcritas en
instrucciones en lenguaje m´ aquina, ya que los s´ ombolos no pueden ser ser
interpretados directamente por el ordenador. Por ello es necesario crear unas
tablas de correspondencia a fin de traducir estos s´ımbolos en c´ odigo de
operaci´ on y direcciones num´ericas. Esta funci´on la desarrolla un progra-
ma particular que se denomina traductor. Por lo tanto, todos los lenguajes
intermedios necesitan la presencia de un traductor, cuya funci´ on es la de
traducir el programa fuente en el programa-m´ aquina, dando como resultado
un programa objeto que es directamente ejecutable por el ordenador.
Entre los lenguajes intermedios m´ as importantes tenemos:
1.- Los lenguajes de ensamblaje o ensambladores.
2.- Los lenguajes autocode.
El lenguaje ensamblador es muy pr´ oximo a la m´aquina, y en ´el cada
orden corresponde directamente a una instrucci´ on en c´ odigo m´aquina, o a
la descripci´on de un dato elemental tal como se presenta en la memoria del
ordenador.
La acci´on del ensamblador consiste en traducir todos los elementos simb´ oli-
cos del programa fuente, tanto los c´ odigos de operaci´ on como las direcciones
de memoria. Con respecto a los c´odigos de operaci´ on, el ensamblador com-
para cada s´ımbolo encontrado en el programa con los existentes en una tabla
preestablecida, cargada en memoria en el momento del ensamblaje. Cuando
se produzca la correspondencia, el c´odigo simb´ olico ser´a sustituido por su
correspondiente num´erico de la tabla. Para las direcciones, el proceso es al-
go diferente. En una primera pasada, el ensamblador crea en memoria una
tabla, asignando una direcci´ on real a cada direcci´on simb´ olica que encuen-
tra. En la segunda pasada, el ensamblador reexamina cada instrucci´ on y
reemplaza los s´ımbolos por las direcciones correspondientes de la tabla. Es
entonces preciso que el ensamblador lea, por lo menos, dos veces el programa
fuente para obtener el programa en lenguaje m´ aquina (programa objeto).
La funci´ on del ensamblador no se reduce a estas operaciones que se
acaban de indicar. Tambi´en se encarga de reservar zonas de memoria, de
cargar alg´ un dato inmediato en la memoria del ordenador, etc.
Por su parte el lenguaje autocode tambi´en es un lenguaje muy pr´ oximo
22 2 Nociones sobre arquitectura de ordenadores
al lenguaje m´ aquina. Se diferencia del ensamblador por la introducci´ on de la
macro-instrucci´on. Se denomina as´ı porque en el momento de la traducci´ on
genera varias instrucciones de c´odigo m´aquina.
La noci´ on de macroinstrucci´ on est´a ligada al concepto de subprograma,
procedimiento o rutina. Supongamos que en un programa se desea leer datos
en diferentes puntos del mismo. Lo l´ ogico ser´ıa disponer de un conjunto
de instrucciones independientes del resto del programa (macro-instrucci´on),
que nos realice esta operaci´on de lectura, y poder llamar a esta macro-
instrucci´ on, cuando lo precisemos.
En el lenguaje autocode, la t´ecnica de ensamblaje es esencialmente la
misma que en el ensamblador. Al ensamblador del autocode se le denomina
macroensamblador.
El autocode presenta adem´ as, con respecto al ensamblador, la ventaja
de poder utilizar subprogramas ya existentes en el sistema, definidos por el
programador o por otro usuario del equipo.
Los lenguajes intermedios se suelen utilizar, bien porque las operaciones
que se desean programar no son posibles con los lenguajes m´as evoluciona-
dos, o bien porque los programas a construir son de ejecuci´ on permanente
y precisan una gran optimizaci´ on tanto de memoria como de tiempo de eje-
cuci´on. Es es el caso de algunos sistemas operativos y procesadores b´asicos.
La llegada de los lenguajes de alto nivel marca una etapa importante en la
evoluci´ on de los lenguajes intermedios. Mientras que ´estos est´an orientados
a la m´aquina, los de alto nivel van orientados al problema. Esto ´ ultimo
explica que la estructura de los lenguajes evolucionados sea completamente
diferente al de los de ensamblaje, estando los primeros mucho m´as pr´ oximos
al lenguaje natural. Los lenguajes evolucionados tienden a una universalidad
de empleo, permitiendo ser procesados, casi, con independencia del tipo de
ordenador.
Tomando como criterio el tipo de aplicaci´ on, se distinguen dos categor´ıas:
1. Lenguajes de prop´ osito general: que permiten resolver, en principio
cualquier tipo de problema. Se dividen en dos grupos dependiendo de
fin para el que han sido desarrollados.
a) Lenguajes definidos para aplicaciones t´ecnico-cient´ıficas: FOR-
TRAN, ALGOL y BASIC que fueron los primeros en ser des-
arrollados, entre 1954 y 1958, y el PASCAL y el lenguaje C que
se desarrollaron posterioremente, en 1970 y 1972 respectivamente,
son los m´as representativos en esta categor´ıa.
2.3 Lenguaje de m´ aquina, lenguajes de ensamblado, lenguajes de alto nivel23
b) Lenguajes de gesti´ on, como el COBOL, RPG, etc. El COBOL
ha sido b´ asicamente estudiado y pensado para programar aplica-
ciones de gesti´on. Hoy en d´ıa en la explotaci´ on y tratamiento de
grandes bases de datos, se emplea ORACLE, ACCES, etc
2. Lenguajes especializados: que han sido dise˜ nados para un tipo especial
de aplicaci´ on (por ejemplo, manejo de m´ aquina herramienta, dise˜ no,
ense˜ nanza asistida, etc). Son a t´ıtulo de ejemplo el CAD, GPSS, SI-
MULA, LISP, entre otros. Dentro de una especializaci´ on m´ as cercana,
S-plus o R.
En los lenguajes de alto nivel se sustituye el traductor (ensamblador o
macroensamblador) por el compilador, y la traducci´ on se denomina compi-
laci´ on.
Un compilador, es pues, un traductor de programas escritos en un len-
guaje de alto nivel. El compilador es un programa complejo (normalmente
escrito en ensamblador) que tiene como misi´on traducir expresiones y sen-
tencias, escritas en un lenguaje de alto nivel, en instrucciones que comprenda
e interprete el ordenador. M´ as adelante, veremos con m´as detalle la forma
de compilar un programa escrito en C.
24 2 Nociones sobre arquitectura de ordenadores
Cap´ıtulo 3
Introducci´on al lenguaje de programaci´ on
C (I)
C es un lenguaje de programaci´ on de uso general. Ha sido y es utilizado
para escribir programas num´ericos, programas de procesamiento de textos y
bases de datos. Dentro de los lenguajes de alto nivel, es clasificado como un
lenguaje de relativo bajo nivel, esto es, trabaja con la misma clase de objetos
que la mayor´ıa de los ordenadores: caracteres, n´ umeros y direcciones, que
pueden ser combinados con los operadores aritm´eticos y l´ogicos, utilizados
normalmente en las m´aquinas. No contiene elementos de alto nivel, que
deber´ an siempre ser aportados por funciones llamadas expl´ıcitamente.
De igual forma, las sentencias de control de flujo en C son sencillas,
secuenciales, de selecci´on, de iteraci´on, bloques y subprogramas, pero no
multiprogramaci´ on, paralelismo sincronizaci´ on o corrutinas.
La ausencia de estas caracter´ısticas es lo que da ventajas al lenguaje. C
es relativamente peque˜ no, puede escribirse en poco espacio y aprenderse con
facilidad. Por el contrario es tremendamente vers´ atil y port´ atil, es decir se
puede hacer casi cualquier cosa y ejecutar sin cambios en una amplia va-
riedad de m´ aquinas. Los compiladores se escriben f´acilmente. Exceptuando
los programas que necesariamente dependen en alguna forma de la m´ aqui-
na, como el compilador, ensamblador y depurador, el software escrito en
C es id´entico en casi cualquier m´ aquina que soporte a C. El lenguaje es
independiente de cualquier arquitectura de la m´ aquina en particular.
El sistema operativo UNIX est´ a escrito en su mayor parte en lenguaje
C. Los programas en C tienden a ser lo suficientemente eficientes como para
que no haya que escribirlos en lenguaje ensamblador. De hecho, casi todas
las aplicaciones de UNIX est´an escritas en C.
25
26 3 Introducci´ on al lenguaje de programaci´ on C (I)
En nuestro caso, hemos optado por describir la situaci´ on del sistema
UNIX, de la HP9000 de nuestro Centro de C´ alculo, pues dicho entorno de
trabajo es el habitual de la mayor´ıa de programadores de C.
Muchas de las ideas principales de C provienen de un lenguaje mucho m´ as
antiguo pero a´ un vigente: el lenguaje BCPL inventado por Martin Richars.
La influencia de BCPL le llega indirectamente a trav´es del lenguaje B, escrito
por Ken Thompson en 1970 para el primer sistema UNIX.
En C los objetos fundamentales son caracteres, enteros de varios ta-
ma˜ nos y n´ umeros en punto flotante. Existe tambien una jerarqu´ıa de tipos
de datos derivados, creados con apuntadores, arreglos estructuras, uniones
y funciones.
C posee las construcciones fundamentales de control de flujo para es-
cribir programas bien estructurados: agrupamiento de sentencias, toma de
decisiones (if), ciclos (bucles), con comprobaci´on de la condici´ on de termi-
naci´ on al principio (while, for) o al final (do) y selecci´ on entre un conjunto
de casos posibles (switch). C incluye apuntadores o punteros y capacidad
aritm´etica de direcciones.
Mostraremos ´estos y otros elementos esenciales del lenguaje C en ´este y
los siguientes cap´ıtulos.
3.1. Nociones sobre trabajo en el Sistema Opera-
tivo UNIX. El editor vi
UNIX es un sistema operativo con las siguientes caracter´ısticas:
a. Interactivo: El sistema acepta peticiones, las procesa en forma
de di´ alogo con el usuario.
b. Multiusuario: El sistema permite atender a varios usuarios
de forma simult´ anea. Para ello, utiliza la t´ecnica denominada de
tiempo compartido.
c. Multiprogramado: El sistema es capaz de realizar m´as de una
tarea para cada uno de los diversos usuarios.
B´asicamente consta de los siguientes elementos:
1. KERNEL: Constituye el n´ ucleo central del S.O., con entre otras fun-
ciones:
3.1 Nociones sobre trabajo en el Sistema Operativo UNIX. El editor vi27
a) Control de los recursos del ordenador, asign´ andolos entre los dis-
tintos usuarios.
b) Planificaci´ on de la ejecuci´on de los distintos programas realizados
por los usuarios.
c) Control de los dispositivos de hardware (discos, impresoras,..).
d) Mantenimiento del sistema de archivos.
2. SHELL: Es la capa externa al n´ ucleo. Su misi´ on es la de proporcionar
un interface entre el usuario y el S.O. Se puede decir que el SHELL es:
a) Un int´erprete de comandos.
b) Un lenguaje de programaci´ on.
c) Un controlador de procesos.
3. HERRAMIENTAS Y UTILIDADES: Son un conjunto de programas
que se entregan con el S.O. y que realizan tareas m´as complicadas y
de diversa ´ındole. Por ejemplo:
a) Manipulaci´ on de ficheros.
b) Manipulaci´ on del contenido de los ficheros.
c) Edici´ on de textos.
d) Compilaciones de programas.
e) Gesti´on de ventanas.
4. COMANDOS: Son un conjunto de expresiones prefijadas que permi-
ten dar ´ ordenes al S.O.. UNIX es un sitema operativo orientado a
comandos: hay que escribir las ´ ordenes. Windows es por el contrario
un sistema visual: hay que seleccionar iconos.
a) Claves de acceso a tu cuenta personal. Normalmente hay un su-
pervisor del sistema que tiene acceso a todas las cuentas. Los
usuarios tienen ´ unicamente acceso a su cuenta personal.
login: etpgamaa
password: ara2411
b) Estructura del ´ arbol de directorios:
Como todo sistema operativo, maneja ficheros los cuales son almace-
nados en distintas divisiones del disco duro, denominados directorios.
Tanto los directorios como los ficheros poseen un nombre.
Ficheros: guardan datos, programas, etc..
Nombre de ficheros:
28 3 Introducci´ on al lenguaje de programaci´ on C (I)
- Se pueden usar como m´aximo 14 caracteres (en versiones modernas,
incluso m´ as).
- Hay caracteres prohibidos (/, ?, ’, ”)
- Para trabajar con distintos ficheros:
Ejemplo 1: p* denota todos ficheros cuyo nombre comienza por p
Ejemplo 2: p?pe denota todos los ficheros cuyo nombre comienza por
p, luego cualquier cosa (incluso nada) y luego pe.
Usualmente los nombres de ficheros tienen lo que se llama extensi´ on.
Por ejemplo, los ficheros fuente en FORTRAN tienen extensi´ on .f, en
C tienen extensi´on .c, etc..
Directorios: guardan ficheros u otros directorios. No guardan infor-
maci´on como tal, sino que sirven para estructurar dicha informaci´ on.
(0) Ra´ız: se denota por /.
(1) bin: guarda los comandos del sistema.
(2) etc: guarda los sistemas de arranque.
(3) lost+found: se va guardando lo que el sistema va perdiendo.
(4) mnt: montaje del ´ arbol. Depende del mismo ´ arbol y es quien orga-
niza el ´arbol (utilidades del montaje,...).
(5) users: todos los usuarios del sistema est´an aqu´ı.
- Cada cuenta tiene el nombre de un usuario que cuelga de aqu´ı.
- Cada usuario puede hacer lo que quiera por debajo, pero con un
l´ımite de espacio (p.e. en mi caso 10 megas). Todo lo dem´as es accesible,
dependiendo de los correspondientes permisos. Es posible leer o incluso
copiar, ficheros existentes en directorios ajenos a la cuenta del usuario.
- En mi caso etpgamaa es el directorio del usuario (directorio home),
que coincide con el nombre de la cuenta a la que accedo.
3.1 Nociones sobre trabajo en el Sistema Operativo UNIX. El editor vi29
/
bin etc ls.+fd. users
pract
p9mnu
p9mnuaa p9mnuga · · · p9mnups
Estructura de comandos:
...>orden -[opciones] [par´ ametros]
([] puede no estar. Las opciones siempre empiezan con un gui´ on). UNIX
diferencia min´ usculas y may´ usculas. La mayor´ıa de las ´ ordenes son en
min´ usculas. Para separar las distintas componentes (orden, opciones,
par´ ametros , se usan espacios en blanco (s´olo aqu´ı, y en ning´ un si-
tio m´as se usa y ´esto es la clave de numerosos errores que se suelen
cometer).
Comandos para manejar el ´ arbol de directorios
pwd, mkdir, rmdir, cd
a) ...>pwd
D´ onde estoy, dame el directorio activo.
b) ...>mkdir nombre-de-directorio
Crea un directorio que se llame nombre-de-directorio.
Ejemplo 3:...>mkdir docencia metnu
Crea ambos directorios.
c) ...>rmdir nombre-de-directorio
Borra el directorio que se llame nombre-de-directorio. Para ello
se requieren dos condiciones: tiene que estar vacio y no tiene que
estar activo (no estar en ´el ni por debajo de ´el).
Ejemplo 4:...>rmdir docencia metnu
Borra ambos directorios.
d) ...>cd nombre-de-directorio
Cambia al directorio nombre-de-directorio.
30 3 Introducci´ on al lenguaje de programaci´ on C (I)
Ejemplo 5:...>cd .. (Sube uno hacia arriba)
Ejemplo 6:...>cd (Sube hasta el ra´ız)
Ejemplo 7:...>cd /users/etpgamaa/docencia/metnu
5. Comandos para manejar ficheros:
cat,ls,rm, cp, mv, chmod, lp,vi
a) >cat nombre-de-fichero visualiza el fichero nombre-de-fichero, pe-
ro no lo modifica (similar al comando TYPE de MS-DOS). La
b´ usqueda la realiza en el directorio activo. Si el fichero no est´ a en
dicho directorio, me dir´ a que no lo encuentra. Si en mi directorio
tengo creados dos subdirectorios: Programas y Datos, me encuen-
tro en este ´ ultimo y quiero que me ense˜ ne un fichero que est´ a en
Programas, hay que dar el camino:
...>cat /users/etpgamaa/programas/nombre-de-fichero
Esto es dar el path absoluto. Otro modo de hacerlo es dar el path
relativo.
...>cat ../Programas/nombre-de-fichero
La orden .., sube un paso hacia arriba.
Adem´as dicho comando permite concatenar:
...>cat pepe ana > alumnos existen tres ficheros pepe, ana y alum-
nos que contiene la uni´ on de pepe y ana.
...>cat pepe ana > alumnos mantiene el fichero alumnos (existen-
te) y le a˜ nade el contenido de pepe y ana.
...>cat p* > pruebas une todos los ficheros que empiezan por p
creando uno de nombre pruebas.
b) >ls [-opciones] [directorios] lista el contenido del directorio en el
que estoy.
• Sin opciones ni par´ ametros: informaci´on completa del
directorio actual.
• ...>ls -l /users/etpgamaa informaci´ on completa de di-
cho directorio.
• ...>ls -R -l /users/etpgamaa informaci´ on completa y
recursiva de todos los dicrectorios. El resultado de ´esto:
- - - - - - - - - - n.-enlaces propiet. grupo tama˜ no fecha n.-fichero
(1) (2) (3) (4) (5) (6) (7) (8) (9) (10)
(1)Tipo de fichero:
- fichero ordinario (Programa, texto, datos).
d directorio.
3.1 Nociones sobre trabajo en el Sistema Operativo UNIX. El editor vi31
c,s,w,r controladores de dispositivos de e/s (impresora, modem,
cd-rom,...)
Permisos: aparece en tres bloques de tres.
(2) Propietario
(3) Otros usuarios del mismo grupo que el propietario
(4) Todos los dem´as usuarios del sistema
En cada grupo de tres:
r Lectura: se puede ver pero no modificar.
w Escritura: se puede modificar.
x Ejecuci´ on. (La tienen los ejecutables).
(5)N.-enlaces: n´ umero de lugares (directorios) desde los que es
accesible el fichero (frente a lo que sucede con el MS-DOS, un
fichero en UNIX es accesible desde distintos lugares).
(6)Propietario
(7)Grupo: grupo del propietario
(8)Tama˜ no: memoria que ocupa el fichero.
(9)Fecha: Fecha y hora de la ´ ultima modificaci´ on del fichero.
(10)N.-fichero: nombre del fichero.
c) >rm [-opciones] ficheros o directorios borra los ficheros indicados.
Puede tener como opciones:
• -r: borrado recursivo dentro de un directorio. • -i: pe-
tici´on de confirmaci´on.
Ejemplo 8: ...>rm -r * borra todo lo que hay en el direc-
torio. Es conveniente pedir confirmaci´ on.
Ejemplo 9: ...>rm pepe ana borra los ficheros pepe y ana.
Ejemplo 10: ...>rm p* borra todos los ficheros cuyo nom-
bre empieza por p.
d) >cp fichero-origen fichero-destino copia el fichero-origen en el
fichero-destino, machacando cualquier contenido que tuviera este
´ ultimo y manteniendo intacto el fichero-origen. Si hay cambio de
directorio hay que indicar el camino.
Ejemplo 11: ...>cp pepe /users/etpgamma/alumnos/pepe.1
e) >mv fichero-origen fichero-destino Frente al comando cp, este
comando no mantiene el fichero-origen, su contenido est´ a ´ unica-
mente en el fichero-destino. (Es similar al comando RENAME de
MSDOS).
f ) >chmod [modo] fichero/directorio cambia los permisos de acceso.
modo: (1) usuario, (2) operador, (3) permiso
(1) u=user (2) + a˜ nadir (3) r lectura
g=group - quitar w escritura
o=other = asignar x ejecuci´on
a=all
32 3 Introducci´ on al lenguaje de programaci´ on C (I)
Ejemplo 12: ...>chmod g+w pepe ana a˜ nade a la gente del grupo
el permiso de escritura sobre los ficheros pepe y ana.
Ejemplo 13: ...>chmod g+wx o-rwx pepe
Ejemplo 14: ...>chmod g=rx pepe
g) >lp pepe imprime el fichero pepe.
h) >vi pepe crea el fichero pepe. Es el comando de edici´ on de textos
en UNIX. Puede estar en modo COMANDO, para posicionarse,
moverse, abrir l´ıneas nuevas, eliminar l´ıneas, etc. pulsando la tecla
ESCAPE. Tambi´en puede estar en modo EDICION, para escribir.
Se entra en modo EDICION tecleando i ´o a.
Para salir: ESCAPE :wq sale guardando; ESCAPE :q! abandona.
Otros comandos:
date, who, passwd
a) >date dame la fecha que tiene registrada la m´ aquina.
b) >who inf´ ormame de los actuales usuarios del sistema.
• Sin opciones ni par´ ametros: usuarios conectados.
• ...>who -a todos los usuarios.
• ...>who am i s´olo de mi mismo.
c) >passwd Sentencia para cambiar el password (palabra de paso).
Aparece:
Old password:
New password:
Re-enter new password:
Para desconectarse en la terminal: CTRL+D o teclear EXIT
3.2. Compilado, ejecuci´ on y depuraci´on de progra-
mas en C
En entornos UNIX, los programas se editan utilizando los editores del
sistema. En nuestra m´ aquina el de uso habitual es vi.
Todo el texto o c´odigo de un programa se almacena en un fichero de ex-
tensi´ on .c, denominado fichero fuente. Para compilar el programa, se invoca
al compilador desde el prompt de sistema operativo, utilizando el comando
cc, seguido del nombre completo del fichero fuente. Por ejemplo:
$ cc programa1.c
3.3 Variables escalares, subindicadas (arrays) y constantes 33
Si el fichero fuente contiene errores, aparecen por pantalla todos ellos.
Hay que volver al programa fuente y subsanar dichos errores. Se vuelve a
compilar el programa. Si dicho programa no tiene errores, el compilador crea
el fichero objeto, que tiene el mismo nombre que el fichero fuente, pero con
extensi´on .o. Si el fichero fuente era ´ unico, la sentencia cc produce tambi´en el
linkaje, y crea adem´as del fichero objeto el fichero ejecutable, que por defecto
se llama a.out. Si s´olo queremos que cree el objeto, hay que especificarlo:
$ cc -c programa1.c
Si queremos que el ejecutable tenga otro nombre (por ejemplo programa),
hay que especificarlo tambi´en:
$ cc -o programa programa1.c
Si existen distintos ficheros fuente, programa1.c, programa2.c, progra-
ma3.c, se pueden compilar todos ellos, crear los correspondientes programas
objeto y linkarlos de manera que tengamos un ´ unico ejecutable de nombre
programa.
$ cc -o programa programa1.c programa2.c programa3.c
Para ejecutar el programa, basta escribir su nombre
$ programa
En C existe adem´ as lo que se denomina en ingl´es debugger. Un debuger
es un depurador de c´ odigo. No genera c´ odigo, sino que realiza una com-
probaci´ on muy estricta de tantos aspectos de un programa como puedan
ser comprobados durante la compilaci´ on. Detecta inconsistencias en los ti-
pos de datos, uso incongruente de argumentos, variables no utilizadas o no
inicializadas, problemas potenciales de portabilidad, etc.
La forma de depurar un programa en nuestro entorno es mediante la
sentencia lint.
$ lint programa1.c
3.3. Variables escalares, subindicadas (arrays) y
constantes
La sentencia
34 3 Introducci´ on al lenguaje de programaci´ on C (I)
j = 5 + 10;
significa lo siguiente: suma los valores 5 y 10 y asigna el resultado a la varia-
ble denominada j. En la lectura de la sentencia anterior, estamos suponiendo
conocidos todos los s´ımbolos involucrados. Sabemos que 5 y 10 son valores
enteros, que + e = son operadores y que j es una variable, lo cual significa
que su valor puede cambiar. Para el ordenador, sin embargo, todos estos
signos son una mera combinaci´ on de ceros y unos. Para que dicha expresi´ on
tenga sentido para un ordenador, hay que decirle previamente el significado
de cada uno de los s´ımbolos mencionados. Esta es una de las funciones del
compilador.
Las variables y las constantes son los objetos b´asicos que se manipulan en
un programa. Como sus propios nombres indican una constante es un valor
que nunca cambia, mientras que una variable puede representar distintos
valores. Una variable consigue su variabilidad representando una posici´ on,
una direcci´ on de memoria.
La variable j est´a localizada en alguna posici´ on, por ejemplo en la 2486.
De manera que la sentencia anterior, para el ordenador significa: suma los
valores 5 y 10 y coloca su resultado en la posici´ on de memoria 2486.
La sentencia,
j = j −2;
es entendida entonces como: coge el contenido de la posici´ on 2486 de la
memoria, r´estale la constante 2 y almacena el resultado de nuevo en dicha
posici´ on de memoria.
Variable Direcci´ on
2482
j 2486
2490
Memoria



4 bytes
Las declaraciones iniciales en un programa indican las variables que se
van a utilizar, establecen su tipo y tal vez las inicializan, es decir las dan un
valor inicial.
3.3.1. Nombres de variables
En el lenguaje C, se puede poner nombre casi a cualquier cosa: variables,
constantes, funciones, e incluso localizaciones en un programa. Los nombres
3.3 Variables escalares, subindicadas (arrays) y constantes 35
pueden contener letras, n´ umeros, o el car´acter barra baja , pero deben
comenzar por una letra o por dicho caracter. Hay algunas otras restricciones
en los nombres de las variables y constantes simb´olicas.
El lenguaje C diferencia entre may´ usculas y min´ usculas, de manera que:
var, Var, VAR
son nombres diferentes.
No se pueden elegir como nombres las palabras reservadas. (Hay una
lista de ellas en casi cualquier manual de C). En general no existe l´ımite
en cuanto al n´ umero de caracteres que puede tener un nombre en C. Al-
gunos compiladores antiguos lo fijan en 8 caracteres, otros en 31. Es buena
costumbre por la portabilidad del programa, intentar que los ocho primeros
caracteres de cada nombre sean ´ unicos.
3.3.2. Tipo y tama˜ no de datos
La primera clasificaci´ on que se puede hacer en cuanto al tipo de datos en
C es, como simples y compuestos. Dentro de los datos simples, tenemos:
1. Num´ericos, que a su vez pueden ser:
a) enteros: int o long (dependiendo de su tama˜ no)
b) reales (en punto flotante): float o double.
2. Car´ acter, que a su vez pueden ser:
a) simples: char
b) cadenas: char*
3. Punteros: representan direcciones de memoria.
Dentro de los datos compuestos, dependiendo de su composici´on:
1. Arrays: secuencia (vector) de datos simples del mismo tipo.
2. Estructuras: composici´ on de datos de distinto tipo.
Variables Num´ericas
Todas las variables hay que declararlas antes de usarlas en el programa,
es decir, dar su nombre, tipo y contenido. Por ejemplo:
36 3 Introducci´ on al lenguaje de programaci´ on C (I)
int x;
int lower, upper, step;
float factorial;
El contenido de la variables no es necesario darlo al principio del progra-
ma, pero s´ı antes del uso de dicha variable.
int x=0;
float factorial=1,0E-5 (que es lo mismo que 0.00001);
Variables Car´ acter
Las declaraciones son:
char respuesta;
char* nombre;
si adem´as las inicializamos:
char respuesta=‘s’; (la s es el valor simple que representa inicialmente la
variable respuesta)
char* nombre =”Jaime ”; (es una cadena de caracteres)
Cuando se inicializa as´ı, el ordenador entiende:
(‘J’‘a’‘i’‘m’‘e’‘¸0’)
Nota: ‘s’ representa el caracter simple s; mientras que ”s”es entendido
como una cadena de caracteres dada por (‘s’ ‘¸0’).
Variables Subindicadas (arrays)
EJEMPLO 1
int tabla [10];
define un vector de elementos enteros con 10 posiciones, (tabla [j], j=0,9).
tabla
2 5 200 520 3 0 1 850 1230 58
0 1 2 3 4 5 6 7 8 9
3.3 Variables escalares, subindicadas (arrays) y constantes 37
Se inicializar´ıa:
int tabla=¦2, 5, 200, 520, 3, 0, 1, 850, 1230, 58¦;
tabla[3]=520;
x=tabla[9]; /* el valor de la variable x es 58*/
EJEMPLO 2
char nombre [20];
define un vector de elementos de tipo car´ acter con 20 posiciones, (nombre[j],
j=0,19).
nombre

0 1 19
La inicializaci´ on es:
char nombre[20]=”Jaime”;
o bien;
char nombre[20]=¦‘J’ ,‘a’,‘i’,‘m’,‘e’,‘¸0’¦;
EJEMPLO 3
int tabla2 [10][20]; /* es una matriz 10x20 */
define una matriz de 10 filas y 20 columnas, de elementos enteros.
La inicializaci´ on para una casilla, ser´ıa:
tabla2 [1][3]=800;
tabla2
0
1 800


9
0 3 19
Variables Estructura
EJEMPLO 4
38 3 Introducci´ on al lenguaje de programaci´ on C (I)
struct persona
{ char nombre [20];
struct fecha
{ int dia, mes, a~ no; } fnacimiento;
int sueldo;
} empleado;
Todo esto define una estructura para cada persona. La variable es em-
pleado y est´a compuesta por tres tipos de datos: el nombre (de tipo car´acter),
la fecha de nacimiento (a su vez una estructura compuesta por tres datos
enteros), y el sueldo (una variable escalar de tipo entero).
Para inicializar esto:
empleado.nombre=”Pepito”;
empleado.fnacimiento.mes=10;
empleado.fnacimiento.a˜ no=72;
empleado.sueldo=150000;
O bien:
empleado=¦ ”Pepito”, ¦ 3,10,72¦,150000¦;
O agrupando los datos para la estructura fnacimiento:
empleado.fnacimiento=¦3, 10, 72¦;
3.4. Un ejemplo. El primer programa en C
El siguiente programa imprime la tabla de conversi´ on de temperatu-
ras Fahrenheit a grados cent´ıgrados o Celsius, utilizando la f´ ormula: C =
(5/9)(F −32).
0 -17.8
20 -6.7
40 4.4
3.4 Un ejemplo. El primer programa en C 39
60 15.6
... ...
260 126.7
280 137.8
300 148.9
El programa es
/* imprime la tabla Fahrenheit-Celsius
para f=0,20,...,300 */
main()
{
int lower, upper, step;
float fahr, celsius;
lower=0; /* l´ ımite inferior de la tabla de temperaturas*/
upper=300; /* l´ ımite superior */
step=20; /*tama~ no del incremento */
fahr=lower;
while (fahr<=upper){
celsius=(5.0/9.0) * (fahr-32.0);
printf("%4.0f %6.1f\n", fahr, celsius);
fahr=fahr+step;
}
}
Las dos primeras l´ıneas de programa son un comentario que, en este caso
explica lo que hace el programa. El compilador prescinde de los caracteres
comprendidos entre /∗ y ∗/.
A continuaci´ on comienza el cuerpo de programa, donde aparece la fun-
ci´on main. Todos los programas en C deben tener una funci´ on main en
alg´ un sitio. Normalmente main har´ a uso de otras funciones para efectuar su
trabajo. El cuerpo del programa est´ a dentro de unas llaves, que encierran el
bloque de todas las sentencias de que consta ´este.
A continuaci´ on aparece la lista de definiciones de las variables a utilizar
(su tipo) y su inicializaci´ on (valor inicial). Como hemos visto anteriormente
el tipo int implica que las variables de la lista son enteros; float indica punto
flotante, es decir n´ umeros que poseen parte fraccionaria. La precisi´ on, tanto
de int como de float depende de la m´ aquina que se est´e utilizando. En el
Cap´ıtulo 4 se especifican m´as detalles al respecto.
Las sentencias o proposiciones individuales se terminan con punto y co-
ma. Cada l´ınea de la tabla se calcula de la misma forma, para lo cual se utiliza
40 3 Introducci´ on al lenguaje de programaci´ on C (I)
un ciclo (bucle) que se repite una vez por cada l´ınea. Este es el prop´ osito
de la sentencia while. Se examina la condici´ on encerrada entre par´entesis.
Si es cierta (fahr es menor o igual que upper), se ejecuta el cuerpo del ciclo
(todas las sentencias encerradas entre llaves). A continuaci´on se vuelve a
evaluar la condici´ on y si es cierta se ejecuta de nuevo el cuerpo del ciclo.
Cuando la condici´ on es falsa, finaliza el ciclo y el control del programa pasa
a la sentencia que lo sigue.
M´ as adelante hablaremos de las reglas que rigen la conversi´on entre en-
teros y punto flotante. Por ahora basta observar, que tanto la asignaci´ on
fahr = lower; como la comprobaci´on while(fahr <= upper) trabajan co-
mo se esperaba: la variable de tipo int es convertida a float antes de realizar
la operaci´ on.
En el ejemplo tambien se muestra c´omo funciona la funci´ on printf.
printf es una funci´ on con formatos de conversi´ on de uso general que de-
tallaremos m´ as adelante. Su primer argumento es una cadena de caracteres
que imprimir, indicando cada signo % el lugar en que ha de sustituirse cada
uno de los restantes (segundo, tercero,...) argumentos y en qu´e forma ha
de ser impreso. Por ejemplo, en la sentencia escrita en el programa la es-
pecificaci´ on de conversi´ on %4,0f indica que se ha de imprimir un n´ umero
en punto flotante que ocupe un espacio de a lo sumo cuatro caracteres, sin
d´ıgitos despu´es del punto decimal. %6,1f pide otro n´ umero que ocupar´ a a
lo sumo 6 posiciones, con un d´ıgito despu´es del punto decimal. Despu´es de
la especificaci´ on de formato y separadas por comas, aparecen las variables
cuyos valores se van a imprimir.
Para terminar, hay que se˜ nalar que printf no forma parte del lenguaje.
En C no se define ni la entrada ni la salida. Es ´ unicamente una funci´ on
´ util de la biblioteca est´ andar de funciones de entrada/salida habitualmente
accesible desde los programas en C.
Evidentemente, hay muchas formas de escribir un programa en C. El
siguiente es una modificaci´ on del anterior:
/* imprime la tabla Fahrenheit-Celsius */
main()
{
int fahr;
for (fahr=0; fahr<= 300; fahr=fahr+20)
printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
}
El programa anterior produce el mismo resultado, pero su aspecto es
diferente. Se han eliminado la mayor´ıa de las variables. La variable fahr
3.4 Un ejemplo. El primer programa en C 41
permanece como int, hasta que en la propia funci´ on printf se realiza la
conversi´on %4d.
Se ha utilizado una nueva expresi´ on iterativa, la proposici´ on for. Consta
de tres partes separadas por punto y coma. La primera se ejecuta una sola
vez, antes de entrar en el ciclo propiamente dicho. La segunda es el criterio
o condici´ on que controla la ejecuci´ on. Esta condici´ on se eval´ ua; si es cierta,
se ejecuta el cuerpo del ciclo, y a continuaci´on se ejecuta la tercera parte
que es la parte de reinicializaci´ on y se vuelve a evaluar la condici´ on. El ciclo
termina cuando la condici´ on se torna falsa. Como en while el cuerpo del
ciclo puede ser una sola sentencia o un grupo de ellas colocadas entre llaves.
La decisi´on de utilizar un while o un for es arbitraria.
Una ´ ultima observaci´ on es la siguiente. Es una mala costumbre sepultar
en el interior del programa n´ umeros como 0, 300 ´o 20 en el caso del programa
anterior. Siendo dif´ıcil cambiarlos sistem´aticamente si la ocasi´on lo merece.
Para ello se utilizan en C las sentencias #define y las constantes simb´oli-
cas. Mediante la construcci´ on #define, al comienzo del programa se puede
definir un nombre simb´ olico como una determinada cadena de caracteres y
darlas un valor.
#include <stlib.h>
#include <stdio.h>
#define LOWER 0
#define UPPER 300
#define STEP 20
/* imprime la tabla Fahrenheit-Celsius */
main()
{
int fahr;
for (fahr=LOWER; fahr<= UPPER; fahr=fahr+STEP)
printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
exit(0);
}
Las tres cantidades definidas son constantes y por lo tanto no pueden
aparecer en las declaraciones. Obs´ervese adem´as que no hay punto y coma
despu´es de las definiciones.
42 3 Introducci´ on al lenguaje de programaci´ on C (I)
3.5. Operadores y funciones standard m´as usuales
Los operadores son las herramientas mediante las cuales se construyen
las expresiones que manipulan los datos. Una clasificaci´on inicial puede ser
la siguiente:
1. Unitarios: −(menos unitario, −x representa la negaci´ on de x) y + (m´ as
unitario, +x representa el valor de x, est´a disponible en compiladores
ANSI). Por ejemplo, si m es igual a 5, −m es igual a -5. No hay que
confundir estos operadores con los operadores binarios aritm´eticos de
diferencia y suma.
2. Aritm´eticos: + (suma), − (diferencia y tambi´en existe el − unitario;
p.e. −x representa la negaci´ on de x), ∗ (producto), / (cociente), %
(divisi´on entera: x%y produce el resto de dividir x entre y, y por
lo tanto es cero, si x es divisible entre y). El operador % no puede
utilizarse con datos de tipo float o double (reales).
Ejemplo 1: j = 3−−x, es interpretado como j = (3−(−x)). El primer
− se˜ nala una resta, el segundo es un − unitario.
Ejemplo 2: 3/4 da como resultado el valor 0, puesto que es una divisi´ on
entera (la parte fraccional se trunca). Si queremos realizar la divisi´ on,
hay que escribir 3,0/4,0.
3. Relacionales: == (igual), ! = (distinto), > (mayor que), < (menor
que), >= (mayor o igual que), <= (menor o igual que).
4. Asignaci´ on: = (Usado en inicializaciones), + = (suma-asignaci´ on),
− = (resta-asignaci´on), ∗ = (multiplica-asignaci´ on), / =(divide-asignaci´ on),
% = (resto-asignaci´on).
Ejemplo 3: a = b pon el valor de b en a.
Ejemplo 4: a+ = b pon el valor de a +b en a.
Ejemplo 5: a− = b pon el valor de a −b en a.
Ejemplo 6: a∗ = b pon el valor de a ∗ b en a.
Ejemplo 7: a/ = b pon el valor de a/b en a.
Ejemplo 8: a % = b pon el valor de a %b en a.
El t´ermino de la izquierda en las anteriores expresiones (a), puede
referirse a una posici´ on de memoria.
5. L´ ogicos: && (and), [[ (or), ! (not).
Ejemplo 9: a&&b vale 1 si a y b son no nulos, vale 0 en otro caso.
Ejemplo 10: a[[b vale 1 si a ´o b son no nulos, vale 0 en otro caso.
3.5 Operadores y funciones standard m´as usuales 43
Ejemplo 11: !b vale 1 si b es nulo, vale 0 en otro caso.
6. Acceso a miembros (Operadores de memoria): [] (usado en declaracio-
nes de arrays) , (usado en declaraciones de estructuras), → (acceso a
estructuras , cuando se dispone del puntero), () (para pasar par´ ame-
tros en funciones, & (pon un objeto en una direcci´ on de memoria), ∗
(obt´en el objeto situado en esa direcci´ on de memoria).
Ejemplo 12: &x obtiene la direcci´ on de x.
Ejemplo 13: ∗x obtiene el objeto situado en la direcci´ on x.
Ejemplo 14: x[5] obtiene el valor del vector x en la posici´ on 5.
Ejemplo 15: x.y obtiene el valor del miembro y en la estructura x.
Ejemplo 16: p → y obtiene el valor del miembro y en la estructura
punteada por p.
7. Operadores de incremento y decremento: C dispone de dos operadores
un poco raros, que incrementan y decrementan el valor de las variables.
El operador ++ le suma 1 a su operando; el operador −− le resta 1. La
caracter´ıstica especial de estos operadores es que pueden ser utilizados
como prefijos (antes de la variable, como en ++n) o bien como sufijos
(despu´es de la variable, n − −). En los dos casos ++ aumenta (−−
disminuye) en 1 el valor de la variable n, pero la expresi´ on + + n
incrementa n antes de utilizar su valor, mientras que n + + lo hace
despu´es de que se ha empleado su valor. Por ejemplo, si n = 5:
x = + +n; pone un 6 en x (incrementa primero el valor de n), pero
x = n + +; pone un 5 (toma primero el valor de n).
8. Operador condicional ? : que resulta ser una versi´ on abreviada de la
expresi´on if...else.... Por ejemplo: z = (x < y)?x : y), comprueba si
x < y, en cuyo caso asigna z = x, si x ≥ y, la asignaci´ on que hace es
z = y.
9. Operadores para la manipulaci´ on de bits: >>, <<, &, [, ∧, ∼.
10. Operador cast, de conversi´ on de tipo.
11. Operador sizeof, que devuelve el tama˜ no en bytes, de datos o expre-
siones.
3.5.1. Funciones m´as usuales
Una funci´ on es una colecci´on de componentes del lenguaje agrupadas
bajo un mismo nombre. Si la funci´ on est´a bien dise˜ nada, debe representar
44 3 Introducci´ on al lenguaje de programaci´ on C (I)
un peque˜ no n´ umero de operaciones, que son indicadas por el nombre de
la funci´ on. A menudo, una funci´ on aparece unas pocas l´ıneas m´ as abajo
de donde se la llama una sola vez, para clarificar una parte del programa.
Vamos a mostrar los mecanismos para la definici´ on de funciones escribiendo
la funci´ on power(m, n) que eleva el entero m a la potencia representada por
el entero positivo n.
/* prueba de la funcion power */
main()
{
int i;
for (i=0; i< 10; ++i)
printf("%d %d %d\n",i,power(2,i), power(-3,i));
}
power(x,n) /* elevar x a la n-´ esima potencia */
int x,n;
{
int i,p;
p=1;
for(i=1; i<=n; ++i)
p=p * x;
return(p);
}
Las funciones aparecen en cualquier orden, y en uno o m´ as archivos
fuente. Por el momento supondremos que est´ an en el mismo archivo, por lo
que se mantiene lo que hemos aprendido sobre ejecuci´ on de programas en
C.
En la l´ınea de impresi´on se llama dos veces a la funci´on power. En cada
llamada se pasan dos argumentos a power, que cada vez devuelve un entero
que se formatea e imprime. Los argumentos de la funci´on power se declaran
para que se conozca su tipo. La declaraci´on de los argumentos se realiza
entre la lista de argumentos y la llave de apertura. Cada declaraci´on debe
acabar con un punto y coma. Los nombre de los argumentos en power son
completamente locales a la funci´on. Esto tambi´en se aplica a las variables i
y p: la i de power no tiene nada que ver con la i de main.
El valor que calcula power se devuelve al programa principal main me-
diante la sentencia return. Puede aparecer cualquier expresi´ on entre los
par´entesis de una sentencia return. Una funci´ on no tiene por qu´e devolver
un valor; una sentencia return sin expresi´ on devuelve el control al programa
principal aunque no le devuelve un valor ´ util.
3.5 Operadores y funciones standard m´as usuales 45
En C todos los argumentos se pasan por valor. Esto significa que la fun-
ci´on recibe los valores de sus argumentos en variables temporales, en lugar
de recibir sus direcciones. La diferencia principal es que en C la funci´ on
llamada no puede alterar el valor de una variable de la funci´ on que llama,
´ unicamente puede cambiar el valor de su copia privada y temporal. Esto es
una ventaja y permite escribir programas m´ as compactos, con pocas varia-
bles externas, ya que los argumentos se tratan como variables locales a la
rutina llamada, si est´ an debidamente inicializadas. Veamos una versi´ on de
power que utiliza este hecho.
power(x,n) /* elevar x a la n-´ esima potencia. Versi´ on 2 */
int x,n;
{
int p;
for(p=1; n>=0; --n)
p=p * x;
return(p);
}
El argumento n se utiliza como variable temporal, siendo decrementada
hasta que llega a ser 0. Cualquier cosa que hagamos con n dentro de power
no tendr´ a ningun efecto sobre el argumento. Cuando sea necesario, es po-
sible hacer que una funci´ on modifique el valor de una variable en la rutina
llamante. Trataremos esto con m´as detalles cuando hablemos de direcciones
de memoria y paso de argumentos por referencia.
En C como en otros lenguajes de programaci´ on existen cientos de fun-
ciones prefabricadas almacenadas en librer´ıas. Algunas de las standard, apa-
recen a continuaci´ on:
Funci´ on Descripci´ on
fgetc() Lee un caracter de un fichero
fopen() Abre un fichero
fclose() Cierra un fichero
time() Calcula el tiempo actual
sin() Calcula el seno de un ´ angulo
log() Calcula el logaritmo de un n´ umero
Como hemos visto tambi´en es posible crear nuevas funciones. Por ejem-
plo, podr´ıamos definir una funci´ on para calcular el cuadrado de un n´ umero
entero. Ser´ıa un caso particular de la funci´ on power:
int square(int num)
{
46 3 Introducci´ on al lenguaje de programaci´ on C (I)
int answer;
answer=num*num;
return answer;
}
La funci´ on main()
Por otro lado, todo programa ejecutable debe contener una funci´ on espe-
cial denominada main(), que indica donde comienza la ejecuci´ on. Por ejem-
plo para invocar a la funci´ on square(), que eleva un n´ umero al cuadrado,
deber´ıamos escribir:
#include <stdlib.h>
int main(void)
{
extern int square(int);
int solution;
solution=square(5);
exit(0);
}
Este peque˜ no programa asigna el cuadrado de 5 a la variable denominada
solution. Las reglas de utilizaci´on de esta funci´ on son las mismas que las
de cualquier otra. Sin embargo, no hemos declarado el tipo de datos que
devuelve, ni los argumentos. Adoptaremos por ahora, que dicha funci´ on
main() genera un valor y trabaja con dos argumentos. Analizaremos este
detalle en el Tema 5.
La funci´ on exit() causa el final del programa, devolviendo el control
al sistema operativo. Si el argumento de exit() es el cero, significa que el
programa termina sin errores. Argumentos distintos del cero, denotan errores
en la ejecuci´on. La llamada de exit() dentro de una funci´ on main() hace
exactamente lo mismo que la sentencia return. Es decir exit(0); es lo mismo
que return 0;. Se debe incluir cualquiera de las dos, dentro de cada funci´ on
main().
Si se usa exit() debe incluirse al comienzo del programa el fichero stdlib.h,
que es donde est´a almacenada dicha funci´ on.
3.5 Operadores y funciones standard m´as usuales 47
En el caso anterior declaramos dos nombres en main(). El primero es
la funci´ on square(), que vamos a llamar. La palabra extern indica que di-
cha funci´ on est´a definida en cualquier sitio, posiblemente en otro fichero
fuente. La otra variable solution, es un entero que nosotros utilizamos para
almacenar el valor que devuelve la funci´ on square().
La siguiente sentencia es en la que se invoca a la funci´ on square(). El
argumento que aparece entre par´entesis, indica que es el valor pasado como
argumento a dicha funci´ on, esto es, es el valor del que se va a calcular el
cuadrado. El operador de asignaci´ on =, indica que el resultado que devuelve
dicha funci´ on ser´a almacenado en la variable solution.
La funci´ on printf()
El programa anterior es correcto pero poco operativo. Una de las razones
es que no nos deja ver su salida. Una soluci´ on para este problema es utilizar
la funci´ on printf().
#include <stdio.h> /*Fichero donde se encuentra printf()*/
#include <stdlib.h>
int main(void)
{
extern int square(int);
int solution;
solution=square(27);
printf("El cuadrado de 27 es %d \n", solution);
exit(0);
}
Se˜ nalar que hemos tenido que a˜ nadir en la cabecera el fichero stdio.h,
donde se encuentra la funci´ on de Input/Output printf(). Aunque describire-
mos dicha funci´ on con m´as detalle en otro cap´ıtulo, se˜ nalar que el s´ımbolo
%d indica que el argumento que va a imprimir es un entero decimal. La
secuencia ¸n indica a dicha funci´ on que salte de l´ınea (newline).
Si tienes almacenado el programa anterior en un fichero de nombre poten-
cia2.c y la funci´ on square() en otro denominado square.c, podr´ıas compilar
ambos programas mediante la sentencia:
48 3 Introducci´ on al lenguaje de programaci´ on C (I)
$ cc -o potencia2 potencia2.c square.c
que crea un ejecutable con nombre potencia2. Basta a continuaci´ on que
teclees
$ potencia2
y obtentr´ as
El cuadrado de 27 es 729
En la sentencia que aparece utilizada la funci´ on printf(), se utilizan dos
argumentos, el primero adem´ as de incluir un peque˜ no texto, especifica el
formato de la variable que se va a imprimir. As´ı %d, indica que la variable
solution es un entero decimal. Hay otros especificadores para otros tipos de
datos, por ejemplo:
Car´ acter Especificaci´ on
%s Vector caracter
%x Entero Hexadecimal
%f Dato real (punto flotante)
%o Entero octal
La versi´on general de la funci´ on printf(), debe contener tantas especifi-
caciones de formato como variables se van a imprimir:
printf(”Imprime tres valores: %d, %d, %d”, num1, num2, num3);
La funci´ on scanf()
Todav´ıa el programa anterior es poco operativo, ya que permite calcular
´ unicamente el cuadrado del n´ umero 27. Para obtener el cuadrado de otro
n´ umero, habr´ıa que modificar el fichero fuente potencia2.c, volver a compilar
y volver a ejecutar. Esto se puede evitar con el uso de la funci´ on scanf().
Dicha funci´ on realiza el trabajo contrario al de printf(), mientras ´esta escribe
la salida del programa en la pantalla, la funci´ on scanf() lee el valor del dato
introducido mediante el teclado y se lo asigna a una variable. Si a˜ nadimos
esta posibilidad al programa anterior:
#include <stdio.h> /*Fichero donde se encuentra printf()*/
#include <stdlib.h>
int main(void)
{
3.5 Operadores y funciones standard m´as usuales 49
extern int square(int);
int solution;
int input_val;
printf("Introduce un valor entero:");
scanf("%d", &input_val);
solution=square(input_val);
printf("El cuadrado de %d es %d\n", input_val, solution);
exit(0);
}
Ahora hemos declarado una nueva variable input val, que sirve para al-
macenar el valor entero introducido por el teclado, luego pasamos este valor
como argumento a square(). La expresi´ on &input val, significa ”la direcci´ on
de memoria de la variable imput val”. En realidad, la funci´ on scanf() alma-
cena el valor introducido directamente en dicha direcci´ on de memoria. La
ejecuci´on del programa tendr´ a el aspecto:
$ potencia2
Introduce un valor entero: 22
El cuadrado de 22 es 484
Hemos visto que la funci´on scanf() realiza la tarea contraria a printf().
Su sintaxis es similar. La diferencia es que en la funci´ on scanf(), los nombres
de variables, van precedidas del s´ımbolo &. Esto hace que el sistema lea el
dato de la terminal y lo coloque en una direcci´ on de memoria.
Sentencias #include
Esta sentencia hace que el compilador lea texto de otros ficheros, al
mismo tiempo que lee el fichero que est´a compilando. Una sentencia de este
tipo puede tener dos estructuras:
#include < fichero >
y
#include “fichero”
50 3 Introducci´ on al lenguaje de programaci´ on C (I)
En el primer caso, el compilador busca en alg´ un lugar designado por el
sistema operativo. En el segundo caso, el compilador busca en el directorio
donde se encuentre el fichero que est´a compilando.
Sentencias #define
Sirven para asociar una constante a una variable, que ocupa una direc-
ci´on de memoria. Por ejemplo:
#define var 1 34
asigna el valor 34 a la variable var 1, de manera que las sentencias
j = 1 + 34;
j = 1 +var 1;
dan el mismo resultado.
3.6. Ambito de definici´ on y validez de las varia-
bles. Variables externas
Las variables de main son privadas o locales a main. Lo anterior tam-
bi´en es v´alido para las variables de otras funciones. Cada variable local de
una rutina comienza a existir cuando se llama a la funci´ on, y desaparece
cuando la funci´ on acaba. Por esta raz´on, tales variables reciben el nombre
de autom´aticas.
Las variables autom´aticas aparecen y desaparecen con la invocaci´on de
la funci´ on, por eso no conservan su valor entre dos llamadas sucesivas, y se
les ha de dar un valor a la entrada de la funci´ on.
Como alternativa es posible definir variables externas a todas las fun-
ciones; esto es, variables globales a las que puede acceder cualquier funci´ on
mediante su nombre. Las variables externas son accesibles globalmente; por
ello pueden usarse en lugar de listas de argumentos para comunicar datos
entre funciones. Adem´ as mantienen sus valores, ya que existen permanente-
mente en lugar de aparecer y desaparecer, incluso despu´es de que finalizan
las funciones que las cambian.
Una variable externa ha de definirse fuera de las funciones. Esto les
asigna memoria real. La variable ha de ser declarada en toda funci´ on que
3.7 Precedencia y orden de evaluaci´ on entre operadores 51
quiera acceder a ella; esto puede hacerse mediante una declaraci´ on extern
expl´ıcita, o en forma impl´ıcita a trav´es del contexto. Veamos un ejemplo:
#include <stdio.h>
/* definici´ on de la variable global var1 */
int var1=50;
main(){ /* comienzo del programa */
printf("%d\n", var1); /* escribe 50 */
{ /* var1 y var2 variables locales al bloque 1 y 2*/
int var1=100, var2=200;
printf("%d %d\n", var1, var2); /* escribe 100 y 200 */
{ /* bloque 2*/
int var1=0;
printf("%d %d\n", var1, var2); /* escribe 0 y 200 */
}
printf("%d\n", var1); /* escribe 100 */
}
printf("%d\n", var1); /* escribe 50 */
} /* final de programa*/
3.7. Precedencia y orden de evaluaci´on entre ope-
radores
Los operadores tiene dos propiedades, precedencia y asociatividad. Las
dos est´an estrechamente relacionadas. Ve´amoslas en un ejemplo:
La expresi´on 2 +3 ∗ 4 evaluada por la m´ aquina es 14, igual que 3 ∗ 4 +2.
Sin embargo la m´ aquina hace la cuenta siempre en el mismo orden. Primero
eval´ ua 3 ∗ 4 y luego al resultado le suma un 2. Esto se resume diciendo que
el operador ∗ tiene una precedencia m´as alta que el operador +. Si se quiere
establecer otro orden hay que utilizar par´entesis. Por ejemplo, 3,0 −4,0/8,0
da como resultado 2,5, distinto al de la expresi´ on (3,0 − 4,0)/8,0 que da
−0,125.
Por otra parte, la expresi´ on de asignaci´on x = y = 3, es asociativa de
derecha a izquierda, es decir se eval´ ua comenzando por la derecha. En primer
lugar se asigna un 3 a la variable y, y posteriormente se asigna este mismo
valor a la variable x. Un operador puede ser asociativo de derecha a izquierda
o de izquierda a derecha.
52 3 Introducci´ on al lenguaje de programaci´ on C (I)
En la siguiente tabla aparecen clasificados los operadores de acuerdo a
su precedencia y asociatividad.
Clase de Operador Operadores en la clase Asociatividad Precedencia
Primario () [] → Izda. a dcha. Alta
Unitario cast
sizeof
& (direcci´on en memoria) Dcha. a izda.
∗ (de referencia)
− +
∼ ++ −− !
Aritm´etico multiplicativo ∗ / % Izda. a dcha.
Aritm´etico aditivo + − Izda. a dcha.
Manipulaci´ on de bytes << >> Izda. a dcha.
Relacional de desigual. < <= > >= Izda. a dcha.
Relacional de igual. == ! = Izda. a dcha.
Manipulaci´ on de bytes & Izda. a dcha.
Manipulaci´ on de bytes ∧ Izda. a dcha.
Manipulaci´ on de bytes [ Izda. a dcha.
L´ogico && Izda. a dcha.
L´ogico [[ Izda. a dcha.
Condicional ? : Dcha. a izda.
Asignaci´on = + = − = ∗ =
/ = % = >> n = <<= Dcha. a izda.
& = ∧ = Baja
Cap´ıtulo 4
Introducci´on al lenguaje de programaci´ on
C (II)
Un tipo de dato es una interpretaci´ on que da el compilador a una cadena
de bits. En el tema anterior vimos una clasificaci´ on de datos como simples (o
escalares) y compuestos (o agregados). Hay adem´as un tipo de dato -void-
que no es ni simple ni compuesto. En este tema vamos a describir los tipos
de datos escalares y el tipo void.
4.1. Declaraciones. Tipos de datos escalares: ente-
ros, car´acter, de coma flotante y enumeraci´ on
Todas las variables deben ser declaradas antes de utilizarlas, aunque cier-
tas declaraciones se realizan impl´ıcitamente por el contexto. La declaraci´ on
le dice al compilador c´ omo interpretar y almacenar una serie de bytes. Para
declarar la variable j como entera, debemos escribir:
int j;
La palabra int es una palabra reservada que especifica que el dato es
de tipo entero. Hay nueve palabras reservadas para especificar los tipos de
datos simples o escalares. Los cinco dados por: char, int, float, double,
enum son tipos b´ asicos. Los otros: short, long, signed y unsigned son
modificaciones calificadoras de datos simples.
53
54 4 Introducci´ on al lenguaje de programaci´ on C (II)
char un byte (octeto) capaz de contener un car´ acter del juego de
caracteres de la instalaci´ on local
int un entero, normalmente del tama˜ no de los enteros de la
instalaci´ on local
float un n´ umero en punto flotante de precisi´ on normal
double un n´ umero en punto flotante de doble precisi´ on
Adem´as hay una serie de calificadores que se aplican a los enteros: short,
long, signed y unsigned. short y long se refieren a diferentes tama˜ nos de
los n´ umeros. Algunos compiladores dedican cuatro bytes para almacenar un
entero, mientras que otros s´olo dedican dos. Por otra parte el tama˜ no de un
byte tampoco es constante. En la mayor´ıa de las m´ aquinas un byte son ocho
bits, pero hay excepciones.
Si no tienes inter´es en el tama˜ no del entero dentro de la m´ aquina, puedes
usar int. En otro caso debes especificar short o long. En la mayor´ıa de las
m´aquinas short int es un entero que ocupa dos bytes, mientras que long
int es un entero que ocupa cuatro bytes.
short int j;
long int k;
es lo mismo que
short j;
long k;
El n´ umero de bits utilizados para representar un entero determina el
rango de valores que pueden ser almacenados en ese tipo.
Tipo Tama˜ no (en bytes) Rango de valores
int 4 −2
31
a 2
31
−1
short int 2 −2
15
a 2
15
−1
long int 4 −2
31
a 2
31
−1
unsigned short int 2 0 a 2
16
−1
unsigned long int 4 0 a 2
32
−1
signed char 1 −2
7
a 2
7
−1
unsigned char 1 0 a 2
8
−1
Consideremos por ejemplo un entero short int (16 bits). Cada bit repre-
senta un valor de 2 elevado a la potencia n, donde n representa la posici´on
4.1 Declaraciones. Tipos de datos escalares: enteros, car´ acter, de coma flotante y enumeraci´on55
del bit:
2
15
2
14
2
13
2
12
2
11
2
10
2
9
2
8
2
7
2
6
2
5
2
4
2
3
2
2
2
1
2
0
Por ejemplo para expresar el n´ umero decimal 9 = 2
3
+ 2
0
= 8 + 1.
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1
Adem´as en C existe la posibilidad de declarar una variable como no
negativa (unsigned). Para hacer ´esto se utiliza el calificador unsigned:
unsigned int k;
unsigned short j;
unsigned long m;
En los compiladores standard ANSI, existe la posibilidad de declarar a
los enteros signed, es decir, con valores positivos o negativos. Un calificador
de este tipo parece superfluo, dado que por defecto una variable no declarada
como unsigned, deber´ıa ser signed. La diferencia est´ a en las variables de tipo
car´acter, que pueden ser signed o unsigned por defecto. La mayor´ıa de los
compiladores utilizan signed char por defecto.
Vamos a ver ahora que la notaci´ on cient´ıfica usual
123,45e
−7
o bien
0,12E3
tambi´en es v´alida para valores float. Toda constante en punto flotante se
toma por defecto como double, por lo que la notaci´ on e sirve tanto para
float como para double. Si queremos declarar e inicializar una variable en
punto flotante
float eps = 1.5e-5;
(le estamos asignando a la variable real eps el valor inicial 0,000015 = 1,5 ∗
10
−5
).
Hay una notaci´ on para constantes octales y hexadecimales: un cero que
encabece una constante int indica octal; el grupo 0x (o bien OX) indica he-
56 4 Introducci´ on al lenguaje de programaci´ on C (II)
xadecimal. Por ejemplo, el decimal 31 de puede escribir como 037 en octal, o
como 0x1f (0X1F) en hexadecimal. Las constantes octales o hexadecimales
pueden acabar en L para hacerlas long. (Ver tabla de conversi´ on ASCII,
para las equivalencias entre los distintos tipos de representaci´ on).
Una constante car´ acter es un ´ unico car´ acter escrito entre ap´ostrofos. El
valor num´erico de una constante car´ acter es el valor num´erico del car´acter en
el conjunto de caracteres de la representaci´ on. Por ejemplo, en el conjunto
de caracteres ASCII, el car´acter cero, ‘0’, es 48, diferente del valor num´erico
0. Las constantes car´acter pueden participar en las operaciones num´ericas
como cualquier otro n´ umero, aunque es m´ as frecuente encontrarlas en com-
paraciones con otros car´acteres.
Ciertos caracteres no imprimibles se representan como constantes car´acter
mediante secuencia de escape como ¸n (nueva l´ınea), ¸t (tabulador); ¸0 (nu-
lo), ¸¸ (barra invertida), ¸

(ap´ ostrofo), etc. Aunque aparecen como dos
caracteres, en realidad son s´olo uno. La constante

¸0

representa el car´acter
con valor nulo.
El tipo de dato de enumeraci´ on enum, es particularmente ´ util, cuando
quieres crear un ´ unico conjunto de valores que pueden ser asociados con
una variable. En el siguiente ejemplo, vamos a declarar dos variables de
enumeraci´ on llamadas color e intensidad. A color le vamos a poder asignar
uno de los cuatro valores constantes rojo, azul, amarillo, verde. A intensidad
le vamos a poder asignar los valores brillante, media, oscura. La declaraci´ on
ser´ıa:
enum rojo, azul, amarillo,verde color;
enum brillante, media, oscura intensidad;
Este tipo de dato no forma parte de la versi´ on de C dada por Kernighan y
Ritchie, por lo que la implementaci´ on puede variar de un compilador a otro.
Un buen compilador, sin embargo, dar´ıa se˜ nal de problemas ante sentencias
como:
color=brillante;
intensidad= azul;
color=1;
color=azul+verde;
4.2 Conversiones de tipo 57
El tipo de dato void
Tampoco forma parte de la versi´ on de C dada por Kernighan y Ritchie.
El tipo dato void, tiene dos importantes usos. El primero es indicar que
una determinada funci´ on no devuelve un valor. Por ejemplo, puedes ver una
funci´ on definida como:
void func (a, b)
int a, b;
{
.
.
}
Esto indica que la funci´ on no devuelve ning´ un valor. Tambi´en pod´ıamos
haberlo utilizado en la llamada de func()
extern void func();
Esto informa al compilador de que no se puede asignar el valor de retorno
de dicha funci´ on, por ejemplo:
num=func(x,y);
dar´ıa un error.
El otro uso de void es declarar un puntero gen´erico. Lo veremos con m´as
detalle, m´as adelante.
4.2. Conversiones de tipo
Los operandos de tipos diferentes que aparecen en una expresi´ on se con-
vierten a un mismo tipo de acuerdo con unas cuantas reglas. Las ´ unicas
conversiones que se realizan autom´ aticamente son las que tiene sentido, co-
mo la conversi´ on de un entero a punto flotante antes de sumar dicho entero
con un n´ umero en punto flotante. (Por ejemplo: 3+2.5=5.5).
Por otra parte no est´ an permitidas expresiones que carecen de sentido,
por ejemplo, utilizar un n´ umero en punto flotante como sub´ındice.
Adem´as los caracteres y los enteros (char e int) se mezclan en expresiones
aritm´eticas: todo char de una expresi´ on se convierte autom´aticamente en int.
58 4 Introducci´ on al lenguaje de programaci´ on C (II)
Ejemplo 4.1: Uso de la funci´ on atoi() que convierte una cadena de
d´ıgitos en su valor num´erico correspondiente.
atoi(s) /* convierte el caracter s en entero*/
char s[];
{
int i,n;
n=0;
for(i=0; s[i] >= ‘0’ && s[i] <= ‘9’; ++i)
n=10*n+s[i]-‘0’;
return(n);
}
La comparaci´ on
s[i] >=‘0’ && s[i]<=‘9’
determina si el car´ acter que hay en s[i] es un d´ıgito. Si lo es, el valor num´eri-
co del d´ıgito es s[i] - ‘0’. Esto ´ ultimo funciona correctamente s´ olo si ‘0’, ‘1’,...
etc., son positivos y est´ an en orden creciente, y s´ olo si hay d´ıgitos entre los
caracteres ‘0’ y ‘9’. Afortunadamente esto es cierto para todos los juegos de
caracteres ordinarios.
Por definici´ on, toda aritm´etica en la que intervienen valores de tipo char
e int, se realiza convirtiendo los valores char al tipo int. Las variables y
constantes de tipo char son esencialmente id´enticas, en cuanto al tipo, a los
valores int en contextos aritm´eticos. Por ejemplo, s[i]-‘0’, es una expresi´ on
entera con un valor entre ‘0’ y ‘9’ seg´ un sea el car´ acter almacenado en s[i],
y dicha expresi´ on se puede utilizar como sub´ındice.
Por ´ ultimo la sentencia
n=10*n+s[i]-‘0’;
expresa el valor del d´ıgito s[i]-‘0’, en sistema decimal (base diez).
Otro ejemplo de conversi´ on de char a int, es la funci´ on lower(), que asigna
a cada letra may´ uscula su correspondiente min´ uscula. (V´ alido para caracte-
res ASCII). Si el car´ acter no es una letra may´ uscula, la funci´ on devuelve la
misma letra sin alteraciones.
lower(c) /* convierte c en min´ uscula, s´ olo para ASCII */
int c;
4.2 Conversiones de tipo 59
{
if( c >= ‘A’ && c <= ‘Z’)
return(c+‘a’- ‘A’);
else
return(c);
}
Esta funci´ on act´ ua correctamente en ASCII, ya que las letras may´ usculas
y min´ usculas correspondientes est´an situadas a una distancia fija, conside-
radas como valores num´ericos, y adem´as el alfabeto es continuo, es decir, no
contiene ning´ un otro car´ acter entre dichas letras.
Otra utilidad de la conversi´ on autom´ atica de tipos est´a relacionada con
expresiones como i > j y expresiones l´ogicas que utilizan && y [[, en las que
se ha establecido que devuelvan el valor 1, en caso de que el resultado sea
cierto y el valor 0 en caso contrario.
Las conversiones autom´ aticas se realizan de acuerdo con las siguientes
reglas. Si un operador como + o ∗ con dos operandos (un operador binario)
tiene los operandos de distinto tipo, el de tipo ”inferior”es ascendido al tipo
”superior”, antes de realizar la operaci´ on. El resultado es de tipo ”superior”.
En general los operandos que intervienen en una operaci´ on, son convertidos
al tipo del operando de precisi´ on m´ as alta.
Las reglas de conversi´on se resumen en las siguientes:
char y short se convierten en int, y float se convierte en double.
Si alg´ un operando es de tipo double, el otro se convierte en double
y el resultado es double.
Si no se aplica la regla anterior, y si alg´ un operando es de tipo
long, el otro se convierte en long y el resultado es long.
Si no se aplica la regla anterior y alg´ un operando es de tipo
unsigned, el otro se convierte en unsigned y el resultado es de
tipo unsigned.
Si no se puede aplicar ninguna de las reglas anteriores, los ope-
randos deben ser de tipo int y el resultado es de tipo int.
En C la aritm´etica del punto flotante se realiza en doble precisi´ on. Las
conversiones tambi´en tienen lugar en las asignaciones; el valor de la parte
derecha se convierte al de la izquierda, que es el tipo del resultado. De
float a int la conversi´ on provoca un truncamiento de la parte fraccionaria.
De double a float, un redondeo. Los enteros de mayor magnitud (long) se
60 4 Introducci´ on al lenguaje de programaci´ on C (II)
convierten en tipo short o en caracteres (char) por eliminaci´ on de los bits
de orden superior.
Los argumentos de una funci´ on son expresiones y por ello las reglas de
converi´ on de tipo tambi´en se aplican con ellos. Concretamente, char y short
se convierten en int y float se vuelve double. A esto se debe el que hayamos
declarado los argumentos de la funci´ on como int y double, incluso cuando
se llama a la funci´ on con char y float.
Por ´ ultimo se puede forzar la conversi´ on expl´ıcita de tipo ¸ coaccionada”de
una expresi´ on, mediante una construcci´ on denominada cast. En la construc-
ci´on
(nombre de tipo) expresi´ on
la expresi´on se convierte al tipo citado mediante las anteriores reglas de con-
versi´on. Por ejemplo, la rutina sqrt() espera que se le pase un argumento de
tipo double, con lo cual transmitir´ a mensajes de error si recibe argumentos
de otro tipo. Si n es entero,
sqrt((double) n)
convierte n al tipo double antes de pas´ arselo a sqrt. El operador cast tiene
la misma precedencia que cualquier otro operador unitario, seg´ un vimos en
la tabla del tema anterior.
4.3. Nociones sobre representaci´on de datos e im-
plicaciones sobre la precisi´ on num´erica
El ordenador digital representa los n´ umeros en base 2, de manera que
cada n´ umero se almacena en un n´ umero finito de d´ıgitos binarios. Hay dos
formas de almacenar estos n´ umeros, en punto fijo y en punto flotante.
La representaci´on en punto fijo es la que utilizamos normalmente. Por
ejemplo: −30,421 ´o 0,3245 son representaciones en punto fijo. Como hemos
dicho, el n´ umero de d´ıgitos de cada n´ umero que almacena un ordenador es
finito n = n
1
+n
2
, donde n
1
son los d´ıgitos de la parte entera y n
2
los de la
fraccionaria.
n
1
n
2
−30,421 − 0030 421000
0.3245 0000 324500
4.3 Nociones sobre representaci´ on de datos e implicaciones sobre la precisi´ on num´erica61
n
1
= 4, n
2
= 6 son valores fijos del ordenador. De esta forma hay valores
que se pueden representar de forma exacta y otros que no. Por ejemplo,
el n´ umero 31,8923467, en un ordenador con n
1
= 4 y n
2
= 6 puede ser
representado como
0031 892346
representaci´on que se denomina corte. O bien
0031 892347
en cuyo caso, la representaci´ on se denomina redondeo. En general la re-
presentaci´on de n´ umeros en punto fijo es muy poco ´ util, se utiliza s´ olo
para operaciones elementales (calculadoras,..). Por ejemplo, los errores de
representaci´on de n´ umeros como: 35420,87 ´ o 0,00000056, son brutales. En
el primer caso, el ordenador aproximar´ıa por 9999 y en el segundo por 0.
Para c´ alculos m´as ambiciosos, el ordenador debe operar en punto flotan-
te.
La representaci´on en punto flotante de un n´ umero x es x = ax10
b
con
0,1 ≤ [a[ < 1 y b entero.
Por ejemplo:
342,17 (en punto fijo) 0,34217x10
3
(en punto flotante)
x = (±.α
1
α
2
...α
n
)x10
±bm...b
0
donde (±.α
1
α
2
...α
n
) se denomina mantisa y ±b
m
...b
0
es el exponente. La
notaci´ on a emplear ser´a:
,34217E3
El n´ umero de d´ıgitos en la mantisa se denominan d´ıgitos significativos.
El n´ umero 0,0001423 = ,1423E-3 tiene cuatro d´ıgitos significativos. Con la
condici´ on α
1
> 0, la representaci´ on anterior es ´ unica.
En la representaci´ on num´erica en punto flotante, es necesario reservar
un n´ umero de d´ıgitos para la mantisa (t) y un n´ umero para el exponente (e).
Si t = 4 y e = 2, el n´ umero 0,00143 se puede expresar 0,00143 = ,1430E-2.
Los valores de t y e son caracter´ısticos de cada ordenador.
62 4 Introducci´ on al lenguaje de programaci´ on C (II)
Por ejemplo, si t = 12 y e ∈ (−500, 500) el rango de valores a representar
en pantalla es ±,999999999999E±499.
Existen valores que no se pueden representar de forma exacta. Para re-
presentar estos n´ umeros se pueden emplear dos t´ecnicas de aproximaci´on:
corte y redondeo.
Para t = 4: x = 3,87184 = 0,387184E1
fl(x) = 0,3871E1 corte (*)
fl(x) = 0,3872E1 redondeo.
Sea A el conjunto de n´ umeros m´aquina (representables de forma exacta
para un cierto valor de t). A es un conjunto finito.
Para cualquier n´ umero x, fl(x) ∈ A. Esta aplicaci´ on debe cumplir
[x −fl(x)[ ≤ [x −y[ ∀y ∈ A (4.1)
(observar que la aproximaci´ on por corte (*) no satisface esta condici´ on).
Si a = ±.α
1
α
2
...α
t
α
t+1
... [a[ = .α
1
...α
t+1
... y su representaci´on por re-
dondeo a

es:
a

=
_

1
α
2
...α
t
si α
t+1
≤ 4

1
α
2
...α
t
+ 10
−t
si α
t+1
≥ 5
fl(x) = signo(x)xa

x10
e
y esta aplicaci´ on s´ı satisface (4.1).
Si x ,= 0
¸
¸
¸
¸
x −fl(x)
x
¸
¸
¸
¸
=
¸
¸
¸
¸
[a[10
e
−a

10
e
[a[10
e
¸
¸
¸
¸
=
¸
¸
¸
¸
[a[ −a

[a[
¸
¸
¸
¸

5 10
−(t+1)
[a[ ≥ 10
−1
≤ 5 10
−t
(4.2)
Es decir [[a[ −a

[ ≤ 5 10
−(t+1)
. Esta segunda cantidad se denomina error
absoluto. No da idea de la proximidad de los n´ umeros. Por ejemplo:
Si x
1
= 5437,43215 y x
2
= 5437,43214, [x
1
−x
2
[ = 0,00001.
Si y
1
= 0,00005 y y
2
= 0,00004, [y
1
−y
2
[ = 0,00001.
La diferencia es la misma y s´ olo en el primer caso dir´ıamos que x
1
es
una buena aproximaci´ on de x
2
.
4.3 Nociones sobre representaci´ on de datos e implicaciones sobre la precisi´ on num´erica63
¸
¸
¸
x
1
−x
2
x
1
¸
¸
¸ = 0,183910341 10
−8
.
[
y
1
−y
2
y
1
[ = 0,2 > 5 10
−5
.
La cantidad (4.2) se denomina error relativo, que est´ a acotado como
vemos por 5 10
−t
.
Si
x−fl(x)
x
= , fl(x) = x(1 −) con [[ ≤ eps. eps se denomina precisi´on
del ordenador.
Por ejemplo si escribimos 3,1415 y decimos que tiene cuatro d´ıgitos sig-
nificativos correctos, significa que su error relativo es < 5 10
−5
.
4.3.1. Errores de redondeo en computaci´ on
Sean por ejemplo x = 0,845E7 e y = 0,324E −2. El resultado de sumar
estos dos n´ umeros es
845000 0
0.00324
845000 0.00324
con t = 8.
x ⊕y = x y en general si [y[ <
eps
10
[x[ ⇒ x ⊕y = x.
Se puede decir que en general la suma, resta, divisi´ on y multiplicaci´ on
de n´ umeros m´aquina no son operaciones asociativas y tampoco distributivas
una con respecto a la otra. El resultado de una serie de operaciones depende
del orden en que se realicen.
Ejemplo 4.2 (t = 8) a = 0,023371258E − 4, b = 0,33678429E2 y
c = −0,33677811E2. Veamos que (a ⊕b) ⊕c ,= a ⊕(b ⊕c).
0.000023 371258
33.678429
33.678452 371258
no aparece si t = 8 (= a ⊕b)
-33.677811
0.000641 371258 (a ⊕b) ⊕c = 0,641E −3
Por otra parte: b ⊕c = 0,000618. (b ⊕c) ⊕a = 0,64137E −3.
64 4 Introducci´ on al lenguaje de programaci´ on C (II)
Ejemplo 4.3 En sumas que involucran sumandos de distinta maginitud,
el orden es importante. Si sumamos de manera exacta estos tres n´ umeros:
(t = 8)
0.00000007
0.00000009
3
3.00000016
Si t = 8 = 0,30000001E1
Sin embargo si los sumamos en disntinto orden:
3
0.00000007
0.00000009
3.0000000
= 0,3000000E1
Este tipo de errores es caracter´ıstico de la suma de series.


i=1
a
i

n
i=1
a
i
.
En las series convergentes el t´ermino general tiende a 0 y los t´erminos
van de mayor a menor. El error relativo a la hora de sumar los t´erminos
peque˜ nos es m´ınimo si los t´erminos se suman de menor a mayor; de esa
forma, el valor de la suma va aumentando y se va aproximando al tama˜ no
de los ´ ultimos sumandos (los primeros t´erminos en la serie). Luego una serie
convergente hay que sumarla siempre en orden regresivo.
4.3.2. Error de cancelaci´ on
Aparece cuando restamos dos n´ umeros del mismo signo y muy pr´oximos:
x −y, x ≈ y.
Por ejemplo, con t = 8:
0.76545421 E1
-0.76545264 E1
0.00000157 E1
Hemos pasado de tener 8 d´ıgitos significativos correctos a tener 3, luego
hemos perdido 5 d´ıgitos correctos.
4.3 Nociones sobre representaci´ on de datos e implicaciones sobre la precisi´ on num´erica65
Ejemplo 4.4 Para resolver: ax
2
+bx +c = 0
x
1
x
2
=
−b±

b
2
−4ac
2a
Si ac << b
2
⇒ [b
2
− 4ac[ ≈ b
2
y uno de los signos ± da un error de
cancelaci´ on.
La forma de evitarlo ser´ıa:
x
1
= −signo(b)
[b[ +

b
2
−4ac
2a
x
2
=
c
ax
1
Con t = 5, al resolver: x
2
+ 111,1x + 1,2121 = 0
De la primera forma: x
1
= −111,10, x
2
= −0,01000.
De la segunda: x
1
= −111,10, x
2
= −0,010910, ´esta ´ ultima m´as pr´oxima
al valor real.
Las ra´ıces ser´ıan (con t = 9): x
1
= −111,09909..., x
2
= −0,01091008...
Recuerda la relaci´on de recurrencia utilizada en el Ejemplo 1.3. Repre-
senta, sin duda, un caso m´as en el que se pierde precisi´ on al restar dos
n´ umeros del mismo signo y magnitd comparable. Debes extremar precau-
ciones cuando utilices relaciones de recurrencia que envuelvan restas.
66 4 Introducci´ on al lenguaje de programaci´ on C (II)
Cap´ıtulo 5
Introducci´on al lenguaje de programaci´ on
C (III)
Las sentencias o proposiciones de control de flujo de un lenguaje especi-
fican el orden en que se realizan las computaciones.
5.1. Sentencias de control de flujo: for, if , do, whi-
le, switch. Ejemplos de uso
Una expresi´ on como x=0 o i++ o printf() se convierte en una proposi-
ci´on cuando va seguida de punto y coma, como en
x=0;
i++;
printf(...);
En el lenguaje C, el punto y coma es un terminador de sentencia, y
no un separador. Con las llaves se agrupan declaraciones y sentencias en
una proposici´ on compuesta o bloque, y son sint´ acticamente equivalentes a
una proposici´ on simple. Las llaves que rodean las proposiciones m´ ultiples
despu´es de if, else, while, o for son otro ejemplo. Nunca se pone punto y
coma despu´es de la llave derecha que cierra un bloque.
If-Else
La proposici´ on if-else sirve para tomar decisiones. Formalmente la sinta-
xis es
67
68 5 Introducci´ on al lenguaje de programaci´ on C (III)
if (expresi´ on)
proposici´ on-1
else
proposici´ on-2
donde la parte else es opcional. Se eval´ ua la expresi´ on; si es cierta (es decir,
si tiene un valor distinto de cero), se ejecuta la proposici´ on-1. Si es falsa,
(expresi´ on es cero) y existe parte else, se ejecuta proposici´on-2. Un if sim-
plemente comprobar´ a el valor num´erico de una expresi´on; por ello pueden
aplicarse abreviaciones. Lo m´as elemental es escribir
if (expresi´ on)
en lugar de
if (expresi´ on !=0)
Dado que la parte else de un if-else es opcional, hay ambig¨ uedad cuando
se omite un else en una secuencia de if anidados. Esto se resuelve asociando
el else al if sin el else m´as cercano. Por ejemplo:
if (n > 0)
if(a > b)
z=a;
else
z=b;
La parte else se asocia con el if m´as interior, como se indica en el sangrado.
Si no fuera as´ı, hay que utilizar llaves, como en:
if (n > 0){
if(a > b)
z=a;
}
else
z=b;
Else-if
La construcci´ on
if (expresi´ on)
proposici´ on
5.1 Sentencias de control de flujo: for, if , do, while, switch. Ejemplos de uso69
else if (expresi´ on)
proposici´ on
else if (expresi´ on)
proposici´ on
else
proposici´ on
es la forma m´as general de escribir una decisi´on m´ ultiple. Las expresiones se
eval´ uan en orden; si alguna es cierta, se ejecuta la proposici´ on asociada con
ellas y se acaba la cadena. El c´odigo para una proposici´ on puede ser simple
o un bloque entre llaves.
La ´ ultima parte else maneja el caso ”ninguno de los anteriores”, o el de
defecto, cuando no se sustituye ninguna de las otras condiciones. Si no hay
acci´on expl´ıcita para el defecto, se puede omitir el ´ ultimo else.
Ejemplo 5.1: Vamos a ver un programa que decribe una funci´ on de
b´ usqueda binaria, que decide si un valor concreto x aparece en un arreglo
ordenado v de dimensi´on n. Los elementos de v est´an ordenados en orden
creciente. La funci´ on devolver´ a la posici´ on (un n´ umero entre 0 y n − 1) si
x est´a en v, y un -1 si no est´ a.
binary(x,v,n) /* buscar x en v[0],...,v[n-1]*/
int x, v[], n;
{
int low, high, mid;
low=0;
high=n-1;
while( low<= high){
mid=(low+high) / 2.;
if(x < v[mid])
high = mid-1;
else if(x > v[mid])
low = mid+1;
else /* encontrado */
return(mid);
}
return(-1);
}
La decisi´ on b´asica es si x es menor, mayor o igual que el elemento central
v[mid] en cada paso.
Switch
70 5 Introducci´ on al lenguaje de programaci´ on C (III)
La proposici´ on switch es una herramienta especial para decisiones m´ ulti-
ples que comprueba si una expresi´ on iguala uno entre varios valores cons-
tantes, y se bifurca a partir de ellos.
int switch_example( char input_arg )
{
switch (input_arg)
{
case ‘A’: return 1;
case ‘B’: return 2;
case ‘C’: return 3;
case ‘D’: return 4;
default : return -1;
}
}
La misma funci´ on puede ser escrita con proposiciones if-else:
5.1 Sentencias de control de flujo: for, if , do, while, switch. Ejemplos de uso71
int switch_example( char input_arg )
{
if (input_arg ==‘A’)
return 1;
else if(input_arg == ‘B’)
return 2;
else if(input_arg == ‘C’)
return 3;
else if(input_arg == ‘D’)
return 4;
else
return -1;
}
La expresi´on switch debe de ir seguida de un par´entesis que encierra otra
expresi´on. Esta ´ ultima expresi´ on debe de ser entera, es decir, puede ser char,
long o short; pero no double, float o long double. En la versi´ on de Kernigham
y Ritchie, debe ser una expresi´on int. La proposici´ on default, es opcional.
Ejemplo 5.2:
/* Programma que imprime mensajes de error, basados en el
* valor de la variable error_code. La funci´ on es declarada
* void porque no devuelve ning´ un valor*/
#include <stdio.h>
#define ERR_INPUT_VAL 1
#define ERR_OPERAND 2
#define ERR_OPERATOR 3
#define ERR_TYPE 4
void print_error (int error_code )
{
switch (error_code)
{
case ERR_INPUT_VAL:
printf("Error: Valor inicial no v´ alido.\n");
break;
case ERR_OPERAND:
printf("Error: Operando no v´ alido.\n");
break;
case ERR_OPERATOR:
printf("Error: Operador no v´ alido.\n");
break;
case ERR_TYPE:
printf("Error: Dato incompatible.\n");
break;
default:
printf("Error: C´ odigo de error desconocido %d\n",
error_code");
break;
}
72 5 Introducci´ on al lenguaje de programaci´ on C (III)
}
La proposici´ on break, hace que se produzca una salida inmediata de la
instrucci´ on switch.
Iteraciones while y for
En
while ( expresi´ on )
proposici´ on
se eval´ ua una expresi´ on. Si es cierta (si no es cero), se ejecuta la proposici´on
y se vuelve a evaluar la expresi´ on. El ciclo contin´ ua hasta que la expresi´ on
vale cero, momento en que la ejecuci´on contin´ ua despu´es de la proposici´on.
La sintaxis en la proposici´ on for es:
for ( expr1; expr2; expr3 )
proposici´ on
que es equivalente a:
expr1;
while ( expr2 )
{
proposici´ on
expr3;
}
Ejemplo 5.3: La siguiente funci´ on devuelve el factorial del argumento:
long int factorial ( long val)
{
int j, fact=1;
for (j=2; j <= val; j++)
fact=fact*j;
return fact;
}
que escrita mediante la proposici´ on while es:
5.1 Sentencias de control de flujo: for, if , do, while, switch. Ejemplos de uso73
long int factorial ( long val)
{
int j, fact=1;
j=2;
while (j <= val)
{
fact=fact*j;
j++;
}
return fact;
}
Gramaticalmente las tres componentes de un for son expresiones. M´ as
com´ unmente, expr1 y expr3 son asignaciones o llamadas a funci´on y expr2
es una expresi´ on de relaci´on. Pueden omitirse cualquiera de las tres, aunque
deben mantenerse los puntos y coma. Si la comprobaci´ on expr2 no est´a se
supone que es cierta.
La elecci´on entre un ciclo while o for suele ser una cuesti´on de gusto.
Otro operador de C es la coma ”,”que casi siempre encuentra aplicaci´on
en la sentencia for. Una pareja de expresiones separadas por una coma se
eval´ ua de izquierda a derecha, y el tipo del resultado es el mismo que el de
operando derecho. Por tanto en una proposici´ on for es posible situar varias
expresiones en cada una de sus partes.
Iteraciones do-while
Las iteraciones while y for comparten la ´ util caracter´ıstica de comprobar
la condici´ on de terminaci´ on al comienzo en lugar de hacerlo al final. El tercer
tipo de iteraci´ on en C, do-while, hace la comprobaci´ on al final, despu´es de
la pasada sobre le cuerpo del ciclo. Y ´este se ejecuta al menos una vez. La
sintaxis es
do
proposici´ on
while ( expresi´ on);
Primero se ejecuta la proposici´on y luego se eval´ ua la expresi´ on. En caso
de ser cierta se ejecuta de nuevo y as´ı sucesivamente. La ejecuci´on termina
cuando la expresi´ on resulta falsa.
Break
A veces es necesario tener la posibilidad de salir de un ciclo por un lugar
distinto al de las comprobaciones del comienzo o del final. La sentencia break
74 5 Introducci´ on al lenguaje de programaci´ on C (III)
proporciona una salida forzada de for, while y do, en la misma forma que
de switch. Una sentencia break obliga a una salida inmediata del ciclo (o
switch) m´as interior.
Continue
La proposici´ on continue est´ a relacionada con break, pero se usa me-
nos. Obliga a ejecutar la siguiente iteraci´on del ciclo (for, while, do), que
la contiene. En while y do, significa que la parte de comprobaci´ on se eje-
cute inmediatamente; en for, el control pasa a la etapa de reinicializaci´ on
(continue se aplica s´olo a ciclos, no a switch).
Ejemplo 5.4:
for (i=0; i < N; i++){
if (a[i] < 0) /*salta los elementos negativos*/
continue;
.....; /* trata los positivos */
}
Notar que cada vez que se ejecuta la sentencia continue, el cuerpo del
for se abandona, inici´ andose la ejecuci´ on del mismo para un nuevo valor de
i.
Saltos (goto) y etiquetas
El lenguaje C contiene la infinitamente seductora sentencia goto, junto
a las etiquetas a las que saltar. Formalmente goto no es necesario nunca y
siempre se puede evitar. Sin embargo en algunas (pocas) ocasiones, puede
resultar ´ util. El uso m´ as normal consiste en abandonar el proceso en alguna
estructura profundamente anidada, tal como la salida de dos ciclos a la vez.
La proposici´ on break, no resulta efectiva en este caso, ya que s´olo abandona
el ciclo interno.
for(...)
for(...){
...
if (desastre)
goto error;
}
...
error: arregla el desatre
Ejemplos de uso
5.1 Sentencias de control de flujo: for, if , do, while, switch. Ejemplos de uso75
/******* Fichero salir.c *************/
#include <stdio.h>
main()
{
int c,f ;
int m[4][4]={2,4,3,1,2,-1,5,-1,-1,2,4,2,1,-1,-1,0};
for (f=0;f<=3;f++)
for(c=0; c<=3; c++)
if(m[f][c]==-1)
goto salir;
salir:
if(f<4 && c<4)
printf("(%d, %d)\n", f,c);
}
/***********Fichero salir2.c********/
#include <stdio.h>
main()
{
int c,f ;
int m[4][4]={2,4,3,1,2,-1,5,-1,-1,2,4,2,1,-1,-1,0};
for (f=0;f<=3;f++)
for(c=0; c<=3; c++)
if(m[f][c]==-1)
{
printf("(%d, %d)\n", f,c);
break;}
if(f<4 && c<4)
printf("(%d, %d)\n", f,c);
}
/**************Fichero salir 3.c ********/
#include <stdio.h>
main()
{
int c,f ;
int m[4][4]={2,4,3,1,2,-1,5,-1,-1,2,4,2,1,-1,-1,0};
for (f=0;f<=3;f++)
for(c=0; c<=3; c++)
if(m[f][c]==-1)
printf("(%d, %d)\n", f,c);
if(f<4 && c<4)
printf("(%d, %d)\n", f,c);
76 5 Introducci´ on al lenguaje de programaci´ on C (III)
}
Objetivo de los tres programas anteriores:
1. salir.c: lee la matriz por filas y en cuanto encuentra un −1, nos da su
posici´on y acaba.
2. salir2.c: lee la matriz por filas. Cuando encuentra en una fila el valor
−1, nos da su posici´ on y salta de fila.
3. salir3.c: nos indica la posici´ on de todos los −1 de la matriz.
5.2. Funciones: anatom´ıa y uso
Las funciones dividen grandes trabajos de computaci´ on en partes m´ as
breves y aprovechan la labor realizada por otras personas en lugar de par-
tir de cero. Los programas escritos e C normalmente constan de una gran
cantidad de peque˜ nas funciones en lugar de pocas y grandes. Un programa
puede residir en uno o m´ as ficheros fuente en cualquier forma conveniente;
los archivos fuente pueden compilarse por separado y enlazarse junto con
funciones de biblioteca compiladas con antelaci´ on.
En cuanto a sintaxis, hay dos formatos para definir una funci´ on. La nueva
sintaxis conocida como forma prototipo viene a ser
/* Prototipo de definici´ on de una funci´ on*/
int func_def(int a, int b, int c);
{
.
.
que es equivalente en la forma tradicional; a
/* Forma tradicional de definici´ on de una funci´ on*/
int func_def( a, b, c)
int a,b,c;
{
.
.
Es decir en la nueva sintaxis, dentro del par´entesis que recoge la lista
de argumentos, aparecen a la vez sus declaraciones, mientras que tradicio-
5.3 Convenciones sobre el paso de argumentos 77
nalmente, dichas declaraciones aparec´ıan a continuaci´ on. Posteriormente, el
cuerpo de la funci´ on aparece entre llaves.
El nombre de la funci´ on deber´ a ir precedido de un tipo, si la funci´ on
devuelve un valor no entero. Si la funci´ on no devuelve ning´ un valor, se
especifica el tipo void.
Un programa es entonces un conjunto de definiciones de funciones. La
comunicaci´on entre las funciones se efect´ ua (en este caso) a trav´es de ar-
gumentos y valores devueltos por la funci´ on. Tambi´en se pueden realizar
mediante variables externas.
Las funciones aparecen en cualquier orden en el fichero fuente, y el fichero
fuente puede estar dividido en varios ficheros. Sin embargo, las funciones son
indivisibles.
La sentencia return es el mecanismo para devolver un valor desde la
funci´ on llamada a su llamador; return puede ir seguido de una expresi´ on.
return (expresi´on);
Tanto la expresi´ on, como los par´entesis de ´esta son opcionales. Una fun-
ci´on puede contener cualquier n´ umero de sentencias return. Si no hay sen-
tencia return, el control del programa vuelve al programa llamador, cuando
se alcanza la llave ¦ que cierra el cuerpo de la funci´ on. En este caso el valor
devuelto es indefinido.
float f()
{
float f2;
int a;
char c;
f2=a; /* OK, convierte a float */
return a; /* OK, convierte a float */
f2=c; /* OK, convierte a float */
return c; /* OK, convierte a float */
}
5.3. Convenciones sobre el paso de argumentos
Hemos visto en el apartado anterior, los aspectos relacionados con la
definici´ on de una funci´ on. A lo largo de un programa, se van utilizando
78 5 Introducci´ on al lenguaje de programaci´ on C (III)
funciones, mediante su llamada o alusi´ on.
Una alusi´on a una funci´ on es una declaraci´ on de una funci´ on que est´a de-
finida en cualquier sitio, generalmente en otro fichero distinto. El prop´ osito
general de una alusi´ on es decir al compilador qu´e tipo de valor devuelve la
funci´ on, y declarar el n´ umero y tipo de argumentos que toma dicha funci´ on.
Se considera por defecto que todas las funciones devuelven un valor entero.
No es necesario declarar funciones que devuelven valores enteros, sin em-
bargo es una buena costumbre declarar todas las funciones que van a ser
llamadas.
En general hay dos tipos de alusiones: prototipo y no-prototipo. Una
alusi´ on no-prototipo a una funci´ on declara el tipo de retorno de la funci´ on,
pero no el n´ umero o tipo de argumentos que emplea:
extern old_function();
Las alusiones prototipo permiten a˜ nadir el n´ umero y tipo de argumento
empleado:
extern int new_function(int j, float x);
Para declarar una funci´ on que no toma argumentos se emplea el tipo
void.
extern int f(void);
T´ıpicamente aparecen las declaraciones de las funciones en la cabecera
del programa, junto a las declaraciones de las variables y antes de la llamada
a la correspondiente funci´ on.
Una llamada a una funci´ on es la invocaci´ on a dicha funci´ on. En este
punto el programa pasa el control a la correspondiente funci´ on. La sintaxis
general es
nombre_funci´on ( argumento1, argumento2,...,argumentok);
En las llamadas de una funci´ on debe haber el mismo n´ umero de argu-
mentos que en su declaraci´on. En cuanto al tipo, puede haber conversiones
autom´ aticas, con la consiguiente falta de precisi´on. Si la conversi´ on no es
posible, el compilador manda mensajes de error. Por ejemplo:
5.3 Convenciones sobre el paso de argumentos 79
{
extern void f(int *);
float x;
f(x); /* ilegal, no se puede convertir un float a un
puntero*/
...
Sin embargo en:
{
extern void f(float, short);
double x;
long j;
f(j,x); /* se convierte un long a float y double a short */
...
A menos que en la declaraci´on de la funci´ on aparezca que el tipo de
retorno es void, las funciones siempre devuelven un valor de retorno, que es
sustitu´ıdo por la llamada a la funci´ on. Por ejemplo, si f() devuelve un 1, la
sentencia
a=f()/3;
es equivalente a
a=1/3;
En el lenguaje de programaci´ on C, la funci´ on llamada recibe una copia
temporal, privada, de cada argumento. Esto significa que la funci´ on no puede
alterar el valor del argumento original en la funci´ on que la llama. En una
funci´ on, cada argumento es una variable temporal inicializada con el valor
con el que se llam´ o la funci´ on.
Cuando aparece el nombre de un arreglo como argumento de una funci´ on,
se pasa la direcci´ on de comienzo del mismo; no se copian los elementos. La
funci´ on puede alterar el valor de elementos del arreglo subindexando a partir
de esta posici´on. Ello hace que el arreglo se pase por referencia. M´ as adelante
veremos el uso de apuntadores para alterar el valor de argumentos que no
sean arreglos.
80 5 Introducci´ on al lenguaje de programaci´ on C (III)
5.4. Clases de almacenamiento
Hemos comentado ya alg´ un aspecto sobre el ´ambito definici´ on y validez
de las variables. Una variable puede ser limitada a un bloque, a un fichero,
a una funci´ on o a una declaraci´ on de una funci´ on prototipo.
Hay cuatro tipos de ´ ambito de validez de las variables en C: local o
bloque, global o fichero, funci´ on y estructura.
Por defecto todas las variables llevan asociada una clase de almacena-
miento que determina su accesibilidad y existencia. La clase de almacena-
miento puede alterarse con los calificadores:
1. auto (almacenamiento autom´atico, de ´ambito local).
2. register (almacenamiento en un registro, de ´ ambito local).
3. static (local, global o funci´ on).
4. extern (global o funci´ on).
Variables declaradas a nivel externo (fuera de toda definici´on
de funci´ on)
Las funciones y variables externas que constituyen un programa en C
no tienen que ser compiladas todas a la vez. El texto fuente del programa
puede mantenerse en varios archivos, y puede enlazarse.
El ´ ambito de validez de una variable externa abarca desde el punto de
su declaraci´ on en un archivo fuente hasta el fin del archivo.
Se pueden utilizar los calificadores static o extern o ninguno. La declara-
ci´on extern es obligatoria si se ha de hacer referencia a una variable externa
antes de su definici´ on o si est´a definida en un archivo fuente que no es aquel
en el que se usa.
Es importante distinguir entre la declaraci´ on de una variable externa y
su definici´ on. Una declaraci´ on da a conocer las propiedades de una variable
(su tipo, tama˜ no, etc.); una definici´ on produce tambien una asignaci´ on de
memoria. Si las l´ıneas:
int sp;
double val[MAXVAL];
5.4 Clases de almacenamiento 81
aparecen fuera de cualquier funci´ on definen las variables externas sp y val,
obligan a asignar memoria y sirven de declaraci´on al resto del archivo. Las
l´ıneas:
extern int sp;
extern double val[];
declaran para el resto del archivo fuente que sp es un entero y val un arreglo
double (cuyo tama˜ no se fija en otro lugar), pero no crean las variables ni les
asignan memoria. S´olo debe de haber un definici´ on de una variable externa
entre todos los archivos que constituyen el programa fuente. Los otros archi-
vos deber´ an contener declaraciones extern para acceder a ´el. La declaraci´on
de una variable externa inicializa la variable a 0 por defecto, o a un valor
especificado. Si se utiliza el calificador static, s´olo se puede acceder a ella
desde el propio fichero fuente.
Variables declaradas a nivel interno (dentro de un bloque)
Pueden ser:
1. no declarada o auto: en este caso s´olo es visible dentro del bloque. Hay
que inicializarlas expl´ıcitamente.
2. extern a nivel interno, hace accesible la variable a m´ odulos a los cuales
no lo es.
3. static: las variables est´aticas constituyen la tercera clase de almacena-
miento adem´ as de las extern y las autom´aticas, ya tratadas. El califi-
cativo static se puede utilizar a nivel externo o interno. Las variables
internas est´aticas son locales a una funci´on en la misma forma que las
autom´ aticas, pero a diferencia de ´estas, su existencia es permanente,
en lugar de aparecer y desaparecer al activar la funci´ on. Se iniciali-
zan por defecto a 0. El almacenamiento est´atico, interno o externo, se
especifica al prefijar la declaraci´on normal con la palabra static. La
variable es externa si se define fuera de las funciones e interna si se
define dentro de una funci´ on.
4. register es la cuarta y ´ ultima clase de almacenamiento. Una decla-
raci´on register avisa al compilador de que la variable en cuesti´on
ser´a muy usada. Si es posible, la variable register ser´ a almacenada
en los registros de la m´aquina, lo que producir´ a programas cortos y
r´ apidos. Este tipo de almacenamiento es v´alido para los tipos int char
y punteros. La declaraci´on es del tipo:
82 5 Introducci´ on al lenguaje de programaci´ on C (III)
register int x;
register char c;
La parte int puede omitirse; register s´olo se aplica a variables autom´ati-
cas y a los par´ametros formales de una funci´ on. La declaraci´ on ser´a:
f(c,n)
register int c,n;
{
register int i;
...
}
S´ olo unas pocas variables en cada funci´ on se pueden mantener en re-
gistros. La declaraci´ on se ignora si hay declaraciones excesivas o no
permitidas. Una variable register ha de ser inicializada expl´ıcitamen-
te.
Ejemplo de uso
main()
{
extern int var1;
static int var2;
register int var3=0;
int var4=0;
var1+=2;
printf("%d %d %d %d\n", var1, var2, var3, var4);
funcion_1();
}
int var1=5;
funcion_1()
{
int var1=15;
static var2=5;
var2+=5;
printf("%d %d \n", var1, var2);
}
¿Qu´e valores se imprimen en el programa anterior?
Soluci´ on: 7 0 0 0
15 10
5.5 Recursi´ on 83
(Si se llamase otra vez a la funci´ on funcion 1, var2 entrar´ıa valiendo 10).
extern int var;
main()
{
var++;
printf("%d \n", var); /* escribe 6*/
funcion_1();
}
int var=5;
funcion_1()
{
var++;
printf("%d \n", var); /*escribe 7*/
funcion_2();
}
---------------------------
/* En otro fichero*/
extern int var;
funcion_2()
{
var++;
printf("%d \n"; var); /* escribe 8*/
}
5.5. Recursi´on
Una funci´ on recursiva es una funci´ on que se llama a s´ı misma. Por ejem-
plo:
printd(int n) /* imprime n en decimal (recursiva) */
{
int i;
if ( n < 0 ){
putchar (‘-’);
n=-n;
}
if(( i=n/10)!= 0)
printd(i);
putchar(n % 10+‘0’);
}
84 5 Introducci´ on al lenguaje de programaci´ on C (III)
Cuando una funci´ on se llama a s´ı misma recursivamente, cada invocaci´ on
crea una nueva copia de todas las variables autom´ aticas, que es independien-
te de la anterior. Por tanto, en printd(), la primera vez n=123. Pasa 12 a
la segunda printd. La segunda printd pasa 1 a la tercera. La tercera toma n
igual a 1, hace i = 0 y escribe 1. En la vuelta hacia arriba la segunda printd
escribe un 2 y la primera escribe un 3.
La recursividad generalmente no ahorra memoria pues ha de mantenerse
una pila con los valores que est´ an siendo procesados. Tampoco ser´a m´as
r´ apida, pero el c´ odigo recursivo es m´as compacto y a menudo m´as senci-
llo de escribir y comprender. Est´a especialmente pensado para estructuras
definidas recursivamente, como los ´ arboles, que veremos m´as adelante.
Nota: Trata de escribir la funci´ on anterior, sin emplear su recursividad.
5.6. Funciones definidas en stdio y math
En stdio.h se encuentran las funciones I/O. Dicho fichero contiene las
declaraciones prototipo de todas las funciones I/O. Algunas de ellas son:
printf()
#include <stdio.h>
int printf (const char *formato[,argumento 1,...],...);
Salida con formato. Escribe los datos formateados en la cadena de salida
standard (stdout). El primer argumento es un vector car´ acter que puede con-
tener texto y expresiones de control de formato. Los siguientes argumentos
representan los datos a ser escritos.
Ejemplo de uso
#include <stdio.h>
main()
{
int a,b,c;
a=20;b=350;c=1991;
printf ("\n Los resultados son:\n");
printf ("a=%6d\t b=%6d\t c=%6d\n",a,b,c);
}
5.6 Funciones definidas en stdio y math 85
Resultado:
Los resultados son:
a = 20 b = 350 c = 1991
scanf()
#include <stdio.h>
int scanf (const char *formato[,argumento 1,..]...);
Entrada con formato. Lee datos de stdin en la forma especificada por
un vector de formato. La sintaxis y sem´ antica de scanf() es la inversa de la
de printf(), donde el argumento representa un puntero a la variable que se
quiere leer. Vimos esta funci´on en el primer cap´ıtulo dedicado a C.
Ejemplo de uso
#include <stdio.h>
main()
{
int a;float b; char c;
scanf ("%d %f %c", &a,&b, &c);
}
Resultado:
Lee de la pantalla, por ejemplo: 5 23.4 b y lo almacena en las variables
correspondientes: a, b, c.
getchar()
#include <stdio.h>
int getchar (void);
Entrada de caracteres. La funci´ on getchar() lee del fichero est´andar de
pantalla (stdin) un car´ acter y avanza una posici´ on. Cuando llega al final del
fichero lee el car´acter EOF.
Ejemplo de uso
#include <stdio.h>
main()
{
86 5 Introducci´ on al lenguaje de programaci´ on C (III)
int car;
car=getchar();
}
Resultado:
Lee un car´ acter y lo almacena en la variable car.
putchar()
#include <stdio.h>
int putchar (int c);
Salida de caracteres. Escribe su argumento en la cadena de salida stan-
dard (stdout) y devuelve el car´ acter escrito. La expresi´ on putchar (c), es
equivalente a putc(c, stdout).
Ejemplo de uso
#include <stdio.h>
main()
{
int car=4;
putchar(car);
}
Resultado:
Escribe en pantalla el valor 4.
Funciones est´andar de entrada/salida. Manipulando ficheros
Permiten escribir y leer datos a, y desde ficheros y dispositivos. La E/S,
en el procesamiento de ficheros, se realiza a trav´es de un buffer o memoria
intermedia. Esta t´ecnica, implementada en software, hace las operaciones de
entrada y salida m´ as eficientes. Dependiendo de los datos que se deseen leer
o escribir se utilizan las siguientes funciones:
1. Datos le´ıdos o escritos car´acter a car´acter:
fgetc(), fputc()
2. Datos le´ıdos o escritos palabra a palabra (palabra m´ aquina=valor
int(normalmente 2 bytes):
5.6 Funciones definidas en stdio y math 87
fgetw(), fputw()
3. Datos le´ıdos o escritos como cadena de caracteres:
fgets(), fputs()
4. Datos le´ıdos o escritos con formato:
fscanf(), fprintf()
5. Datos de longitud fija (estructuras o arrays) le´ıdos como registros o
bloques:
fread(), fwrite()
88 5 Introducci´ on al lenguaje de programaci´ on C (III)
Abrir y cerrar ficheros
fopen()
#include <stdio.h>
FILE *fopen (const char *nombrefichero, const char *modo de
acceso);
Abre el fichero identificado por nombrefichero y asocia una cadena con
dicho fichero. El segundo argumento es un puntero a una cadena de carac-
teres que identifica el tipo de acceso al fichero.
fclose()
#include <stdio.h>
int fclose (FILE *cadena);
Cierra el fichero asociado con la cadena especificada. Dicha funci´ on de-
vuelve un cero, si termina satisfactoriamente y un elemento nocero (EOF)
si sucede alg´ un error.
Salidas y entradas con formato
fprintf()
#include <stdio.h>
int fprintf (FILE *pf, const char *formato[,arg]...);
Escribe sus argumentos (arg) en el fichero apuntado por pf, con el formato
especificado. La descripci´on del formato es la misma que en printf.
fscanf()
#include <stdio.h>
int fscanf (FILE *pf, const char *formato[,arg]...);
Lee sus argumentos (arg) del fichero apuntado por pf, con el formato
especificado. Cada formato debe ser un puntero a una variable en la que
queremos almacenar el valor le´ıdo.
Ejemplo de escritura en un fichero de nombre resul
5.6 Funciones definidas en stdio y math 89
/* Este fichero realiza el mismo calculo que primos.c, pero escribe*/
/* el resultado en un fichero*/
/* Calculo de numeros primos mediante la criba de Eratostenes*/
#include <stdio.h>
main()
{
FILE *presul;
presul = fopen ("resul", "w");
if(presul != NULL)
{
#define SIZE 1000
char flags[SIZE+1];
int i, k, primo, cuenta;
cuenta = 0;
for(i=0; i<=SIZE; i++) /* contador */
flags[i]=1; /* inicio de la tabla a 1 */
for(i=0; i<=SIZE; i++) {
if(flags[i]) { /*primo encontrado */
primo=i+i+3;
fprintf (presul, "%d\n" , primo);
for (k=i+primo; k<= SIZE; k+= primo)
flags[k]=0; /* quitar todos los multiplos */
cuenta++;
}
}
fprintf (presul, "Hay %d numeros primos\n ", cuenta);
}
}
Nota: Extrae el algoritmo del programa anterior.
Funciones definidas en math.h
En < math.h > se encuentran las funciones matem´aticas, divididas en
tres grupos: trigonom´etricas e hiperb´ olicas, exponenciales y logar´ıtmicas y
miscel´aneas. Todas ellas operan con valores en double.
90 5 Introducci´ on al lenguaje de programaci´ on C (III)
Dentro de las trigonom´etricas, se encuentran: acos(), asin(), atan(),
cos(), cosh(), sin(), sinh(), tan(), tanh().
Dentro de las exponenciales y logar´ıtmicas: exp(), log(), log10(), sqrt()
entre otras.
Por ´ ultimo dentro de las miscel´ aneas: fabs(), fmod(), son quiz´ as las m´as
utilizadas.
Cap´ıtulo 6
Algoritmos iterativos
6.1. Introducci´ on
Las ra´ıces de la ecuaci´on no lineal f(x) = 0 no pueden obtenerse ge-
neralmente de forma exacta. Por ello, si queremos resolver ecuaciones no
lineales, nos veremos obligados a utilizar m´etodos aproximados. Estos m´eto-
dos se basan en ideas como la aproximaci´ on sucesiva o la linearizaci´ on. Tales
m´etodos son iterativos, esto es, comienzan en una aproximaci´on inicial a la
ra´ız y producen una sucesi´ on x
0
, x
1
,..., x
n
,... que presumiblemente converge
a la ra´ız deseada.
En determinados m´etodos, es suficiente (en t´erminos de convergencia)
conocer un intervalo [a, b] en el que se encuentra la ra´ız buscada. Otros
m´etodos requieren de una aproximaci´ on inicial que est´e cerca de la ra´ız
deseada, ello hace que converjan m´as r´apidamente.
Por simplicidad, nos dedicaremos a estudiar el problema de determinar
una ra´ız real simple, p, de la ecuaci´ on f(x) = 0; es decir, supondremos que
p ∈ R y f

(p) ,= 0.
Al final del cap´ıtulo, describiremos someramente algunos procedimientos
para la resoluci´ on de sistemas de ecuaciones no lineales. Veremos que aunque
los m´etodos utilizados para la resoluci´on de ecuaciones no lineales son gene-
ralizables a la resoluci´on de sistemas de ecuaciones, todos ellos presuponen
el conocimiento de una buena aproximaci´ on de la ra´ız. Se sabe muy poco,
de c´omo resolver el problema si no se dispone de informaci´ on a priori de la
localizaci´ on de las ra´ıces.
Una forma de obtener un aproximaci´ on inicial a la ra´ız de una ecuaci´ on
91
92 6 Algoritmos iterativos
Figura 6.1: y = senx −
x
2
4
, x ∈ [
π
2
, 2]
1.6 1.7 1.8 1.9
-0.1
0.1
0.2
0.3
f(x) = 0, es mediante su representaci´on gr´ afica. Para ello, se pueden con-
siderar las abscisas de los puntos de intersecci´on de la gr´ afica de la funci´ on
y = f(x) con el eje X.
A veces es aconsejable sustituir la ecuaci´on dada por otra ecuaci´ on equi-
valente (es decir con las mismas ra´ıces), ϕ(x) = ψ(x), donde las funciones
ϕ y ψ son m´as sencillas que f. Se representan entonces las gr´aficas de las
funciones y = ϕ(x) e y = ψ(x), y las ra´ıces buscadas ser´an entonces las
abscisas de los puntos de intersecci´on de dichas gr´ aficas.
Ejemplo 6.1 Obtener una aproximaci´ on de la ra´ız real positiva que tiene
la ecuaci´on:
x
2
4
−senx = 0
Expresando la ecuaci´on de forma equivalente:
x
2
4
= senx. Si se repre-
sentan gr´ aficamente las curvas y =
x
2
4
e y = senx, se observa que una ra´ız
queda en el intervalo (
π
2
, 2), posiblemente cerca de x = 1,9.
Otra posibilidad es dar valores a la funci´ on f(x) y observar los cambios
de signo de los resultados obtenidos.
6.2 Resoluci´ on de ecuaciones 93
x
x
2
4
senx(calculado en radianes) f(x)
1.6 0.64 0.9996 ¡0
1.8 0.81 0.974 ¡0
2.0 1.0 0.909 ¿0
De esta forma se puede concluir que p ∈ (1,8, 2,0).
En general, un resultado existente en este sentido es el siguiente:
Teorema del Valor Intermedio Si una funci´on f(x) ∈ ([a, b] y k es
un n´ umero cualquiera entre f(a) y f(b), entonces existe c ∈ (a, b), tal que
f(c) = k.
Corolario Si una funci´on f(x) ∈ ([a, b] y f(a) f(b) < 0, la ecuaci´ on
f(x) = 0 tiene al menos una ra´ız en el intervalo (a, b). Es decir, existe al
menos un n´ umero p ∈ (a, b), tal que f(p) = 0.
La ra´ız p ser´a ´ unica si f

(x) existe y mantiene el signo en el intervalo
(a, b).
6.2. Resoluci´ on de ecuaciones
A lo largo de esta secci´on mostraremos distintos m´etodos de resoluci´on
de la ecuaci´ on f(x) = 0. El problema consiste en encontrar los valores de la
variable x que satisfacen la ecuaci´ on f(x) = 0, para una funci´ on f dada.
El primer m´etodo basado en el Teorema del Valor Intermedio, se deno-
mina m´etodo de bisecci´ on, de b´ usqueda binaria o m´etodo de Bolzano.
6.2.1. M´etodo de bisecci´ on
Sea f(x) una funci´ on continua en el intervalo (a
0
, b
0
) tal que f(a
0
)f(b
0
) <
0. Determinaremos entonces, una secuencia de intervalos (a
1
, b
1
) ⊃ (a
2
, b
2
) ⊃
(a
3
, b
3
)..., tales que todos ellos contendr´an en su interior una ra´ız de la ecua-
ci´on f(x) = 0. Supongamos en particular que f(a
0
) < 0 y f(b
0
) > 0 (´esto
no supone ninguna limitaci´ on puesto que en otro caso se podr´ıa considerar
−f(x) = 0). Los intervalos I
k
= (a
k
, b
k
), k = 1, 2, ... son determinados de
manera recursiva de la siguiente forma. El punto medio del intervalo I
k−1
es:
m
k
=
1
2
(a
k−1
+b
k−1
)
94 6 Algoritmos iterativos
Podemos suponer que f(m
k
) ,= 0, puesto que en otro caso, tendr´ıamos
determinada la ra´ız de la ecuaci´ on. Computamos f(m
k
) y tenemos:
(a
k
, b
k
) =
_
(m
k
, b
k
), si f(m
k
) < 0
(a
k
, m
k
), si f(m
k
) > 0
De la construcci´ on de (a
k
, b
k
) se sigue inmediatamente que f(a
k
) < 0
y f(b
k
) > 0, y adem´ as cada intervalo I
k
contiene una ra´ız de la ecuaci´ on
f(x) = 0.
Despu´es de n pasos, tenemos la ra´ız dentro del intervalo (a
n
, b
n
) de lon-
gitud:
b
n
−a
n
=
1
2
(b
n−1
−a
n−1
) =
1
2
2
(b
n−2
−a
n−2
) = ... =
1
2
n
(b
0
−a
0
)
Tenemos adem´as que m
n+1
es un estimador de la ra´ız que buscamos, y
p = m
n+1
±d
n
, d
n
=
1
2
n+1
(b
0
−a
0
)
6.2 Resoluci´ on de ecuaciones 95
Algoritmo
Tolerancia = TOL. N´ umero de iteraciones= NUMITE. Valores
iniciales a
0
, b
0
.
Paso 1: Sea i = 1
Paso 2: Mientras que i ≤ NUMITE, hacer:
Paso 3: m
i
= a
i−1
+
1
2
(b
i−1
−a
i−1
)
Paso 4: Si f(m
i
) = 0, ´ o
¸
¸
¸
b
i−1
−a
i−1
2
¸
¸
¸ < TOL, STOP. La ra´ız
buscada es m
i
.
Paso 5: Si f(m
i
) < 0, a
i
= m
i
, b
i
= b
i−1
.
Si f(m
i
) > 0, a
i
= a
i−1
, b
i
= m
i
.
Hacer i = i + 1, e ir al Paso 3.
Paso 6: SALIDA. Despu´es de NUMITE iteraciones el proceso
ha terminado sin ´exito.
Ejemplo 6.2 El m´etodo de bisecci´ on aplicado a la ecuaci´ on
x
2
4
−senx = 0
con I
0
= (1,5, 2), genera la secuencia de intervalos:
k a
k−1
b
k−1
m
k
f(m
k
)
1 1.5 2 1.75 ¡0
2 1.75 2 1.875 ¡0
3 1.875 2 1.9375 ¿0
4 1.875 1.9375 1.90625 ¡0
5 1.90625 1.9375
6.2.2. M´etodo de Newton-Raphson
La idea b´ asica del m´etodo de Newton Raphson para la resoluci´ on de una
ecuaci´ on f(x) = 0, ha sido descrita en la introducci´ on del tema. A partir
de una aproximaci´ on inicial x
0
, se computa una secuencia x
1
, x
2
, ..., x
n
, ...
donde x
n+1
se determina como explicamos a continuaci´on:
Sea f(x) una funci´ on continuamente diferenciable dos veces en el inter-
valo [a, b], es decir f ∈ (
2
[a, b]. Sea x
0
una aproximaci´ on de la ra´ız p, tal que
f

(x
0
) ,= 0 y [x
0
−p[ peque˜ no. Si consideramos el desarrollo de Taylor hasta
grado dos, alrededor de x
0
:
f(x) = f(x
0
) + (x −x
0
)f

(x
0
) +
(x −x
0
)
2
2
f

(ψ(x)),
96 6 Algoritmos iterativos
donde ψ(x) est´a entre x y x
0
. Como f(p) = 0:
0 = f(x
0
) + (p −x
0
)f

(x
0
) +
(p −x
0
)
2
2
f

(ψ(p)), (6.1)
El m´etodo de Newton Rapson se deriva suponiendo que el t´ermino cua-
dr´ atico de la anterior expresi´ on es pr´ acticamente nulo y que:
0 ≈ f(x
0
) + (x −x
0
)f

(x
0
), (6.2)
Es decir, la funci´ on f(x) es aproximada por su tangente en el punto
(x
n
, f(x
n
)), y x
n+1
es la abscisa del punto de intersecci´ on de la tangente
con el eje X. Luego para determinar x
n+1
tenemos que resolver la siguiente
ecuaci´ on:
f(x
n
) + (x
n+1
−x
n
)f

(x
n
) = 0 (6.3)
El m´etodo de Newton-Raphson es definido por la siguiente f´ ormula ite-
rativa:
x
n+1
= x
n
+h
n
, h
n
=
−f(x
n
)
f

(x
n
)
(6.4)
Algoritmo
Tolerancia = TOL. N´ umero de iteraciones= NUMITE. Valor
inicial x
0
.
Paso 1: Sea i = 1.
Paso 2: Mientras que i ≤ NUMITE, hacer:
Paso 3: x
i
= x
0

f(x
0
)
f

(x
0
)
Paso 4: Si [x
i
−x
0
[ < TOL, STOP. La ra´ız buscada es x
i
.
Paso 5: Hacer x
0
= x
i
, i = i + 1, e ir al Paso 3.
Paso 6: SALIDA. Despu´es de NUMITE iteraciones el proceso
ha terminado sin ´exito.
Ejemplo 6.3 Dada f(x) = senx −
x
2
4
, f

(x) = cosx −
x
2
. Queremos
determinar una ra´ız positiva con cinco decimales correctos, i.e. TOL = 0,5
10
−5
. Dado el Ejemplo 2, tomamos, x
0
= 1,5.
6.2 Resoluci´ on de ecuaciones 97
i x
i
f(x
i
) f

(x
i
) h(x
i
)
0 1.5 0.434995 -0.67926 0.64039
1 2.14039 -0.303197 -1.60948 -0.18838
2 1.95201 -0.024372 -1.34805 -0.01826
3 1.93393 -0.000233 -0.00018 0.00018
4 1.93375 0.000005 -1.32191 < 0,5 10
−5
La ra´ız buscada es entonces p = 1,93375. S´ olo han sido necesarias 4 ite-
raciones, incluso teniendo en cuenta que la aproximaci´ on inicial es bastante
mala. Vemos tambi´en que [h
i
[ decrece m´as y m´as r´apidamente hasta que los
errores de redondeo empiezan a dominar. Sin embargo, cuando uno est´ a lejos
de la ra´ız, se puede evitar trabajar con tantos decimales. En el ejemplo ante-
rior, es ´ unicamente necesario trabajar con f(x
n
) con tantos decimales como
deber´ıan ser correctos en x
n+1
. En este caso, la derivada ha sido calculada
con una innecesaria precisi´ on. Entonces no es necesario calcular f

(x
n
) con
tanta precisi´ on como f(x
n
). Como la precisi´ on de f(x
n
) es la que nos dice
c´omo x
n
se aproxima a la ra´ız, se podr´ıa utilizar f

(x
2
) en las iteraciones
i = 3 ´ o 4.
6.2.3. M´etodo de la secante
El m´etodo de Newton Raphson es una t´ecnica extremadamente poderosa,
pero tiene una dificultad grande: la necesidad de conocer el valor de la
derivada de f en cada aproximaci´on de la ra´ız. Habitualmente la expresi´on
de f

(x) es todav´ıa m´as complicada y necesita m´as operaciones aritm´eticas
para su c´ alculo que f(x).
El m´etodo de la secante trata de evitar este problema, aproximando el
valor de la derivada f

(x
n
) por el cociente incremental
f(xn)−f(x
n−1
)
xn−x
n−1
. Esto ge-
nera el siguiente m´etodo. Calcular, a partir de dos aproximaciones iniciales,
x
0
y x
1
, la secuencia x
2
, x
3
, ..., a partir de la f´ ormula iterativa:
x
n+1
= x
n
+h
n
, h
n
= −f(x
n
)
x
n
−x
n−1
f(x
n
) −f(x
n−1
)
, f(x
n
) ,= f(x
n−1
)
La interpretaci´on geom´etrica de este m´etodo es que x
n+1
se calcula como
la abscisa del punto de intersecci´ on entre la secante a trav´es de (x
n−1
, f(x
n−1
))
y (x
n
, f(x
n
)) y el eje de las X. Se˜ nalar que el m´etodo de la secante a diferen-
cia del m´etodo de Newton Raphson requiere de dos aproximaciones iniciales,
pero s´olo se requiere de una evaluaci´on de la funci´ on en cada paso.
Los valores iniciales x
0
y x
1
, son los extremos del intervalo de referencia
en el que se encuentra la ra´ız buscada, de manera que se cumple: f(x
0
)
98 6 Algoritmos iterativos
f(x
1
) < 0. Como en el m´etodo de bisecci´ on consideraremos, por simplicidad
que f(x
0
) < 0 y f(x
1
) > 0. En caso de suceder lo contrario resolveremos la
ecuaci´ on −f(x) = 0.
Nota: La ecuaci´ on de la secante que pasa por (x
n−1
, f(x
n−1
)) y por
(x
n
, f(x
n
)) es:
x −x
n−1
x
n
−x
n−1
=
y −f(x
n−1
)
f(x
n
) −f(x
n−1
)
En el punto de intersecci´ on con el eje X, y = 0, de donde se deduce la
f´ ormula iterativa anterior.
Algoritmo
Tolerancia = TOL. N´ umero de iteraciones= NUMITE. Valores
iniciales x
0
y x
1
.
Paso 1: Sea i = 2.
Paso 2: Mientras que i ≤ NUMITE, hacer:
Paso 3: x
i
= x
i−1
−f(x
i−1
)
(x
i−1
−x
i−2
)
(f(x
i−1
)−f(x
i−2
))
Paso 4: Si [x
i
−x
i−1
[ < TOL, STOP. La ra´ız buscada es x
i
.
Paso 5: Hacer i = i + 1, e ir al Paso 3.
Paso 6: SALIDA. Despu´es de NUMITE iteraciones el proceso
ha terminado sin ´exito.
Ejemplo 6.4 Tomamos de nuevo la ecuaci´on f(x) = senx−
x
2
4
. Quere-
mos determinar una ra´ız positiva con cinco decimales correctos, i.e. TOL =
0,5 10
−5
. Tomamos, x
0
= 1, x
1
= 2. f(1) = 0,5915 > 0, f(2) = −0,0907 <
0. Por lo tanto trabajaremos con g(x) = −f(x) =
x
2
4
−senx.
i x
i
g(x
i
) h(x
i
)
0 1 -0.59147
1 2 0.090703
2 1.86704 -0.084980 -0.13296
3 1.93135 -0.003177 0.06431
4 1.93384 0.000114 0.00249
5 1.93375 -0.000005 -0.00009
6 1.93375
Este ejemplo, proporciona la misma precisi´on con el mismo n´ umero de
iteraciones en el m´etodo de Newton-Raphson que en el de la secante. Esto
6.2 Resoluci´ on de ecuaciones 99
puede ser debido a que una de las aproximaciones x
1
, estaba muy cerca de
la ra´ız. Cuando [x
n
−x
n−1
[ es peque˜ no, el cociente
(xn−x
n−1
)
f(xn)−f(x
n−1
)
ser´a deter-
minado en general con una pobre precisi´ on relativa. Si por ejemplo, elegimos
las aproximaciones iniciales x
0
, x
1
muy cerca de p, los errores de redondeo
pueden hacer que [x
n
− p[ sea grande. El an´ alisis de errores que haremos
posteriormente nos dir´ a que el m´etodo de la secante proporciona en general
una secuencia tal que [x
n
−x
n−1
[ >> [x
n
−p[.
Una an´ alisis m´as minucioso muestra que la contribuci´ on dominante al
error relativo de h
n
proviene del error cometido en el c´ alculo de f(x
n
), una
pobre precisi´ on en el otro factor, resulta ser de menor importancia.
Se˜ nalar, sin embargo, que la ecuaci´ on de recurrencia anterior puede ser
expresada de la forma:
x
n+1
=
x
n−1
f(x
n
) −x
n
f(x
n−1
)
f(x
n
) −f(x
n−1
)
, f(x
n
) ,= f(x
n−1
)
de manera que se puede obtener una cancelaci´ on cuando x
n
≈ x
n−1
y
f(x
n
)f(x
n−1
) > 0.
El m´etodo de Newton-Raphson o el de la secante se utilizan frecuente-
mente para refinar las respuestas obtenidas mediante otras t´ecnicas, como
el m´etodo de bisecci´ on. El motivo es que dan convergencia r´ apida, pero
necesitan una buena primera aproximaci´ on.
6.2.4. Regula Falsi
El m´etodo de regula falsi o de la falsa posici´ on entra dentro de los m´eto-
dos de interpolaci´ on, muy ´ utiles a la hora de determinar los ceros de una
funci´ on real f(x) cualquiera. A diferencia del m´etodo de Newton-Raphson,
en los m´etodos de interpolaci´ on no se necesita calcular la derivada de f, y
adem´as convergen m´as r´apidamente.
En este sentido, se puede considerar tambi´en como una variante del
m´etodo de la secante. En el m´etodo de regula falsi, se elige en cada iteraci´on
la secante entre (x
n
, f(x
n
)) y (x

n
, f(x

n
)), donde n

es el mayor ´ındice, n

< n,
para el cual f(x
n
)f(x

n
) < 0.
Las aproximaciones iniciales x
0
y x
1
, deben ser elegidas tambi´en, de
manera que f(x
0
) f(x
1
) < 0. La ventaja del m´etodo de regula falsi, al igual
que el de bisecci´ on, es que es siempre convergente para funciones continuas
f(x). En contraste con el m´etodo de la secante, sin embargo, el m´etodo
100 6 Algoritmos iterativos
de regula falsi es un m´etodo de primer orden (convergencia lineal). Esto lo
probaremos m´as adelante.
El m´etodo de regula falsi es considerado un buen m´etodo siempre que no
se utilice en un entorno muy pr´ oximo de la ra´ız. Puede ser utilizado como
parte de un m´etodo h´ıbrido que tendr´ıa buenas propiedades cerca de la ra´ız.
El planteamiento es similar al del m´etodo de bisecci´ on donde a partir
de un intervalo inicial [a
0
, b
0
] = [x
0
, x
1
], (f(x
0
) f(x
1
) < 0), se determina
en cada iteraci´ on un intervalo [a
n
, b
n
], de forma que f(a
n
) f(b
n
) < 0. El
intervalo [a
n
, b
n
] contiene entonces al menos un cero de f(x), y los valores
a
n
se determinan de manera que convergen hacia uno de estos ceros. En
general se sigue la siguiente f´ormula iterativa:
x
n
= a
n
−f(a
n
)
b
n
−a
n
f(b
n
) −f(a
n
)
= (6.5)
=
b
n
f(a
n
) −a
n
f(b
n
)
f(a
n
) −f(b
n
)
(6.6)
El hecho de que f(a
n
) f(b
n
) < 0, hace que f(a
n
) f(b
n
) ,= 0, luego x
n
est´a bien definido. Adem´ as, satisface a
n
< x
n
< b
n
´o b
n
< x
n
< a
n
. En este
caso, a menos que f(x
n
) = 0, se define:
a
n+1
= x
n
, b
n+1
= b
n
, si f(x
n
) f(a
n
) > 0
a
n+1
= a
n
, b
n+1
= x
n
, si f(x
n
) f(a
n
) < 0
El algoritmo termina cuando f(x
n
) = 0.
Ejercicio 1
Intenta especificar el algoritmo para el m´etodo de regula falsi, y probarlo
con la ecuaci´ on dada en los Ejemplos 1,2,3 y 4 anteriores. Comenta los
resultados que obtienes.
6.3. Comparaci´ on de los distintos ´ ordenes de con-
vergencia
6.3.1. Convergencia del m´etodo de bisecci´ on
El m´etodo de bisecci´ on, aunque conceptualmente claro, tiene inconve-
nientes importantes. Converge muy lentamente, (es decir NUMITE puede
6.3 Comparaci´on de los distintos ´ordenes de convergencia 101
ser muy grande antes de que [x
n
− p[ sea suficientemente peque˜ no) y, m´ as
a´ un, una buena aproximaci´ on intermedia puede ser desechada sin que nos
demos cuenta. Sin embargo, no debemos olvidar una propiedad importante
del m´etodo, y es que se trata de un m´etodo que converge siempre a una
soluci´ on.
Definici´on 1 Diremos que la sucesi´ on ¦α
n
¦

n=1
converge a α con rapidez
de convergencia O(β
n
), donde ¦β
n
¦

n=1
es otra sucesi´ on con β
n
,= 0 para cada
n, si

n
−α[

n
[
≤ K, para n suficientemente grande
donde K es una constante independiente de n. Esto se indica escribiendo
α
n
= α +O(β
n
), o bien α
n
→ α con una rapidez O(β
n
).
Teorema 2 Sea f ∈ ([a, b], tal que f(a) f(b) < 0. El procedimiento de
bisecci´ on genera una sucesi´ on x
n
que aproxima a p con la propiedad
[x
n
−p[ ≤
1
2
n
(b −a), n ≥ 1. (6.7)
Demostraci´on: para cada n ≥ 1, tenemos
b
n
−a
n
=
1
2
n−1
(b −a), y p ∈ (a, b)
Ya que x
n
=
1
2
(a
n
+b
n
), para todo n, se sigue que
[x
n
−p[ = [
1
2
(a
n
+b
n
) −p[ ≤ [
1
2
(a
n
+b
n
) −a
n
[ =
1
2
(b
n
−a
n
) = 2
−n
(b −a).•
De acuerdo con la definici´ on de rapidez de convergencia, la desigualdad
(6.7) implica que ¦x
n
¦

n=1
converge a p y est´a acotada por una sucesi´ on que
converge a 0 con una rapidez de convergencia O(2
−n
). Es importante hacer
notar que teoremas como ´este, ´ unicamente acotan superiormente los errores,
que en la pr´ actica pueden ser muy inferiores.
Ejemplo 6.5 Determinar aproximadamente el n´ umero de iteraciones
necesarias para resolver la ecuaci´ on f(x) = x
3
+ 4x
2
− 10 = 0 con una
precisi´ on de 10
−5
, tomando como intervalo inicial [a, b] = [1, 2].
Se trata de encontrar n tal que:
[x
n
−p[ = 10
−5

1
2
n
(b −a) = 2
−n
(6.8)
102 6 Algoritmos iterativos
Tomando logaritmos (en base diez, pues la tolerancia est´a dada en base 10),
tenemos que
log
10
(10
−5
) = −5 ≤ log
10
(2
−n
)
−nlog
10
2 ≥ −5 ⇒ n ≤
5
log
10
2
≈ 16,6
Seg´ un la expresi´ on anterior se requieren como mucho 16 iteraciones para
obtener una aproximaci´ on con precisi´on 10
−5
.
Otra caracter´ıstica a se˜ nalar de este m´etodo es que su velocidad de con-
vergencia es completamente independiente de la ecuaci´on a resolver.
6.3.2. Convergencia del m´etodo de Newton-Raphson
Definici´on 2 Sea ¦x
n
¦

n=0
una sucesi´ on que converge a p y e
n
= x
n
−p,
para cada n ≥ 0. Si existen dos n´ umeros positivos λ y α tales que
lim
n→∞
[x
n+1
−p[
[x
n
−p[
α
= lim
n→∞
[e
n+1
[
[e
n
[
α
= λ
entonces se dice que ¦x
n
¦

n=0
converge a p con orden α, con una constante
de error asint´otico λ, o que la convergencia es de orden α.
Si α = 1, el m´etodo se denomina lineal o de primer orden. Si α = 2, el
m´etodo se denomina cuadr´ atico o de segundo orden.
Teorema 3 Sea f ∈ (
2
[a, b]. Si f(a) f(b) < 0, y f

(x) y f

(x) son
no nulas y conservan el signo para a ≤ x ≤ b, entonces, a partir de una
aproximaci´on inicial x
0
∈ [a, b] que satisface f(x
0
) f

(x
0
) > 0, es posible
utilizando el m´etodo de Newton (f´ormula (6.2)), calcular una ra´ız ´ unica, p,
de la ecuaci´ on f(x) = 0 con cualquier grado de precisi´ on.
Por esta raz´on, al aplicar el m´etodo de Newton-Raphson, se debe aplicar
la siguiente regla: para el punto inicial x
0
el´ıjase el extremo del intervalo
(a, b) asociado con una ordenada del mismo signo que el de f

(x
0
).
Teorema 4 Sea f ∈ ((−∞, ∞), f(a) f(b) < 0, f

(x) ,= 0 para a ≤ x ≤
b. Si f

(x) existe en cualquier punto y conserva el signo, entonces puede
tomarse cualquier valor c ∈ [a, b] como valor inicial x
0
al utilizar el m´etodo
de Newton para hallar una ra´ız de la ecuaci´ on f(x) = 0, que caiga en el
intervalo [a, b]. Se puede tomar, por ejemplo, x
0
= a ´o x
0
= b.
6.3 Comparaci´on de los distintos ´ordenes de convergencia 103
N´otese que en la f´ ormula iterativa (6.3) est´ a claro que cuanto mayor sea
el valor num´erico de f

(x) en un entorno de la ra´ız, tanto menor ser´a la
correcci´on que ha de a˜ nadirse a la aproximaci´ on n-´esima para obtener la
aproximaci´on (n+1). El m´etodo de Newton, es por lo tanto, muy conveniente
cuando la gr´ afica de la funci´ on tiene una gran pendiente en un entorno de la
ra´ız dada, pero si el valor num´erico de la derivada es peque˜ no, las correciones
ser´an entonces mayores, y calcular la ra´ız mediante este procedimiento puede
ser un proceso largo o a veces imposible. Es decir: no utilices el m´etodo de
Newton para resolver una ecuaci´ on f(x) = 0, si la curva y = f(x) es casi
horizontal en un entorno del punto de intersecci´ on con el eje X.
La obtenci´ on de la f´ ormula iterativa del m´etodo de Newton, a partir de la
serie de Taylor, resalta la importancia de una buena aproximaci´ on inicial. La
hip´ otesis necesaria para pasar de (6.1) a (6.2) es que el t´ermino que contiene
a (p − x
0
)
2
puede ser eliminado. Esto, no se cumple a menos que x
0
sea
una buena aproximaci´ on de p. El siguiente resultado ilustra la importancia
te´orica de la elecci´on de x
0
.
Teorema 5 Sea f ∈ ([a, b]. Si p ∈ [a, b] es tal que f(p) = 0, f

(p) ,= 0,
entonces existe un δ > 0, tal que, el m´etodo de Newton-Raphson genera
una sucesi´ on ¦x
n
¦

n=1
que converge a p, para cualquier aproximaci´ on inicial
x
0
∈ [p −δ, p +δ].
Bajo las anteriores condiciones, vamos a obtener una f´ ormula que rela-
cione los errores absolutos de dos aproximaciones sucesivas. Obtendremos
entonces una relaci´on entre e
n
= x
n
− p y e
n+1
. Utilizando el desarrollo de
Taylor, en un entorno de la ra´ız:
0 = f(p) = f(x
n
) +f

(x
n
)(p −x
n
) +
1
2
f


n
)(p −x
n
)
2
, (6.9)
donde ψ
n
, est´a entre p y x
n
. Dividiendo por f

(x
n
) y despejando p:
p = x
n

f(x
n
)
f

(x
n
)

1
2

f


n
)
f

(x
n
)
(p −x
n
)
2
, (6.10)
y teniendo en cuenta la expresi´on (6.4), tenemos
p −x
n+1
= −
1
2

f


n
)
f

(x
n
)
(p −x
n
)
2
, (6.11)
Entonces
e
n+1
e
2
n
=
1
2

f


n
)
f

(x
n
)
(6.12)
104 6 Algoritmos iterativos
y cuando x
n
→ p,
e
n+1
e
2
n

1
2

f

(p)
f

(p)
(6.13)
Como e
n+1
es proporcional al cuadrado de e
n
, y de acuerdo con la De-
finici´ on 2, se dice que el m´etodo de Newton-Raphson tiene convergencia
cuadr´ atica, o que es un m´etodo de segundo orden.
Un razonamiento intuitivo que demuestra el teorema anterior es el si-
guiente:
Supongamos que I es un intervalo en torno a p, tal que
1
2

[f

(y)[
[f

(x)[
≤ m, ∀x, y ∈ I (6.14)
Si x
n
∈ I de (6.12), se tiene que [e
n+1
[ ≤ me
2
n
⇔ [me
n+1
[ ≤ (me
n
)
2
.
Supongamos que [me
0
[ < 1 y que el intervalo [p −[e
0
[, p +[e
0
[] ⊆ I. Por
inducci´ on podemos ver que x
n
∈ I ∀n y [e
n
[ ≤
1
m
(me
0
)
2
n
.
Por este motivo, puede probarse que el m´etodo de Newton-Raphson siem-
pre converge (a una ra´ız simple) si x
0
ha sido elegido lo suficientemente cerca
de la ra´ız p. (Si [me
0
[ = m[x
0
−p[ < 1).
Sin embargo en la pr´ actica p es desconocido y la condici´ on anterior es
dif´ıcil de comprobar. El teorema que damos a continuaci´ on da un criterio
m´as pr´ actico a la hora de demostrar la convergencia.
Teorema 6 Sea x
0
una aproximaci´ on inicial y definimos x
n
y h
n
tales
que x
n+1
= x
n
+ h
n
, h
n
=
−f(xn)
f

(xn)
. Sea I
0
el intervalo (x
0
, x
0
+ 2h
0
) y
supongamos que
2[h
0
[M ≤ [f

(x
0
)[, M = m´ax
x
0
∈I
0
[f

(x)[ (6.15)
Entonces, x
n
∈ I
0
, n = 1, 2, ..., y lim
n→∞
x
n
= p, donde p es la ´ unica
ra´ız de f(x) = 0 en I
0
.
Otro criterio que en ocasiones es m´as f´ acil de aplicar, viene dado por el
siguiente resultado.
Teorema 7 Supongamos que f

(x) ,= 0 y f

(x) no cambia de signo en
6.3 Comparaci´on de los distintos ´ordenes de convergencia 105
el intervalo [a, b], y que f(a) f(b) < 0. Si
¸
¸
¸
¸
f(a)
f

(a)
¸
¸
¸
¸
< b −a,
¸
¸
¸
¸
f(b)
f

(b)
¸
¸
¸
¸
< b −a (6.16)
entonces el m´etodo de Newton-Raphson converge a partir de una aproxima-
ci´ on inicial x
0
∈ [a, b].
6.3.3. Convergencia del m´etodo de la secante
Para probar la convergencia del proceso, consideraremos que la ra´ız
est´a separada y la segunda derivada f

(x) tiene signo constante en el in-
tervalo [a, b] = [x
0
, x
1
].
Supongamos que f

(x) > 0 para a ≤ x ≤ b. La curva y = f(x) en este
caso ser´a convexa hacia abajo y por lo tanto estar´ a localizada por debajo
de la secante que pasa por (a, f(a)), (b, f(b)). Si hemos supuesto en la des-
cripci´ on del m´etodo, f(a) < 0, el extremo b est´a fijo y las aproximaciones
sucesivas:
x
0
= a, x
n+1
= x
n
−f(x
n
)
(b −x
n
)
f(b) −f(x
n
)
, n = 0, 1, 2... (6.17)
forman una secuencia mon´ otona creciente y acotada:
x
0
< x
1
< ... < x
n
< x
n+1
< ... < p < b (6.18)
Resumiendo:
1. El extremo fijo es aquel para el cual el signo de la funci´ on f(x) coincide
con el signo de su segunda derivada f

(x).
2. Las aproximaciones sucesivas caen en el lado de la ra´ız p, donde el
signo de la ra´ız es opuesto al signo de su segunda derivada f

(x).
En cualquier caso, cada aproximaci´ on x
n+1
est´a m´as pr´ oxima a la ra´ız
p, que la precedente x
n
.
Supongamos que p = lim
n→∞
x
n
, a < p < b. Dicho l´ımite existe, puesto
que la sucesi´ on es mon´otona y est´ a acotada. Tomando l´ımites en la expresi´on
(6.17),
p = p −f(p)
(b −p)
f(b) −f(p)
, (6.19)
106 6 Algoritmos iterativos
de donde f(p) = 0. Como p es la ´ unica ra´ız de f(x) = 0 en el intervalo (a, b),
se deduce que p = p.
Se trata de un m´etodo que no converge globalmente. Al igual que en el
caso del m´etodo de Newton, tenemos convergencia local.
El siguiente resultado nos permite obtener el orden de convergencia.
Teorema 8 (de interpolaci´on) Sea f ∈ ([a, b], p, x
n−1
, x
n
, x
n+1

[a, b], tales que:
1. f

(x) ,= 0 ∀x ∈ [a, b]
2. f(p) = 0
3. x
n+1
= x
n
−f(x
n
)
xn−x
n−1
f(xn)−f(x
n−1
)
Entonces ∃ψ, ν ∈ (a, b) (ψ, ν entre p, x
n−1
, x
n
, x
n+1
), tal que:
(p −x
n+1
) =
f

(ψ)
2f

(ν)
(p −x
n
)(p −x
n−1
). (6.20)
Tenemos entonces que, para n suficientemente grande, bajo las hip´ otesis
del teorema anterior:
[e
n+1
[ ≈ C[e
n
[[e
n−1
[, C =
|f

(ψ)|
2|f

(ν)|
Intentamos determinar el orden de convergencia, haciendo uso de la si-
guiente conjetura:
[e
n+1
[ ≈ K[e
n
[
a
, [e
n
[ ≈ K[e
n−1
[
a
, ¿a?
Sustituyendo en la ecuaci´ on anterior:
K[e
n
[
a
≈ C[e
n
[K

1
a
[e
n
[
1
a
Esta relaci´on se mantiene s´olo si:
a = 1 +
1
a
⇔ a =
1
2
(1 ±

5)
C = K
1+
1
a
= K
a
La ra´ız a =
1
2
(1−

5) = −0,618 puede ser ignorada, pues [e
n+1
[ ≈ K[e
n
[
a
y en dicho caso el error cometido en la iteraci´ on n+1 ser´ıa superior al error
en la iteraci´ on n (o lo que es lo mismo el m´etodo no ser´ıa convergente).
6.3 Comparaci´on de los distintos ´ordenes de convergencia 107
Por lo tanto [e
n+1
[ ≈ C
1
a
[e
n
[
a
, a =
1
2
(1 +

5) = 1,618.
6.3.4. Convergencia del m´etodo de regula falsi
Para probar la convergencia del m´etodo de regula falsi, supondremos por
simplicidad que f

(x) existe y que para alg´ un valor i,
a
i
< b
i
, f(a
i
) < 0, f(b
i
) > 0 (6.21)
f

(x) ≥ 0, ∀x ∈ [a
i
, b
i
] (6.22)
Con ´estas hip´ otesis, ´o f(x
i+1
) = 0 ´ o f(x
i+1
) f(a
i
) > 0, y entonces
a
i
< a
i+1
= x
i
< b
i+1
= b
i
.
Adem´as es f´acil ver que las f´ ormulas (6.21), (6.22) son v´alidas para todo
i ≥ i
0
si lo son para i
0
. Entonces, b
i
= b, para i ≥ i
0
y las a
i
forman una
sucesi´on mon´ otona creciente y acotada, por lo tanto es convergente, es decir
existe p = lim
n→∞
a
i
. Por el hecho de ser f continua y por (6.21) y (6.22),
se tiene
f(b) > 0, f(p) ≤ 0 (6.23)
Adem´as tomando l´ımites en (6.6)
p =
bf(p) −pf(b)
f(p) −f(b)
(6.24)
que implica (p −b)f(p) = 0.
Pero p ,= b, y entonces f(p) = 0.
En el caso particular que estamos analizando, (6.22), se ve claramente
que las sucesivas iteraciones matienen fijo el extremo b, de manera que,
utilizando ´esto en la misma relaci´on obtenida para el m´etodo de la secante:
lim
n→∞
[e
n+1
[
[e
n
[
= C

(6.25)
puesto que:
p −x
n+1
=
f

(ψ)
2f

(ψ)
(p −x
n
)(p −x
n−1
) (6.26)
108 6 Algoritmos iterativos
Si x
n
= b;
[e
n+1
[
[e
n
[
→ [
f

(ψ)
2f

(ψ)
(p −b)[ = C

(6.27)
Entonces α = 1 y tenemos convergencia lineal. El m´etodo de regula falsi,
es en general un m´etodo de primer orden. Es convergente siempre que f sea
continua.
6.4. Teor´ıa general de los m´etodos iterativos. Ite-
raci´ on del punto fijo
El m´etodo de Newton-Raphson y el de la secante pueden ser conside-
rados como casos especiales de un m´etodo iterativo m´ as general. Sea x
n+1
determinado por el valor de una funci´ on y de su derivada en m puntos
x
n
, x
n−1
, ..., x
n−m+1
y sea
x
n+1
= ϕ(x
n
, x
n−1
, ..., x
n−m+1
)
Llamaremos a ϕ funci´ on de iteraci´on.
Ejemplo 6.6 En el m´etodo de Newton-Raphson:
ϕ(x) = x −
f(x)
f

(x)
, m = 1
En el de la secante:
ϕ(x) = x −f(x)
x −y
f(x) −f(y)
, m = 2
La teor´ıa general de los procesos iterativos es m´as simple cuando m = 1.
En este caso tenemos:
x
n+1
= ϕ(x
n
)
que se denomina m´etodo de iteraci´on uni-punto o del punto fijo. Nos dedi-
caremos a continuaci´ on al estudio de este tipo de procesos.
Supongamos ahora que tenemos una sucesi´ on ¦x
n
¦ generada a partir
de cierto valor inicial x
0
, y que lim
n→∞
x
n
= p. Si ϕ es continua, p =
6.4 Teor´ıa general de los m´etodos iterativos. Iteraci´on del punto fijo 109
lim
n→∞
x
n+1
= lim
n→∞
ϕ(x
n
) = ϕ(p); es decir el valor p es una ra´ız de la
ecuaci´ on x = ϕ(x).
Es decir, para construir un m´etodo iterativo para la resoluci´ on de f(x) =
0, podemos intentar escribir dicha expresi´ on de la forma x = ϕ(x), lo cual
define un m´etodo de iteraci´on: x
n+1
= ϕ(x
n
).
Ejemplo 6.7 Sea la ecuaci´ on: x
3
−x−5 = 0 que puede ser escrita como
x = ϕ
1
(x) = x
3
−5; x = ϕ
2
(x) =
3

x + 5; x = ϕ
3
(x) =
5
x
2
−1
es decir el m´etodo iterativo puede no ser ´ unico.
Tampoco podemos asegurar que todos ellos sean convergentes. Una con-
dici´ on suficiente para la convergencia la da el siguiente resultado:
Teorema 9 Supongamos que la ecuaci´ on x = ϕ(x) tiene una ra´ız p, y
que en el intervalo
J = ¦x : [x −p[ ≤ ρ¦
ϕ

(x) existe y satisface la condici´ on

(x)[ ≤ m < 1
Entonces, para todo x
0
∈ J:
1. x
n
∈ J, n = 0, 1, 2, 3....
2. lim
n→∞
x
n
= p.
3. p es la ´ unica ra´ız en J de x = ϕ(x).
Demostraci´on:
1. Por inducci´ on: sea x
0
∈ J y supongamos que x
n−1
∈ J. Por el teorema
del valor medio,
x
n
−p = ϕ(x
n−1
) −ϕ(p) = ϕ

(ψ)(x
n−1
−p), ψ ∈ J
[x
n
−p[ ≤ m[x
n−1
−p[ ≤ mρ < ρ ⇒ x
n
∈ J
110 6 Algoritmos iterativos
2. Utilizando la desigualdad anterior repetidas veces:
[x
n
−p[ ≤ m[x
n−1
−p[ ≤ ... ≤ m
n
[x
0
−p[
Como m < 1, se tiene el resultado.
3. Supongamos finalmente que x = ϕ(x) tiene otra ra´ız, β, en J. β ,= p.
Entonces:
p −β = ϕ(p) −ϕ(β) = ϕ

(ψ)(p −β), ψ ∈ J
[p −β[ ≤ m[p −β[ < [p −β[,
lo cual es absurdo.•
En el Teorema 9, hemos partido de suponer la existencia de una ra´ız p.
El teorema puede ser modificado y utilizado para probar la existencia de
una ra´ız de la ecuaci´ on x = ϕ(x).
Teorema 10 Sea J un intervalo cerrado en el cual ϕ

(x) existe y sa-
tisface la desigualdad [ϕ

(x)[ ≤ m < 1. Entonces la sucesi´ on ¦x
n
¦ definida
como x
n+1
= ϕ(x
n
), x
0
∈ J satisface los apartados 1,2 y 3 del Teorema 9 si
x
1
±
m
1 −m
[x
1
−x
0
[ ∈ J
El m´etodo iterativo x
n+1
= ϕ(x
n
) es en general un m´etodo de primer
orden, a menos que ϕ(x) sea elegida de manera especial. Si ϕ(x) es α veces
diferenciable y sus derivadas continuas ϕ ∈ (
α
, en un entorno de p, donde
p = ϕ(p), y ϕ
(j)
(p) = 0, j = 1, ..., α − 1, ϕ
(α)
(p) ,= 0. De acuerdo con el
teorema de Taylor tenemos:
x
n+1
= ϕ(x
n
) = p +
1
α!
ϕ
(α)
(ψ)(x
n
−p)
α
, ψ ∈ (x
n
, p)
Si lim
n→∞
x
n
= p,
lim
n→∞
[e
n+1
[
[e
n
[
α
=
1
α!

(α)
(p)[ , = 0,
n
= x
n
−p (6.28)
Luego el m´etodo de iteraci´on es de orden α para p.
6.5 Generalizaci´ on a sistemas de ecuaciones 111
Este argumento da una demostraci´ on alternativa del hecho de que el
m´etodo de Newton-Raphson sea al menos de segundo orden para ra´ıces
simples, ya que:
ϕ(x) = x −
f(x)
f

(x)
ϕ

(x) =
f(x) f

(x)
(f

(x))
2
= 1 −
[(f

(x))
2
−f(x) f

(x)]
(f

(x))
2
Si p es una ra´ız simple, f

(p) ,= 0 ⇒ ϕ

(p) = 0
6.5. Generalizaci´ on a sistemas de ecuaciones
Algunos de los m´etodos que hemos visto para resolver ecuaciones no li-
neales simples, pueden ser generalizados a sistemas de ecuaciones no lineales.
Consideremos un sistema general de n ecuaciones no lineales con n
inc´ ognitas.
f
i
(x
1
, ..., x
n
) = 0, i = 1, ..., n
Un m´etodo de iteraci´on uni-punto para la resoluci´ on de este sistema
puede ser constru´ıdo escribiendo el sistema de la forma:
x
i
= ϕ
i
(x
1
, ..., x
n
), i = 1, ..., n
que sugiere el m´etodo iterativo:
x
(k+1)
i
= ϕ
i
(x
(k)
1
, ..., x
(k)
n
), i = 1, ..., n
La similitud formal al caso n = 1 es m´as f´ acil de ver si utilizamos notaci´on
vectorial.
x = (x
1
, ..., x
n
)
t
, ϕ(x) = (ϕ
1
(x), ..., ϕ
n
(x))
t
El m´etodo iterativo puede entonces describirse como:
x
(k+1)
= ϕ(x
(k)
), k = 0, 1, ...
112 6 Algoritmos iterativos
Un criterio de convergencia similar al descrito en el Teorema 9 puede
utilizarse en este caso.
Supongamos que p = ϕ(p) y que las derivadas parciales
d
ij
(x) =
∂ϕ
i
∂x
j
(x), 1 ≤ i, j ≤ n
existen para x ∈ R, R = ¦x : [[x − p[[ < ρ¦. Sea D(x) una matriz nxn
cuyos elementos son d
ij
(x). En este caso una condici´ on suficiente para que
el m´etodo de iteraci´on anterior converja para cada x
0
∈ R es que para alguna
elecci´on de la norma se cumpla
[[D(x)[[ ≤ m < 1, x ∈ R (6.29)
Nota: Normas de vectores:
L
p
: [[x[[
p
= ([x
1
[
p
+... +[x
n
[
p
)
1
p
, 1 ≤ p ≤ ∞
p = 2 (norma eucl´ıdea) [[x[[
2
= ([x
1
[
2
+... +[x
n
[
2
)
1
2
p = ∞ [[x[[

= m´ax
i=1,...,n
[x
i
[
Norma matricial: [[A[[

= m´ax
i=1,..,n

n
j=1
[a
ij
[
Definici´on 3 Se dice que ϕ es una funci´ on de contracci´on cuando [[D(x)[[ ≤
m < 1, x ∈ R, puesto que
en este caso dicha condici´on implica la desigualdad
[[ϕ(x) −ϕ(y)[[ ≤ m[[x −y[[, ∀x, y ∈ R
Una condici´ on necesaria para que el proceso iterativo anterior converja
es que el radio espectral de D(p) sea menor o igual que 1. La velocidad de
convergencia depende linealmente de m y tenemos
[[x
(k+1)
−p[[ = [[ϕ(x
(k)
) −ϕ(p)[[ ≤ m[[x
(k)
−p[[, k = 0, 1, ...
Nota: En algunas aplicaciones nos puede interesar resolver un
sistema de la forma
x = a +hϕ(x)
6.5 Generalizaci´ on a sistemas de ecuaciones 113
donde a es un vector constante y h es un par´ ametro 0 < h << 1.
Si ϕ(x) tiene derivadas parciales acotadas, entonces el criterio
de convergencia anterior, (6.29), se satisface siempre para h su-
ficientemente peque˜ no y el m´etodo de iteraci´on es:
x
(k+1)
= a +hϕ(x
(k)
), k = 0, 1, ...
6.5.1. M´etodo de Newton-Raphson
Para n = 1 hemos deducido este m´etodo a partir de la f´ ormula de Taylor:
f(x) = f(x
k
) + (x −x
k
)f

(x
k
) +O([x −x
k
[
2
)
Si nos olvidamos del ´ ultimo t´ermino, obtenemos para f(x) = 0 el m´etodo
iterativo:
f(x
k
) + (x
k+1
−x
k
)f

(x
k
) = 0, k = 0, 1, ...
Utilizando la f´ ormula de Taylor en dimensi´ on n:
f(x) = f(x
(k)
) +f

(x
(k)
)(x −x
(k)
) +O([[x −x
(k)
[[
2
)
donde f

(x) es una matriz nxn, el jacobiano de f, denotado en ocasiones
por J, cuyos elementos son
f

ij
(x) =
∂f
i
∂x
j
(x), 1 ≤ i, j ≤ n
De esta forma el m´etodo de Newton-Raphson en dimensi´ on n es:
f(x
(k)
) + (x
(k+1)
−x
(k)
)f

(x
(k)
) = 0, k = 0, 1, ...
Este es un sistema de ecuaciones lineal para x
(k+1)
y si f

(x
(k)
) es no
singular puede ser resuelto como veremos m´as adelante.
El m´etodo de Newton en dimensi´ on n es de segundo orden, es decir existe
una constante C = C(ρ), tal que, ∀x
(k)
, [[x
(k)
−p[[ ≤ ρ, y se tiene
[[x
(k+1)
−p[[ ≤ C[[x
(k)
−p[[
2
Desigualdad que se sigue directamente de:
x
(k+1)
= f(x
(k)
) = p +
1
2!
f
(2)
(ψ)[[x
(k)
−p[[
2
, ψ ∈ (x
(k)
, p)
114 6 Algoritmos iterativos
De aqui se puede demostrar su convergencia supuesto que x
(0)
est´e lo
suficientemente cerca de p.
Como hemos visto cada iteraci´on del m´etodo de Newton-Raphson re-
quiere la resoluci´ on de un sistema de ecuaciones lineales el cual, cuando n es
grande puede ser dif´ıcil de resolver. Adem´as en cada paso hay que calcular
los n
2
elementos de f

(x
(k)
). Esto es imposible de hacer a menos que dichos
elementos tengan una forma funcional sencilla.
Existen m´etodos derivados de ´este, que proponen estimar f

(x), como el
m´etodo de la secante que lo aproxima por un cociente de diferencias. Una
aproximaci´on utilizada en le pr´ actica es:
∂f
i
∂x
j
(x) ≈ ∆
ij
(x, h) =
f
i
(x +h
j
e
j
) −f
i
(x)
h
j
donde e
j
= (0, ..., j, ..,0) y h es un par´ ametro n-dimensional con componentes
h
j
,= 0, j = 1, ..., n.
6.5.2. M´etodo de Gauss-Seidel
Otro m´etodo de resoluci´ on de un sistema no lineal f(x) = 0 consiste en
utilizar la i-´esima ecuaci´on a resolver para obtener x
(k+1)
i
, i = 1, 2, ..., n. De
esta forma en cada iteraci´on se utiliza la ecuaci´ on no lineal de dimensi´ on
uno:
f
i
(x
(k+1)
1
, ..., x
(k+1)
i−1
, x
i
, x
(k)
i+1
, ..., x
(k)
n
) = 0
para obtener x
(k+1)
i
.
Ejemplo de aplicaci´on Sea un sistema no lineal de dimensi´on n = 3.
f
1
(x
1
, x
2
, x
3
) = 0
f
2
(x
1
, x
2
, x
3
) = 0
f
3
(x
1
, x
2
, x
3
) = 0
En k = 0:
i = 1: f
1
(x
1
, x
(0)
2
, x
(0)
3
) = 0 → x
(1)
1
i = 2: f
2
(x
(1)
1
, x
2
, x
(0)
3
) = 0 → x
(1)
2
i = 3: f
3
(x
(1)
1
, x
(1)
2
, x
3
) = 0 → x
(1)
3
6.6 Ejercicios de aplicaci´ on 115
En k = 1:
i = 1: f
1
(x
1
, x
(1)
2
, x
(1)
3
) = 0 → x
(2)
1
i = 2: f
2
(x
(2)
1
, x
2
, x
(1)
3
) = 0 → x
(2)
2
i = 3: f
3
(x
(2)
1
, x
(2)
2
, x
3
) = 0 → x
(2)
3

6.6. Ejercicios de aplicaci´ on
Como aplicaci´on de los m´etodos anteriores, puedes intentar resolver las
siguientes ecuaciones:
1. x = tg(x) en [1, 2].
2. x
3
−x + 1 = 0 en [−2, 2].
3. x −senx −0,25 = 0 en [1, 2].
4. e
−3
= xe
−x
en [0, 1].
5. (1 +x)senx −1 = 0 en [0,5, 1].
6. senx −x + 2 = 0 en [2, 3].
Si tienes muchas inquietudes, intenta resolver el sistema:
x = 1 +h
2
(e
y

x
+ 3x
2
)
y = 0,5 +h
2
tang(e
x
+y
2
)
x
0
= 1, y
0
= 0,5
Prueba con valores de h = 0,1 ´o 0.01.
116 6 Algoritmos iterativos
Cap´ıtulo 7
M´etodos de ordenaci´ on
7.1. An´alisis te´ orico: orden de complejidad ´optimo
alcanzable para algoritmos internos
Definici´on 1
Un orden parcial en un conjunto S es una relaci´on R, tal que para cada
a, b, c en S:
1. aRa es verdad (R es reflexiva).
2. aRb y bRa implican a = b (antisim´etrica).
3. aRb y bRc implican aRc (transitiva).
La relaci´on R = ” ≤ ” entre enteros y la relaci´on R = ” ⊆ ” entre
conjuntos son dos ´ ordenes parciales.
Un orden total o lineal en un conjunto S es un orden parcial en el que
adem´as se cumple que para cada dos elementos a, b ∈ S o bien aRb o bRa.
La relaci´on ≤ sobre enteros es un orden total (lineal), ⊆ sobre conjuntos no
lo es.
El problema de la ordenaci´ on puede ser planteado como sigue. Dada una
sucesi´on de n elementos a
1
, ..., a
n
elegidos de un conjunto con orden total,
que usualmente denotaremos por ≤, vamos a encontrar una permutaci´ on π
de esos n elementos que describir´a la secuencia dada en forma no decreciente
a
π(1)
, ..., a
π(n)
, tal que a
π(i)
≤ a
π(i+1)
, para 1 ≤ i ≤ n − 1. Usualmente
daremos la secuencia ordenada en lugar de la permutaci´ on de ordenaci´ on π.
117
118 7 M´etodos de ordenaci´on
1 [tnpos=r]a < b 2 [tnpos=r]b < c 4 [tnpos=r]Orden:a b c
5 [tnpos=r]a < c 8 [tnpos=r]Orden: a c b 9 [tnpos=r]Orden:
c a b 3 [tnpos=r]a < c 6 [tnpos=r]Orden: b a c
7 [tnpos=r]b < c 10 [tnpos=r]Orden: b c a 11 [tnpos=r]Orden: c b a
Figura (a)
Los m´etodos de ordenaci´ on son clasificados como internos, donde los
datos residen en la propia memoria del ordenador, o externos (los datos est´ an
principalmente fuera de la memoria, almacenados en mecanismos como cd’s
o cintas).
Otra clasificaci´ on posible surge atendiendo a la estructura de los ele-
mentos que van a ser ordenados. La diferencia est´ a en si los elementos son
n´ umeros enteros con un rango fijo, o por el contrario son elementos de los
que no se conoce nada de su estructura en particular. Esta segunda clase de
algoritmos tiene como operaci´on b´ asica la comparaci´on entre distintos pares
de elementos. Con algoritmos de esta naturaleza veremos que se necesitan
al menos nlogn comparaciones para ordenar una secuencia de n elementos.
Arboles de decisi´on
Vamos a considerar un algoritmo en el que en cada paso se realiza una
bifurcaci´ on (ramificaci´ on en dos ramas) depeniendo de una comparaci´ on
realizada entre dos elementos. La representaci´on usual para un algoritmo
de este tipo es un ´arbol binario denominado ´ arbol de decisi´ on. Cada v´ertice
interior representa una decisi´ on. Es test que representa el nodo ra´ız es el
primero en realizarse, y luego el control pasa a uno de sus hijos, dependiendo
de la respuesta. En general, el control contin´ ua pasando de cada v´ertice a
uno de sus hijos, la elecci´ on en cada caso depende del resultado del test,
hasta que se llega a un final de rama (hoja). El resultado del algoritmo se
obtiene cuando se alcanza dicho nodo (final de rama).
Ejemplo 1 Vamos a ilustrar mediante un ´ arbol de decisi´ on el algoritmo
de ordenaci´on de tres elementos a, b y c en orden alfab´etico. Los tests est´an
indicados a la derecha del un c´ırculo que respresenta el nodo correspondiente.
El control se mueve hacia la izquierda si la respuesta es ”si”, y hacia la
derecha si la respuesta es ”no”.
La complejidad computacional (en cuanto a tiempo), de un ´ arbol de de-
7.1 An´alisis te´ orico: orden de complejidad ´ optimo alcanzable para algoritmos internos119
cisi´on es la altura del ´ arbol, como una funci´ on del tama˜ no del problema.
Normalmente deseamos contar el m´aximo n´ umero de comparaciones nece-
sarias para encontrar el camino de la ra´ız a un v´ertice de abandono (n´ umero
bifurcaciones). Es de destacar que el n´ umero total de v´ertices en el ´arbol
puede ser mucho mayor que su altura. En cualquier caso, podemos probar
un buen resultado acerca de la altura de un ´ arbol de decisi´on que ordena n
elementos.
Lema 1 Un ´arbol binario de altura h tiene a lo sumo 2
h
hojas.
Demostraci´on: Por inducci´ on sobre h. Se necesita simplemente obser-
var que un ´ arbol binario de altura h est´a compuesto por una ra´ız y a lo
sumo dos sub´ arboles de altura a lo m´ as h −1.
Teorema 2 Cualquier ´ arbol de decisi´ on que orden n elementos tiene de
altura al menos log
2
n!.
Demostraci´on: Como el resultado de la ordenaci´ on de n elementos
puede ser una de las n! permutaciones de los datos, debe haber al menos
n! hojas en el ´ arbol de decisi´ on. Por el Lema 1, la altura debe ser al menos
log
2
n!.
Corolario 1 Cualquier algoritmo que ordene mediante comparaciones
requiere al menos cnlogn comparaciones para ordenar n elementos, para
c > 0 y n suficientemente grande.
Demostraci´on: Para n > 1:
n! ≥ n(n −1)(n −2)...
_
¸
n
2
|
_

_
n
2
_n
2
de manera que
logn! ≥
n
2
log
_
n
2
_

n
4
logn para n ≥ 4
De la aproximaci´on de Stirling podemos obtener una aproximaci´ on m´ as
precisa de n! como
_
n
e
_
n
, de manera que n(log
2
n −log
2
e) = nlog
2
n −1,44n,
resulta ser una buena aproximaci´ on inferior del n´ umero de comparaciones
necesarias para ordenar n elementos.
120 7 M´etodos de ordenaci´on
7.2. M´etodos simples de complejidad O(n
2
): com-
paraci´ on, inserci´ on, burbuja
7.2.1. M´etodo de comparaci´ on (selecci´ on)
Es uno de los m´etodos de ordenaci´ on m´ as simples y trabaja de la si-
guiente forma:
1. Encontrar el elemento m´as grande del vector.
2. Intercambiarlo con el elemento de la ´ ultima posici´ on.
3. Encontrar el segundo mayor elemento.
4. Intercambiarlo con el elemento de la ante´ ultima posici´ on.
5. Continuar con dicho proceso hasta obtener el vector ordenado.
Tambi´en se denomina algoritmo de selecci´on porque trabaja repetida-
mente seleccionando el actual mayor elemento.
Algorithm selectsort
for index := n to 2, −1, do
largest := index
for loop := index − 1 to 1, −1, do
if k(loop) > k(largest) then largest := loop endif
enddo
//intercambio de posiciones de los dos elementos//
temp := k(largest)
k(largest) := k(index)
k(index) := temp
enddo
Para estimar el n´ umero de comparaciones es importante se˜ nalar que el
algoritmo se reduce a dos ciclos que est´an anidados y la comparaci´ on se
realiza una vez en cada paso del ciclo interior. El ciclo interior tiene j − 1
pasos por cada paso j en el ciclo exterior y hay n − 1 pasos en el ciclo
exterior, luego el n´ umero de comparaciones requerido por el algoritmo es:
(n −1) + (n −2) +... + (2) + (1) =
n(n −1)
2
7.2 M´etodos simples de complejidad O(n
2
): comparaci´ on, inserci´ on, burbuja121
Este n´ umero es constante sea cual sea la ordenaci´on inicial de los ele-
mentos.
En el algoritmo, el n´ umero de veces que los elementos cambian sus po-
siciones est´a dado por el n´ umero de veces S
n
, que se ejecuta la sentencia
largest := loop. Este n´ umero S
n
, depende de la ordenaci´ on inicial de los
elementos y puede describirse por sus valores m´ınimo, m´ aximo y medio.
Si el vector inicial est´a ordenado, dicha sentencia no se ejecuta nunca y
S
n,min
= 0. Si el vector inicial est´a ordenado en el orden decreciente, en
cada paso del ciclo exterior se intercambian dos elementos y la longitud del
vector desordenado es reducida en uno en cada paso. Esto nos da el m´aximo
valor para S
n,max
, que es:
(n −1) + (n −3) +... + 1 =
_
n
2
4
si n es par
(n
2
−1)
4
si n es impar
Para determinar el n´ umero medio de elementos intercambiados hay que
se˜ nalar que:
1. Si el algoritmo comienza con una permutaci´ on aleatoria de 1, 2, ..., n,
entonces el primer paso en el ciclo externo genera una permutaci´on
aleatoria de 1, ..., n−1 seguida por n, ya que la permutaci´ on q
1
, ..., q
n−1
, n
es producida por cada uno de los datos.
n q
2
q
3
q
n−1
q
n
q
1
n q
3
q
n−1
q
n

q
1
q
2
q
3
q
n−1
n
2. La ocurrencia de cada permutaci´ on de 1, 2, ..., n−1 en k(1), k(2), ..., k(n−
1) es igualmente probable y el n´ umero medio de veces que se ejecuta
la sentencia largest := loop durante el primer paso del ciclo externo
viene dada por H
n
−1 =
1
2
+
1
3
+... +
1
n
.
En general, el valor medio de S
n
, S
n
satisface la relaci´on de recurrencia
S
n
= H
n
−1 +S
n−1
, de manera que
S
n
=
n

j=2
H
j
−(n −1) = (n + 1)H
n
−2n
Nota: Hemos utilizado el resultado

n
j=1
H
j
= (n+1)H
n
−n, H
1
= 1,
que puede ser probado por inducci´ on.
122 7 M´etodos de ordenaci´on
En resumen, el n´ umero de comparaciones entre elementos es
n(n −1)
2
= O(n
2
)
y el n´ umero de intercambios es:
min = 0
media = (n + 1)H
n
− 2n = O(nlogn) (Notar que H
n
≈ log
2
n, para n
grande)
max =
_
n
2
4
si n es par
(n
2
−1)
4
si n es impar
= O(n
2
)
La complejidad total del algoritmo es entonces O(n
2
) y queda determi-
nada por el n´ umero de operaciones.
7.2.2. M´etodo de inserci´ on
Los m´etodos de ordenaci´ on por inserci´ on est´ an basados en la siguiente
idea:
Sea un vector ordenado de n elementos distintos
k(1) < k(2) < ... < k(n)
Queremos insertar un elemento X en el vector anterior compar´andolo
con los elementos del vector y situ´andolo en su posici´ on moviendo despu´es
los elementos posteriores un lugar a la derecha. Un algoritmo que realiza
´esto se denomina algoritmo de inserci´on.
Si queremos aplicar un algoritmo de este tipo a la ordenaci´on de un
vector de n elementos, tendremos que ir considerando elemento a elemento
y situ´ andolo en su propio lugar en un vector ya ordenado.
Un posible algoritmo ser´ıa:
Algorithm insertsort
k(0) := minvalue
for i := 2 to n, do
pivot := k(i)
7.2 M´etodos simples de complejidad O(n
2
): comparaci´ on, inserci´ on, burbuja123
j := i
while k(j − 1) > pivot do
k(j) := k(j − 1)
j := j − 1
enddo
k(j) := pivot
enddo
El algoritmo anterior requiere
n(n−1)
2
movimientos de elementos en el
peor caso. Esta alta complejidad es debida a que cada pasada del ciclo interno
produce ´ unicamente intercambios entre elementos adyacentes. Esto produce
muchos movimientos redundantes de elementos, porque, si por ejemplo, el
elemento m´as peque˜ no est´a situado en el extremo contrario del vector hacen
falta n pasos para moverle a la posici´on que le corresponde.
Una modificaci´ on de este algoritmo es el que se denomina Shellsort. (Ver
por ejemplo p´ ag. 231 y ss. de [9]).
7.2.3. M´etodo de intercambio (burbuja)
Otra familia de algoritmos de ordenaci´ on por comparaci´ on est´a basada
en la idea de que cada comparaci´ on entre dos elementos debe ser seguida
sistem´aticamente por el intercambio de pares de elementos desordenados,
hasta que no aparezcan mas pares a ordenar.
El algoritmo trabaja de la siguiente manera: dada una secuencia de
elementos desordenados k(1), ..., k(n), generalmente no todos distintos; el
m´etodo de la burbuja los ordena intercambiando elementos adyacentes, si es
necesario, en sucesivas pasadas. Cuando no es necesario ning´ un intercambio
m´as, la secuencia est´a ordenada.
Algorithm bubblesort
repeat
pivot := 1
for k := 2 to n do
if A(k − 1) > A(k) then
temp := A(k − 1)
A(k − 1) := A(k)
A(k) := temp
pivot := k − 1
endif
enddo
124 7 M´etodos de ordenaci´on
until pivot = 1
Ejemplo 2Vamos a ver como ordena la secuencia 3 4 2 1 7
3 4 2
3 2 4 1
3 2 1 4 7 pasada 1, pivot=4
3 2
2 3 1
2 1 3 4 7 pasada 2, pivot=3
2 1
1 2 3 4 7 pasada 3, pivot=1
Hay tres cantidades involucradas en el tiempo de desarrollo del m´etodo
de la burbuja: el n´ umero de comparaciones, el n´ umero de intercambios y
el n´ umero de pasadas a trav´es de los datos. Para estimar dichas cantida-
des podemos ver que el algoritmo se ha implementado utilizando dos ciclos
anidados. Un paso del ciclo exterior corresponde a una pasada a trav´es del
vector inicial, de manera que el n´ umero de pasadas corresponde al n´ umero
de pasos ejecutados en el ciclo exterior.
Si los elementos est´an ordenados, la pasada inicial ser´ a tambi´en la ´ ultima
y no har´ an falta intercambios de elementos. Entonces el valor m´ınimo para
el n´ umero de comparaciones, el n´ umero de intercambios y el n´ umero de
pasadas son n −1, 0 y 1 respectivamente.
Si los datos iniciales est´an en orden inverso, entonces en la primera pasa-
da se colocar´ a el elemento m´as grande en su propia posici´ on, y se utilizar´ an
n − 1 comparaciones y n − 1 intercambios. La segunda pasada colocar´ a el
segundo mayor elemento en su posici´on y utilizar´a para ello n −2 compara-
ciones y n−2 intercambios. Continuando con este argumento, concluiremos
que este m´etodo requiere de
n(n−1)
2
comparaciones e intercambios y n pasa-
das.
El an´ alisis de valores del n´ umero medio de comparaciones, pasadas e
intercambios no es f´acil de realizar. El motivo es que dicho c´ alculo requiere
de una interesante herramienta matem´ atica como son las tablas de inversi´on.
(Ver por ejemplo p´ ag. 220 y 223 de [9]). En cualquier caso podemos concluir
que la complejidad del m´etodo de la burbuja en media no es mejor que
O(n
2
).
7.3 M´etodos de complejidad O(nlogn): Quicksort y Heapsort 125
23 [tnpos=r]A(1) 19 [tnpos=r]A(2) 11 [tnpos=r]A(4) 1 [tnpos=r]A(8) 2 [tnpos=r]A(9)
7 [tnpos=r]A(5) 4 [tnpos=r]A(10) 6 [tnpos=r]A(11)
10 [tnpos=r]A(3) 5 [tnpos=r]A(6)
3 [tnpos=r]A(7)
Figura (b)
7.3. M´etodos de complejidad O(nlogn): Quicksort
y Heapsort
7.3.1. Heapsort
Es un elegante m´etodo de ordenaci´ on que utiliza la idea de una repeti-
da selecci´on de elementos. Fue inventado por Williams y Floyd (1964). El
proceso consiste en dos etapas: los primeros n pasos construyen un vector
con estructura de ”mont´ on”(o pila) y los siguientes n extraen los elementos
en orden decreciente y construyen la secuencia ordenada final, situada de
derecha a izquierda en un vector. En la etapa de creaci´ on del mont´ on, la
informaci´ on relativa al orden de los elementos que es obtenido despu´es de
cada comparaci´ on es implementada en forma de estructura de datos amon-
tonados. Veamos esto con el siguiente ejemplo.
Consideremos un vector A(1 : n). Asociamos a este vector una estructura
de ´ arbol binario. Un ´ arbol binario puede representarse gr´ aficamente de dife-
rentes formas. Normalmente se representa como si creciese hacia abajo, y el
nodo m´ as alto, A(1), se denomina ra´ız. Una forma usual de numerar todos
los nodos es de izquierda a derecha en cada nivel, de manera que si A(k) es
el nodo ra´ız de un sub´ arbol, A(2k) y A(2k+1) son los dos nodos inmediatos
colocados detr´ as de ´el, que son llamados nodos hijos. Inversamente A(k) es
el nodo padre de A(2k) y A(2k + 1).
Definimos la ra´ız de un ´ arbol como el nodo que est´ a en el nivel 1. Si A(k)
est´a en el nivel i, A(2k) y A(2k +1) est´an en el nivel i +1. Un nodo que no
tiene hijos se denomina nodo terminal (hoja), y si no es un nodo terminal, es
un nodo interno o de bifurcaci´ on. El n´ umero de nodos que hay que recorrer
desde la ra´ız hasta el nodo A(j), se denomina longitud del camino hasta
A(j). El nodo ra´ız es un nodo de longitud 1, sus hijos est´ an a una longitud
2 y as´ı sucesivamente. En general un nodo de nivel i tiene un camino de
longitud i hasta la ra´ız.
126 7 M´etodos de ordenaci´on
Definici´on 2
Una pila o mont´on es un ´ arbol binario con nodos A(1) a A(n) inclusive,
el cual tiene todos sus nodos terminales al mismo nivel, o en dos niveles
adyacentes a lo sumo; y tiene la propiedad de que el valor de un nudo padre
es mayor o igual que los valores de los hijos.
A(k) ≥ max(A(2k), A(2k + 1)), para 1 ≤ k ≤ ¸
n
2
|, n ≥ 2
La secuencia de elementos en el camino de la ra´ız a un nodo terminal
est´a linealmente ordenada. Por ejemplo: 23 > 19 > 11 > 1. El elemento m´as
grande en un sub´ arbol es siempre la ra´ız de ese ´arbol. El algoritmo heapsort
funciona de la siguiente forma: dado un vector desordenado de tama˜ no n:
A(1 : n), el algoritmo en una primera parte construye la pila o mont´ on y
luego sistem´aticamente selecciona la ra´ız actual y situa dicho elemento en
la ´ ultima posici´ on del vector A.
Algorithm createheap A(1 : n)
for r :=
n
2
to 1, −1 do
k := r
10 if A(k) < max(A(2k), A(2k + 1)) then
index :=´ındice del mayor elemento de A(2k), A(2k + 1) (si s´olo
hay un hijo, el ´ındice del mayor elemento es el de ese hijo)
temp := A(k)
A(k) := A(index)
A(index) := temp
if(index ≤
n
2
) (index no es una hoja)
k := index
goto 10
endif
endif
enddo
Ejemplo 3
7.3 M´etodos de complejidad O(nlogn): Quicksort y Heapsort 127
6 [tnpos=r] 19 [tnpos=r] 11 [tnpos=r] 1 [tnpos=r] 2 [tnpos=r]
7 [tnpos=r] 4 [tnpos=r] 23 [tnpos=r]
10 [tnpos=r] 5 [tnpos=r] 3 [tnpos=r] 19 [tnpos=r] 11 [tnpos=r] 6 [tnpos=r]
1 [tnpos=r] 2 [tnpos=r]
7 [tnpos=r] 4 [tnpos=r] 23 [tnpos=r]
10 [tnpos=r] 5 [tnpos=r] 3 [tnpos=r]
A(1) A(2) A(3) A(4) A(5) A(6) A(7) A(8) A(9) A(10) A(11)
11 2 3 1 4 5 10 23 19 7 6
k = 5 11 2 3 1 (7) 5 10 23 19 (4 6)
k = 4 11 2 3 (23) 7 5 10 (1 19) 4 6
k = 3 11 2 (10) 23 7 (5 3) 1 19 4 6
k = 2 11 (23) 10 (2 7) 5 3 1 19 4 6
11 23 10 (19) 7 5 3 (1 2) 4 6
k = 1 (23) (11 10) (19) 7 5 3 1 2 4 6
23 (19) 10 (11 7) 5 3 1 2 4 6
23 19 10 (11) 7 5 3 (1 2) 4 6
La ´ ultima fila de la tabla anterior, es la que estaba representada en el
´arbol binario de la Figura (b).
En la segunda etapa, Heapsort extrae el elemento m´as grande de la
ra´ız y lo almacena en A(n), intercambiando los elementos A(1) y A(n).
Adem´as la posici´on n no es considerada posteriormente parte del mont´ on.
Para reordenar los elementos en las posiciones 1, ..., n −1 en un mont´ on, el
elemento de A(1) es hundido tan abajo en el ´ arbol como es necesario. El
proceso de intercambiar A(1) y A(n − 1) se repite y adem´as el ´arbol ocupa
las posiciones de los elementos 1, ..., n −2.
Pasamos de:
6 19 10 11 7 5 3 1 2 4 23
a:
19 11 10 6 7 5 3 1 2 4 23
Algorithm updateheap A(1 : s)
(A(1 : s) es el nuevo vector amontonado, s > 1)
Intercambiamos el elemento m´as grande que est´a en A(1) con A(s)
pivot := A(1)
128 7 M´etodos de ordenaci´on
A(1) := A(s)
A(s) := pivot
k := 1
while 2k < (s − 1) do
index := ´ındice m´as grande de A(2k), A(2k + 1)
if A(k) < A(index) then
//intercambiar A(k) con A(index)//
pivot := A(index)
A(index) := A(k)
A(k) := pivot
k := index
else goto fin
endif
enddo
fin. Return (A)
El algoritmo Heapsort completo se implementa como sigue:
Algoritmo Heapsort
createheap A(1:n)
for s := n to 2, −1 do
updateheap A(1 : s)
enndo
N´ umero de comparaciones necesario en Heapsort
El primer algoritmo createheap consta de dos ciclos que est´an anidados.
El ciclo exterior tiene
n
2
pasos, mientras que el interior no se realiza mas
veces que la altura, h, que tiene el mont´ on considerado. Adem´ as en cada
paso del ciclo interior se realizan al menos dos comparaciones. El n´ umero de
comparaciones necesarias es O(
n
2
h) = O(nh).
El algoritmo updateheap contiene ´ unicamente un ciclo. Se realiza una
comparaci´ on por cada paso del ciclo y el ciclo se realiza al menos el n´ umero
de veces que corresponde a la altura del mont´ on. Es decir el n´ umero de
7.4 Quicksort 129
comparaciones es O(h), donde h representa la altura del mont´ on. Si el ´ arbol
tiene n elementos y en cada nodo se produce una bifurcaci´ on, h ≈ log
2
n.
El n´ umero total de comparaciones requeridas por el algoritmo completo
heapsort es:
O(nh) + (n −1)O(h) = O(nlog
2
n) +nO(log
2
n) = O(nlogn)
El n´ umero de intercambios de elementos requerido por heapsort para
cualquier vector inicial, es tambi´en O(nlogn).
7.4. Quicksort
Supongamos un vector de elementos desordenado K(1), ..., K(n). Quick-
sort trabaja particionando el vector en dos partes y ordenando cada una de
las partes independientemente. La posici´on ex´ acta de la partici´ on depende
de los datos. En la etapa en la que se realiza la partici´ on, el vector se ordena
de manera que se mantienen las siguientes condiciones:
1. El elemento que particiona K(k), est´a en su propia posici´ on.
2. Todos los elementos K(1), ..., K(k−1) son menores o iguales que K(k).
3. Todos los elementos K(k+1), ..., K(n) son mayores o iguales que K(k).
Esto puede ser implementado f´ acilmente utilizando la siguiente estrate-
gia. Declarar dos punteros i, j con i = 1, j = n inicialmente. Comparar
K(i) con K(j), K(j − 1), ..., K(s) hasta enontrar un elemento K(s) menor
que K(i). Intercambiar ambos elementos, incrementar i por 1 y continuar
la comparaci´ on despu´es de incrementar el valor de i y hasta que se produz-
ca otro intercambio. Despu´es del intercambio disminuir j en 1 y continuar
la comparaci´ on hasta que se produzca otro intercambio. Despu´es de dicho
intercambio incrementar i y as´ı sucesivamente hasta que i = j.
Para un mejor desarrollo del algoritmo, se produce un intercambio in-
cluso cuando para i ,= j, K(i) y K(j) son elementos con el mismo valor.
Cuando los punteros i, j alcanzan la misma posici´on, dicha posici´ on deter-
mina la partici´ on del vector en dos subvectores. Cada uno de los dos vectores
es ordenado independientemente. Esto significa que despu´es de cada parti-
ci´on del vector, una de las partes necesita ser almacenada temporalmente
mientras Quicksort ordena la otra parte. Esto requiere de alguna memoria
auxiliar por parte del algoritmo.
130 7 M´etodos de ordenaci´on
Una descripci´ on completa del algoritmo ser´ıa la siguiente:
Algorithm Quicksort (r, s)
i := r
j := s + 1
pivot := K(i)
if s > 1 then
repeat
repeat j := j − 1 until K(j) ≤ pivot
repeat i := i + 1 until K(i) ≥ pivot
if i < j
temp := K(i)
K(i) := K(j)
K(j) := temp
endif
until j ≤ i
K(r) := K(j)
K(j) := pivot (el elemento que determina la partici´on est´a en la posici´on j)
endif
if r < s then
quicksort(r, j − 1)
quicksort(j + 1, s)
endif
Tiempo medio requerido por Quicksort
Antes de calcular el tiempo medio de ejecuci´on de un algoritmo, vamos
a determinar c´ ual es la distribuci´ on de probabilidad de los datos. Para al-
goritmos de ordenaci´ on, una hip´ otesis natural, y que nosotros haremos, es
que cada permutaci´ on de la secuencia a ordenar es igualmente probable a
la hora de aparecer como datos. Bajo esta hip´ otesis, f´ acilmente se puede
acotar inferiormente el n´ umero de comparaciones necesarias para ordenar
una secuencia de n elementos.
7.4 Quicksort 131
El m´etodo general es asociar con cada hoja del ´arbol de decisi´ on ν, la
probabilidad de alcanzar dicha hoja, para unos datos dados. Si conocemos
la distribuci´ on de probabilidad de los datos, se pueden determinar las pro-
babilidades de cada una de las hojas. Entonces, se puede calcular el n´ umero
esperado de comparaciones realizado por un algoritmo de ordenaci´on parti-
cular, evaluando sobre todas las hojas del ´ arbol de decisi´on, la suma

i
p
i
d
i
,
donde p
i
es la probabilidad de alcanzar la hoja i y d
i
su altura. Esta suma
es la altura esperada de un ´ arbol de decisi´ on.
Teorema 3 Bajo la hip´ otesis de que todas las permutaciones de la se-
cuencia de n elementos son igualmente probables, cualquier ´arbol de decisi´ on
que ordene n elementos tiene una altura esperada de al menos logn!.
Demostraci´on: Sea D(T) la suma de las alturas de las hojas de un
´arbol binario T. Sea D(m) el valor m´as peque˜ no de D(T), elegido de entre
todos los ´arboles T con m hojas. Demostraremos por inducci´on sobre m,
que D(m) ≥ mlogm. Para m = 1 el resultado es trivial. Supongamos que
es cierto para valores de m menores que k y consideremos un ´ arbol T con
k hojas. T est´a formado por una ra´ız y por un ´ arbol a la izquierda T
i
con i
hojas y un ´ arbol a la derecha T
k−i
con k −i hojas (la ra´ız de T no pertenece
a ninguno de los dos sub´ arboles T
i
ni T
k−i
), para alg´ un i, 1 ≤ i ≤ k.
Claramente
D(T) = i +D(T
i
) + (k −i) +D(T
k−i
)
Adem´as, la suma m´ınima est´a dada por
D(k) = min
1≤i≤k
¦k +D(i) +D(k −i)¦
De la hip´ otesis inductiva, tenemos:
D(k) ≥ k +min
1≤i<k
[ilogi + (k −i)log(k −i)] = k +min
1≤i<k
f(i)
Derivando f

(i) = 0, se deduce que i =
k
2
con f

(
k
2
) > 0. El m´ınimo se
alcanza en i =
k
2
; luego:
D(k) ≥ k +klog
k
2
= k +klogk −klog2 = klogk
Conclu´ımos entonces que D(m) ≥ mlogm, ∀m ≥ 1.
Recordemos del Teorema 2, que un ´ arbol de decisi´ on T que ordene n
elementos cualesquiera tiene al menos n! hojas. M´ as a´ un, tiene ex´ actamente
132 7 M´etodos de ordenaci´on
n! hojas con probabilidad
1
n!
cada una, y las restantes con probabilidad
0. Podemos eliminar de T todos los nodos antecesores s´olo de hojas con
probabilidad 0, sin cambiar la altura esperada de T. Tenemos entonces un
´arbol T

con n! hojas, cada una de probabilidad
1
n!
. Como D(T

) ≥ n!logn!,
la altura esperada de T

y por lo tanto de T es al menos
1
n!
n!logn! = logn!.

Corolario 2 Cada algoritmo de ordenaci´on mediante comparaciones rea-
liza al menos cnlogn comparaciones en media, para alguna constante c > 0.
Veremos ahora que el algoritmo Quicksort tiene un tiempo medio de eje-
cuci´on acotado inferiormente por cnlogn para alguna constante c. El hecho
significativo es que el tiempo de ejecuci´ on para el peor caso en dicho algo-
ritmo es cuadr´ atico. Sin embargo no es un hecho importante en la mayor´ıa
de los casos.
Teorema 4 El algoritmo Quicksort ordena una secuencia de n elementos
en un tiempo esperado de O(nlogn).
Demostraci´on: Sea una secuencia de n elementos a
1
, ..., a
n
. Por simpli-
cidad supongamos que todos los elementos en S son distintos. Esta hip´ otesis
maximizar´a los tama˜ nos de los vectores resultantes de su partici´on.
Sea T(n) el tiempo medio necesario por Quicksort para ordenar una se-
cuencia de n elementos. Claramente T(0) = T(1) = b para alguna constante
b.
Supongamos que despu´es de la primera partici´on hemos creado dos vec-
tores en S. Denotamos por T(s) y T(n − s), respectivamente, los tiempos
medios requeridos por Quicksort para ordenar cada uno de los dos vectores.
Como s es iguamente probable al resto de valores entre 1 y n, y el tiempo
necesario para obtener la primera partici´on es cn, donde c es una constante,
1 < c < 2 (se sigue del hecho de que se necesitan n comparaciones y ≤ n
intercambios), tenemos:
T(0) = T(1) = b
T(n) =
1
n
n

s=1
(T(s) +T(n −s)) +cn, n ≥ 2
=
1
n
¦
n

s=1
T(s) +
n−1

j=0
T(j)¦ +cn
=
1
n
¦
n−1

s=1
T(s) +
n−1

j=0
T(j) +T(n)¦ +cn
7.4 Quicksort 133
=
2
n
n−1

s=1
T(s) +
1
n
T(n) +cn
o escrito de otro modo:
(n −1)T(n) = 2
n−1

s=1
T(s) +cn
2
, 1 < c < 2
Expresi´on que nos da una relaci´ on de recurrencia. Si la escribimos para
n + 1.
nT(n + 1) = 2
n

s=1
T(s) +c(n + 1)
2
, 1 < c < 2
(n −1)T(n) = 2
n−1

s=1
T(s) +cn
2
restando la segunda de la primera, se obtiene:
nT(n + 1) −(n −1)T(n) = 2T(n) +c(2n + 1)
o lo que es lo mismo:
nT(n + 1) = (n + 1)T(n) +c(2n + 1)
T(n + 1)
n + 1
=
T(n)
n
+c
(2n + 1)
n(n + 1)
que escrita recursivamente:
T(n + 1)
n + 1
=
T(n)
n
+c
(2n + 1)
n(n + 1)
=
T(n)
n
+c(
1
(n + 1)
+
1
n
)
T(n)
n
=
T(n −1)
n −1
+c(
1
n
+
1
n −1
)

T(2)
2
=
T(1)
1
+c(
1
2
+ 1)
De donde:
T(n + 1)
n + 1
=
b
1
+c
__
1
n + 1
+
1
n
+... +
1
2
_
+
_
1
n
+
1
n −1
+... + 1
__
=
= b +c
_
(H
n+1
−1) + (H
n+1

1
n + 1
)
_
=
= b + 2cH
n+1
−c
n + 2
n + 1
134 7 M´etodos de ordenaci´on
Si y s´ olo si:
T(n + 1) = (n + 1)b + 2c(n + 1)H
n+1
−c(n + 2) ⇔
T(n) = nb + 2cnH
n
−c(n + 1)
Donde:
H
n
≈ log
e
n =

n=1
1
n + 1
de manera que:
T(n) ≈ nb −c(n + 1) + 2cnlog
e
n = O(nlogn)
Notar que:
k = log
e
n ⇔ e
k
= n
l = log
2
n ⇔ 2
l
= n
2 < e, luego n = 2
l
< e
l
, de donde k < l y por tanto log
e
n < log
2
n.•
Cap´ıtulo 8
El lenguaje de programaci´on C (IV)
Los apuntadores o punteros son variables que contienen la direcci´ on de
otra variable. Son frecuentemente utilizados en C, puesto que son, en ocasio-
nes, la ´ unica forma de expresar algunos c´ alculos, mediante c´odigo compacto
y eficiente.
Los apuntadores se han comparado con la proposici´ on goto, como una
manera de crear programas imposibles de entender. Esto es completamente
cierto cuando se emplean sin cuidado. Sin embargo, con disciplina se pueden
emplear para conseguir claridad y simplicidad. Este es el aspecto que vamos
a intentar desarrollar en este tema.
8.1. Direccionamiento indirecto. Punteros: varia-
bles que contienen la ubicaci´ on en memoria
de otras variables
Puesto que un puntero contiene la direcci´ on de un objeto, se puede acce-
der al objeto “indirectamente” a trav´es de ´el. Sea x una variable de tipo int,
y px un puntero. El operador unitario & devuelve la direcci´ on de un objeto,
por lo que la proposici´ on
px = &x;
asigna la direcci´ on de x a la variable px; ahora se dice que px “apunta a” x.
El operador & s´ olo se puede aplicar a variables y elementos de un arreglo
(array). Construcciones como &(x + 1) o &3 son ilegales. Tambi´en es ilegal
obtener la direcci´ on de una variable register.
135
136 8 El lenguaje de programaci´ on C (IV)
El operador unitario ∗ toma su operando como una direcci´ on para obte-
ner su contenido. Si y tambi´en es de tipo int,
y=*px;
asigna a y el contenido de cualquier parte a la que apunte px. La secuencia
px = &x;
y = *px;
asigna a y el mismo valor de x.
Tambi´en es necesario declarar las variables que intervienen, la forma de
hacerlo es:
int x,y;
int *px;
Esta ´ ultima declaraci´ on se entiende como un mnemot´ecnico. Se quiere
indicar que la combinaci´ on ∗px es de tipo int, es decir si px aparece en
el contexto ∗px, ello equivale a una variable int. La sintaxis en la declara-
ci´on de una variable imita a la sintaxis de las expresiones en las que puede
aparecer la variable. Este razonamiento es ´ util cuando aparecen expresiones
complicadas. Por ejemplo:
double atof(), *dp;
indican que atof() y ∗dp toman valores de tipo double si aparecen en
una expresi´ on. Hay que se˜ nalar que en la declaraci´ on de un apuntador se
restringe el tipo de los objetos a los que puede apuntar. Los apuntadores
pueden aparecer en expresiones. Si px apunta al entero x, entonces ∗px
puede aparecer en cualquier contexto que pudiera hacerlo x.
A un puntero se le puede sumar o restar un entero. En el caso del ejemplo
anterior, px ha sido declarado como un puntero a un entero x. La expresi´on
y=*px+1;
asigna a y una unidad m´ as que x. La expresi´ on
8.1 Direccionamiento indirecto. Punteros: variables que contienen la ubicaci´on en memoria de otras variab
printf("%d\n", *px);
imprime el valor actual de x; y
d=sqrt((double)*px);
asigna a d la raiz cuadrada de x, variable que es convertida a double para
poder utilizar sqrt.
En expresiones como
y=*px+1;
los operadores unitarios ∗ y & tienen mayor precedencia que los opera-
dores aritm´eticos, por lo que al evaluar la expresi´ on se toma el valor del
objeto al que apunta px y se le suma 1, y el resultado es asignado a y. M´ as
adelante veremos el significado de ∗(px + 1).
Tambi´en pueden aparecer referencias a apuntadores en la parte izquierda
de una asignaci´ on. Si px apunta a x, entonces
*px=0;
pone x a 0, y
*px+=1;
incrementa en 1 el valor de x, al igual que
(*px)++;
En este ´ ultimo ejemplo se necesitan los par´entesis. En otro caso, se in-
crementar´ıa en 1 px y no el objeto al que apunta, ya que los operadores
unitarios como ∗ y ++ se eval´ uan de derecha a izquierda.
Adem´as como los apuntadores son variables, se pueden manipular igual
que cualquier variable. Si py es otro apuntador a enteros,
py=px;
copia el contenido de px en py, con lo que se consigue que ahora py
apunte al mismo objeto que px.
138 8 El lenguaje de programaci´ on C (IV)
8.2. Punteros y funciones: paso de argumentos de
referencia haciendo uso de punteros
Cuando hablamos de las funciones en C, dijimos que los argumentos se
pasaban por valor. Tambi´en es cierto que no hay forma directa de hacer que
la funci´ on llamada altere una variable de la funci´ on que la llama. ¿Qu´e hacer
entonces cuando realmente queremos modificar alguno de los argumentos?
Por ejemplo, un programa de clasificaci´ on podr´ıa intercambiar dos ele-
mentos no ordenados con una funci´ on llamada swap. La llamada ser´ıa
swap (a,b);
y la definici´ on (incorrecta, como veremos) ser´ıa:
swap(x,y)
int x,y;
{
int temp;
temp=x;
x=y;
y=temp;
}
Pero, por defecto de la llamada “por valor”, la funci´ on swap no puede
modificar los argumentos a y b. Sin embargo hay una forma de hacerlo, que
permite conseguir el f´ın deseado. En la llamada a la fuci´ on swap se utilizan
apuntadores a los valores que se desea cambiar:
swap (&a,&b);
El operador & devuelve la direcci´ on de una variable, por lo que &a es un
apuntador a a. En la definici´ on (correcta) de la funci´ on swap se tienen que
declarar los argumentos como apuntadores, a trav´es de los cuales se accede
a los operandos reales.
swap(px,py) /* intercambio de *px y *py */
int *px,*py;
{
int temp;
temp=*px;
8.2 Punteros y funciones: paso de argumentos de referencia haciendo uso de punteros139
*px=*py;
*py=temp;
}
Los apuntadores como argumentos suelen emplearse en el caso de funcio-
nes que devuelven m´ as de un valor simple. Podr´ıa decirse que swap devuelve
dos valores: los nuevos valores de sus argumentos.
Ejemplo: Consideremos una funci´ on getint() que realice la conversi´ on
de una cadena de caracteres en valores enteros, sin formato y un entero por
llamada. getint debe devolver el valor hallado o una indicaci´ on de fin de
archivo, si no hay caracteres. Dichos valores hay que devolverlos en objetos
independientes, puesto que en otro caso, el valor empleado para EOF, podr´ıa
coincidir con el del entero hallado en la entrada.
Una soluci´ on consiste en que getint devuelva EOF si detecta el fin de
fichero o cualquier otro valor, para indicar un entero normal. El valor num´eri-
co del entero hallado se devuelve a trav´es del argumento, que deber´ a ser un
apuntador a un entero. Esta organizaci´ on diferencia la indicaci´on de fin de
archivo y los valores num´ericos.
Con las siguientes sentencias se consigue llenar un array con valores
enteros llamando a getint:
int n, v, array[SIZE];
for (n=0;n<SIZE && getint(&v)!=EOF;n++)
array[n]=v;
Cada llamada deja en v el entero hallado. Observa que es imprescindible
escribir &v en lugar de v, como argumento de getint. Si se utiliza v se puede
cometer un error de direccionamiento puesto que getint supone que se le
pasa un puntero como argumento. La definici´ on de getint es la siguiente:
getint(pn) /* toma el siguiente entero de la entrada */
int *pn;
{
int c, sign;
while((c=getch())==’ ’|| c==’\n’ || c==’\t’)
; /* salta espacios en blanco */
sign=1;
if(c==’+’ || c==’-’)
{ /* registra signo */
sign= (c==’+’) ? 1: -1;
c=getch();
}
140 8 El lenguaje de programaci´ on C (IV)
for(*pn=0; c>=’0’ && c<=’9’; c=getch())
*pn=10* *pn + c - ’0’;
*pn *= sign;
if(c != EOF)
ungetch(c);
return ();
}
∗pn se utiliza en getint como cualquier variable de tipo int.
Nota: ¿Qu´e hacen getch y ungetch? A menudo se da el caso
de que un programa que lee no puede determinar si ha le´ıdo
suficiente hasta que ha le´ıdo demasiado. Un ejemplo es la reco-
lecci´on de los caracteres que constituyen un n´ umero: mientras no
se encuentra el primer car´acter que no sea d´ıgito se sabe que el
n´ umero no est´a completo. Pero entonces el programa habr´ a le´ıdo
un car´ acter de m´as para el que no est´ a preparado.
El problema estar´ıa resuelto si fuera posible “desleer” el car´acter
no deseado. Entonces, cada vez que un programa leyera un car´ acter
de m´as, podr´ıa devolverlo a la entrada y el resto del c´odigo se
comportar´ıa como si nunca hubiera sido le´ıdo. Afortunadamen-
te es f´acil simular la devoluci´ on de un caracter con s´ olo escribir
un par de funciones cooperantes. getch toma un car´ acter de la
entrada y ungetch lo devuelve, para que la siguiente llamada
de getch pueda encontrarlo de nuevo. La forma de funcionar es
sencilla. ungetch sit´ ua el car´ acter devuelto en un buffer compar-
tido (un arreglo de caracteres). getch lee en el buffer, si contiene
algo. Llama a getchar si el buffer est´a vac´ıo. Tambi´en debe de
existir una variable ´ındice que recuerda la posici´ on siguiente del
car´acter en el buffer. Ya que el buffer y el ´ındice son compartidos
por getch y ungetch y deben conservar sus valores entre llama-
das, deber´ an ser externos a ambas. Una forma de escribir getch
y ungetch y sus variables compartidas es:
#define BUFSIZE 100
char buf[BUFSIZE]; /* buffer para ungetch */
int bufp=0; /* sigue posici´ on libre en buf */
getch() /* lee un (posiblemente devuelto) car´ acter */
{
return ((bufp > 0) ? buf[--bufp] : getchar());
}
ungetch(c) /* devuelve un car´ acter */
8.2 Punteros y funciones: paso de argumentos de referencia haciendo uso de punteros141
int c;
{
if(bufp > BUFSIZE)
printf("ungetch: demasiados caracteres \n");
else
buf[bufp++]=c;
}
8.2.1. Punteros y arrays
En C existe entre punteros y arrays un relaci´ on tal que, cualquier opera-
ci´on que pueda realizarse mediante la indexaci´ on de un array se puede reali-
zar tambi´en con punteros. La versi´ on con ´estos ser´a m´as r´apida en t´erminos
generales, pero para los no iniciados, m´ as dif´ıcil de entender.
La declaraci´on
int a[10];
define un array de tama˜ no 10, es decir un bloque con diez objetos conse-
cutivos denominados a[0], a[1], ..., a[9]. La notaci´ on a[i] significa que el ele-
mento del array se encuentra a i posiciones del comienzo. Si pa es un puntero
a un entero, declarado como
int *pa;
entonces la asignaci´on
pa=&a[0];
hace que pa apunte al elemento 0 de a; es decir, pa contiene la direcci´ on
de a[0]. Ahora la asignaci´ on
x=*pa;
copiar´ a el contenido de a[0] en x.
Si pa apunta a un elemento particular de un arreglo a, entonces, por
definici´ on pa+1 apunta al siguiente elemento, y en general pa−i al elemento
situado i posiciones antes que pa, y pa + i apunta al elemento situado i
posiciones despu´es. Si pa apunta a a[0], entonces
142 8 El lenguaje de programaci´ on C (IV)
*(pa+1)
se refiere al contenido de a[1], pa +i es la direcci´on de a[i] y ∗(pa +i) es
el contenido de a[i]. Estos comentarios son ciertos cualquiera que sea el tipo
de variables del array a. Y por extensi´on, toda la aritm´etica de punteros
establece que el incremento se adec´ ua al tama˜ no en memoria del objeto
apuntado. En pa +i, i se multiplica por el tama˜ no de los objetos a los que
apunta pa antes de ser sumado a pa.
La correspondencia entre indexaci´ on y aritm´etica de punteros es eviden-
temente muy estrecha. El compilador convierte toda referencia a un array en
un puntero al comienzo del arreglo. El efecto es que el nombre de un array
es una expresi´on de tipo puntero. Este hecho tiene algunas implicaciones
muy ´ utiles. Puesto que el nombre de un arreglo es sin´ onimo de la posici´on
del elemento cero, la asignaci´on
pa=&a[0]
se puede escribir as´ı
pa=a
Del mismo modo, una referencia a a[i] se puede escribir tambi´en como
∗(a+i). Al aplicar & a los dos lados de esta equivalencia se deduce tambi´en
que &a[i] y a + i tambi´en son id´enticas. a + i es la direcci´on del i-´esimo
elemento de a. Por otra parte, si pa es un puntero, en las expresiones puede
aparecer como un sub´ındice: pa[i] es id´entico a ∗(pa+i). En suma, cualquier
expresi´on en que aparezca un array y un sub´ındice se puede escribir como un
puntero y un desplazamiento y viceversa, incluso en la misma proposici´ on.
Sin embargo, hay una diferencia entre el nombre de un array y un pun-
tero, que hay que tener en cuenta. Un puntero es una variable, por lo que
operaciones como pa = a y pa++ son correctas. Pero el nombre de un array
es una constante, no una variable; de manera que expresiones como a = pa
o a + + son ilegales.
Cuando se pasa el nombre de un array a una funci´ on, se pasa la direcci´on
del comienzo del array. En la funci´ on, este argumento es una variable, por lo
que el nombre de un array como argumento es un puntero, o sea una variable
que contiene una direcci´ on. Podemos ver ´esto en el siguiente ejemplo:
strlen(s) /* regresa la longitud de la cadena s */
8.2 Punteros y funciones: paso de argumentos de referencia haciendo uso de punteros143
char *s;
{
int n;
for (n = 0; *s != ’\0’; s++)
n++;
return n;
}
Es perfectamente legal incrementar s, puesto que es un puntero; s + +
no modifica la cadena de caract´eres s, no hace m´as que incrementar la copia
privada de la direcci´ on.
Las definiciones de par´ ametros
char s[];
y
char *s;
son completamente equivalentes; la forma como debe escribirse depende
del tipo de expresiones en las que aparece. Cuando se pasa el nombre de un
array a una funci´ on, la funci´ on puede establecer a su conveniencia si va a
manejar un array o un puntero, y hacer las manipulaciones de acuerdo con
´esto. Incluso puede emplear los dos tipos de operaciones si le parece claro y
conveniente.
Tambi´en es posible pasar parte de un arreglo a una funci´ on, pasando un
puntero al comienzo del subarreglo. Por ejemplo si a es un array
f(&a[2]);
y
f(a+2);
pasan a la funci´ on f la direcci´ on del elemento a[2], ya que &a[2] y a +2
son expresiones de tipo puntero que apuntan al tercer elemento de a. En f,
la declaraci´ on de los argumentos podr´ıa ser
144 8 El lenguaje de programaci´ on C (IV)
f(arr)
int arr[];
{
...
}
o bien
f(arr)
int *arr;
{
...
}
8.3. Algebra de punteros
El lenguaje C es consistente y formal en su manejo de la aritm´etica de
direcciones; la integraci´on de punteros, arrays y aritm´etica de direcciones
es una de sus principales virtudes. Veremos a continuaci´ on algunas de sus
propiedades, escribiendo un rudimentario administrador de memoria. Hay
dos rutinas: alloc(n) devuelve un puntero p a n caracteres consecutivos;
free(p) libera la memoria adquirida de esa manera, para poder ser utiliza-
da posteriormente. Las rutinas son rudimentarias, en el sentido de que las
llamadas a free deben hacerse en orden inverso a las llamadas a alloc. Es
decir, la memoria administrada por alloc y free es una pila, o estructura
´ ultima-en-entrar, primera-en-salir. La biblioteca est´andar de C tiene funcio-
nes an´ alogas que no tienen las restricciones anteriores. Sin embargo, muchas
aplicaciones s´olo necesitan una sencilla funci´ on alloc que les proporcione pe-
que˜ nas zonas de memoria de tama˜ no impredecible y en instantes tambi´en
impredecibles.
La realizaci´ on m´ as sencilla consiste en que alloc devuelve trozos de un
gran array de caracteres que llamaremos allocbuf. Este array es exclusivo de
alloc y free. Puesto que se trabaja con punteros y no con sub´ındices, nin-
guna otra rutina necesita conocer el nombre del array, por lo que se declara
como externo y est´atico, es decir, local al archivo fuente que contenga alloc
y free e invisible fuera de ´el. En aplicaciones pr´ acticas, el array no necesita
nombre; en lugar de ello se puede obtener pidiendo al sistema operativo una
zona libre de memoria.
En cuanto a la manipulaci´ on del array allocbuf, se emplear´a un puntero
al primer elemento libre, llamado allocp. Cuando se le piden a alloc n ca-
8.3 Algebra de punteros 145
racteres, se comprueba si hay espacio suficiente en allocbuf (el comienzo de
un bloque libre) y se incrementa su valor en n para que apunte a la nueva
zona libre. free(p) le da el valor p a allocp, si p se encuentra en allocbuf.
#define NULL 0 /* valor del puntero para devolver errores */
#define ALLOCSIZE 1000 /* tama~ no del area disponible */
static char allocbuf[ALLOCSIZE]; /* espacio para alloc */
static char *allocp = allocbuf; /* siguiente posici´ on libre */
char *alloc(n) /* devuelve puntero a n caracteres */
int n;
{
if (allocp + n <= allocbuf+ALLOCSIZE){ /* se encontr´ o */
allocp += n;
return (allocp - n); /* p anterior */
}
else /* no hubo espacio suficiente */
return (NULL);
}
free(p) /* p apunta al ´ area libre */
char *p;
{
if(p >= allocbuf && p < allocbuf+ ALLOCSIZE)
allocp = p;
}
En general un apuntador se puede inicializar como cualquier otra varia-
ble, aunque normalmente los valores significativos son NULL o una expre-
si´on en que aparezcan direcciones de objetos del tipo apropiado definidas
previamente.
La declaraci´on
static char *allocp = allocbuf;
define allocp como un puntero a caracteres y lo inicializa para que apunte
a allocbuf, que es la zona libre cuando comienza el programa. Esto se pod´ıa
haber hecho escribiendo
static char *allocp = &allocbuf[0];
ya que el nombre del array es la direcci´ on del elemento cero.
La pregunta
146 8 El lenguaje de programaci´ on C (IV)
if (allocp + n <= allocbuf+ALLOCSIZE)
comprueba si hay espacio suficiente para satisfacer la petici´ on de n ca-
racteres; si lo hay, el nuevo valor de allocp ser´ıa como m´aximo una posici´ on
m´as all´a del fin de allocbuf. Si la petici´ on se puede satisfacer, alloc devuelve
un apuntador. En caso contrario, alloc debe devolver alguna indicaci´ on de
que no tiene espacio.
El lenguaje C garantiza que un apuntador que apunte a datos v´ alidos
nunca tendr´ a valor cero. Se puede emplear para se˜ nalar una condici´ on anor-
mal: en este caso, indica que no hay espacio. Se escribe NULL en lugar de
cero, para indicar que es un valor especial para punteros. En general no tiene
sentido asignar enteros a punteros, el cero es un caso especial.
Preguntas como las aparecidas en las sentencias if, del ejemplo anterior,
muestran diferentes facetas de la aritm´etica de punteros. Los punteros se
pueden comparar en ciertas circunstancias. Si p y q apuntan a miembros de
un mismo array, se pueden utilizar relaciones como <, >, =, etc.
Tambi´en se pueden utilizar relaciones como == y ! =. Todo puntero
se puede comparar con igualdad o desigualdad con el valor NULL. No se
pueden comparar punteros a distintos arreglos. (Esto puede funcionar en al-
gunas m´ aquinas, lo que puede hacer que nuestro programa deje de funcionar
misteriosamente cuando lo trasladamos de maquina).
Adem´as se pueden sumar o restar un puntero y un entero. La construc-
ci´on
p + n
significa el n-´esimo objeto a partir del que apunta p. Esto es cierto in-
dependientemente del objeto al que apunte p. Si p y q apuntan a miembros
del mismo array, tambi´en se pueden restar: p −q es el n´ umero de elementos
entre p y q. Esto nos permite escribir otra versi´on de la funci´ on strlen:
strlen(s) /* regresa la longitud de la cadena s */
char *s;
{
char *p = s;
while (*p != ’\0’)
p++;
return (p-s);
}
En esta versi´on p se inicializa con s, es decir, apunta al primer car´ acter.
En la iteraci´ on while se examina cada car´acter hasta encontrar el ¸0. Puesto
8.4 Punteros a caracteres 147
que ¸0 es cero y la iteraci´on while comprueba s´ olo si la expresi´on es cero,
se puede omitir la comprobaci´ on expl´ıcita, y escribir la iteraci´on como
while (*p)
p++;
Como p apunta a caracteres, p + + actualiza p para que apunte al si-
guiente car´acter cada vez, y p − s indica el n´ umero de caracteres tratados,
es decir la longitud de la cadena. Si estuvi´esemos apuntando a float, que
ocupan m´ as memoria que los char y si p fuese un puntero a float, p + + se
posicionar´ıa en el siguiente float.
No se pueden hacer m´as operaciones con punteros de las se˜ naladas aqu´ı.
No se permite sumar dos punteros, multiplicarlos, dividirlos, sumarles valo-
res float o double, etc.
8.4. Punteros a caracteres
Vamos a analizar inicialmente dos funciones de la biblioteca est´andar de
C, string.h. La primera strcpy(s, t) que copia la cadena t en la cadena s. La
versi´on con arrays es:
strcpy(s,t) /* copia t a s */
char s[], t[];
{
int i;
i = 0;
while((s[i] = t[i]) != ’\0’)
i++;
}
Mientras que la versi´ on con punteros es:
strcpy(s,t) /* copia t a s */
char *s, *t;
{
while((*s = *t) != ’\0’)
{
s++;
t++;
}
}
Como los argumentos se pasan por valor, strcpy puede usar s y t con
absoluta libertad. Son punteros que recorren el array car´ acter a car´acter
148 8 El lenguaje de programaci´ on C (IV)
hasta que el ¸0 con que termina t se ha copiado a s. La tercera versi´on m´ as
compacta es:
strcpy(s,t) /* copia t a s */
char *s, *t;
{
while(*s++ = *t++)
;
}
Por una parte se ha introducido los incrementos de s y de t en la parte de
comprobaci´ on de la iteraci´ on. El valor de ∗t++ es el car´acter al que apuntaba
t antes de ser incrementado; el operador sufijo ++ no se aplica hasta que se
ha obtenido ese car´ acter. De la misma manera el caracter se almacena en la
antigua posici´ on donde apuntaba s antes de ser incrementado. Este car´acter
es tambi´en el valor que se compara con ¸0 para controlar la iteraci´ on. El
efecto neto es la copia de los caracteres de t a s hasta copiar el ¸0 final.
Por otra parte se ha eliminado la comparaci´ on expl´ıcita con ¸0 por re-
sultar redundante.
La segunda rutina que vamos a examinar es strcmp(s, t), que compara
las cadenas de caracteres s y t, y devuelve un valor entero negativo, cero o
positivo, seg´ un que s sea lexicogr´aficamente menor, igual o mayor que t. El
valor se obtiene al restar los caracteres de la primera posici´on donde s y t
difieren.
strcmp(s,t) /* devuelve <0 si s<t, =0 si s=t, >0 si s>t */
char s[], t[];
{
int i;
i = 0;
while(s[i] == t[i])
if(s[i++] == ’\0’)
return 0;
return(s[i]-t[i]);
}
La versi´on con punteros es:
strcmp(s,t) /* devuelve <0 si s<t, =0 si s=t, >0 si s>t */
char *s, *t;
{
for( ; *s == *t; s++, t++ )
if(*s == ’\0’)
return 0;
return(*s - *t);
}
8.5 Ejemplos de utilizaci´ on de punteros 149
Generalmente se ha visto que en la mayor parte de las computadoras
se puede asignar un puntero a un entero y viceversa, sin que se altere el
puntero. Lamentablemente esto ha conducido a abusar de rutinas que de-
vuelven punteros que pasan posteriormente a otras rutinas. En ocasiones se
omiten declaraciones, de manera que el c´odigo resultante sigue funcionan-
do aunque arriesgadamente, puesto que su funcionamiento depende de la
particular arquitectura del ordenador empleado.
8.5. Ejemplos de utilizaci´ on de punteros
Vamos a considerar como primer ejemplo, el problema de la conversi´ on
de la fecha, del d´ıa del mes a d´ıa del a˜ no y viceversa. Por ejemplo, el 1 de
Marzo es el d´ıa 60 en un a˜ no no bisiesto, y el 61 de uno bisiesto. Veremos
dos funciones: day of year, que realizar´a la primera operaci´ on y month day,
que convertir´ a un d´ıa del a˜ no, en mes y d´ıa.
Ambas funciones necesitan de la misma informaci´on inicial, una tabla
con los d´ıas que tiene cada mes (treinta d´ıas tiene Junio, Septiembre,...).
Puesto que el n´ umero de d´ıas del mes de Febrero var´ıa entre a˜ nos bisiestos
y no bisiestos, mantendremos dicha informaci´on en el siguiente array de dos
filas:
static int day_tab[2][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
La primera funci´ on ser´a:
day_of_year(year, month, day) /* calcula el d´ ıa del a~ no
* a partir de mes y d´ ıa */
int year, month, day;
{
int i, bisiesto;
bisiesto= year % 4==0 && year % 100 != 0 || year % 400 == 0;
for(i=1; i < month ; i++)
day += day_tab[bisiesto][i];
return (day);
}
month_day(year, yearday, pmonth, pday) /* calcula mes, d´ ıa
150 8 El lenguaje de programaci´ on C (IV)
* a partir del d´ ıa y a~no */
int year, yearday, *pmonth, *pday;
{
int i, bisiesto;
bisiesto= year % 4==0 && year % 100 != 0 || year % 400 == 0;
for(i=1; yearday > day_tab[bisiesto][i]; i++)
yearday -= day_tab[bisiesto][i];
*pmonth= i;
*pday=yearday;
}
El array day tab ha de ser externo a ambas funciones para poder ser
utilizado por ambas.
day tab es el primer array bidimensional con el que trabajamos. En reali-
dad, en C, un array bidimensional es por definici´ on un array unidimensional,
en el que cada elemento vuelve a ser un array. Los sub´ındices se escriben
como
day_tab[i][j]
en lugar de
day_tab[i,j]
empleado en la mayor´ıa de los dem´as lenguajes. Por otra parte, los ele-
mentos se almacenan por filas, es decir, el sub´ındice m´as a la derecha var´ıa
m´as r´apidamente cuando se accede a los elementos por orden de almacena-
miento. La inicializaci´ on se realiza listando los valores entre llaves. Cada fila
se inicializa mediante la correspondiente sublista. En este caso se ha comen-
zado con una columna de ceros, para que los ´ındices correspondientes a los
distintos meses var´ıen entre 1 y 12.
En el caso de tener que transferir un array a una funci´ on, la declaraci´ on
del argumento en la funci´ on deber´ a incluir el n´ umero de columnas. El n´ umero
de filas es irrelevante, pudi´endose utilizar un apuntador para declarar este
dato. Son posibles las siguientes declaraciones de f:
f(day_tab)
int day_tab[2][13];
{
...
}
8.5 Ejemplos de utilizaci´ on de punteros 151
O bien:
int day_tab[][13];
o tambi´en
int (*day_tab)[13];
que indica que el argumento es un apuntador a un array de 13 ente-
ros. Los par´entesis son necesarios puesto que los corchetes [] tienen mayor
precedencia que el operador de indirecci´ on *. Sin par´entesis, la declaraci´on
int *day_tab[13];
declara un array de 13 apuntadores a enteros, como veremos en el si-
guiente ejemplo.
Vamos a ver ahora un ejemplo de un programa que ordene un conjunto
de l´ıneas de texto en orden alfab´etico, versi´ on reducida de la utiler´ıa sort de
UNIX.
La dificultad del ejemplo estriba en el hecho de trabajar con l´ıneas de
texto, de longitud variable, que exigen ser comparadas y movidas en m´ as de
una s´ola operaci´ on, a diferencia de lo que sucede con un n´ umero entero. Se
necesita por lo tanto una representaci´ on eficiente de los datos, que funcione
correctamente con l´ıneas de texto de longitud variable.
Utilizaremos en este caso un array de punteros. Si las l´ıneas a ordenar se
almacenan una tras otra en un gran array de caracteres, se podr´ a acceder a
cada l´ınea a trav´es de un puntero a su primer car´ acter. Los punteros pueden
almacenarse a su vez en un array. Se pueden comparar dos l´ıneas con s´ olo
pasar sus punteros a strcmp. Cuando tengamos que intercambiar dos l´ıneas
desordenadas, se intercambiar´an los punteros en el arreglo y no las l´ıneas.
Como siempre es mejor dividir el programa principal en funciones que
sigan esta divisi´ on y que la rutina principal controle las cosas.
Si comenzamos pensando en la entrada de datos, la rutina de entrada
ha de recoger y guardar los caracteres de cada l´ınea y construir un array
de punteros a las l´ıneas. Tambi´en tiene que contar el n´ umero de l´ıneas de
entrada, pues esta informaci´ on ser´a necesaria para el proceso de ordenaci´ on
152 8 El lenguaje de programaci´ on C (IV)
y el de salida. La funci´ on de entrada devolver´ a un -1, si existen demasiadas
l´ıneas. La funci´ on de salida s´olo tiene que imprimir las l´ıneas en el orden en
el que aparecen los correspondientes punteros.
#define NULL 0
#define MAXLINES 100 /* n´ umero m´ aximo de l´ ıneas a ordenar */
main() /* ordena las l´ ıneas */
{
char *lineptr[MAXLINES]; /* punteros a las l´ ıneas de texto */
int nlines; /* n´ umero de l´ ıneas le´ ıdas */
if((nlines = readlines (lineptr, MAXLINES)) >= 0)
{
sort(lineptr, nlines);
writelines(lineptr, nlines);
}
else
printf(" Demasiadas l´ ıneas a ordenar\n ");
}
Veamos las definiciones de las funciones utilizadas
#define MAXLEN 1000
readlines (lineptr, maxlines) /* lee l´ ıneas de entrada */
char *lineptr[];
int maxlines;
{
int len, nlines;
char *p, *alloc(), line[MAXLEN];
nlines=0;
while((len=getline(line, MAXLEN)) > 0)
if(nlines >= maxlines)
return -1;
else if((p=alloc(len) == NULL)
return -1;
else{
line[len-1] = ’\0’; /* quita el car´ acter nueva l´ ınea */
strcpy(p, line);
lineptr[nlines++]=p;
}
return nlines;
}
El car´ acter nueva l´ınea al final de cada l´ınea se elimina, pues no afecta
a la forma de ordenar.
8.5 Ejemplos de utilizaci´ on de punteros 153
writelines(lineptr, nlines) /* escribe l´ ıneas */
char *lineptr[];
int nlines;
{
int i;
for(i = 0; i < nlines; i++)
printf("%s \n", lineptr[i]);
}
la novedad es la declaraci´ on de lineptr.
char *lineptr[LINES];
indica que lineptr es un array de LINES elementos, cada uno de los
cuales es un puntero a un car´ acter y ∗lineptr[i] tiene acceso a un car´acter.
Una vez que la entrada y salida est´ an controladas, podemos proceder
a su ordenaci´ on. El m´etodo elegido en este caso, no ha sido desarrollado
previamente en teor´ıa, se denomina Shellsort.
sort(v,n) /* ordena las cadenas v[0],...v[n-1] *
* en orden creciente */
char *v[];
int n;
{
int gap, i,j;
char *temp;
for( gap = n/2; gap > 0; gap /= 2)
for(i = gap; i < n; i++)
for(j = i - gap; j >= 0; j -=gap){
if(strcmp(v[j], v[j+gap]) <= 0)
break;
temp=v[j];
v[j]=v[j+gap];
v[j+gap]=temp;
}
}
Debido a que los elementos de v son punteros, tambi´en debe serlo temp.
Podr´ıa mejorarse el programa sustituyendo el m´etodo de ordenaci´ on Shell
por ejemplo por Quicksort. Int´entalo como ejercicio.
El programa anterior ha sido implementado, junto con las funciones re-
adlines, writelines, sort, getline y alloc. El resultado es el fichero siguiente:
#include<stdio.h>
154 8 El lenguaje de programaci´ on C (IV)
#include<string.h>
#define NULL 0
#define MAXLINES 100 /*numero maximo de lineas a ordenar */
#define MAXLEN 1000
main(){
char *lineptr[MAXLINES];
int nlines; /* numero de lineas leidas */
if((nlines=readlines(lineptr, MAXLINES))>=0)
{
sort(lineptr, nlines);
writelines(lineptr, nlines);
}
else
printf("Demasiadas lineas a ordenar\n ");
}
readlines(lineptr, maxlines) /* lee las lineas de la entrada */
char *lineptr[];
int maxlines;
{
int len, nlines;
char *p, *alloc(), line[MAXLEN];
nlines=0;
while((len=getline(line,MAXLEN))>0)
if(nlines >= maxlines)
return (-1);
else if ((p=alloc(len)) == NULL)
return (-1);
else{
line[len-1]=’\0’; /* quita el caracter nueva linea */
strcpy(p,line);
lineptr[nlines++]=p;
}
return (nlines);
}
writelines(lineptr,nlines) /*escribe lineas */
char *lineptr[];
int nlines;
{
int i;
for (i=0; i < nlines; i++ )
printf("%s\n", lineptr[i]);
}
sort(v,n) /*ordena las cadenas v[0],...v[n-1] en orden creciente */
char *v[];
int n;
{
int gap,i,j;
char *temp;
8.5 Ejemplos de utilizaci´ on de punteros 155
for(gap=n/2; gap>0; gap/=2)
for(i=gap; i<n; i++)
for(j=i-gap; j>=0; j-=gap){
if(strcmp(v[j],v[j+gap])<=0)
break;
temp=v[j];
v[j]=v[j+gap];
v[j+gap]=temp;
}
}
getline(s,lim) /* para la cadena s da su longitud */
char s[];
int lim;
{
int c,i;
i=0;
while(--lim>0 && (c=getchar())!= EOF && c!= ’\n’)
s[i++]=c;
if(c==’\n’)
s[i++]=c;
s[i]=’\0’;
return(i);
}
#define ALLOCSIZE 1000 /*tama~ no del area disponible */
static char allocbuf[ALLOCSIZE];
static char *allocp=allocbuf;
char *alloc(n) /*devuelve apuntador a cadena de caracteres */
int n;
{
if(allocp+n<=allocbuf+ALLOCSIZE){
allocp+=n;
return(allocp-n);
}
else
return(NULL);
}
Observaciones sobre E/S
La entrada y salida del programa anterior est´ an sin especificar. Una
opci´on a utilizar desde el sistema operativo, es direccionar la entrada desde
un fichero, por ejemplo lineas.txt. Para ello basta teclear
$ a.out<lineas.txt
Si adem´ as queremos que la salida se escriba en un fichero, basta direc-
cionar la salida del programa a dicho fichero:
156 8 El lenguaje de programaci´ on C (IV)
$ a.out<lineas.txt>salida.txt
Por ejemplo si en lineas.txt, aparecen los nombres de los alumnos de
M´etodos Num´ericos del curso 98-99. El fichero salida.txt resultante ser´a:
Beato Unai
Blanco Maria Isabel
Cao Rosa Maria
Caracena Jose Antonio
Fuente Rosa Maria de la
Gomez Pedro Alberto
Guisasola Ainhoa
Herran Belen
Juan Mario de
Morillo Alberto
Nunez David
Quintela Monica
Rivas Silvia
Ruiz Diego
Otra forma de hacer lo mismo, esta vez desde dentro del programa, es
la que aparece a continuaci´ on:
/* este es el programa en el fichero fordena.c */
/* ordena alfabeticamente cadenas de caracteres */
/* que lee del fichero lineas.txt */
/* la salida va al fichero salida.txt */
#include<stdio.h>
#include<string.h>
#define MAXLINES 500
#define MAXLENGTH 100
main(){
char *lineptr[MAXLINES];
char linea[MAXLINES][MAXLENGTH];
int c,i,n;
FILE *fp;
FILE *fresul;
fp=fopen("lineas.txt","r");
fresul=fopen("salida.txt", "a");
i=1;
while((lineptr[i] = fgets(&linea[i][0], MAXLENGTH,fp))
!= NULL && (i<MAXLINES))
8.5 Ejemplos de utilizaci´ on de punteros 157
i++;
n=i-1;
for(i=1; i<=n; i++)
{
printf("%s", lineptr[i]);
}
sort(lineptr+1, n);
fprintf(fresul,"\n\n Lineas ordenadas lexicograficamente: \n\n");
for (i=1; i<=n; i++)
{
fprintf(fresul, "%s", lineptr[i]);
}
return 0;
}
sort(lineptr, n)
char *lineptr[];
int n;
{
int gap,i,j;
char *temp;
for ( gap=n/2; gap >0; gap/=2)
for (i=gap; i<n; i++)
for (j=i-gap; j>=0; j-=gap){
if(strcmp(lineptr[j],lineptr[j+gap])<=0)
break;
temp=lineptr[j];
lineptr[j]=lineptr[j+gap];
lineptr[j+gap]=temp;
}
}
El cambio de la entrada de datos a un programa tambi´en es invisible si la
entrada proviene de la salida de otro programa a trav´es de una interconexi´ on,
(pipe). En este caso, la linea de comando
$ prog1.out |prog2.out
ejecuta los programas prog1.out y prog2.out, y conecta la salida est´ andar
de prog1.out con la entrada est´ andar de prog2.out.
En la pr´actica muchos programas leen de un s´olo archivo y escriben en un
´ unico archivo. Para estos programas, la entrada/salida mediante getchar(),
putchar() y printf es totalmente adecuada.
Adem´as varios archivos se pueden unir usando por ejemplo la utiler´ıa
cat de UNIX. Ello hace innecesario aprender a acceder a los archivos desde
158 8 El lenguaje de programaci´ on C (IV)
dentro del programa.
Por ejemplo, con un sencillo programa como el siguiente:
#include<stdio.h>
main()
{
int c;
while((c=getchar())!=EOF)
putchar(issuper(c) ? tolower(c) :c );
}
se traduce la entrada a min´ usculas. Las funciones issuper y tolower son
macros definidas en stdio.h, que convierten en may´ uscula y min´ usculas, res-
pectivamente, el car´acter le´ıdo. Con la l´ınea de comando:
$ cat lineas.txt lineas2.txt | lower.out> output
se unen los ficheros lineas.txt y lineas2.txt y se ejecuta sobre el fichero
unido, el programa lower.out. La salida aparece en el archivo output.
Cap´ıtulo 9
El lenguaje de programaci´on C (V)
9.1. Tipos de datos definidos por el usuario: es-
tructuras
Una estructura es un conjunto de diversas variables, posiblemente de
distinto tipo, agrupadas bajo un mismo nombre, lo que hace m´ as eficiente
su manejo.
Las estructuras ayudan a organizar datos complicados, particularmen-
te en programas grandes, puesto que permiten considerar como unidad un
conjunto de variables relacionadas, en lugar de tratarlas como unidades in-
dependientes.
Una fecha tiene por ejemplo varios componentes, d´ıa, mes, a˜ no, d´ıa del
a˜ no y nombre del mes. Estas cinco variables se pueden agrupar bajo una
estructura como la siguiente:
struct date {
int day;
int month;
int year;
int yearday;
char mon_name[4];
};
La palabra clave struct introduce la declaraci´ on de una estructura, que
no es m´as que un conjunto de declaraciones encerradas entre llaves. Op-
cionalmente se puede a˜ nadir un nombre despu´es de la palabre clave struct
(como el date en el ejemplo anterior). A dicho nombre se le denomina nom-
bre de la estructura y se puede emplear en declaraciones posteriores como
159
160 9 El lenguaje de programaci´ on C (V)
abreviatura de la estructura.
Los elementos o variables citados en una estructura se denominan miem-
bros. Un miembro de una estructura, la propia estructura y una variable
ordinaria pueden tener el mismo nombre; siempre que se puedan distinguir
a trav´es del contexto.
La llave de cierre que termina la declaraci´ on de la lista de miembros
puede ir seguida de una lista de variables
struct{...} x, y, z;
en el sentido de que x, y, z son declaradas las tres como estructuras del
mismo tipo y se reserva la memoria correspondiente.
Si la declaraci´ on de una estructura no va seguida de una lista de variables,
no se reserva memoria, y se considera ´ unicamente descrita una plantilla de
la estructura. Posteriormente se puede utilizar dicha plantilla, para definir
estructuras del mismo tipo. Por ejemplo, una vez constru´ıda la plantilla de
la estructura date,
struct date d;
declara la variable d como una estructura de tipo date. Si la variable
estructura es de tipo externa o est´atica, se puede inicializar de la siguiente
forma:
struct date d={4 ,7 ,1776, 186, "Jul"};
Si nos queremos referir a un elemento de la estructura, tenemos que
utilizar una construcci´ on de la forma
nombre_de_estructura.miembro
El operador miembro de estructura ”¸conecta el nombre de la estructura
y el del miembro. Para dar un valor a la variable bisiesto a partir de la
estructura date se har´a:
bisiesto=d.year % 4 == 0 && d.year % 100 != 0
|| d.year % 400 == 0;
9.2 Operaciones con estructuras 161
o para comprobar el nombre del mes:
if (strcmp (d.mon_name, "Aug") == 0) . . .
o para convertir a min´ uscula el primer car´ acter del nombre del mes:
d.mon_name[0] = lower(d.mon_name[0]);
Por otra parte las estructuras se pueden anidar. Un registro de la n´ omina
puede ser por ejemplo:
struct person{
char name[NAMESIZE]; /* dimension = tama~no del nombre */
char address[ADRSIZE];
long zipcode;
long ss_number;
double salary;
struct date birthdate;
struct date hiredate;
};
La estructura persona tiene dos fechas. Si declaramos la variable emp
como una estructura del mismo tipo:
struct person emp;
entonces
emp.birthdate.month
se refiere al mes de nacimiento. El operador de miembro de una estruc-
tura es asociativo de izquierda a derecha.
9.2. Operaciones con estructuras
Con una variable declarada como una estructura pueden realizarse las
siguientes operaciones:
162 9 El lenguaje de programaci´ on C (V)
1. Tomar su direcci´on mediante el operador &.
2. Acceder a uno de sus miembros.
3. Asignar una estructura a otra con el operador de asignaci´ on.
Esta ´ ultima operaci´ on es posible s´olo en las nuevas versiones de C, pero
no en el C est´andar. En general si se desean hacer programas portables, hay
que tener cuidado con lo que no se puede hacer en C est´andar.
Si queremos copiar una estructura como una unidad, o pasarla como
argumento a una funci´ on, tendremos que utilizar punteros.
Por otra parte, las estructuras autom´ aticas no se pueden inicializar, s´ olo
pueden inicializarse estructuras externas o est´aticas.
Como ejemplos de uso, volveremos a escribir las funciones de conversi´ on
de fechas que vimos en el Tema 8, utilizando estructuras. Como no es po-
sible pasar una estructura a una funci´ on como argumento, pasaremos las
componentes por separado, o bien pasaremos un puntero a la estructura.
La primera alternativa emplea day of year igual que se escribi´ o en el
Tema 8.
d.yearday = day_of_year(d.year, d.month, d.day);
La otra alternativa es pasar un puntero. Si hemos declarado hiredate
(fecha de contrato) como una estructura del tipo date, es decir:
struct date hiredate;
hay que modificar la funci´ on day of year, para que su argumento sea un
puntero y no una lista de variables. La modificaci´ on puede ser:
day_of_year (pd) /* convertir en dia del a~ no */
struct date *pd;
{
int i, day, bisiesto;
day = pd -> day;
bisiesto=pd -> year % 4 == 0 && pd -> year % 100 != 0
|| pd -> year % 400 == 0;
for (i=1; i< pd -> month; i++)
day += day_tab[bisiesto][i];
return (day);
}
9.2 Operaciones con estructuras 163
con lo que la llamada a la funci´ on ser´ıa:
d.yearday = day_of_year(&hiredate);
La declaraci´on
struct date *pd;
especifica que pd es un puntero a una estructura de tipo date. La notaci´ on
pd− >year sirve para describir el operador de acceso al miembro de una
estructura. En este caso apunta al miembro denominado year. Puesto que
pd apunta a una estructura, el miembro year se puede referenciar mediante
(*pd).year
pero dada la frecuencia con la que se emplean los punteros a estructu-
ras, se ha introducido la notaci´ on − > m´as abreviada que la anterior. En
(∗pd).year hacen falta los par´entesis, ya que la precedencia del operador de
miembro de estructura es mayor que la del operador *. Tanto − > como
son asociativos de izquierda a derecha, de manera que
p -> q -> memb
emp.birthdate.month
equivalen a
(p -> q) -> memb
(emp.birthdate).month
Podemos modificar en el mismo sentido, la funci´on month day para uti-
lizar estructuras:
month_day (pd) /* convertir a mes y dia */
struct date *pd;
{
int i, bisiesto;
bisiesto=pd -> year % 4 == 0 && pd -> year % 100 != 0
|| pd -> year % 400 == 0;
pd -> day = pd -> yearday;
for (i=1; pd -> day > day_tab[bisiesto][i]; i++)
pd -> day -= day_tab[bisiesto][i];
pd -> month =i;
}
164 9 El lenguaje de programaci´ on C (V)
Los operadores de estructuras − > y junto con los par´entesis y los cor-
chetes tienen la m´axima precedencia entre todos los operadores. Por ejemplo,
dada la declaraci´ on
struct{
int x;
int *y;
} *p;
la sentencia
++p -> x
incrementa x en lugar de p, ya que los par´entesis impl´ıcitos son ++(p− >
x). Se deben utilizar par´entesis para alterar el orden de aplicaci´on de los
operadores. (+ + p)− > x incrementa p antes de acceder a x, mientras que
(p++)− > x lo incrementa despu´es. En este ´ ultimo caso, no se necesitar´ıan
par´entesis ¿verdad?
Las estructuras tienen una aplicaci´on bastante ´ util en arrays de variables
relacionadas entre s´ı. Vamos a analizar un ejemplo de aplicaci´ on en este
sentido.
Consid´erese un programa que intenta contar las veces que aparece una
palabra clave de C. En tal programa necesitar´ıamos un array de caracteres
para almacenar los nombres de las distintas palablas claves y otro para
contar el n´ umero de ocurrencias.
Una posibilidad consistir´ıa en mantener dos arreglos en paralelo keyword
y keycount
char *keyword[nkeys];
int keycount[nkeys];
Sin embargo el hecho de que los dos arreglos se vayan a manipular en
paralelo indica otra posible organizaci´ on. Cada palabra clave es en realidad
un par:
char *keyword;
int keycount;
9.2 Operaciones con estructuras 165
y se necesita un array de pares. Se puede entonces declarar una estructura
de la forma
struct key {
char *keyword;
int keycount;
}
keytab[nkeys];
que define un array keytab de estructuras de este tipo y le asigna me-
moria. Cada elemento del array es una estructura. Otra posible definici´ on
es:
struct key {
char *keyword;
int keycount;
};
struct key keytab[nkeys];
La estructura keytab puede inicializarse de una vez para siempre con el
conjunto de nombres de las palabras clave de C. En cuanto al n´ umero de
ocurrencias, se inicializa en todas ellas a 0.
struct key {
char *keyword;
int keycount;
} keytab[]={
"break", 0,
"case", 0,
"char", 0,
"continue", 0,
/* . . . */
"unsigned", 0,
"while", 0
};
Los inicializadores se agrupan por pares. Tambi´en se podr´ıan agrupar
entre llaves los inicializadores en cada fila.
{"break", 0},
{"case", 0},
. . .
166 9 El lenguaje de programaci´ on C (V)
Estas llaves no son necesarias si los inicializadores son variables simples
o cadenas de caracteres y si adem´as est´an todos presentes. La dimensi´ on del
array en el caso de que est´en todos presentes la calcula el compilador y [] se
deja vac´ıo.
Escribimos a continuaci´ on el programa que cuenta palabras clave. Se de-
fine inicialmente keytab. El programa principal lee datos llamando repetida-
mente a getword que busca en keytab mediante una funci´ on que implementa
un algoritmo de b´ usqueda binaria. (Dicha funci´ on de b´ usqueda necesita que
las palabras clave en keytab est´en ordenadas en orden creciente, o lo que es
lo mismo en orden alfab´etico).
#define MAXWORD 20
main() /*cuenta palabras clave de C */
{
int n, t;
char word[MAXWORD];
while ((t = getword(word,MAXWORD))!= EOF)
if(t == LETTER)
if ((n = binary(word, keytab, NKEYS)) >= 0)
keytab[n].keycount++;
for (n=0; n < NKEYS; n++)
if(keytab[n].keycount > 0)
printf("%4d %s\n",
keytab[n].keycount, keytab[n].keyword);
}
binary(word, tab, n) /*busca palabra en tab[0]...tab[n-1]*/
char *word;
struct key tab[];
int n;
{
int low, high, mid, cond;
low = 0;
high = n-1;
while (low <= high){
mid = (low+high) / 2;
if ((cond = strcmp(word, tab[mid].keyword)) < 0)
high = mid - 1;
else if (cond > 0)
low = mid + 1;
else
return(mid);
}
return(-1);
}
9.2 Operaciones con estructuras 167
La funci´ on getword la veremos un poco m´as adelante. De ella s´olo sabe-
mos por ahora, que devuelve LETTER cada vez que encuentra una palabra,
y copia la palabra en su primer argumento word.
La constante NKEY S es el n´ umero de palabras que caben en keytab.
Esta cantidad puede determinarse al contar las palabras clave en la tabla,
pero es m´as c´omodo y seguro que esta cantidad se calcule autom´aticamente,
puesto que adem´as dicha lista puede variar. Una posibilidad es utilizar el
hecho de que el tama˜ no del array se conoce en el momento de la compilaci´ on.
El n´ umero de elementos es
tama˜ no de keytab (40) / tama˜ no de struct key (2)
C tiene un operador unitario llamado sizeof que calcula el tama˜ no de
cualquier objeto en el momento de la compilaci´ on. La expresi´ on sizeof(objeto)
devuelve un entero igual al tama˜ no del objeto especificado. Dicho objeto
puede ser una variable, un array o una estructura, el nombre de un tipo
b´ asico como int o double, o el nombre de una estructura. En nuestro caso,
el n´ umero de palabras clave se obtiene de la siguiente manera.
#define NKEYS (sizeof(keytab) /sizeof(struct key)) /% (20) %/
Vamos a describir ahora la funci´ on getword. Dicha funci´ on devuelve la
constante LETTER si el s´ımbolo es una palabra (cadena de letras y d´ıgitos
que comienza con una letra, o bien un ´ unico car´ acter). Si se alcanza el final
del archivo, la funci´ on devuelve EOF.
getword(w, lim) /* leer la siguiente palabra */
char *w;
int lim;
{
int c, t;
if(type(c = *w++ = getch()) != LETTER){
*w = ’\0’;
return (c);
}
while (--lim >0) {
t = type(c = *w++ =getch());
if (t != LETTER && t != DIGIT) {
ungetch(c);
break;
}
}
*(w-1) = ’\0’;
return(LETTER);
}
168 9 El lenguaje de programaci´ on C (V)
getword utiliza las rutinas getch y ungetch que vimos en el Tema 8.
Cuando acaba la recolecci´on de una cadena alfanum´erica se llama a la rutina
ungetch para devolver el ´ ultimo car´ acter le´ıdo, que ser´ a el siguiente en ser
procesado en la pr´ oxima llamada.
getword llama a type para determinar el tipo de car´ acter de entrada.
La funci´ on type descrita a continuaci´ on, es v´alida s´ olo para el conjunto de
caracteres ASCII.
type(c) /*devuelve el tipo de un caracter ASCII*/
int c;
{
if(c >= ’a’ && c <= ’z’ || c >= ’A’ && c <= ’Z’)
return LETTER;
else if (c >= ’0’ && c <= ’9’)
return DIGIT;
else
return c;
}
Las constantes simb´olicas LETTER y DIGIT pueden tener cualquier
valor que no entre en conflicto con un car´ acter alfanum´erico o EOF; las
elecciones obvias son:
#define LETTER ’a’
#define DIGIT ’0’
El programa correspondiente est´ a implementado y su nombre es cuenta.c.
9.3. Punteros a estructuras y estructuras ligadas.
Arboles binarios. Ejemplos de uso
Vamos a volver a escribir un programa en C que cuente palabras clave.
Esta vez vamos a utilizar punteros en lugar de ´ındices del array. Hay que
cambiar main y binary, que quedar´ıan de la siguiente forma:
main() /*cuenta palabras clave de C */
{
int t;
char word[MAXWORD];
struct key *binary(), *p;
while ((t = getword(word,MAXWORD))!= EOF)
if(t == LETTER)
9.3 Punteros a estructuras y estructuras ligadas. Arboles binarios. Ejemplos de uso169
if ((p = binary(word, keytab, NKEYS)) != NULL)
p->.keycount++;
for (p = keytab; p < keytab + NKEYS; p++)
if(p -> keycount > 0)
printf("%4d %s\n", p->keycount, p ->keyword);
}
struct key *binary(word, tab, n) /*busca palabra en
tab[0]...tab[n-1]*/
char *word;
struct key tab[];
int n;
{
int cond;
struct key *low = &tab[0];
struct key *high = &tab[n-1];
struct key *mid;
while (low <= high){
mid = low + (high-low) / 2;
if ((cond = strcmp(word, mid -> keyword)) < 0)
high = mid - 1;
else if (cond > 0)
low = mid + 1;
else
return(mid);
}
return(NULL);
}
En la nueva versi´ on se aprecian varios cambios. La declaraci´on de binary
indica que devuelve un puntero a una estructura de tipo key, en lugar de
un entero; esta declaraci´ on hay que hacerla en main y en binary. Si binary
encuentra la palabra, devuelve un puntero a ella, en caso contrario devuelve
NULL.
Adem´as el acceso a los elementos de keytab se hace a trav´es de punteros.
Esto provoca bastante cambio en binary: el c´ alculo del elemento central no
puede hacerse como antes
mid =
(low+high)
2
ya que la suma de los punteros no produce ning´ un tipo de respuesta ´ util
(tampoco dividir por 2), que de hecho es ilegal. El c´ alculo hay que cambiarlo
por
mid = low +
(high−low)
2
que hace que mid apunte al elemento situado en la mitad de low y high.
170 9 El lenguaje de programaci´ on C (V)
Tambi´en hay que observar los inicializadores de low y high. Se puede
inicializar un puntero con la direcci´ on de un objeto definido previamente,
que es en este caso lo que se hace.
En main se tendr´a:
for (p = keytab; p < keytab + NKEYS; p++)
Si p es un puntero a una estructura, cualquier operaci´ on aritm´etica que
se realice con ´el tendr´ a en cuenta el tama˜ no de la estructura, por lo que
p + + incrementa p en la cantidad adecuada para acceder al siguiente ele-
mento del arreglo de estructuras. No se debe suponer que el tama˜ no de una
estructura es la suma de los tama˜ nos de cada uno de los miembros, ya que
los requerimientos de alineaci´ on de los diferentes objetos pueden ocasionar
huecos en la estructura.
Por ´ ultimo hay que comentar algo sobre el formato de un programa.
Cuando una funci´ on devuelve un tipo complicado como
struct key *binary(word, tab, n)
puede ser dif´ıcil encontrar el nombre de la funci´ on. Una forma alternativa
de escribirlo, quiz´a m´as clara puede ser
struct key *
binary(word, tab, n)
Vamos a analizar por ´ ultimo el siguiente ejemplo. Sup´ ongase que se de-
sea contar el n´ umero de ocurrencias de todas las palabras de alg´ un texto.
Dado que de entrada no se conoce la lista de palabras, no se pueden ordenar
para utilizar un algoritmo de b´ usqueda binaria. Tampoco ser´ıa correcta una
b´ usqueda lineal de cada palabra encontrada, puesto que ser´ıa un procedi-
miento muy lento. ¿C´ omo podemos organizar nuestros datos para controlar
eficientemente una lista de palabras arbitrarias?
Una soluci´ on es mantener en todo momento las palabras ordenadas, co-
locando en su sitio cada palabra que llega. Esto no es posible hacerlo, des-
plazando las palabras en un array lineal, pues se emplear´ıa mucho tiempo.
Lo que s´ı podemos utilizar es una estructura de datos denominada ´arbol
binario.
El ´ arbol va a contener un nodo por cada palabra diferente, en cada nodo
tendremos:
9.3 Punteros a estructuras y estructuras ligadas. Arboles binarios. Ejemplos de uso171
un puntero a los caracteres de la palabra
un contador del n´ umero de ocurrencias
un puntero a su hijo izquierdo (que es un nodo)
un puntero a su hijo derecho (que tambi´en es un nodo)
Ning´ un nodo puede tener m´ as de dos hijos, pero puede tener uno o
ninguno. La estructura del ´ arbol est´ a definida, de manera que: dado un
nodo, el sub´ arbol izquierdo contiene palabras que son (lexicogr´ aficamente)
menores que la palabra del nodo, y el sub´ arbol derecho contiene palabras que
son mayores que la palabra del nodo actual. Para averiguar si una palabra ya
est´a en el ´arbol, se comienza comparando dicha palabra con la almacenada
en el nodo ra´ız del ´arbol.
Si coinciden, es que la palabra ya est´ a en el ´arbol, en este caso se ac-
tualiza el contador de ocurrencias. Si la palabra es menor, se contin´ ua la
b´ usqueda por el sub´ arbol izquierdo; si la palabra es mayor, es investigada
en el sub´ arbol derecho. Si no existe descendiente en la direcci´on examinada
es que la palabra no est´a en el ´arbol, y su lugar es el del descendiente que
no existe.
Este proceso de b´ usqueda es claramente recursivo, puesto que la b´ usque-
da en cualquier nodo se realiza de igual manera en cualquiera de sus hijos.
La estructura de cada nodo, puede estar definida de la siguiente manera:
struct tnode{ /* el nodo*/
char *word; /* puntero a los caracteres */
int count;
struct tnode *left; /*hijo izquierdo */
struct tnode *right; /*hijo derecho */
};
Es ilegal que una estructura contenga un elemento que sea ella misma,
pero la sentencia
struct tnode *left; /*hijo izquierdo */
declara left como un puntero a un nodo, no como un nodo en s´ı.
El c´ odigo del programa es muy corto ya que utiliza funciones que han sido
definidas anteriormente. Dichas funciones son getword que lee la siguiente
palabra de la entrada y alloc que obtiene memoria para las palabras.
172 9 El lenguaje de programaci´ on C (V)
El programa principal lee palabras llamando a getword y las a˜ nade al
´arbol mediante la funci´ on tree que luego veremos:
#define MAXWORD 20
main() /* cuenta frecuencia de palabras*/
{
struct tnode *root, *tree();
char word[MAXWORD];
int t;
root=NULL;
while(( t=getword(word, MAXWORD))!= EOF)
if(t == LETTER)
root = tree(root, word);
treeprint(root);
}
main presenta una palabra a comparar comenzando por la ra´ız del ´ arbol.
En cada etapa, dicha palabra se compara con la almacenada en el nodo
y se prosigue la b´ usqueda por el sub´ arbol derecho o izquierdo mediante
una llamada recursiva a tree. Pueden darse dos posibilidades, si la palabra
coincide con una que est´ a en el ´arbol, se incrementa el contador, mientras
que si se encuentra un puntero nulo, esto quiere decir que hay que crear y
a˜ nadir un nodo al ´ arbol. Si se crea un nodo, tree devuelve su puntero para
poder instalarlo en el nodo padre.
struct tnode *tree(p,w) /*introduce w en p o a continuaci´ on de p */
struct tnode *p;
char *w;
{
struct tnode *talloc();
char *strsave();
int cond;
if(p == NULL)
{ /* llega una nueva palabra */
p=talloc();
p->word = strsave(w);
p->count = 1;
p->left = p->right = NULL;
}
else if ((cond = strcmp(w, p->word)) == 0)
p->count++; /*palabra repetida */
else if (cond < 0) /* las menores va al izdo.*/
p->left=tree(p->left, w);
else
p->right=tree(p->right, w);/*las mayores al dcho. */
return (p);
}
La memoria para crear el nuevo nodo se obtiene llamando a la rutina
talloc, adaptaci´ on de la rutina alloc vista anteriormente. Devuelve un pun-
9.3 Punteros a estructuras y estructuras ligadas. Arboles binarios. Ejemplos de uso173
tero a la zona de memoria donde almacenar el nodo.
La nueva palabra se copia a una zona vac´ıa mediante strsave, el contador
se inicializa y los dos hijos toman el valor NULL. Esta parte de la rutina se
ejecuta al alcanzar un extremo del ´arbol para a˜ nadir un nuevo nodo.
freeprint imprime el ´arbol en orden sim´etrico; dado un nodo, se imprime
el sub´ arbol izquierdo (todas las palabras menores que la del nodo), el propio
nodo, y luego el sub´ arbol derecho (todas las palabras mayores).
treeprint(p) /*imprime el ´ arbol recursivamente */
struct tnode *p;
{
if(p!= NULL)
{
treeprint(p->left);
printf("%4d %s\n", p->count, p->word);
treeprint(p->right);
}
}
Por ´ ultimo una observaci´ on pr´ actica: el ´arbol puede estar desequilibrado
o no balanceado, si las palabras no aparecen aleatoriamente y el tiempo
de ejecuci´on puede, entonces, ser muy alto. En el peor de los casos, si las
palabras ya est´ an en orden, el programa realiza una b´ usqueda lineal.
Antes de acabar con el ejemplo, vamos a realizar una peque˜ na reflexi´ on
sobre administradores de memoria. Parece deseable que a lo largo de un
programa exista un ´ unico administrador de memoria, aunque maneje dife-
rentes tipos de objetos. Pero si un gestor acepta peticiones de apuntadores
a char y a struct tnode se plantean dos cuestiones. En primer lugar ¿c´ omo
satisfacer los requerimientos de casi todas las computadoras que imponen
restricciones de alineaci´on a diferentes tipos de objetos? (por ejemplo, los
enteros a menudo suelen ubicarse en una direcci´ on par). En segundo lugar,
¿c´omo se puede expresar el hecho de que alloc devuelva diferentes tipos de
apuntadores?.
La declaraci´on del tipo en alloc puede ser un inconveniente para cualquier
lenguaje que realice una comprobaci´ on de tipos. En C, un procedimiento es
declarar que alloc devuelva un apuntador a caracteres y forzarlo mediante
una declaraci´ on encastrada “cast” al tipo deseado.
Si p se declara como
char *p;
174 9 El lenguaje de programaci´ on C (V)
(struct tnode *)p;
lo convierte en un puntero a la estructura tnode. De esta forma la rutina
talloc se escribe:
9.3 Punteros a estructuras y estructuras ligadas. Arboles binarios. Ejemplos de uso175
struct tnode *talloc()
{
char *alloc();
return((struct tnode *)alloc(sizeof(struct tnode)));
}
Observar que devuelve un puntero a estructura, de la posici´ on donde
puede empezar a ser almacenada una estructura de ese tama˜ no.
176 9 El lenguaje de programaci´ on C (V)
Cap´ıtulo 10
Soluci´ on de sistemas de ecuaciones
lineales
Un sistema de ecuaciones lineales con m ecuaciones y n inc´ ognitas (n ≤
m)
a
11
x
1
+ +a
1n
x
n
= b
1

a
m1
x
1
+ +a
mn
x
n
= b
m
puede escribirse con notaci´ on matricial como Ax = b. Si b = 0 el sistema
se dice homog´eneo. Un sistema homog´eneo siempre tiene la soluci´on trivial
x = 0. Si rang(A) = r < n, el sistema Ax = 0 tiene (n − r) soluciones
linealmente independientes.
El sistema anterior puede escribirse de la forma
x
1
a
·1
+ +x
n
a
·n
= b
que expresa el vector b como una combinaci´ on lineal de los vectores columna
de A. Con esta representaci´on, se puede ver que la condici´ on para que un
sistema lineal tenga soluci´on es que b ∈ R(A), donde R(A) es el subespacio
generado por todas las columnas de A. Esto puede escribirse diciendo que
rang(A, b) = rang(A)
Si m = n = r, entonces R(A) = R
n
y el sistema puede resolverse para
cualquier b. En este caso la soluci´on es ´ unica y se obtiene como x = A
−1
b.
Si sucede que rang(A) = r < n, entonces el sistema Ax = b tiene n − r
soluciones.
177
178 10 Soluci´ on de sistemas de ecuaciones lineales
10.1. M´etodos directos para la resoluci´ on de sis-
temas de ecuaciones lineales
Entenderemos como m´etodo directo aqu´el que necesita de un n´ umero
finito de pasos para obtener la soluci´ on, en este caso de un sistema de ecua-
ciones lineales. Resultan m´ as eficientes para sistemas Ax = b en los que la
matriz A es densa (la mayor´ıa de sus elementos son no nulos). Sin embar-
go, si la matriz es dispersa (una gran porci´ on de sus elementos son nulos)
suelen ser m´as apropiados los m´etodos iterativos. Dichos m´etodos propor-
cionan una sucesi´ on de soluciones aproximadas que converge a la verdadera
soluci´ on cuando el n´ umero de iteraciones tiende a infinito. En general, dan
buenas soluciones con pocas operaciones elementales (no hay casi error de
redondeo) y trabajan, principalmente con los elementos no nulos de la ma-
triz, lo que permite que sean aplicados a sistemas de dimensi´on mayor que
un m´etodo directo.
La elecci´on entre un m´etodo directo y un m´etodo iterativo depende nor-
malmente del tama˜ no del sistema y de la forma (densa o dispersa) de la
matriz A. En este cap´ıtulo y en el siguiente, nos dedicaremos a comentar
algunos de los m´etodos directos m´as habituales.
10.1.1. Sistemas triangulares
Los sistemas en los que la matriz A es triangular, son especialmente
sencillos de resolver.
Consideremos por ejemplo un sistema triangular superior:
u
11
x
1
+ + + u
1n
x
n
= b
1

u
n−1,n−1
x
n−1
+ u
n−1,n
x
n
= b
n−1
u
nn
x
n
= b
n
Si u
ii
,= 0, ∀i = 1, ..., n, entonces las inc´ognitas pueden ser despejadas y
obtenidas en el orden x
n
, x
n−1
, ..., x
1
.
x
n
=
b
n
u
nn
x
n−1
=
b
n−1
−u
n−1n
x
n
u
n−1,n−1

10.2 Eliminaci´ on gaussiana 179
x
1
=
b
1
−u
1n
x
n
− −u
12
x
2
u
11
sistema que puede ser escrito en forma compacta como:
x
i
=
b
i

n
k=i+1
u
ik
x
k
u
ii
, i = n, n −1, ..., 1
Como las inc´ognitas se obtienen en orden decreciente, el algoritmo se
denomina de sustituci´ on hacia atr´ as. Si el sistema fuese triangular inferior,
la resoluci´ on ser´ıa similar. Suponiendo los coeficientes en la diagonal prin-
cipal no nulos, l
ii
,= 0, ∀i = 1, ..., n, las variables, en este caso se obtienen
sustituyendo hacia adelante, a partir de la expresi´ on:
x
i
=
b
i

i−1
k=1
l
ik
x
k
l
ii
, i = 1, 2, ..., n
De las expresiones anteriores, se deduce que la obtenci´on de las n inc´ ogni-
tas necesita n divisiones. Adem´ as las sumas y multiplicaciones son
n

i=1
(i −1) =
n(n −1)
2

n
2
2
Podemos decir entonces que la complejidad computacional, para la ob-
tenci´ on de las soluciones en un sistema lineal triangular es O(
n
2
2
).
10.2. Eliminaci´ on gaussiana
Se trata del m´etodo m´as importante de resoluci´on de sistemas de ecua-
ciones lineales. La idea consiste en eliminar inc´ ognitas de forma sistem´atica
hasta que el sistema tenga forma triangular, y entonces resolverlo. Conside-
remos el sistema:
a
11
x
1
+ +a
1n
x
n
= b
1

a
n1
x
1
+ +a
nn
x
n
= b
n
o Ax = b, donde la matriz A = (a
ij
) es no singular. En este caso el sistema
anterior tiene soluci´on ´ unica.
Si a
11
,= 0, podemos eliminar x
1
de las (n−1) ´ ultimas ecuaciones restando
de la i-´esima ecuaci´on la primera multiplicada por m
i1
=
a
i1
a
11
, i = 2, ..., n
180 10 Soluci´ on de sistemas de ecuaciones lineales
Las n −1 ´ ultimas ecuaciones se pueden escribir despu´es de dicha opera-
ci´on como:
a
(2)
22
x
2
+ +a
(2)
2n
x
n
= b
(2)
2

a
(2)
n2
x
2
+ +a
(2)
nn
x
n
= b
(2)
n
donde los coeficientes:
a
(2)
ij
= a
ij
−m
i1
a
1j
b
(2)
i
= b
i
−m
i1
b
1
i = 2, ..., n
Este es un sistema de (n−1) ecuaciones con (n−1) inc´ ognitas. Si a
(2)
22
,= 0,
podemos eliminar de manera similar x
2
de las n −2 restantes ecuaciones.
Si llamamos m
i2
=
a
(2)
i2
a
(2)
22
, i = 3, ..., n, los coeficientes del sistema resultante
son:
a
(3)
ij
= a
(2)
ij
−m
i2
a
(2)
2j
b
(3)
i
= b
(2)
i
−m
i2
b
(2)
2
i = 3, ..., n
Los elementos a
11
, a
(2)
22
, a
(3)
33
, ... se denominan elementos de pivotaje o
pivotes. Si todos ellos son no nulos, podemos continuar la eliminaci´ on y
despu´es de (n −1) pasos llegar a:
a
(n)
nn
x
n
= b
(n)
n
Si escribimos el sistema triangular resultante:
a
(1)
11
x
1
+ + + a
(1)
1n
x
n
= b
(1)
1
a
(2)
22
x
2
+ + a
(2)
2n
x
n
= b
(2)
2

a
(n−1)
n−1,n−1
x
n−1
+ a
(n−1)
n−1,n
x
n
= b
(n−1)
n−1
a
(n)
nn
x
n
= b
(n)
n
Se˜ nalar que el t´ermino independiente b es transformado de la misma
forma que las columnas de A.
Adem´as la descripci´on de la eliminaci´ on se simplifica si expresamos b
como la ´ ultima columna de A
a
(k)
i,n+1
= b
(k)
i
, i, k = 1, 2..., n
10.2 Eliminaci´ on gaussiana 181
Entonces las f´ ormulas se pueden resumir como sigue: La eliminaci´ on se
realiza en (n −1) pasos, k = 1, ..., n −1. En el paso k los elementos a
(k)
ij
con
i, j > k, son transformados de acuerdo con:
m
ik
=
a
(k)
ik
a
(k)
kk
,
a
(k+1)
ij
= a
(k)
ij
−m
ik
a
(k)
kj
(10.1)
i = k + 1, ..., n j = k + 1, ..., n + 1
Por otra parte si tenemos distintos sistemas de ecuaciones con la misma
matriz A:
Ax
1
= b
1
, Ax
2
= b
2
, , Ax
p
= b
p
dichos sistemas pueden ser tratados simult´aneamente, adjuntando b
j
como
la n + j-´esima columna de A. La ´ unica diferencia con el algoritmo dado en
(10.1), es que el ´ındice j toma los valores k + 1, ..., n, ..., n + p. Despu´es de
la eliminaci´ on tendremos p sistemas triangulares para resolver.
182 10 Soluci´ on de sistemas de ecuaciones lineales
Coste operativo
Vamos a calcular el n´ umero de operaciones aritm´eticas necesario para
reducir mediante la eliminaci´ on gaussiana, un sistema con p t´erminos inde-
pendientes, a su forma triangular. De las ecuaciones (10.1) se sigue que en
el paso k se realizan (n−k) divisiones y (n−k)(n−k+p) multiplicaciones y
sumas. Si estamos interesados en ver el coste operativo para valores grandes
de n, podemos eliminar el n´ umero de divisiones, de manera que el n´ umero
total de operaciones es aproximadamente:
n−1

k=1
(n −k)(n −k +p) =
n−1

k=1
(n −k)
2
+p
n−1

k=1
(n −k) =
=
(n −1)n(2n −1)
6
+p
(n −1)n
2
=
=
n
2
(n −1)
3

n(n −1)
6
+
p
2
n(n −1) =
=
n
2
(n −1)
3
+ (3p −1)
n(n −1)
6

1
3
n
3
+
(3p −1)
6
n
2
donde cada operaci´ on es en realidad, una suma y una multiplicaci´ on. Si se
separan las sumas de las multiplicaciones el coste operativo var´ıa. Como el
n´ umero de operaciones necesario para resolver un sistema triangular es
n
2
2
,
tenemos que cuando p = 1 y n suficientemente grande, el trabajo principal
del algoritmo recae en la reducci´on del sistema a una forma triangular.
10.3. Estabilidad num´erica y su mejora. Pivotado
total y parcial. Complejidad algor´ıtmica
Hemos visto que el algoritmo de eliminaci´ on gaussiana es inoperativo si
alg´ un elemento a
(k)
kk
= 0.
Vamos a ver c´omo remodelar el m´etodo anterior para hacer el pro-
cedimiento operativo. La idea es buscar en este caso otro elemento a
(k)
ik
,
i = k + 1, ..., n, en la columna k que sea distinto de 0. Dicho elemento tie-
ne que existir, puesto que en otro caso, las k primeras filas de A ser´ıan
linealmente dependientes y recordemos que A es no singular. Sea a
(k)
rk
,= 0 el
elemento buscado, entonces podemos intercambiar las filas k y r y proceder
con la eliminaci´ on. De ´esto se sigue que cualquier sistema no singular de
ecuaciones puede ser reducido a una forma triangular mediante la elimina-
ci´on gaussiana combinada con un intercambio de filas.
Para asegurar la estabilidad num´erica, a menudo es necesario realizar el
10.3 Estabilidad num´erica y su mejora. Pivotado total y parcial. Complejidad algor´ıtmica183
intercambio de filas incluso cuando el pivote no es ex´ actamente 0, pero es
cercano a 0.
Ejemplo 10.1
x
1
+ x
2
+ x
3
= 1
x
1
+ 1,0001x
2
+ 2x
3
= 2
x
1
+ 2x
2
+ 2x
3
= 1
El sistema triangular que resulta es:
x
1
+ x
2
+ x
3
= 1
0,0001x
2
+ x
3
= 1
9999x
3
= 10000
Si realizamos las sustituci´on hacia atr´ as y permitimos tres decimales:
x
3
= 1, x
2
= 0, x
1
= 0
mientras que si permitimos cuatro decimales:
x
3
= 1,0001, x
2
= −1,0001, x
1
= 1
Si resolvemos el sistema con pivotaje, cambiando las filas 2 y 3, el sistema
triangular que obtenemos es:
x
1
+ x
2
+ x
3
= 1
x
1
+ 2x
2
+ 2x
3
= 1
x
1
+ 1,0001x
2
+ 2x
3
= 2

x
1
+ x
2
+ x
3
= 1
x
2
+ x
3
= 0
0,9999x
3
= 1
Resolviendo con tres decimales:
x
3
= 1, x
2
= −1, x
1
= 1
y si permitimos cuatro decimales:
x
3
= 1,0001, x
2
= −1,0001, x
1
= 1
De este modo para prevenir posibles errores, es necesario elegir el pivote
en el paso k, de una de las siguientes maneras:
Pivotaje parcial
184 10 Soluci´ on de sistemas de ecuaciones lineales
Elegir r como el menor entero para el que
[a
(k)
rk
[ = m´ax [a
(k)
ik
[, k ≤ i ≤ n
k = 1, ..., n −1 e intercambiar filas k y r
a
(k+1)
ij
=
_
¸
¸
¸
¸
¸
¸
¸
¸
_
¸
¸
¸
¸
¸
¸
¸
¸
_
a
(k)
ij
_
i = 1, ..., k, ∀j
i = k + 1, ..., n, j = 1, ..., k −1
0 i = k + 1, ..., n, j = k
a
(k)
ij
−m
ik
a
(k)
kj
j = k + 1, ..., n + 1
donde m
ik
=
a
(k)
ik
a
(k)
kk
, i = k + 1, ..., n
10.3 Estabilidad num´erica y su mejora. Pivotado total y parcial. Complejidad algor´ıtmica185
Pivotaje total
Elegir r y s como los menores enteros para los que
[a
(k)
rs
[ = m´ax [a
(k)
ij
[, k ≤ i, j ≤ n
k = 1, ..., n −1 e intercambiar filas k y r y las columnas k y s
a
(k+1)
ij
=
_
¸
¸
¸
¸
¸
¸
¸
¸
_
¸
¸
¸
¸
¸
¸
¸
¸
_
a
(k)
ij
_
i = 1, ..., k, ∀j
i = k + 1, ..., n, j = 1, ..., k −1
0 i = k + 1, ..., n, j = k
a
(k)
ij
−m
ik
a
(k)
kj
j = k + 1, ..., n + 1
donde m
ik
=
a
(k)
ik
a
(k)
kk
, i = k + 1, ..., n
En cuanto al coste operativo, hemos visto que en el m´etodo de elimina-
ci´on gaussiana sin pivotaje para un sistema de ecuaciones, necesita en torno
a
n
3
3
operaciones.
En el caso de pivotaje parcial, en cada paso k hay que a˜ nadir un nuevo
tipo de operaci´ on: b´ usqueda del m´ aximo de (n −k + 1) valores.
Una forma de determinar el m´ aximo de r elementos (v
1
, ..., v
r
) es:
Comparar v
1
con v
2
: obtenemos m´ax¦v
1
, v
2
¦.
Comparar m´ ax¦v
1
, v
2
¦ con v
3
: obtenemos m´ax¦v
1
, v
2
, v
3
¦.

Comparar m´ ax¦v
1
, ..., v
r−1
¦ con v
r
: obtenemos m´ax¦v
1
, ..., v
r
¦.
El n´ umero de comparaciones necesarias es r−1. De manera que el n´ umero
total de operaciones es aproximadamente:
n
3
3
+
n−1

k=1
(n −k) ≈
n
3
3
+
n
2
2
En el caso del pivotaje total, en cada paso k hay que realizar la b´ usqueda
del m´aximo de (n − k + 1)
2
elementos, de manera que el n´ umero total de
operaciones es:
n
3
3
+
n−1

k=1
[(n −k + 1)
2
−1] =
n
3
3
+ [n
2
+ (n −1)
2
+... + 2
2
] −(n −1) =

n
3
3
+
n
3
3
=
2n
3
3
es decir el n´ umero de operaciones se duplica.
186 10 Soluci´ on de sistemas de ecuaciones lineales
10.4. La variante de Gauss-Jordan
Se trata de un m´etodo tan eficiente como la eliminaci´ on gaussiana y m´ as
caro en cuanto a coste operativo. Se eliminan inc´ ognitas transformando el
sistema en diagonal. Dado el sistema Ax = b, A no singular, existe una ´ unica
soluci´ on.
a
11
x
1
+ +a
1n
x
n
= b
1

a
n1
x
1
+ +a
nn
x
n
= b
n
Si a
11
,= 0, podemos eliminar x
1
de las (n − 1) restantes ecuaciones
restando de la i-´esima ecuaci´on la primera multiplicada por m
i1
=
a
i1
a
11
,
i = 2, ..., n. De manera que el sistema resultante es:
a
11
x
1
+ a
12
x
2
+ +a
1n
x
n
= b
1
a
(2)
22
x
2
+ +a
(2)
2n
x
n
= b
(2)
2

a
(2)
n2
x
2
+ +a
(2)
nn
x
n
= b
(2)
n
Si a
(2)
22
,= 0, eliminamos x
2
de las (n −1) restantes ecuaciones.
-. Para eliminarla de la primera ecuaci´ on, restamos de dicha ecuaci´ on la
segunda multiplicada por:
m
12
=
a
12
a
(2)
22
-. Para eliminarla de las (n − 2) ´ ultimas, restamos de cada ecuaci´on la
segunda multiplicada por:
m
i2
=
a
(2)
i2
a
(2)
22
, i = 3, 4, ..., n
De nuevo a los elementos a
11
, a
(2)
22
, ..., a
(n)
nn
se les denomina pivotes. Si
todos ellos son no nulos, se realiza la eliminaci´ on, llegando despu´es de n
pasos al sistema:
a
(n+1)
11
x
1
= b
(n+1)
1
a
(n+1)
22
x
2
= b
(n+1)
2

a
(n+1)
nn
x
n
= b
(n+1)
n
10.4 La variante de Gauss-Jordan 187
que puede resolverse:
x
k
=
b
(n+1)
k
a
(n+1)
kk
, k = 1, ..., n
Es decir, la eliminaci´ on se realiza en n pasos, k = 1, ..., n. Si A
(1)
= A y
b
(1)
= b.
A
(1)
= A, b
(1)
= b
k = 1, ..., n a
(k+1)
ij
=
_
¸
¸
¸
¸
¸
¸
¸
¸
_
¸
¸
¸
¸
¸
¸
¸
¸
_
a
(k)
ij
_
j = 1, ..., k −1, ∀i
j = k, ..., n, i = k
0 j = k, i ,= k
a
(k)
ij
−m
ik
a
(k)
kj
j = k + 1, ..., n, i ,= k
donde m
ik
=
a
(k)
ik
a
(k)
kk
b
(k+1)
i
=
_
b
(k)
i
i = k
b
(k)
i
−m
ik
b
(k)
k
i ,= k
En cuanto al pivotaje, tanto el parcial como el total se realizan de manera
similar al realizado en la eliminaci´ on gaussiana. El pivote se busca entre los
elementos de la columna posteriores al elemento considerado, no entre los
anteriores.
Coste operativo
En cada paso k:
-. Divisiones: 1 por cada fila para calcular el multiplicador m
ik
, i ,= k.
(n −1) filas.
-. Multiplicaciones y sumas (contadas como un sola operaci´ on): 1 por
cada uno de los (n−k+1) elementos de cada fila y hay (n−1) filas. Adem´ as
1 por cada t´ermino independiente y hay (n −1).
En total:
n

k=1
[1 + (n −k + 1) + 1](n −1) =
= (n −1)
n

k=1
[2 + (n −k + 1)] =
= (n −1)[2n +
n

k=1
(n −k + 1) =
188 10 Soluci´ on de sistemas de ecuaciones lineales
= 2n(n −1) + (n −1)
n(n + 1)
2

n
3
2
Faltar´ıan las n divisiones necesarias para resolver el sistema diagonal.
Observaci´ on:
Eliminaci´ on gaussiana:
n
3
3
+
n
2
2
¡
n
3
2
+n en la variante de Gauss-Jordan.
Cap´ıtulo 11
Soluci´ on de sistemas de ecuaciones
lineales sin inversi´ on de la matriz de
coeficientes
11.1. La descomposici´ on LU
Hemos visto que el m´etodo de eliminaci´ on gaussiana permite tratar va-
rios sistemas con distintos t´erminos independientes. Sin embargo en muchas
ocasiones, todos los t´erminos independientes no se conocen al principio. Po-
demos querer resolver por ejemplo:
Ax
1
= b
1
, Ax
2
= b
2
, donde b
2
es funci´ on de x
1
Parece entonces que habr´ıa que repetir el proceso de eliminaci´ on desde
el principio, con un coste considerable en cuanto al n´ umero de operaciones.
Veremos c´omo evitar ´esto.
Supongamos que tenemos una descomposici´on de A en el producto de
una matriz triangular inferior y otra triangular superior. A = LU.
Entonces el sistema Ax = b ⇔ LUx = b, que se puede descomponer en
dos sistemas triangulares:
Ux = y, Ly = b
De manera que si conoci´eramos L y U podr´ıamos resolver Ax = b, con
2
1
2
n
2
= n
2
operaciones.
Teorema 11.1 Sea A una matriz nxn, y denotemos por A
k
la matriz
de orden kxk formada por la intersecci´ on de las k primeras filas y columnas
189
19011 Soluci´ on de sistemas de ecuaciones lineales sin inversi´on de la matriz de coeficientes
de A. Si det(A
k
) ,= 0, k = 1, ..., n − 1, entonces existe una ´ unica matriz
triangular inferior L = (l
ij
) con l
ii
= 1, i = 1, ..., n y una ´ unica matriz
triangular superior U = (u
ij
), tal que LU = A.
11.1 La descomposici´ on LU 191
Demostraci´on:
Por inducci´ on sobre n. Para n = 1, la descomposici´on a
11
= 1u
11
es
´ unica. Supongamos que el resultado es cierto para n = k − 1 y lo vamos a
probar para n = k.
Descomponemos A
k
, L
k
y U
k
de la siguiente forma:
A
k
=
_
A
k−1
b
c
T
a
kk
_
, L
k
=
_
L
k−1
0
l
T
1
_
, U
k
=
_
U
k−1
u
0 u
kk
_
donde b, c, l y u son vectores columna con n−1 componentes: si form´aramos
el producto L
k
U
k
y lo identificamos con A
k
:
L
k−1
U
k−1
= A
k−1
, L
k−1
u = b
l
T
U
k−1
= c
T
, l
T
u +u
kk
= a
kk
Por hip´ otesis de inducci´ on, L
k−1
y U
k−1
est´an determinadas de manera
´ unica y como det(A
k
) = det(L
k
)det(U
k
) ,= 0, son no singulares.
Se sigue que u y l quedan ´ unicamente determinadas por los sistemas
triangulares: L
k−1
u = b, l
T
U
k−1
= c
T
, s´ı y s´olo si U
T
k−1
l = c. Finalmente
u
kk
= a
kk
−l
T
u. Entonces L
k
y U
k
est´an ´ unicamente determinadas, lo cual
concluye la demostraci´ on. •
Ejemplo 1: Si para alg´ un k, det(A
k
) = 0, puede no existir la descom-
posici´ on LU de A. Por ejemplo si
A =
_
0 1
1 1
_
y suponemos que A = LU, donde
LU =
_
l
11
0
l
21
l
22
__
u
11
u
12
0 u
22
_
=
_
l
11
u
11
l
11
u
12
l
21
u
11
l
21
u
12
+l
22
u
22
_
=
_
0 1
1 1
_
Entonces forz´ osamente l
11
= 0 ´ o u
11
= 0, pero entonces la primera fila
de L o la primera columna de U son 0, por lo tanto A ,= LU.
Sin embargo si las filas de A se intercambian, entonces la matriz se con-
vierte en triangular (superior), y la descomposici´ on LU existe trivialmente.
En efecto, para cualquier matriz no singular A, las filas pueden ser reor-
denadas, de manera que exista una transformaci´ on LU. Esto se sigue de
la equivalencia entre la eliminaci´ on gaussiana y la descomposici´on LU que
veremos ahora.
19211 Soluci´ on de sistemas de ecuaciones lineales sin inversi´on de la matriz de coeficientes
Supongamos primero que la matriz A es tal que la eliminaci´ on gaussiana
puede ser llevada a cabo sin intercambio de filas ni columnas. Podemos
pensar en la eliminaci´ on como en la generaci´on de una sucesi´ on de matrices,
A = A
(1)
, A
(2)
, ..., A
(n)
, donde A
(k)
= (a
(k)
ij
) es la matriz que se obtiene antes
de realizar la eliminaci´ on de los coeficientes correspondientes a la variable
x
k
:
_
_
_
_
_
_
_
_
_
_
_
a
(1)
11
a
(1)
12
a
(1)
1k
a
(1)
1n
a
(2)
22
a
(2)
2k
a
(2)
2n

a
(k)
kk
a
(k)
kn

a
(k)
nk
a
(k)
nn
_
_
_
_
_
_
_
_
_
_
_
Consideremos ahora un cierto elemento a
ij
de A durante la eliminaci´ on.
Si a
ij
est´a en la diagonal principal o sobre ´esta (es decir i ≤ j), entonces:
a
(n)
ij
= ... = a
(i+1)
ij
= a
(i)
ij
, i ≤ j
Si a
ij
est´a bajo la diagonal principal, (i > j), entonces:
a
(n)
ij
= ... = a
(j+1)
ij
= 0, i > j
Adem´as los elementos a
ij
son transformados en cada iteraci´ on k =
1, ..., r, donde r = m´ın(i −1, j), de manera que
a
(k+1)
ij
= a
(k)
ij
−m
ik
a
(k)
kj
Si sumamos estas ecuaciones para k = 1, ..., r, obtenemos:
r

k=1
a
(k+1)
ij
=
r

k=1
a
(k)
ij

r

k=1
m
ik
a
(k)
kj

r

k=1
a
(k+1)
ij

r

k=1
a
(k)
ij
= −
r

k=1
m
ik
a
(k)
kj

a
(r+1)
ij
−a
(1)
ij
= −
r

k=1
m
ik
a
(k)
kj
expresi´on que puede ser escrita: (a
(1)
ij
= a
ij
)
a
ij
=
_
_
_
a
(i)
ij
+

i−1
k=1
m
ik
a
(k)
kj
, si i ≤ j, (r = i −1)
0 +

j
k=1
m
ik
a
(k)
kj
, si i > j, (r = j)
11.1 La descomposici´ on LU 193
o, si definimos m
ii
= 1, i = 1, ..., n:
a
ij
=
p

k=1
m
ik
a
(k)
kj
, p = m´ın¦i, j¦
Sin embargo, estas ecuaciones son equivalentes a la ecuaci´on A = LU,
donde los elementos no nulos de L y U son:
(L)
ik
= m
ik
, i ≥ k
(U)
kj
= a
(k)
kj
, j ≥ k
Conclu´ımos entonces que los elementos de L, son los multiplicadores
calculados en la eliminaci´ on gaussiana y la matriz U, la descomposici´on
final de A obtenida mediante eliminaci´ on gaussiana.
Entonces para obtener la matriz L, debemos guardar los multiplicadores,
m
ik
=
a
(k)
ik
a
(k)
kk
, en los lugares correspondientes a los elementos a
(k)
ik
. Adem´as los
elementos de la diagonal de L no necesitan ser almacenados.
Gr´ aficamente;
A =
_
_
_
_
_
a
11
a
1n


a
n1
a
nn
_
_
_
_
_

_
_
_
_
_
u
11
u
1n
m
21
u
22
u
2n

m
n1
m
nn−1
u
nn
_
_
_
_
_
= LU
Adem´as, como detL = 1, detA = a
(1)
11
a
(n)
nn
. En general:
det(L
k
) = 1 ⇒ det(A
k
) = a
(1)
11
a
(k)
kk
, k = 1, ..., n
Entonces, la eliminaci´ on gaussiana puede ser realizada sin intercambio de
filas o columnas, s´ı y s´olo si det(A
k
) ,= 0, k = 1, ..., n−1, que es la condici´ on
para la descomposici´on ´ unica en el Teorema 11.1.
Nota: Sabemos que la eliminaci´ on gaussiana puede resultar ines-
table sin pivotaje parcial, de manera que es usual el intercambio
de filas durante la eliminaci´ on. Es importante se˜ nalar que en este
19411 Soluci´ on de sistemas de ecuaciones lineales sin inversi´on de la matriz de coeficientes
caso, despu´es de los correspondientes intercambios obtendremos
una descomposici´on LU = A

, donde A

es la matriz que resul-
tar´ıa si los intercambios realizados en la eliminaci´ on, se realizan
en el mismo orden en la matriz A.
11.2. Escenas compactas en la eliminaci´ on gaus-
siana: m´etodos de Doolittle, Crout y Cho-
leski
Cuando resolvemos un sistema de ecuaciones lineales mediante elimina-
ci´on gaussiana tenemos que obtener aproximadamente
n
3
3
resultados inter-
medios, uno para cada operaci´ on. Incluso para valores peque˜ nos de n ´esto
resulta bastante tedioso, y en ocasiones da lugar a errores. Sin embargo,
es posible ordenar los c´alculos de manera que los elementos de L y U se
obtengan directamente. Como en la eliminaci´ on gaussiana, en el paso k se
determinan los elementos de la fila k-´esima de U y de la columna k-´esima
de L, pero en el m´etodo compacto, los elementos a
ij
, i, j > k est´an todav´ıa
sin intercambiar. La ecuaci´ on matricial A = LU es equivalente a
a
ij
=
r

p=1
m
ip
u
pj
, r = m´ın(i, j), m
ip
= l
ip
Esto puede suponer unas n
2
operaciones para las n(n +1) inc´ ognitas en
L y U. Para el k-´esimo paso utilizamos las siguientes ecuaciones:
a
kj
=
k

p=1
m
kp
u
pj
, j ≥ k,
a
ik
=
k

p=1
m
ip
u
pk
, i > k,
Si hacemos m
kk
= 1, entonces los elementos
u
kk
, u
kk+1
, ..., u
kn
, y m
k+1k
, ..., m
nk
pueden ser obtenidos en este orden de las expresiones:
u
kj
= a
kj

k−1

p=1
m
kp
u
pj
, j ≥ k
m
ik
=
a
ik

k−1
p=1
m
ip
u
pk
u
kk
, i > k
11.2 Escenas compactas en la eliminaci´ on gaussiana: m´etodos de Doolittle, Crout y Choleski195
Este se denomina m´etodo de Doolittle y da como resultado los mismos
factores L, U que la eliminaci´ on gaussiana. El m´etodo es incluso num´eri-
camente equivalente a la eliminaci´ on gaussiana, aunque para ordenadores
donde las sumas y los productos pueden ser acumuladas en doble precisi´ on,
´este m´etodo presenta ventajas computacionales sobre el m´etodo de elimina-
ci´on gaussiana.
Si en lugar de normalizar de manera que m
kk
= 1, lo hacemos de manera
que los elementos de la diagonal principal de U cumplan u
kk
= 1, k = 1, ..., n,
se obtiene el m´etodo de Crout. En este caso, las ecuaciones para el paso k
son:
m
ik
= a
ik

k−1

p=1
m
ip
u
pk
, i = k, k + 1, ..., n
u
kj
=
a
kj

k−1
p=1
m
kp
u
pj
m
kk
, j = k + 1, ..., n
En ambos casos hemos supuesto que no ha habido intercambio de filas
(pivotaje). Sin embargo es esencial pensar en ambos m´etodos como candi-
datos a ser combinados con estrategias de pivotaje (al menos parcial). M´as
dif´ıcil resulta, sin embargo, realizar pivotaje total en cualquiera de los dos
casos.
Caso de matrices sim´etricas definidas positivas
Sea A = (a
ij
) una matriz sim´etrica i.e. a
ij
= a
ji
, 1 ≤ i, j ≤ n. Proba-
remos que si la eliminaci´ on gaussiana se realiza, sin intercambio de filas ni
columnas (pivotaje), entonces:
a
(k)
ij
= a
(k)
ji
, k ≤ i, j ≤ n
es decir, los elementos transformados forman matrices sim´etricas de orden
(n + 1 −k), k = 2, ..., n. Esta afirmaci´ on se puede probar por inducci´ on. Es
cierta para k = 1. En el paso k, los elementos son transformados de acuerdo
con:
a
(k+1)
ij
= a
(k)
ij
−m
ik
a
(k)
kj
= a
(k)
ij

a
(k)
ik
a
(k)
kk
a
(k)
kj
Si la hip´ otesis de simetr´ıa es cierta para alg´ un k: (a
(k)
ij
= a
(k)
ji
)
a
(k+1)
ij
= a
(k+1)
ji
= a
(k)
ji
−m
jk
a
(k)
ki
= a
(k)
ji

a
(k)
jk
a
(k)
kk
a
(k)
ki
19611 Soluci´ on de sistemas de ecuaciones lineales sin inversi´on de la matriz de coeficientes
y se obtiene el resultado.
Hemos visto entonces, que si la eliminaci´ on gaussiana se realiza sin pi-
votaje, en una matriz sim´etrica, tenemos que computar ´ unicamente los ele-
mentos de A
(k)
, que est´an en la diagonal principal o sobre ella. Esto significa
que el n´ umero de operaciones realizado es aproximadamente
n
3
6
y se ahorra
aproximadamente la mitad del espacio en memoria.
Sin embargo, no es siempre posible realizar eliminaci´on gaussiana sin
pivotaje sobre matrices sim´etricas. La simetr´ıa se preserva si elegimos cual-
quier elemento de la diagonal principal como pivote y completamos el pivo-
taje, pero como muestra la matriz
_
0 1
1
_
, [[ << 1
a veces ´esto no resulta estable. Por otro lado, el intercambio de filas destruye
la simetr´ıa, lo cual se ve en el mismo ejemplo. De manera que la eliminaci´on
gaussiana anterior no se puede utilizar para cualquier matriz sim´etrica.
Para matrices sim´etricas definidas positivas, la eliminaci´ on gaussiana sin
pivotaje es siempre estable. Una forma de determinar si una matriz es defini-
da positiva es utilizar el siguiente criterio que enunciamos sin demostraci´on.
Teorema 11.2(Criterio de Sylvester) Una matriz sim´etrica de orden
nxn es definida positiva s´ı y s´ olo si det(A
k
) > 0, k = 1, ...n, donde A
k
es la
matriz kxk formada por las k primeras filas y columnas de A.
Es decir, todos los pivotes de A son positivos s´ı y s´ olo si A es definida
positiva y la eliminaci´ on gaussiana est´ a bien definida.
11.2 Escenas compactas en la eliminaci´ on gaussiana: m´etodos de Doolittle, Crout y Choleski197
Teorema 11.3 Para una matriz sim´etrica definida positiva A:
[a
ij
[
2
≤ a
ii
a
jj
y por lo tanto los mayores elementos de A est´an en la diagonal.
Demostraci´on:
Si la misma permutaci´ on se aplica a las filas y columnas de A, la ma-
triz resultante es sim´etrica y definida positiva. Para k = 2 y una cierta
permutaci´ on, el Terorema 11.2, da
0 < det
_
a
ii
a
ij
a
ji
a
jj
_
= a
ii
a
jj
−(a
ij
)
2
de donde se obtiene el resultado. •
Teorema 11.4 Sea A una matriz sim´etrica y definida positiva. Entonces
existe una ´ unica matriz triangular superior R con elementos positivos en la
diagonal principal tal que A = R
T
R.
Demostraci´on:
Del Teorema 11.1, tenemos que A = LU, donde u
11
= a
11
> 0 y
u
kk
=
det(A
k
)
det(A
k−1
)
> 0, k = 2, ..., n
Si introducimos la matriz diagonal D = diag(u
11
, ..., u
nn
), podemos es-
cribir:
A = LU = LDD
−1
U = LDU

, U

= D
−1
U
donde L y U

son matrices triangulares con unos en la diagonal principal y
est´an ´ unicamente determinadas.
Como A es sim´etrica:
A = A
T
= (U

)
T
DL
T
´o L
T
= U

= D
−1
U
Ahora, si hacemos R = D

1
2
U, donde D

1
2
tiene elementos positivos en
su diagonal (u

1
2
kk
), obtenemos:
R
T
R = U
T
D

1
2
D

1
2
U = U
T
D
−1
U = LU = A
19811 Soluci´ on de sistemas de ecuaciones lineales sin inversi´on de la matriz de coeficientes
Por lo tanto, podemos afirmar que la eliminaci´ on gaussiana sin pivotaje
es siempre posible y estable en matrices sim´etricas definidas positivas y
adem´as es posible elegir los elementos de la diagonal de L reales y de manera
que U = L
T
. •
As´ı, la eliminaci´ on compacta en este caso particular, nos dar´ıa expresio-
nes en las que u
kk
= m
kk
y u
pk
= m
kp
,
u
kk
= m
kk
= (a
kk

k−1

p=1
m
2
kp
)
1
2
u
ki
= m
ik
=
a
ik

k−1
p=1
m
ip
m
kp
m
kk
, i = k + 1, ..., n
Este m´etodo se denomina m´etodo de Choleski o de la ra´ız cuadrada. En
cuanto a coste operativo:
Son necesarias del orden de
n
2
2
operaciones hasta obtener la descompo-
sici´on LL
T
, +n ra´ıces cuadradas, (1 ra´ız cuadrada ≈ 6 operaciones). Luego
n
2
2
+ 6n operaciones.
LL
T
x = b
L
T
x = y → sistema triangular inferior (
n
2
2
operaciones).
Ly = b → sistema triangular superior (
n
2
2
operaciones).
Total:
n
2
2
+
n
2
2
+
n
2
2
+ 6n =
3n
2
2
(+6n de las ra´ıces cuadradas).

´ Indice general

1. Nociones b´sicas sobre algoritmos a 1.1. Introducci´n . . . . . . . . . . . . . . . . . . . . o 1.2. ¿Qu´ es un algoritmo? Medidas de eficiencia . . e 1.3. Complejidad. Complejidad computacional . . . 1.4. Complejidad agebr´ica y anal´ a ıtica . . . . . . . o e 1.5. Precisi´n num´rica . . . . . . . . . . . . . . . . 1.6. An´lisis de Algoritmos. L´ a ımites de complejidad

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

1 1 2 5 6 7 10

2. Introducci´n al lenguaje de programaci´n C (I) o o 13 2.1. Nociones sobre trabajo en el Sistema Operativo UNIX. El editor vi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.2. Compilado, ejecuci´n y depuraci´n de programas en C . . . . 20 o o 2.3. Variables escalares, subindicadas (arrays) y constantes . . . . 21 2.3.1. Nombres de variables . . . . . . . . . . . . . . . . . . . 22 2.3.2. Tipo y tama˜o de datos . . . . . . . . . . . . . . . . . 23 n 2.4. Un ejemplo. El primer programa en C . . . . . . . . . . . . . 26 2.5. Operadores y funciones standard m´s usuales . . . . . . . . . 30 a 2.5.1. Funciones m´s usuales . . . . . . . . . . . . . . . . . . 31 a 2.6. Ambito de definici´n y validez de las variables. Variables exo ternas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 2.7. Precedencia y orden de evaluaci´n entre operadores . . . . . . 39 o 3. Introducci´n al lenguaje de programaci´n C (II) o o 41 3.1. Declaraciones. Tipos de datos escalares: enteros, car´cter, de a coma flotante y enumeraci´n . . . . . . . . . . . . . . . . . . 41 o 3.2. Conversiones de tipo . . . . . . . . . . . . . . . . . . . . . . . 45 3.3. Nociones sobre representaci´n de datos e implicaciones sobre o la precisi´n num´rica . . . . . . . . . . . . . . . . . . . . . . . 48 o e 3.3.1. Errores de redondeo en computaci´n . . . . . . . . . . 51 o 3.3.2. Error de cancelaci´n . . . . . . . . . . . . . . . . . . . 52 o 4. Introducci´n al lenguaje de programaci´n C (III) o o 55 4.1. Sentencias de control de flujo: for, if , do, while, switch. Ejemplos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 i

ii 4.2. 4.3. 4.4. 4.5. 4.6. Funciones: anatom´ y uso . . . . . . . . . ıa Convenciones sobre el paso de argumentos Clases de almacenamiento . . . . . . . . . Recursi´n . . . . . . . . . . . . . . . . . . o Funciones definidas en stdio y math . . . . . . . . . . . . . . . . . . . . . . . . . . . .

´ Indice general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 65 68 71 72 79 79 81 81 83 85 87 88 88 90 93 95 96 99 101 102 103

5. Algoritmos iterativos 5.1. Introducci´n . . . . . . . . . . . . . . . . . . . . . . . . . . . . o 5.2. Resoluci´n de ecuaciones . . . . . . . . . . . . . . . . . . . . . o 5.2.1. M´todo de bisecci´n . . . . . . . . . . . . . . . . . . . e o 5.2.2. M´todo de Newton-Raphson . . . . . . . . . . . . . . e 5.2.3. M´todo de la secante . . . . . . . . . . . . . . . . . . . e 5.2.4. Regula Falsi . . . . . . . . . . . . . . . . . . . . . . . . 5.3. Comparaci´n de los distintos ´rdenes de convergencia . . . . o o 5.3.1. Convergencia del m´todo de bisecci´n . . . . . . . . . e o 5.3.2. Convergencia del m´todo de Newton-Raphson . . . . . e 5.3.3. Convergencia del m´todo de la secante . . . . . . . . . e 5.3.4. Convergencia del m´todo de regula falsi . . . . . . . . e 5.4. Teor´ general de los m´todos iterativos. Iteraci´n del punto ıa e o fijo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.5. Generalizaci´n a sistemas de ecuaciones . . . . . . . . . . . . o 5.5.1. M´todo de Newton-Raphson . . . . . . . . . . . . . . e 5.5.2. M´todo de Gauss-Seidel . . . . . . . . . . . . . . . . . e 5.6. Ejercicios de aplicaci´n . . . . . . . . . . . . . . . . . . . . . . o

6. M´todos de ordenaci´n e o 105 6.1. An´lisis te´rico: orden de complejidad optimo alcanzable para a o ´ algoritmos internos . . . . . . . . . . . . . . . . . . . . . . . . 105 o e 6.2. M´todos simples de complejidad O(n2 ): comparaci´n, inserci´n, burbuja . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 o 6.2.1. M´todo de comparaci´n (selecci´n) . . . . . . . . . . . 108 e o o 6.2.2. M´todo de inserci´n . . . . . . . . . . . . . . . . . . . 110 e o 6.2.3. M´todo de intercambio (burbuja) . . . . . . . . . . . . 111 e 6.3. M´todos de complejidad O(nlogn): Quicksort y Heapsort . . . 113 e 6.3.1. Heapsort . . . . . . . . . . . . . . . . . . . . . . . . . 113 6.4. Quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 7. El lenguaje de programaci´n C (IV) o 125 7.1. Direccionamiento indirecto. Punteros: variables que contienen la ubicaci´n en memoria de otras variables . . . . . . . . . . . 125 o 7.2. Punteros y funciones: paso de argumentos de referencia haciendo uso de punteros . . . . . . . . . . . . . . . . . . . . . . 128 7.2.1. Punteros y arrays . . . . . . . . . . . . . . . . . . . . . 131 7.3. Algebra de punteros . . . . . . . . . . . . . . . . . . . . . . . 134

Ejemplos . . . . . . . . . . . . . . . . . . M´todo de transformaci´n. . . . . . . . M´todo de inversi´n. . . . . . . . . . . . . Ejemplos . . . . . .3. .3. . . . . . Otras aplicaciones . . . . . . . . . . . . . . . . 184 11. . . . . .1. . . . . . . . . . . . M´todos espec´ e ıficos . Generaci´n de variables no uniformes o o 12. . . Punteros a caracteres . . Ejemplos de uso . . 179 o e 10. . . . . . . . . . Pivotado total y parcial. . 149 8. . .4. .4. . . . .1. . . . .1. . . . . . . e o 12. . . . . . . Generaci´n de n´meros aleatorios o o u 189 11. . El lenguaje de programaci´n C (V) o 149 8. Punteros a estructuras y estructuras ligadas. 195 11. Integraci´n num´riu e o e ca . . . . .1. . . . Sistemas triangulares . .2. . . . . . . . . .3. . . . . . . . . .3. . 168 9. . . .5. . . . . . . . e o 12. . . M´todo de rechazo. . . . . . . 139 o 8. . . . . . . La descomposici´n LU .1.Soluci´n de sistemas de ecuaciones lineales sin inversi´n de o o la matriz de coeficientes 179 o 10. . m´duo o lo y semilla . . . . . . . . La variante de Gauss-Jordan . . 195 o 11. . . . . . . . . . . .4. . 176 10. . . . . . . . . . . . Soluci´n de sistemas de ecuaciones lineales o 167 9. . . Operaciones con estructuras . . . . . . . . M´todos directos para la resoluci´n de sistemas de ecuaciones e o lineales . . . . . . . . 198 12. . . . . Escenas compactas en la eliminaci´n gaussiana: m´todos de Doolittle. . . . .1. Crout y Choleski . 190 11. . . . . .Simulaci´n (I). . . . . 205 205 207 210 211 . . . . . . . . . .2. . . . . . . . . . . . . . . Arboles binarios. . . . . . . . . . . .´ Indice general 1 7.2. Eliminaci´n gaussiana . . . . . . Tipos de datos definidos por el usuario: estructuras . . . . .2. . . . . . . . . . . . . 151 8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ejemplos . Ejemplos de utilizaci´n de punteros . . . . . . e ıtmica . 137 7. . . Generaci´n de observaciones pseudoaleatorias uniformes . .4.1. . . . . . . . . . . . N´meros aleatorios y M´todo de Montecarlo. e 12. . . . . . . 168 9. . . . . . . . . .Simulaci´n (II). . . . . . . Generadores multiplicativos: elecci´n del multiplicador. . Estabilidad num´rica y su mejora. . . . . . . .2. . . 172 Complejidad algor´ 9. . . . . . . . . . 169 o 9. . . . 158 9. . . .

2 ´ Indice general .

Introducci´n o Es evidente que las matem´ticas son utilizadas de una forma u otra en a la mayor´ de las areas de la ciencia y la industria. se reduce el problema a un problema lineal. desarrollado en los ultimos cincuenta o ´ a˜os. las aplicaciones generan problemas matem´ticos que por a su complejidad no pueden ser resueltos de manera exacta. etc. una ecuaci´n diferencial. a veces. Sin embargo. Uno entonces. En la mayoria de los casos. A menudo. significa en la mayor´ de los casos. se restringe a casos especiales o modelos demasiado simplificados que puedan ser analizados. Tal aproximaci´n puede ser muy efectiva y o o explicar conceptos y puntos de vista que al menos cualitativamente pueden ser aplicados al problema inicial. La alternativa que nosotros plateamos aqui. tal aproximaci´n no o es suficiente o incluso puede inducir a error. utilizando el potencial del c´lculo num´rico.Cap´ ıtulo 1 Nociones b´sicas sobre algoritmos a 1.1. la e e ıa aplicaci´n de un peque˜o n´mero de ideas generales y relativamente simples. La cantidad de trabajo depender´ del grado de precisi´n demandada. o n u Dichas ideas han de ser combinadas de forma intuitiva con un conocimiento 3 . las posibilidades de utilizar m´todos num´ricos han crecido enormen e e mente. por ejemplo. a o Con la utilizaci´n del ordenador. ıa ´ Durante el siglo XX avanzados m´todos y modelos matem´ticos han sido e a aplicados en distintas areas como la medicina. la econom´ o las ciencias so´ ıa ciales. la funci´n objetivo en un o o problema de optimizaci´n. es la de tratar un problema a e menos simplificado. Desarrollar un m´todo num´rico.

o bien. ya que dicho n´mero aunque u finito puede llegar a ser muy elevado. variables ino o dependientes en el problema. Las acciones a llevar a cabo deben estar rigurosamente u especificadas en cada paso. por su parte. que le son dados al algoritmo inicialmente. Cada paso de un algoritmo debe estar definido de manera precisa y sin ambig¨edad. tambi´n definidos como cantidades. sino adem´s un n´mero razonable de pasos o u a u hasta obtener la soluci´n del problema. estar impl´ ıcita o expl´ ıcitamente representada. de una o clase espec´fica de problemas. Normalmente dichos problemas aparecen como a subproblemas o detalles computacionales de problemas m´s grandes y coma plejos. Cada algoritmo posee cero o m´s datos y proporciona uno o m´s resula a tados. 2. ¿Qu´ es un algoritmo? Medidas de eficiencia e Por problema num´rico entenderemos una clara e inambigaa descripe ci´n de una relaci´n funcional entre datos (inputs) -es decir.4 1 · Nociones b´sicas sobre algoritmos a del problema que puede ser obtenido de distintas formas fundamentalmente con m´todos del an´lisis matem´tico. y los resultados (outputs). Un algoritmo util. que constituyen un conjunto finito de cantidades reales. La restricci´n general de necesitar un n´mero finito o u de pasos. debe requerir ´ no s´lo un n´mero finito. 1. tienen algue .2. e a a A lo largo de la asignatura ilustratremos la utilizaci´n de las ideas geo nerales que se esconden detr´s de los m´todos num´ricos que resuelven los a e e problemas m´s habituales.y resultados (outputs). o 3. Los datos (inputs) pueden ser definidos como cantidades. El algoritmo debe llegar a la soluci´n del problema en un n´mero o u finito de pasos. antes de ser ejecutado. mediante las cuales se obtiene la soluci´n de un problema. Datos y resultados. La relaci´n funcional o puede. Por algoritmo entenderemos un procedimiento formado por un conjunto finito de reglas que especifican una secuencia finita de operaciones. ı Analizaremos algunos aspectos de esta ultima definici´n: ´ o 1. en ocasiones no es suficiente. En este cap´ ıtulo comenzamos presentando conceptos y terminolog´ b´siıa a ca en el desarrollo de posteriores cap´ ıtulos.

para x = h. a o A lo largo de la asignatura nos dedicaremos al estudio de algoritmos dise˜ados para ser ejecutados en un ordenador. a1 . siempre deseable en un algoritmo. que puede ser aproximado por un problema num´rico. que no puede ser especificada simpleo mente por un conjunto finito de n´meros. basados en el m´todo de Newton Rapson. sin embargo. de acuerdo con la definici´n dada arriba un problema num´rico. El objetivo entonces.2 · ¿Qu´ es un algoritmo? Medidas de eficiencia e 5 na relaci´n con los datos y son proporcionados cuando se completa la o ejecuci´n del algoritmo.1. . en a e el sentido de que el resultado ser´ un conjunto de valores que aproximar´n a a a la funci´n y(x). Como hemos mencionado. El problema es entonces un prou blema matem´tico. a1 . es o inventar buenos algoritmos y probar que dichos algoritmos son buenos. para un problema num´rico dado se pueden e considerar distintos algoritmos. 3h. no es. Es preferible que un algoritmo sea aplicable en la resoluci´n de cualo quier miembro de una clase de problemas y no unicamente en un ´ problema aislado. aunque no necesaria. y(5) = 1. El resultado es el valor de la ra´z buscada x. quiere buenos algoritmos. 5. o Medidas de eficiencia Es muy f´cil inventar algoritmos.2 El problema de resolver la ecuaci´n diferencial o d2 x = x2 + y 2 d2 y con condiciones de contorno y(0) = 0. etc. o 4.. Esta propiedad de generalidad es.. En la pr´ctica. que est´ impl´citamente definida en funci´n de los datos. aclarar que quiz´ la noci´n de algoritmo es muy general. el de la secante. a2 es un problema num´rico.. el o e de bisecci´n. debe ser implementado en un lenguaje de programaci´n y a la o larga constituir un programa o conjunto de programas. Un algoritmo de este n tipo. La .1 Determinar la ra´z real m´s grande de la ecuaci´n: ı a o x3 + a2 x2 + a1 x + a0 = 0 e con coeficientes reales a0 . El vector de datos ı a ı es (a0 . Ejemplo 1. o Ejemplo 1. Finalmente. 2h. En este problema los datos o e (input) se reducen a una funci´n y. uno no a a quiere s´lo algoritmos. a2 ). Existen distintos algoritmos para o la resoluci´n de este problema.

Encontrar el mayor elemento en una secuencia de n n´meros enteros.6 1 · Nociones b´sicas sobre algoritmos a “bondad” de un algoritmo puede ser valorada de acuerdo con varios criterios. Un cambio en un programa o puede no representar un cambio significativo en el algoritmo correspondiente y. En cada uno de estos problemas. Resolver un conjunto de ecuaciones lineales Ax = b. 3. suponiendo que tenemos un programa de ordenador que eventualmente acaba. Una alternativa util a esta medida emp´ ´ ırica. distintos aspectos dentro del criterio que controla el tiempo. el tiempo y o el espacio de memoria requeridos por un programa. sin embargo. Hay a tambi´n. o es una importante fuente de informaci´n. o o El tiempo de proceso de un algoritmo es una funci´n del tama˜o del proo n blema computacional a resolver. Para estos algoritmos. los resultados estar´n siempre o a ısticas de la m´quina como por la destreza a afectados tanto por las caracter´ y habilidad del programador. Adem´s. ambas comparaa ciones pueden generar diferentes conclusiones. si dos programas o a son comparados. Sin embargo. primero en una m´quina y luego en otra. 1. o bien el tiempo requerido n . tal an´lisis proporciona una importante a medida de evaluaci´n del costo de ejecuci´n de un algoritmo. Ordenar en orden decreciente un vector de n n´meros enteros distintos. el par´metro n proporciona una medida a del tama˜o del problema. Veamos por ejemplo la siguiente clase de problemas. variar´ con el ejemplo a particular a ser resuelto. u n 4. est´ fuertemente relacionada con el programa y con la m´quina a a utilizada en la implementaci´n del algoritmo. Esta medida o emp´ ırica. Uno e de ellos. o De mayor inter´s son los algoritmos que pueden ser aplicados a una e colecci´n de problemas de un cierto tipo. Pn (x) = b − k=0 ak xk en x = x0 . As´ mientras la comparaci´n de programas corriendo en ordenadores ı. u 2. es el tiempo de ejecuci´n requerido por distintos algoritmos para la o resoluci´n de un mismo problema en un ordenador concreto. Evaluar un polinomio de grado n. donde A es una matriz real nxn y b es un vector real de longitud n. Uno de los m´s importantes es el tiempo que tarda en ejecutarse. es la realizaci´n de un an´lio a sis matem´tico de la dificultad intr´ a ınseca de la resoluci´n computacional del o problema. Juiciosamente utilizado. afectar a su rapidez de ejecuci´n. la resoluci´n de un probleo ma s´lo requiere de suficiente tiempo y suficiente memoria para almacenar o informaci´n. en el sentido de que.

donde puede verse c´mo crecen las funciones de o complejidad de determinados algoritmos.3. n = 102 n = 103 n = 104 F (n) n = 10 log2 n n nlog2 n n2 n3 2n n! 3. el costo de obtenci´n de una soluci´n crece de acuerdo con o o el incremento del tama˜o del problema. n.3 · 105 108 1012 > 10100 > 10100 n: tama˜o del problema n F (n): funci´n de complejidad temporal o En la pr´ctica es importante controlar el comportamiento del algorita mo para valores grandes de n. al crecer n. en la que el algoritmo o no puede ejecutarse en un periodo razonable de tiempo. sin ıa embargo.33 · 10 102 103 1024 3 · 106 6. de manera que a la elecci´n de un algoritmo para un problema peque˜o no es cr´ o n ıtico (a menos que el problema sea resuelto muchas veces). definiremos una funci´n o o de complejidad (o costo) F . 1. Tambi´n podemos referirnos a cualquiera de las dos. e simplemente como funci´n de complejidad (o costo) del algoritmo. al incrementar n. Complejidad computacional 7 para resolver cada problema o el espacio de memoria requerido por el algoritmo.3 104 1.1. o ambos a la vez. crecen con n. o . En la mayor´ de los casos. se llega a una situaci´n. Este punto se ilustra en la siguiente tabla. es decir. De esta forma. donde F (n) es una medida del tiempo requerido para ejecutar el algoritmo sobre un problema de tama˜o n. Complejidad. Complejidad computacional Para medir el coste de ejecuci´n de un programa. n n incluso un algoritmo ineficiente tardar´ poco en ejecutarse. Si el valor n es muy peque˜o. el comportamiento asint´tico de la o funci´n de complejidad. hablaremos o de la complejidad en cuanto a tiempo o de la complejidad en cuanto a memoria del algoritmo.3 · 1030 > 10100 10 103 104 106 109 > 10100 > 10100 13. o una medida del n espacio de memoria requerido para tal ejecuci´n.7 · 102 104 106 1.3 · Complejidad.6 102 0.3 10 0. o En general.

El algoritmo es ejecutado en un tiempo O(nlogn). etc. mientras que si es un conjuntos de letras o palabras. o en n cualquier caso no supera esta cantidad. En los Temas 5 y 6 del programa analizaremos algoritmos para resolver estos problemas. un algoritmo que resuelve un e problema en el que el contenido num´rico es esencial. De esta forma. la n funci´n asint´tica (temporal o espacial) determina el tama˜ o m´ximo del o o n a problema a ser resuelto por el algoritmo. 1. todos ellos para decir lo mismo: e 1. Hay muchos ejemplos e de algoritmos num´ricos entre los que se encuentran los siguientes: e 1. 3. La evaluaci´n de un polinomio en un valor dado. resuelven problemas y la soluci´n de los e o mismos. 2. o Los algoritmos no num´ricos. si el tiempo de proceso del algoritmo para un ejemplo de tama˜o n es proporcional a nlogn. que puede ser expresado como O(nlogn).4. El c´lculo de las raices de una ecuaci´n no lineal. en orden creciente o decreciente. o bien de un sistema a o de ecuaciones no lineales. es de naturaleza no num´rica. La complejidad temporal del algoritmo es de orden nlogn. la ordenaci´n de un conjunto de elementos dese o u ordenados (si es un conjunto de n´meros. A lo largo de diferentes cap´ ıtulos. Complejidad agebr´ica y anal´ a ıtica Entenderemos por algoritmo num´rico. aunque en ocasiones se representa por un n´mero (o conjunto de u n´meros). en orden alfab´tico) y la e u b´squeda de un elemento en un conjunto. En el an´lisis de la complejidad computacional de los algoritmos num´ria e cos es conveniente distinguir entre la medida de complejidad algebr´ica y a . hablaremos en los siguientes t´rminos. La cantidad de trabajo requerida por el algoritmo es proporcional a nlogn. o es O(nlogn). En estos t´rminos diremos que la complejidad del algoritmo es O(nlogn) e y leeremos “de orden n logaritmo (en base dos) de n”.8 1 · Nociones b´sicas sobre algoritmos a La complejidad asint´tica es el crecimiento en el l´ o ımite de la funci´n o de complejidad cuando crece el tama˜o del problema n. 2. Dos importantes ejemplos de prou e blemas no num´ricos son.

si son muy grandes para ser representados en el ordenador. La mayor´ de los n´meros. que el n´mero de operaciones aritm´ticas necesarias tambi´n u e e es finito. que es capaz de representar los n´meros a u unicamente en un n´mero finito de posiciones. con un nivel de precisi´n prefijado. Por una parte a u intenta estimar el n´mero de operaciones aritm´ticas requeridas por un algou e ritmo. ´ a Los objetivos de la complejidad algebr´ica son m´ltiples. el proceso es interrumpido en un punto y. algu- . Un aspecto importante de los algoritmos que son a utilizados para resolver estos problemas. Esto implica que si no se tiene cuidado. La evaluaci´n de un polinomio en uno o varios valores dados y la resoluci´n o o de ecuaciones o sistemas de ecuaciones lineales mediante m´todos directos e son ejemplos de problemas que pueden ser estudiados en t´rminos de su e complejidad algebr´ica. Tambi´n intenta describir un algoritmo o e algoritmos que resuelvan un problema en un n´mero m´ u ınimo de operaciones. en otro caso o la computaci´n contin´a hasta obtener un resultado satisfactorio. En este o u caso. donde ambas representan ramas de la propia complejidad computacional. La necesidad de examinar la precisi´n de los c´lculos matem´ticos viene del hecho de considerar que un o a a ordenador es una m´quina finita. En el caso de los algoritmos no num´ricos. es el n´ mero finito de pasos que u son necesarios hasta la obtenci´n de la soluci´n del problema. Esto implica.5 · Precisi´n num´rica o e 9 anal´ ıtica. o o evidentemente. ´ u ıa u incluso si son enteros. En este caso. uno estima el n´mero de operaciones aritm´ticas por paso en cada iteu e raci´n y un “buen” algoritmo en t´rminos de su complejidad computacional o e es definido como aqu´l que requiere el menor n´mero total de operaciones e u aritm´ticas para alcanzar un resultado. la complejidad anal´ ıtica resuelve la cuesti´n de cu´nta o a o computaci´n ha de ser desarrollada hasta obtener un resultado con un detero a minado grado de precisi´n y se centra m´s en los procesos computacionales que en cierto sentido nunca terminan. e o 1. Por otra parte. Los procesos iterativos son un evidente ejemplo.5. la prea e cisi´n de los resultados obtenidos es otro importante criterio a la hora de o distinguir entre un buen y un mal algoritmo. la complejidad es e unicamente abgebr´ica. se redondean y s´lo una aproximaci´n finita a dicho n´mero es o o u almacenada en el ordenador. dicho resultado se toma como soluci´n del problema. Adem´s intenta estimar el n´mero m´ a u ınimo de operaciones necesario para resolver un problema dado.1. si el valor actual del resultado satisface el problema con un margen de error acotado. Precisi´n num´rica o e En el an´lisis de algoritmos que resuelven problemas num´ricos.

Este ejemplo ´ ser´ el primero en ilustrar este principio. .165 absurdo!! y4 = 4 y3 > y2 La raz´n del absurdo resulta ser un error de redondeo en y0 cuya mago nitud puede ser como mucho de 5 · 10−4 .05 y2 = 2 1 − 5y2 ≈ 0. En general las relaciones de recurrencia son ubicuas en estad´stica computacional y probabilidad.182. En la mayor´a de los casos surgen ı ı de considerar el primero y el ultimo de una cadena de eventos.3 Vamos a analizar el problema de computar para n = 0.083 extra˜o que n y3 = 3 1 − 5y3 ≈ −0.10 1 · Nociones b´sicas sobre algoritmos a nos algoritmos num´ricos implementados en un ordenador pueden producir e aproximaciones al verdadero valor de la soluci´n. Dicho valor es multiplicado por -5 . bastante imprecisas.09 1 − 5y1 ≈ 0. con el siguiente ejemplo vamos a ver c´mo los o errores de redondeo pueden destrozar el resultado de un c´lculo si se elige a un mal algoritmo. y1 = 1 − 5y0 ≈ 0. 8. Ejemplo 1... a En nuestro caso la relaci´n de recurrencia viene dada por: o 1 − 5yn−1 n y se sigue directamente de la identidad: yn = 1 yn = 0 xn−1 (x + 5 − 5) dx = x+5 1 0 xn−1 dx − 5 1 0 1 xn−1 dx = − 5yn−1 x+5 n En teor´a esta relaci´n de recurrencia permitir´ crear un proceso iterativo ı o a 1 dx y computar yn a partir del valor inicial y0 = 0 x+5 = [ln(x + 5)]1 = ln6 − 0 ln5 = 0.. la integral: 1 yn = 0 xn dx x+5 Vamos a utilizar un procedimiento iterativo basado en una relaci´n de o recurrencia. o Aunque todav´ no hemos introducido el concepto de error de redondeo ıa (lo haremos en el Cap´ ıtulo 3).

019 45 5 . o Este algoritmo es un ejemplo de un fen´meno denominado inestabilio dad num´rica. Dicho error produce a un error en y2 de 25 .017 60 De este modo: y8 = y9 1 − ≈ 0. De manera que y10 ≈ y9 .1. En este caso sabemos que. u Una soluci´n al problema en este caso.3125. puede ser utilizar la f´rmula de o o recurrencia en la otra direcci´n. para n modea a radamente grande. una buena aproximaci´n de yn−1 ser´a: o yn−1 ≈ 1 1+5 1 0 xn−1 dx = 1 6n Es decir: yn ≈ 5 1 5 1 − = (1 − ) n 6n n 6 Por lo tanto perdemos precisi´n porque en cada iteraci´n restamos dos o o n´meros del mismo signo y parecida magnitud. etc. De esta forma el error en y4 es 625 que puede alcanzar el valor 625 · 5 · 10−4 = 0. Podremos evitar dicha inestabilidad num´rica eligiendo un e e algoritmo m´s adecuado. Por lo tanto. de donde: y9 + 5y9 ≈ 1 . 10 y9 ≈ 1 ≈ 0. lo que sucede es que se retrasa la aparici´n del absurdo. que entonces tiene un error de −5 . Podemos ver directamente de la definici´n que yn decrece cuando n crece. Se puede suponer entonces que yn o decrece suavemente cuando n es grande. a Si se comienza en y0 utilizando m´s decimales. o yn−1 = yn 1 − 5n 5 Pero necesitamos un valor inicial. a Otra forma de explicar dicha inestabilidad num´rica es utilizando conoe cimientos de an´lisis matem´tico.5 · Precisi´n num´rica o e 11 en el c´lculo de y1 . la mayor´a de la masa de la integral se agrupa en torno ı ı a x = 1.

cu´ntas veces es conveniente a ejecutar cada parte del algoritmo y la complejidad espacial.088 y0 ≈ 0. As´ un m´todo o e e ı.043 y2 ≈ 0. entonces intentaremos establecer l´ ımites de la complejidad de los algoritmos de dicha clase. y en otros para resolver un problema a e num´rico. En el an´lisis de una clase de algoritmos. 1. Especificar la f´rmula de recursi´n que permite resolver una sue o o cesi´n de integrales. An´lisis de Algoritmos. o Aunque en este caso la soluci´n ha sido elegir una f´rmula de recursi´n o o o hacia atr´s. estudiaremos la clase de algoritmos que resuelven un problema particular e intentaremos seleccionar el “mejor” algoritmo de la clase capaz de hacerlo.12 y7 = y6 1 · Nociones b´sicas sobre algoritmos a y8 1 − ≈ 0. ´sto es.058 y1 ≈ 0.034 y3 ≈ 0. e num´rico es m´s general que un algoritmo y hace menos ´nfasis en detalles e a e computacionales. esto no supone siempre una mejor alternativa. En el an´lisis de un algoritmo en a a concreto deber´ ıamos probablemente investigar sus caracter´ ısticas m´s ima portantes tales como su complejidad temporal. . a La aproximaci´n inicial es distinta pero el error se divide por 5 en cada o iteraci´n y a partir de y7 se obtienen los mismos resultados.182 Correcto!! Este mismo algoritmo hacia atr´s puede elegir como valor inicial y10 = 0. a En adelante utilizaremos el t´rmino m´todo num´rico para denotar e e e un procedimiento util en algunos casos para aproximar un problema ma´ tem´tico con un problema num´rico. cu´nta e a memoria va a ser necesaria.021 40 5 ≈ 0.6. es proporcionar un m´todo num´rico. por a otra parte.025 y5 ≈ 0.028 y4 ≈ 0. L´ a ımites de complejidad Es conveniente distinguir entre el an´lisis de un algoritmo en particular a y el an´lisis de una clase de algoritmos. Si tal algoritmo no se encuentra.

sin la necesidad de construir el algoritmo para el problema. a e . donde una cota superior m´ lizada y sin embargo el m´s r´pido de todos los m´todos conocidos tiene a a e 2. En algunos de estos casos las demostraciones de los teoremas no son constructivas en el sentido o de que no indican la forma de construir un algoritmo para la resoluci´n del o problema y ni siquiera nos dicen si es posible su construcci´n. Finalmente. Este intervalo sugiere que la cota O(n2 ) no una complejidad de O(n es alcanzada en la pr´ctica. Un ejemplo de este caso es la multiplicaci´n a ınima de O(n2 ) es f´cilmente visuade matrices. a Otros problemas son tales que se conoce la cota inferior de su complejidad. a L´ ımites de Complejidad El an´lisis de la complejidad est´ relacionado entre otras cosas con la a a obtenci´n de l´ o ımites superiores e inferiores de la ejecuci´n de un algoritmo o o una clase de algoritmos que resuelve una clase de problemas. pero no se conoce algoritmo o capaz de alcanzar dicha cota. M´todos r´pidos e e a de multiplicaci´n de matrices pertencen a esta categor´ o ıa.1. o Hay algunos problemas para los que los l´ ımites de complejidad de su o resoluci´n pueden ser determinados utilizando teoremas apropiados. Para otros problemas ha sido posible acotar su complejidad. La existencia de l´ ımites de la complejidad de algunos algoritmos nos puede servir como base para la clasificaci´n de algunos problemas. se pueden construir algoritmos que satisfacen dichas cotas y sin embargo dichos algoritmos son num´ricamente inestables.496 ).6 · An´lisis de Algoritmos. L´mites de complejidad a ı 13 An´lisis del primer tipo han sido realizados desde los comienzos de la proa gramaci´n en ordenador. Es f´cil ver que an´lisis del e a a segundo tipo son bastante m´s potentes y utiles ya que permiten estudiar a ´ varios algoritmos simult´neamente. o a salvo raras excepciones. hay problemas para los cuales los l´ ımites inferiores de complejidad son conocidos y los algoritmos que alcanzan dichas cotas pueden construirse y adem´s son num´ricamente estables. comienzan a realizarse. An´lisis del segundo tipo. mucho tiempo despu´s.

14 1 · Nociones b´sicas sobre algoritmos a .

o El primer punto ha sido la causa por la que se invent´ y desarroll´ el o o ordenador. a El unico l´ ´ ımite existente proviene del n´mero de personas expertas en u un determinado campo. Sin embargo el segundo punto es el que ha experimentado un mayor desarrollo en los ultimos a˜os y ya domina en todos los campos de la ´ n tecnolog´ inform´tica. o composici´n editorial de las p´ginas en los peri´dicos. no o son m´s que algunos ejemplos en los que interviene un ordenador.Cap´ ıtulo 2 Nociones sobre arquitectura de ordenadores 2. C´lculo num´rico. a 15 . recuperaci´n procesamiento y an´lisis de datos o ino a formaci´n. Estamos sin duda en la era de la gesti´n inform´tica ıa a o a de la informaci´n. Almacenamiento. que posean la habilidad y conocimientos necesarios para aplicar la inform´tica a ese campo. b´squeda de s´ o a o u ıntomas en grandes bases de datos que ayudan al diagn´stico de enfermedades. grupos: 1. Introducci´n o Las aplicaciones de un ordenador caen. Facturaci´n y control de grandes compa˜´ edici´n y o o nıas. a e 2. en su mayor´ dentro de los ıa.1.

por ejemplo. o 2.16 2 · Nociones sobre arquitectura de ordenadores 2. ıan a e El resultado fu´ una m´quina monstruosa en tama˜o. Pero lo que ha hecho posible la revoluci´n n o inform´tica ha sido la aparici´n de los circuitos integrados. unidad central de proceso o CPU En sus m´s remotos or´ a ıgenes cada uno de los conmutadores que constitu´ un ordenador era un dispositivo electromec´nico conocido como rel´.1. e tambi´n. Podemos convertir de forma aproximada. demasiado complicadas. Memoria. que. Por otra parte podremos representar los n´meros u en base 2 en lugar de utilizar la base de numeraci´n decimal como hacemos o normalmente. poco fiables v´lvulas de vacio. dispositivos de entrada/salida. la informaci´n contenida en una imagen. en o una serie de puntos cuyos valores de posici´n color e intensidad podremos o codificar y procesar o transmitir a trav´s de l´ e ıneas telef´nicas. el 372 se puede representar en base decimal 372 = 3(102 ) + 7(10) + 2(100 ) pero tambi´n podemos escribirlo como e 372 = 1(28 ) + 0(27 ) + 1(26 ) + 1(25 ) + 1(24 ) + 0(23 ) + 1(22 ) + 0(21 ) + 0(20 ) = = 1(256) + 0(128) + 1(64) + 1(32) + 1(16) + 0(8) + 1(4) + 0(2) + 0(1) o bien (372)2 = (101110100)2 es decir necesitaremos nueve posiciones para representar el n´ mero 372. Representaci´n binaria de la informaci´n o o Las operaciones que realiza internamente un ordenador no son. u Adem´s de n´meros podemos representar otros tipos de datos en c´digo a u o binario. cada una de las letras del abecedario. Los n´meros expresados en base dos solamente utilizan los u d´ ıgitos 0 y 1. e a a a A continuaci´n se introdujo el transistor. podremos representar grandes cantidades de ceros y unos.1. por lo menos eran m´s r´pidas. Desde hace siglos se sabe que un conmutador al tener dos estados (abierto y cerrado) puede representar los n´meros 0 y u 1. ruido y consumo. en principio. as´ que podemos representar un determinado n´mero por meı u dio del estado de una serie de conmutadores. El e a n siguiente paso fue sustituir los ruidosos y poco fiables rel´s por calurosas y. a o . Si disponemos de muchos conmutadores. Por ejemplo.2. lo que supuso un gran avance (ya o estamos en los a˜ os sesenta).

1. que nos permite acceder a ella.2. tiene una representaci´n peri´dica en base 2. El tiempo . La unidad de informaci´n que se transfiere a o desde la memoria principal recibe el nombre de palabra. As´ los n´meros cuya a ı u representaci´n binaria sea ilimitada. Cada unidad de informaci´n binaria es un bit. Una suma se suele realizar en menos tiempo del necesario en buscar los sumandos en memoria. 16 o 32 bits. o m´s. Por ejemo plo 0. unidad central de proceso o CPU17 Existe un circuito b´sico formado por dos transistores llamado flip-flop a o biestable. de nuevo tendremos nuestro sistema de representaci´n de n´meros binarios. Las t´cnicas que permiten un grado de e empaquetamiento tan elevado de denominan tecnolog´ VLSI (Very Large ıa Scale of Integration). y baratos que sus antecesores. Sin embargo. mientras que ´ a en los grandes puede oscilar entre 12 y 64. bits. El tiempo de acceso (tiempo necesario para leer informaci´n almacenao da en una direcci´n determinada) es un par´metro de importancia crucial o a en el funcionamiento de un ordenador. Los dos unicos puntos importantes a considerar son los siguientes: ´ 1. Estos o avances tecnol´gicos han hecho posible que los ordenadores actuales sean o mucho m´s r´pidos. ser´n truncados al guardarse en o a una direcci´n de memoria. dispositivos de entrada/salida. 2. lo que da idea del grado de miniaturizaci´n del proceso. fiables. Hay dos valores asociados con cada elemento de memoria: Su contenido que ser´ su dato o instrucci´n en c´digo binario y la direcci´n de esa a o o o posici´n de memoria. a a La memoria principal de un ordenador consiste en varios millones de circuitos elementales biestables capaces de almacenar un cero o un uno.2 · Memoria. En los ordenadores personales la longitud de una palabra suele ser de 8. Nos estamos refiriendo a los n´ meros cuya o u representaci´n binaria (no decimal) tiene infinitos decimales. Los detalles sobre o o la construcci´n y organizaci´n de la memoria principar de un odenador no suelen tener demasiada importancia a la hora de escribir un programa. Esto signin fica que dispondremos solamente de un n´mero finito de d´ u ıgitos para representar las cantidades durante su c´lculo. Gracias a la microelectr´nica se pueden colocar varias decenas de mio les de circuitos dentro de un chip. o El tama˜o de la palabra de cualquier ordenador es finito. La longitud de la o o memoria ocupada por una dato nos limitar´ la precisi´n de los c´lculos a o a num´ricos y es una caracter´ e ıstica importante de cada ordenador. o formando un byte. El estado en el que o u se encuentra el circuito puede medirse (lectura) o alterarse (escritura). El tama˜o resultante no suele superar el cent´ n ımetro cuadrado. Si asignamos el valor 1 a uno de estos estados y el 0 al otro. no suele accederse o de forma individual a un bit. Este circuito posee dos estados de funcionamiento estable. sino que suelen agruparse de ocho en ocho.

y suelen consistir en cintas magn´ticas. o La CPU consta de dos secciones que realizan cada una de estas funciones. discos fijos. Control y observaci´n del sistema completo. Ejecuci´n de las instrucciones recibidas desde la memoria principal. as´ como del almao a ı cenamiento de ambos en registro de acceso inmediato. La CPU realiza dos tipos de tareas: 1. sino que a deber´n pasar a la memoria principal. Estas o o ´rdenes consisten principalmente en operaciones aritm´ticas y de compae raci´n entre dos datos. o CPU). Este consta de todos los o dispositivos de entrada y salida de informaci´n del ordenador junto o con el tr´fico de informaci´n interna al ordenador. o La memoria principal no es el unico sitio en el que se almacena la in´ formaci´n. Los e datos de la memoria secundaria no son accesibles directamente. o ALU). es la unidad central de proceso (central processing unit. La secci´n del ordenador que realiza las operaciones con los datos. Este tiempo aunque es extraordinariamente peque˜o. Estos diso positivos reciben el nombre de memoria secundaria o memoria masiva. o En una RAM la informaci´n no se guarda de forma secuencial. existen otros lugares donde a cambio de unos tiempos de acceso o mayores se pueden conservar grandes cantidades de informaci´n. cd’s. etc. sio guiendo las instrucciones almacenadas en la memoria principal.18 2 · Nociones sobre arquitectura de ordenadores de acceso en una memoria de gran tama˜o es del orden de 5 · 10−7 segunn dos. e o u La unidad de control es la responsable de la b´squeda de la siguiente instrucci´n y del c´lculo de las direcciones de los datos. a o 2. La ´ memoria principal recibe el nombre de memoria de acceso aleatorio (random access memory. Con este t´rmino se indica que el tiempo de acceso e a una determinada posici´n de memoria es pr´cticamente independiente de o a su direcci´n. aunque o puede ser c´modo imaginar las direcciones ordenadas consecutivamente. diskketes. La idea es que los t´rminos utilizados n a e ultimamente tienen grandes probabilidades de ser necesarios de nuevo. Las instrucciones se traen de la memoria principal en el orden indicado por el programa. o mientras que la unidad de proceso de datos e instrucciones ser´ la respona sable de ejecutar las ´rdenes elementales que forman el programa. puede reducirse n colocando los datos e instrucciones m´s recientes en otra memoria m´s pea a que˜a y r´pida. Esta segunda secci´n recibe el nombre de unidad o o aritm´tico-l´gica (arithmetic-logic unit. es la memoria cache. Si hay . o RAM). La unidad de control se encarga de la realizaci´n de las tareas de control.

La transcripci´n del algoritmo en sentencias de o un lenguaje de programaci´n se denomina “codificaci´n” y constituye el o o “programa” inform´tico. Los dispositivos m´s com´nmena u te usados. ´stos se transfieren a la ALU o e junto con la orden de sumarlos. La met´fora del enano computador Lee atentamente este a art´ ıculo que te servir´ para comprender mejor lo descrito en esta secci´n. est´n escritos en c´digo binario. Recuerda que todos los elementos procesados en la CPU. a e Los resultados de un programa pueden presentarse en una pantalla o en una impresora o bien guardarse en un disco. por una parte. de colocar el resultado o en la memoria principal.3. La unidad de control. lenguajes de ensamblaa do. datos e instrucciones. Nota 2. una vez realizada la operaci´n. Los o o dispositivos de entrada m´s comunes son el teclado y los discos magn´ticos. lenguajes de alto nivel Como se ha comentado anteriormente. Los programas escritos de esta forma o reciben el nombre de programas en lenguaje m´quina o microprogramas. Lenguaje de m´quina. de o manera que. lenguajes de alto nivel19 a que realizar alguna operaci´n con los datos. Pr´cticamente todos los programas constan de tres fases: a introducci´n de los datos. compararlos. son el teclado y la pantalla. se puedan ejecutar por un ordenador. u Para ello se han creado los lenguajes de programaci´n definidos formalmente o para especificar algoritmos. restarlos. o El lenguaje de programaci´n es un medio de comunicaci´n entre el homo o . a o 2. Esta es la unica forma de comunicaa o ´ ci´n que puede entender el ordenador. El conjunto de programas forman el software. las memorias principal y secundaria y los dispositivos de entrada E/S forman parte del llamado hardware del ordenador. se encarga.2.1. la definibilidad de un algoritmo exige especificar de modo riguroso y sin ambig¨edades las acciones a realizar.3 · Lenguaje de m´quina. lenguajes de ensamblado. etc. especialmente en los ordenadores personales. Un lenguaje de programaci´n est´ formado por un a o a conjunto de c´digos que permiten describir los algoritmos y sus datos. y por otra. su descripci´n sea comprensible por el usuario. Todos los componentes f´ ısicos tales como la CPU. a Dos de los errores m´s comunes son el de realizar una serie de operaa ciones con datos que no se han introducido en la memoria principal y el de ejecutar un programa que carece de las instrucciones para la impresi´n o de los resultados. procesamiento e impresi´n de los resultados.

o a 2. hay que disponer de las direcciones que los datos toman en la memoria central. b) La utilizaci´n de lenguajes intermedios. Interesa precisar dos hechos: 1. Todo ello oblig´ a las empresas inform´ticas a desarrollar una forma de o a programaci´n m´s sencilla y plantearon distintas formas de automatizarla. Como la palabra a lo dice. etc. en el ordenador real se numeran las direcciones de memoria. Pero la verdadera evoluci´n la marcaron dos aspectos: o o a) La simbolizaci´n. Dado que la circuiter´ del ordenador s´lo acepta. Un ordenador s´lo entiende y acepta su propio lenguaje de m´quina.20 2 · Nociones sobre arquitectura de ordenadores bre y el ordenador. La utilizaci´n de un lenguaje m´s evolucionado y por lo tanto los proo a gramas escritos en ese lenguaje. o Al igual que se ha descrito en el art´ ıculo del enano computador. Para describir al ordenador un m´todo para resolver e un problema. o a Un primer paso fue utilizar bases num´ricas superiores a dos para la e representaci´n de las instrucciones. exigir´ la existencia de un “traductor” a a (escrito en el propio lenguaje m´quina”) para que convierta estos proa gramas al lenguaje m´quina. decimal. Ello presenta una gran inconveniente en la programaci´n. Los lenguajes “simb´licos” obvian este inconveniente al permitir reemo plazar los c´digos de operaci´n y las direcciones num´ricas de memoria por o o e s´ ımbolos que representan esos c´digos y direcciones. por el hecho de que el programador o o debe conocer en todo momento la ubicaci´n de sus datos en la memoria central. Por una o parte hay que conocer los c´digos de todas las operaciones. en la entrada y la salida del ordenador: o octal. el lenguaje binario es la base del “lenguaje m´quina”. hexadecimal. el “lenguaje m´quina” es particular de cada ordenador en concreto a y aunque su base sea binaria existen entre unos y otros grandes diferencias. y por otra. es necesario formalizarlo adecuadamente para que lo pueda entender. o a o L´gicamente el lenguaje m´quina es muy inc´modo de utilizar. e interpreta ceros y ıa o unos. o .

el ensamblador crea en memoria una tabla. Cuando se produzca la correspondencia. tanto los c´digos de operaci´n como las direcciones o o de memoria. todos los lenguajes intermedios necesitan la presencia de un traductor. por ejemplo: limitaci´n del n´mero o o u de caracteres. 2. cargada en memoria en el momento del ensamblaje. dando como resultado a un programa objeto que es directamente ejecutable por el ordenador. lenguajes de ensamblado. y en ´l cada o a e orden corresponde directamente a una instrucci´n en c´digo m´quina. etc.Los lenguajes de ensamblaje o ensambladores. el c´digo simb´lico ser´ sustituido por su o o a correspondiente num´rico de la tabla.. o a Las instrucciones escritas de manera simb´lica deben ser transcritas en o instrucciones en lenguaje m´quina. Es entonces preciso que el ensamblador lea. Por lo tanto. el ensamblador como o para cada s´ ımbolo encontrado en el programa con los existentes en una tabla preestablecida. obligaci´n de que aparezca un car´cter en particular. En la segunda pasada.Los lenguajes autocode. lenguajes de alto nivel21 a Los nombres simb´licos de las direcciones de memoria exigen la observano cia de determinadas reglas de formaci´n. el ensamblador reexamina cada instrucci´n y o reemplaza los s´ ımbolos por las direcciones correspondientes de la tabla. dos veces el programa fuente para obtener el programa en lenguaje m´quina (programa objeto). cuya funci´n es la de o traducir el programa fuente en el programa-m´quina. La acci´n del ensamblador consiste en traducir todos los elementos simb´lio o cos del programa fuente. Por ello es necesario crear unas tablas de correspondencia a fin de traducir estos s´ ımbolos en c´digo de o operaci´n y direcciones num´ricas. a La funci´n del ensamblador no se reduce a estas operaciones que se o acaban de indicar. u Por su parte el lenguaje autocode tambi´n es un lenguaje muy pr´ximo e o . Con respecto a los c´digos de operaci´n.3 · Lenguaje de m´quina.. El lenguaje ensamblador es muy pr´ximo a la m´quina. etc. Esta funci´n la desarrolla un prograo e o ma particular que se denomina traductor. asignando una direcci´n real a cada direcci´n simb´lica que encueno o o tra. Tambi´n se encarga de reservar zonas de memoria. por lo menos.2. o a o o a la descripci´n de un dato elemental tal como se presenta en la memoria del o ordenador. de e cargar alg´n dato inmediato en la memoria del ordenador. En una primera pasada. Para las direcciones. el proceso es ale go diferente. ya que los s´mbolos no pueden ser ser a o interpretados directamente por el ordenador. Entre los lenguajes intermedios m´s importantes tenemos: a 1.

la t´cnica de ensamblaje es esencialmente la e misma que en el ensamblador. estando los primeros mucho m´s pr´ximos a o al lenguaje natural. la ventaja a de poder utilizar subprogramas ya existentes en el sistema. son los m´s representativos en esta categor´ a ıa. Esto ultimo a ´ explica que la estructura de los lenguajes evolucionados sea completamente diferente al de los de ensamblaje. Se denomina as´ porque en el momento de la traducci´n o ı o genera varias instrucciones de c´digo m´quina. . El autocode presenta adem´s. cuando lo precisemos. se distinguen dos categor´ o ıas: 1. entre 1954 y 1958. definidos por el programador o por otro usuario del equipo. ALGOL y BASIC que fueron los primeros en ser desarrollados. Los lenguajes evolucionados tienden a una universalidad de empleo.22 2 · Nociones sobre arquitectura de ordenadores al lenguaje m´quina. en 1970 y 1972 respectivamente. en principio o cualquier tipo de problema. los de alto nivel van orientados al problema. o bien porque los programas a construir son de ejecuci´n permanente o y precisan una gran optimizaci´n tanto de memoria como de tiempo de ejeo cuci´n. Supongamos que en un programa se desea leer datos en diferentes puntos del mismo. Se diferencia del ensamblador por la introducci´n de la a o macro-instrucci´n. o que nos realice esta operaci´n de lectura. Tomando como criterio el tipo de aplicaci´n. con independencia del tipo de ordenador. Lo l´gico ser´ disponer de un conjunto o ıa de instrucciones independientes del resto del programa (macro-instrucci´n). Se dividen en dos grupos dependiendo de fin para el que han sido desarrollados. o En el lenguaje autocode. o o a procedimiento o rutina. bien porque las operaciones que se desean programar no son posibles con los lenguajes m´s evolucionaa dos. Los lenguajes intermedios se suelen utilizar. casi. o a La llegada de los lenguajes de alto nivel marca una etapa importante en la evoluci´n de los lenguajes intermedios. Es es el caso de algunos sistemas operativos y procesadores b´sicos. Mientras que ´stos est´n orientados o e a a la m´quina. Al ensamblador del autocode se le denomina macroensamblador. Lenguajes de prop´sito general: que permiten resolver. permitiendo ser procesados. o a La noci´n de macroinstrucci´n est´ ligada al concepto de subprograma. y el PASCAL y el lenguaje C que se desarrollaron posterioremente. a) Lenguajes definidos para aplicaciones t´cnico-cient´ e ıficas: FORTRAN. y poder llamar a esta macroo instrucci´n. con respecto al ensamblador.

es pues. en instrucciones que comprenda e interprete el ordenador. LISP.2. o Un compilador. En los lenguajes de alto nivel se sustituye el traductor (ensamblador o macroensamblador) por el compilador. etc. GPSS. manejo de m´quina herramienta. se emplea ORACLE. . etc). Dentro de una especializaci´n m´s cercana. Hoy en d´ en la explotaci´n y tratamiento de o ıa o grandes bases de datos. un traductor de programas escritos en un lenguaje de alto nivel. El compilador es un programa complejo (normalmente escrito en ensamblador) que tiene como misi´n traducir expresiones y seno tencias. lenguajes de ensamblado. Son a t´ n ıtulo de ejemplo el CAD. ACCES.3 · Lenguaje de m´quina. M´s adelante. etc 2. lenguajes de alto nivel23 a b) Lenguajes de gesti´n. entre otros. o a S-plus o R. RPG. y la traducci´n se denomina compio laci´n. escritas en un lenguaje de alto nivel. SIMULA. El COBOL o ha sido b´sicamente estudiado y pensado para programar aplicaa ciones de gesti´n. como el COBOL. veremos con m´s detalle la forma a a de compilar un programa escrito en C. Lenguajes especializados: que han sido dise˜ados para un tipo especial n de aplicaci´n (por ejemplo. o a n ense˜anza asistida. dise˜o.

24 2 · Nociones sobre arquitectura de ordenadores .

Cap´ ıtulo 3 Introducci´n al lenguaje de programaci´n o o C (I) C es un lenguaje de programaci´n de uso general. ensamblador y depurador. es decir se a a puede hacer casi cualquier cosa y ejecutar sin cambios en una amplia variedad de m´quinas. o o La ausencia de estas caracter´ ısticas es lo que da ventajas al lenguaje. secuenciales. De hecho. El lenguaje es e a independiente de cualquier arquitectura de la m´quina en particular. de iteraci´n. paralelismo sincronizaci´n o corrutinas. trabaja con la misma clase de objetos que la mayor´ de los ordenadores: caracteres. que a deber´n siempre ser aportados por funciones llamadas expl´ a ıcitamente. Dentro de los lenguajes de alto nivel. programas de procesamiento de textos y e bases de datos. De igual forma. utilizados e o normalmente en las m´quinas. a 25 . de selecci´n. pero no o o multiprogramaci´n. Ha sido y es utilizado o para escribir programas num´ricos. Por el contrario es tremendamente vers´til y port´til. esto es. bloques y subprogramas. casi todas las aplicaciones de UNIX est´n escritas en C. como el compilador. Los compiladores se escriben f´cilmente. las sentencias de control de flujo en C son sencillas. a El sistema operativo UNIX est´ escrito en su mayor parte en lenguaje a C. el software escrito en C es id´ntico en casi cualquier m´quina que soporte a C. n´meros y direcciones. puede escribirse en poco espacio y aprenderse con n facilidad. Los programas en C tienden a ser lo suficientemente eficientes como para que no haya que escribirlos en lenguaje ensamblador. es clasificado como un lenguaje de relativo bajo nivel. C es relativamente peque˜o. que ıa u pueden ser combinados con los operadores aritm´ticos y l´gicos. No contiene elementos de alto nivel. Exceptuando a a los programas que necesariamente dependen en alguna forma de la m´quia na.

C incluye apuntadores o punteros y capacidad e aritm´tica de direcciones. Nociones sobre trabajo en el Sistema Operativo UNIX. El editor vi UNIX es un sistema operativo con las siguientes caracter´ ısticas: a. c. Interactivo: El sistema acepta peticiones. C posee las construcciones fundamentales de control de flujo para escribir programas bien estructurados: agrupamiento de sentencias. Mostraremos ´stos y otros elementos esenciales del lenguaje C en ´ste y e e los siguientes cap´ ıtulos. arreglos estructuras. enteros de varios tama˜os y n´meros en punto flotante. uniones y funciones.O. las procesa en forma de di´logo con el usuario. Existe tambien una jerarqu´ de tipos n u ıa de datos derivados.26 3 · Introducci´n al lenguaje de programaci´n C (I) o o En nuestro caso. a b. u La influencia de BCPL le llega indirectamente a trav´s del lenguaje B. En C los objetos fundamentales son caracteres. Para ello. B´sicamente consta de los siguientes elementos: a 1.1. ıa Muchas de las ideas principales de C provienen de un lenguaje mucho m´s a antiguo pero a´n vigente: el lenguaje BCPL inventado por Martin Richars. 3. Multiprogramado: El sistema es capaz de realizar m´s de una a tarea para cada uno de los diversos usuarios. escrito e por Ken Thompson en 1970 para el primer sistema UNIX. con entre otras funu ciones: .. for) o al final (do) y selecci´n entre un conjunto de casos posibles (switch). utiliza la t´cnica denominada de a e tiempo compartido. de la HP9000 de nuestro Centro de C´lculo. ciclos (bucles). KERNEL: Constituye el n´cleo central del S. toma de o o decisiones (if). hemos optado por describir la situaci´n del sistema o UNIX. pues dicho entorno de a trabajo es el habitual de la mayor´ de programadores de C. con comprobaci´n de la condici´n de termio o naci´n al principio (while. creados con apuntadores. Multiusuario: El sistema permite atender a varios usuarios de forma simult´nea.

impresoras. maneja ficheros los cuales son almacenados en distintas divisiones del disco duro. o c) Edici´n de textos. COMANDOS: Son un conjunto de expresiones prefijadas que permiten dar ordenes al S. asign´ndolos entre los disa tintos usuarios. Nombre de ficheros: . d) 2. denominados directorios. o b) Manipulaci´n del contenido de los ficheros. Tanto los directorios como los ficheros poseen un nombre. HERRAMIENTAS Y UTILIDADES: Son un conjunto de programas que se entregan con el S. Ficheros: guardan datos. Por ejemplo: a) Manipulaci´n de ficheros..). Normalmente hay un supervisor del sistema que tiene acceso a todas las cuentas. e) Gesti´n de ventanas.O. o c) Un controlador de procesos. SHELL: Es la capa externa al n´cleo. o d) Compilaciones de programas. programas. o 4. Windows es por el contrario ´ un sistema visual: hay que seleccionar iconos.1 · Nociones sobre trabajo en el Sistema Operativo UNIX. Se puede decir que el SHELL es: a) Un int´rprete de comandos. 3. b) Planificaci´n de la ejecuci´n de los distintos programas realizados o o por los usuarios. a) Claves de acceso a tu cuenta personal. Su misi´n es la de proporcionar u o un interface entre el usuario y el S.. etc.O. y que realizan tareas m´s complicadas y a de diversa ´ ındole..3..O. Mantenimiento del sistema de archivos. UNIX es un sitema operativo orientado a ´ comandos: hay que escribir las ordenes. Los usuarios tienen unicamente acceso a su cuenta personal. ´ login: etpgamaa password: ara2411 b) Estructura del arbol de directorios: ´ Como todo sistema operativo. e b) Un lenguaje de programaci´n. El editor vi27 a) Control de los recursos del ordenador. c) Control de los dispositivos de hardware (discos.

No guardan inforo o maci´n como tal. o Por ejemplo. etc.Para trabajar con distintos ficheros: Ejemplo 1: p* denota todos ficheros cuyo nombre comienza por p Ejemplo 2: p?pe denota todos los ficheros cuyo nombre comienza por p. ficheros existentes en directorios ajenos a la cuenta del usuario. ’. luego cualquier cosa (incluso nada) y luego pe.Cada cuenta tiene el nombre de un usuario que cuelga de aqu´ ı. que coincide con el nombre de la cuenta a la que accedo.f. a (5) users: todos los usuarios del sistema est´n aqu´ a ı.. pero con un l´ ımite de espacio (p. Es posible leer o incluso copiar.28 3 · Introducci´n al lenguaje de programaci´n C (I) o o .).. en mi caso 10 megas).Hay caracteres prohibidos (/. . ız: (1) bin: guarda los comandos del sistema. a .. los ficheros fuente en FORTRAN tienen extensi´n .Se pueden usar como m´ximo 14 caracteres (en versiones modernas. sino que sirven para estructurar dicha informaci´n. ”) .En mi caso etpgamaa es el directorio del usuario (directorio home).c. a incluso m´s). ´ (4) mnt: montaje del arbol. ?. Todo lo dem´s es accesible. . o Directorios: guardan ficheros u otros directorios.e. (2) etc: guarda los sistemas de arranque. (3) lost+found: se va guardando lo que el sistema va perdiendo. Depende del mismo arbol y es quien orga´ niza el ´rbol (utilidades del montaje. . en o C tienen extensi´n . ..Cada usuario puede hacer lo que quiera por debajo. (0) Ra´ se denota por /. Usualmente los nombres de ficheros tienen lo que se llama extensi´n. a dependiendo de los correspondientes permisos.

.+fd... u tio m´s se usa y ´sto es la clave de numerosos errores que se suelen a e cometer)... La mayor´ de las ordenes son en u u ıa ´ min´sculas. Ejemplo 3:.. mkdir.>cd nombre-de-directorio Cambia al directorio nombre-de-directorio. UNIX o diferencia min´sculas y may´sculas. u par´metros .>mkdir nombre-de-directorio Crea un directorio que se llame nombre-de-directorio.1 · Nociones sobre trabajo en el Sistema Operativo UNIX. Para separar las distintas componentes (orden.>rmdir nombre-de-directorio Borra el directorio que se llame nombre-de-directorio. El editor vi29 / bin etc ls...>mkdir docencia metnu Crea ambos directorios. Para ello se requieren dos condiciones: tiene que estar vacio y no tiene que estar activo (no estar en ´l ni por debajo de ´l). users pract p9mnu p9mnuaa p9mnuga ··· p9mnups Estructura de comandos: . Comandos para manejar el arbol de directorios ´ pwd.. dame el directorio activo. se usan espacios en blanco (s´lo aqu´ y en ning´n sia o ı. rmdir. opciones. cd a) .>pwd D´nde estoy.... d) .. Las opciones siempre empiezan con un gui´n). o b) .. e e Ejemplo 4:.>orden -[opciones] [par´metros] a ([] puede no estar.. c) .3.>rmdir docencia metnu Borra ambos directorios.

..-enlaces (5) propiet..ls. ana y alumnos que contiene la uni´n de pepe y ana..-fichero (10) (1)Tipo de fichero: .>ls -l /users/etpgamaa informaci´n completa de dio cho directorio. pero no lo modifica (similar al comando TYPE de MS-DOS).>cat /users/etpgamaa/programas/nombre-de-fichero Esto es dar el path absoluto... me dir´ que no lo encuentra...>cd (Sube hasta el ra´ ız) Ejemplo 7:.... . Comandos para manejar ficheros: cat. mv. (Sube uno hacia arriba) Ejemplo 6:.vi a) >cat nombre-de-fichero visualiza el fichero nombre-de-fichero. (6) grupo (7) tama˜o n (8) fecha (9) n. • Sin opciones ni par´metros: informaci´n completa del a o directorio actual. d directorio. .>cat pepe ana > alumnos mantiene el fichero alumnos (existente) y le a˜ade el contenido de pepe y ana.>cat p* > pruebas une todos los ficheros que empiezan por p creando uno de nombre pruebas... sube un paso hacia arriba....>ls -R -l /users/etpgamaa informaci´n completa y o recursiva de todos los dicrectorios.>cd /users/etpgamaa/docencia/metnu 5./Programas/nombre-de-fichero La orden .rm. hay que dar el camino: . texto. o . b) >ls [-opciones] [directorios] lista el contenido del directorio en el que estoy. datos).. Si en mi directorio a tengo creados dos subdirectorios: Programas y Datos.fichero ordinario (Programa.. Adem´s dicho comando permite concatenar: a . Si el fichero no est´ en u a dicho directorio. • ...>cd .>cat . El resultado de ´sto: e (1) --(2) --(3) --(4) n.. n .. cp. • .. chmod. me encuentro en este ultimo y quiero que me ense˜e un fichero que est´ en ´ n a Programas.>cat pepe ana > alumnos existen tres ficheros pepe.. lp.30 3 · Introducci´n al lenguaje de programaci´n C (I) o o Ejemplo 5:. La b´squeda la realiza en el directorio activo. Otro modo de hacerlo es dar el path relativo.

3.>cp pepe /users/etpgamma/alumnos/pepe.>rm pepe ana borra los ficheros pepe y ana. • -i: petici´n de confirmaci´n. modo: (1) usuario.. cd-rom. >rm [-opciones] ficheros o directorios borra los ficheros indicados. machacando cualquier contenido que tuviera este ultimo y manteniendo intacto el fichero-origen. w Escritura: se puede modificar..-fichero: nombre del fichero. Si hay cambio de ´ directorio hay que indicar el camino.. (10)N.. o (5)N.s. este comando no mantiene el fichero-origen.1 >mv fichero-origen fichero-destino Frente al comando cp. Ejemplo 10: ..quitar w escritura o=other = asignar x ejecuci´n o a=all c) d) e) f) . (2) operador...r controladores de dispositivos de e/s (impresora. (2) Propietario (3) Otros usuarios del mismo grupo que el propietario (4) Todos los dem´s usuarios del sistema a En cada grupo de tres: r Lectura: se puede ver pero no modificar.w. Es conveniente pedir confirmaci´n. (La tienen los ejecutables).. o Ejemplo 9: . n ´ o (9)Fecha: Fecha y hora de la ultima modificaci´n del fichero. El editor vi31 c. >cp fichero-origen fichero-destino copia el fichero-origen en el fichero-destino. (6)Propietario (7)Grupo: grupo del propietario (8)Tama˜o: memoria que ocupa el fichero. (3) permiso (1) u=user (2) + a˜adir (3) r lectura n g=group . (Es similar al comando RENAME de MSDOS).>rm p* borra todos los ficheros cuyo nombre empieza por p. modem.. su contenido est´ unicaa´ mente en el fichero-destino. Puede tener como opciones: • -r: borrado recursivo dentro de un directorio. Ejemplo 11: ..>rm -r * borra todo lo que hay en el directorio.-enlaces: n´mero de lugares (directorios) desde los que es u accesible el fichero (frente a lo que sucede con el MS-DOS. o o Ejemplo 8: .) Permisos: aparece en tres bloques de tres.1 · Nociones sobre trabajo en el Sistema Operativo UNIX. >chmod [modo] fichero/directorio cambia los permisos de acceso. x Ejecuci´n.. un fichero en UNIX es accesible desde distintos lugares).

a Todo el texto o c´digo de un programa se almacena en un fichero de exo tensi´n . a o b) >who inf´rmame de los actuales usuarios del sistema. Compilado.c. o Para salir: ESCAPE :wq sale guardando.32 3 · Introducci´n al lenguaje de programaci´n C (I) o o Ejemplo 12: .>chmod el permiso de escritura Ejemplo 13: . h) >vi pepe crea el fichero pepe.>who -a todos los usuarios.>chmod g+w pepe ana a˜ade a la gente del grupo n sobre los ficheros pepe y ana. eliminar l´ ıneas. a • . Aparece: Old password: New password: Re-enter new password: Para desconectarse en la terminal: CTRL+D o teclear EXIT 3.. passwd a) >date dame la fecha que tiene registrada la m´quina.. Para compilar el programa.. utilizando el comando cc. En nuestra m´quina el de uso habitual es vi. Tambi´n puede estar en modo EDICION. who. e Se entra en modo EDICION tecleando i ´ a. para escribir. abrir l´ ıneas nuevas. seguido del nombre completo del fichero fuente.>chmod Ejemplo 14: ..2.>who am i s´lo de mi mismo. pulsando la tecla ESCAPE.c .. se invoca o al compilador desde el prompt de sistema operativo. Por ejemplo: $ cc programa1. ESCAPE :q! abandona.. Otros comandos: date. o c) >passwd Sentencia para cambiar el password (palabra de paso).. ejecuci´n y depuraci´n de prograo o mas en C En entornos UNIX. denominado fichero fuente. moverse. etc. • . Puede estar en modo COMANDO. para posicionarse.. • Sin opciones ni par´metros: usuarios conectados.. Es el comando de edici´n de textos o en UNIX. los programas se editan utilizando los editores del sistema.. g+wx o-rwx pepe g=rx pepe g) >lp pepe imprime el fichero pepe.

que por defecto a se llama a. Variables escalares. subindicadas (arrays) y constantes La sentencia .3 · Variables escalares.c. Un debuger a e es un depurador de c´digo. Se vuelve a compilar el programa.c Para ejecutar el programa. $ lint programa1. hay que especificarlo: o $ cc -c programa1.c Si queremos que el ejecutable tenga otro nombre (por ejemplo programa). No genera c´digo.c programa2.o. hay que especificarlo tambi´n: e $ cc -o programa programa1.c. Si dicho programa no tiene errores. subindicadas (arrays) y constantes 33 Si el fichero fuente contiene errores. $ cc -o programa programa1. La forma de depurar un programa en nuestro entorno es mediante la sentencia lint.3. problemas potenciales de portabilidad. sino que realiza una como o probaci´n muy estricta de tantos aspectos de un programa como puedan o ser comprobados durante la compilaci´n.c. programa3. y crea adem´s del fichero objeto el fichero ejecutable. etc. programa2.c 3. el compilador crea el fichero objeto. aparecen por pantalla todos ellos.3. la sentencia cc produce tambi´n el o ´ e linkaje. Detecta inconsistencias en los tio pos de datos.out.c Si existen distintos ficheros fuente. que tiene el mismo nombre que el fichero fuente. se pueden compilar todos ellos. programa1. Si el fichero fuente era unico. basta escribir su nombre $ programa En C existe adem´s lo que se denomina en ingl´s debugger. variables no utilizadas o no inicializadas. uso incongruente de argumentos.c programa3. crear los correspondientes programas objeto y linkarlos de manera que tengamos un unico ejecutable de nombre ´ programa. Si s´lo queremos que cree el objeto. pero con extensi´n . Hay que volver al programa fuente y subsanar dichos errores.

Nombres de variables En el lenguaje C. Como sus propios nombres indican una constante es un valor que nunca cambia. Una variable consigue su variabilidad representando una posici´n. o La sentencia.34 j = 5 + 10. j = j − 2. a o De manera que la sentencia anterior. Esta es una de las funciones del compilador. Sabemos que 5 y 10 son valores enteros. o Variable j Direcci´n o 2482 2486 2490 Memoria · · · · 4 bytes · · · · · · · · Las declaraciones iniciales en un programa indican las variables que se van a utilizar. r´stale la constante 2 y almacena el resultado de nuevo en dicha e posici´n de memoria. Para el ordenador. es decir las dan un valor inicial. 3 · Introducci´n al lenguaje de programaci´n C (I) o o significa lo siguiente: suma los valores 5 y 10 y asigna el resultado a la variable denominada j. Para que dicha expresi´n o o tenga sentido para un ordenador. es entendida entonces como: coge el contenido de la posici´n 2486 de la o memoria. En la lectura de la sentencia anterior. lo cual significa que su valor puede cambiar. estamos suponiendo conocidos todos los s´ ımbolos involucrados. se puede poner nombre casi a cualquier cosa: variables. mientras que una variable puede representar distintos valores.1. Los nombres . por ejemplo en la 2486. establecen su tipo y tal vez las inicializan. todos estos signos son una mera combinaci´n de ceros y unos. o una direcci´n de memoria. sin embargo. para el ordenador significa: suma los valores 5 y 10 y coloca su resultado en la posici´n de memoria 2486.3. hay que decirle previamente el significado de cada uno de los s´ ımbolos mencionados. e incluso localizaciones en un programa. funciones. que + e = son operadores y que j es una variable. o La variable j est´ localizada en alguna posici´n. 3. Las variables y las constantes son los objetos b´sicos que se manipulan en a un programa. constantes.

2. Punteros: representan direcciones de memoria. Dentro de los datos compuestos.3 · Variables escalares. Por ejemplo: . subindicadas (arrays) y constantes 35 pueden contener letras. 2. que a su vez pueden ser: e a) enteros: int o long (dependiendo de su tama˜o) n b) reales (en punto flotante): float o double. de manera que: u u var. tenemos: 1. 2. es decir. otros en 31. o El lenguaje C diferencia entre may´sculas y min´sculas. Tipo y tama˜o de datos n La primera clasificaci´n que se puede hacer en cuanto al tipo de datos en o C es. como simples y compuestos. En general no existe l´ ımite en cuanto al n´mero de caracteres que puede tener un nombre en C. tipo y contenido. ´ 3.3. Hay algunas otras restricciones en los nombres de las variables y constantes simb´licas. dar su nombre. (Hay una lista de ellas en casi cualquier manual de C).3. Estructuras: composici´n de datos de distinto tipo. o el car´cter barra baja . Num´ricos. Car´cter. Dentro de los datos simples. No se pueden elegir como nombres las palabras reservadas. intentar que los ocho primeros caracteres de cada nombre sean unicos. o Variables Num´ricas e Todas las variables hay que declararlas antes de usarlas en el programa. Var. Alu gunos compiladores antiguos lo fijan en 8 caracteres. n´meros. que a su vez pueden ser: a a) simples: char b) cadenas: char* 3. pero deben u a comenzar por una letra o por dicho caracter. VAR son nombres diferentes. dependiendo de su composici´n: o 1. Arrays: secuencia (vector) de datos simples del mismo tipo. Es buena costumbre por la portabilidad del programa.

(es una cadena de caracteres) Cuando se inicializa as´ el ordenador entiende: ı. si adem´s las inicializamos: a char respuesta=‘s’.9). (tabla [j]. Variables Car´cter a Las declaraciones son: char respuesta. tabla 2 0 5 1 200 2 520 3 3 4 0 5 1 6 850 7 1230 8 58 9 .36 int x. float factorial=1. (la s es el valor simple que representa inicialmente la variable respuesta) char* nombre =”Jaime ”. define un vector de elementos enteros con 10 posiciones. mientras que ”s”es entendido como una cadena de caracteres dada por (‘s’ ‘\0’). upper. ı int x=0. float factorial. j=0. (‘J’‘a’‘i’‘m’‘e’‘\0’) Nota: ‘s’ representa el caracter simple s. char* nombre.0E-5 (que es lo mismo que 0. pero s´ antes del uso de dicha variable. Variables Subindicadas (arrays) EJEMPLO 1 int tabla [10]. step. 3 · Introducci´n al lenguaje de programaci´n C (I) o o int lower. El contenido de la variables no es necesario darlo al principio del programa.00001).

char nombre[20]={‘J’ .‘e’.19). tabla[3]=520.‘a’. 0 1 tabla2 9 0 Variables Estructura EJEMPLO 4 3 · · · · · · · · · · 19 800 . /* es una matriz 10x20 */ define una matriz de 10 filas y 20 columnas. 200. subindicadas (arrays) y constantes Se inicializar´ ıa: int tabla={2. 1230. 5. 3. EJEMPLO 3 int tabla2 [10][20]. La inicializaci´n para una casilla. 520. 1. 0. 850. x=tabla[9]. a j=0. 58}. o bien.‘m’.‘i’. 37 define un vector de elementos de tipo car´cter con 20 posiciones.3 · Variables escalares. /* el valor de la variable x es 58*/ EJEMPLO 2 char nombre [20]. nombre · 0 1 · · 19 La inicializaci´n es: o char nombre[20]=”Jaime”. de elementos enteros. (nombre[j].3. ser´ o ıa: tabla2 [1][3]=800.‘\0’}.

fnacimiento. 0 20 40 -17. n empleado. 72}.mes=10.nombre=”Pepito”.fnacimiento.a˜o=72.10. O agrupando los datos para la estructura fnacimiento: empleado.4 . mes. El primer programa en C El siguiente programa imprime la tabla de conversi´n de temperatuo ras Fahrenheit a grados cent´ ıgrados o Celsius.7 4. La variable es empleado y est´ compuesta por tres tipos de datos: el nombre (de tipo car´cter). empleado. y el sueldo (una variable escalar de tipo entero). Un ejemplo.sueldo=150000. utilizando la f´rmula: C = o (5/9)(F − 32). a~o.38 struct persona { char nombre [20]. 3. Para inicializar esto: empleado.fnacimiento={3.72}. struct fecha 3 · Introducci´n al lenguaje de programaci´n C (I) o o { int dia. 10. O bien: empleado={ ”Pepito”.150000}. Todo esto define una estructura para cada persona. } fnacimiento. } empleado.8 -6.4. a a la fecha de nacimiento (a su vez una estructura compuesta por tres datos enteros). { 3. empleado. n int sueldo.

lower=0. en este caso explica lo que hace el programa. donde aparece la funo ci´n main. e A continuaci´n aparece la lista de definiciones de las variables a utilizar o (su tipo) y su inicializaci´n (valor inicial). } } Las dos primeras l´ ıneas de programa son un comentario que.. Todos los programas en C deben tener una funci´n main en o o alg´n sitio. El primer programa en C 60 .0).20. fahr.8 148. En el a e Cap´ ıtulo 4 se especifican m´s detalles al respecto. printf("%4. tanto u o de int como de f loat depende de la m´quina que se est´ utilizando. A continuaci´n comienza el cuerpo de programa. celsius).. /*tama~o del incremento */ n fahr=lower. /* l´mite superior */ ı step=20.1f\n". float fahr.6 .9 39 El programa es /* imprime la tabla Fahrenheit-Celsius para f=0.. /* l´mite inferior de la tabla de temperaturas*/ ı upper=300.7 137. Normalmente main har´ uso de otras funciones para efectuar su u a trabajo.. f loat indica punto flotante. que encierran el a bloque de todas las sentencias de que consta ´ste. 126.3. Cada l´ ınea de la tabla se calcula de la misma forma. Como hemos visto anteriormente o el tipo int implica que las variables de la lista son enteros.0f %6. celsius... El compilador prescinde de los caracteres comprendidos entre /∗ y ∗/. step. para lo cual se utiliza . fahr=fahr+step. es decir n´meros que poseen parte fraccionaria.. a Las sentencias o proposiciones individuales se terminan con punto y coma. El cuerpo del programa est´ dentro de unas llaves.4 · Un ejemplo. La precisi´n. upper.. 260 280 300 15. while (fahr<=upper){ celsius=(5.0/9.0) * (fahr-32.300 */ main() { int lower.

finaliza el ciclo y el control del programa pasa o a la sentencia que lo sigue. Es unicamente una funci´n ´ o util de la biblioteca est´ndar de funciones de entrada/salida habitualmente ´ a accesible desde los programas en C. fahr. fahr<= 300. aparecen las variables o cuyos valores se van a imprimir. } El programa anterior produce el mismo resultado. que tanto la asignaci´n o f ahr = lower. pero su aspecto es diferente. con un d´ la especificaci´n de formato y separadas por comas..0/9.) argumentos y en qu´ forma ha de ser impreso. o Cuando la condici´n es falsa. indicando cada signo % el lugar en que ha de sustituirse cada e uno de los restantes (segundo. Para terminar. Evidentemente. se ejecuta el cuerpo del ciclo (todas las sentencias encerradas entre llaves). El siguiente es una modificaci´n del anterior: o /* imprime la tabla Fahrenheit-Celsius */ main() { int fahr. Este es el prop´sito o de la sentencia while. como la comprobaci´n while(f ahr <= upper) trabajan coo mo se esperaba: la variable de tipo int es convertida a f loat antes de realizar la operaci´n. M´s adelante hablaremos de las reglas que rigen la conversi´n entre ena o teros y punto flotante. fahr=fahr+20) printf("%4d %6. en la sentencia escrita en el programa la eso o u pecificaci´n de conversi´n %4. hay que se˜alar que printf no forma parte del lenguaje..1f\n". n En C no se define ni la entrada ni la salida.0)*(fahr-32)).. (5. o e Si es cierta (fahr es menor o igual que upper). Por ejemplo. o En el ejemplo tambien se muestra c´mo funciona la funci´n printf .1f pide otro n´mero que ocupar´ a e u a d´ ıgito despu´s del punto decimal. A continuaci´n se vuelve a o evaluar la condici´n y si es cierta se ejecuta de nuevo el cuerpo del ciclo. La variable f ahr ıa . %6. o o printf es una funci´n con formatos de conversi´n de uso general que deo o tallaremos m´s adelante. for (fahr=0. Se examina la condici´n encerrada entre par´ntesis. hay muchas formas de escribir un programa en C. Despu´s de e e lo sumo 6 posiciones. Su primer argumento es una cadena de caracteres a que imprimir. Se han eliminado la mayor´ de las variables. Por ahora basta observar.40 3 · Introducci´n al lenguaje de programaci´n C (I) o o un ciclo (bucle) que se repite una vez por cada l´ ınea. sin ıgitos despu´s del punto decimal.0f indica que se ha de imprimir un n´mero en punto flotante que ocupe un espacio de a lo sumo cuatro caracteres. tercero.

3.4 · Un ejemplo. El primer programa en C

41

permanece como int, hasta que en la propia funci´n printf se realiza la o conversi´n %4d. o Se ha utilizado una nueva expresi´n iterativa, la proposici´n f or. Consta o o de tres partes separadas por punto y coma. La primera se ejecuta una sola vez, antes de entrar en el ciclo propiamente dicho. La segunda es el criterio o condici´n que controla la ejecuci´n. Esta condici´n se eval´a; si es cierta, o o o u se ejecuta el cuerpo del ciclo, y a continuaci´n se ejecuta la tercera parte o que es la parte de reinicializaci´n y se vuelve a evaluar la condici´n. El ciclo o o termina cuando la condici´n se torna falsa. Como en while el cuerpo del o ciclo puede ser una sola sentencia o un grupo de ellas colocadas entre llaves. La decisi´n de utilizar un while o un f or es arbitraria. o Una ultima observaci´n es la siguiente. Es una mala costumbre sepultar ´ o en el interior del programa n´meros como 0, 300 ´ 20 en el caso del programa u o anterior. Siendo dif´ cambiarlos sistem´ticamente si la ocasi´n lo merece. ıcil a o Para ello se utilizan en C las sentencias #def ine y las constantes simb´lio cas. Mediante la construcci´n #def ine, al comienzo del programa se puede o definir un nombre simb´lico como una determinada cadena de caracteres y o darlas un valor.

#include <stlib.h> #include <stdio.h> #define LOWER 0 #define UPPER 300 #define STEP 20 /* imprime la tabla Fahrenheit-Celsius */ main() { int fahr; for (fahr=LOWER; fahr<= UPPER; fahr=fahr+STEP) printf("%4d %6.1f\n", fahr, (5.0/9.0)*(fahr-32)); exit(0); }

Las tres cantidades definidas son constantes y por lo tanto no pueden aparecer en las declaraciones. Obs´rvese adem´s que no hay punto y coma e a despu´s de las definiciones. e

42

3 · Introducci´n al lenguaje de programaci´n C (I) o o

3.5.

Operadores y funciones standard m´s usuales a

Los operadores son las herramientas mediante las cuales se construyen las expresiones que manipulan los datos. Una clasificaci´n inicial puede ser o la siguiente: 1. Unitarios: − (menos unitario, −x representa la negaci´n de x) y + (m´s o a unitario, +x representa el valor de x, est´ disponible en compiladores a ANSI). Por ejemplo, si m es igual a 5, −m es igual a -5. No hay que confundir estos operadores con los operadores binarios aritm´ticos de e diferencia y suma. 2. Aritm´ticos: + (suma), − (diferencia y tambi´n existe el − unitario; e e p.e. −x representa la negaci´n de x), ∗ (producto), / (cociente), % o (divisi´n entera: x %y produce el resto de dividir x entre y, y por o lo tanto es cero, si x es divisible entre y). El operador % no puede utilizarse con datos de tipo float o double (reales). Ejemplo 1: j = 3 − −x, es interpretado como j = (3 − (−x)). El primer − se˜ala una resta, el segundo es un − unitario. n Ejemplo 2: 3/4 da como resultado el valor 0, puesto que es una divisi´n o entera (la parte fraccional se trunca). Si queremos realizar la divisi´n, o hay que escribir 3,0/4,0. 3. Relacionales: == (igual), ! = (distinto), > (mayor que), < (menor que), >= (mayor o igual que), <= (menor o igual que). o o 4. Asignaci´n: = (Usado en inicializaciones), + = (suma-asignaci´n), o o o − = (resta-asignaci´n), ∗ = (multiplica-asignaci´n), / =(divide-asignaci´n), o % = (resto-asignaci´n). Ejemplo 3: a = b pon el valor de b en a. Ejemplo 4: a+ = b pon el valor de a + b en a. Ejemplo 5: a− = b pon el valor de a − b en a. Ejemplo 6: a∗ = b pon el valor de a ∗ b en a. Ejemplo 7: a/ = b pon el valor de a/b en a. Ejemplo 8: a % = b pon el valor de a %b en a. El t´rmino de la izquierda en las anteriores expresiones (a), puede e referirse a una posici´n de memoria. o 5. L´gicos: && (and), || (or), ! (not). o Ejemplo 9: a&&b vale 1 si a y b son no nulos, vale 0 en otro caso. Ejemplo 10: a||b vale 1 si a ´ b son no nulos, vale 0 en otro caso. o

3.5 · Operadores y funciones standard m´s usuales a Ejemplo 11: !b vale 1 si b es nulo, vale 0 en otro caso. 6.

43

Acceso a miembros (Operadores de memoria): [] (usado en declaraciones de arrays) , · (usado en declaraciones de estructuras), → (acceso a estructuras , cuando se dispone del puntero), () (para pasar par´mea tros en funciones, & (pon un objeto en una direcci´n de memoria), ∗ o (obt´n el objeto situado en esa direcci´n de memoria). e o Ejemplo 12: &x obtiene la direcci´n de x. o Ejemplo 13: ∗x obtiene el objeto situado en la direcci´n x. o Ejemplo 14: x[5] obtiene el valor del vector x en la posici´n 5. o Ejemplo 15: x.y obtiene el valor del miembro y en la estructura x. Ejemplo 16: p → y obtiene el valor del miembro y en la estructura punteada por p.

7.

Operadores de incremento y decremento: C dispone de dos operadores un poco raros, que incrementan y decrementan el valor de las variables. El operador ++ le suma 1 a su operando; el operador −− le resta 1. La ıstica especial de estos operadores es que pueden ser utilizados caracter´ como prefijos (antes de la variable, como en + + n) o bien como sufijos e (despu´s de la variable, n − −). En los dos casos ++ aumenta (−− o disminuye) en 1 el valor de la variable n, pero la expresi´n + + n incrementa n antes de utilizar su valor, mientras que n + + lo hace e despu´s de que se ha empleado su valor. Por ejemplo, si n = 5: x = + + n; pone un 6 en x (incrementa primero el valor de n), pero x = n + +; pone un 5 (toma primero el valor de n).

8.

Operador condicional ? : que resulta ser una versi´n abreviada de la o expresi´n if...else.... Por ejemplo: z = (x < y)?x : y), comprueba si o x < y, en cuyo caso asigna z = x, si x ≥ y, la asignaci´n que hace es o z = y. Operadores para la manipulaci´n de bits: >>, <<, &, |, ∧, ∼. o Operador cast, de conversi´n de tipo. o Operador sizeof, que devuelve el tama˜o en bytes, de datos o expren siones.

9. 10. 11.

3.5.1.

Funciones m´s usuales a

Una funci´n es una colecci´n de componentes del lenguaje agrupadas o o bajo un mismo nombre. Si la funci´n est´ bien dise˜ada, debe representar o a n

44

3 · Introducci´n al lenguaje de programaci´n C (I) o o

un peque˜o n´mero de operaciones, que son indicadas por el nombre de n u la funci´n. A menudo, una funci´n aparece unas pocas l´ o o ıneas m´s abajo a de donde se la llama una sola vez, para clarificar una parte del programa. Vamos a mostrar los mecanismos para la definici´n de funciones escribiendo o la funci´n power(m, n) que eleva el entero m a la potencia representada por o el entero positivo n.

/* prueba de la funcion power */ main() { int i; for (i=0; i< 10; ++i) printf("%d %d %d\n",i,power(2,i), power(-3,i)); } power(x,n) /* elevar x a la n-´sima e int x,n; { int i,p; p=1; for(i=1; i<=n; ++i) p=p * x; return(p); } potencia */

Las funciones aparecen en cualquier orden, y en uno o m´s archivos a fuente. Por el momento supondremos que est´n en el mismo archivo, por lo a que se mantiene lo que hemos aprendido sobre ejecuci´n de programas en o C. En la l´ ınea de impresi´n se llama dos veces a la funci´n power. En cada o o llamada se pasan dos argumentos a power, que cada vez devuelve un entero que se formatea e imprime. Los argumentos de la funci´n power se declaran o para que se conozca su tipo. La declaraci´n de los argumentos se realiza o entre la lista de argumentos y la llave de apertura. Cada declaraci´n debe o acabar con un punto y coma. Los nombre de los argumentos en power son completamente locales a la funci´n. Esto tambi´n se aplica a las variables i o e y p: la i de power no tiene nada que ver con la i de main. El valor que calcula power se devuelve al programa principal main mediante la sentencia return. Puede aparecer cualquier expresi´n entre los o par´ntesis de una sentencia return. Una funci´n no tiene por qu´ devolver e o e un valor; una sentencia return sin expresi´n devuelve el control al programa o principal aunque no le devuelve un valor util. ´

n) /* elevar x a la n-´sima e int x. --n) p=p * x. Ser´ un caso particular de la funci´n power: ıa o int square(int num) { . Esto es ´ una ventaja y permite escribir programas m´s compactos. en lugar o de recibir sus direcciones. power(x. Por ejeme plo. Veamos una versi´n de a o power que utiliza este hecho. La diferencia principal es que en C la funci´n o llamada no puede alterar el valor de una variable de la funci´n que llama. podr´ ıamos definir una funci´n para calcular el cuadrado de un n´mero o u entero. Versi´n 2 */ o El argumento n se utiliza como variable temporal. o unicamente puede cambiar el valor de su copia privada y temporal. si est´n debidamente inicializadas. ya que los argumentos se tratan como variables locales a la rutina llamada.n. siendo decrementada hasta que llega a ser 0.5 · Operadores y funciones standard m´s usuales a 45 En C todos los argumentos se pasan por valor. { int p. con pocas variaa bles externas. Cualquier cosa que hagamos con n dentro de power no tendr´ ningun efecto sobre el argumento. Trataremos esto con m´s detalles cuando hablemos de direcciones a de memoria y paso de argumentos por referencia.3. for(p=1. es poa sible hacer que una funci´n modifique el valor de una variable en la rutina o llamante. Esto significa que la funci´n recibe los valores de sus argumentos en variables temporales. En C como en otros lenguajes de programaci´n existen cientos de funo ciones prefabricadas almacenadas en librer´ Algunas de las standard. recen a continuaci´n: o Funci´n o fgetc() fopen() fclose() time() sin() log() Descripci´n o Lee un caracter de un fichero Abre un fichero Cierra un fichero Calcula el tiempo actual Calcula el seno de un angulo ´ Calcula el logaritmo de un n´mero u Como hemos visto tambi´n es posible crear nuevas funciones. Cuando sea necesario. return(p). } potencia. n>=0. apaıas.

46 int answer.h> int main(void) { extern int square(int). que dicha funci´n o main() genera un valor y trabaja con dos argumentos. Si el argumento de exit() es el cero.h. La llamada de exit() dentro de una funci´n main() hace o o exactamente lo mismo que la sentencia return. todo programa ejecutable debe contener una funci´n espeo cial denominada main(). es lo mismo que return 0. return answer. La funci´n exit() causa el final del programa. Se debe incluir cualquiera de las dos. solution=square(5). exit(0). devolviendo el control o al sistema operativo. } Este peque˜o programa asigna el cuadrado de 5 a la variable denominada n solution. Adoptaremos por ahora.. Si se usa exit() debe incluirse al comienzo del programa el fichero stdlib. ni los argumentos. Las reglas de utilizaci´n de esta funci´n son las mismas que las o o de cualquier otra. Sin embargo. Analizaremos este detalle en el Tema 5. o u deber´ ıamos escribir: #include <stdlib. que indica donde comienza la ejecuci´n. dentro de cada funci´n o main(). denotan errores en la ejecuci´n. Argumentos distintos del cero. Por ejemo plo para invocar a la funci´n square(). que es donde est´ almacenada dicha funci´n. a o . no hemos declarado el tipo de datos que devuelve. answer=num*num. Es decir exit(0). que eleva un n´mero al cuadrado. int solution. significa que el programa termina sin errores. } 3 · Introducci´n al lenguaje de programaci´n C (I) o o La funci´n main() o Por otro lado.

int solution.h.h> int main(void) { extern int square(int). El o argumento que aparece entre par´ntesis.c. Si tienes almacenado el programa anterior en un fichero de nombre potencia2. La otra variable solution. podr´ compilar o ıas ambos programas mediante la sentencia: .3. o a La funci´n printf() o El programa anterior es correcto pero poco operativo. que vamos a llamar. Aunque describireo mos dicha funci´n con m´s detalle en otro cap´ o a ıtulo. Una de las razones es que no nos deja ver su salida. o #include <stdio. es un entero que nosotros utilizamos para almacenar el valor que devuelve la funci´n square().h> /*Fichero donde se encuentra printf()*/ #include <stdlib. printf("El cuadrado de 27 es %d \n". indica que el resultado que devuelve o dicha funci´n ser´ almacenado en la variable solution. es el valor del que se va a calcular el o cuadrado. exit(0). posiblemente en otro fichero o a fuente. } Se˜alar que hemos tenido que a˜adir en la cabecera el fichero stdio. esto es. Una soluci´n para este problema es utilizar o la funci´n printf(). La palabra extern indica que dio cha funci´n est´ definida en cualquier sitio. se˜alar que el s´ n ımbolo %d indica que el argumento que va a imprimir es un entero decimal. El operador de asignaci´n =. indica que es el valor pasado como e argumento a dicha funci´n.5 · Operadores y funciones standard m´s usuales a 47 En el caso anterior declaramos dos nombres en main(). n n donde se encuentra la funci´n de Input/Output printf(). solution). solution=square(27). La secuencia \n indica a dicha funci´n que salte de l´ o ınea (newline).c y la funci´n square() en otro denominado square. El primero es la funci´n square(). o La siguiente sentencia es en la que se invoca a la funci´n square().

As´ %d. volver a compilar u ıa y volver a ejecutar.c square. o Dicha funci´n realiza el trabajo contrario al de printf(). especifica el a n ı formato de la variable que se va a imprimir.h> int main(void) { . Para obtener el cuadrado de otro u ´ n´mero.c. num1. Basta a continuaci´n que o teclees $ potencia2 y obtentr´s a El cuadrado de 27 es 729 En la sentencia que aparece utilizada la funci´n printf(). Si a˜adimos n esta posibilidad al programa anterior: #include <stdio. se utilizan dos o argumentos. mientras ´sta escribe o e la salida del programa en la pantalla.c que crea un ejecutable con nombre potencia2. habr´ que modificar el fichero fuente potencia2. %d. debe contener tantas especifio o caciones de formato como variables se van a imprimir: printf(”Imprime tres valores: %d. indica que la variable solution es un entero decimal. el primero adem´s de incluir un peque˜o texto. la funci´n scanf() lee el valor del dato o introducido mediante el teclado y se lo asigna a una variable. Hay otros especificadores para otros tipos de datos.48 3 · Introducci´n al lenguaje de programaci´n C (I) o o $ cc -o potencia2 potencia2. num2. %d”. Esto se puede evitar con el uso de la funci´n scanf(). La funci´n scanf() o Todav´ el programa anterior es poco operativo. num3). ya que permite calcular ıa unicamente el cuadrado del n´mero 27. por ejemplo: Car´cter a %s %x %f %o Especificaci´n o Vector caracter Entero Hexadecimal Dato real (punto flotante) Entero octal La versi´n general de la funci´n printf().h> /*Fichero donde se encuentra printf()*/ #include <stdlib.

La diferencia es que en la funci´n scanf(). la funci´n scanf() almacena el valor introducido directamente en dicha direcci´n de memoria.5 · Operadores y funciones standard m´s usuales a extern int square(int). los nombres o de variables. o Su sintaxis es similar. Una sentencia de este a tipo puede tener dos estructuras: #include < fichero > y #include “fichero” . } 49 Ahora hemos declarado una nueva variable input val. int input_val. &input_val). van precedidas del s´ ımbolo &. La expresi´n &input val. printf("El cuadrado de %d es %d\n". int solution. o Sentencias #include Esta sentencia hace que el compilador lea texto de otros ficheros. solution).3. printf("Introduce un valor entero:"). luego pasamos este valor o como argumento a square(). significa ”la direcci´n o o de memoria de la variable imput val”. scanf("%d". input_val. que sirve para almacenar el valor entero introducido por el teclado. exit(0). solution=square(input_val). Esto hace que el sistema lea el dato de la terminal y lo coloque en una direcci´n de memoria. La o ejecuci´n del programa tendr´ el aspecto: o a $ potencia2 Introduce un valor entero: 22 El cuadrado de 22 es 484 Hemos visto que la funci´n scanf() realiza la tarea contraria a printf(). al mismo tiempo que lee el fichero que est´ compilando. En realidad.

ya que existen permanentea mente en lugar de aparecer y desaparecer. por ello pueden usarse en lugar de listas de argumentos para comunicar datos entre funciones. En el segundo caso. de manera que las sentencias j = 1 + 34. Cada variable local de llama a la funci´n.50 3 · Introducci´n al lenguaje de programaci´n C (I) o o En el primer caso. el compilador busca en alg´n lugar designado por el u sistema operativo. a Sentencias #define Sirven para asociar una constante a una variable. o Como alternativa es posible definir variables externas a todas las funciones. Una variable externa ha de definirse fuera de las funciones. Las variables externas son accesibles globalmente. variables globales a las que puede acceder cualquier funci´n o mediante su nombre. Variables externas locales a main. dan el mismo resultado. Por esta raz´n. Esto les asigna memoria real. j = 1 + var 1. Adem´s mantienen sus valores. o o de autom´ticas.6. Por ejemplo: o #define var 1 34 asigna el valor 34 a la variable var 1. incluso despu´s de que finalizan e las funciones que las cambian. esto es. que ocupa una direcci´n de memoria. por eso no conservan su valor entre dos llamadas sucesivas. y se o les ha de dar un valor a la entrada de la funci´n. y desaparece o tales variables reciben el nombre Las variables de main son privadas o bi´n es v´lido para las variables de otras e a una rutina comienza a existir cuando se cuando la funci´n acaba. a Las variables autom´ticas aparecen y desaparecen con la invocaci´n de a o la funci´n. el compilador busca en el directorio donde se encuentre el fichero que est´ compilando. Lo anterior tamfunciones. 3. Ambito de definici´n y validez de las variao bles. La variable ha de ser declarada en toda funci´n que o .

o en forma impl´ ıcita a trav´s del contexto.0 e da como resultado 2. main(){ /* comienzo del programa */ printf("%d\n". var1).3. precedencia y asociatividad. var2). la expresi´n de asignaci´n x = y = 3. Las dos est´n estrechamente relacionadas. var2=200. var1).h> /* definici´n de la variable global var1 */ o int var1=50.0/8. o a Sin embargo la m´quina hace la cuenta siempre en el mismo orden.125. Ve´moslas en un ejemplo: a a La expresi´n 2 + 3 ∗ 4 evaluada por la m´quina es 14. var1). 3. es asociativa de o o derecha a izquierda. /* escribe 50 */ { /* var1 y var2 variables locales al bloque 1 y 2*/ int var1=100. distinto al de la expresi´n (3. y posteriormente se asigna este mismo valor a la variable x. Esto se resume diciendo que u el operador ∗ tiene una precedencia m´s alta que el operador +.0 que da o −0.5.7. igual que 3 ∗ 4 + 2. Primero a eval´a 3 ∗ 4 y luego al resultado le suma un 2. /* escribe 0 y 200 */ } printf("%d\n". var1. Si se quiere a establecer otro orden hay que utilizar par´ntesis. /* escribe 100 */ } printf("%d\n". /* escribe 50 */ } /* final de programa*/ 3. Por ejemplo.0 − 4. var1. printf("%d %d\n".0)/8. var2).7 · Precedencia y orden de evaluaci´n entre operadores o 51 quiera acceder a ella. En primer u lugar se asigna un 3 a la variable y. Un operador puede ser asociativo de derecha a izquierda o de izquierda a derecha. esto puede hacerse mediante una declaraci´n extern o expl´ ıcita.0 − 4. /* escribe 100 y 200 */ { /* bloque 2*/ int var1=0. . Precedencia y orden de evaluaci´n entre opeo radores Los operadores tiene dos propiedades. Por otra parte. es decir se eval´a comenzando por la derecha. printf("%d %d\n". Veamos un ejemplo: e #include <stdio.

a dcha. a dcha. a izda. Manipulaci´n de bytes o Manipulaci´n de bytes o Manipulaci´n de bytes o L´gico o L´gico o Condicional Asignaci´n o Izda.52 3 · Introducci´n al lenguaje de programaci´n C (I) o o En la siguiente tabla aparecen clasificados los operadores de acuerdo a su precedencia y asociatividad. a izda. a dcha. a dcha. Izda. Izda. a izda. Aritm´tico multiplicativo e Aritm´tico aditivo e Manipulaci´n de bytes o Relacional de desigual. Izda. a dcha. Precedencia Alta Dcha. Izda. a dcha. a dcha. Relacional de igual. a dcha. Izda. Izda. Izda. a dcha. a dcha. Clase de Operador Primario Unitario Operadores en la clase () [] → · cast sizeof & (direcci´n en memoria) o ∗ (de referencia) − + ∼ ++ −− ! ∗ / % + − << >> < <= > >= == ! = & ∧ | && || ?: = += −= ∗= / = % = >> n = <<= &= ∧= Asociatividad Izda. Izda. Izda. Dcha. Dcha. a dcha. Baja .

long. Los otros: short. En este tema vamos a describir los tipos de datos escalares y el tipo void.Cap´ ıtulo 4 Introducci´n al lenguaje de programaci´n o o C (II) Un tipo de dato es una interpretaci´n que da el compilador a una cadena o de bits.1. Hay adem´s un tipo de dato -voida que no es ni simple ni compuesto. Los cinco dados por: char. signed y unsigned son a modificaciones calificadoras de datos simples. car´cter. aunque ciertas declaraciones se realizan impl´ ıcitamente por el contexto. Tipos de datos escalares: enteros. La palabra int es una palabra reservada que especifica que el dato es de tipo entero. Para o declarar la variable j como entera. int. Hay nueve palabras reservadas para especificar los tipos de datos simples o escalares. La declaraci´n o le dice al compilador c´mo interpretar y almacenar una serie de bytes. En el tema anterior vimos una clasificaci´n de datos como simples (o o escalares) y compuestos (o agregados). double. 53 . 4. enum son tipos b´sicos. de coma flotante y enumeraci´n a o Todas las variables deben ser declaradas antes de utilizarlas. debemos escribir: int j. float. Declaraciones.

mientras que otros s´lo dedican dos. long int k. En la mayor´ de las m´quinas un byte son ocho ıa a bits. puedes e n a usar int. El n´mero de bits utilizados para representar un entero determina el u rango de valores que pueden ser almacenados en ese tipo. Si no tienes inter´s en el tama˜o del entero dentro de la m´quina. pero hay excepciones. a long. short int j. En la mayor´ de las ıa m´quinas short int es un entero que ocupa dos bytes. es lo mismo que short j. Algunos compiladores dedican cuatro bytes para almacenar un u entero. signed y unsigned. Tipo int short int long int unsigned short int unsigned long int signed char unsigned char Tama˜o (en bytes) n 4 2 4 2 4 1 1 Rango de valores −231 a 231 − 1 −215 a 215 − 1 −231 a 231 − 1 0 a 216 − 1 0 a 232 − 1 −27 a 27 − 1 0 a 28 − 1 Consideremos por ejemplo un entero short int (16 bits). normalmente del tama˜o de los enteros de la n instalaci´n local o un n´mero en punto flotante de precisi´n normal u o un n´mero en punto flotante de doble precisi´n u o Adem´s hay una serie de calificadores que se aplican a los enteros: short. donde n representa la posici´n o . short y long se refieren a diferentes tama˜os de n los n´meros. Cada bit representa un valor de 2 elevado a la potencia n.54 char int float double 4 · Introducci´n al lenguaje de programaci´n C (II) o o un byte (octeto) capaz de contener un car´cter del juego de a caracteres de la instalaci´n local o un entero. En otro caso debes especificar short o long. long k. Por otra parte el tama˜o de un o n byte tampoco es constante. mientras que long a int es un entero que ocupa cuatro bytes.

12E3 tambi´n es v´lida para valores float. Hay una notaci´n para constantes octales y hexadecimales: un cero que o encabece una constante int indica octal. En los compiladores standard ANSI. Toda constante en punto flotante se e a toma por defecto como double. Tipos de datos escalares: enteros. dado que por defecto una variable no declarada como unsigned. u 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 Adem´s en C existe la posibilidad de declarar una variable como no a negativa (unsigned). con valores positivos o negativos. existe la posibilidad de declarar a los enteros signed. La diferencia est´ en las variables de tipo ıa a car´cter. que pueden ser signed o unsigned por defecto. Vamos a ver ahora que la notaci´n cient´ o ıfica usual 123. por lo que la notaci´n e sirve tanto para o float como para double.4. Para hacer ´sto se utiliza el calificador unsigned: e unsigned int k.5 ∗ 10−5 ). unsigned short j. de coma flotante y enumeraci´n55 a o del bit: 215 214 213 212 211 210 29 28 27 26 25 24 23 22 21 20 Por ejemplo para expresar el n´mero decimal 9 = 23 + 20 = 8 + 1. es decir.1 · Declaraciones.5e-5. car´cter. Si queremos declarar e inicializar una variable en punto flotante float eps = 1.000015 = 1. La mayor´ de los a ıa compiladores utilizan signed char por defecto. (le estamos asignando a la variable real eps el valor inicial 0. deber´ ser signed. Un calificador de este tipo parece superfluo. el grupo 0x (o bien OX) indica he- .45e−7 o bien 0. unsigned long m.

(Ver tabla de conversi´n ASCII. amarillo. por lo que la implementaci´n puede variar de un compilador a otro. sin embargo. media. o para las equivalencias entre los distintos tipos de representaci´n). El a ´ a o valor num´rico de una constante car´cter es el valor num´rico del car´cter en e a e a el conjunto de caracteres de la representaci´n. La constante \0 representa el car´cter o con valor nulo. vamos a declarar dos variables de enumeraci´n llamadas color e intensidad. color=1. Aunque aparecen como dos a caracteres. ‘0’. \\ (barra invertida).verde color. Las constantes car´cter pueden participar en las operaciones num´ricas a e como cualquier otro n´mero. en realidad son s´lo uno. es particularmente util. \t (tabulador). oscura intensidad. amarillo. cuando o ´ quieres crear un unico conjunto de valores que pueden ser asociados con ´ una variable. azul.56 4 · Introducci´n al lenguaje de programaci´n C (II) o o xadecimal. Este tipo de dato no forma parte de la versi´n de C dada por Kernighan y o Ritchie. diferente del valor num´rico a e 0. \ (ap´strofo). oscura. A color le vamos a poder asignar o uno de los cuatro valores constantes rojo. enum brillante. media. color=azul+verde. Por ejemplo. el decimal 31 de puede escribir como 037 en octal. La declaraci´n o ser´ ıa: enum rojo. dar´ se˜al de problemas ante sentencias ıa n como: color=brillante. o Un buen compilador. el car´cter cero. . etc. \0 (nuo lo). A intensidad le vamos a poder asignar los valores brillante. intensidad= azul. Por ejemplo. o como 0x1f (0X1F ) en hexadecimal. El tipo de dato de enumeraci´n enum. azul. es 48. en el conjunto o de caracteres ASCII. aunque es m´s frecuente encontrarlas en comu a paraciones con otros car´cteres. a Ciertos caracteres no imprimibles se representan como constantes car´cter a mediante secuencia de escape como \n (nueva l´ ınea). o Una constante car´cter es un unico car´cter escrito entre ap´strofos. verde. Las constantes octales o hexadecimales pueden acabar en L para hacerlas long. En el siguiente ejemplo.

} Esto indica que la funci´n no devuelve ning´n valor. Conversiones de tipo Los operandos de tipos diferentes que aparecen en una expresi´n se cono vierten a un mismo tipo de acuerdo con unas cuantas reglas. El primero es indicar que una determinada funci´n no devuelve un valor. Lo veremos con m´s e a detalle. u Por otra parte no est´n permitidas expresiones que carecen de sentido. { .4. . e o a . b) int a. b. m´s adelante. Tambi´n pod´ o u e ıamos haberlo utilizado en la llamada de func() extern void func().y).2 · Conversiones de tipo El tipo de dato void 57 Tampoco forma parte de la versi´n de C dada por Kernighan y Ritchie. utilizar un n´mero en punto flotante como sub´ u ındice. Esto informa al compilador de que no se puede asignar el valor de retorno de dicha funci´n. Adem´s los caracteres y los enteros (char e int) se mezclan en expresiones a aritm´ticas: todo char de una expresi´n se convierte autom´ticamente en int. puedes ver una o funci´n definida como: o void func (a. coa mo la conversi´n de un entero a punto flotante antes de sumar dicho entero o con un n´mero en punto flotante. a por ejemplo. Por ejemplo.2. tiene dos importantes usos. a 4. ıa El otro uso de void es declarar un puntero gen´rico. dar´ un error.5). (Por ejemplo: 3+2. por ejemplo: o num=func(x.5=5. Las unicas ´ conversiones que se realizan autom´ticamente son las que tiene sentido. o El tipo dato void.

lower(c) /* convierte c en min´scula. s[i]-‘0’. Si lo es. { int i. ı Otro ejemplo de conversi´n de char a int.1: Uso de la funci´n atoi() que convierte una cadena de o d´gitos en su valor num´rico correspondiente. Por definici´n. (V´lido para caracteres ASCII). n=0.. s[i] >= ‘0’ && s[i] <= ‘9’. y s´lo si hay d´gitos entre los a o ı caracteres ‘0’ y ‘9’. el valor num´ria ı e co del d´gito es s[i] ...n. se realiza convirtiendo los valores char al tipo int. ı ´ o etc. Esto ultimo funciona correctamente s´lo si ‘0’. ı e atoi(s) /* convierte el caracter s en entero*/ char s[]. Las variables y e constantes de tipo char son esencialmente id´nticas. Por ultimo la sentencia ´ n=10*n+s[i]-‘0’. son positivos y est´n en orden creciente. o ı y dicha expresi´n se puede utilizar como sub´ndice. ‘1’. . en sistema decimal (base diez). s´lo para ASCII */ u o int c. } La comparaci´n o s[i] >=‘0’ && s[i]<=‘9’ determina si el car´cter que hay en s[i] es un d´gito. la funci´n devuelve la a u o misma letra sin alteraciones. expresa el valor del d´gito s[i]-‘0’. toda aritm´tica en la que intervienen valores de tipo char o e e int.. Por ejemplo. a los e o valores int en contextos aritm´ticos. Si el car´cter no es una letra may´scula. Afortunadamente esto es cierto para todos los juegos de caracteres ordinarios. return(n).58 4 · Introducci´n al lenguaje de programaci´n C (II) o o Ejemplo 4. en cuanto al tipo. for(i=0.‘0’. es la funci´n lower(). que asigna o o u u a a cada letra may´scula su correspondiente min´scula. ++i) n=10*n+s[i]-‘0’. es una expresi´n u a entera con un valor entre ‘0’ y ‘9’ seg´n sea el car´cter almacenado en s[i].

o En general los operandos que intervienen en una operaci´n. Si alg´n operando es de tipo double. Los enteros de mayor magnitud (long) se . el de tipo ”inferior”es ascendido al tipo ”superior”. El resultado es de tipo ”superior”. o De double a float. Si no se aplica la regla anterior. los operandos deben ser de tipo int y el resultado es de tipo int. en caso de que el resultado sea cierto y el valor 0 en caso contrario. ya que las letras may´sculas o u u y min´sculas correspondientes est´n situadas a una distancia fija. en las que o se ha establecido que devuelvan el valor 1. Las e o conversiones tambi´n tienen lugar en las asignaciones. el valor de la parte e derecha se convierte al de la izquierda. u a Otra utilidad de la conversi´n autom´tica de tipos est´ relacionada con o a a expresiones como i > j y expresiones l´gicas que utilizan && y ||. el otro se convierte en double u y el resultado es double. y float se convierte en double. En C la aritm´tica del punto flotante se realiza en doble precisi´n. y adem´s el alfabeto es continuo. el otro se convierte en long y el resultado es long. consideu a radas como valores num´ricos.2 · Conversiones de tipo { if( c >= ‘A’ && c <= ‘Z’) return(c+‘a’. } 59 Esta funci´n act´a correctamente en ASCII. De float a int la conversi´n provoca un truncamiento de la parte fraccionaria. Si no se aplica la regla anterior y alg´n operando es de tipo u unsigned.‘A’). que es el tipo del resultado. el otro se convierte en unsigned y el resultado es de tipo unsigned. Si no se puede aplicar ninguna de las reglas anteriores. Las conversiones autom´ticas se realizan de acuerdo con las siguientes a reglas. Las reglas de conversi´n se resumen en las siguientes: o char y short se convierten en int. un redondeo.4. y si alg´n operando es de tipo u long. Si un operador como + o ∗ con dos operandos (un operador binario) tiene los operandos de distinto tipo. else return(c). son convertidos o o a al tipo del operando de precisi´n m´s alta. no e a contiene ning´n otro car´cter entre dichas letras. es decir. antes de realizar la operaci´n.

con lo cual transmitir´ mensajes de error si recibe argumentos a de otro tipo.421 0. la rutina sqrt() espera que se le pase un argumento de o tipo double. de manera que u cada n´mero se almacena en un n´mero finito de d´ u u ıgitos binarios. Por o ejemplo: −30.421 ´ 0. donde n1 son los d´ fraccionaria. el n´mero de d´ u ıgitos de cada n´mero que almacena un ordenador es u ıgitos de la parte entera y n2 los de la finito n = n1 + n2 . sqrt((double) n) convierte n al tipo double antes de pas´rselo a sqrt. Concretamente. u La representaci´n en punto fijo es la que utilizamos normalmente. en punto fijo y en punto flotante.3245 son representaciones en punto fijo. o Por ultimo se puede forzar la conversi´n expl´ ´ o ıcita de tipo coaccionada”de ¸ una expresi´n. En la construco o ci´n o (nombre de tipo) expresi´n o la expresi´n se convierte al tipo citado mediante las anteriores reglas de cono versi´n. mediante una construcci´n denominada cast. Nociones sobre representaci´n de datos e imo plicaciones sobre la precisi´n num´rica o e El ordenador digital representa los n´meros en base 2. char y short o e se convierten en int y float se vuelve double.3. Como hemos o dicho. seg´n vimos en u la tabla del tema anterior. 4. A esto se debe el que hayamos declarado los argumentos de la funci´n como int y double. El operador cast tiene a la misma precedencia que cualquier otro operador unitario. Por ejemplo.60 4 · Introducci´n al lenguaje de programaci´n C (II) o o convierten en tipo short o en caracteres (char) por eliminaci´n de los bits o de orden superior. Los argumentos de una funci´n son expresiones y por ello las reglas de o converi´n de tipo tambi´n se aplican con ellos.3245 − . incluso cuando o se llama a la funci´n con char y float. Hay dos formas de almacenar estos n´meros. n1 0030 0000 n2 421000 324500 −30. Si n es entero.

4.3 · Nociones sobre representaci´n de datos e implicaciones sobre la precisi´n num´rica61 o o e n1 = 4, n2 = 6 son valores fijos del ordenador. De esta forma hay valores que se pueden representar de forma exacta y otros que no. Por ejemplo, el n´mero 31,8923467, en un ordenador con n1 = 4 y n2 = 6 puede ser u representado como 0031 892346

representaci´n que se denomina corte. O bien o 0031 892347

en cuyo caso, la representaci´n se denomina redondeo. En general la reo presentaci´n de n´meros en punto fijo es muy poco util, se utiliza s´lo o u ´ o para operaciones elementales (calculadoras,..). Por ejemplo, los errores de representaci´n de n´meros como: 35420,87 ´ 0,00000056, son brutales. En o u o el primer caso, el ordenador aproximar´ por 9999 y en el segundo por 0. ıa Para c´lculos m´s ambiciosos, el ordenador debe operar en punto flotana a te. La representaci´n en punto flotante de un n´mero x es x = ax10b con o u 0,1 ≤ |a| < 1 y b entero. Por ejemplo: 342,17 (en punto fijo) 0,34217x103 (en punto flotante)

x = (±.α1 α2 ...αn )x10±bm ...b0 donde (±.α1 α2 ...αn ) se denomina mantisa y ±bm ...b0 es el exponente. La notaci´n a emplear ser´: o a ,34217E3 El n´mero de d´ u ıgitos en la mantisa se denominan d´ ıgitos significativos. El n´mero 0,0001423 = ,1423E-3 tiene cuatro d´ u ıgitos significativos. Con la o ´ condici´n α1 > 0, la representaci´n anterior es unica. o En la representaci´n num´rica en punto flotante, es necesario reservar o e un n´mero de d´ u ıgitos para la mantisa (t) y un n´mero para el exponente (e). u Si t = 4 y e = 2, el n´mero 0,00143 se puede expresar 0,00143 = ,1430E-2. u Los valores de t y e son caracter´ ısticos de cada ordenador.

62

4 · Introducci´n al lenguaje de programaci´n C (II) o o

Por ejemplo, si t = 12 y e ∈ (−500, 500) el rango de valores a representar en pantalla es ±,999999999999E±499. Existen valores que no se pueden representar de forma exacta. Para representar estos n´meros se pueden emplear dos t´cnicas de aproximaci´n: u e o corte y redondeo. Para t = 4: x = 3,87184 = 0,387184E1 f l(x) = 0,3871E1 corte (*) f l(x) = 0,3872E1 redondeo. Sea A el conjunto de n´meros m´quina (representables de forma exacta u a para un cierto valor de t). A es un conjunto finito. Para cualquier n´mero x, f l(x) ∈ A. Esta aplicaci´n debe cumplir u o |x − f l(x)| ≤ |x − y| ∀y ∈ A (4.1)

(observar que la aproximaci´n por corte (*) no satisface esta condici´n). o o o Si a = ±.α1 α2 ...αt αt+1 ... |a| = .α1 ...αt+1 ... y su representaci´n por redondeo a es: a = .α1 α2 ...αt si αt+1 ≤ 4 .α1 α2 ...αt + 10−t si αt+1 ≥ 5

o ı f l(x) = signo(x)xa x10e y esta aplicaci´n s´ satisface (4.1). Si x = 0

5 · 10−(t+1) x − f l(x) |a|10e − a 10e |a| − a = = ≤ ≤ 5 · 10−t (4.2) x |a|10e |a| |a| ≥ 10−1 Es decir ||a| − a | ≤ 5 · 10−(t+1) . Esta segunda cantidad se denomina error absoluto. No da idea de la proximidad de los n´meros. Por ejemplo: u Si x1 = 5437,43215 y x2 = 5437,43214, |x1 − x2 | = 0,00001. Si y1 = 0,00005 y y2 = 0,00004, |y1 − y2 | = 0,00001. La diferencia es la misma y s´lo en el primer caso dir´ o ıamos que x1 es una buena aproximaci´n de x2 . o

4.3 · Nociones sobre representaci´n de datos e implicaciones sobre la precisi´n num´rica63 o o e
x1 −x2 x1

= 0,183910341 · 10−8 .

−y | y1y1 2 | = 0,2 > 5 · 10−5 .

La cantidad (4.2) se denomina error relativo, que est´ acotado como a −t . vemos por 5 · 10 o Si x−f l(x) = , f l(x) = x(1 − ) con | | ≤ eps. eps se denomina precisi´n x del ordenador. Por ejemplo si escribimos 3,1415 y decimos que tiene cuatro d´ ıgitos significativos correctos, significa que su error relativo es < 5 · 10−5 .

4.3.1.

Errores de redondeo en computaci´n o

Sean por ejemplo x = 0,845E7 e y = 0,324E − 2. El resultado de sumar estos dos n´meros es u

845000 845000

0 0.00324 con t = 8. 0.00324

x ⊕ y = x y en general si |y| <

eps 10 |x|

⇒ x ⊕ y = x.

Se puede decir que en general la suma, resta, divisi´n y multiplicaci´n o o de n´meros m´quina no son operaciones asociativas y tampoco distributivas u a una con respecto a la otra. El resultado de una serie de operaciones depende del orden en que se realicen. Ejemplo 4.2 (t = 8) a = 0,023371258E − 4, b = 0,33678429E2 y c = −0,33677811E2. Veamos que (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c). 0.000023 33.678429 33.678452 -33.677811 0.000641 371258 371258 no aparece si t = 8

(= a ⊕ b)

371258 (a ⊕ b) ⊕ c = 0,641E − 3

Por otra parte: b ⊕ c = 0,000618. (b ⊕ c) ⊕ a = 0,64137E − 3.

64

4 · Introducci´n al lenguaje de programaci´n C (II) o o

Ejemplo 4.3 En sumas que involucran sumandos de distinta maginitud, el orden es importante. Si sumamos de manera exacta estos tres n´meros: u (t = 8) 0.00000007 0.00000009 Si t = 8 = 0,30000001E1 3 3.00000016 Sin embargo si los sumamos en disntinto orden: 3 0.00000007 = 0,3000000E1 0.00000009 3.0000000 Este tipo de errores es caracter´ ıstico de la suma de series.
n i=1 ai . ∞ i=1 ai

En las series convergentes el t´rmino general tiende a 0 y los t´rminos e e van de mayor a menor. El error relativo a la hora de sumar los t´rminos e peque˜os es m´ n ınimo si los t´rminos se suman de menor a mayor; de esa e forma, el valor de la suma va aumentando y se va aproximando al tama˜o n de los ultimos sumandos (los primeros t´rminos en la serie). Luego una serie ´ e convergente hay que sumarla siempre en orden regresivo.

4.3.2.

Error de cancelaci´n o

Aparece cuando restamos dos n´meros del mismo signo y muy pr´ximos: u o x − y, x ≈ y. Por ejemplo, con t = 8: 0.76545421 E1 -0.76545264 E1 0.00000157 E1 Hemos pasado de tener 8 d´ ıgitos significativos correctos a tener 3, luego hemos perdido 5 d´ ıgitos correctos.

ıan Recuerda la relaci´n de recurrencia utilizada en el Ejemplo 1.4 Para resolver: ax2 + bx + c = 0 x1 = x2 √ −b± b2 −4ac 2a Si ac << b2 ⇒ |b2 − 4ac| ≈ b2 y uno de los signos ± da un error de cancelaci´n. Repreo senta.4. sin duda. o La forma de evitarlo ser´a: ı √ b2 − 4ac 2a x1 = −signo(b) · c x2 = ax1 |b| + Con t = 5.. e ´ a o De la segunda: x1 = −111.10. Debes extremar precauu ciones cuando utilices relaciones de recurrencia que envuelvan restas.01091008.2121 = 0 De la primera forma: x1 = −111. .3 · Nociones sobre representaci´n de datos e implicaciones sobre la precisi´n num´rica65 o o e Ejemplo 4. x2 = −0. al resolver: x2 + 111. x2 = −0.010910...01000...3. un caso m´s en el que se pierde precisi´n al restar dos a o n´meros del mismo signo y magnitd comparable.09909. x2 = −0. ´sta ultima m´s pr´xima al valor real.1x + 1.10. Las ra´ ıces ser´ (con t = 9): x1 = −111.

66 4 · Introducci´n al lenguaje de programaci´n C (II) o o .

Nunca se pone punto y e coma despu´s de la llave derecha que cierra un bloque. e If-Else La proposici´n if-else sirve para tomar decisiones. Sentencias de control de flujo: for. como en o x=0.1. Formalmente la sintao xis es 67 .. while.. el punto y coma es un terminador de sentencia. printf(. Las llaves que rodean las proposiciones m´ ltiples o u despu´s de if.). o for son otro ejemplo. Ejemplos de uso Una expresi´n como x=0 o i++ o printf () se convierte en una proposio ci´n cuando va seguida de punto y coma. y no un separador.Cap´ ıtulo 5 Introducci´n al lenguaje de programaci´n o o C (III) Las sentencias o proposiciones de control de flujo de un lenguaje especifican el orden en que se realizan las computaciones. switch. if . while. i++. else. En el lenguaje C. y son sint´cticamente equivalentes a o a una proposici´n simple. 5. Con las llaves se agrupan declaraciones y sentencias en una proposici´n compuesta o bloque. do.

68 if (expresi´n) o proposici´n-1 o else proposici´n-2 o 5 · Introducci´n al lenguaje de programaci´n C (III) o o donde la parte else es opcional. se ejecuta proposici´n-2. Lo m´s elemental es escribir a if (expresi´n) o en lugar de if (expresi´n !=0) o Dado que la parte else de un if-else es opcional. a Si no fuera as´ hay que utilizar llaves. como en: ı. como se indica en el sangrado. Esto se resuelve asociando a el else al if sin el else m´s cercano. else z=b. Un if simo o plemente comprobar´ el valor num´rico de una expresi´n. hay ambig¨edad cuando u se omite un else en una secuencia de if anidados. si es cierta (es decir. por ello pueden a e o aplicarse abreviaciones. Si es falsa. o (expresi´n es cero) y existe parte else. if (n > 0){ if(a > b) z=a. Por ejemplo: if (n > 0) if(a > b) z=a. } else z=b. se ejecuta la proposici´n-1. Se eval´a la expresi´n. u o si tiene un valor distinto de cero). Else-if La construcci´n o if (expresi´n) o proposici´n o . La parte else se asocia con el if m´s interior.

que decide si un valor concreto x aparece en un arreglo u ordenado v de dimensi´n n. cuando no se sustituye ninguna de las otras condiciones. while.. do. v[]. else if(x > v[mid]) low = mid+1. mid.n) /* buscar x en v[0].v.. n. Las expresiones se a o u eval´an en orden. se ejecuta la proposici´n asociada con u o ellas y se acaba la cadena. se puede omitir el ultimo else. si alguna es cierta. o el de ´ defecto. El c´digo para una proposici´n puede ser simple o o o un bloque entre llaves. y un -1 si no est´. { int low. if(x < v[mid]) high = mid-1.1: Vamos a ver un programa que decribe una funci´n de o b´squeda binaria. mayor o igual que el elemento central o a v[mid] en cada paso. if .1 · Sentencias de control de flujo: for. Si no hay acci´n expl´ o ıcita para el defecto. else /* encontrado */ return(mid). Los elementos de v est´n ordenados en orden o a creciente. low=0.. ´ Ejemplo 5. } return(-1). } La decisi´n b´sica es si x es menor.5. switch.v[n-1]*/ int x. while( low<= high){ mid=(low+high) / 2. La funci´n devolver´ la posici´n (un n´mero entre 0 y n − 1) si o a o u x est´ en v.. Switch . Ejemplos de uso69 else if (expresi´n) o proposici´n o else if (expresi´n) o proposici´n o else proposici´n o es la forma m´s general de escribir una decisi´n m´ltiple. high. La ultima parte else maneja el caso ”ninguno de los anteriores”.. a a binary(x. high=n-1.

y se bifurca a partir de ellos. case ‘D’: return 4. } } La misma funci´n puede ser escrita con proposiciones if-else: o . int switch_example( char input_arg ) { switch (input_arg) { case ‘A’: return 1.70 5 · Introducci´n al lenguaje de programaci´n C (III) o o La proposici´n switch es una herramienta especial para decisiones m´ ltio u ples que comprueba si una expresi´n iguala uno entre varios valores conso tantes. case ‘B’: return 2. default : return -1. case ‘C’: return 3.

else return -1.5. case ERR_OPERAND: printf("Error: Operando no v´lido.\n"). Ejemplos de uso71 int switch_example( char input_arg ) { if (input_arg ==‘A’) return 1. debe ser una expresi´n int. o ´ o long o short. pero no double. a break. La proposici´n default. case ERR_TYPE: printf("Error: Dato incompatible. break. Esta ultima expresi´n debe de ser entera. if .\n"). do. a break. En la versi´n de Kernigham o y Ritchie. } La expresi´n switch debe de ir seguida de un par´ntesis que encierra otra o e expresi´n. La funci´n es declarada o * void porque no devuelve ning´n valor*/ u #include <stdio. else if(input_arg == ‘B’) return 2. puede ser char. while.1 · Sentencias de control de flujo: for. default: printf("Error: C´digo de error desconocido %d\n".\n"). } . float o long double. es opcional. o error_code"). a break. es decir.h> #define ERR_INPUT_VAL 1 #define ERR_OPERAND 2 #define ERR_OPERATOR 3 #define ERR_TYPE 4 void print_error (int error_code ) { switch (error_code) { case ERR_INPUT_VAL: printf("Error: Valor inicial no v´lido. basados en el * valor de la variable error_code. else if(input_arg == ‘D’) return 4. o o Ejemplo 5.2: /* Programma que imprime mensajes de error. switch. break. case ERR_OPERATOR: printf("Error: Operador no v´lido. else if(input_arg == ‘C’) return 3.\n").

El ciclo contin´a hasta que la expresi´n o u o vale cero. o Iteraciones while y for En while ( expresi´n ) o proposici´n o se eval´a una expresi´n. j <= val.3: La siguiente funci´n devuelve el factorial del argumento: o long int factorial ( long val) { int j. } Ejemplo 5.72 } 5 · Introducci´n al lenguaje de programaci´n C (III) o o La proposici´n break. while ( expr2 ) { proposici´n o expr3. } que escrita mediante la proposici´n while es: o . hace que se produzca una salida inmediata de la o instrucci´n switch. momento en que la ejecuci´n contin´a despu´s de la proposici´n. o u e o La sintaxis en la proposici´n for es: o for ( expr1. return fact. for (j=2. Si es cierta (si no es cero). expr3 ) proposici´n o que es equivalente a: expr1. expr2. se ejecuta la proposici´n u o o y se vuelve a evaluar la expresi´n. fact=1. j++) fact=fact*j.

La sentencia break . aunque o o deben mantenerse los puntos y coma. y el tipo del resultado es el mismo que el de u operando derecho. do. Si la comprobaci´n expr2 no est´ se o a supone que es cierta.”que casi siempre encuentra aplicaci´n o en la sentencia for. fact=1. El tercer o o tipo de iteraci´n en C. Por tanto en una proposici´n for es posible situar varias o expresiones en cada una de sus partes. Y ´ste se ejecuta al menos una vez. } return fact. M´s a com´nmente. } Gramaticalmente las tres componentes de un for son expresiones. La e sintaxis es do proposici´n o while ( expresi´n). j++.1 · Sentencias de control de flujo: for. hace la comprobaci´n al final.5. Pueden omitirse cualquiera de las tres. do-while. while. La elecci´n entre un ciclo while o for suele ser una cuesti´n de gusto. if . o Primero se ejecuta la proposici´n y luego se eval´a la expresi´n. while (j <= val) { fact=fact*j. La ejecuci´n termina ı o cuando la expresi´n resulta falsa. expr1 y expr3 son asignaciones o llamadas a funci´n y expr2 u o es una expresi´n de relaci´n. Iteraciones do-while Las iteraciones while y for comparten la util caracter´ ´ ıstica de comprobar la condici´n de terminaci´n al comienzo en lugar de hacerlo al final. switch. j=2. Una pareja de expresiones separadas por una coma se eval´a de izquierda a derecha. En caso o u o de ser cierta se ejecuta de nuevo y as´ sucesivamente. Ejemplos de uso73 long int factorial ( long val) { int j. despu´s de o o e la pasada sobre le cuerpo del ciclo. o Break A veces es necesario tener la posibilidad de salir de un ciclo por un lugar distinto al de las comprobaciones del comienzo o del final. o o Otro operador de C es la coma ”.

/* trata los positivos */ } Notar que cada vez que se ejecuta la sentencia continue... . no resulta efectiva en este caso. } .) for(. En while y do... el cuerpo del for se abandona. en la misma forma que de switch. Sin embargo en algunas (pocas) ocasiones..4: for (i=0. o Ejemplo 5. no a switch).){ . tal como la salida de dos ciclos a la vez. Formalmente goto no es necesario nunca y siempre se puede evitar. while y do. La proposici´n break... inici´ndose la ejecuci´n del mismo para un nuevo valor de a o i. ya que s´lo abandona o o el ciclo interno. Obliga a ejecutar la siguiente iteraci´n del ciclo (for. significa que la parte de comprobaci´n se ejeo cute inmediatamente. i < N.. pero se usa meo a nos.74 5 · Introducci´n al lenguaje de programaci´n C (III) o o proporciona una salida forzada de for. for(. do). en for. El uso m´s normal consiste en abandonar el proceso en alguna ´ a estructura profundamente anidada.. a Continue La proposici´n continue est´ relacionada con break.. error: arregla el desatre Ejemplos de uso .. while. que o la contiene. Saltos (goto) y etiquetas El lenguaje C contiene la infinitamente seductora sentencia goto. Una sentencia break obliga a una salida inmediata del ciclo (o switch) m´s interior. puede resultar util.. if (desastre) goto error. el control pasa a la etapa de reinicializaci´n o (continue se aplica s´lo a ciclos.. junto a las etiquetas a las que saltar. i++){ if (a[i] < 0) /*salta los elementos negativos*/ continue.

salir: if(f<4 && c<4) printf("(%d.-1.4.4.2.f++) for(c=0. for (f=0.-1.-1.c). %d)\n".f .c).2.2.1. f. f.-1.2.3.2.c).-1. if(f<4 && c<4) printf("(%d. switch.-1.f . %d)\n".f++) for(c=0.h> main() { int c. if .2. int m[4][4]={2.c *************/ #include <stdio.f<=3.0}. c<=3.h> main() { int c.-1. c<=3.c). f.-1. int m[4][4]={2. break.c ********/ #include <stdio.4.2.c********/ #include <stdio.} if(f<4 && c<4) printf("(%d. f.5.3. c<=3.-1.3. f.1.1.5.c).2.f .4.-1.f<=3.0}.2.-1. int m[4][4]={2.5.-1.4. c++) if(m[f][c]==-1) { printf("(%d.4. c++) if(m[f][c]==-1) goto salir.1.5.h> main() { int c. c++) if(m[f][c]==-1) printf("(%d. for (f=0. %d)\n".f<=3.1.1.0}. while. } /**************Fichero salir 3. Ejemplos de uso75 /******* Fichero salir.-1.1 · Sentencias de control de flujo: for. for (f=0. do. } /***********Fichero salir2.-1. .f++) for(c=0. %d)\n".-1. %d)\n".

{ . nos da su posici´n y salta de fila. .2. salir3. dentro del par´ntesis que recoge la lista e de argumentos. que es equivalente en la forma tradicional. o 3. La nueva o sintaxis conocida como forma prototipo viene a ser /* Prototipo de definici´n de una funci´n*/ o o int func_def(int a. aparecen a la vez sus declaraciones. hay dos formatos para definir una funci´n. salir2.c: lee la matriz por filas.c: lee la matriz por filas y en cuanto encuentra un −1. mientras que tradicio- .b. Los programas escritos e C normalmente constan de una gran cantidad de peque˜as funciones en lugar de pocas y grandes. a los archivos fuente pueden compilarse por separado y enlazarse junto con funciones de biblioteca compiladas con antelaci´n.76 } 5 · Introducci´n al lenguaje de programaci´n C (III) o o Objetivo de los tres programas anteriores: 1. b. nos da su posici´n y acaba. o En cuanto a sintaxis. Cuando encuentra en una fila el valor −1. int b. int c). Un programa n puede residir en uno o m´s ficheros fuente en cualquier forma conveniente.c: nos indica la posici´n de todos los −1 de la matriz.c. a /* Forma tradicional de definici´n de una funci´n*/ o o int func_def( a. salir. . Funciones: anatom´ y uso ıa Las funciones dividen grandes trabajos de computaci´n en partes m´s o a breves y aprovechan la labor realizada por otras personas en lugar de partir de cero. o 2. o 5. { . c) int a. Es decir en la nueva sintaxis.

3. Posteriormente. int a. Convenciones sobre el paso de argumentos Hemos visto en el apartado anterior. como los par´ntesis de ´sta son opcionales.5. char c. /* OK. o El nombre de la funci´n deber´ ir precedido de un tipo. float f() { float f2. Si no hay seno u tencia return. f2=a. el ıan o cuerpo de la funci´n aparece entre llaves. return puede ir seguido de una expresi´n. y el fichero fuente puede estar dividido en varios ficheros. Las funciones aparecen en cualquier orden en el fichero fuente. La sentencia return es el mecanismo para devolver un valor desde la funci´n llamada a su llamador. La comunicaci´n entre las funciones se efect´a (en este caso) a trav´s de aro u e gumentos y valores devueltos por la funci´n. Un programa es entonces un conjunto de definiciones de funciones. si la funci´n o a o devuelve un valor no entero.3 · Convenciones sobre el paso de argumentos 77 nalmente. o Tanto la expresi´n. dichas declaraciones aparec´ a continuaci´n. Una funo e e ci´n puede contener cualquier n´mero de sentencias return. Si la funci´n no devuelve ning´n valor. Sin embargo. Tambi´n se pueden realizar o e mediante variables externas. o o return (expresi´n). return a. /* f2=c. las funciones son indivisibles. A lo largo de un programa. En este caso el valor o devuelto es indefinido. cuando se alcanza la llave } que cierra el cuerpo de la funci´n. /* OK. return c. convierte a float */ 5. /* } convierte a float */ OK. el control del programa vuelve al programa llamador. convierte a float */ convierte a float */ OK. se o u especifica el tipo void. los aspectos relacionados con la definici´n de una funci´n. se van utilizando o o .

En este o o o punto el programa pasa el control a la correspondiente funci´n. El prop´sito o general de una alusi´n es decir al compilador qu´ tipo de valor devuelve la o e funci´n. con la consiguiente falta de precisi´n. Si la conversi´n no es a o o posible. T´ ıpicamente aparecen las declaraciones de las funciones en la cabecera del programa. float x). sin embargo es una buena costumbre declarar todas las funciones que van a ser llamadas. o Una llamada a una funci´n es la invocaci´n a dicha funci´n. o En las llamadas de una funci´n debe haber el mismo n´mero de arguo u mentos que en su declaraci´n. o Una alusi´n a una funci´n es una declaraci´n de una funci´n que est´ deo o o o a finida en cualquier sitio. junto a las declaraciones de las variables y antes de la llamada a la correspondiente funci´n.. En general hay dos tipos de alusiones: prototipo y no-prototipo. extern int f(void). o u o Se considera por defecto que todas las funciones devuelven un valor entero. puede haber conversiones o autom´ticas.. el compilador manda mensajes de error. o o o pero no el n´mero o tipo de argumentos que emplea: u extern old_function()..78 5 · Introducci´n al lenguaje de programaci´n C (III) o o funciones.. Para declarar una funci´n que no toma argumentos se emplea el tipo o void. generalmente en otro fichero distinto. argumento2. La sintaxis o general es nombre_funci´n ( argumento1. En cuanto al tipo.argumentok). y declarar el n´mero y tipo de argumentos que toma dicha funci´n. mediante su llamada o alusi´n. No es necesario declarar funciones que devuelven valores enteros. Las alusiones prototipo permiten a˜adir el n´mero y tipo de argumento n u empleado: extern int new_function(int j. Una alusi´n no-prototipo a una funci´n declara el tipo de retorno de la funci´n. Por ejemplo: .

short). Esto significa que la funci´n no puede o alterar el valor del argumento original en la funci´n que la llama.3 · Convenciones sobre el paso de argumentos { extern void f(int *).. la funci´n llamada recibe una copia o o temporal. no se puede convertir un float a un puntero*/ . En una o funci´n. de cada argumento.. o se pasa la direcci´n de comienzo del mismo. f(x).. Por ejemplo. privada. o o Cuando aparece el nombre de un arreglo como argumento de una funci´n.5. no se copian los elementos. double x. /* se convierte un long a float y double a short */ . es equivalente a a=1/3. float x.x). A menos que en la declaraci´n de la funci´n aparezca que el tipo de o o retorno es void. . 79 Sin embargo en: { extern void f(float.. La o funci´n puede alterar el valor de elementos del arreglo subindexando a partir o de esta posici´n. las funciones siempre devuelven un valor de retorno. que es sustitu´ por la llamada a la funci´n. la ıdo o sentencia a=f()/3. /* ilegal. Ello hace que el arreglo se pase por referencia. f(j. En el lenguaje de programaci´n C. si f () devuelve un 1. long j. cada argumento es una variable temporal inicializada con el valor o con el que se llam´ la funci´n. M´s adelante o a veremos el uso de apuntadores para alterar el valor de argumentos que no sean arreglos.

. extern (global o funci´n). Una variable puede ser limitada a un bloque. Es importante distinguir entre la declaraci´n de una variable externa y o su definici´n. static (local. o 4.).4. de ambito local). La clase de almacenamiento puede alterarse con los calificadores: 1. de ´mbito local). a un fichero. etc. auto (almacenamiento autom´tico. y puede enlazarse. global o fichero. tama˜o. funci´n y estructura. Clases de almacenamiento Hemos comentado ya alg´ n aspecto sobre el ´mbito definici´n y validez u a o de las variables. La declaraci´n extern es obligatoria si se ha de hacer referencia a una variable externa o antes de su definici´n o si est´ definida en un archivo fuente que no es aquel o a en el que se usa.80 5 · Introducci´n al lenguaje de programaci´n C (III) o o 5. register (almacenamiento en un registro. Una declaraci´n da a conocer las propiedades de una variable o o (su tipo. El ambito de validez de una variable externa abarca desde el punto de ´ su declaraci´n en un archivo fuente hasta el fin del archivo. o o Variables declaradas a nivel externo (fuera de toda definici´n de funci´n) o Las funciones y variables externas que constituyen un programa en C no tienen que ser compiladas todas a la vez. a a 2. double val[MAXVAL]. Si las l´ ıneas: int sp. o Se pueden utilizar los calificadores static o extern o ninguno. El texto fuente del programa puede mantenerse en varios archivos. global o funci´n). o Por defecto todas las variables llevan asociada una clase de almacenamiento que determina su accesibilidad y existencia. una definici´n produce tambien una asignaci´n de n o o memoria. ´ 3. o o o Hay cuatro tipos de ambito de validez de las variables en C: local o ´ bloque. a una funci´n o a una declaraci´n de una funci´n prototipo.

declaran para el resto del archivo fuente que sp es un entero y val un arreglo double (cuyo tama˜o se fija en otro lugar). hace accesible la variable a m´dulos a los cuales o no lo es. Las o l´ ıneas: extern int sp. Una decla´ raci´n register avisa al compilador de que la variable en cuesti´n o o ser´ muy usada. extern double val[]. extern a nivel interno. static: las variables est´ticas constituyen la tercera clase de almacenaa miento adem´s de las extern y las autom´ticas. lo que producir´ programas cortos y a a r´pidos. 3. o register es la cuarta y ultima clase de almacenamiento. o obligan a asignar memoria y sirven de declaraci´n al resto del archivo. S´lo debe de haber un definici´n de una variable externa o o entre todos los archivos que constituyen el programa fuente. pero a diferencia de ´stas. Este tipo de almacenamiento es v´lido para los tipos int char a a y punteros. o a un valor especificado. Las variables internas est´ticas son locales a una funci´n en la misma forma que las a o autom´ticas. Si es posible. a e en lugar de aparecer y desaparecer al activar la funci´n.5. El almacenamiento est´tico. La declaraci´n es del tipo: o 2. Variables declaradas a nivel interno (dentro de un bloque) Pueden ser: 1.4 · Clases de almacenamiento 81 aparecen fuera de cualquier funci´n def inen las variables externas sp y val. Los otros archivos deber´n contener declaraciones extern para acceder a ´l. Se inicialio zan por defecto a 0. La declaraci´n a e o de una variable externa inicializa la variable a 0 por defecto. pero no crean las variables ni les n asignan memoria. El califia a cativo static se puede utilizar a nivel externo o interno. interno o externo. . su existencia es permanente. ya tratadas. s´lo se puede acceder a ella o desde el propio fichero fuente. Si se utiliza el calificador static. Hay o que inicializarlas expl´ ıcitamente. no declarada o auto: en este caso s´lo es visible dentro del bloque. la variable register ser´ almacenada a a en los registros de la m´quina. se a especifica al prefijar la declaraci´n normal con la palabra static. 4. La o variable es externa si se define fuera de las funciones e interna si se define dentro de una funci´n.

. { register int i. register char c. La declaraci´n ser´: a o o a f(c. var2).. printf("%d %d %d %d\n". printf("%d %d \n". register int var3=0. } S´lo unas pocas variables en cada funci´n se pueden mantener en reo o gistros. var1+=2. Una variable register ha de ser inicializada expl´ ıcitamente. int var4=0. } ¿Qu´ valores se imprimen en el programa anterior? e Soluci´n: 7 0 0 0 o 15 10 . var2.n) register int c. var1.n. . register s´lo se aplica a variables autom´tio a cas y a los par´metros formales de una funci´n. Ejemplo de uso main() { extern int var1.82 5 · Introducci´n al lenguaje de programaci´n C (III) o o register int x. funcion_1(). funcion_1() { int var1=15. static var2=5. var1. var4). var3. static int var2. La parte int puede omitirse. } int var1=5. La declaraci´n se ignora si hay declaraciones excesivas o no o permitidas. var2+=5.

Recursi´n o Una funci´n recursiva es una funci´n que se llama a s´ misma. } --------------------------/* En otro fichero*/ extern int var. /*escribe 7*/ funcion_2(). var). var2 entrar´ valiendo 10). o extern int var. n=-n. funcion_2() { var++. } . var). printf("%d \n". /* escribe 6*/ funcion_1(). var). Por ejemo o ı plo: printd(int n) /* imprime n en decimal (recursiva) */ { int i. if ( n < 0 ){ putchar (‘-’). funcion_1() { var++. } if(( i=n/10)!= 0) printd(i). putchar(n % 10+‘0’). /* escribe 8*/ } 5.5 · Recursi´n o 83 ıa (Si se llamase otra vez a la funci´n funcion 1.5. } int var=5. printf("%d \n". printf("%d \n". main() { var++.5.

b. Est´ especialmente pensado para estructuras a definidas recursivamente. Funciones definidas en stdio y math En stdio..c).). Escribe los datos formateados en la cadena de salida standard (stdout). Ejemplo de uso #include <stdio.argumento 1. Pasa 12 a la segunda printd.]. La recursividad generalmente no ahorra memoria pues ha de mantenerse una pila con los valores que est´n siendo procesados.h se encuentran las funciones I/O.c=1991. El primer argumento es un vector car´cter que puede cona tener texto y expresiones de control de formato.a. ´ a Nota: Trata de escribir la funci´n anterior.. a=20.. que veremos m´s adelante. como los arboles.. que es independiena te de la anterior.84 5 · Introducci´n al lenguaje de programaci´n C (III) o o Cuando una funci´n se llama a s´ misma recursivamente..c.h> int printf (const char *formato[.b=350. La tercera toma n igual a 1. Salida con formato.b. La segunda printd pasa 1 a la tercera. Algunas de ellas son: printf() #include <stdio. Dicho fichero contiene las declaraciones prototipo de todas las funciones I/O. la primera vez n=123.. } . cada invocaci´n o ı o crea una nueva copia de todas las variables autom´ticas. o 5. Por tanto. printf ("a=%6d\t b=%6d\t c=%6d\n". printf ("\n Los resultados son:\n").6. Los siguientes argumentos representan los datos a ser escritos. Tampoco ser´ m´s a a a r´pida. En la vuelta hacia arriba la segunda printd escribe un 2 y la primera escribe un 3. hace i = 0 y escribe 1.h> main() { int a. en printd(). pero el c´digo recursivo es m´s compacto y a menudo m´s sencia o a a llo de escribir y comprender. sin emplear su recursividad.

La funci´n getchar() lee del fichero est´ndar de o a pantalla (stdin) un car´cter y avanza una posici´n. } Resultado: Lee de la pantalla. &c). scanf ("%d %f %c".).6 · Funciones definidas en stdio y math Resultado: Los resultados son: a= 20 b = 350 c = 1991 85 scanf() #include <stdio. Entrada de caracteres. Entrada con formato. b. Lee datos de stdin en la forma especificada por un vector de formato.h> int getchar (void). por ejemplo: 5 23. a Ejemplo de uso #include <stdio. La sintaxis y sem´ntica de scanf() es la inversa de la a de printf()..&b. donde el argumento representa un puntero a la variable que se quiere leer. &a..h> int scanf (const char *formato[.h> main() { int a.. char c.float b.].argumento 1. Vimos esta funci´n en el primer cap´ o ıtulo dedicado a C. c.5.. Ejemplo de uso #include <stdio.4 b y lo almacena en las variables correspondientes: a. Cuando llega al final del a o fichero lee el car´cter EOF. getchar() #include <stdio.h> main() { .

Manipulando ficheros a Permiten escribir y leer datos a. car=getchar().86 int car. es a o equivalente a putc(c.h> main() { int car=4. Dependiendo de los datos que se deseen leer a o escribir se utilizan las siguientes funciones: 1. Salida de caracteres. Datos le´ ıdos o escritos car´cter a car´cter: a a fgetc(). Ejemplo de uso #include <stdio. Datos le´ ıdos o escritos palabra a palabra (palabra m´quina=valor a int(normalmente 2 bytes): . Funciones est´ndar de entrada/salida. } Resultado: Escribe en pantalla el valor 4. a putchar() #include <stdio. en el procesamiento de ficheros. La E/S. stdout). fputc() 2. putchar(car). Esta t´cnica. Escribe su argumento en la cadena de salida standard (stdout) y devuelve el car´cter escrito. } 5 · Introducci´n al lenguaje de programaci´n C (III) o o Resultado: Lee un car´cter y lo almacena en la variable car. La expresi´n putchar (c). implementada en software.h> int putchar (int c). se realiza a trav´s de un buffer o memoria e intermedia. hace las operaciones de e entrada y salida m´s eficientes. y desde ficheros y dispositivos.

fprintf() 5. Datos le´ ıdos o escritos como cadena de caracteres: fgets(). fputs() 4.5. 87 Datos de longitud fija (estructuras o arrays) le´ ıdos como registros o bloques: fread(). Datos le´ ıdos o escritos con formato: fscanf(). fputw() 3.6 · Funciones definidas en stdio y math fgetw(). fwrite() .

Abre el fichero identificado por nombrefichero y asocia una cadena con dicho fichero. si termina satisfactoriamente y un elemento nocero (EOF) si sucede alg´ n error. const char *modo de acceso).). Dicha funci´n deo vuelve un cero.arg].). Escribe sus argumentos (arg) en el fichero apuntado por pf. Cada formato debe ser un puntero a una variable en la que queremos almacenar el valor le´ ıdo.88 5 · Introducci´n al lenguaje de programaci´n C (III) o o Abrir y cerrar ficheros fopen() #include <stdio. El segundo argumento es un puntero a una cadena de caracteres que identifica el tipo de acceso al fichero. Cierra el fichero asociado con la cadena especificada..h> int fscanf (FILE *pf.h> int fprintf (FILE *pf. o fscanf() #include <stdio. const char *formato[.. con el formato especificado. La descripci´n del formato es la misma que en printf. u Salidas y entradas con formato fprintf() #include <stdio.arg]. const char *formato[. fclose() #include <stdio. con el formato especificado..h> int fclose (FILE *cadena).h> FILE *fopen (const char *nombrefichero. Ejemplo de escritura en un fichero de nombre resul .. Lee sus argumentos (arg) del fichero apuntado por pf.

} } fprintf (presul. } } /* quitar todos los multiplos */ Nota: Extrae el algoritmo del programa anterior.5. cuenta = 0. k<= SIZE. k+= primo) flags[k]=0. i++) /* contador */ flags[i]=1. primo). presul = fopen ("resul". Funciones definidas en math. i<=SIZE. "Hay %d numeros primos\n ". cuenta). int i. cuenta. cuenta++. i++) { if(flags[i]) { /*primo encontrado */ primo=i+i+3. Todas ellas operan con valores en double.h> main() { FILE *presul.c.6 · Funciones definidas en stdio y math /* Este fichero realiza el mismo calculo que primos. fprintf (presul. for (k=i+primo. pero escribe*/ /* el resultado en un fichero*/ /* Calculo de numeros primos mediante la criba de Eratostenes*/ 89 #include <stdio. divididas en tres grupos: trigonom´tricas e hiperb´licas. /* inicio de la tabla a 1 */ for(i=0. for(i=0.h a En < math. a . k. if(presul != NULL) { #define SIZE 1000 char flags[SIZE+1]. primo. "%d\n" . i<=SIZE.h > se encuentran las funciones matem´ticas. exponenciales y logar´ e o ıtmicas y miscel´neas. "w").

sinh(). sin(). tanh(). e cos(). sqrt() entre otras. Dentro de las exponenciales y logar´ ıtmicas: exp(). se encuentran: acos(). tan(). log10().90 5 · Introducci´n al lenguaje de programaci´n C (III) o o Dentro de las trigonom´tricas. son quiz´s las m´s ´ a a a utilizadas. cosh(). log(). Por ultimo dentro de las miscel´neas: fabs(). fmod(). atan(). . asin().

todos ellos presuponen o el conocimiento de una buena aproximaci´n de la ra´ Se sabe muy poco.. Otros ız m´todos requieren de una aproximaci´n inicial que est´ cerca de la ra´ e o e ız deseada. nos veremos obligados a utilizar m´todos aproximados. p.. si queremos resolver ecuaciones no lineales. Al final del cap´ ıtulo. Por ello. a a Por simplicidad. comienzan en una aproximaci´n inicial a la e o ra´ y producen una sucesi´n x0 ..Cap´ ıtulo 6 Algoritmos iterativos 6. Una forma de obtener un aproximaci´n inicial a la ra´ de una ecuaci´n o ız o 91 . Estos m´toe e dos se basan en ideas como la aproximaci´n sucesiva o la linearizaci´n.. ız En determinados m´todos. esto es. xn . Veremos que aunque o los m´todos utilizados para la resoluci´n de ecuaciones no lineales son genee o ralizables a la resoluci´n de sistemas de ecuaciones. o ız.. b] en el que se encuentra la ra´ buscada. ello hace que converjan m´s r´pidamente..1. de la ecuaci´n f (x) = 0. x1 . de c´mo resolver el problema si no se dispone de informaci´n a priori de la o o localizaci´n de las ra´ o ıces. Tales o o m´todos son iterativos. que presumiblemente converge ız o a la ra´ deseada. describiremos someramente algunos procedimientos para la resoluci´n de sistemas de ecuaciones no lineales. nos dedicaremos a estudiar el problema de determinar una ra´ real simple. es suficiente (en t´rminos de convergencia) e e conocer un intervalo [a. Introducci´n o Las ra´ ıces de la ecuaci´n no lineal f (x) = 0 no pueden obtenerse geo neralmente de forma exacta.. es decir. supondremos que ız o p ∈ R y f (p) = 0.

Otra posibilidad es dar valores a la funci´n f (x) y observar los cambios o de signo de los resultados obtenidos. 6 · Algoritmos iterativos Figura 6.92 x2 4 . se pueden cono a siderar las abscisas de los puntos de intersecci´n de la gr´fica de la funci´n o a o y = f (x) con el eje X.1: y = senx − x ∈ [ π .9 -0.1 Obtener una aproximaci´n de la ra´z real positiva que tiene o ı la ecuaci´n: o x2 − senx = 0 4 Expresando la ecuaci´n de forma equivalente: x = senx. Se representan entonces las gr´ficas de las a a funciones y = ϕ(x) e y = ψ(x).1 f (x) = 0.8 1. y las ra´ ıces buscadas ser´n entonces las a abscisas de los puntos de intersecci´n de dichas gr´ficas. ϕ(x) = ψ(x). o a Ejemplo 6.6 1. donde las funciones ϕ y ψ son m´s sencillas que f . Si se repreo 4 2 ız sentan gr´ficamente las curvas y = x e y = senx. se observa que una ra´ a 4 π queda en el intervalo ( 2 . es mediante su representaci´n gr´fica. 2] 2 0. 2 . posiblemente cerca de x = 1.7 1.1 1.2 0. Para ello. A veces es aconsejable sustituir la ecuaci´n dada por otra ecuaci´n equio o valente (es decir con las mismas ra´ ıces).9.3 0. 2).

se denoe mina m´todo de bisecci´n. tales que todos ellos contendr´n en su interior una ra´ de la ecuae ci´n f (x) = 0. para una funci´n f dada.. existe al ı menos un n´mero p ∈ (a. b). son determinados de manera recursiva de la siguiente forma.0). la ecuaci´n f (x) = 0 tiene al menos una ra´z en el intervalo (a.64 0. tal que u f (c) = k.8 2.. b1 ) ⊃ (a2 . o o Corolario Si una funci´n f (x) ∈ C[a. 6. Determinaremos entonces. tal que f (p) = 0.. 2. 2.974 0.2 · Resoluci´n de ecuaciones o x 1. una secuencia de intervalos (a1 . Los intervalos Ik = (ak .. b2 ) ⊃ a ız (a3 . u La ra´ p ser´ unica si f (x) existe y mantiene el signo en el intervalo ız a ´ (a. e o u e 6.0 senx(calculado en radianes) 0.8.. b] y f (a) · f (b) < 0. El punto medio del intervalo Ik−1 es: 1 mk = (ak−1 + bk−1 ) 2 . .6 1. o o El primer m´todo basado en el Teorema del Valor Intermedio.9996 0. b). bk ). entonces existe c ∈ (a. El problema consiste en encontrar los valores de la o variable x que satisfacen la ecuaci´n f (x) = 0.2.1. b] y k es o un n´mero cualquiera entre f (a) y f (b). un resultado existente en este sentido es el siguiente: Teorema del Valor Intermedio Si una funci´n f (x) ∈ C[a. M´todo de bisecci´n e o Sea f (x) una funci´n continua en el intervalo (a0 . b). k = 1.6. b0 ) tal que f (a0 )f (b0 ) < o 0. Resoluci´n de ecuaciones o A lo largo de esta secci´n mostraremos distintos m´todos de resoluci´n o e o de la ecuaci´n f (x) = 0.2. b). En general.909 De esta forma se puede concluir que p ∈ (1. b3 ). Es decir. Supongamos en particular que f (a0 ) < 0 y f (b0 ) > 0 (´sto o no supone ninguna limitaci´n puesto que en otro caso se podr´ considerar o ıa −f (x) = 0). de b´squeda binaria o m´todo de Bolzano.0 x2 4 93 f (x) ¡0 ¡0 ¿0 0.81 1.

y adem´s cada intervalo Ik contiene una ra´ de la ecuaci´n f (x) = 0. y a p = mn+1 ± dn . si f (mk ) > 0 De la construcci´n de (ak . = n (b0 − a0 ) 2 2 2 ız Tenemos adem´s que mn+1 es un estimador de la ra´ que buscamos.. puesto que en otro caso. dn = 1 2n+1 (b0 − a0 ) . bk ). si f (mk ) < 0 (ak . bk ) se sigue inmediatamente que f (ak ) < 0 o a ız o y f (bk ) > 0. tendr´ determinada la ra´ de la ecuaci´n. Despu´s de n pasos.94 6 · Algoritmos iterativos ıamos Podemos suponer que f (mk ) = 0. tenemos la ra´ dentro del intervalo (an . Computamos f (mk ) y tenemos: ız o (ak . bk ) = (mk . mk ).. bn ) de lone ız gitud: bn − an = 1 1 1 (bn−1 − an−1 ) = 2 (bn−2 − an−2 ) = .

. es decir f ∈ C 2 [a. b].90625 f (mk ) ¡0 ¡0 ¿0 ¡0 =0 6.2 · Resoluci´n de ecuaciones o Algoritmo 95 Tolerancia = T OL. Sea x0 una aproximaci´n de la ra´ p. b].5. Paso 6: SALIDA.2. A partir o o de una aproximaci´n inicial x0 . bi = mi . STOP. hacer: Paso 3: mi = ai−1 + 1 (bi−1 − ai−1 ) 2 ´ Paso 4: Si f (mi ) = 0.2 El m´todo de bisecci´n aplicado a la ecuaci´n e o o con I0 = (1.. ai = mi . La ra´ ız Paso 5: Si f (mi ) < 0. e ir al Paso 3. 2 .2. se computa una secuencia x1 ..90625 bk−1 2 2 2 1. e x2 4 −senx Ejemplo 6. .9375 1. Paso 1: Sea i = 1 Paso 2: Mientras que i ≤ N U M IT E. xn . b0 .9375 1. Hacer i = i + 1. M´todo de Newton-Raphson e La idea b´sica del m´todo de Newton Raphson para la resoluci´n de una a e o ecuaci´n f (x) = 0. .6. Valores u iniciales a0 .875 1. o o donde xn+1 se determina como explicamos a continuaci´n: Sea f (x) una funci´n continuamente diferenciable dos veces en el intero o ız valo [a. x2 . Si consideramos el desarrollo de Taylor hasta grado dos.. ha sido descrita en la introducci´n del tema.9375 mk 1. N´mero de iteraciones= N U M IT E.75 1.. genera la secuencia de intervalos: k 1 2 3 4 5 ak−1 1.5 1. bi−1 −ai−1 2 < T OL.875 1.875 1. 2). tal que n f (x0 ) = 0 y |x0 − p| peque˜o. ai = ai−1 . alrededor de x0 : f (x) = f (x0 ) + (x − x0 )f (x0 ) + (x − x0 )2 f (ψ(x)).75 1. o buscada es mi . Si f (mi ) > 0. Despu´s de NUMITE iteraciones el proceso e ha terminado sin ´xito. bi = bi−1 .

4) Algoritmo Tolerancia = T OL. Queremos 4 2 determinar una ra´z positiva con cinco decimales correctos. Paso 6: SALIDA. e ir al Paso 3. 2 . N´mero de iteraciones= N U M IT E. Despu´s de NUMITE iteraciones el proceso e ha terminado sin ´xito.e.5. Paso 2: Mientras que i ≤ N U M IT E. Valor u inicial x0 . la funci´n f (x) es aproximada por su tangente en el punto o o (xn . Dado el Ejemplo 2. x0 = 1. e Ejemplo 6. 2 (6. ız Paso 5: Hacer x0 = xi .1) El m´todo de Newton Rapson se deriva suponiendo que el t´rmino cuae e dr´tico de la anterior expresi´n es pr´cticamente nulo y que: a o a 0 ≈ f (x0 ) + (x − x0 )f (x0 ). f (xn )).3 Dada f (x) = senx − x . y xn+1 es la abscisa del punto de intersecci´n de la tangente con el eje X. (6.96 donde ψ(x) est´ entre x y x0 . Luego para determinar xn+1 tenemos que resolver la siguiente ecuaci´n: o f (xn ) + (xn+1 − xn )f (xn ) = 0 (6.5 · ı 10−5 . La ra´ buscada es xi . tomamos. i = i + 1. hacer: Paso 3: xi = x0 − f (x0 ) f (x0 ) Paso 4: Si |xi − x0 | < T OL. hn = −f (xn ) f (xn ) (6.3) El m´todo de Newton-Raphson es definido por la siguiente f´rmula itee o rativa: xn+1 = xn + hn .2) Es decir. STOP. Paso 1: Sea i = 1. f (x) = cosx − x . i. Como f (p) = 0: a 0 = f (x0 ) + (p − x0 )f (x0 ) + 6 · Algoritmos iterativos (p − x0 )2 f (ψ(p)). T OL = 0.

cuando uno est´ lejos a de la ra´ se puede evitar trabajar con tantos decimales.67926 -1.00018 -1.2. ıa c´mo xn se aproxima a la ra´ se podr´ utilizar f (x2 ) en las iteraciones o i = 3 o 4.18838 -0. x3 . Esto genera el siguiente m´todo.14039 1. e o x0 y x1 . e pero s´lo se requiere de una evaluaci´n de la funci´n en cada paso.5 · 10−5 97 La ra´ buscada es entonces p = 1. Como la precisi´n de f (xn ) es la que nos dice o ız.303197 -0.93375 f (xi ) 0.5 2. Vemos tambi´n que |hi | decrece m´s y m´s r´pidamente hasta que los e errores de redondeo empiezan a dominar.60948 -1.34805 -0. Entonces no es necesario calcular f (xn ) con o o tanta precisi´n como f (xn ). f (xn−1 )) o e n e y (xn .32191 h(xi ) 0.024372 -0. hn = −f (xn ) · xn − xn−1 .00018 < 0.93375. es unicamente necesario trabajar con f (xn ) con tantos decimales como ´ deber´ ser correctos en xn+1 . rior.6.. Sin embargo. e e pero tiene una dificultad grande: la necesidad de conocer el valor de la derivada de f en cada aproximaci´n de la ra´ Habitualmente la expresi´n o ız. la derivada ha sido calculada ıan con una innecesaria precisi´n. ´ 6. S´lo han sido necesarias 4 iteız o raciones. Calcular. o ıa a a e de f (x) es todav´ m´s complicada y necesita m´s operaciones aritm´ticas para su c´lculo que f (x).000005 f (xi ) -0. a partir de dos aproximaciones iniciales. de manera que se cumple: f (x0 ) · ız .93393 1.434995 -0.000233 0. f (xn )) y el eje de las X. En el ejemplo anteız. En este caso. son los extremos del intervalo de referencia en el que se encuentra la ra´ buscada. a El m´todo de la secante trata de evitar este problema. f (xn ) = f (xn−1 ) f (xn ) − f (xn−1 ) La interpretaci´n geom´trica de este m´todo es que xn+1 se calcula como o e e la abscisa del punto de intersecci´n entre la secante a trav´s de (xn−1 .64039 -0. aproximando el e n )−f (xn−1 valor de la derivada f (xn ) por el cociente incremental f (xxn −xn−1 ) . .. a partir de la f´rmula iterativa: xn+1 = xn + hn .3.2 · Resoluci´n de ecuaciones o i 0 1 2 3 4 xi 1.. Se˜alar que el m´todo de la secante a diferencia del m´todo de Newton Raphson requiere de dos aproximaciones iniciales.95201 1. o o o Los valores iniciales x0 y x1 .01826 0. M´todo de la secante e El m´todo de Newton Raphson es una t´cnica extremadamente poderosa. la secuencia x2 . incluso teniendo en cuenta que la aproximaci´n inicial es bastante o a a a mala.

Despu´s de NUMITE iteraciones el proceso e ha terminado sin ´xito. N´mero de iteraciones= N U M IT E.000005 h(xi ) 2 -0. e Ejemplo 6.59147 0.000114 -0. y = 0. T OL = ı 0. o Algoritmo Tolerancia = T OL. proporciona la misma precisi´n con el mismo n´mero de o u iteraciones en el m´todo de Newton-Raphson que en el de la secante. Paso 1: Sea i = 2.5 · 10−5 . f (2) = −0. de donde se deduce la o f´rmula iterativa anterior. La ra´ buscada es xi . Valores u iniciales x0 y x1 . 4 i 0 1 2 3 4 5 6 xi 1 2 1. STOP.00009 Este ejemplo. f (xn )) es: y − f (xn−1 ) x − xn−1 = xn − xn−1 f (xn ) − f (xn−1 ) En el punto de intersecci´n con el eje X. En caso de suceder lo contrario resolveremos la ecuaci´n −f (x) = 0. Paso 6: SALIDA.084980 -0. f (xn−1 )) y por o (xn .93384 1. ız Paso 5: Hacer i = i + 1.06431 0.4 Tomamos de nuevo la ecuaci´n f (x) = senx − x .5915 > 0. Esto e . x1 = 2. e ir al Paso 3. hacer: (xi−1 −xi−2 ) Paso 3: xi = xi−1 − f (xi−1 ) (f (xi−1 )−f (xi−2 )) Paso 4: Si |xi − xi−1 | < T OL.93375 g(xi ) -0.13296 0.98 6 · Algoritmos iterativos e o f (x1 ) < 0. o Nota: La ecuaci´n de la secante que pasa por (xn−1 . por simplicidad que f (x0 ) < 0 y f (x1 ) > 0. Por lo tanto trabajaremos con g(x) = −f (x) = x − senx.93135 1. i. f (1) = 0. x0 = 1. Tomamos. Quereo 4 mos determinar una ra´z positiva con cinco decimales correctos.86704 1.0907 < 2 0.00249 -0.090703 -0.93375 1. Paso 2: Mientras que i ≤ N U M IT E.e.003177 0. Como en el m´todo de bisecci´n consideraremos.

al igual que el de bisecci´n. que la ecuaci´n de recurrencia anterior puede ser n o expresada de la forma: xn+1 = xn−1 f (xn ) − xn f (xn−1) . se puede considerar tambi´n como una variante del e m´todo de la secante. la secante entre (xn . Si por ejemplo. muy utiles a la hora de determinar los ceros de una o ´ funci´n real f (x) cualquiera. como e el m´todo de bisecci´n. f (xn ) = f (xn−1) f (xn ) − f (xn−1) de manera que se puede obtener una cancelaci´n cuando xn ≈ xn−1 y o f (xn )f (xn−1 ) > 0. a a a En este sentido. x1 muy cerca de p. La ventaja del m´todo de regula falsi. )−f (xn−1 minado en general con una pobre precisi´n relativa. o 6. y e o adem´s convergen m´s r´pidamente. Regula Falsi El m´todo de regula falsi o de la falsa posici´n entra dentro de los m´toe o e dos de interpolaci´n. de e manera que f (x0 ) · f (x1 ) < 0. f (xn )) y (xn . sin embargo. o e en los m´todos de interpolaci´n no se necesita calcular la derivada de f . elegimos o las aproximaciones iniciales x0 . En contraste con el m´todo de la secante. deben ser elegidas tambi´n.2.4. e Las aproximaciones iniciales x0 y x1 .2 · Resoluci´n de ecuaciones o 99 puede ser debido a que una de las aproximaciones x1 . una pobre precisi´n en el otro factor. es que es siempre convergente para funciones continuas o f (x). donde n es el mayor ´ para el cual f (xn )f (xn ) < 0. se elige en cada iteraci´n e e o ındice. f (xn )). o Se˜alar. Una an´lisis m´s minucioso muestra que la contribuci´n dominante al a a o a error relativo de hn proviene del error cometido en el c´lculo de f (xn ). pero e o a necesitan una buena primera aproximaci´n. sin embargo. A diferencia del m´todo de Newton-Raphson. resulta ser de menor importancia. En el m´todo de regula falsi. el cociente f (xnn −xn−1 ) ) ser´ deterız. el m´todo e e . El m´todo de Newton-Raphson o el de la secante se utilizan frecuentee mente para refinar las respuestas obtenidas mediante otras t´cnicas. El an´lisis de errores que haremos a posteriormente nos dir´ que el m´todo de la secante proporciona en general a e una secuencia tal que |xn − xn−1 | >> |xn − p|.6. El motivo es que dan convergencia r´pida. los errores de redondeo pueden hacer que |xn − p| sea grande. n < n. estaba muy cerca de (x n a la ra´ Cuando |xn − xn−1 | es peque˜o.

aunque conceptualmente claro. bn+1 = bn . hace que f (an ) · f (bn ) = 0. Adem´s.3. se define: an+1 = xn . x1 ].3 y 4 anteriores. de forma que f (an ) · f (bn ) < 0. si f (xn ) · f (an ) < 0 El algoritmo termina cuando f (xn ) = 0. El m´todo de bisecci´n.2. a menos que f (xn ) = 0. luego xn est´ bien definido. Converge muy lentamente. tiene inconvee o nientes importantes. y los valores an se determinan de manera que convergen hacia uno de estos ceros. Esto lo e probaremos m´s adelante. El o intervalo [an . satisface an < xn < bn ´ bn < xn < an . b0 ] = [x0 .3.100 6 · Algoritmos iterativos de regula falsi es un m´todo de primer orden (convergencia lineal). Comenta los o resultados que obtienes.1. Ejercicio 1 Intenta especificar el algoritmo para el m´todo de regula falsi. parte de un m´todo h´ e ıbrido que tendr´ buenas propiedades cerca de la ra´ ıa ız. y probarlo e con la ecuaci´n dada en los Ejemplos 1.5) (6. bn ]. 6.6) El hecho de que f (an ) · f (bn ) < 0. En este a a o caso. En general se sigue la siguiente f´rmula iterativa: o xn = an − f (an ) = bn − an = f (bn ) − f (an ) bn f (an ) − an f (bn ) f (an ) − f (bn ) (6. se determina en cada iteraci´n un intervalo [an . bn ] contiene entonces al menos un cero de f (x). a El m´todo de regula falsi es considerado un buen m´todo siempre que no e e se utilice en un entorno muy pr´ximo de la ra´ Puede ser utilizado como o ız. (es decir NUMITE puede . Comparaci´n de los distintos ordenes de cono ´ vergencia Convergencia del m´todo de bisecci´n e o 6. bn+1 = xn . El planteamiento es similar al del m´todo de bisecci´n donde a partir e o de un intervalo inicial [a0 . (f (x0 ) · f (x1 ) < 0). si f (xn ) · f (an ) > 0 an+1 = an .

Teorema 2 Sea f ∈ C[a. b] = [1. tenemos o bn − an = 1 2n−1 (b − a).• 2 2 2 De acuerdo con la definici´n de rapidez de convergencia. si |αn − α| ≤ K. se sigue que 2 1 1 1 |xn − p| = | (an + bn ) − p| ≤ | (an + bn ) − an | = (bn − an ) = 2−n (b − a). n ≥ 1. o Se trata de encontrar n tal que: |xn − p| = 10−5 ≤ 1 (b − a) = 2−n 2n (6. o Definici´n 1 Diremos que la sucesi´n {αn }∞ converge a α con rapidez o o n=1 de convergencia O(βn ).6. tal que f (a) · f (b) < 0. para n suficientemente grande |βn | donde K es una constante independiente de n. Esto se indica escribiendo αn = α + O(βn ). y es que se trata de un m´todo que converge siempre a una e e soluci´n. Sin embargo. El procedimiento de bisecci´n genera una sucesi´n xn que aproxima a p con la propiedad o o |xn − p| ≤ 1 (b − a).8) . e ´ que en la pr´ctica pueden ser muy inferiores. la desigualdad o a o (6. Es importante hacer converge a 0 con una rapidez de convergencia O(2 notar que teoremas como ´ste. 2n (6. para todo n.7) implica que {xn }∞ converge a p y est´ acotada por una sucesi´n que n=1 −n ). donde {βn }∞ es otra sucesi´n con βn = 0 para cada o n=1 n.7) Demostraci´n: para cada n ≥ 1. tomando como intervalo inicial [a. unicamente acotan superiormente los errores.5 Determinar aproximadamente el n´mero de iteraciones u necesarias para resolver la ecuaci´n f (x) = x3 + 4x2 − 10 = 0 con una o precisi´n de 10−5 . una buena aproximaci´n intermedia puede ser desechada sin que nos u o demos cuenta. m´s a´n. b) Ya que xn = 1 (an + bn ). 2]. o bien αn → α con una rapidez O(βn ). b]. no debemos olvidar una propiedad importante del m´todo. a Ejemplo 6.3 · Comparaci´n de los distintos ´rdenes de convergencia o o 101 n a ser muy grande antes de que |xn − p| sea suficientemente peque˜o) y. y p ∈ (a.

o o Por esta raz´n. el m´todo se denomina lineal o de primer orden. x0 = a ´ x0 = b. e a Teorema 3 Sea f ∈ C 2 [a. por ejemplo. que caiga en el ı o o intervalo [a. b] como valor inicial x0 al utilizar el m´todo de Newton para hallar una ra´z de la ecuaci´n f (x) = 0. entonces. Convergencia del m´todo de Newton-Raphson e Definici´n 2 Sea {xn }∞ una sucesi´n que converge a p y en = xn − p. o que la convergencia es de orden α. Si f (x) existe en cualquier punto y conserva el signo. o o n=0 para cada n ≥ 0. o o Otra caracter´ ıstica a se˜alar de este m´todo es que su velocidad de conn e vergencia es completamente independiente de la ecuaci´n a resolver. b]. se debe aplicar o e ıjase el extremo del intervalo la siguiente regla: para el punto inicial x0 el´ (a. con una constante n=0 de error asint´tico λ. Si f (a) · f (b) < 0.3. e o ı ´ de la ecuaci´n f (x) = 0 con cualquier grado de precisi´n. a tenemos que log10 (10−5 ) = −5 ≤ log10 (2−n ) 5 −nlog10 2 ≥ −5 ⇒ n ≤ ≈ 16. es posible o utilizando el m´todo de Newton (f´rmula (6. o Si α = 1. Si α = 2. a partir de una aproximaci´n inicial x0 ∈ [a. o 6. f (a) · f (b) < 0. Si existen dos n´meros positivos λ y α tales que u limn→∞ |xn+1 − p| |en+1 | = limn→∞ =λ α |xn − p| |en |α entonces se dice que {xn }∞ converge a p con orden α. ∞). f (x) = 0 para a ≤ x ≤ b. pues la tolerancia est´ dada en base 10). b].2. .6 log10 2 Seg´n la expresi´n anterior se requieren como mucho 16 iteraciones para u o obtener una aproximaci´n con precisi´n 10−5 . b] que satisface f (x0 ) · f (x0 ) > 0.2)). el e m´todo se denomina cuadr´tico o de segundo orden. calcular una ra´z unica. Teorema 4 Sea f ∈ C(−∞. y f (x) y f (x) son no nulas y conservan el signo para a ≤ x ≤ b.102 6 · Algoritmos iterativos Tomando logaritmos (en base diez. p. al aplicar el m´todo de Newton-Raphson. b) asociado con una ordenada del mismo signo que el de f (x0 ). Se puede tomar. entonces puede e tomarse cualquier valor c ∈ [a.

el m´todo de Newton-Raphson genera e ∞ que converge a p.2) es que el t´rmino que contiene o e a (p − x0 )2 puede ser eliminado. no se cumple a menos que x0 sea una buena aproximaci´n de p. es por lo tanto.11) .1) a (6.3 · Comparaci´n de los distintos ´rdenes de convergencia o o 103 N´tese que en la f´rmula iterativa (6. en un entorno de la ra´ ız: 1 0 = f (p) = f (xn ) + f (xn )(p − xn ) + f (ψn )(p − xn )2 . 2 a donde ψn . Esto. pero si el valor num´rico de la derivada es peque˜o. El m´todo de Newton. Dividiendo por f (xn ) y despejando p: p = xn − 1 f (ψn ) f (xn ) − · (p − xn )2 . resalta la importancia de una buena aproximaci´n inicial.12) (6. f (p) = 0.4). entonces existe un δ > 0. vamos a obtener una f´rmula que relao cione los errores absolutos de dos aproximaciones sucesivas. La o hip´tesis necesaria para pasar de (6. El siguiente resultado ilustra la importancia o te´rica de la elecci´n de x0 . b] es tal que f (p) = 0. muy conveniente o e cuando la gr´fica de la funci´n tiene una gran pendiente en un entorno de la a o ra´ dada. si la curva y = f (x) es casi o horizontal en un entorno del punto de intersecci´n con el eje X.3) est´ claro que cuanto mayor sea o o a ız. o o Teorema 5 Sea f ∈ C[a. tal que. a partir de la o o e serie de Taylor. p + δ]. f (xn ) 2 f (xn ) (6. est´ entre p y xn .10) (6. o La obtenci´n de la f´rmula iterativa del m´todo de Newton. Obtendremos entonces una relaci´n entre en = xn − p y en+1 . Si p ∈ [a. p − xn+1 = − · 2 f (xn ) Entonces 1 f (ψn ) en+1 = · 2 en 2 f (xn ) (6. para cualquier aproximaci´n inicial o una sucesi´n {xn }n=1 o x0 ∈ [p − δ. y calcular la ra´ mediante este procedimiento puede a ız ser un proceso largo o a veces imposible. b].9) y teniendo en cuenta la expresi´n (6. Utilizando el desarrollo de o Taylor. Bajo las anteriores condiciones. tenemos o 1 f (ψn ) (p − xn )2 .6. las correciones ız e n ser´n entonces mayores. Es decir: no utilices el m´todo de e Newton para resolver una ecuaci´n f (x) = 0. a el valor num´rico de f (x) en un entorno de la ra´ tanto menor ser´ la e correcci´n que ha de a˜adirse a la aproximaci´n n-´sima para obtener la o n o e aproximaci´n (n+1).

p + |e0 |] ⊆ I. . 2. a a o Teorema 6 Sea x0 una aproximaci´n inicial y definimos xn y hn tales −f (xn ) que xn+1 = xn + hn .. donde p es la unica ra´z de f (x) = 0 en I0 . ız Sin embargo en la pr´ctica p es desconocido y la condici´n anterior es a o dif´ de comprobar. y de acuerdo con la Definici´n 2. o Por este motivo. puede probarse que el m´todo de Newton-Raphson sieme pre converge (a una ra´ simple) si x0 ha sido elegido lo suficientemente cerca ız de la ra´ p. n Supongamos que |me0 | < 1 y que el intervalo [p − |e0 |.104 y cuando xn → p. x0 + 2h0 ) y supongamos que a 2|h0 |M ≤ |f (x0 )|. 1 f (p) en+1 → · e2 2 f (p) n 6 · Algoritmos iterativos (6. Por n 1 inducci´n podemos ver que xn ∈ I ∀n y |en | ≤ m (me0 )2 . o que es un m´todo de segundo orden. hn = f (xn ) .. n = 1. tal que 1 |f (y)| · ≤ m. y ∈ I 2 |f (x)| (6. se dice que el m´todo de Newton-Raphson tiene convergencia o e cuadr´tica. Sea I0 el intervalo (x0 .14) Si xn ∈ I de (6.12). M = m´x |f (x)| x0 ∈I0 (6. ı Otro criterio que en ocasiones es m´s f´cil de aplicar. El teorema que damos a continuaci´n da un criterio ıcil o m´s pr´ctico a la hora de demostrar la convergencia. ∀x.. se tiene que |en+1 | ≤ me2 ⇔ |men+1 | ≤ (men )2 .13) Como en+1 es proporcional al cuadrado de en . y limn→∞ xn = p. a e Un razonamiento intuitivo que demuestra el teorema anterior es el siguiente: Supongamos que I es un intervalo en torno a p.15) ´ Entonces. xn ∈ I0 . viene dado por el a a siguiente resultado. (Si |me0 | = m|x0 − p| < 1). Teorema 7 Supongamos que f (x) = 0 y f (x) no cambia de signo en .

6. b]. < xn < xn+1 < . o 6. 2.18) a a o ız En cualquier caso. El extremo fijo es aquel para el cual el signo de la funci´n f (x) coincide o con el signo de su segunda derivada f (x).. La curva y = f (x) en este caso ser´ convexa hacia abajo y por lo tanto estar´ localizada por debajo a a de la secante que pasa por (a. el extremo b est´ fijo y las aproximaciones o e a sucesivas: x0 = a.. Tomando l´ o o a ımites en la expresi´n o (6.3 · Comparaci´n de los distintos ´rdenes de convergencia o o el intervalo [a. donde el ız signo de la ra´ es opuesto al signo de su segunda derivada f (x). f (b)). Supongamos que f (x) > 0 para a ≤ x ≤ b. f (a) < 0. 2. cada aproximaci´n xn+1 est´ m´s pr´xima a la ra´ o p. f (b) − f (xn ) (6. ımite existe. y que f (a) · f (b) < 0. xn+1 = xn − f (xn ) (b − xn ) .16) entonces el m´todo de Newton-Raphson converge a partir de una aproximae ci´n inicial x0 ∈ [a. Dicho l´ que la sucesi´n es mon´tona y est´ acotada. b] = [x0 . a < p < b. Las aproximaciones sucesivas caen en el lado de la ra´ p.. puesto Supongamos que p = limn→∞ xn . ız (6. n = 0. Convergencia del m´todo de la secante e Para probar la convergencia del proceso. p = p − f (p) (b − p) . que la precedente xn . Si f (a) < b − a.3. f (b) − f (p) (6. x1 ]. Si hemos supuesto en la descripci´n del m´todo. consideraremos que la ra´ ız est´ separada y la segunda derivada f (x) tiene signo constante en el ina tervalo [a.3..17). f (a)).19) .17) forman una secuencia mon´tona creciente y acotada: o x0 < x1 < . f (a) f (b) < b−a f (b) 105 (6. b]. 1.. < p < b Resumiendo: 1. (b..

2. b] f (p) = 0 xn+1 = xn − f (xn ) · xn −xn−1 f (xn )−f (xn−1 ) Entonces ∃ψ. se deduce que p = p. 3. tal que: (p − xn+1 ) = f (ψ) (p − xn )(p − xn−1 ). haciendo uso de la siguiente conjetura: |en+1 | ≈ K|en |a . Como p es la unica ra´ de f (x) = 0 en el intervalo (a.20) Tenemos entonces que. xn+1 ). para n suficientemente grande. pues |en+1 | ≈ K|en |a ız 2 y en dicho caso el error cometido en la iteraci´n n + 1 ser´ superior al error o ıa en la iteraci´n n (o lo que es lo mismo el m´todo no ser´ convergente). bajo las hip´tesis o del teorema anterior: |en+1 | ≈ C|en ||en−1 |. Al igual que en el e caso del m´todo de Newton. p. tenemos convergencia local. ν ∈ (a. 2f (ν) (6.106 6 · Algoritmos iterativos ´ ız de donde f (p) = 0. xn .618 puede ser ignorada. xn−1 . C = |f (ψ)| 2|f (ν)| Intentamos determinar el orden de convergencia. b) (ψ. ¿a? Sustituyendo en la ecuaci´n anterior: o K|en |a ≈ C|en |K − a |en | a Esta relaci´n se mantiene s´lo si: o o √ 1 1 a = 1 + ⇔ a = (1 ± 5) a 2 1 C = K 1+ a = K a √ La ra´ a = 1 (1− 5) = −0. o e ıa 1 1 . |en | ≈ K|en−1 |a . xn . xn−1 . Se trata de un m´todo que no converge globalmente. f (x) = 0 ∀x ∈ [a. Teorema 8 (de interpolaci´n) Sea f ∈ C[a. b). e El siguiente resultado nos permite obtener el orden de convergencia. b]. b]. ν entre p. xn+1 ∈ o [a. tales que: 1.

(6. para i ≥ i0 y las ai forman una sucesi´n mon´tona creciente y acotada.26) |en+1 | =C |en | (6. Adem´s tomando l´ a ımites en (6.3 · Comparaci´n de los distintos ´rdenes de convergencia o o Por lo tanto |en+1 | ≈ C a |en |a . por lo tanto es convergente. bi ] (6.6) p= que implica (p − b)f (p) = 0.21) y (6. En el caso particular que estamos analizando. a = 1 (1 + 2 1 107 √ 5) = 1. ∀x ∈ [ai . se tiene f (b) > 0.22). y entonces e o o ai < ai+1 = xi < bi+1 = bi .618.6. ´ f (xi+1 ) = 0 o f (xi+1 ) · f (ai ) > 0. y entonces f (p) = 0. Adem´s es f´cil ver que las f´rmulas (6. (6. Entonces. Por el hecho de ser f continua y por (6. es decir o o existe p = limn→∞ ai .24) f (p) ≤ 0 (6.25) bf (p) − pf (b) f (p) − f (b) (6.3. utilizando ´sto en la misma relaci´n obtenida para el m´todo de la secante: e o e limn→∞ puesto que: p − xn+1 = f (ψ) (p − xn )(p − xn−1 ) 2f (ψ) (6.23) . se ve claramente que las sucesivas iteraciones matienen fijo el extremo b.4. de manera que. ai < bi .21).22) ´ Con ´stas hip´tesis. supondremos por e u simplicidad que f (x) existe y que para alg´n valor i.22). f (ai ) < 0. f (bi ) > 0 (6. Convergencia del m´todo de regula falsi e Para probar la convergencia del m´todo de regula falsi. bi = b. Pero p = b.22) son v´lidas para todo a a o a i ≥ i0 si lo son para i0 . 6.21) f (x) ≥ 0.

Nos dedie o caremos a continuaci´n al estudio de este tipo de procesos.. Teor´ general de los m´todos iterativos. Iteıa e raci´n del punto fijo o El m´todo de Newton-Raphson y el de la secante pueden ser considee rados como casos especiales de un m´todo iterativo m´s general. Si ϕ es continua.27) Entonces α = 1 y tenemos convergencia lineal. f (x) − f (y) m=2 La teor´ general de los procesos iterativos es m´s simple cuando m = 1. e es en general un m´todo de primer orden. . xn−1 ..108 Si xn = b. xn−1 .4. y que limn→∞ xn = p. xn−m+1 y sea xn+1 = ϕ(xn . f (ψ) |en+1 | →| (p − b)| = C |en | 2f (ψ) 6 · Algoritmos iterativos (6. o Supongamos ahora que tenemos una sucesi´n {xn } generada a partir o de cierto valor inicial x0 ..6 En el m´todo de Newton-Raphson: ϕ(x) = x − f (x) . ıa a En este caso tenemos: xn+1 = ϕ(xn ) que se denomina m´todo de iteraci´n uni-punto o del punto fijo. xn−m+1 ) Llamaremos a ϕ funci´n de iteraci´n. o o e Ejemplo 6. p = . 6. Es convergente siempre que f sea e continua.. ... f (x) m=1 En el de la secante: ϕ(x) = x − f (x) · x−y . Sea xn+1 e a determinado por el valor de una funci´n y de su derivada en m puntos o xn . El m´todo de regula falsi.

2. Por el teorema o del valor medio.. o Es decir. Iteraci´n del punto fijo 109 ı e o ız limn→∞ xn+1 = limn→∞ ϕ(xn ) = ϕ(p). lo cual o define un m´todo de iteraci´n: xn+1 = ϕ(xn ). podemos intentar escribir dicha expresi´n de la forma x = ϕ(x). e o Ejemplo 6. para construir un m´todo iterativo para la resoluci´n de f (x) = e o 0. p es la unica ra´z en J de x = ϕ(x). 2. 1. y o ı que en el intervalo J = {x : |x − p| ≤ ρ} o ϕ (x) existe y satisface la condici´n |ϕ (x)| ≤ m < 1 Entonces.4 · Teor´a general de los m´todos iterativos. n = 0.. ψ ∈ J |xn − p| ≤ m|xn−1 − p| ≤ mρ < ρ ⇒ xn ∈ J . xn − p = ϕ(xn−1 ) − ϕ(p) = ϕ (ψ)(xn−1 − p). 3.. para todo x0 ∈ J: 1. xn ∈ J. x = ϕ2 (x) = √ 3 x + 5.6. x = ϕ3 (x) = 5 x2 −1 es decir el m´todo iterativo puede no ser unico. es decir el valor p es una ra´ de la ecuaci´n x = ϕ(x). limn→∞ xn = p. ´ ı Demostraci´n: o 1. Por inducci´n: sea x0 ∈ J y supongamos que xn−1 ∈ J. 3.7 Sea la ecuaci´n: x3 − x − 5 = 0 que puede ser escrita como o x = ϕ1 (x) = x3 − 5. Una condici´n suficiente para la convergencia la da el siguiente resultado: o Teorema 9 Supongamos que la ecuaci´n x = ϕ(x) tiene una ra´z p. e ´ Tampoco podemos asegurar que todos ellos sean convergentes.

y ϕ(j) (p) = 0.. j = 1.110 6 · Algoritmos iterativos 2. a menos que ϕ(x) sea elegida de manera especial..28) Luego el m´todo de iteraci´n es de orden α para p. limn→∞ 1 (α) ϕ (ψ)(xn − p)α . hemos partido de suponer la existencia de una ra´ p. Entonces la sucesi´n {xn } definida como xn+1 = ϕ(xn ). ≤ mn |x0 − p| Como m < 1.2 y 3 del Teorema 9 si x1 ± m |x1 − x0 | ∈ J 1−m e El m´todo iterativo xn+1 = ϕ(xn ) es en general un m´todo de primer e orden. α − 1. Utilizando la desigualdad anterior repetidas veces: |xn − p| ≤ m|xn−1 − p| ≤ . β = p. 3. ψ ∈ J |p − β| ≤ m|p − β| < |p − β|. lo cual es absurdo. ız. .. α |en | α! n = xn − p (6. se tiene el resultado. ψ ∈ (xn . Entonces: p − β = ϕ(p) − ϕ(β) = ϕ (ψ)(p − β). en J. p) α! 1 |en+1 | = |ϕ(α) (p)| = 0. x0 ∈ J satisface los apartados 1. Supongamos finalmente que x = ϕ(x) tiene otra ra´ β. ϕ(α) (p) = 0. en un entorno de p. El teorema puede ser modificado y utilizado para probar la existencia de una ra´ de la ecuaci´n x = ϕ(x). ız o Teorema 10 Sea J un intervalo cerrado en el cual ϕ (x) existe y sao tisface la desigualdad |ϕ (x)| ≤ m < 1..• ız En el Teorema 9.. De acuerdo con el teorema de Taylor tenemos: xn+1 = ϕ(xn ) = p + Si limn→∞ xn = p. e o . donde p = ϕ(p). Si ϕ(x) es α veces diferenciable y sus derivadas continuas ϕ ∈ C α .

5 · Generalizaci´n a sistemas de ecuaciones o 111 Este argumento da una demostraci´n alternativa del hecho de que el o m´todo de Newton-Raphson sea al menos de segundo orden para ra´ e ıces simples... . i = 1... o fi (x1 . . 1. f (p) = 0 ⇒ ϕ (p) = 0 ız 6. . . xn ).. .5.. ya que: ϕ(x) = x − ϕ (x) = f (x) f (x) f (x) · f (x) [(f (x))2 − f (x) · f (x)] =1− (f (x))2 (f (x))2 Si p es una ra´ simple. ... .. n (k) La similitud formal al caso n = 1 es m´s f´cil de ver si utilizamos notaci´n a a o vectorial. pueden ser generalizados a sistemas de ecuaciones no lineales........ xn ) = 0. Generalizaci´n a sistemas de ecuaciones o Algunos de los m´todos que hemos visto para resolver ecuaciones no lie neales simples.6. ... x = (x1 .. ϕ(x) = (ϕ1 (x).. . . i = 1. ϕn (x))t El m´todo iterativo puede entonces describirse como: e x(k+1) = ϕ(x(k) ).... n Un m´todo de iteraci´n uni-punto para la resoluci´n de este sistema e o o puede ser constru´ escribiendo el sistema de la forma: ıdo xi = ϕi (x1 . xn ).. Consideremos un sistema general de n ecuaciones no lineales con n inc´gnitas. n que sugiere el m´todo iterativo: e xi (k+1) (k) = ϕi (x1 . k = 0. i = 1... xn )t .

29) 1≤p≤∞ 1 p = 2 (norma eucl´ ıdea) ||x||2 = (|x1 |2 + . + |xn |p ) p .n |xi | a a Norma matricial: ||A||∞ = m´xi=1. Nota: Normas de vectores: Lp : ||x||p = (|x1 |p + ..n n j=1 |aij | 1 x∈R (6. R = {x : ||x − p|| < ρ}. puesto que en este caso dicha condici´n implica la desigualdad o ||ϕ(x) − ϕ(y)|| ≤ m||x − y||.. 1.. p = ∞ ||x||∞ = m´xi=1. 1 ≤ i... Nota: En algunas aplicaciones nos puede interesar resolver un sistema de la forma x = a + hϕ(x) ...112 6 · Algoritmos iterativos Un criterio de convergencia similar al descrito en el Teorema 9 puede utilizarse en este caso. k = 0. En este caso una condici´n suficiente para que el m´todo de iteraci´n anterior converja para cada x0 ∈ R es que para alguna e o elecci´n de la norma se cumpla o ||D(x)|| ≤ m < 1. La velocidad de convergencia depende linealmente de m y tenemos ||x(k+1) − p|| = ||ϕ(x(k) ) − ϕ(p)|| ≤ m||x(k) − p||.. x ∈ R.. Sea D(x) una matriz nxn o cuyos elementos son dij (x).. .. + |xn |2 ) 2 Definici´n 3 Se dice que ϕ es una funci´n de contracci´n cuando ||D(x)|| ≤ o o o m < 1. j ≤ n ∂xj existen para x ∈ R. ∀x. Supongamos que p = ϕ(p) y que las derivadas parciales dij (x) = ∂ϕi (x). y ∈ R Una condici´n necesaria para que el proceso iterativo anterior converja o es que el radio espectral de D(p) sea menor o igual que 1...

tal que. 113 6. a El m´todo de Newton en dimensi´n n es de segundo orden.5 · Generalizaci´n a sistemas de ecuaciones o donde a es un vector constante y h es un par´metro 0 < h << 1. a Si ϕ(x) tiene derivadas parciales acotadas. k = 0. y se tiene ||x(k+1) − p|| ≤ C||x(k) − p||2 Desigualdad que se sigue directamente de: x(k+1) = f (x(k) ) = p + 1 (2) f (ψ)||x(k) − p||2 . Utilizando la f´rmula de Taylor en dimensi´n n: o o f (x) = f (x(k) ) + f (x(k) )(x − x(k) ) + O(||x − x(k) ||2 ) donde f (x) es una matriz nxn..29). se satisface siempre para h suficientemente peque˜o y el m´todo de iteraci´n es: n e o x(k+1) = a + hϕ(x(k) ). ..5.. cuyos elementos son fij (x) = ∂fi (x). obtenemos para f (x) = 0 el m´todo ´ e e iterativo: f (xk ) + (xk+1 − xk )f (xk ) = 0. 1 ≤ i. denotado en ocasiones por J. M´todo de Newton-Raphson e Para n = 1 hemos deducido este m´todo a partir de la f´rmula de Taylor: e o f (x) = f (xk ) + (x − xk )f (xk ) + O(|x − xk |2 ) Si nos olvidamos del ultimo t´rmino. k = 0. . es decir existe e o una constante C = C(ρ). 1.6. entonces el criterio de convergencia anterior. (6. ∀x(k) . p) 2! . 1.1.. el jacobiano de f . . Este es un sistema de ecuaciones lineal para x(k+1) y si f (x(k) ) es no singular puede ser resuelto como veremos m´s adelante... 1. ψ ∈ (x(k) . ||x(k) − p|| ≤ ρ. k = 0. j ≤ n ∂xj De esta forma el m´todo de Newton-Raphson en dimensi´n n es: e o f (x(k) ) + (x(k+1) − x(k) )f (x(k) ) = 0.

2. x3 ) = 0 → x3 (1) (1) (1) (0) (0) (0) (1) (1) (1) . j. h) = ∂xj hj donde ej = (0. como el e e m´todo de la secante que lo aproxima por un cociente de diferencias.0) y h es un par´metro n-dimensional con componentes a hj = 0. x2 . j = 1. x2 .. De utilizar la i-´sima ecuaci´n a resolver para obtener xi e o esta forma en cada iteraci´n se utiliza la ecuaci´n no lineal de dimensi´n o o o uno: fi (x1 para obtener xi (k+1) (k+1) (k) . . . cuando n es o grande puede ser dif´ de resolver.2. ... Existen m´todos derivados de ´ste. Como hemos visto cada iteraci´n del m´todo de Newton-Raphson reo e quiere la resoluci´n de un sistema de ecuaciones lineales el cual... M´todo de Gauss-Seidel e Otro m´todo de resoluci´n de un sistema no lineal f (x) = 0 consiste en e o (k+1) . Adem´s en cada paso hay que calcular ıcil a 2 elementos de f (x(k) ). x3 ) = 0 f2 (x1 ... xn ) = 0 (k+1) (k) . x2 .5.. x2 ...... o o f1 (x1 . i = 1.114 6 · Algoritmos iterativos e De aqui se puede demostrar su convergencia supuesto que x(0) est´ lo suficientemente cerca de p.. x3 ) = 0 → x2 i = 3: f3 (x1 . xi . . 6. x3 ) = 0 → x1 i = 2: f2 (x1 . . . x3 ) = 0 En k = 0: i = 1: f1 (x1 . xi+1 . x2 . x2 . n. Ejemplo de aplicaci´n Sea un sistema no lineal de dimensi´n n = 3. Esto es imposible de hacer a menos que dichos los n elementos tengan una forma funcional sencilla. Una e aproximaci´n utilizada en le pr´ctica es: o a ∂fi fi (x + hj ej ) − fi (x) (x) ≈ ∆ij (x... que proponen estimar f (x). xi−1 . x3 ) = 0 f3 (x1 .. n.

5.01.25 = 0 en [1. 2]. x3 ) = 0 → x3 ··· (2) (2) (2) (1) (1) (1) (2) (2) (2) 115 6. o . 1]. x2 . 3]. (1 + x)senx − 1 = 0 en [0. 2]. x − senx − 0.5 + h2 tang(ex + y 2 ) x0 = 1. x3 − x + 1 = 0 en [−2. 3. 6.5 Prueba con valores de h = 0. x2 . x3 ) = 0 → x1 i = 2: f2 (x1 . 2. 5. puedes intentar resolver las o e siguientes ecuaciones: 1. y0 = 0. senx − x + 2 = 0 en [2. x2 . x = tg(x) en [1. 2].6.6 · Ejercicios de aplicaci´n o En k = 1: i = 1: f1 (x1 . e−3 = xe−x en [0.1 ´ 0. intenta resolver el sistema: x = 1 + h2 (ey √ x + 3x2 ) y = 0.6. Si tienes muchas inquietudes. x3 ) = 0 → x2 i = 3: f3 (x1 . Ejercicios de aplicaci´n o Como aplicaci´n de los m´todos anteriores. 1]. 4.

116 6 · Algoritmos iterativos .

que usualmente denotaremos por ≤.Cap´ ıtulo 7 M´todos de ordenaci´n e o 7. . ´ Un orden total o lineal en un conjunto S es un orden parcial en el que adem´s se cumple que para cada dos elementos a. b ∈ S o bien aRb o bRa. vamos a encontrar una permutaci´n π o de esos n elementos que describir´ la secuencia dada en forma no decreciente a aπ(1) . an elegidos de un conjunto con orden total. El problema de la ordenaci´n puede ser planteado como sigue. c en S: 1.1. e aRb y bRc implican aRc (transitiva). 3. 2... ⊆ sobre conjuntos no o lo es.. Usualmente daremos la secuencia ordenada en lugar de la permutaci´n de ordenaci´n π. La relaci´n R = ” ≤ ” entre enteros y la relaci´n R = ” ⊆ ” entre o o conjuntos son dos ordenes parciales. ... aRb y bRa implican a = b (antisim´trica). para 1 ≤ i ≤ n − 1. aπ(n) . Dada una o o sucesi´n de n elementos a1 .. An´lisis te´rico: orden de complejidad ´ptimo a o o alcanzable para algoritmos internos Definici´n 1 o Un orden parcial en un conjunto S es una relaci´n R. aRa es verdad (R es reflexiva). b. a La relaci´n ≤ sobre enteros es un orden total (lineal). o o 117 . tal que aπ(i) ≤ aπ(i+1) . tal que para cada o a.

y hacia la derecha si la respuesta es ”no”. almacenados en mecanismos como cd’s o cintas). En general. la elecci´n en cada caso depende del resultado del test.118 7 · M´todos de ordenaci´n e o 1 [tnpos=r]a < b 2 [tnpos=r]b < c 4 [tnpos=r]Orden:a b c 5 [tnpos=r]a < c 8 [tnpos=r]Orden: a c b 9 [tnpos=r]Orden: c a b 3 [tnpos=r]a < c 6 [tnpos=r]Orden: b a c 7 [tnpos=r]b < c 10 [tnpos=r]Orden: b c a 11 [tnpos=r]Orden: c b a Figura (a) Los m´todos de ordenaci´n son clasificados como internos. Esta segunda clase de algoritmos tiene como operaci´n b´sica la comparaci´n entre distintos pares o a o de elementos. La complejidad computacional (en cuanto a tiempo). o por el contrario son elementos de los u que no se conoce nada de su estructura en particular. Es test que representa el nodo ra´ es el o ız primero en realizarse. o hasta que se llega a un final de rama (hoja). Los tests est´n o e a indicados a la derecha del un c´rculo que respresenta el nodo correspondiente. el control contin´a pasando de cada v´rtice a u e uno de sus hijos. donde los e o datos residen en la propia memoria del ordenador. El resultado del algoritmo se obtiene cuando se alcanza dicho nodo (final de rama). o externos (los datos est´n a principalmente fuera de la memoria. Otra clasificaci´n posible surge atendiendo a la estructura de los eleo mentos que van a ser ordenados. y luego el control pasa a uno de sus hijos. dependiendo de la respuesta. b y c en orden alfab´tico. ı El control se mueve hacia la izquierda si la respuesta es ”si”. Cada v´rtice a ´ o e interior representa una decisi´n. Arboles de decisi´n o Vamos a considerar un algoritmo en el que en cada paso se realiza una o o o bifurcaci´n (ramificaci´n en dos ramas) depeniendo de una comparaci´n o realizada entre dos elementos. La representaci´n usual para un algoritmo de este tipo es un ´rbol binario denominado arbol de decisi´n. Ejemplo 1 Vamos a ilustrar mediante un arbol de decisi´n el algoritmo ´ o de ordenaci´n de tres elementos a. Con algoritmos de esta naturaleza veremos que se necesitan al menos nlogn comparaciones para ordenar una secuencia de n elementos. La diferencia est´ en si los elementos son a n´meros enteros con un rango fijo. de un arbol de de´ .

Demostraci´n: Como el resultado de la ordenaci´n de n elementos o o puede ser una de las n! permutaciones de los datos. Es de destacar que el n´mero total de v´rtices en el ´rbol u e a puede ser mucho mayor que su altura.7. En cualquier caso. resulta ser una buena aproximaci´n inferior del n´mero de comparaciones o u necesarias para ordenar n elementos.44n.. Corolario 1 Cualquier algoritmo que ordene mediante comparaciones requiere al menos cnlogn comparaciones para ordenar n elementos.. de manera que n(log2 n − log2 e) = nlog2 n − 1. n 2 ≥ n 2 de manera que logn! ≥ n n log 2 2 ≥ n logn 4 para n ≥ 4 o o a De la aproximaci´n de Stirling podemos obtener una aproximaci´n m´s n n precisa de n! como e . a Demostraci´n: Por inducci´n sobre h. Se necesita simplemente obsero o var que un arbol binario de altura h est´ compuesto por una ra´ y a lo ´ a ız sumo dos sub´rboles de altura a lo m´s h − 1. Lema 1 Un ´rbol binario de altura h tiene a lo sumo 2h hojas. Demostraci´n: Para n > 1: o n 2 n! ≥ n(n − 1)(n − 2). debe haber al menos n! hojas en el arbol de decisi´n. podemos probar un buen resultado acerca de la altura de un arbol de decisi´n que ordena n ´ o elementos. .1 · An´lisis te´rico: orden de complejidad optimo alcanzable para algoritmos internos119 a o ´ cisi´n es la altura del arbol. o ´ o n Normalmente deseamos contar el m´ximo n´mero de comparaciones necea u sarias para encontrar el camino de la ra´ a un v´rtice de abandono (n´mero ız e u bifurcaciones). a a Teorema 2 Cualquier arbol de decisi´n que orden n elementos tiene de ´ o altura al menos log2 n!. para c > 0 y n suficientemente grande. Por el Lema 1. como una funci´n del tama˜o del problema. la altura debe ser al menos ´ o log2 n!.

a 2. M´todos simples de complejidad O(n2 ): come paraci´n. do largest := index for loop := index − 1 to 1. El ciclo interior tiene j − 1 pasos por cada paso j en el ciclo exterior y hay n − 1 pasos en el ciclo exterior. Encontrar el segundo mayor elemento..1.2. −1.. Algorithm selectsort for index := n to 2. u o 5. inserci´n. Tambi´n se denomina algoritmo de selecci´n porque trabaja repetidae o mente seleccionando el actual mayor elemento. 4.120 7 · M´todos de ordenaci´n e o 7. −1. do if k(loop) > k(largest) then largest := loop endif enddo //intercambio de posiciones de los dos elementos// temp := k(largest) k(largest) := k(index) k(index) := temp enddo Para estimar el n´mero de comparaciones es importante se˜ alar que el u n algoritmo se reduce a dos ciclos que est´n anidados y la comparaci´n se a o realiza una vez en cada paso del ciclo interior. burbuja o o M´todo de comparaci´n (selecci´n) e o o 7. Continuar con dicho proceso hasta obtener el vector ordenado. Encontrar el elemento m´s grande del vector. ´ o 3. + (2) + (1) = n(n − 1) 2 . Intercambiarlo con el elemento de la ultima posici´n. Intercambiarlo con el elemento de la ante´ltima posici´n.2. luego el n´mero de comparaciones requerido por el algoritmo es: u (n − 1) + (n − 2) + . Es uno de los m´todos de ordenaci´n m´s simples y trabaja de la sie o a guiente forma: 1.

.. S n satisface la relaci´n de recurrencia S n = Hn − 1 + S n−1 . el n´mero de veces que los elementos cambian sus pou siciones est´ dado por el n´mero de veces Sn . Este n´mero Sn . o entonces el primer paso en el ciclo externo genera una permutaci´n o aleatoria de 1. que es: (n − 1) + (n − 3) + . de manera que n Sn = j=2 Hj − (n − 1) = (n + 1)Hn − 2n Nota: Hemos utilizado el resultado n Hj = (n + 1)Hn − n. . + 1 = n2 4 (n2 −1) 4 si n es par si n es impar Para determinar el n´mero medio de elementos intercambiados hay que u se˜alar que: n 1. o . en cada paso del ciclo exterior se intercambian dos elementos y la longitud del vector desordenado es reducida en uno en cada paso.7.. . 2. depende de la ordenaci´n inicial de los u o elementos y puede describirse por sus valores m´ ınimo.min = 0. Esto nos da el m´ximo a valor para Sn. H1 = 1.. que se ejecuta la sentencia a u largest := loop. q2 n q2 q3 q3 q3 ··· ··· ··· qn−1 qn−1 qn−1 qn qn ··· n La ocurrencia de cada permutaci´n de 1. n q1 ··· q1 2. n.. a Si el vector inicial est´ ordenado. 2. . k(2). Si el algoritmo comienza con una permutaci´n aleatoria de 1. burbuja121 e o o Este n´mero es constante sea cual sea la ordenaci´n inicial de los eleu o mentos..2 · M´todos simples de complejidad O(n2 ): comparaci´n. qn−1 . En el algoritmo. inserci´n.max ... n o es producida por cada uno de los datos. n−1 seguida por n. ya que la permutaci´n q1 .. 2 3 o En general.. n−1 en k(1).... . el valor medio de Sn ... m´ximo y medio. dicha sentencia no se ejecuta nunca y a a Sn. Si el vector inicial est´ ordenado en el orden decreciente.. k(n− o 1) es igualmente probable y el n´mero medio de veces que se ejecuta u la sentencia largest := loop durante el primer paso del ciclo externo 1 viene dada por Hn − 1 = 1 + 1 + ... + n .. j=1 que puede ser probado por inducci´n..

tendremos que ir considerando elemento a elemento y situ´ndolo en su propio lugar en un vector ya ordenado.122 7 · M´todos de ordenaci´n e o En resumen.2. Un algoritmo que realiza ´sto se denomina algoritmo de inserci´n.. e o Si queremos aplicar un algoritmo de este tipo a la ordenaci´n de un o vector de n elementos. para n grande) max = n2 4 (n2 −1) 4 si n es par = O(n2 ) si n es impar La complejidad total del algoritmo es entonces O(n2 ) y queda determinada por el n´mero de operaciones. do pivot := k(i) . M´todo de inserci´n e o Los m´todos de ordenaci´n por inserci´n est´n basados en la siguiente e o o a idea: Sea un vector ordenado de n elementos distintos k(1) < k(2) < . < k(n) Queremos insertar un elemento X en el vector anterior compar´ndolo a con los elementos del vector y situ´ndolo en su posici´n moviendo despu´s a o e los elementos posteriores un lugar a la derecha.. el n´mero de comparaciones entre elementos es u n(n − 1) = O(n2 ) 2 y el n´mero de intercambios es: u min = 0 media = (n + 1)Hn − 2n = O(nlogn) (Notar que Hn ≈ log2 n. u 7. a Un posible algoritmo ser´ ıa: Algorithm insertsort k(0) := minvalue for i := 2 to n.2.

la secuencia est´ ordenada. Esto produce ´ muchos movimientos redundantes de elementos. o Una modificaci´n de este algoritmo es el que se denomina Shellsort. k(n).. burbuja123 e o o j := i while k(j − 1) > pivot do k(j) := k(j − 1) j := j − 1 enddo k(j) := pivot enddo El algoritmo anterior requiere n(n−1) movimientos de elementos en el 2 peor caso.. si es e necesario. (Ver o por ejemplo p´g. el m´todo de la burbuja los ordena intercambiando elementos adyacentes. si por ejemplo. el elemento m´s peque˜o est´ situado en el extremo contrario del vector hacen a n a falta n pasos para moverle a la posici´n que le corresponde. porque. El algoritmo trabaja de la siguiente manera: dada una secuencia de elementos desordenados k(1). generalmente no todos distintos. Cuando no es necesario ning´n intercambio u m´s.2 · M´todos simples de complejidad O(n2 ): comparaci´n. Esta alta complejidad es debida a que cada pasada del ciclo interno produce unicamente intercambios entre elementos adyacentes. en sucesivas pasadas.7. M´todo de intercambio (burbuja) e Otra familia de algoritmos de ordenaci´n por comparaci´n est´ basada o o a en la idea de que cada comparaci´n entre dos elementos debe ser seguida o sistem´ticamente por el intercambio de pares de elementos desordenados. a hasta que no aparezcan mas pares a ordenar. de [9]).3. inserci´n. 231 y ss.2. a 7.. . a a Algorithm bubblesort repeat pivot := 1 for k := 2 to n do if A(k − 1) > A(k) then temp := A(k − 1) A(k − 1) := A(k) A(k) := temp pivot := k − 1 endif enddo .

124 until pivot = 1 7 · M´todos de ordenaci´n e o Ejemplo 2Vamos a ver como ordena la secuencia 3 3 3 3 3 2 2 2 1 4 2 2 2 3 1 1 2 2 4 1 1 3 3 4 2 1 7 1 4 7 pasada 1. pivot=1 Hay tres cantidades involucradas en el tiempo de desarrollo del m´todo e de la burbuja: el n´mero de comparaciones. pivot=4 4 4 7 7 pasada 2. Un paso del ciclo exterior corresponde a una pasada a trav´s del e vector inicial. a o (Ver por ejemplo p´g. y se utilizar´n a a o a n − 1 comparaciones y n − 1 intercambios. pasadas e a u intercambios no es f´cil de realizar. el n´mero de intercambios y u u el n´mero de pasadas a trav´s de los datos. La segunda pasada colocar´ el a segundo mayor elemento en su posici´n y utilizar´ para ello n − 2 comparao a ciones y n − 2 intercambios. pivot=3 pasada 3. entonces en la primera pasaa da se colocar´ el elemento m´s grande en su propia posici´n. de manera que el n´mero de pasadas corresponde al n´mero u u de pasos ejecutados en el ciclo exterior. . Si los datos iniciales est´n en orden inverso. Para estimar dichas cantidau e des podemos ver que el algoritmo se ha implementado utilizando dos ciclos anidados. 220 y 223 de [9]). 0 y 1 respectivamente. Si los elementos est´n ordenados. El motivo es que dicho c´lculo requiere a a de una interesante herramienta matem´tica como son las tablas de inversi´n. concluiremos que este m´todo requiere de n(n−1) comparaciones e intercambios y n pasae 2 das. Entonces el valor m´ u u u el n´mero de comparaciones. El an´lisis de valores del n´mero medio de comparaciones. la pasada inicial ser´ tambi´n la ultima a a e ´ a ınimo para y no har´n falta intercambios de elementos. En cualquier caso podemos concluir a que la complejidad del m´todo de la burbuja en media no es mejor que e O(n2 ). el n´mero de intercambios y el n´mero de pasadas son n − 1. Continuando con este argumento.

En general un nodo de nivel i tiene un camino de ı longitud i hasta la ra´ ız. y si no es un nodo terminal. En la etapa de creaci´n del mont´n. Definimos la ra´ de un arbol como el nodo que est´ en el nivel 1. El o proceso consiste en dos etapas: los primeros n pasos construyen un vector con estructura de ”mont´n”(o pila) y los siguientes n extraen los elementos o en orden decreciente y construyen la secuencia ordenada final. se denomina ra´ Una forma usual de numerar todos los nodos es de izquierda a derecha en cada nivel. El nodo ra´ es un nodo de longitud 1. A(2k) y A(2k + 1) est´n en el nivel i + 1. Inversamente A(k) es a e el nodo padre de A(2k) y A(2k + 1). Un arbol binario puede representarse gr´ficamente de dife´ rentes formas.7. Consideremos un vector A(1 : n). Un nodo que no a a tiene hijos se denomina nodo terminal (hoja). M´todos de complejidad O(nlogn): Quicksort e y Heapsort Heapsort 7.3 · M´todos de complejidad O(nlogn): Quicksort y Heapsort e 125 23 [tnpos=r]A(1) 19 [tnpos=r]A(2) 11 [tnpos=r]A(4) 1 [tnpos=r]A(8) 2 [tnpos=r]A(9) 7 [tnpos=r]A(5) 4 [tnpos=r]A(10) 6 [tnpos=r]A(11) 10 [tnpos=r]A(3) 5 [tnpos=r]A(6) 3 [tnpos=r]A(7) Figura (b) 7. es o u un nodo interno o de bifurcaci´n. y el a ız. sus hijos est´n a una longitud ız a 2 y as´ sucesivamente. Es un elegante m´todo de ordenaci´n que utiliza la idea de una repetie o da selecci´n de elementos. Si A(k) ız ´ a est´ en el nivel i. Fue inventado por Williams y Floyd (1964). la o o informaci´n relativa al orden de los elementos que es obtenido despu´s de o e cada comparaci´n es implementada en forma de estructura de datos amono tonados. Asociamos a este vector una estructura ´ a de arbol binario. A(1).3. . de manera que si A(k) es el nodo ra´ de un sub´rbol. que son llamados nodos hijos.1.3. A(2k) y A(2k + 1) son los dos nodos inmediatos ız a colocados detr´s de ´l. El n´mero de nodos que hay que recorrer desde la ra´ hasta el nodo A(j). nodo m´s alto. Normalmente se representa como si creciese hacia abajo. situada de derecha a izquierda en un vector. se denomina longitud del camino hasta ız A(j). Veamos esto con el siguiente ejemplo.

el ´ ındice del mayor elemento es el de ese hijo) temp := A(k) A(k) := A(index) A(index) := temp n ) 2 if(index ≤ k := index goto 10 endif (index no es una hoja) endif enddo Ejemplo 3 . A(2k + 1)). A(2k + 1) (si s´lo o hay un hijo. o en dos niveles adyacentes a lo sumo. A(2k + 1)) then n 2 to 1. El elemento m´s a a grande en un sub´rbol es siempre la ra´ de ese ´rbol.126 Definici´n 2 o 7 · M´todos de ordenaci´n e o Una pila o mont´n es un arbol binario con nodos A(1) a A(n) inclusive. A(k) ≥ max(A(2k). 2 n≥2 La secuencia de elementos en el camino de la ra´ a un nodo terminal ız est´ linealmente ordenada. −1 do index :=´ ındice del mayor elemento de A(2k). o ´ el cual tiene todos sus nodos terminales al mismo nivel. Por ejemplo: 23 > 19 > 11 > 1. ´ o Algorithm createheap A(1 : n) for r := k := r 10 if A(k) < max(A(2k). y tiene la propiedad de que el valor de un nudo padre es mayor o igual que los valores de los hijos. El algoritmo heapsort a ız a funciona de la siguiente forma: dado un vector desordenado de tama˜o n: n A(1 : n). el algoritmo en una primera parte construye la pila o mont´n y o luego sistem´ticamente selecciona la ra´ actual y situa dicho elemento en a ız la ultima posici´n del vector A. para 1 ≤ k ≤ n .

.3 · M´todos de complejidad O(nlogn): Quicksort y Heapsort e 127 6 [tnpos=r] 19 [tnpos=r] 11 [tnpos=r] 1 [tnpos=r] 2 [tnpos=r] 7 [tnpos=r] 4 [tnpos=r] 23 [tnpos=r] 10 [tnpos=r] 5 [tnpos=r] 3 [tnpos=r] 19 [tnpos=r] 11 [tnpos=r] 6 [tnpos=r] 1 [tnpos=r] 2 [tnpos=r] 7 [tnpos=r] 4 [tnpos=r] 23 [tnpos=r] 10 [tnpos=r] 5 [tnpos=r] 3 [tnpos=r] k k k k =5 =4 =3 =2 k=1 A(1) 11 11 11 11 11 11 (23) 23 23 A(2) 2 2 2 2 (23) 23 (11 (19) 19 A(3) 3 3 3 (10) 10 10 10) 10 10 A(4) 1 1 (23) 23 (2 (19) (19) (11 (11) A(5) 4 (7) 7 7 7) 7 7 7) 7 A(6) 5 5 5 (5 5 5 5 5 5 A(7) 10 10 10 3) 3 3 3 3 3 A(8) 23 23 (1 1 1 (1 1 1 (1 A(9) 19 19 19) 19 19 2) 2 2 2) A(10) 7 (4 4 4 4 4 4 4 4 A(11) 6 6) 6 6 6 6 6 6 6 La ultima fila de la tabla anterior. . s > 1) Intercambiamos el elemento m´s grande que est´ en A(1) con A(s) a a pivot := A(1) . Heapsort extrae el elemento m´s grande de la a ra´ y lo almacena en A(n).. n − 1 en un mont´n.7.. El ´ proceso de intercambiar A(1) y A(n − 1) se repite y adem´s el ´rbol ocupa a a las posiciones de los elementos 1... ız Adem´s la posici´n n no es considerada posteriormente parte del mont´n. a o o Para reordenar los elementos en las posiciones 1. el o elemento de A(1) es hundido tan abajo en el arbol como es necesario. es la que estaba representada en el ´ a ´rbol binario de la Figura (b). intercambiando los elementos A(1) y A(n).. n − 2. En la segunda etapa.. Pasamos de: 6 a: 19 11 10 6 7 5 3 1 2 4 23 19 10 11 7 5 3 1 2 4 23 Algorithm updateheap A(1 : s) (A(1 : s) es el nuevo vector amontonado.

que tiene el mont´n considerado. −1 do updateheap A(1 : s) enndo N´ mero de comparaciones necesario en Heapsort u El primer algoritmo createheap consta de dos ciclos que est´n anidados. Es decir el n´mero de o u . El n´mero de u comparaciones necesarias es O( n · h) = O(nh). a El ciclo exterior tiene n pasos. Return (A) 7 · M´todos de ordenaci´n e o El algoritmo Heapsort completo se implementa como sigue: Algoritmo Heapsort createheap A(1:n) for s := n to 2. Adem´s en cada o a paso del ciclo interior se realizan al menos dos comparaciones. A(2k + 1) a if A(k) < A(index) then //intercambiar A(k) con A(index)// pivot := A(index) A(index) := A(k) A(k) := pivot k := index else goto fin endif enddo fin. 2 El algoritmo updateheap contiene unicamente un ciclo. h. Se realiza una ´ comparaci´n por cada paso del ciclo y el ciclo se realiza al menos el n´ mero o u de veces que corresponde a la altura del mont´n.128 A(1) := A(s) A(s) := pivot k := 1 while 2k < (s − 1) do index := ´ ındice m´s grande de A(2k). mientras que el interior no se realiza mas 2 veces que la altura.

Despu´s del intercambio disminuir j en 1 y continuar e la comparaci´n hasta que se produzca otro intercambio. e 7. Quicksort trabaja particionando el vector en dos partes y ordenando cada una de las partes independientemente. 3. el vector se ordena o de manera que se mantienen las siguientes condiciones: 1. K(n) son mayores o iguales que K(k). a o Todos los elementos K(1). est´ en su propia posici´n. Esto puede ser implementado f´cilmente utilizando la siguiente estratea gia.. El elemento que particiona K(k). Si el arbol o ´ tiene n elementos y en cada nodo se produce una bifurcaci´n. es tambi´n O(nlogn). o El n´mero total de comparaciones requeridas por el algoritmo completo u heapsort es: O(nh) + (n − 1)O(h) = O(nlog2 n) + nO(log2 n) = O(nlogn) El n´mero de intercambios de elementos requerido por heapsort para u cualquier vector inicial.. K(k−1) son menores o iguales que K(k). . Esto significa que despu´s de cada partie ci´n del vector. se produce un intercambio incluso cuando para i = j... K(i) y K(j) son elementos con el mismo valor. .4. Todos los elementos K(k+1). o o Cuando los punteros i. Declarar dos punteros i. Despu´s de dicho o e intercambio incrementar i y as´ sucesivamente hasta que i = j. j con i = 1. K(s) hasta enontrar un elemento K(s) menor que K(i)... Cada uno de los dos vectores o es ordenado independientemente.. En la etapa en la que se realiza la partici´n. La posici´n ex´cta de la partici´n depende o a o de los datos. h ≈ log2 n. incrementar i por 1 y continuar la comparaci´n despu´s de incrementar el valor de i y hasta que se produzo e ca otro intercambio.. j alcanzan la misma posici´n.... ı Para un mejor desarrollo del algoritmo. j = n inicialmente. donde h representa la altura del mont´n. Comparar K(i) con K(j). K(j − 1).7. . Intercambiar ambos elementos.. 2. Quicksort Supongamos un vector de elementos desordenado K(1). dicha posici´n determina la partici´n del vector en dos subvectores.4 · Quicksort 129 comparaciones es O(h). K(n). una de las partes necesita ser almacenada temporalmente o mientras Quicksort ordena la otra parte. . . Esto requiere de alguna memoria auxiliar por parte del algoritmo.

y que nosotros haremos. s) endif Tiempo medio requerido por Quicksort Antes de calcular el tiempo medio de ejecuci´n de un algoritmo.130 7 · M´todos de ordenaci´n e o Una descripci´n completa del algoritmo ser´ la siguiente: o ıa Algorithm Quicksort (r. s) i := r j := s + 1 pivot := K(i) if s > 1 then repeat repeat j := j − 1 until K(j) ≤ pivot repeat i := i + 1 until K(i) ≥ pivot if i < j temp := K(i) K(i) := K(j) K(j) := temp endif until j ≤ i K(r) := K(j) K(j) := pivot (el elemento que determina la partici´n est´ en la posici´n j) o a o endif if r < s then quicksort(r. Para alu o goritmos de ordenaci´n. vamos o a determinar c´al es la distribuci´n de probabilidad de los datos. f´cilmente se puede o a acotar inferiormente el n´mero de comparaciones necesarias para ordenar u una secuencia de n elementos. j − 1) quicksort(j + 1. . es o o que cada permutaci´n de la secuencia a ordenar es igualmente probable a o la hora de aparecer como datos. una hip´tesis natural. Bajo esta hip´tesis.

7. 1 ≤ i ≤ k. Recordemos del Teorema 2. ´ o Teorema 3 Bajo la hip´tesis de que todas las permutaciones de la seo cuencia de n elementos son igualmente probables. cualquier ´rbol de decisi´n a o que ordene n elementos tiene una altura esperada de al menos logn!. ´ o donde pi es la probabilidad de alcanzar la hoja i y di su altura. elegido de entre ´ a n todos los ´rboles T con m hojas. la suma m´ a ınima est´ dada por a D(k) = min1≤i≤k {k + D(i) + D(k − i)} De la hip´tesis inductiva. la suma i pi di . Entonces. ∀m ≥ 1. se puede calcular el n´mero u esperado de comparaciones realizado por un algoritmo de ordenaci´n partio cular. se deduce que i = alcanza en i = k . luego: 2 D(k) ≥ k + klog k 2 con f ( k ) > 0. M´s a´n. se pueden determinar las proo babilidades de cada una de las hojas. que un arbol de decisi´n T que ordene n ´ o elementos cualesquiera tiene al menos n! hojas. Demostraci´n: Sea D(T ) la suma de las alturas de las hojas de un o arbol binario T . T est´ formado por una ra´ y por un arbol a la izquierda Ti con i a ız ´ ız hojas y un arbol a la derecha Tk−i con k − i hojas (la ra´ de T no pertenece ´ u a ninguno de los dos sub´rboles Ti ni Tk−i ). Esta suma es la altura esperada de un arbol de decisi´n. para unos datos dados. Demostraremos por inducci´n sobre m.4 · Quicksort 131 El m´todo general es asociar con cada hoja del ´rbol de decisi´n ν. El m´ ınimo se 2 k = k + klogk − klog2 = klogk 2 Conclu´ ımos entonces que D(m) ≥ mlogm. evaluando sobre todas las hojas del arbol de decisi´n. Si conocemos la distribuci´n de probabilidad de los datos. Para m = 1 el resultado es trivial. a o que D(m) ≥ mlogm. tiene ex´ctamente a u a . Sea D(m) el valor m´s peque˜o de D(T). la e a o probabilidad de alcanzar dicha hoja. tenemos: o D(k) ≥ k + min1≤i<k [ilogi + (k − i)log(k − i)] = k + min1≤i<k f (i) Derivando f (i) = 0. Supongamos que es cierto para valores de m menores que k y consideremos un arbol T con ´ k hojas. para alg´n i. a Claramente D(T ) = i + D(Ti ) + (k − i) + D(Tk−i ) Adem´s.

• Corolario 2 Cada algoritmo de ordenaci´n mediante comparaciones reao liza al menos cnlogn comparaciones en media. tenemos: T (0) = T (1) = b T (n) = = 1 n (T (s) + T (n − s)) + cn. Teorema 4 El algoritmo Quicksort ordena una secuencia de n elementos en un tiempo esperado de O(nlogn). 1 la altura esperada de T y por lo tanto de T es al menos n! n!logn! = logn!. a n o Sea T (n) el tiempo medio necesario por Quicksort para ordenar una secuencia de n elementos. n ≥ 2 n s=1 n−1 1 n { T (s) + T (j)} + cn n s=1 j=0 n−1 1 n−1 { T (s) + T (j) + T (n)} + cn n s=1 j=0 = . sin cambiar la altura esperada de T . cada una de probabilidad n! . . Por simplio cidad supongamos que todos los elementos en S son distintos.. o 1 < c < 2 (se sigue del hecho de que se necesitan n comparaciones y ≤ n intercambios). an . Claramente T (0) = T (1) = b para alguna constante b. para alguna constante c > 0.. donde c es una constante. los tiempos medios requeridos por Quicksort para ordenar cada uno de los dos vectores. Como D(T ) ≥ n!logn!.132 7 · M´todos de ordenaci´n e o 1 n! hojas con probabilidad n! cada una. Denotamos por T (s) y T (n − s). Tenemos entonces un 1 a ´rbol T con n! hojas. Como s es iguamente probable al resto de valores entre 1 y n. Podemos eliminar de T todos los nodos antecesores s´lo de hojas con o probabilidad 0. y las restantes con probabilidad 0. Supongamos que despu´s de la primera partici´n hemos creado dos vece o tores en S.. Veremos ahora que el algoritmo Quicksort tiene un tiempo medio de ejecuci´n acotado inferiormente por cnlogn para alguna constante c. El hecho o significativo es que el tiempo de ejecuci´n para el peor caso en dicho algoo ritmo es cuadr´tico. respectivamente. Demostraci´n: Sea una secuencia de n elementos a1 . Esta hip´tesis o maximizar´ los tama˜os de los vectores resultantes de su partici´n. y el tiempo necesario para obtener la primera partici´n es cn. Sin embargo no es un hecho importante en la mayor´ a ıa de los casos.

. n nT (n + 1) = 2 s=1 T (s) + c(n + 1)2 .. Si la escribimos para o o n + 1.7.. 1 < c < 2 Expresi´n que nos da una relaci´n de recurrencia.4 · Quicksort = 1 2 n−1 T (s) + T (n) + cn n s=1 n 133 o escrito de otro modo: n−1 (n − 1)T (n) = 2 s=1 T (s) + cn2 . + n+1 n 2 + 1 1 + + .. + 1 n n−1 = = b + c (Hn+1 − 1) + (Hn+1 − = b + 2cHn+1 − c n+2 n+1 1 ) = n+1 . 1 < c < 2 n−1 (n − 1)T (n) = 2 s=1 T (s) + cn2 restando la segunda de la primera. se obtiene: nT (n + 1) − (n − 1)T (n) = 2T (n) + c(2n + 1) o lo que es lo mismo: nT (n + 1) = (n + 1)T (n) + c(2n + 1) T (n) (2n + 1) T (n + 1) = +c n+1 n n(n + 1) que escrita recursivamente: T (n) (2n + 1) T (n) 1 1 T (n + 1) = +c = + c( + ) n+1 n n(n + 1) n (n + 1) n T (n − 1) 1 1 T (n) = + c( + ) n n−1 n n−1 · · · T (1) 1 T (2) = + c( + 1) 2 1 2 De donde: T (n + 1) n+1 = b +c 1 1 1 1 + + .

134 Si y s´lo si: o 7 · M´todos de ordenaci´n e o T (n + 1) = (n + 1)b + 2c(n + 1)Hn+1 − c(n + 2) ⇔ T (n) = nb + 2cnHn − c(n + 1) Donde: Hn ≈ loge n = de manera que: T (n) ≈ nb − c(n + 1) + 2cnloge n = O(nlogn) Notar que: k = loge n ⇔ ek = n l = log2 n ⇔ 2l = n 2 < e. de donde k < l y por tanto loge n < log2 n. luego n = 2l < el .• 1 n+1 n=1 ∞ .

o El operador & s´lo se puede aplicar a variables y elementos de un arreglo o (array). en ocasiones. Sin embargo. Los apuntadores se han comparado con la proposici´n goto. e e y px un puntero. Sea x una variable de tipo int. con disciplina se pueden emplear para conseguir claridad y simplicidad.1. asigna la direcci´n de x a la variable px. Construcciones como &(x + 1) o &3 son ilegales. puesto que son. ahora se dice que px “apunta a” x. mediante c´digo compacto ´ a o y eficiente. Esto es completamente cierto cuando se emplean sin cuidado. o por lo que la proposici´n o px = &x. Este es el aspecto que vamos a intentar desarrollar en este tema. El operador unitario & devuelve la direcci´n de un objeto. Son frecuentemente utilizados en C. Punteros: variables que contienen la ubicaci´n en memoria o de otras variables Puesto que un puntero contiene la direcci´n de un objeto. Tambi´n es ilegal e obtener la direcci´n de una variable register. o 135 . se puede acceo der al objeto “indirectamente” a trav´s de ´l. 8.Cap´ ıtulo 8 El lenguaje de programaci´n C (IV) o Los apuntadores o punteros son variables que contienen la direcci´n de o otra variable. como una o manera de crear programas imposibles de entender. la unica forma de expresar algunos c´lculos. Direccionamiento indirecto.

asigna a y el mismo valor de x. Si y tambi´n es de tipo int. En el caso del ejemplo anterior. asigna a y el contenido de cualquier parte a la que apunte px. int *px. A un puntero se le puede sumar o restar un entero. Hay que se˜alar que en la declaraci´n de un apuntador se o n o restringe el tipo de los objetos a los que puede apuntar. e y=*px. La secuencia px = &x. Por ejemplo: double atof(). y = *px. Esta ultima declaraci´n se entiende como un mnemot´cnico. es decir si px aparece en o el contexto ∗px. *dp. La expresi´n o y=*px+1. La expresi´n a o . ello equivale a una variable int. Si px apunta al entero x. Los apuntadores pueden aparecer en expresiones. px ha sido declarado como un puntero a un entero x. Se quiere ´ o e indicar que la combinaci´n ∗px es de tipo int. entonces ∗px puede aparecer en cualquier contexto que pudiera hacerlo x. La sintaxis en la declaraci´n de una variable imita a la sintaxis de las expresiones en las que puede o aparecer la variable. Este razonamiento es util cuando aparecen expresiones ´ complicadas.136 8 · El lenguaje de programaci´n C (IV) o El operador unitario ∗ toma su operando como una direcci´n para obteo ner su contenido. Tambi´n es necesario declarar las variables que intervienen.y. la forma de e hacerlo es: int x. indican que atof () y ∗dp toman valores de tipo double si aparecen en una expresi´n. asigna a y una unidad m´s que x.

incrementa en 1 el valor de x. pone x a 0. Si py es otro apuntador a enteros. variable que es convertida a double para poder utilizar sqrt. imprime el valor actual de x. Punteros: variables que contienen la ubicaci´n en memoria de otras variab o printf("%d\n". y *px+=1. y el resultado es asignado a y. u Adem´s como los apuntadores son variables. los operadores unitarios ∗ y & tienen mayor precedencia que los operadores aritm´ticos. py=px. Tambi´n pueden aparecer referencias a apuntadores en la parte izquierda e o de una asignaci´n. En este ultimo ejemplo se necesitan los par´ntesis. por lo que al evaluar la expresi´n se toma el valor del e o objeto al que apunta px y se le suma 1.8. se in´ e crementar´ en 1 px y no el objeto al que apunta. con lo que se consigue que ahora py apunte al mismo objeto que px.1 · Direccionamiento indirecto. En otro caso. ya que los operadores ıa unitarios como ∗ y ++ se eval´an de derecha a izquierda. En expresiones como y=*px+1. M´s a adelante veremos el significado de ∗(px + 1). al igual que (*px)++. y d=sqrt((double)*px). entonces *px=0. Si px apunta a x. se pueden manipular igual a que cualquier variable. asigna a d la raiz cuadrada de x. *px). . copia el contenido de px en py.

py) /* intercambio de int *px. por lo que &a es un o apuntador a a. por defecto de la llamada “por valor”. a trav´s de los cuales se accede e a los operandos reales. La llamada ser´ o ıa swap (a. En la definici´n (correcta) de la funci´n swap se tienen que o o declarar los argumentos como apuntadores. x=y. } Pero. El operador & devuelve la direcci´n de una variable. y la definici´n (incorrecta. En la llamada a la fuci´n swap se utilizan ın o apuntadores a los valores que se desea cambiar: swap (&a. temp=*px. Tambi´n es cierto que no hay forma directa de hacer que e la funci´n llamada altere una variable de la funci´n que la llama. la funci´n swap no puede o modificar los argumentos a y b. { int temp. Punteros y funciones: paso de argumentos de referencia haciendo uso de punteros Cuando hablamos de las funciones en C.b).*py. y=temp. { int temp.&b). como veremos) ser´ o ıa: swap(x.y) int x. que permite conseguir el f´ deseado. ¿Qu´ hacer o o e entonces cuando realmente queremos modificar alguno de los argumentos? Por ejemplo. temp=x. un programa de clasificaci´n podr´ intercambiar dos eleo ıa mentos no ordenados con una funci´n llamada swap. swap(px. dijimos que los argumentos se pasaban por valor. Sin embargo hay una forma de hacerlo. *px y *py */ .138 8 · El lenguaje de programaci´n C (IV) o 8.2.y.

while((c=getch())==’ ’|| c==’\n’ || c==’\t’) . } Los apuntadores como argumentos suelen emplearse en el caso de funciones que devuelven m´s de un valor simple. que deber´ ser un e a apuntador a un entero. El valor num´rie co del entero hallado se devuelve a trav´s del argumento. si no hay caracteres. Esta organizaci´n diferencia la indicaci´n de fin de o o e archivo y los valores num´ricos. *py=temp. getint debe devolver el valor hallado o una indicaci´n de fin de o archivo. para indicar un entero normal. Observa que es imprescindible escribir &v en lugar de v.n++) array[n]=v. Podr´ decirse que swap devuelve a ıa dos valores: los nuevos valores de sus argumentos. podr´ ıa coincidir con el del entero hallado en la entrada. Con las siguientes sentencias se consigue llenar un array con valores enteros llamando a getint: int n. Una soluci´n consiste en que getint devuelva EOF si detecta el fin de o fichero o cualquier otro valor. Si se utiliza v se puede cometer un error de direccionamiento puesto que getint supone que se le pasa un puntero como argumento. La definici´n de getint es la siguiente: o getint(pn) /* toma el siguiente entero de la entrada */ int *pn.8. /* salta espacios en blanco */ sign=1. array[SIZE].2 · Punteros y funciones: paso de argumentos de referencia haciendo uso de punteros139 *px=*py. el valor empleado para EOF. Cada llamada deja en v el entero hallado. } . sign.n<SIZE && getint(&v)!=EOF. for (n=0. { int c. puesto que en otro caso. Dichos valores hay que devolverlos en objetos independientes. if(c==’+’ || c==’-’) { /* registra signo */ sign= (c==’+’) ? 1: -1. sin formato y un entero por llamada. c=getch(). v. como argumento de getint. Ejemplo: Consideremos una funci´n getint() que realice la conversi´n o o de una cadena de caracteres en valores enteros.

Ya que el buffer y el ´ a ındice son compartidos por getch y ungetch y deben conservar sus valores entre llamadas. Afortunadamente es f´cil simular la devoluci´n de un caracter con s´lo escribir a o o un par de funciones cooperantes. Pero entonces el programa habr´ le´ u a a ıdo un car´cter de m´s para el que no est´ preparado. cada vez que un programa leyera un car´cter a de m´s. getch toma un car´cter de la a entrada y ungetch lo devuelve. } ungetch(c) /* devuelve un car´cter */ a . } ∗pn se utiliza en getint como cualquier variable de tipo int. c=getch()) *pn=10* *pn + c . /* buffer para ungetch */ int bufp=0. para que la siguiente llamada de getch pueda encontrarlo de nuevo. Entonces. si contiene algo. ungetch sit´a el car´cter devuelto en un buffer comparu a tido (un arreglo de caracteres). Un ejemplo es la recoıdo lecci´n de los caracteres que constituyen un n´mero: mientras no o u se encuentra el primer car´cter que no sea d´ a ıgito se sabe que el n´mero no est´ completo. Llama a getchar si el buffer est´ vac´ Tambi´n debe de a ıo. deber´n ser externos a ambas.’0’. if(c != EOF) ungetch(c). return (). getch lee en el buffer. Una forma de escribir getch a y ungetch y sus variables compartidas es: #define BUFSIZE 100 char buf[BUFSIZE].140 8 · El lenguaje de programaci´n C (IV) o for(*pn=0. podr´ devolverlo a la entrada y el resto del c´digo se a ıa o comportar´ como si nunca hubiera sido le´ ıa ıdo. *pn *= sign. Nota: ¿Qu´ hacen getch y ungetch? A menudo se da el caso e de que un programa que lee no puede determinar si ha le´ ıdo suficiente hasta que ha le´ demasiado. e existir una variable ´ ındice que recuerda la posici´n siguiente del o car´cter en el buffer. /* sigue posici´n libre en buf */ o getch() /* lee un (posiblemente devuelto) car´cter */ a { return ((bufp > 0) ? buf[--bufp] : getchar()). La forma de funcionar es sencilla. c>=’0’ && c<=’9’. a a a El problema estar´ resuelto si fuera posible “desleer” el car´cter ıa a no deseado.

} 8. entonces. es decir. pa contiene la direcci´n o de a[0]. { if(bufp > BUFSIZE) printf("ungetch: demasiados caracteres \n"). cualquier operao ci´n que pueda realizarse mediante la indexaci´n de un array se puede realio o zar tambi´n con punteros. a ıcil La declaraci´n o int a[10].8.2 · Punteros y funciones: paso de argumentos de referencia haciendo uso de punteros141 int c. a[1]. Si pa es un puntero a un entero.2. . copiar´ el contenido de a[0] en x. declarado como int *pa. pero para los no iniciados. y en general pa−i al elemento o situado i posiciones antes que pa. La versi´n con ´stos ser´ m´s r´pida en t´rminos e o e a a a e generales. define un array de tama˜o 10.. m´s dif´ de entender. por definici´n pa+1 apunta al siguiente elemento. y pa + i apunta al elemento situado i posiciones despu´s. es decir un bloque con diez objetos consen cutivos denominados a[0].. La notaci´n a[i] significa que el eleo mento del array se encuentra a i posiciones del comienzo. entonces la asignaci´n o pa=&a[0]. a[9]. entonces e .1. Punteros y arrays En C existe entre punteros y arrays un relaci´n tal que. a Si pa apunta a un elemento particular de un arreglo a. Si pa apunta a a[0]. hace que pa apunte al elemento 0 de a.. else buf[bufp++]=c. Ahora la asignaci´n o x=*pa.

i se multiplica por el tama˜o de los objetos a los que n apunta pa antes de ser sumado a pa. En la funci´n. Estos comentarios son ciertos cualquiera que sea el tipo de variables del array a. de manera que expresiones como a = pa o a + + son ilegales.142 *(pa+1) 8 · El lenguaje de programaci´n C (IV) o se refiere al contenido de a[1]. pa + i es la direcci´n de a[i] y ∗(pa + i) es o el contenido de a[i]. Puesto que el nombre de un arreglo es sin´nimo de la posici´n ´ o o o del elemento cero. En suma. cualquier e expresi´n en que aparezca un array y un sub´ o ındice se puede escribir como un puntero y un desplazamiento y viceversa. en las expresiones puede aparecer como un sub´ ındice: pa[i] es id´ntico a ∗(pa + i). Pero el nombre de un array es una constante. En pa + i. si pa es un puntero. a + i es la direcci´n del i-´simo e e o e elemento de a. Por otra parte. La correspondencia entre indexaci´n y aritm´tica de punteros es evideno e temente muy estrecha. no una variable. Al aplicar & a los dos lados de esta equivalencia se deduce tambi´n e que &a[i] y a + i tambi´n son id´nticas. El efecto es que el nombre de un array es una expresi´n de tipo puntero. o Sin embargo. incluso en la misma proposici´n. por lo o que el nombre de un array como argumento es un puntero. Cuando se pasa el nombre de un array a una funci´n. hay una diferencia entre el nombre de un array y un puntero. Este hecho tiene algunas implicaciones o muy utiles. Y por extensi´n. se pasa la direcci´n o o del comienzo del array. este argumento es una variable. o sea una variable o e que contiene una direcci´n. una referencia a a[i] se puede escribir tambi´n como e ∗(a + i). que hay que tener en cuenta. El compilador convierte toda referencia a un array en un puntero al comienzo del arreglo. Podemos ver ´sto en el siguiente ejemplo: strlen(s) /* regresa la longitud de la cadena s */ . Un puntero es una variable. la asignaci´n pa=&a[0] se puede escribir as´ ı pa=a Del mismo modo. toda la aritm´tica de punteros o e establece que el incremento se adec´a al tama˜o en memoria del objeto u n apuntado. por lo que operaciones como pa = a y pa + + son correctas.

puesto que es un puntero. no hace m´s que incrementar la copia e a privada de la direcci´n. return n. son completamente equivalentes.2 · Punteros y funciones: paso de argumentos de referencia haciendo uso de punteros143 char *s. y f(a+2).8. la declaraci´n de los argumentos podr´ ser o ıa . la forma como debe escribirse depende del tipo de expresiones en las que aparece. Por ejemplo si a es un array f(&a[2]). *s != ’\0’. for (n = 0. { int n. y char *s. la funci´n puede establecer a su conveniencia si va a o o manejar un array o un puntero. pasando un e o puntero al comienzo del subarreglo. s++) n++. Cuando se pasa el nombre de un array a una funci´n. o Las definiciones de par´metros a char s[]. Tambi´n es posible pasar parte de un arreglo a una funci´n. } Es perfectamente legal incrementar s. y hacer las manipulaciones de acuerdo con ´sto. ya que &a[2] y a + 2 o o son expresiones de tipo puntero que apuntan al tercer elemento de a. s + + no modifica la cadena de caract´res s. En f . Incluso puede emplear los dos tipos de operaciones si le parece claro y e conveniente. pasan a la funci´n f la direcci´n del elemento a[2].

. la memoria administrada por alloc y f ree es una pila. Las rutinas son rudimentarias. se emplear´ un puntero o a al primer elemento libre. arrays y aritm´tica de direcciones o e es una de sus principales virtudes.. ninguna otra rutina necesita conocer el nombre del array. el array no necesita e a nombre. Este array es exclusivo de alloc y f ree. muchas a aplicaciones s´lo necesitan una sencilla funci´n alloc que les proporcione peo o que˜as zonas de memoria de tama˜o impredecible y en instantes tambi´n n n e impredecibles. { . escribiendo un rudimentario administrador de memoria. Sin embargo. o estructura ultima-en-entrar.. Veremos a continuaci´n algunas de sus o propiedades. por lo que se declara como externo y est´tico. para poder ser utilizada posteriormente. Algebra de punteros El lenguaje C es consistente y formal en su manejo de la aritm´tica de e direcciones. en el sentido de que las llamadas a f ree deben hacerse en orden inverso a las llamadas a alloc. } 8 · El lenguaje de programaci´n C (IV) o 8. en lugar de ello se puede obtener pidiendo al sistema operativo una zona libre de memoria. } o bien f(arr) int *arr. La biblioteca est´ndar de C tiene funcio´ a nes an´logas que no tienen las restricciones anteriores. primera-en-salir. local al archivo fuente que contenga alloc a y f ree e invisible fuera de ´l. En aplicaciones pr´cticas. es decir. { . La realizaci´n m´s sencilla consiste en que alloc devuelve trozos de un o a gran array de caracteres que llamaremos allocbuf .3. Puesto que se trabaja con punteros y no con sub´ ındices. llamado allocp.144 f(arr) int arr[]. En cuanto a la manipulaci´n del array allocbuf . Hay dos rutinas: alloc(n) devuelve un puntero p a n caracteres consecutivos. f ree(p) libera la memoria adquirida de esa manera. Cuando se le piden a alloc n ca- .. Es decir. la integraci´n de punteros.

si p se encuentra en allocbuf . define allocp como un puntero a caracteres y lo inicializa para que apunte a allocbuf. La declaraci´n o static char *allocp = allocbuf. Esto se pod´ ıa haber hecho escribiendo static char *allocp = &allocbuf[0].n). /* p anterior */ } else /* no hubo espacio suficiente */ return (NULL). aunque normalmente los valores significativos son NULL o una expresi´n en que aparezcan direcciones de objetos del tipo apropiado definidas o previamente. f ree(p) le da el valor p a allocp. } En general un apuntador se puede inicializar como cualquier otra variable.8. se comprueba si hay espacio suficiente en allocbuf (el comienzo de un bloque libre) y se incrementa su valor en n para que apunte a la nueva zona libre. #define NULL 0 /* valor del puntero para devolver errores */ #define ALLOCSIZE 1000 /* tama~o del area disponible */ n static char allocbuf[ALLOCSIZE]. /* espacio para alloc */ static char *allocp = allocbuf.3 · Algebra de punteros 145 racteres. { if (allocp + n <= allocbuf+ALLOCSIZE){ /* se encontr´ */ o allocp += n. ya que el nombre del array es la direcci´n del elemento cero. { if(p >= allocbuf && p < allocbuf+ ALLOCSIZE) allocp = p. o La pregunta . /* siguiente posici´n libre */ o char *alloc(n) /* devuelve puntero a n caracteres */ int n. return (allocp . que es la zona libre cuando comienza el programa. } free(p) /* p apunta al area libre */ ´ char *p.

Si la petici´n se puede satisfacer. Tambi´n se pueden utilizar relaciones como == y ! =. Preguntas como las aparecidas en las sentencias if . >. el nuevo valor de allocp ser´ como m´ximo una posici´n ıa a o m´s all´ del fin de allocbuf . tambi´n se pueden restar: p − q es el n´mero de elementos e u entre p y q. return (p-s). Los punteros se e pueden comparar en ciertas circunstancias. La construca ci´n o p + n significa el n-´simo objeto a partir del que apunta p. Se escribe NULL en lugar de cero. Adem´s se pueden sumar o restar un puntero y un entero. apunta al primer car´cter. Esto es cierto ine dependientemente del objeto al que apunte p. Puesto o a . =. } */ En esta versi´n p se inicializa con s. El lenguaje C garantiza que un apuntador que apunte a datos v´lidos a nunca tendr´ valor cero. alloc debe devolver alguna indicaci´n de o que no tiene espacio. es decir. etc. para indicar que es un valor especial para punteros. { char *p = s. while (*p != ’\0’) p++.146 8 · El lenguaje de programaci´n C (IV) o if (allocp + n <= allocbuf+ALLOCSIZE) comprueba si hay espacio suficiente para satisfacer la petici´n de n cao racteres. se pueden utilizar relaciones como <. indica que no hay espacio. En caso contrario. o a En la iteraci´n while se examina cada car´cter hasta encontrar el \0. (Esto puede funcionar en ala gunas m´quinas. del ejemplo anterior. Se puede emplear para se˜alar una condici´n anora n o mal: en este caso. Si p y q apuntan a miembros del mismo array. el cero es un caso especial. No se pueden comparar punteros a distintos arreglos. Si p y q apuntan a miembros de un mismo array. Todo puntero e se puede comparar con igualdad o desigualdad con el valor NULL. si lo hay. alloc devuelve a a o un apuntador. lo que puede hacer que nuestro programa deje de funcionar misteriosamente cuando lo trasladamos de maquina). muestran diferentes facetas de la aritm´tica de punteros. Esto nos permite escribir otra versi´n de la funci´n strlen: o o strlen(s) /* regresa la longitud de la cadena s char *s. En general no tiene sentido asignar enteros a punteros.

t) /* copia t a s char *s. La versi´n con arrays es: o strcpy(s. dividirlos. Son punteros que recorren el array car´cter a car´cter a a .8. que e ocupan m´s memoria que los char y si p fuese un puntero a float. o o o se puede omitir la comprobaci´n expl´ o ıcita. La primera strcpy(s. etc. ıa No se pueden hacer m´s operaciones con punteros de las se˜aladas aqu´ a n ı. a u es decir la longitud de la cadena.t) /* copia t a s char s[]. sumarles valores float o double. Como p apunta a caracteres. *t. { while((*s = *t) != ’\0’) { s++. y escribir la iteraci´n como o while (*p) p++. string. } */ Mientras que la versi´n con punteros es: o strcpy(s. i = 0. t++. No se permite sumar dos punteros. Si estuvi´semos apuntando a float.4. strcpy puede usar s y t con absoluta libertad. p + + actualiza p para que apunte al siguiente car´cter cada vez. p + + se a posicionar´ en el siguiente float. } } */ Como los argumentos se pasan por valor. 8. multiplicarlos. { int i. t[]. y p − s indica el n´mero de caracteres tratados. while((s[i] = t[i]) != ’\0’) i++.4 · Punteros a caracteres 147 que \0 es cero y la iteraci´n while comprueba s´lo si la expresi´n es cero. t) que copia la cadena t en la cadena s.h. Punteros a caracteres Vamos a analizar inicialmente dos funciones de la biblioteca est´ndar de a C.

148

8 · El lenguaje de programaci´n C (IV) o

hasta que el \0 con que termina t se ha copiado a s. La tercera versi´n m´s o a compacta es:
strcpy(s,t) /* copia t a s char *s, *t; { while(*s++ = *t++) ; } */

Por una parte se ha introducido los incrementos de s y de t en la parte de comprobaci´n de la iteraci´n. El valor de ∗t++ es el car´cter al que apuntaba o o a t antes de ser incrementado; el operador sufijo ++ no se aplica hasta que se ha obtenido ese car´cter. De la misma manera el caracter se almacena en la a antigua posici´n donde apuntaba s antes de ser incrementado. Este car´cter o a es tambi´n el valor que se compara con \0 para controlar la iteraci´n. El e o efecto neto es la copia de los caracteres de t a s hasta copiar el \0 final. Por otra parte se ha eliminado la comparaci´n expl´ o ıcita con \0 por resultar redundante. La segunda rutina que vamos a examinar es strcmp(s, t), que compara las cadenas de caracteres s y t, y devuelve un valor entero negativo, cero o positivo, seg´n que s sea lexicogr´ficamente menor, igual o mayor que t. El u a valor se obtiene al restar los caracteres de la primera posici´n donde s y t o difieren.
strcmp(s,t) /* devuelve <0 si s<t, =0 si s=t, >0 si s>t */ char s[], t[]; { int i; i = 0; while(s[i] == t[i]) if(s[i++] == ’\0’) return 0; return(s[i]-t[i]); }

La versi´n con punteros es: o
strcmp(s,t) /* devuelve <0 si s<t, =0 si s=t, >0 si s>t */ char *s, *t; { for( ; *s == *t; s++, t++ ) if(*s == ’\0’) return 0; return(*s - *t); }

8.5 · Ejemplos de utilizaci´n de punteros o

149

Generalmente se ha visto que en la mayor parte de las computadoras se puede asignar un puntero a un entero y viceversa, sin que se altere el puntero. Lamentablemente esto ha conducido a abusar de rutinas que devuelven punteros que pasan posteriormente a otras rutinas. En ocasiones se omiten declaraciones, de manera que el c´digo resultante sigue funcionano do aunque arriesgadamente, puesto que su funcionamiento depende de la particular arquitectura del ordenador empleado.

8.5.

Ejemplos de utilizaci´n de punteros o

Vamos a considerar como primer ejemplo, el problema de la conversi´n o de la fecha, del d´ del mes a d´ del a˜o y viceversa. Por ejemplo, el 1 de ıa ıa n Marzo es el d´ 60 en un a˜o no bisiesto, y el 61 de uno bisiesto. Veremos ıa n a o dos funciones: day of year, que realizar´ la primera operaci´n y month day, que convertir´ un d´ del a˜o, en mes y d´ a ıa n ıa. Ambas funciones necesitan de la misma informaci´n inicial, una tabla o con los d´ que tiene cada mes (treinta d´ tiene Junio, Septiembre,...). ıas ıas Puesto que el n´mero de d´ del mes de Febrero var´ entre a˜os bisiestos u ıas ıa n y no bisiestos, mantendremos dicha informaci´n en el siguiente array de dos o filas:

static int day_tab[2][13]={ {0,31,28,31,30,31,30,31,31,30,31,30,31}, {0,31,29,31,30,31,30,31,31,30,31,30,31} }; La primera funci´n ser´: o a
day_of_year(year, month, day) /* calcula el d´a del a~o ı n * a partir de mes y d´a */ ı int year, month, day; { int i, bisiesto; bisiesto= year % 4==0 && year % 100 != 0 || for(i=1; i < month ; i++) day += day_tab[bisiesto][i]; return (day); } month_day(year, yearday, pmonth, pday) /* calcula mes, d´a ı year % 400 == 0;

150

8 · El lenguaje de programaci´n C (IV) o

* a partir del d´a y a~o */ ı n int year, yearday, *pmonth, *pday; { int i, bisiesto; bisiesto= year % 4==0 && year % 100 != 0 || year % 400 == 0; for(i=1; yearday > day_tab[bisiesto][i]; i++) yearday -= day_tab[bisiesto][i]; *pmonth= i; *pday=yearday; }

El array day tab ha de ser externo a ambas funciones para poder ser utilizado por ambas. day tab es el primer array bidimensional con el que trabajamos. En realidad, en C, un array bidimensional es por definici´n un array unidimensional, o en el que cada elemento vuelve a ser un array. Los sub´ ındices se escriben como day_tab[i][j] en lugar de day_tab[i,j] empleado en la mayor´ de los dem´s lenguajes. Por otra parte, los eleıa a mentos se almacenan por filas, es decir, el sub´ ındice m´s a la derecha var´ a ıa m´s r´pidamente cuando se accede a los elementos por orden de almacenaa a miento. La inicializaci´n se realiza listando los valores entre llaves. Cada fila o se inicializa mediante la correspondiente sublista. En este caso se ha comenzado con una columna de ceros, para que los ´ ındices correspondientes a los distintos meses var´ entre 1 y 12. ıen En el caso de tener que transferir un array a una funci´n, la declaraci´n o o del argumento en la funci´n deber´ incluir el n´mero de columnas. El n´mero o a u u de filas es irrelevante, pudi´ndose utilizar un apuntador para declarar este e dato. Son posibles las siguientes declaraciones de f :
f(day_tab) int day_tab[2][13]; { ... }

8.5 · Ejemplos de utilizaci´n de punteros o O bien: int day_tab[][13]; o tambi´n e int (*day_tab)[13];

151

que indica que el argumento es un apuntador a un array de 13 enteros. Los par´ntesis son necesarios puesto que los corchetes [] tienen mayor e o e o precedencia que el operador de indirecci´n *. Sin par´ntesis, la declaraci´n int *day_tab[13]; declara un array de 13 apuntadores a enteros, como veremos en el siguiente ejemplo. Vamos a ver ahora un ejemplo de un programa que ordene un conjunto de l´ ıneas de texto en orden alfab´tico, versi´n reducida de la utiler´ sort de e o ıa UNIX. La dificultad del ejemplo estriba en el hecho de trabajar con l´ ıneas de texto, de longitud variable, que exigen ser comparadas y movidas en m´s de a una s´la operaci´n, a diferencia de lo que sucede con un n´mero entero. Se o o u necesita por lo tanto una representaci´n eficiente de los datos, que funcione o correctamente con l´ ıneas de texto de longitud variable. Utilizaremos en este caso un array de punteros. Si las l´ ıneas a ordenar se almacenan una tras otra en un gran array de caracteres, se podr´ acceder a a cada l´ ınea a trav´s de un puntero a su primer car´cter. Los punteros pueden e a almacenarse a su vez en un array. Se pueden comparar dos l´ ıneas con s´lo o pasar sus punteros a strcmp. Cuando tengamos que intercambiar dos l´ ıneas desordenadas, se intercambiar´n los punteros en el arreglo y no las l´ a ıneas. Como siempre es mejor dividir el programa principal en funciones que sigan esta divisi´n y que la rutina principal controle las cosas. o Si comenzamos pensando en la entrada de datos, la rutina de entrada ha de recoger y guardar los caracteres de cada l´ ınea y construir un array de punteros a las l´ ıneas. Tambi´n tiene que contar el n´mero de l´ e u ıneas de entrada, pues esta informaci´n ser´ necesaria para el proceso de ordenaci´n o a o

152

8 · El lenguaje de programaci´n C (IV) o

y el de salida. La funci´n de entrada devolver´ un -1, si existen demasiadas o a l´ ıneas. La funci´n de salida s´lo tiene que imprimir las l´ o o ıneas en el orden en el que aparecen los correspondientes punteros.

#define NULL 0 #define MAXLINES 100 /* n´mero m´ximo de l´neas a ordenar */ u a ı main() /* ordena las l´neas */ ı { char *lineptr[MAXLINES]; /* punteros a las l´neas de texto */ ı int nlines; /* n´mero de l´neas le´das */ u ı ı if((nlines = readlines (lineptr, MAXLINES)) >= 0) { sort(lineptr, nlines); writelines(lineptr, nlines); } else printf(" Demasiadas l´neas a ordenar\n "); ı }

Veamos las definiciones de las funciones utilizadas
#define MAXLEN 1000

readlines (lineptr, maxlines) /* lee l´neas de entrada */ ı char *lineptr[]; int maxlines; { int len, nlines; char *p, *alloc(), line[MAXLEN]; nlines=0; while((len=getline(line, MAXLEN)) > 0) if(nlines >= maxlines) return -1; else if((p=alloc(len) == NULL) return -1; else{ line[len-1] = ’\0’; /* quita el car´cter nueva l´nea */ a ı strcpy(p, line); lineptr[nlines++]=p; } return nlines; }

El car´cter nueva l´ a ınea al final de cada l´ ınea se elimina, pues no afecta a la forma de ordenar.

8.5 · Ejemplos de utilizaci´n de punteros o
writelines(lineptr, nlines) /* escribe l´neas ı char *lineptr[]; int nlines; { int i; for(i = 0; i < nlines; i++) printf("%s \n", lineptr[i]); } */

153

la novedad es la declaraci´n de lineptr. o
char *lineptr[LINES];

indica que lineptr es un array de LINES elementos, cada uno de los cuales es un puntero a un car´cter y ∗lineptr[i] tiene acceso a un car´cter. a a Una vez que la entrada y salida est´n controladas, podemos proceder a a su ordenaci´n. El m´todo elegido en este caso, no ha sido desarrollado o e previamente en teor´ se denomina Shellsort. ıa,

sort(v,n) /* ordena las cadenas v[0],...v[n-1] * * en orden creciente */ char *v[]; int n; { int gap, i,j; char *temp; for( gap = n/2; gap > 0; gap /= 2) for(i = gap; i < n; i++) for(j = i - gap; j >= 0; j -=gap){ if(strcmp(v[j], v[j+gap]) <= 0) break; temp=v[j]; v[j]=v[j+gap]; v[j+gap]=temp; } }

Debido a que los elementos de v son punteros, tambi´n debe serlo temp. e Podr´ mejorarse el programa sustituyendo el m´todo de ordenaci´n Shell ıa e o por ejemplo por Quicksort. Int´ntalo como ejercicio. e El programa anterior ha sido implementado, junto con las funciones readlines, writelines, sort, getline y alloc. El resultado es el fichero siguiente:
#include<stdio.h>

154
#include<string.h>

8 · El lenguaje de programaci´n C (IV) o

#define NULL 0 #define MAXLINES 100 /*numero maximo de lineas a ordenar */ #define MAXLEN 1000 main(){ char *lineptr[MAXLINES]; int nlines; /* numero de lineas leidas */ if((nlines=readlines(lineptr, MAXLINES))>=0) { sort(lineptr, nlines); writelines(lineptr, nlines); } else printf("Demasiadas lineas a ordenar\n "); } readlines(lineptr, maxlines) /* lee las lineas de la entrada */ char *lineptr[]; int maxlines; { int len, nlines; char *p, *alloc(), line[MAXLEN]; nlines=0; while((len=getline(line,MAXLEN))>0) if(nlines >= maxlines) return (-1); else if ((p=alloc(len)) == NULL) return (-1); else{ line[len-1]=’\0’; /* quita el caracter nueva linea */ strcpy(p,line); lineptr[nlines++]=p; } return (nlines); } writelines(lineptr,nlines) char *lineptr[]; int nlines; { int i; for (i=0; i < nlines; i++ ) printf("%s\n", lineptr[i]); } /*escribe lineas */

sort(v,n) /*ordena las cadenas v[0],...v[n-1] en orden creciente */ char *v[]; int n; { int gap,i,j; char *temp;

8.5 · Ejemplos de utilizaci´n de punteros o
for(gap=n/2; gap>0; gap/=2) for(i=gap; i<n; i++) for(j=i-gap; j>=0; j-=gap){ if(strcmp(v[j],v[j+gap])<=0) break; temp=v[j]; v[j]=v[j+gap]; v[j+gap]=temp; } } getline(s,lim) /* para la cadena s da su longitud */ char s[]; int lim; { int c,i; i=0; while(--lim>0 && (c=getchar())!= EOF && c!= ’\n’) s[i++]=c; if(c==’\n’) s[i++]=c; s[i]=’\0’; return(i); } #define ALLOCSIZE 1000 /*tama~o del area disponible */ n static char allocbuf[ALLOCSIZE]; static char *allocp=allocbuf; char *alloc(n) /*devuelve apuntador a cadena de caracteres */ int n; { if(allocp+n<=allocbuf+ALLOCSIZE){ allocp+=n; return(allocp-n); } else return(NULL); }

155

Observaciones sobre E/S La entrada y salida del programa anterior est´n sin especificar. Una a opci´n a utilizar desde el sistema operativo, es direccionar la entrada desde o un fichero, por ejemplo lineas.txt. Para ello basta teclear $ a.out<lineas.txt Si adem´s queremos que la salida se escriba en un fichero, basta direca cionar la salida del programa a dicho fichero:

h> #include<string.txt */ #include<stdio. while((lineptr[i] = fgets(&linea[i][0]. aparecen los nombres de los alumnos de M´todos Num´ricos del curso 98-99. i=1.txt>salida.txt". int c.fp)) != NULL && (i<MAXLINES)) .156 8 · El lenguaje de programaci´n C (IV) o $ a.n. fresul=fopen("salida.txt Por ejemplo si en lineas. El fichero salida. es la que aparece a continuaci´n: o /* /* /* /* este es el programa en el fichero fordena.txt resultante ser´: e e a Beato Unai Blanco Maria Isabel Cao Rosa Maria Caracena Jose Antonio Fuente Rosa Maria de la Gomez Pedro Alberto Guisasola Ainhoa Herran Belen Juan Mario de Morillo Alberto Nunez David Quintela Monica Rivas Silvia Ruiz Diego Otra forma de hacer lo mismo.c */ ordena alfabeticamente cadenas de caracteres */ que lee del fichero lineas. FILE *fresul.txt */ la salida va al fichero salida. FILE *fp.h> #define MAXLINES 500 #define MAXLENGTH 100 main(){ char *lineptr[MAXLINES].out<lineas. esta vez desde dentro del programa. fp=fopen("lineas.txt".i. "a")."r"). char linea[MAXLINES][MAXLENGTH]. MAXLENGTH.txt.

i++) { fprintf(fresul.i. } return 0.5 · Ejemplos de utilizaci´n de punteros o i++. y conecta la salida est´ndar a de prog1. fprintf(fresul.out y prog2. int n. i++) for (j=i-gap. lineptr[j]=lineptr[j+gap]. Adem´s varios archivos se pueden unir usando por ejemplo la utiler´ a ıa cat de UNIX."\n\n Lineas ordenadas for (i=1. Ello hace innecesario aprender a acceder a los archivos desde .out con la entrada est´ndar de prog2. e o (pipe). j>=0. lineptr[i]). for ( gap=n/2. lineptr[j+gap]=temp.j. temp=lineptr[j].8. } sort(lineptr. Para estos programas. j-=gap){ if(strcmp(lineptr[j]. n=i-1. } } lexicograficamente: \n\n"). gap/=2) for (i=gap. la entrada/salida mediante getchar(). char *temp. } sort(lineptr+1. gap >0. i++) { printf("%s". lineptr[i]). 157 El cambio de la entrada de datos a un programa tambi´n es invisible si la e entrada proviene de la salida de otro programa a trav´s de una interconexi´n. i<=n.out ejecuta los programas prog1. i<n.out |prog2. En este caso.lineptr[j+gap])<=0) break. for(i=1. n). ´ putchar() y printf es totalmente adecuada.out. { int gap. "%s". n) char *lineptr[].out. i<=n. la linea de comando $ prog1. a En la pr´ctica muchos programas leen de un s´lo archivo y escriben en un a o unico archivo.

while((c=getchar())!=EOF) putchar(issuper(c) ? tolower(c) :c ). con un sencillo programa como el siguiente: #include<stdio. Las funciones issuper y tolower son u macros definidas en stdio. el programa lower. resu u pectivamente.h> main() { int c.txt y se ejecuta sobre el fichero unido. que convierten en may´scula y min´sculas.158 dentro del programa.txt | lower. .out> output se unen los ficheros lineas. Con la l´ ınea de comando: $ cat lineas.h. } se traduce la entrada a min´sculas.txt y lineas2. La salida aparece en el archivo output. el car´cter le´ a ıdo.txt lineas2.out. 8 · El lenguaje de programaci´n C (IV) o Por ejemplo.

Tipos de datos definidos por el usuario: estructuras Una estructura es un conjunto de diversas variables. a˜o. n ıa a˜o y nombre del mes. en lugar de tratarlas como unidades independientes. agrupadas bajo un mismo nombre. int year. Estas cinco variables se pueden agrupar bajo una n estructura como la siguiente: struct date { int day. d´ del ıa. Opa cionalmente se puede a˜adir un nombre despu´s de la palabre clave struct n e (como el date en el ejemplo anterior). posiblemente de distinto tipo. char mon_name[4]. int month. Las estructuras ayudan a organizar datos complicados. Una fecha tiene por ejemplo varios componentes. que o no es m´s que un conjunto de declaraciones encerradas entre llaves. int yearday.Cap´ ıtulo 9 El lenguaje de programaci´n C (V) o 9. particularmente en programas grandes. lo que hace m´s eficiente a su manejo. }. d´ mes. A dicho nombre se le denomina nombre de la estructura y se puede emplear en declaraciones posteriores como 159 . La palabra clave struct introduce la declaraci´n de una estructura.1. puesto que permiten considerar como unidad un conjunto de variables relacionadas.

declara la variable d como una estructura de tipo date.. z son declaradas las tres como estructuras del mismo tipo y se reserva la memoria correspondiente. Posteriormente se puede utilizar dicha plantilla. Si la variable estructura es de tipo externa o est´tica.year % 4 == 0 && d. se puede inicializar de la siguiente a forma: struct date d={4 . y. 9 · El lenguaje de programaci´n C (V) o Los elementos o variables citados en una estructura se denominan miembros..year % 100 != 0 || d. "Jul"}.} x. o no se reserva memoria. Por ejemplo.miembro El operador miembro de estructura ”·¸onecta el nombre de la estructura c y el del miembro. en el sentido de que x. Si nos queremos referir a un elemento de la estructura. Un miembro de una estructura. siempre que se puedan distinguir a trav´s del contexto. struct date d.1776. tenemos que utilizar una construcci´n de la forma o nombre_de_estructura. e La llave de cierre que termina la declaraci´n de la lista de miembros o puede ir seguida de una lista de variables struct{. Si la declaraci´n de una estructura no va seguida de una lista de variables. z. y se considera unicamente descrita una plantilla de ´ la estructura. Para dar un valor a la variable bisiesto a partir de la estructura date se har´: a bisiesto=d. una vez constru´ la plantilla de ıda la estructura date. . para definir estructuras del mismo tipo.7 .160 abreviatura de la estructura. la propia estructura y una variable ordinaria pueden tener el mismo nombre. 186. y.year % 400 == 0.

long ss_number. Si declaramos la variable emp como una estructura del mismo tipo: struct person emp. o para convertir a min´scula el primer car´cter del nombre del mes: u a d. Operaciones con estructuras Con una variable declarada como una estructura pueden realizarse las siguientes operaciones: . Un registro de la n´mina o puede ser por ejemplo: struct person{ char name[NAMESIZE].mon_name[0]).2 · Operaciones con estructuras o para comprobar el nombre del mes: if (strcmp (d. 161 Por otra parte las estructuras se pueden anidar.9. }. La estructura persona tiene dos fechas. entonces emp. struct date birthdate. 9. struct date hiredate. long zipcode. El operador de miembro de una estructura es asociativo de izquierda a derecha. "Aug") == 0) .2. double salary. .mon_name. .mon_name[0] = lower(d. /* dimension = tama~o del nombre */ n char address[ADRSIZE].birthdate.month se refiere al mes de nacimiento.

3. day = pd -> day. las estructuras autom´ticas no se pueden inicializar. La otra alternativa es pasar un puntero. a Como ejemplos de uso. i< pd -> month. es decir: struct date hiredate. Asignar una estructura a otra con el operador de asignaci´n. pasaremos las componentes por separado. d. } . i++) day += day_tab[bisiesto][i]. o Por otra parte. o pasarla como argumento a una funci´n. Como no es poo sible pasar una estructura a una funci´n como argumento. d. o 2.day). hay a que tener cuidado con lo que no se puede hacer en C est´ndar. Acceder a uno de sus miembros.month. En general si se desean hacer programas portables. a Si queremos copiar una estructura como una unidad. para que su argumento sea un o puntero y no una lista de variables. hay que modificar la funci´n day of year. day.yearday = day_of_year(d.year. utilizando estructuras.162 9 · El lenguaje de programaci´n C (V) o 1. s´lo a o pueden inicializarse estructuras externas o est´ticas. o Esta ultima operaci´n es posible s´lo en las nuevas versiones de C. o bien pasaremos un puntero a la estructura. { int i. volveremos a escribir las funciones de conversi´n o de fechas que vimos en el Tema 8. pero ´ o o no en el C est´ndar. bisiesto=pd -> year % 4 == 0 && pd -> year % 100 != 0 || pd -> year % 400 == 0. tendremos que utilizar punteros. for (i=1. Si hemos declarado hiredate (fecha de contrato) como una estructura del tipo date. Tomar su direcci´n mediante el operador &. bisiesto. La modificaci´n puede ser: o day_of_year (pd) /* convertir en dia del a~o */ n struct date *pd. d. o La primera alternativa emplea day of year igual que se escribi´ en el Tema 8. return (day).

ya que la precedencia del operador de e miembro de estructura es mayor que la del operador *.2 · Operaciones con estructuras con lo que la llamada a la funci´n ser´ o ıa: d.year hacen falta los par´ntesis. pd -> day > day_tab[bisiesto][i]. Tanto − > como · son asociativos de izquierda a derecha. de manera que p -> q -> memb emp.birthdate).month Podemos modificar en el mismo sentido. La notaci´n o pd− >year sirve para describir el operador de acceso al miembro de una estructura. La declaraci´n o struct date *pd. bisiesto. se ha introducido la notaci´n − > m´s abreviada que la anterior. { int i.yearday = day_of_year(&hiredate).year pero dada la frecuencia con la que se emplean los punteros a estructuras.9. 163 especifica que pd es un puntero a una estructura de tipo date. En este caso apunta al miembro denominado year. for (i=1. i++) pd -> day -= day_tab[bisiesto][i]. En o a (∗pd). la funci´n month day para utio lizar estructuras: month_day (pd) /* convertir a mes y dia */ struct date *pd. el miembro year se puede referenciar mediante (*pd).month equivalen a (p -> q) -> memb (emp. pd -> month =i. } . pd -> day = pd -> yearday.birthdate. bisiesto=pd -> year % 4 == 0 && pd -> year % 100 != 0 || pd -> year % 400 == 0. Puesto que pd apunta a una estructura.

Se deben utilizar par´ntesis para alterar el orden de aplicaci´n de los operadores. Sin embargo el hecho de que los dos arreglos se vayan a manipular en paralelo indica otra posible organizaci´n. o sentido. int *y. En tal programa necesitar´ ıamos un array de caracteres para almacenar los nombres de las distintas palablas claves y otro para contar el n´mero de ocurrencias. a dada la declaraci´n o struct{ int x. no se necesitar´ e par´ntesis ¿verdad? Las estructuras tienen una aplicaci´n bastante util en arrays de variables o ´ relacionadas entre s´ Vamos a analizar un ejemplo de aplicaci´n en este ı. Por ejemplo. la sentencia ++p -> x incrementa x en lugar de p. int keycount. int keycount[nkeys]. (+ + p)− > x incrementa p antes de acceder a x. ya que los par´ntesis impl´ e ıcitos son ++(p− > e o x). mientras que e ´ ıan (p + +)− > x lo incrementa despu´s. e Consid´rese un programa que intenta contar las veces que aparece una palabra clave de C. En este ultimo caso. } *p. Cada palabra clave es en realidad o un par: char *keyword. u Una posibilidad consistir´ en mantener dos arreglos en paralelo keyword ıa y keycount char *keyword[nkeys].164 9 · El lenguaje de programaci´n C (V) o Los operadores de estructuras − > y · junto con los par´ntesis y los core chetes tienen la m´xima precedencia entre todos los operadores. .

0. . {"break". Tambi´n se podr´ agrupar e ıan entre llaves los inicializadores en cada fila. /* . }. que define un array keytab de estructuras de este tipo y le asigna memoria. . Los inicializadores se agrupan por pares. En cuanto al n´mero de u ocurrencias. Otra posible definici´n o es: struct key { char *keyword. 0}. "while". Cada elemento del array es una estructura. int keycount. . .9. . 0. } keytab[nkeys]. Se puede entonces declarar una estructura de la forma struct key { char *keyword. "char". se inicializa en todas ellas a 0. struct key { char *keyword. La estructura keytab puede inicializarse de una vez para siempre con el conjunto de nombres de las palabras clave de C. . {"case". 0}. int keycount. struct key keytab[nkeys]. 0 }. "case". "continue".2 · Operaciones con estructuras 165 y se necesita un array de pares. 0. int keycount. 0. } keytab[]={ "break". 0. */ "unsigned".

mid.166 9 · El lenguaje de programaci´n C (V) o Estas llaves no son necesarias si los inicializadores son variables simples o cadenas de caracteres y si adem´s est´n todos presentes. char word[MAXWORD].keyword). keytab[n]. Se deo fine inicialmente keytab. (Dicha funci´n de b´squeda necesita que u o u las palabras clave en keytab est´n ordenadas en orden creciente.keycount++.keyword)) < 0) high = mid . e #define MAXWORD 20 main() /*cuenta palabras clave de C */ { int n. La dimensi´n del a a o array en el caso de que est´n todos presentes la calcula el compilador y [] se e deja vac´ ıo. t.1. { int low. NKEYS)) >= 0) keytab[n]. else if (cond > 0) low = mid + 1.tab[n-1]*/ char *word. n++) if(keytab[n]. n) /*busca palabra en tab[0]. Escribimos a continuaci´n el programa que cuenta palabras clave. if ((cond = strcmp(word. struct key tab[]. } .. while (low <= high){ mid = (low+high) / 2. keytab. low = 0. } binary(word. int n. high = n-1.MAXWORD))!= EOF) if(t == LETTER) if ((n = binary(word.keycount. cond. keytab[n]. tab[mid]. for (n=0. high.keycount > 0) printf("%4d %s\n".. else return(mid). o lo que es e lo mismo en orden alfab´tico). n < NKEYS. } return(-1). El programa principal lee datos llamando repetidamente a getword que busca en keytab mediante una funci´n que implementa o un algoritmo de b´squeda binaria. while ((t = getword(word. tab.

la funci´n devuelve EOF. Si se alcanza el final ´ a del archivo. t. { int c. n o El n´mero de elementos es u tama˜o de keytab (40) / tama˜o de struct key (2) n n C tiene un operador unitario llamado sizeof que calcula el tama˜o de n cualquier objeto en el momento de la compilaci´n. Dicho objeto puede ser una variable. } .9. int lim. } while (--lim >0) { t = type(c = *w++ =getch()). En nuestro caso. y copia la palabra en su primer argumento word. #define NKEYS (sizeof(keytab) /sizeof(struct key)) /% (20) %/ Vamos a describir ahora la funci´n getword. } } *(w-1) = ’\0’. que devuelve LETTER cada vez que encuentra una palabra. La constante N KEY S es el n´mero de palabras que caben en keytab. pero es m´s c´modo y seguro que esta cantidad se calcule autom´ticamente. break. La expresi´n sizeof (objeto) o o n devuelve un entero igual al tama˜o del objeto especificado. el nombre de un tipo a b´sico como int o double.2 · Operaciones con estructuras 167 La funci´n getword la veremos un poco m´s adelante. if (t != LETTER && t != DIGIT) { ungetch(c). u Esta cantidad puede determinarse al contar las palabras clave en la tabla. Dicha funci´n devuelve la o o constante LET T ER si el s´ ımbolo es una palabra (cadena de letras y d´ ıgitos que comienza con una letra. o el nombre de una estructura. De ella s´lo sabeo a o mos por ahora. return (c). un array o una estructura. return(LETTER). o bien un unico car´cter). a o a puesto que adem´s dicha lista puede variar. Una posibilidad es utilizar el a hecho de que el tama˜o del array se conoce en el momento de la compilaci´n. lim) /* leer la siguiente palabra */ char *w. o getword(w. if(type(c = *w++ = getch()) != LETTER){ *w = ’\0’. u el n´mero de palabras clave se obtiene de la siguiente manera.

Hay que cambiar main y binary. else return c. while ((t = getword(word.MAXWORD))!= EOF) if(t == LETTER) . es v´lida s´lo para el conjunto de o o a o caracteres ASCII. que quedar´ de la siguiente forma: ıan main() /*cuenta palabras clave de C */ { int t. Ejemplos de uso Vamos a volver a escribir un programa en C que cuente palabras clave. else if (c >= ’0’ && c <= ’9’) return DIGIT. *p.168 9 · El lenguaje de programaci´n C (V) o getword utiliza las rutinas getch y ungetch que vimos en el Tema 8. char word[MAXWORD]. Punteros a estructuras y estructuras ligadas. Arboles binarios. a La funci´n type descrita a continuaci´n.3.c. a 9. las a e elecciones obvias son: #define LETTER ’a’ #define DIGIT ’0’ El programa correspondiente est´ implementado y su nombre es cuenta. type(c) /*devuelve el tipo de un caracter ASCII*/ int c. struct key *binary(). Cuando acaba la recolecci´n de una cadena alfanum´rica se llama a la rutina o e ungetch para devolver el ultimo car´cter le´ ´ a ıdo. Esta vez vamos a utilizar punteros en lugar de ´ ındices del array. { if(c >= ’a’ && c <= ’z’ || c >= ’A’ && c <= ’Z’) return LETTER. } Las constantes simb´licas LETTER y DIGIT pueden tener cualquier o valor que no entre en conflicto con un car´cter alfanum´rico o EOF. o getword llama a type para determinar el tipo de car´cter de entrada. que ser´ el siguiente en ser a procesado en la pr´xima llamada.

p->keycount. else return(mid).3 · Punteros a estructuras y estructuras ligadas. en lugar de un entero.tab[n-1]*/ char *word. esta declaraci´n hay que hacerla en main y en binary. int n.1. if ((cond = strcmp(word. struct key *high = &tab[n-1]. p++) if(p -> keycount > 0) printf("%4d %s\n". en caso contrario devuelve NULL. p ->keyword). Ejemplos de uso169 if ((p = binary(word. p < keytab + NKEYS. n) /*busca palabra en tab[0].. for (p = keytab. Adem´s el acceso a los elementos de keytab se hace a trav´s de punteros. struct key *mid. tab. .keycount++. keytab. La declaraci´n de binary o o indica que devuelve un puntero a una estructura de tipo key. a e Esto provoca bastante cambio en binary: el c´lculo del elemento central no a puede hacerse como antes mid = (low+high) 2 ya que la suma de los punteros no produce ning´n tipo de respuesta util u ´ (tampoco dividir por 2). struct key *low = &tab[0]. struct key tab[]. } struct key *binary(word. Si binary o encuentra la palabra. { int cond. devuelve un puntero a ella. NKEYS)) != NULL) p->. } En la nueva versi´n se aprecian varios cambios. mid -> keyword)) < 0) high = mid . } return(NULL). while (low <= high){ mid = low + (high-low) / 2.. El c´lculo hay que cambiarlo a por mid = low + (high−low) 2 que hace que mid apunte al elemento situado en la mitad de low y high. que de hecho es ilegal. Arboles binarios.9. else if (cond > 0) low = mid + 1.

coo locando en su sitio cada palabra que llega. Una forma alternativa ıcil o de escribirlo. tab. ya que n los requerimientos de alineaci´n de los diferentes objetos pueden ocasionar o huecos en la estructura. Esto no es posible hacerlo. Por ultimo hay que comentar algo sobre el formato de un programa. o que es en este caso lo que se hace. p++) Si p es un puntero a una estructura. ıa Lo que s´ podemos utilizar es una estructura de datos denominada ´rbol ı a binario. quiz´ m´s clara puede ser a a struct key * binary(word. p < keytab + NKEYS. puesto que ser´ un procediu ıa miento muy lento. En main se tendr´: a for (p = keytab. Se puede e inicializar un puntero con la direcci´n de un objeto definido previamente. El arbol va a contener un nodo por cada palabra diferente. ´ Cuando una funci´n devuelve un tipo complicado como o struct key *binary(word. Sup´ngase que se de´ o sea contar el n´mero de ocurrencias de todas las palabras de alg´n texto. No se debe suponer que el tama˜o de una n estructura es la suma de los tama˜os de cada uno de los miembros. n) puede ser dif´ encontrar el nombre de la funci´n. por lo que e a n p + + incrementa p en la cantidad adecuada para acceder al siguiente elemento del arreglo de estructuras. ¿C´mo podemos organizar nuestros datos para controlar o eficientemente una lista de palabras arbitrarias? Una soluci´n es mantener en todo momento las palabras ordenadas. tab.170 9 · El lenguaje de programaci´n C (V) o Tambi´n hay que observar los inicializadores de low y high. Tampoco ser´ correcta una u ıa b´squeda lineal de cada palabra encontrada. desplazando las palabras en un array lineal. no se pueden ordenar para utilizar un algoritmo de b´squeda binaria. u u Dado que de entrada no se conoce la lista de palabras. cualquier operaci´n aritm´tica que o e se realice con ´l tendr´ en cuenta el tama˜o de la estructura. pues se emplear´ mucho tiempo. en cada nodo ´ tendremos: . n) Vamos a analizar por ultimo el siguiente ejemplo.

3 · Punteros a estructuras y estructuras ligadas. no como un nodo en s´ ı. de manera que: dado un ´ a nodo. Este proceso de b´squeda es claramente recursivo. y el sub´rbol derecho contiene palabras que a son mayores que la palabra del nodo actual. el sub´rbol izquierdo contiene palabras que son (lexicogr´ficamente) a a menores que la palabra del nodo. La estructura de cada nodo. Para averiguar si una palabra ya est´ en el ´rbol. se comienza comparando dicha palabra con la almacenada a a ız a en el nodo ra´ del ´rbol.9. si la palabra es mayor. pero puede tener uno o u a ninguno. /*hijo izquierdo */ declara lef t como un puntero a un nodo. puede estar definida de la siguiente manera: struct tnode{ /* el nodo*/ char *word. puesto que la b´squeu u da en cualquier nodo se realiza de igual manera en cualquiera de sus hijos. Si coinciden. struct tnode *left. Arboles binarios. Ejemplos de uso171 un puntero a los caracteres de la palabra un contador del n´mero de ocurrencias u un puntero a su hijo izquierdo (que es un nodo) un puntero a su hijo derecho (que tambi´n es un nodo) e Ning´n nodo puede tener m´s de dos hijos. /*hijo derecho */ }. La estructura del arbol est´ definida. y su lugar es el del descendiente que a a no existe. es que la palabra ya est´ en el ´rbol. El c´digo del programa es muy corto ya que utiliza funciones que han sido o definidas anteriormente. Es ilegal que una estructura contenga un elemento que sea ella misma. Dichas funciones son getword que lee la siguiente palabra de la entrada y alloc que obtiene memoria para las palabras. se contin´a la u b´squeda por el sub´rbol izquierdo. /*hijo izquierdo */ struct tnode *right. en este caso se aca a tualiza el contador de ocurrencias. /* puntero a los caracteres */ int count. es investigada u a en el sub´rbol derecho. Si la palabra es menor. Si no existe descendiente en la direcci´n examinada a o es que la palabra no est´ en el ´rbol. . pero la sentencia struct tnode *left.

tree devuelve su puntero para n ´ poder instalarlo en el nodo padre. } main presenta una palabra a comparar comenzando por la ra´ del arbol. } La memoria para crear el nuevo nodo se obtiene llamando a la rutina talloc. w). int t. p->word)) == 0) p->count++. p->count = 1. dicha palabra se compara con la almacenada en el nodo y se prosigue la b´squeda por el sub´rbol derecho o izquierdo mediante u a una llamada recursiva a tree. adaptaci´n de la rutina alloc vista anteriormente. w).172 9 · El lenguaje de programaci´n C (V) o El programa principal lee palabras llamando a getword y las a˜ade al n a ´rbol mediante la funci´n tree que luego veremos: o #define MAXWORD 20 main() /* cuenta frecuencia de palabras*/ { struct tnode *root. p->word = strsave(w). /*palabra repetida */ else if (cond < 0) /* las menores va al izdo.*/ p->left=tree(p->left. esto quiere decir que hay que crear y a˜adir un nodo al arbol. char *w. char *strsave(). se incrementa el contador. *tree(). */ return (p). if(p == NULL) { /* llega una nueva palabra */ p=talloc(). root=NULL. treeprint(root).w) /*introduce w en p o a continuaci´n de p */ o struct tnode *p. p->left = p->right = NULL. int cond. while(( t=getword(word. Si se crea un nodo. struct tnode *tree(p. MAXWORD))!= EOF) if(t == LETTER) root = tree(root. word). char word[MAXWORD]./*las mayores al dcho. Devuelve un puno . mientras a a que si se encuentra un puntero nulo. } else if ((cond = strcmp(w. else p->right=tree(p->right. { struct tnode *talloc(). ız ´ En cada etapa. Pueden darse dos posibilidades. si la palabra coincide con una que est´ en el ´rbol.

a u Antes de acabar con el ejemplo. En el peor de los casos. En C. aunque maneje dife´ rentes tipos de objetos. Pero si un gestor acepta peticiones de apuntadores a char y a struct tnode se plantean dos cuestiones. se imprime a e el sub´rbol izquierdo (todas las palabras menores que la del nodo). Ejemplos de uso173 tero a la zona de memoria donde almacenar el nodo. } } Por ultimo una observaci´n pr´ctica: el ´rbol puede estar desequilibrado ´ o a a o no balanceado. printf("%4d %s\n". los o enteros a menudo suelen ubicarse en una direcci´n par). vamos a realizar una peque˜a reflexi´n n o sobre administradores de memoria. si las o palabras ya est´n en orden. treeprint(p->right). Arboles binarios. { if(p!= NULL) { treeprint(p->left). ser muy alto. a n f reeprint imprime el ´rbol en orden sim´trico. el contador ıa se inicializa y los dos hijos toman el valor NULL. La declaraci´n del tipo en alloc puede ser un inconveniente para cualquier o lenguaje que realice una comprobaci´n de tipos. dado un nodo. un procedimiento es o declarar que alloc devuelva un apuntador a caracteres y forzarlo mediante una declaraci´n encastrada “cast” al tipo deseado. En segundo lugar. p->count. Esta parte de la rutina se ejecuta al alcanzar un extremo del ´rbol para a˜adir un nuevo nodo. y luego el sub´rbol derecho (todas las palabras mayores). En primer lugar ¿c´mo o satisfacer los requerimientos de casi todas las computadoras que imponen restricciones de alineaci´n a diferentes tipos de objetos? (por ejemplo. . La nueva palabra se copia a una zona vac´ mediante strsave. si las palabras no aparecen aleatoriamente y el tiempo de ejecuci´n puede.9. o ¿c´mo se puede expresar el hecho de que alloc devuelva diferentes tipos de o apuntadores?. a treeprint(p) /*imprime el arbol recursivamente */ ´ struct tnode *p. Parece deseable que a lo largo de un programa exista un unico administrador de memoria. el programa realiza una b´squeda lineal. p->word). entonces. o Si p se declara como char *p. el propio a nodo.3 · Punteros a estructuras y estructuras ligadas.

9 · El lenguaje de programaci´n C (V) o lo convierte en un puntero a la estructura tnode.174 (struct tnode *)p. De esta forma la rutina talloc se escribe: .

3 · Punteros a estructuras y estructuras ligadas. } Observar que devuelve un puntero a estructura. Ejemplos de uso175 struct tnode *talloc() { char *alloc(). de la posici´n donde o puede empezar a ser almacenada una estructura de ese tama˜o. return((struct tnode *)alloc(sizeof(struct tnode))). n .9. Arboles binarios.

176 9 · El lenguaje de programaci´n C (V) o .

Esto puede escribirse diciendo que rang(A. o ´ Si sucede que rang(A) = r < n. entonces R(A) = Rn y el sistema puede resolverse para cualquier b. Si b = 0 el sistema o se dice homog´neo. el sistema Ax = 0 tiene (n − r) soluciones linealmente independientes. donde R(A) es el subespacio o generado por todas las columnas de A. Si rang(A) = r < n. En este caso la soluci´n es unica y se obtiene como x = A−1 b. 177 . Un sistema homog´neo siempre tiene la soluci´n trivial e e o x = 0. entonces el sistema Ax = b tiene n − r soluciones. Con esta representaci´n.Cap´ ıtulo 10 Soluci´n de sistemas de ecuaciones o lineales Un sistema de ecuaciones lineales con m ecuaciones y n inc´gnitas (n ≤ o m) a11 x1 + ··· am1 x1 + ··· ··· ··· +a1n xn ··· +amn xn = b1 ··· = bm puede escribirse con notaci´n matricial como Ax = b. b) = rang(A) Si m = n = r. El sistema anterior puede escribirse de la forma x1 a·1 + · · · + xn a·n = b que expresa el vector b como una combinaci´n lineal de los vectores columna o de A. se puede ver que la condici´n para que un o o sistema lineal tenga soluci´n es que b ∈ R(A).

dan o u buenas soluciones con pocas operaciones elementales (no hay casi error de redondeo) y trabajan. son especialmente sencillos de resolver. .. entonces las inc´gnitas pueden ser despejadas y o obtenidas en el orden xn . lo que permite que sean aplicados a sistemas de dimensi´n mayor que o un m´todo directo. e a 10. bn unn bn−1 − un−1n xn xn−1 = un−1. En este cap´ ıtulo y en el siguiente. En general. n.n−1 ··· ··· xn = . Dichos m´todos propora e e cionan una sucesi´n de soluciones aproximadas que converge a la verdadera o soluci´n cuando el n´mero de iteraciones tiende a infinito..1.n xn unn xn = b1 ··· = bn−1 = bn Si uii = 0.n−1 xn−1 + u1n xn ··· un−1.178 10 · Soluci´n de sistemas de ecuaciones lineales o 10. xn−1 .1. nos dedicaremos a comentar algunos de los m´todos directos m´s habituales.1. en este caso de un sistema de ecuao ciones lineales. Consideremos por ejemplo un sistema triangular superior: u11 x1 + ··· ···+ ··· ···+ ··· un−1. ∀i = 1.. principalmente con los elementos no nulos de la matriz. Resultan m´s eficientes para sistemas Ax = b en los que la a matriz A es densa (la mayor´ de sus elementos son no nulos). x1 . Sistemas triangulares Los sistemas en los que la matriz A es triangular. . Sin embarıa go... e La elecci´n entre un m´todo directo y un m´todo iterativo depende noro e e malmente del tama˜o del sistema y de la forma (densa o dispersa) de la n matriz A. si la matriz es dispersa (una gran porci´n de sus elementos son nulos) o suelen ser m´s apropiados los m´todos iterativos. M´todos directos para la resoluci´n de sise o temas de ecuaciones lineales Entenderemos como m´todo directo aqu´l que necesita de un n´mero e e u finito de pasos para obtener la soluci´n..

a partir de la expresi´n: o xi = bi − i−1 k=1 lik xk lii . n De las expresiones anteriores. Adem´s las sumas y multiplicaciones son a n (i − 1) = i=1 n2 n(n − 1) ≈ 2 2 Podemos decir entonces que la complejidad computacional. . podemos eliminar x1 de las (n−1) ultimas ecuaciones restando ai1 de la i-´sima ecuaci´n la primera multiplicada por mi1 = a11 .. . i = 1..10... o 2 10. se deduce que la obtenci´n de las n inc´gnio o tas necesita n divisiones. n. ∀i = 1. Si el sistema fuese triangular inferior. . y entonces resolverlo. 2...2.2 · Eliminaci´n gaussiana o x1 = b1 − u1n xn − · · · − u12 x2 u11 179 sistema que puede ser escrito en forma compacta como: xi = bi − n k=i+1 uik xk uii . donde la matriz A = (aij ) es no singular. para la ob2 tenci´n de las soluciones en un sistema lineal triangular es O( n ).. En este caso el sistema anterior tiene soluci´n unica. en este caso se obtienen sustituyendo hacia adelante. i = n. n e o . Suponiendo los coeficientes en la diagonal prino ıa cipal no nulos. o ´ ´ Si a11 = 0. i = 2. .. lii = 0... 1 Como las inc´gnitas se obtienen en orden decreciente. Eliminaci´n gaussiana o Se trata del m´todo m´s importante de resoluci´n de sistemas de ecuae a o ciones lineales. Consideremos el sistema: a11 x1 + ··· an1 x1 + ··· ··· ··· +a1n xn ··· +ann xn = b1 ··· = bn o Ax = b. las variables.. el algoritmo se o denomina de sustituci´n hacia atr´s. La idea consiste en eliminar inc´gnitas de forma sistem´tica o a hasta que el sistema tenga forma triangular. n − 1.. o a la resoluci´n ser´ similar.

Si todos ellos son no nulos. podemos continuar la eliminaci´n y o despu´s de (n − 1) pasos llegar a: e a(n) xn = b(n) nn n Si escribimos el sistema triangular resultante: a11 x1 + ··· (1) (2) (3) ···+ (2) a22 x2 + ··· ···+ ···+ ··· (n−1) an−1. o podemos eliminar de manera similar x2 de las n − 2 restantes ecuaciones. a33 ... (k) (k) i. Adem´s la descripci´n de la eliminaci´n se simplifica si expresamos b a o o como la ultima columna de A ´ ai.. n. 2. a22 .. .n xn (n) ann xn (1) = b1 (2) = b2 ··· (n−1) = bn−1 (n) = bn (1) Se˜alar que el t´rmino independiente b es transformado de la misma n e forma que las columnas de A.. ......n+1 = bi . . i = 3.n−1 xn−1 + a1n xn (2) a2n xn ··· (n−1) an−1. se denominan elementos de pivotaje o pivotes.. n . Si a22 = 0.180 10 · Soluci´n de sistemas de ecuaciones lineales o Las n − 1 ultimas ecuaciones se pueden escribir despu´s de dicha opera´ e ci´n como: o a22 x2 + ··· (2) an2 x2 + (2) ··· ··· ··· +a2n xn ··· (2) +ann xn (2) (2) (2) = b2 ··· (2) = bn (2) donde los coeficientes: aij bi i = 2. . los coeficientes del sistema resultante aij bi (3) (3) = aij − mi2 a2j = bi − mi2 b2 (2) (2) (2) (2) i = 3. k = 1... n Este es un sistema de (n−1) ecuaciones con (n−1) inc´gnitas.. n Los elementos a11 . Si llamamos mi2 = son: ai2 (2) (2) = aij − mi1 a1j = bi − mi1 b1 (2) a22 .

.. Despu´s de e la eliminaci´n tendremos p sistemas triangulares para resolver. En el paso k los elementos aij con i.1). Axp = bp dichos sistemas pueden ser tratados simult´neamente.. adjuntando bj como a la n + j-´sima columna de A. . n + p. j > k. n + 1 Por otra parte si tenemos distintos sistemas de ecuaciones con la misma matriz A: Ax1 = b1 . o .10. es que el ´ ındice j toma los valores k + 1. k = 1..1) i = k + 1. ... Ax2 = b2 . . · · · ... (k) akk = aij − mik akj (k) (10.. . .. n j = k + 1.... n. son transformados de acuerdo con: mik = aij (k+1) aik (k) (k) .. n − 1.2 · Eliminaci´n gaussiana o 181 Entonces las f´rmulas se pueden resumir como sigue: La eliminaci´n se o o (k) realiza en (n − 1) pasos. La unica diferencia con el algoritmo dado en e ´ (10..

i = k + 1. Si estamos interesados en ver el coste operativo para valores grandes de n. o 10. a menudo es necesario realizar el e . en la columna k que sea distinto de 0. un sistema con p t´rminos indeo e pendientes. puesto que en otro caso.1) se sigue que en el paso k se realizan (n − k) divisiones y (n − k)(n − k + p) multiplicaciones y sumas. De ´sto se sigue que cualquier sistema no singular de o e ecuaciones puede ser reducido a una forma triangular mediante la eliminaci´n gaussiana combinada con un intercambio de filas. n. el trabajo principal del algoritmo recae en la reducci´n del sistema a una forma triangular. Complejidad algor´ ıtmica Hemos visto que el algoritmo de eliminaci´n gaussiana es inoperativo si o (k) alg´n elemento akk = 0.. podemos eliminar el n´mero de divisiones.182 Coste operativo 10 · Soluci´n de sistemas de ecuaciones lineales o Vamos a calcular el n´mero de operaciones aritm´ticas necesario para u e reducir mediante la eliminaci´n gaussiana. las k primeras filas de A ser´ ıan (k) linealmente dependientes y recordemos que A es no singular. Pivotado e total y parcial. u Vamos a ver c´mo remodelar el m´todo anterior para hacer el proo e (k) cedimiento operativo. o Para asegurar la estabilidad num´rica. . a su forma triangular. de manera que el n´mero u u total de operaciones es aproximadamente: n−1 n−1 k=1 (n − k)(n − k + p) = k=1 (n − k)2 + p n−1 (n − k) = k=1 = = = (n − 1)n (n − 1)n(2n − 1) +p = 6 2 n2 (n − 1) n(n − 1) p − + n(n − 1) = 3 6 2 n(n − 1) 1 (3p − 1) 2 n2 (n − 1) + (3p − 1) ≈ n3 + n 3 6 3 6 donde cada operaci´n es en realidad.. Sea ark = 0 el elemento buscado. De las ecuaciones (10.3. 2 n´mero de operaciones necesario para resolver un sistema triangular es n . entonces podemos intercambiar las filas k y r y proceder con la eliminaci´n. La idea es buscar en este caso otro elemento aik . u 2 tenemos que cuando p = 1 y n suficientemente grande. una suma y una multiplicaci´n.. Si se o o separan las sumas de las multiplicaciones el coste operativo var´ Como el ıa. Estabilidad num´rica y su mejora. Dicho elemento tiene que existir.

0001. x2 = −1. x1 = 1 Si resolvemos el sistema con pivotaje.0001.0001x2 + x3 x3 9999x3 =1 =1 = 10000 Si realizamos las sustituci´n hacia atr´s y permitimos tres decimales: o a x3 = 1.1 x1 + x1 + x1 + x2 + 1.0001. es necesario elegir el pivote en el paso k. x2 = 0.0001. de una de las siguientes maneras: Pivotaje parcial . x1 = 0 mientras que si permitimos cuatro decimales: x3 = 1. x1 = 1 y si permitimos cuatro decimales: x3 = 1.3 · Estabilidad num´rica y su mejora. Pivotado total y parcial. cambiando las filas 2 y 3.9999x3 =1 =0 =1 Resolviendo con tres decimales: x3 = 1. x2 = −1. pero es a cercano a 0. el sistema triangular que obtenemos es: x1 + x1 + x1 + x2 + 2x2 + 1. Ejemplo 10.0001x2 + x3 2x3 2x3 =1 =1 =2 ⇒ x1 + x2 + x2 + x3 x3 0. x2 = −1.0001x2 + 2x2 + x3 2x3 2x3 =1 =2 =1 El sistema triangular que resulta es: x1 + x2 + 0. x1 = 1 De este modo para prevenir posibles errores. Complejidad algor´tmica183 e ı intercambio de filas incluso cuando el pivote no es ex´ctamente 0.10.

... k.. .. n − 1 donde mik = aik (k) (k) akk .. . k ≤ i ≤ n a e intercambiar filas k y r   (k) i = 1.. j = k aij =  a(k) − m a(k) j = k + 1... n.......... i = k + 1.184 10 · Soluci´n de sistemas de ecuaciones lineales o Elegir r como el menor entero para el que (k) (k) |ark | = m´x |aik |. n . n. . ∀j    aij   i = k + 1... . k − 1    (k+1) 0 i = k + 1. . j = 1.. n + 1  ij ik kj        k = 1.. .

. n. a El n´mero de comparaciones necesarias es r−1. Complejidad algor´tmica185 e ı Pivotaje total Elegir r y s como los menores enteros para los que (k) (k) |ars | = m´x |aij |. n En cuanto al coste operativo. j = k aij =  a(k) − m a(k) j = k + 1. .. .. hemos visto que en el m´todo de eliminae ci´n gaussiana sin pivotaje para un sistema de ecuaciones.. ∀j   a  ij   i = k + 1... vr−1 } con vr : obtenemos m´x{v1 . k. ... . u ..3 · Estabilidad num´rica y su mejora. a ··· ··· a Comparar m´x{v1 . k ≤ i. .. Pivotado total y parcial. necesita en torno o 3 a n operaciones.. .. ... n.... i = k + 1. de manera que el n´mero total de a operaciones es: n3 n−1 + [(n − k + 1)2 − 1] = 3 k=1 n3 + [n2 + (n − 1)2 + .. .. De manera que el n´mero u u total de operaciones es aproximadamente: n3 n2 n3 n−1 + + (n − k) ≈ 3 3 2 k=1 En el caso del pivotaje total.... vr ) es: a a Comparar v1 con v2 : obtenemos m´x{v1 .. v2 }.. a Comparar m´x{v1 . v2 } con v3 : obtenemos m´x{v1 . j ≤ n a e intercambiar filas k y r y las columnas k y s   (k) i = 1. v2 .. + 22 ] − (n − 1) = 3 2n3 n3 n3 + = ≈ 3 3 3 es decir el n´mero de operaciones se duplica... . en cada paso k hay que realizar la b´squeda u u del m´ximo de (n − k + 1)2 elementos. n + 1  ij ik kj        k = 1. o u a Una forma de determinar el m´ximo de r elementos (v1 . ..10. vr }. j = 1.. 3 En el caso de pivotaje parcial. v3 }.. n − 1 donde mik = aik (k) (k) akk . k − 1    (k+1) 0 i = k + 1.. en cada paso k hay que a˜adir un nuevo n tipo de operaci´n: b´squeda del m´ximo de (n − k + 1) valores..

.. . n a22 De nuevo a los elementos a11 ..186 10 · Soluci´n de sistemas de ecuaciones lineales o 10. Dado el sistema Ax = b. 4.. podemos eliminar x1 de las (n − 1) restantes ecuaciones ai1 restando de la i-´sima ecuaci´n la primera multiplicada por mi1 = a11 . eliminamos x2 de las (n − 1) restantes ecuaciones.. o a11 x1 + ··· an1 x1 + ··· ··· ··· +a1n xn ··· +ann xn = b1 ··· = bn Si a11 = 0.4. restamos de cada ecuaci´n la ´ o segunda multiplicada por: mi2 = ai2 (2) (2) . . llegando despu´s de n o e pasos al sistema: a11 ··· (n+1) (2) (n) x1 a22 ··· (n+1) x2 ··· (n+1) ann xn ··· = b1 (n+1) = b2 ··· (n+1) = bn (n+1) . e o i = 2. restamos de dicha ecuaci´n la o o segunda multiplicada por: m12 = a12 a22 (2) (2) -. -.. Si todos ellos son no nulos. Para eliminarla de la primera ecuaci´n. La variante de Gauss-Jordan Se trata de un m´todo tan eficiente como la eliminaci´n gaussiana y m´s e o a caro en cuanto a coste operativo. Se eliminan inc´gnitas transformando el o sistema en diagonal.. i = 3. Para eliminarla de las (n − 2) ultimas. ann se les denomina pivotes. n.. a22 . . De manera que el sistema resultante es: a11 x1 + a12 x2 + (2) a22 x2 + ··· (2) an2 x2 + ··· ··· ··· ··· +a1n xn (2) +a2n xn ··· (2) +ann xn = b1 (2) = b2 ··· (2) = bn Si a22 = 0.. se realiza la eliminaci´n. A no singular. existe una unica ´ soluci´n.

. n aij (k+1) = 0  a(k) − m a(k)  ij ik kj        (k) bi (k) bi j = 1.. k − 1. n akk Es decir. n.. b(1) = b   (k)   a  ij      k = 1. (n − 1) filas. b A(1) = A.... .10. -. i = k donde mik = i=k i=k aik (k) (k) akk bi (k+1) = − (k) mik bk En cuanto al pivotaje. la eliminaci´n se realiza en n pasos. . i=k j = k.. Adem´s a 1 por cada t´rmino independiente y hay (n − 1). e En total: n [1 + (n − k + 1) + 1](n − 1) = k=1 n = (n − 1) [2 + (n − k + 1)] = k=1 n = (n − 1)[2n + (n − k + 1) = k=1 . n. . Divisiones: 1 por cada fila para calcular el multiplicador mik .. k = 1. . n. Coste operativo En cada paso k: -.. i = k.4 · La variante de Gauss-Jordan que puede resolverse: xk = bk (n+1) (n+1) 187 .. ∀i j = k.. . tanto el parcial como el total se realizan de manera similar al realizado en la eliminaci´n gaussiana.. Multiplicaciones y sumas (contadas como un sola operaci´n): 1 por o cada uno de los (n − k + 1) elementos de cada fila y hay (n − 1) filas.. k = 1. no entre los anteriores.. .. El pivote se busca entre los o elementos de la columna posteriores al elemento considerado... i = k j = k + 1. Si A(1) = A y o (1) = b..

ıan Observaci´n: o Eliminaci´n gaussiana: o n3 3 + n2 n3 2 ¡ 2 + n en la variante de Gauss-Jordan.188 10 · Soluci´n de sistemas de ecuaciones lineales o = 2n(n − 1) + (n − 1) n3 n(n + 1) ≈ 2 2 Faltar´ las n divisiones necesarias para resolver el sistema diagonal. .

que se puede descomponer en dos sistemas triangulares: U x = y. Entonces el sistema Ax = b ⇔ LU x = b. A = LU . con un coste considerable en cuanto al n´mero de operaciones. Ax2 = b2 . u Veremos c´mo evitar ´sto.1 Sea A una matriz nxn. Ly = b 2 1 n2 2 De manera que si conoci´ramos L y U podr´ e ıamos resolver Ax = b. todos los t´rminos independientes no se conocen al principio. La descomposici´n LU o Hemos visto que el m´todo de eliminaci´n gaussiana permite tratar vae o rios sistemas con distintos t´rminos independientes. =n Teorema 11. con 2 operaciones.1.Cap´ ıtulo 11 Soluci´n de sistemas de ecuaciones o lineales sin inversi´n de la matriz de o coeficientes 11. o e Supongamos que tenemos una descomposici´n de A en el producto de o una matriz triangular inferior y otra triangular superior. Sin embargo en muchas e ocasiones. donde b2 es funci´n de x1 o Parece entonces que habr´ que repetir el proceso de eliminaci´n desde ıa o el principio. Poe demos querer resolver por ejemplo: Ax1 = b1 . y denotemos por Ak la matriz de orden kxk formada por la intersecci´n de las k primeras filas y columnas o 189 .

n − 1. i = 1. k = 1. . .19011 · Soluci´n de sistemas de ecuaciones lineales sin inversi´n de la matriz de coeficientes o o ´ de A... n y una unica matriz triangular superior U = (uij ).. entonces existe una unica matriz ´ triangular inferior L = (lij ) con lii = 1... . tal que LU = A. Si det(Ak ) = 0..

Entonces Lk y Uk est´n unicamente determinadas. ´ Se sigue que u y l quedan unicamente determinadas por los sistemas ´ T ı o triangulares: Lk−1 u = b. son no singulares. Para n = 1. por lo tanto A = LU . de manera que exista una transformaci´n LU . Lk−1 u cT . . s´ y s´lo si Uk−1 l = c. det(Ak ) = 0. Descomponemos Ak . Sin embargo si las filas de A se intercambian. entonces la matriz se convierte en triangular (superior). la descomposici´n a11 = 1u11 es o o unica. o En efecto. lo cual concluye la demostraci´n. Finalmente a ´ ukk = akk − lT u. • o Ejemplo 1: Si para alg´n k. Lk y Uk de la siguiente forma: Ak = Ak−1 b akk cT . puede no existir la descomu posici´n LU de A.11. donde LU = l11 0 l21 l22 u11 u12 0 u22 = l11 u11 l11 u12 l21 u11 l21 u12 + l22 u22 = 0 1 1 1 Entonces forz´samente l11 = 0 o u11 = 0. Supongamos que el resultado es cierto para n = k − 1 y lo vamos a ´ probar para n = k. las filas pueden ser reordenadas.1 · La descomposici´n LU o Demostraci´n: o 191 Por inducci´n sobre n. c. Por ejemplo si o A= 0 1 1 1 y suponemos que A = LU . Lk = Lk−1 0 1 lT . lT u =b l Uk−1 = + ukk = akk Por hip´tesis de inducci´n. para cualquier matriz no singular A. l y u son vectores columna con n − 1 componentes: si form´ramos a el producto Lk Uk y lo identificamos con Ak : Lk−1 Uk−1 T = Ak−1 . pero entonces la primera fila o ´ de L o la primera columna de U son 0. y la descomposici´n LU existe trivialmente. Uk = Uk−1 u 0 ukk donde b. Lk−1 y Uk−1 est´n determinadas de manera o o a unica y como det(Ak ) = det(Lk )det(Uk ) = 0. Esto se sigue de o la equivalencia entre la eliminaci´n gaussiana y la descomposici´n LU que o o veremos ahora. lT Uk−1 = cT .

. donde r = m´ − 1. o o o (1) . = aij (n) (j+1) = 0. j).. si i > j..... r.. (i) i≤j a Si aij est´ bajo la diagonal principal.. A(2) . de manera que ın(i aij (k+1) = aij − mik akj (k) (k) Si sumamos estas ecuaciones para k = 1. entonces: aij = . A(n) . r... Podemos pensar en la eliminaci´n como en la generaci´n de una sucesi´n de matrices..19211 · Soluci´n de sistemas de ecuaciones lineales sin inversi´n de la matriz de coeficientes o o Supongamos primero que la matriz A es tal que la eliminaci´n gaussiana o puede ser llevada a cabo sin intercambio de filas ni columnas. donde A(k) = (a(k) ) es la matriz que se obtiene antes A=A ij de realizar la eliminaci´n de los coeficientes correspondientes a la variable o xk :      ···      ···  a11 (1) a12 · · · a1k (2) (2) a22 · · · a2k ··· ··· ··· (k) akk ··· ··· ··· (k) ank (1) (1) ··· ··· ··· ··· ··· ··· a1n (2) a2n ··· (k) akn ··· (k) ann (1)            Consideremos ahora un cierto elemento aij de A durante la eliminaci´n. (i > j). o a e Si aij est´ en la diagonal principal o sobre ´sta (es decir i ≤ j). . si i ≤ j.. ij k=1 ik kj aij =  0 + j mik a(k) . entonces: aij = . i>j o Adem´s los elementos aij son transformados en cada iteraci´n k = a 1. obtenemos: r k=1 r k=1 aij aij (k+1) r (k+1) r = k=1 aij − k=1 r (k) (k) r mik akj ⇔ (k) − k=1 aij (k) = − k=1 r mik akj ⇔ mik akj k=1 (k) aij (r+1) − aij (1) = − (1) expresi´n que puede ser escrita: (aij = aij ) o   a(i) + i−1 m a(k) . k=1 kj (r = i − 1) (r = j) . . = aij (n) (i+1) = aij .. .

... Es importante se˜alar que en este o n . como detL = 1. detA = a11 · · · ann . de manera que es usual el intercambio de filas durante la eliminaci´n. k = 1. en los lugares correspondientes a los elementos aik . . (k) p = m´ ın{i. n Entonces. . (U )kj = (k) akj .. En general: a det(Lk ) = 1 ⇒ det(Ak ) = a11 · · · akk . Adem´s los a (k) A=    a11 ··· ··· an1 ··· ··· ··· ··· · · · a1n ··· ··· ··· ··· · · · ann     ⇒   u11 · · · ··· u1n m21 u22 ··· u2n ··· ··· ··· ··· mn1 · · · mnn−1 unn (1) (n) Adem´s.1 · La descomposici´n LU o o..1. que es la condici´n ı o para la descomposici´n unica en el Teorema 11. (1) (k) k = 1.11. Gr´ficamente. j} Sin embargo. n − 1. debemos guardar los multiplicadores. o Entonces para obtener la matriz L. n: p 193 aij = k=1 mik akj . o donde los elementos no nulos de L y U son: (L)ik = mik .. a        = LU  akk .. i≥k j≥k Conclu´ ımos entonces que los elementos de L. estas ecuaciones son equivalentes a la ecuaci´n A = LU .. s´ y s´lo si det(Ak ) = 0. si definimos mii = 1. i = 1. mik = aik (k) (k) elementos de la diagonal de L no necesitan ser almacenados. la eliminaci´n gaussiana puede ser realizada sin intercambio de o o filas o columnas. son los multiplicadores calculados en la eliminaci´n gaussiana y la matriz U . la descomposici´n o o final de A obtenida mediante eliminaci´n gaussiana. . o ´ Nota: Sabemos que la eliminaci´n gaussiana puede resultar ineso table sin pivotaje parcial..

entonces los elementos ukk .. ukk+1 . i.2. en el paso k se o determinan los elementos de la fila k-´sima de U y de la columna k-´sima e e a ıa de L. mip = lip Esto puede suponer unas n2 operaciones para las n(n + 1) inc´gnitas en o L y U . j). los elementos aij . y en ocasiones da lugar a errores. i > k.19411 · Soluci´n de sistemas de ecuaciones lineales sin inversi´n de la matriz de coeficientes o o caso. k−1 p=1 mip upk j≥k . La ecuaci´n matricial A = LU es equivalente a o r aij = p=1 mip upj .. se realizan ıa o en el mismo orden en la matriz A. p=1 j ≥ k. i>k mik = aik − ukk . y mk+1k . aik = Si hacemos mkk = 1. j > k est´n todav´ e sin intercambiar. Escenas compactas en la eliminaci´n gauso siana: m´todos de Doolittle. pero en el m´todo compacto. mip upk . mnk pueden ser obtenidos en este orden de las expresiones: k−1 ukj = akj − p=1 mkp upj . donde A es la matriz que resulo tar´ si los intercambios realizados en la eliminaci´n. r = m´ ın(i... Crout y Choe leski Cuando resolvemos un sistema de ecuaciones lineales mediante elimina3 ci´n gaussiana tenemos que obtener aproximadamente n resultados intero 3 medios. ukn . 11. .. Incluso para valores peque˜os de n ´sto o n e resulta bastante tedioso. uno para cada operaci´n. Sin embargo. . despu´s de los correspondientes intercambios obtendremos e una descomposici´n LU = A . Para el k-´simo paso utilizamos las siguientes ecuaciones: e k akj = p=1 k mkp upj . Como en la eliminaci´n gaussiana.. es posible ordenar los c´lculos de manera que los elementos de L y U se a obtengan directamente.

. k−1 p=1 mkp upj i = k. En el paso k. k = 2. k + 1. . Probaremos que si la eliminaci´n gaussiana se realiza. . U que la eliminaci´n gaussiana. los elementos transformados forman matrices sim´tricas de orden e (n + 1 − k).. Crout y Choleski195 o e Este se denomina m´todo de Doolittle y da como resultado los mismos e factores L. o ´ste m´todo presenta ventajas computacionales sobre el m´todo de eliminae e e ci´n gaussiana. j = k + 1.. aij = aji . Es o o cierta para k = 1. . 1 ≤ i. entonces: aij = aji . Caso de matrices sim´tricas definidas positivas e e Sea A = (aij ) una matriz sim´trica i. n ukj = akj − mkk . Sin embargo es esencial pensar en ambos m´todos como candie datos a ser combinados con estrategias de pivotaje (al menos parcial). los elementos son transformados de acuerdo con: aij (k+1) = aij − mik akj = aij − (k) (k) (k) (k) a (k) kj akk (k) (k) aik (k) Si la hip´tesis de simetr´ es cierta para alg´ n k: (aij = aji ) o ıa u aij (k+1) = aji (k+1) = aji − mjk aki = aji − (k) (k) (k) (k) a (k) ki akk ajk (k) . n En ambos casos hemos supuesto que no ha habido intercambio de filas (pivotaje). n. sin intercambio de filas ni o columnas (pivotaje).. El m´todo es incluso num´rio e e camente equivalente a la eliminaci´n gaussiana.. . j ≤ n. M´s a dif´ resulta. n. En este caso. realizar pivotaje total en cualquiera de los dos ıcil casos.e. k = 1. sin embargo. las ecuaciones para el paso k e son: k−1 mik = aik − p=1 mip upk ...11.2 · Escenas compactas en la eliminaci´n gaussiana: m´todos de Doolittle. aunque para ordenadores o donde las sumas y los productos pueden ser acumuladas en doble precisi´n. se obtiene el m´todo de Crout.. j ≤ n es decir. (k) (k) k ≤ i... Esta afirmaci´n se puede probar por inducci´n.. o Si en lugar de normalizar de manera que mkk = 1. lo hacemos de manera que los elementos de la diagonal principal de U cumplan ukk = 1..

todos los pivotes de A son positivos s´ y s´lo si A es definida ı o positiva y la eliminaci´n gaussiana est´ bien definida. De manera que la eliminaci´n ıa. Una forma de determinar si una matriz es definida positiva es utilizar el siguiente criterio que enunciamos sin demostraci´n. en una matriz sim´trica. Por otro lado. Sin embargo. Es decir. o Teorema 11.19611 · Soluci´n de sistemas de ecuaciones lineales sin inversi´n de la matriz de coeficientes o o y se obtiene el resultado. o gaussiana anterior no se puede utilizar para cualquier matriz sim´trica. no es siempre posible realizar eliminaci´n gaussiana sin o pivotaje sobre matrices sim´tricas. | | << 1 a veces ´sto no resulta estable. que si la eliminaci´n gaussiana se realiza sin pio votaje. e Para matrices sim´tricas definidas positivas. Hemos visto entonces. la eliminaci´n gaussiana sin e o pivotaje es siempre estable. pero como muestra la matriz 0 1 1 . Esto significa a mentos de A 3 que el n´mero de operaciones realizado es aproximadamente n y se ahorra u 6 aproximadamente la mitad del espacio en memoria.2(Criterio de Sylvester) Una matriz sim´trica de orden e nxn es definida positiva s´ y s´lo si det(Ak ) > 0. k = 1.n. o a . tenemos que computar unicamente los elee ´ (k) . La simetr´ se preserva si elegimos cuale ıa quier elemento de la diagonal principal como pivote y completamos el pivotaje. donde Ak es la ı o matriz kxk formada por las k primeras filas y columnas de A... que est´n en la diagonal principal o sobre ella. el intercambio de filas destruye e la simetr´ lo cual se ve en el mismo ejemplo. .

det(Ak−1 ) k = 2..4 Sea A una matriz sim´trica y definida positiva. donde u11 = a11 > 0 y ukk = det(Ak ) > 0. • Teorema 11. obtenemos: RT R = U T D− 2 D− 2 U = U T D−1 U = LU = A 1 1 1 1 −1 .. n Si introducimos la matriz diagonal D = diag(u11 . . tenemos que A = LU . Para k = 2 y una cierta e permutaci´n. a ´ Como A es sim´trica: e o A = AT = (U )T DLT ´ LT = U = D−1 U Ahora. el Terorema 11. da o 0 < det aii aij aji ajj = aii ajj − (aij )2 de donde se obtiene el resultado.3 Para una matriz sim´trica definida positiva A: e |aij |2 ≤ aii ajj y por lo tanto los mayores elementos de A est´n en la diagonal.. donde D− 2 tiene elementos positivos en su diagonal (ukk2 ).1. .. a Demostraci´n: o Si la misma permutaci´n se aplica a las filas y columnas de A..11. podemos escribir: A = LU = LDD−1 U = LDU . unn ).. si hacemos R = D− 2 U . Demostraci´n: o Del Teorema 11. Entonces e existe una unica matriz triangular superior R con elementos positivos en la ´ diagonal principal tal que A = RT R. U = D−1 U donde L y U son matrices triangulares con unos en la diagonal principal y est´n unicamente determinadas. Crout y Choleski197 o e Teorema 11.2 · Escenas compactas en la eliminaci´n gaussiana: m´todos de Doolittle. la mao triz resultante es sim´trica y definida positiva.2.

k−1 ukk = mkk = (akk − p=1 m2 ) 2 kp . . nos dar´ expresioı. podemos afirmar que la eliminaci´n gaussiana sin pivotaje o es siempre posible y estable en matrices sim´tricas definidas positivas y e adem´s es posible elegir los elementos de la diagonal de L reales y de manera a que U = LT . Luego o n2 2 + 6n operaciones. 2 Total: n2 2 2 2 2 + n2 2 + n2 2 + 6n = 3n2 2 (+6n de las ra´ ıces cuadradas). n 1 uki = mik = aik − k−1 p=1 mip mkp mkk Este m´todo se denomina m´todo de Choleski o de la ra´ cuadrada. En e e ız cuanto a coste operativo: Son necesarias del orden de n operaciones hasta obtener la descompo2 ıces ız sici´n LLT .. • As´ la eliminaci´n compacta en este caso particular.19811 · Soluci´n de sistemas de ecuaciones lineales sin inversi´n de la matriz de coeficientes o o Por lo tanto. (1 ra´ cuadrada ≈ 6 operaciones). LLT x = b LT x = y → sistema triangular inferior ( n operaciones). . 2 Ly = b → sistema triangular superior ( n operaciones). +n ra´ cuadradas.. o ıa nes en las que ukk = mkk y upk = mkp . i = k + 1..

You're Reading a Free Preview

Descarga
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->