You are on page 1of 315

Problemas y Algoritmos

8 3 4 2 9 1 7 6

5
9 7 7 7 9 6 6

Por Luis E. Vargas Azcona Algunas imagenes por Roberto Lpez

Acuerdo de Licencia

Esta obra est bajo una licencia Atribucin-No comercial-Licenciamiento Recproco 2.5 Mxico de Creative Commons. Eres libre de: copiar, distribuir y comunicar pblicamente la obra hacer obras derivadas Bajo las condiciones siguientes:

Atribucin.

Debes reconocer la autora de la obra en los trminos

especicados por el propio autor o licenciante.

No comercial. No puedes utilizar esta obra para nes comerciales. Licenciamiento Recproco. Si alteras, transformas o creas una obra
a partir de esta obra, solo podrs distribuir la obra resultante bajo una licencia igual a sta. Al reutilizar o distribuir la obra, tiene que dejar bien claro los trminos de la licencia de esta obra. Alguna de estas condiciones puede no aplicarse si se obtiene el permiso del titular de los derechos de autor. Nada en esta licencia menoscaba o restringe los derechos morales del autor. Los derechos derivados de usos legtimos u otras limitaciones reconocidas por ley no se ven afectados por lo anterior. Esto es solamente un resumen fcilmente legible del texto legal de la licencia. Para ver una copia de esta licencia, visite http://creativecommons.org/licenses/bync-sa/2.5/mx/ o envie una carta a Creative Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.

Prefacio

El propsito general de este libro, es el de introducir al lector en la resolucin de problemas de programacin as como en el diseo de algoritmos. Si bien se presentan algoritmos, el objetivo no es saturar el conocimiento del lector con una gran cantidad de algoritmos sino mostar sus fundamentos, mostrar tambin maneras inteligentes de usarlos para resolver problemas, y sobre todo, lograr que el lector sea capaz de disear sus propios algoritmos. Muchos libros de algoritmos se limitan a explicar algoritmos sin detenerse en su fundamento matemtico y sin decir las aplicaciones que tiene en solucin de problemas. Algunos otros si explican el fundamento de los algoritmos, pero resultan inadecuados para estudiantes con poco conocimiento matemtico ya que suelen dar por hecho que el lector ya sabe elementos de teora de conjuntos y matemticas discretas. Este libro est dirigido a estudiantes con gusto de programar y resolver problemas pero que todava no adquieren las bases matemticas necesarias para poder leer libros de algoritmos con suciente fundamento matemtico; por lo que se pretende, no solamente explicar los algoritmos, sino tambin darle al lector las herramientas necesarias para entender, analizar y disear. Por ello, una gran cantidad de pginas se dedican a establecer las bases matemticas que sirven como soporte para la resolucin de problemas de programacin y tambin muchas pginas se dedican a explicar de dnde pueden surgir las ideas para disear cada algoritmo. Este libro tambin puede ser leido como un ensayo en el cual doy mi punto de vista acerca de lo que es el estudio de los algoritmos, y cmo los algoritmos y las matemticas discretas se entrelazan de tal manera que un problema de algoritmos nos lleva a las matemticas discretas y a su vez problemas de matemticas discretas nos llevan de regreso a los algoritmos a tal grado que se vuelven imposibles de separar.

Los temas tratados aqu son indispensables para comprender el gran campo de estudio de la solucin de problemas mediante el uso de las computadoras, constituyen los fundamentos de una cantidad interminable de conocimientos y tcnicas que estan en constante desarrollo por la investigacin en las ciencias de la computacin. Otra peculiaridad es que a lo largo del libro se muestran implementaciones en C++ que no usan memoria dinmica ni estructuras, esto es por los siguientes motivos: A veces al tener el pseudocdigo de un algoritmo, no resulta claro cmo implementarlo sin escribir demasiado. Por ello es preferible mostrar algunas implementaciones de ejemplo para que el lector conozca al menos una implementacin corta de cada algoritmo. Muchos elementos del lenguaje C++ son muy conocidos e incluso estan presentes en otros lenguajes con una sintaxis casi idntica a la de C++, por ello, si se utilizan pocos elementos del lenguaje, es posible que un lector que no est familiarizado con este lenguaje pueda igualmente entender los cdigos. No est por dems decir que este libro fue escrito pensando en participantes de un concurso llamado olimpiada de informtica, en ese concurso los participantes tienen tiempo limitado para implementar y depurar sus programas; las implementaciones son mas fciles de depurar con memoria esttica.

Al Estudiante
Puede parecer extrao encontrar un libro de algoritmos con estas caractersticas, ya que dedica muchas pginas a los fundamentos matemticos y omite varias cosas que varios libros de algoritmos orientados a licenciatura no dejaran pasar por alto, como lo son listas doblemente ligadas y tablas de dispersin entre otras cosas. El motivo de esto es simple: este libro no pretende ser una referencia ni mucho menos un repaso de los temas de licenciatura; pretende aportar las herramientas bsicas para aplicar la programacin en la resolucin de problemas. Por esto mismo puede llegar a ser un recurso valioso ya que permite abordar la programacin desde un punto de vista mas creativo y menos repetitivo, y al mismo tiempo el enfoque a resolver nuevos problemas es la base de la inovacin.

quicksort, shellsort, listas circulares,

Este libro esta hecho para leerse casi como si fuera una novela, un captulo tras otro, y si se decide saltarse uno o mas captulos se corre el riesgo de que mas adelante no se pueda seguir bien. La diferencia con una novela es la gran cantidad de problemas de ejemplo, los cuales conviene intentar resolver antes de leer la solucin.

Al Instructor
La teora necesaria para leer este libro de principio a n es muy poca, incluso un alumno de preparatora la debera de saber. Sin embargo, muchos problemas que se mencionan, por su dicultad, estan lejos de ser adecuados para cualquier alumno de preparatoria e incluso pueden darle dicultades a graduados de la universidad; pero resultan bastante adecuados para aquellos que buscan algo mas interesante que problemas de rutina o para quienes se preparan para concursos de programacin. El libro est escrito de la manera en la que me gustara presentar los temas si fuera yo el instructor. Es decir, procura motivar todos los conceptos antes de abordarlos formalmente, intenta nunca dejar

huecos,

en la teora; y sobre todo, buscar

aplicaciones creativas a cada tema que se aborda evitando decir muy rpido la solucin y dando tiempo para especular. Para resolver un problema, es necesario plantearse un dilogo consigo mismo de preguntas y respuestas. Dicho dilogo tambin se debe de presentar entre alumno e instructor y en muchas partes del libro se intenta plasmar el dilogo abordando los razonamientos que podran llevar al lector a resolver cada uno de los ejemplos.

Al Olmpico
Este libro lo escrib pensando principalmente en los olmpicos, por lo que todo lo que se menciona aqu tiene aplicacin directa o indirecta con la olimpiada de informtica. La olimpiada esta centrada en resolver problemas mediante la programacin y el diseo de algoritmos; para ello se requieren bases matemticas slidas y un conocimiento profundo(aunque no necesariamente amplio) de los algoritmos. Es por eso que este libro dedica muchas pginas a dejar claras las bases matemticas y a explorar las propiedades de algoritmos; no slo en introducirlos.

Los ejemplos se deben de intentar resolver por cuenta propia para aprender de los propios errores e ir desarrollando poco a poco un mtodo propio para resolver problemas. Si de pronto no logras resolver un ejemplo, puedes empezar a leer la solucin para darte una idea y continuar por cuenta propia.

ndice general

Recursin

15

1. Induccin Matemtica
1.1. 1.2. 1.3. 1.4. Ejemplos de Induccin Errores Comunes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Denicin de Induccin . . . . . . . . . . . . . . . . . . . . . . Problemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1. 1.4.2. 1.4.3. 1.5. Sumas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19
20 27 28 29 29 30 31 33

Tablero de Ajedrez Chocolate

. . . . . . . . . . . . . . . . . . . . . . . . .

Sugerencias

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

2. Denicin y Caractersticas de la Recursin


2.1. 2.2. 2.3. Factorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Imprimir Nmeros en Binario . . . . . . . . . . . . . . . . . . Los Conejos de Fibonacci . . . . . . . . . . . . . . . . . . . . .

35
35 38 39

3. Recursin con Memoria o Memorizacin


3.1. 3.2. 3.3. 3.4. Mejorando el Rendimiento de Fibonacci Error Comn en la Memorizacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . Triangulo de Pascal . . . . . . . . . . . . . . . . . . . . . . . . Teorema del Binomio . . . . . . . . . . . . . . . . . . . . . . .

43
43 45 45 48

4. Divide y Vencers
4.1. 4.2. 4.3. Mximo en un Arreglo . . . . . . . . . . . . . . . . . . . . . . Bsqueda Binaria . . . . . . . . . . . . . . . . . . . . . . . . . Torres de Hanoi . . . . . . . . . . . . . . . . . . . . . . . . . .

51
52 53 56

10

NDICE GENERAL

5. Bsqueda Exhaustiva
5.1. 5.2. 5.3. Cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conjuntos y Subconjuntos Permutaciones . . . . . . . . . . . . . . . . . . . . . . . . . . .

61
61 65 70

II

Anlisis de Complejidad

75

6. Tcnicas Bsicas de Conteo


6.1. 6.2. 6.3. 6.4. 6.5. Reglas Bsicas de Conteo . . . . . . . . . . . . . . . . . . . . . Conjuntos, Subconjuntos, Multiconjuntos . . . . . . . . . . . . Permutaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . Combinaciones Separadores . . . . . . . . . . . . . . . . . . . . . . . . . .

79
82 83 85 88 89

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

7. Funciones
7.1. 7.2. Las Funciones como Reglas . . . . . . . . . . . . . . . . . . . . El Concepto Formal de Funcin . . . . . . . . . . . . . . . . .

91
91 94

8. Anlisis de Complejidad
8.1. 8.2. 8.3. 8.4. 8.5. 8.6. 8.7. Un ejemplo no muy computacional . . . . . . . . . . . . . . . Algunos Ejemplos de Complejidad en Programas . . . . . . . .

97
98 99

Funcin de Tiempo . . . . . . . . . . . . . . . . . . . . . . . . 101 La Necesidad del Smbolo

O . . . . . . . . . . . . . Denicin de la notacin O -mayscula . . . . . . . Mltiples Complejidades en Notacin O -Mayscula

. . . . . . 102 . . . . . . 105 . . . . . . 108

Cundo Un Algoritmo es Factible y Cundo Buscar Otro . . . 108

9. Reglas para Medir la Complejidad


9.1. 9.2. 9.3. 9.4. 9.5. 9.6. Producto por Constante

111

Regla de la Suma . . . . . . . . . . . . . . . . . . . . . . . . . 111 . . . . . . . . . . . . . . . . . . . . . 114

Regla del Producto . . . . . . . . . . . . . . . . . . . . . . . . 114 Complejidad en Polinomios . . . . . . . . . . . . . . . . . . . . 115 Medir antes de implementar . . . . . . . . . . . . . . . . . . . 116

Bsqueda de Cotas Mayores . . . . . . . . . . . . . . . . . . . 118

10.Complejidades Logartmicas
10.3. Complejidades

121
. . . . . . . . . . . . . . . . . . . . 124

10.1. Anlisis de la Bsqueda Binaria . . . . . . . . . . . . . . . . . 121 10.2. Bases de logaritmos . . . . . . . . . . . . . . . . . . . . . . . . 123

O(N logN )

NDICE GENERAL

11

11.Complejidades en Funciones Recursivas


11.1. Estados 11.2. Cortes

125

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

III

Algoritmos de Ordenamiento I

131

12.Ordenamiento
12.1. Funcin de comparacin 12.2. Conjunto con un rden Total

135
. . . . . . . . . . . . . . . . . . . . . 136 . . . . . . . . . . . . . . . . . . 137

12.3. Algoritmo de Seleccin . . . . . . . . . . . . . . . . . . . . . . 138 12.4. Algoritmo de Insercin . . . . . . . . . . . . . . . . . . . . . . 139 12.5. Anlisis de los Algoritmos de Seleccin e Insercin . . . . . . . 140 12.6. El Odiado Burbuja 12.7. Ordenamiento en . . . . . . . . . . . . . . . . . . . . . . . . 141 por Mezcla . . . . . . . . . . . . 142

O(n log n)

12.8. Problemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 12.8.1. Mediana . . . . . . . . . . . . . . . . . . . . . . . . . . 147

IV

Estructuras de Datos

149

13.Pilas, Colas, Listas


13.3. Listas

153

13.1. Pilas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 13.2. Colas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

13.4. Problemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 13.4.1. Equilibrando Smbolos . . . . . . . . . . . . . . . . . . 162

13.4.2. Pirmides Relevantes . . . . . . . . . . . . . . . . . . . 164

14.rboles Binarios
14.2. Propiedades Bsicas

167
. . . . . . . . . . . . . . . . . . . . . . . 170

14.1. Implementacin . . . . . . . . . . . . . . . . . . . . . . . . . . 169

14.3. Recorridos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 14.4. rboles Binarios de Bsqueda . . . . . . . . . . . . . . . . . . 173 14.5. Encontrar un Elemento en el rbol Binario de Bsqueda . . . 176

14.6. Complejidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 14.7. El Barrido, una Tcnica til . . . . . . . . . . . . . . . . . . . 179 14.8. Problemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 14.8.1. Ancho de un Arbol . . . . . . . . . . . . . . . . . . . . 182 14.8.2. Cuenta rboles . . . . . . . . . . . . . . . . . . . . . . 185

12

NDICE GENERAL

15.Montculos
15.2. Representacin en Memoria

187
. . . . . . . . . . . . . . . . . . . 189

15.1. Qu son los Montculos? . . . . . . . . . . . . . . . . . . . . . 187

15.3. Insercin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 15.4. Eliminacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192

16.Grafos
16.1. Qu son los grafos? 16.2. Propiedades Elementales de los Grafos

195
. . . . . . . . . . . . . . . . . . . . . . . 196 . . . . . . . . . . . . . 198

16.3. Implementacin . . . . . . . . . . . . . . . . . . . . . . . . . . 199 16.4. Recorridos en Grafos 16.5. Conectividad 16.6. rboles . . . . . . . . . . . . . . . . . . . . . . . 201

. . . . . . . . . . . . . . . . . . . . . . . . . . . 205

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 . . . . . . . . . . . . . . . . . . . . . . . 212

16.7. Unin y Pertenencia

16.8. Mejorando el Rendimiento de Unin-Pertenencia . . . . . . . . 214 16.9. Problemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 16.9.1. Una Ciudad Unida . . . . . . . . . . . . . . . . . . . . 218

16.9.2. Abba . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 16.9.3. Cdigos de Prfer . . . . . . . . . . . . . . . . . . . . . 222 16.10. Sugerencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224

Algoritmos de Ordenamiento II

225

17.rboles y Ordenamiento
17.1. Ordenamiento por rboles Binarios de Bsqueda

229
. . . . . . . 229

17.2. Ordenamiento por Montculo . . . . . . . . . . . . . . . . . . . 230

18.Mas rpido que O(N log N)?


18.2. *Bucket-Sort

233

18.1. Count-Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233 . . . . . . . . . . . . . . . . . . . . . . . . . . . 234

18.3. *La Inexistencia de Mejores Algoritmos . . . . . . . . . . . . . 236

VI

Optimizacin Combinatoria

239

19.Estructura de la Solucin y Espacio de Bsqueda


19.2. Juego de Nmeros

241

19.1. Estructura de la Solucin . . . . . . . . . . . . . . . . . . . . . 242 . . . . . . . . . . . . . . . . . . . . . . . . 245

19.3. Empujando Cajas . . . . . . . . . . . . . . . . . . . . . . . . . 253 19.4. Camino Escondido . . . . . . . . . . . . . . . . . . . . . . . . 259

NDICE GENERAL

13

20.Programacin Dinmica en Los Espacios de Bsqueda


20.1. Clculo de Fibonacci 20.3. Primer Teorema del Cmputo de Abajo hacia Arriba

269

. . . . . . . . . . . . . . . . . . . . . . . 269 . . . . . 273

20.2. El Ladrn y la Mochila . . . . . . . . . . . . . . . . . . . . . . 271 20.4. Tringulo de Pascal . . . . . . . . . . . . . . . . . . . . . . . . 275 20.5. La Mochila (0, 1) . . . . . . . . . . . . . . . . . . . . . . . . . 276 20.6. Segundo Teorema del Cmputo de Abajo Hacia Arriba 20.7. Robot Recolector 20.8. Subsecuencia Comn Mxima . . . . 280 . . . . . . . . . . . . . . . . . . . . . . . . . 282 . . . . . . . . . . . . . . . . . . 284

21.Programacin Dinmica en Cortes


21.1. Cortando Cadenas 21.2. Cortando en Cuadrados

287

. . . . . . . . . . . . . . . . . . . . . . . . 288 . . . . . . . . . . . . . . . . . . . . . 292

21.3. Polgono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293

VII

Cmo Resolver Problemas

295

22.Entendiendo el Problema 23.Encontrando la solucin

299 305

23.1. Las Dimensiones del Problema . . . . . . . . . . . . . . . . . . 305 23.2. Dibuja Una o Varias Figuras . . . . . . . . . . . . . . . . . . . 306 23.3. Libro Incompleto y Dnde Continuar Leyendo . . . . . . . . . 307

VIII

Apndices

309

A. Otros Algoritmos y otras Estructuras de Datos B. Bibliografa Recomendada

311 313

14

NDICE GENERAL

Parte I Recursin

15

17

La recursin es uno de los temas mas bsicos en el estudio de los algoritmos. De hecho, muchas veces(no siempre) es el primer tema tratado en lo que se reere a resolver problemas de programacin. Esto de primer tema puede parecer un poco confuso, ya que para llegar a recursin es necesario de antemano ya saber programar. Es primer tema en lo que se reere a resolver problemas, lo cual no trata de saber cmo escribir una solucin en una computadora, trata de saber cmo encontrar soluciones. El conocimiento de un lenguaje de programacin se limita a expresiones lgicas para expresar las ideas en una computadora y eso no es encontrar una solucin sino simplemente saber describir la solucin. Podra parecer para algunos una prdida de tiempo leer acerca de cmo encontrar soluciones a problemas, y pueden tambin estarse preguntando No basta con ser inteligente para poder encontrar la solucin a un problema?. En teora es cierto que cualquiera podra llegar a la solucin de un problema simplemente entendiendo el problema y ponindose a pensar. Sin embargo, en la prctica, pretender resolver un problema de cierto nivel sin haber resuelto nunca antes problemas mas fciles resulta imposible. En el entendimiento claro y la buena aplicacin de la recursin descansan las bases tericas y prcticas de buena parte(casi me atrevera a decir que de la mayora) de los algoritmos que se aprenden mas adelante. Esto no quiere decir que las implementaciones recursivas sean la respuesta para todo ni para la mayora; pero un razonamiento partiendo de una recursin es con mucha frecuencia utilizado para implementar algoritmos no recursivos.

18

Cap tulo

Induccin Matemtica
La induccin matemtica no es mas que el uso de razonamientos recursivos para comprobar cosas. An no hemos denido que quiere decir recursivo, sin embargo el buen entendimiento de la induccin puede ser un buen escaln para la recursin en general. Tambin hay que tomar en cuenta que la induccin es un tema muy importante(y desgraciadamente muy subestimado) para el entendimiento correcto de los algoritmos y en algn momento es conveniente tratarlo, por ello comenzaremos con el uso y la denicin de la induccin matemtica, que es algo bastante simple, para luego generalizarlo en la recursin. En el momento de resolver problemas, es muy importante observar propiedades, y si no se est seguro si una propiedad es cierta, la induccin puede servir ms de lo que se pueda imaginar. Es importante mencionar que la induccin sirve para probar

ciones, es decir, enunciados que declaran que algo es verdadero o falso(nunca


ambas cosas a la vez ni tampoco un trmino medio). En todo el libro trabajaremos con proposiciones, ya que es la forma mas simple de pensar y de hacer buenos razonamientos sin tener duda alguna al respecto. Empezaremos probando un ejemplo sencillo, luego pasaremos a ejemplos mas complejos para posteriormente denir formalmente la induccin matemtica. Tambin, aprovecharemos esta seccin para poner como ejemplos algunas propiedades que luego sern muy tiles en el momento de resolver algunos problemas. Antes de continuar es importante mencionar que en este libro se utilizarn puntos suspensivos(...) en algunas frmulas. Estos puntos suspensivos deben de completar la frmula con el patrn

proposi-

19

20

CAPTULO 1. INDUCCIN MATEMTICA

que se muestra. Por ejemplo:

1 + 2 + 3 + ... + 9 + 10

signica

1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10

1.1. Ejemplos de Induccin


Problema Resuelto 1.1.1.
Prueba que para todo entero

n1

1 + 2 + 3 + 4 + 5 + ... + n 1 + n =

(n)(n + 1) 2

Si analizamos un poco ,la proposicin  para todo entero n +1) , quiere decir que dicha 1, 1 + 2 + 3 + 4 + 5 + ... + n 1 + n = (n)(n 2 propiedad debe ser cierta para n = 1, para n = 2, para n = 3, para n = 4, para

Solucin

n = 5,

etc...

Podemos comenzar vericando que es cierto para los primeros enteros positivos:

(1)(1 + 1) =1 2 (2)(2 + 1) =3=1+2 2 (3)(3 + 1) =6=1+2+3 2 ...


Esta claro que si tratamos de aplicar este procedimiento para todos los nmeros enteros positivos nunca acabaramos a menos que encontrramos un nmero en el que no se cumpliera la proposicin(lo cual no podemos asegurar). Aqu muchos se sentiran tentados a pensar

en todos los casos que veriqu. Esa no es la salida correcta, y no slo es un


capricho terico, ya que en la prctica se pueden cometer errores bastante serios si ingenuamente se cree que vericar varios casos pequeos es suciente para estar seguro de algo. Por ejempo, la proposicin  x

esto es cierto porque funciona

+ x + 41

es primo cuando

es entero

positivo puede resultar engaosa, ya que si vericamos con varios valores de

x,

el resultado parecer que siempre es primo

1.1. EJEMPLOS DE INDUCCIN

21

para x=1 sucede que para x=2 sucede que para x=3 sucede que para x=4 sucede que Notamos que

(1)2 + (1) + 41 (2)2 + (2) + 41 (3)2 + (3) + 41 (4)2 + (4) + 41

= = = =

43 47 53 61

(1.1) (1.2) (1.3) (1.4)

43, 47, 53

61

son primos, por tanto esto nos puede tentar

a creer que esa expresin produce un primo para cualquier entero positivo Pero ahora, examinemos que sucede cuando

x.

x = 40:

(40)2 +40+41 = (40)(40)+40+41 = 40(1+40)+41 = 40(41)+41 = 412


Es obvio que

(1.5)

412

no es primo; por lo tanto la proposicin es falsa. En

realidad pudimos haber vericado con 39 casos(x todos habra funcionado.

= 1, x = 2, ..., x = 39) y en

Este fue un claro ejemplo de como muchas veces, lo que parece funcionar siempre, no siempre funciona, en casos pequeos puede parecer que siempre funciona, pero al llegar a casos mas grandes(algunos tan grandes que no se pueden vericar manualmente), es inevitable que el error se haga presente. Incluso hay ocasiones en las que se pueden vericar miles de casos(sin exagerar) sin encontrar un ejemplo donde la proposicin no se cumpla y que a pesar de todo eso, la proposicin sea falsa. Otra razn para demostrar las cosas es que la solucin completa de un problema(la solucin de un problema incluye las demostraciones de todas sus proposiciones) puede servir para descubrir propiedades tiles para resolver muchos otros problemas. Y es ah donde resolver un problema realmente sirve de algo. Volviendo al tema, podemos transformar este problema de comprobar que n+1) en comprobar este conjunto 1 + 2 + 3 + 4 + 5 + ... + n 1 + n = (n)(2 innito de proposiciones:

(1)(1 + 1) (2)(2 + 1) (3)(3 + 1) (4)(4 + 1) = 1, = 1+2, = 1+2+3, = 1+2+3+4, ... 2 2 2 2


Para hacerlo basta con probar las siguientes dos cosas: Que la primera proposicin es verdadera. Que si una proposicin es verdadera, la siguiente tambin lo es.

22

CAPTULO 1. INDUCCIN MATEMTICA


Probar que la primera proposicin es verdadera es realmente fcil:

Ahora, suponiendo

(1)(1 + 1) (1)(2) 2 = = =1 2 2 2 que para alguna n

(n)(n + 1) = 1 + 2 + 3 + 4 + .... + n 1 + n 2
vamos a probar que:

(n + 1)(n + 2) = 1 + 2 + 3 + 4 + .... + n 1 + n + n + 1 2 Es decir, vamos a probar que si se cumple con alguna n, se debe de cumplir con n + 1.
Partimos de la ecuacin inicial

Sumamos

(n)(n + 1) = 1 + 2 + 3 + 4 + ... + n 1 + n 2 n + 1 en ambos lados de la ecuacin

(n)(n + 1) + n + 1 = 1 + 2 + 3 + 4 + ... + n + n + 1 2 (n)(n + 1) (2)(n + 1) + = 1 + 2 + 3 + 4 + ... + n + n + 1 2 2


Y sumando por factor comn se concluye que:

(n + 2)(n + 1) = 1 + 2 + 3 + 4 + ... + n + n + 1 2
La proposicin es correcta con 1, y si es correcta con 1 entonces ser correcta con 2, si es correcta con 2 lo ser con 3, si es correcta con 3 entonces lo ser con 4, y as sucesivamente. Ahora, intentando ir en contra de nuestro sentido comn, es posible que haya nmeros para los cuales no se cumpla la proposicin?, si existieran tales nmeros, tambin existira el menor de esos nmeros, llammosle ser

m,

y al

el menor entero positivo para el cual no se cumple la proposicin, ten-

m 1 se cumple la proposicin; pero ya comprobamos que si se cumple para m 1 entonces se debe de cumplir para m, lo cual es absurdo puesto que denimos m como un nmero que no cumple la proposicin.
dramos que para No nos queda otra salida mas que aceptar que la proposicin si se cumple para todos los enteros positivos.

1.1. EJEMPLOS DE INDUCCIN

23

Problema Resuelto 1.1.2.

En una esta hay

invitados, se asume que

si un invitado conoce a otro, ste ltimo conoce al primero. Prueba que el nmero de invitados que conocen a un nmero impar de invitados es par.

Solucin

Nos encontramos ahora con un problema un poco mas abstracto

que el anterior. Cmo tratar este problema?, primeramente sera conveniente imaginar a las personas como puntos y si dos personas se conocen entre s, imaginar sus respectivos puntos unidos por una lnea. Si no es suciente imaginarlo es conveniente dibujar una gura. Nuevamente imaginaremos el caso ms simple que puede haber: una esta en la que nadie se conozca, para la cual imginamos una nube de puntos donde no hay ningn par de puntos unidos por alguna lnea. En este caso, cuntos invitados conocen a un nmero impar de invitados?, la respuesta es obvia: ninguno, es decir, 0. Recordemos que el 0 es par, y que si

es un nmero par

p+1

es impar, y

p+2

es par.

Ahora, si imaginamos en la esta que dos personas se presentan mutuamente(unimos dos puntos por una lnea), habr pues, 2 personas que conocen a un solo invitado y las dems no conocen a nadie. El nmero de invitados que conocen a un nmero impar de invitados ser 2. Nuevamente, si otras 2 personas que no se conocen entre s se presentan mutuamente, entonces el nmero de invitados que conocen a un nmero impar de personas aumentar a 4. Pero despus de ello, qu sucedera si una persona que conoce a un solo invitado se presenta con una persona que no conoce a nadie? La persona que no conoce a nadie conocera a un solo invitado, mientras que la persona que ya conoca a un solo invitado, pasar a conocer a dos invitados. Ntese que una persona que conoca a un nmero impar de invitados(conoca a 1) pas a conocer a un nmero par de invitados(acab conociendo a 2), y la que conoca a un nmero par de invitados(no conoca a ninguno) pas a conocer a un nmero impar de invitados(acab conociendo a 1) el nmero de personas que conocen a un nmero impar de invitados no cambi!. Si nos olvidamos de las personas que se acaban de conocer, y solo sabemos que el nmero de personas que conocen a un nmero impar de invitados es par, si dos invitados que no se conocan, de pronto se conocieran, hay 3 posibilidades: Una persona conoce a un nmero impar de invitados y la otra conoce a un nmero par de invitados. En este caso ambos aumentarn su nmero de conocidos en 1, el que conoca a un nmero impar de invitados pasar a conocer a un nmero

24

CAPTULO 1. INDUCCIN MATEMTICA


par de invitados, el que conoca un nmero par de invitados, pasar a conocer un nmero impar de invitados. Por ello el nmero de personas que conocen a un nmero impar de invitados no cambia y sigue siendo par. Ambas personas conocen a un nmero par de invitados. En este caso, ambas personas pasarn a conocer a un nmero impar de invitados. El nmero de personas que conocen a un nmero impar de invitados aumenta en 2, por ello sigue siendo par. Ambas personas conocen a un nmero impar de invitados. En este caso, ambas personas pasarn a conocer un nmero par de invitados. El nmero de personas que conocen a un nmero impar de invitados disminuye en 2, por ello sigue siendo par. Entonces, si al principio de sus vidas nadie se conoce, el nmero de per-

sonas que conocen a un nmero impar de invitados es par, y conforme se van conociendo, el nmero seguir siendo par. Por ello se concluye que siempre ser par.

Problema Resuelto 1.1.3. Solucin

Prueba que para todo entero

n 4, n! > 2n .

Tal vez despus de haber ledo las soluciones de los dos primeros

problemas te haya sido ms fcil llegar a la solucin de este.

4! = (1)(2)(3)(4) = 24 24 = (2)(2)(2)(2) = 16
Ya comprobamos que

4! > 24 ,

ahora, suponiendo que para alguna

n! > 2n
proseguimos a comprobar que

(n + 1)! > 2n+1


Partiendo de la desigualdad inicial:

n! > 2n

1.1. EJEMPLOS DE INDUCCIN


multiplicamos por

25

n+1

el miembro izquierdo de la desigualdad y multipli-

camos por 2 el lado derecho de la desigualdad. Como

n + 1 > 2,

podemos

estar seguros que el lado izquierdo de la desigualdad seguir siendo mayor.

(n!)(n + 1) > (2n )2


Sintetizando:

(n + 1)! > 2n+1


Ello implica que si se cumple para un nmero

n,

tambin se cumple para

n + 1,

como se cumple para 4, entonces se cumplir para 5, y si se cumple

para 5, entonces se cumplir para 6, etc. Se puede inferir que se cumplir para todos los nmeros enteros mayores o iguales a 4.

Problema Resuelto 1.1.4.

Prueba que para todo entero positivo

12 + 22 + 32 + 42 + ... + n2 =

n(n + 1)(2n + 1) 6 n=1

Solucin

Nuevamente comenzaremos por el caso mas simple, cuando

1(1 + 1)(2 + 1) 6 = =1 6 6 Ahora, suponiendo que para alguna n n(n + 1)(2n + 1) 12 + 22 + 32 + 42 + ... + n2 = 6 2 veremos que sucede si se le suma (n + 1) a ambos miembros de 12 + 22 + ... + n2 + (n + 1)2 =

la ecuacin

n(n + 1)(2n + 1) 6(n + 1)(n + 1) + 6 6 (n + 1)(n(2n + 1) + 6n + 6) 12 + 22 + ... + n2 + (n + 1)2 = 6 ( n + 1)(2 n2 + 7n + 6) 12 + 22 + ... + n2 + (n + 1)2 = 6 ( n + 1)( n + 2)(2( n + 1) + 1) 12 + 22 + ... + n2 + (n + 1)2 = 6 n
tam-

Nuevamente observamos que se cumple con 1, y si se cumple con bin se cumplir con

n+1

y por ello con todo nmero mayor

n,

por lo tanto

se cumple con todos los enteros positivos.

Problema Resuelto 1.1.5.


de 5 centavos.

Muestra que para cualquier cantidad de dinero

mayor a 7 centavos puede ser formada usando solo monedas de 3 centavos y

26

CAPTULO 1. INDUCCIN MATEMTICA


La cantidad de 8 centavos puede ser formada con una moneda

Solucin

de 3 y una de 5; la cantidad de 9 centavos puede ser formada con 3 monedas de 9 centavos; la cantidad de 10 centavos puede ser formada con 2 monedas de 5 centavos. Suponiendo que para algn entero positivo formar las cantidades las cantidades

a, tal que a > 7 fuera posible a, a + 1 y a + 2, si a cada una de esas cantidades se les
es decir, para cualquier tercia de nmeros

aade una moneda de 3 centavos, resuelta que tambin ser posible formar

a + 3, a + 4 y a + 5,

consecutivos que se pueda formar, tal que el menor de ellos sea mayor que la siguiente tercia tambin se podr formar.

7,

Con ello podemos asumir que cualquier tercia de nmeros consecutivos mayores a 7 se podr formar y por ende, cualquier nmero entero positivo mayor a 7.

Problema Resuelto 1.1.6.


elegir de entre

Prueba que si se tienen

personas, es posible

2 1

grupos de personas distintos para hacer una marcha.

Por ejemplo, con 3 personas A, B y C se pueden elegir 7 grupos:

A B C A, B A, C B, C A, B, C

Solucin
cha.

Consideremos el caso de que solo hay una persona, con esta per-

sona, solamente se puede elegir un grupo(de un solo integrante) para la mar-

21 1 = 1
Ahora, suponiendo que con algn entero

personas se pueden elegir

n,

un desconocido va pasando cerca de las

2n 1 grupos, para n personas y se une

a ellas, puede formar parte de la marcha, o bien no formar parte. Por cada grupo que se poda formar con a lo ms grupos con a lo ms

n personas habr otros 2

n + 1 personas(uno en el caso de que el nuevo integrante

1.2. ERRORES COMUNES


participe y otro en el caso de que no participe).

27

Como la marcha se puede crear solamente con la persona que acaba de llegar. n+1 Entonces con n + 1 personas habr 2 1 grupos que se pueden elegir. Con ello queda demostrada la proposicin.

1.2. Errores Comunes


A veces al estar intentando probar algo por induccin se llega a caer en ciertos errores, algunos de los cuales se ejemplican a continuacin probando cosas que son obviamente falsas:

Proposicin

Todos los perros son del mismo color

Pseudo-prueba
mente

Si tenemos un solo perro, es trivial que es de su propio

color; ahora, si para algun entero positivo

todos los conjuntos con exacta-

perros constaran de perros del mismo color entonces para cualquier

conjunto

con

perros, los cuales llamaremos:

p1 , p2 , ..., pn
Existiran 2 perros:

p1 , pn+1
Tal que se cumpla que

p1

forma parte del conjunto

cualquier perro que no est en el conjunto

(a menos que

pn+1 pueda ser P tuviera todos pn+1 (ya


que

los perros que existen en cuyo caso se cumplira la proposicin). Por construccion tendramos que todos los conjuntos con color), y que

p1

es del mismo color que

n o menos

perros tienen solamente perros del mismo

p1

es del mismo color que todos los perros de

P,

por lo que

p1 , p2 , ..., pn+1 seran perros del mismo color, y con esto se comprueba que si cualesquiera n perros son todos del mismo color entonces cualesquiera n + 1
perros seran del mismo color. Con induccin queda probado que todos los perros son del mismo color.

28

CAPTULO 1. INDUCCIN MATEMTICA


Aqu se est suponiendo que se tienen

Error

n perros del mismo color, pero

implcitamente se est asumiendo que Est bastante claro que esta

n2

cuando se supone que cualquier

pareja de perros va a constar de perros del mismo color.

prueba

es incorrecta, sin embargo errores

similares pero menos obvios pueden suceder en la prctica. Siempre hay que vericar que si un caso se cumple el siguiente tambin lo har, as como vericar que se este usando el caso base adecuado(en el ejemplo anterior se us un caso base demasiado pequeo).

Existe otro error(menos comn) en la induccin:

Proposicin

Todos los nmeros enteros son iguales.

Pseudo-prueba
queda demostrado.

Si para algn nmero entero

do 1 en ambos lados de la ecuacin,

k , k = k +1 entonces, sumank + 1 = k + 2, por lo que por induccin

Error

Aunque se est tomando en cuenta la segunda condicin de la in-

duccin (que si un caso se cumple el siguiente tambin) no se est tomando en cuenta la primera (que debe de existir un caso en el que se cumpla).

1.3. Denicin de Induccin


Despus de estos ejemplos le ser posible al lector abstraer lo que tienen en comn y as hacerse de una idea clara de lo que es la induccin. La induccin es un mtodo para demostrar una proposicin matemtica basndose en la vericacin de la forma mas simple de la proposicin y luego comprobando que una forma compleja de la proposicin se cumple siempre y cuando una forma mas simple se cumpla. O dicho de una manera mas formal, es un mtodo para demostrar una sucesin innita de proposiciones es. y posteriormente demostrando que si

P0 , P1 , P2 , ... demostrando que P0 es cierta Pk es cierta entonces Pk+1 tambin lo

1.4. PROBLEMAS

29

1.4. Problemas
1.4.1. Sumas
Demuestra por induccin que: 1.

1 + 22 + 32 + 42 + ... + n2 =
2.

n(n + 1)(2n + 1) 6

1 + 3 + 5 + ... + (2n 1) = n2
3.

1 2 + 3 4 + 5 6 + ...n = (1)n+1
4.

n 2

1 + 2 + 4 + ... + 2n = 2n+1 1
5. Cualquier cantidad de dinero mayor que 34 centavos puede ser formada usando solamente monedas de 5 y de 9 centavos.

30

CAPTULO 1. INDUCCIN MATEMTICA


Tablero de Ajedrez

1.4.2.

Demuestra por induccin que: 1. Para todo entero

n y todo par m, un tablero de ajedrez de n m tiene n


y

exactamente el mismo nmero de cuadros blancos que negros. 2. Para todos los impares

m,

un tablero de ajedrez de

nm

tiene

exactamente un cuadro blanco ms que cuadros negros. Un

trimino

es una pieza con forma de L compuesta de tres cuadros

adyacentes en un tablero de ajedrez. Un arreglo de triminos es una sin que un trimino quede parcialmente afuera del tablero. Demuestra por induccin que: 1. Para

cobertura

del tablero de ajedrez si cubre todos los cuadros del tablero sin traslape y

faltante, puede ser cubierto con triminos, sin importar en dnde est
el cuadro faltante.

n1

cualquier tablero de ajedrez de

2n 2n ,

con un cuadro

2. Para

faltante,

n 1 cualquier tablero de ajedrez de 2n 2n , con

un cuadrado

puede ser cubierto con triminos de solo 3 colores(de man-

era que 2 triminos del mismo color no tengan bordes en comn), sin importar en dnde est el cuadro faltante.

1.4. PROBLEMAS
1.4.3. Chocolate

31

Tiempo Lmite:1 Segundo Supongamos que tenemos una barra de chocolate de en cuadrados de 1 x 1. Las partes del chocolate pueden ser cortadas a travs de cortes horizontales y/o verticales como se muestra en la gura. Un corte(ya sea horizontal o vertical) de un pedazo del chocolate siempre divide ese pedazo en dos pedazos mas pequeos. Como todo cuesta en esta vida, cada corte que realizes en el chocolate tambin tendr un costo, dicho costo se puede expresar como un nmero entero positivo. Este costo no depende del tamao del pedazo que se corte, sino que depende de la recta horizontal o vertical por la cual se est cortando. Denotaremos los costos de cortar por cada recta vertical como y los costos de cortar por cada recta horizontal como

m x n piezas cuadradas

de 1x1(es una suposicin, por lo tanto no puedes comertela) y debes partirla

x1 , x2 , x3 , ..., xm1 y1 , y2 , y3 , ..., yn1 .

El costo de cortar la barra entera es la suma de los costos de todos los cortes requeridos.

Por ejemplo, si cortamos el chocolate a lo largo de las rectas horizontales y despus cada pedazo obtenido lo cortamos a lo largo de las rectas verticales, el costo total por cortar la barra ser

y1 + y2 + y3 +4(x1 + x2 + x3 + x4 + x5

).

Problema
Escribe un programa que dado el tamao de la barra de chocolate, determine el costo mnimo para cortarla en cuadrados de 1x1.

Entrada

Descripcin
Siguientes

m -1 lneas: Los valores de x1 , x2 , x3 , ..., xm1 Siguientes n -1 lneas: Los valores de y1 , y2 , y3 , ..., yn1

Lnea 1: Dos enteros positivos

separados por un espacio

32

CAPTULO 1. INDUCCIN MATEMTICA

Ejemplo
6 4 2 1 3 1 4 4 1 2

Salida

Descripcin
Lnea 1: Un solo nmero entero: el costo mnimo de cortar todo el chocolate en cuadrados de 1x1

Ejemplo
42

Lmites

2 m, n 1000
Ninguno de los costos superar a 1000

Referencias
Fuente: X Polish Olympiad in Informatics 2002/2003 Autor: Marcin Kubica Traductor: Luis Enrique Vargas Azcona

1.5. SUGERENCIAS

33

1.5. Sugerencias
Esta seccin est dedicada a dar pistas para encontrar algunas soluciones de los problemas del captulo. Suele ser mejor leer una sugerencia y volver a intentar el problema que pasar directo a la solucin.

Sumas
Sugerencia para el quinto inciso: intenta demostrar que si puedes formar 5 nmeros consecutivos formar

n, n +1, n +2, n +3 y n +4, entonces tambin puedes n + 5, n + 6, n + 7, n + 8 y n + 9.

Tablero de Ajedrez
Sugerencias para el conteo de cuadros blancos y negros: 1. Intenta demostrarlo para un tablero de 2 x mostracin como caso pase para uno de

n m.

y luego usar esa de-

2. Intenta usar el inciso anterior agregando/quitando una columna. Sugerencias para los problemas de triminos:

n n n+1 1. Intenta juntar 4 tableros de 2 x 2 para obtener un tablero de 2 x n+1 2 . Puedes usar el hecho de que puedes poner el vaco donde quieras
para unir los 4 tableros?. 2. Luego de ver casos pequeos, Notas algo en los bordes?, Puedes intercambiar algunos colores para lograr juntar los tableros como en el inciso anterior?.

Chocolate
Qu sucede si en lugar de buscar cmo cortar el chocolate supones que el chocolate ya est cortado y quieres unir las piezas con un procedimiento similar?, en qu cambia el problema?

34

CAPTULO 1. INDUCCIN MATEMTICA

Cap tulo

Denicin y Caractersticas de la Recursin


Ya vimos con la induccin que a veces una proposicin se puede vericar en su forma ms simple y luego probar que si se cumple para una de cierta complejidad tambin se cumplir para otra con ms complejidad. Ahora, la recursividad o recursin se trata de algo parecido, se trata de denir explcitamente la forma ms simple de un proceso y denir las formas mas complejas de dicho proceso en base a formas un poco ms simples. Es decir:

Denicin 2.0.1

(Recursin)

Forma de denir un objeto o un proceso

deniendo explcitamente su forma mas simple, y deniendo sus formas mas complejas con respecto a formas mas simples. Veremos cual es la utilidad de este tipo de deniciones con los siguientes ejemplos:

2.1. Factorial
n! se lee como ene factorial y n! = (1)(1)(2)(3)(4)(5)...(n 1)(n) Por ejemplo 5! = (1)(1)(2)(3)(4)(5) = 120
y se lee como

cinco factorial

Pero puede parecer incmodo para una denicin seria tener que usar los puntos suspensivos(...) y depender de cmo la interprete el lector. Por ello, vamos a convertir esa denicin en una denicin recursiva. La forma mas simple de la funcin factorial es:

0! = 1
35

36CAPTULO 2. DEFINICIN Y CARACTERSTICAS DE LA RECURSIN

Y ahora, teniendo

n!, Cmo obtenemos (n+1)! ?, simplemente multiplicando (n + 1)! = (n!)(n + 1)

n!

por

n + 1.

De esta forma especicamos cada que se tenga un nmero, cmo obtener el siguiente y la denicin queda completa, sta ya es una denicin recursiva,

(n + 1)! en trminos n!. Y para ir ms de acuerdo con la idea de la recursividad hay que denir n! en trminos de (n 1)!. As que, teniendo (n 1)!, para obtener n! hay que multiplicar por n. De
sin embargo, se ve un tanto sucia, puesto que dene a de esa forma nuestra denicin recursiva queda as:

Denicin 2.1.1 (Factorial).


0! = 1 n! = (n 1)!n
Al igual que las matemticas, una computadora tambin soporta funciones recursivas, por ejemplo, la funcin factorial que acaba de ser denida puede ser implementada en lenguaje C de la siguiente manera: Cdigo 2.1: Funcin recursiva factorial 1 2 3 4 } Para los estudiantes de programacin que comienzan a ver recursin suele causar confusin ver una funcin denida en trminos de s misma. Suele causar la impresin de ser una denicin cclica. Pero ya que construimos la funcin paso a paso es posible que ello no le ocurra al lector. De todas formas es buena idea ver cmo trabaja esta funcin en un depurador o ir simulndola a mano. Podemos notar algunas cosas interesantes: Si llamamos a

int

factorial (

int n ) { i f ( n == 0 ) return 1 ; else return f a c t o r i a l ( n 1) n ;

factorial, n
vez

factorial(5),

la primera vez que se llame a la funcin

ser igual a 5, la segunda vez

ser igual a 4, la tercera

ser igual a 3, etc.

Despus de que la funcin regresa el valor 1,

vuelve a tomar todos

los valores anteriores pero esta vez en el orden inverso al cual fueron llamados, es decir, en lugar de que fuera 5, 4, 3, 2, 1, ahora

va

tomando los valores 1, 2, 3, 4, 5; conforme el programa va regresando de la recursividad va multiplicando el valor de retorno por los distintos valores de

n.

2.1. FACTORIAL

37

Cuando la funcin factorial regresa por primera vez 1, la computadora recuerda los valores de

n, entonces guarda dichos valores en algn lado.

La primera observacin parece no tener importancia, pero combinada con la segunda se vuelve una propiedad interesante que examinaremos despus. La tercera observacin hace obvia la necesidad de una estructura para recordar los valores de las variables cada que se hace una llamada a funcin. Dicha estructura recibe el nombre de pila o

stack.

Y por el solo hecho

de contener informacin, la pila requiere memoria de la computadora y tiene un lmite de memoria a ocupar(vara mucho dependiendo del compilador y/o sistema operativo), si dicho lmite es superado, el programa terminar abruptamente por acceso a un rea restringida de la memoria, este error recibe el nombre de desbordamiento de pila o

stack overow.

A veces, al abordar un problema, es mejor no pensar en la pila dado que el tamao de la pila nunca crecer mucho en ese problema; otras veces hay que tener cuidado con eso; y en ocasiones, tener presente que la pila recuerda todo puede ayudar a encontrar la solucin a un problema. Para observar mas claramente las propiedades mencionadas, conviene echar un vistazo a esta versin modicada de la funcin factorial: Cdigo 2.2: Funcin recursiva factorial modicada 1 2 3 4 5 6 7 8 } Si llamas

int

factorial (

int

int

n){ n) ; 1; n, fminus1 ) ;

fminus1 ; == 0 )

printf (" % d\n" ,

i f (n

return
% d\n" ,

f m i n u s 1= f a c t o r i a l ( n 1) ;

return
salida en pantalla:

printf (" % d

f m i n u s 1 n ;

factorial(5)

con el cdigo de arriba obtendrs la siguiente

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

38CAPTULO 2. DEFINICIN Y CARACTERSTICAS DE LA RECURSIN

4 6 5 24
All se ve claramente cmo

toma los valores en el orden inverso cuando

va regresando de la recursividad, cmo la lnea 3 se ejecuta 6 veces mientras se van haciendo las llamadas recursivas y cmo la linea 6 se ejecuta ya se hicieron todas las llamadas recursivas, no antes.

2.2. Imprimir Nmeros en Binario


Con la funcin factorial ya vimos varias propiedades de las funciones recursivas cuando se ejecutan dentro de una computadora. Aunque siendo sinceros, es mucho mas prctico calcular el factorial utilizando

for

que con una funcin recursiva.

Ahora, para comenzar a aprovechar algunas ventajas de la recursividad se tratar el problema de imprimir nmeros en binario, el cual es fcilmente extendible a otras bases. Cada vez que observemos un nmero con subndice, vamos a interpretar el subndice como la base de numeracin en la cual el nmero est escrito. Por ejemplo decimal. Estos son los primeros nmeros enteros positivos en sistema binario:

1012

signica el nmero binario

101,

es decir,

en sistema

12 , 102 , 112 , 1002 , 1012 , 1102 , 1112 , 10002 , 10012 , 10102 , 10112 , ...
Esto no debe causar confusin en el momento en el que veamos los subndices para denotar sucesiones, por ejemplo en expresiones tales como

x1 , x2 , x3 , ..., xn , y esto es porque


no est escrito en ninguna base.

x1 ,

se tiene que

Problema Resuelto 2.2.1. Solucin

Escribe una funcin recursiva que imprima un

nmero entero positivo en su formato binario(no dejes ceros a la izquierda).

La instancia mas simple de este proceso es un nmero binario

de un solo dgito(1 0). Cuando haya mas de un dgito, hay que imprimir primero los dgitos de la izquierda y luego los dgitos de la derecha. Tambin hay que hacer notar algunas propiedades de la numeracin binaria: Si un nmero es par, termina en 0, si es impar termina en 1. Por ejemplo

1012 = 5

1002 = 4

Si un nmero se divide entre 2(ignorando el residuo), el cociente se escribe igual que el dividendo, simplemente sin el ltimo dgito de la derecha.

2.3. LOS CONEJOS DE FIBONACCI


Por ejemplo

39

100110012 2

= 10011002

Tomando en cuenta todo lo anterior concluimos que si que imprimir y 0 si

n < 2 entonces hay n, si no, hay que imprimir n/2 y luego imprimir 1 si n es impar

es par. Por lo tanto, la funcin recursiva quedara de esta manera:

Cdigo 2.3: Funcin recursiva que Imprime un Nmero Binario 1 2 3 4 5 6 7 8 } } }

void

imprime_binario (

i f ( n>=2){ else {

int

n){

imprime_binario ( n/2) ; printf (" % d" , printf (" % d" , n %2) ; n) ;

Aqu vemos que esta funcin recursiva si supone una mejora en la sencillez con respecto a las posibles soluciones iterativas(no recursivas), ya que para hacerlo iterativamente se necesitara llenar un arreglo y en cada posicin un dgito, y luego recorrerlo en el orden inverso al que se llen. Pero hay que hacer notar que un proceso recursivo funciona ligeramente mas lento que si se hiciera de manera iterativa. Esto es porque las operaciones de aadir elementos a la pila y quitar elementos de la pila toman tiempo. Por eso se concluye que la recursin hay que usarla cuando simplique sustancialmente las cosas y valga la pena pagar el precio con un poco menos de rendimiento.

2.3. Los Conejos de Fibonacci


Haba una vez cierto matemtico llamado Leonardo de Pisa, apodado Fibonacci, que propuso el siguiente problema:

Problema Resuelto 2.3.1.

Alguien compra una pareja de conejos(un ma-

cho y una hembra), luego de un mes de haber hecho la compra esos conejos son adultos, despus de dos meses de haber hecho la compra esa pareja de conejos da a luz a otra pareja de conejos(un macho y una hembra), al tercer mes, la primera pareja de conejos da a luz a otra pareja de conejos y al mismo tiempo, sus primeros hijos se vuelven adultos.

40CAPTULO 2. DEFINICIN Y CARACTERSTICAS DE LA RECURSIN

Figura 2.1: Reproduccin de los Conejos de Fibonacci

Cada mes que pasa, cada pareja de conejos adultos da a luz a una nueva pareja de conejos, y una pareja de conejos tarda un mes en crecer. Escribe una funcin que regrese cuntos conejos adultos se tienen pasados de la compra.

meses

Solucin

Sea

F ( x)

el nmero de parejas de conejos adultos pasados

meses. Podemos ver claramente que pasados 0 meses hay 0 parejas adultas y

F (0) = 0 y F (1) = 1. Ahora, suponiendo que para alguna x ya sabemos F (0), F (1), F (2), F (3), ..., F (x 1), en base a eso cmo podemos saber el valor de F (x)? Si en un mes se tienen a parejas jvenes y b parejas adultas, al siguiente mes se tendrn a + b parejas adultas y b parejas jvenes. Por lo tanto, el nmero de conejos adultos en un mes n, es el nmero de conejos adultos en el mes n-1 ms el nmero de conejos jvenes en el mes n-1. Como el nmero de conejos jvenes en el mes n-1 es el nmero de conejos
pasado un mes hay una sola pareja adulta. Es decir

2.3. LOS CONEJOS DE FIBONACCI


adultos en el mes

41

n-2,

entonces podemos concluir que:

F (0) = 0 F (1) = 1 F (n) = F (n 1) + F (n 2)


El siguiente cdigo en C muestra una implementacin de una funcin recursiva para resolver el problema planteado por Fibonacci: Cdigo 2.4: Funcin recursiva de la serie de Fibonacci

1 2 3 4 5 6 7 8 9 10

int

F(

int n ) { i f ( n==0){ return 0 ; } else i f ( n==1){ return 1 ; } else { return F ( n 1)+F ( n 2) ;


}

Si corres el cdigo anterior en una computadora, te dars cuenta que el tamao de los nmeros crece muy rpido y con nmeros como 39 o 40 se tarda mucho tiempo en responder, mientras que con el nmero 50 parece nunca terminar. Hemos resuelto de manera terica el problema de los conejos de Fibonacci, sin embargo esta solucin es irrazonablemente lenta!. Como curiosidad matemtica, posiblemente alguna vez leas u oigas hablar sobre la sucesin de Fibonacci, cuando suceda, ten presente que la sucesin de Fibonacci es

F (0), F (1), F (2), F (3), F (4), F (5), ...


O escrita de otra manera:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...


Adems de servir como solucin a este problema, la serie de Fibonacci cumple tambin con muchas propiedades interesantes que pueden servir para resolver o plantear otros problemas. Por el momento no se mostrarn aqu, ya que ese no es el objetivo del libro, pero si te interesa puedes investigarlo, en internet hay bastante referente a eso.

42CAPTULO 2. DEFINICIN Y CARACTERSTICAS DE LA RECURSIN

Cap tulo

Recursin con Memoria o Memorizacin


La recursin con Memoria o Memorizacin es un mtodo para evitar que una misma funcin recursiva se calcule varias veces ejecutndose bajo las mismas condiciones; consiste en tener en una estructura(por lo general un arreglo de una o varias dimensiones) para guardar los resultados ya calculados.

3.1. Mejorando el Rendimiento de Fibonacci


Problema Resuelto 3.1.1.
como Recordando, la sucesin de Fibonacci se dene

F (0) = 0 F (1) = 1 F (n) = F (n 1) + F (n 2)


Escribe un programa que calcule e imprima los primeros 50 nmeros de la serie de Fibonacci en menos de un segundo.

Solucin

Ya habamos visto en la seccin anterior que la funcin recur-

siva de Fibonacci(vase cdigo 2.4) es extremadamente lenta, vale la pena preguntarnos Qu la hace lenta?. Si nos ponemos a ver su ejecucin en un depurador o la realizamos mentalmente, nos daremos cuenta que para calcular

F (n),

se est calculando

F (n 1) y F (n 2), F (n 2).

y para calcular

F (n 1)

tambin se est calculando

43

44

CAPTULO 3. RECURSIN CON MEMORIA O MEMORIZACIN

Figura 3.1: Llamadas a funcin que se realizan para calcular

F (6)

Una vez que se termin de calcular y sin embargo, se vuelve a calcular. se esta calculando 2 veces. Extrapolando este razonamiento veces,

F (n1) ya se haba calculado F (n2), Con ello, ya hemos visto que F (n 2)


se est calculando al menos 4

F (n 4)

F (n 6) se esta calculando al menos 8 veces, etc. Es decir, para algn m 1 m < n tal que m es par F (n m) se calcula al menos 2 2 veces(es un
ejercicio corto demostrarlo por induccin). Exactamente de cuntas veces estamos hablando que se calcula cada nmero de Fibonacci?. Por el momento no vale la pena ver eso, es suciente con saber que es un nmero exponencial de veces. Para solucionar esta problemtica, vamos a declarar un arreglo tamao que

de

50 y con F (49) > 232 .

el tipo de datos

long long,

ya que a nadie le extraara

F (n), se vericar si v [n] ya v se inicializan en 0, ya que es un arreglo global), si ya fue usado simplemente regresar v [n] si no ha sido usado entonces calcular el valor de F (n) y lo guardar en v [n].
Luego, cada que se quiera saber el valor de fue usado(los valores de As concluimos nuestros razonamientos para llegar a este cdigo: Cdigo 3.1: Fibonacci utilizando recursin con memoria 1 2 3 4 5 6 7 8 9 10 11 12 }

#include < s t d i o . h> int v [ 5 0 ] ; int F ( int n ) { i f (v [ n]!=0) { return } i f ( n==0){ return } i f ( n==1){ return return
v[n];

v[n]; 0; 1;

} v [ n ]=F ( n 1)+F ( n 2) ;

3.2. ERROR COMN EN LA MEMORIZACIN


13 14 15 16 17 18 } }

45

int

main ( ) {

int i ; for ( i =0; i < 5 0 ; i ++){ return


printf (" % l l d \n" , 0; F( i ) ) ;

Aunque el cdigo anterior es notablemente mas largo que el cdigo 2.4, solamente las lneas de la 2 a la 12 son interesantes, lo dems es solamente para imprimir en pantalla el resultado. Si ejecutas esta nueva funcin

en una computadora te dars cuenta que

es mucho mas rpida que la funcin

que se muestra en el cdigo 2.4.

3.2. Error Comn en la Memorizacin


La recursin con memoria solamente sirve cuando la funcin recursiva que se quiere optimizar no utiliza los valores de variables estticas ni globales para calcular su resultado; de lo contrario si la funcin es llamada con los mismos parmetros no se garantiza que se est calculando exactamente lo mismo mas de una vez y la recursin con memoria no sirve en ese caso. En ciertas ocaciones es conveniente memorizar an cuando la funcin recursiva original utilice variables globales, pero en esos casos debemos estar seguros que mientras usemos la funcin, las variables globales debern de mantenerse constantes para que de esa forma el valor que regrese la funcin original solamente dependa de los parmetros. Es comn para alguien que va empezando en la recursin con memoria cometer este tipo de errores por ello hay que tomar precauciones adicionales.

3.3. Triangulo de Pascal


Probablemente alguna vez viste este tringulo: 1 1 1 1 1 1 1 2 1 3 3 4 6 4 1 1 5 1

5 10 10

46

CAPTULO 3. RECURSIN CON MEMORIA O MEMORIZACIN


1 6 15 20 15 6 .......... Este tringulo se conoce como Tringulo de Pascal, la manera de constru1

irlo es bastante simple, se coloca un 1 en el primer rengln, en cada rengln a partir del segundo, se forman nmeros sumando cada par de nmeros adyacentes del rengln anterior(es decir, cada nmero es la suma de los dos nmeros que tiene arriba) y se coloca un 1 en cada extremo del rengln. Vamos a llamar al

j -simo

nmero del

i-simo

rengln

P (i, j ).

Problema Resuelto 3.3.1. Escribe una funcin recursiva que regrese P (i, j ),
para

i < 50

j < 50

P (i, j ) < 231 .

P (1, 1) = 1, luego que el primer numero de P (i, 1) = 1, y el ltimo nmero de cada rengln tambin es 1, osea P (i, i) = 1; en otro caso es la suma de los dos nmeros que tiene arriba, es decir P (i, j ) = P (i 1, j 1) + P (i 1, j ). De
Primero hay que notar que cada rengln tambin es 1; es decir esta manera completamos la funcin recursiva as:

P (i, 1) = 1 P (i, i) = 1 P (i, j ) = P (i 1, j 1) + P (i 1, j )


Aqu se ignor utilizaremos un arreglo llamado para

1<j<i

P (1, 1) = 1 ya que P (i, 1) = 1 implica P (1, 1) = 1 Nuevamente v para guardar los resultados ya calculados, y ya que ningn resultado puede ser 0, cuando v [i][j ] sea 0 sabremos que no se ha calculado an. Ahora el cdigo de la funcin en C resulta as:
Cdigo 3.2: Triangulo de Pascal utilizando Recursin con Memoria

1 2 3 4 5 6 7 8 9 10

int int

v[51][51]; P(

int i , int j ) { i f ( j ==1 | | i==j ) { return 1 ; } i f (v [ i ] [ j ]!=0) { return v [ i ] [ j ] ;


} v [ i ] [ j ]=P( i

return

1 ,

1)+P( i 1 ,

j);

v[ i ][ j ];

3.3. TRIANGULO DE PASCAL

47

El tringulo de Pascal tiene varias propiedades tiles, una muy til sirve para calcular combinaciones.

Denicin 3.3.1
Se denota como:

(Combinaciones)

Las combinaciones de

en

son la

cantidad de formas de escoger

m objetos entre un total de n objetos distintos. m n

Por convencin diremos que

n 0

= 1 para cualquier

entero no negativo

n.

comn, ya que

La ltima parte de la denicin puede parecer que va contra el sentido n indica el nmero de maneras de elegir 0 objetos de un 0 total de n objetos distintos, se suele pensar que si no se elige ningn objeto n realmente no se est nada por lo que debera ser igual a 0 pero 0 en realidad es igual a 1.

eligiendo

El motivo de esta confusin proviene de la palabra

elegir, no est claro si

es vlido decir que se eligen 0 objetos. Mas adelante, cuando se introduzca la nocin de conjuntos, no sonar extrao el hecho de que se pueden elegir 0 objetos de una sola manera; pero por el momento el lector tendr que adaptarse a la idea

antinatural

de que es vlido elegir

objetos.

Problema Resuelto 3.3.2. Solucin

Prueba que

i 1 j 1

= P (i, j ).

Recordemos cmo se calcula

P:

P (i, 1) = 1 P (i, i) = 1 P (i, j ) = P (i 1, j 1) + P (i 1, j )


De esa manera obtenemos 3 proposiciones: para

1<j<i

P (i, 1) = 1

i1 = 1, es decir, el nmero de formas de elegir 0 0 objetos entre un total de i 1 objetos distintos es 1.


implica de elegir

1 P (i, i) = 1 implica i = 1, es decir, el nmero de formas i 1 i 1 objetos entre un total de i 1 objetos distintos es 1.

P (i, j ) = P (i 1, j 1) + P (i 1, j ) implica 1 < j < i esta proposicin es equivalente a: i j =

i1 j 1

i2 j 2

i 2 j 1

para

i1 i1 + j1 j

48

CAPTULO 3. RECURSIN CON MEMORIA O MEMORIZACIN

La primera proposicin es obvia, solo hay una manera de elegir 0 objetos entre un total de objetos de entre La segunda proposicin tambin es obvia, la nica manera de elegir

i 1 objetos distintos, y sta es no eligiendo ningn objeto. i1 un total de i 1 objetos es eligiendo todos. n;
y se quieren elegir

La tercera proposicin es ms difcil de aceptar, digamos que se tienen

objetos numerados de 1 a

objetos entre ellos, en

particular, se puede optar entre elegir el objeto El nmero de formas de elegir

o no elegir el objeto

n.

objetos de entre un total de

distintos, es el nmero de formas de hacer eso eligiendo al objeto nmero de formas de hacer eso sin elegir al objeto Si se elige el objeto

n objetos n ms el

n.

entonces habr que elegir m 1 objetos de entre n1 un total de n 1 objetos distintos; hay formas distintas de hacerlo. m1 Si no se elige el objeto n, entonces habr que elegir m objetos de entre

n,

n 1 objetos distintos(si ya se decidi no elegir a n, entonces n1 quedan n 1 objetos que pueden ser elegidos); hay formas de hacerlo. m n n1 n1 Por ello concluimos que = m1 + m lo que prueba tercera proposim
un total de cin.

La solucin de este problema prueba un resultado muy importante llamado la Frmula de Pascal, vale la pena resaltarlo.

Teorema 1

(Frmula de Pascal)

. Sean i, j enteros positivos, se tiene que:


i1 i1 + j1 j

i j

3.4. Teorema del Binomio


El tringulo de Pascal otras propiedades interesantes, una de ellas est estrechamente relacionada con el teorema del binomio; el cual se le atribuye a Newton. Para dar una idea de a qu se reere el teorema del binomio; basta ver los coecientes de las siguientes ecuaciones:

(a + b)0 = 1 (a + b)1 = a + b (a + b)2 = a2 + 2ab + b2 (a + b)3 = a3 + 3a2 b + 3ab2 + b3

3.4. TEOREMA DEL BINOMIO

49

(a + b)4 = a4 + 4a3 b + 6a2 b2 + 4ab3 + b4 (a + b)5 = a5 + 5a4 b + 10a3 b2 + 10a2 b3 + 5ab4 + b5


Despus de ver los coecientes es natural que a uno le llegue a la mente el tringulo de Pascal y eso es exactamente a lo que se reere el teorema del binomio.

Teorema 2
(a + b)n =

(Teorema del Binomio)

. Para cualquier entero no negativo n:

n n3 3 n 0 n n n 0 n n1 1 n n2 2 a b + a b + a b + a b + ... + ab 0 1 2 3 n
Ya vimos que para

n=0 si se cumple el teorema. Ahora, suponiendo que para algn nmero n se cumple el teorema se aplicar cumplir n + 1? Por construccin vamos suponiendo que para alguna n: (a + b)n = n n 0 n n1 1 n n2 2 n n3 3 n 0 n a b + a b + a b + a b + ... + ab 0 1 2 3 n ( a + b)

Demostracin.

Multiplicamos ambos miembros de la ecuacin por

((a + b)n )(a + b) = ( (a+b)n+1 =

n n1 1 n n2 2 n 0 n n n 0 a b + a b + a b + ... + a b )(a + b) 1 2 n 0

n n+1 0 n n n n n 0 n+1 a b +( + )an b1 +( + )an1 b2 +...+ ab 0 1 0 2 1 n


n m

Recordamos que

n1 m1

n1 . m

(a + b)n+1 =
Como

n n+1 0 n+1 n 1 n+1 1 n n 0 n+1 a b + a b + ... + ab + ab 0 1 n n


n+1 n+1

n n

=1

0 n

0 n+1

=1

entonces:

(a + b)n+1 =

n+1 n 1 n + 1 0 n+1 n + 1 n+1 0 a b + a b + ... + ab 0 1 n+1

Por induccin el teorema queda demostrado.

coecientes binomiales.

Debido al teorema del binomio, a las combinaciones tambien se les llama El Triangulo de Pascal an tiene muchas ms propiedades interesantes,

pero por el momento esto es todo lo que hay en el libro, si algn lector se qued intrigado por este famoso tringulo puede buscar en internet y encontrar muchas cosas.

50

CAPTULO 3. RECURSIN CON MEMORIA O MEMORIZACIN

Cap tulo

4
Divide y Vencers
se dene de una manera bastante simple:

Divide y Vencers
sta clebre frase para estrategias de guerra ha llegado a ser bastante popular en el campo de las matemticas y sobre todo, en el de las matemticas aplicadas a la computacin. La estrategia

Divide un problema en partes mas pequeas, resuelve el problema por las partes, y combina las soluciones de las partes en una solucin para todo el problema. Es difcil encontrar un problema donde no se utilice sta estrategia de una u otra forma. Sin embargo, aqu se tratar exclusivamente de la estrategia Divide y Vencers en su forma recursiva:

Divide.

Un problema es dividido en copias mas pequeas del mismo

problema.

Vence. Se resuelven por separado las copias mas pequeas del problema.
Si el problema es sucientemente pequeo, se resuelven de la manera mas obvia.

Combina. Combina los resultados de los subproblemas para obtener la


solucin al problema original. La dicultad principal en resolver este tipo de problemas radica un poco en cmo dividirlos en copias mas pequeas del mismo problema y sobre todo cmo combinarlos. A veces la estrategia recursiva Divide y Vencers mejora sustancialmente la eciencia de una solucin, y otras veces sirve solamente para simplicar las cosas.

51

52

CAPTULO 4. DIVIDE Y VENCERS


Como primer ejemplo de Divide y Vencers veremos un problema que

puede resultar mas sencillo resolverse sin recursin, pero esto es solo para dar una idea de cmo aplicar la estrategia.

4.1. Mximo en un Arreglo


Problema Resuelto 4.1.1.
enteros ltimo). Escribe una funcin que dado un arreglo de nmero mas grande en el arreglo que est entre los ndices

v y dados dos enteros a y b, regrese el nmero mas grande en v [a..b](el a y b, incluido este

Solucin

Lo mas sencillo sera iterar desde

hasta

con un

for,

guardar

el mximo en una variable e intentar actualizarla en cada iteracin. Pero una forma de hacerlo con Divide y Vencers puede ser: Si

a < b, dividir el problema en encontrar el mximo en v [a..(a + b)/2] y el mximo en v [(a + b)/2 + 1..b] y resolver ambos problemas a=b
el mximo sera

recursivamente. Si

v [ a]

Una vez teniendo las respuestas de ambos subproblemas, ver cual de ellas es mayor y regresar esa. Los 3 puntos de ste algoritmo son los 3 puntos de la estrategia Divide y Vencers aplicados. Esta claro que este algoritmo realmente encuentra el mximo, puesto que en

v [a..b]

est compuesto por

v [a..(a + b)/2]

v [(a + b)/2 + 1..b],

sin quedar

un solo nmero del intervalo excluido. Tambin sabemos que si un nmero

x > y y y > z , entonces x > z , por ello

podemos estar seguros que alguno de los dos resultados de los subproblemas debe de ser el resultado al problema original. Para terminar de resolver este problema, hay que escribir el cdigo: Cdigo 4.1: Mximo en un intervalo cerrado 1 2 3 4 5 6

a, b

int maximo ( int v [ ] , int int maximo1 , maximo2 ; i f ( a<b ) {

a,

int
a,

b){

maximo1=maximo ( v , maximo2=maximo ( v ,

( a+b ) / 2 ) ; b) ;

i f ( maximo1>maximo2 ) {

( a+b ) /2+1 ,

4.2. BSQUEDA BINARIA


7 8 9 10 11 12 13 14 } } } } }

53

else {

return return

maximo1 ; maximo2 ;

else {

return

v[a ];

4.2. Bsqueda Binaria


Ahora, despus de haber visto un ejemplo imprctico sobre el uso de Divide y Vencers, veremos un ejemplo prctico. Supongamos que tenemos un arreglo manera ascendente. Es decir,

v v [a] > v [a 1]

con los nmeros ordenados de y queremos saber si un nmero

se encuentra en el arreglo, y de ser as, dnde se encuentra? Una posible forma sera iterar desde el principio del arreglo con un

for

hasta llegar al nal y si ninguno de los valores es igual a si alguno de los valores es igual a

x,

entonces no est,

x,

guardar dnde est.

Pero qu sucedera si quisiramos saber un milln de veces dnde est algn nmero(el nmero puede variar)? Como ya te lo podrs imaginar, el algoritmo anterior repetido un milln de veces se volver lento. Es como si intentramos encontrar el nombre de un conocido en el directorio telefnico leyendo todos los nombres de principio a n a pesar de que estn ordenados alfabticamente.

Problema Resuelto 4.2.1.


en

Escribe una funcin que dado un arreglo orde-

nado de manera ascendente, y tres enteros

a, b

x,

regrese -1 si

no est

v [a..b]

y un entero diciendo en qu ndice se encuentra

si lo est. Tu

programa no deber hacer mas de 100 comparaciones y puedes asumir que

b a<1000000.

Solucin

La solucin a este problema se conoce como el algoritmo de la

bsqueda binaria. La idea consiste en ver qu nmero se encuentra a la mitad del intervalo

v [a..b]. Si v [(a + b)/2] es menor que x, entonces x deber estar en v [(a + b)/2+ 1..b], si v [(a + b)/2] es mayor que x, entonces x deber estar en v[a..(a + b)/2]. En caso de que a = b, si v [a] = x, entonces x se encuentra en a.

54

CAPTULO 4. DIVIDE Y VENCERS


As que, los 3 pasos de la estrategia Divide y Vencers con la bsqueda

binaria son los siguientes: Si

v [(a + b)/2] con x, si x es mayor entonces resolver el problema con v [(a + b)/2 + 1..b], si x es menor resolver el problema con v [a..(a + b)/2 1], y si x es igual, entonces ya se encontr x.
comparar si

b > a,

Si

b a, comparar v [a] con x, si x es igual entonces x es diferente entonces x no se encuentra.

se encuentra en

a,

Ya sabiendo en qu mitad del intervalo puede estar, simplemente hay que regresar el resultado de ese intervalo.

Cdigo 4.2: Bsqueda Binaria Recursiva 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 } A pesar de que la idea de la bsqueda binaria es puramente recursiva, tambin se puede eliminar por completo la recursin de ella: Cdigo 4.3: Bsqueda Binaria Iterativa 1 2 3 4 5 }

int

Busqueda_Binaria ( x){

int

v[] ,

int

a,

int

b,

int

i f ( a>=b ) { i f ( v [ a]==x ) return a ; else return 1; i f ( v [ ( a+b ) /2]==x ) { return ( a+b ) / 2 ; } else i f ( v [ ( a+b ) /2] < x ) { return B u s q u e d a _ B i n a r i a ( v ,
} }

( a+b ) /2+1 ,

b,

else {

x) ;

return
x) ;

Busqueda_Binaria ( v ,

a,

( a+b ) /2 1 ,

int

Busqueda_Binaria (

while ( a<b ) i f ( v [ ( a+b ) /2]==x ) { return ( a+b ) / 2 ; } else i f ( v [ ( a+b ) /2] < x ) {

int

v[] ,

int

a,

int

b,

int

x){

4.2. BSQUEDA BINARIA

55

Figura 4.1: Rendimiento de la Bsqueda Binaria. El espacio de bsqueda(intervalo donde puede estar la solucin) esta marcado con gris, y el valor que se esta comparando est marcado con negro.

56

CAPTULO 4. DIVIDE Y VENCERS

Figura 4.2: Trres de Hanoi

6 7 8 9 10 11 12 13 14 15 } } }

else {

a=(a+b ) / 2 + 1 ; b=(a+b ) /2 1;

i f ( v [ a]==x ) { return a ; } else { return 1;


} Aunque de que los cdigos estn del mismo tamao, muchas veces resulta mas prctica la iterativa, ya que no es necesario declarar una nueva funcin para realizarla.

4.3. Torres de Hanoi


El problema de las Torres de Hanoi es un problema utilizado frecuentemente como ejemplo de recursin. Imagina que tienes 3 postes llamados En el poste

A, B

C.

A tienes n discos de diferente dimetro, acomodados en orden

creciente de dimetro desde lo ms alto hasta lo ms bajo.

4.3. TORRES DE HANOI

57

Solamente puedes mover un disco a la vez desde un poste hasta otro y no esta permitido poner un disco mas grande sobre otro mas pequeo. Tu tarea es mover todos los discos desde el poste

hasta el poste

C.

Problema Resuelto 4.3.1. Escribe una funcin que reciba como parmetro
n
y que imprima en pantalla todos los pasos a seguir para mover los discos del poste A al poste

C. n = 1, C.
tendramos

Solucin

Pensando primero en el caso mas pequeo, si

un solo disco y solo habra que moverlo de la torre Ahora, suponiendo que para algn

a la

n ya sabemos cmo mover n 1 discos

de una torre a cualquier otra qu deberamos hacer? Luego de hacerse esta pregunta directamente llegamos a la conclusin de que primero hay que mover los primeros disco

n1

discos a la torre

B,

luego el

a la torre

C,

y posteriormente mover los

n1

discos de la torre

la torre

C. n1 n,
por lo cual

Podemos estar seguros que lo anterior funciona ya que los primeros discos de la torre siempre sern mas pequeos que el disco podran colocarse libremente sobre el disco hace recursivamente.

si as lo requirieran.

Se puede probar por induccin el procedimiento anterior funciona si se As que nuestro algoritmo de Divide y Vencers queda de la siguiente manera: Sea

la torre original,

la torre a la cual se quieren mover los discos, y

la otra torre. Para

n > 1, hay que mover n 1 discos de la torre x a la z , luego mover un disco de la torre x a la y y nalmente mover n 1 discos de la torre z a la y .
torre

Para

n = 1, z.

hay que mover el disco de la torre

a la

ignorando la

Ntese que aqu los pasos de

Divide

Combina

se resumieron en uno slo,

pero si estn presentes ambos. El siguiente cdigo muestra una implementacin del algoritmo anterior, consta de dos funciones, una que recibe solamente el nmero de discos y otra que recibe el nmero de discos y los nombres de los postes. Cdigo 4.4: Torres de Hanoi 1 2

void

mueve (

i f ( n==1){

int

n,

char

x,

char

y,

char

z){

58

CAPTULO 4. DIVIDE Y VENCERS


p r i n t f ( " Mueve mueve ( n 1 , mueve ( n 1 , } x, z, de z, de y, % c y) ; % c x) ; a % c . \ n" , x, y) ; a % c . \ n" , x, y) ;

3 4 5 6 7 8 9 10 11 12 } } }

else {

p r i n t f ( " Mueve

void

hanoi (

int

n){ 'C ' , 'B ' ) ;

hanoi (n ,

'A ' ,

Una leyenda cuenta que en un monasterio en la ciudad de Hanoi hay 3 postes colocados de esa manera y unos monjes han estado trabajando para mover 48 discos del poste

al poste

C,

una vez que terminen de mover los

48 discos el mundo se acabar. Te parecen pocos 48 discos?, corre la solucin a este problema con en mover cada disco).

n=48

y vers que parece nunca terminar(ahora imagina si se tardaran 2 minutos

Problema Resuelto 4.3.2. Cuntas lneas imprime hanoi(n) (asumiendo


que esta funcin est implementada como se muestra en el cdigo 4.4)?

Solucin
'C').

Sea

H (n)

el nmero de lneas que imprime

que hanoi(n) imprime el mismo nmero de lneas que Es obvio que lnea. Ntese que cada que se llama a veces a en la lnea 6 imprime un movimiento.

hanoi(n). Est claro mueve(n, 'A', B',

H (1) = 1

puesto que

hanoi(1)

solamente imprime una se est llamando dos

hanoi(n-1, ...) una vez en la lnea 5 y otra en la lnea 7. Adems,

mueve(n, ...)

Por lo tanto obtenemos la siguiente funcin recursiva:

H (1) = 1 H (n) = H (n 1) 2 + 1
Ahora haremos unas cuantas observaciones para simplicar an mas esta funcin recursiva:

4.3. TORRES DE HANOI

59

H (1) = 1 = 1 H (2) = 2 + 1 = 3 H (3) = 4 + 2 + 1 = 7 H (4) = 8 + 4 + 2 + 1 = 15 H (5) = 16 + 8 + 4 + 2 + 1 = 31 H (6) = 32 + 16 + 8 + 4 + 2 + 1 = 63 ...


Probablemente ya ests sospechando que

H (n) = 2n1 + 2n2 + 2n3 + ... + 20


Por induccin podemos darnos cuenta que como con

H (1)

si se cumple y

(2n1 + 2n2 + 2n3 + ... + 20 )2 + 1 = 2n + 2n1 + 2n2 + ... + 20


Entonces para cualquier nmero

la proposicin se debe de cumplir.

O tambin puedes estar sospechando que:

H (n) = 2n 1
De nuevo por induccin

H (0) = 20 1
Y suponiendo que para alguna

n, H (n) = 2n 1

(2n 1)(n) + 1 = 2n+1 2 + 1 = 2n+1 1


Por lo tanto, podemos concluir que la respuesta de este problema es sin n lugar a dudas 2 1.

Adems de haber resuelto este problema pudimos darnos cuenta que

2n 1 = 2n1 + 2n2 + 2n3 + ... + 20


Esta propiedad de la suma de potencias de 2 hay que recordarla.

(4.1)

60

CAPTULO 4. DIVIDE Y VENCERS

Cap tulo

Bsqueda Exhaustiva
A veces parece que no hay mejor manera de resolver un problema que tratando todas las posibles soluciones. Esta aproximacin es llamada Bsqueda Exhaustiva, casi siempre es lenta, pero a veces es lo nico que se puede hacer. Tambin a veces es til plantear un problema como Bsqueda Exhaustiva y a partir de ah encontrar una mejor solucin. Otro uso prctico de la Bsqueda Exhaustiva es resolver un problema con con un tamao de datos de entrada lo sucientemente pequeo. La mayora de los problemas de Bsqueda Exhaustiva pueden ser reducidos a generar objetos de combinatoria, como por ejemplo cadenas de caracteres, permutaciones(reordenaciones de objetos) y subconjuntos.

5.1. Cadenas
Problema Resuelto 5.1.1. Escribe una funcin que dados dos nmeros enn y c, imprima todas las cadenas de caracteres de longitud n que utilicen solamente las primeras c letras del alfabeto(todas minsculas), puedes asumir que n < 20.
teros

Solucin

Llamemos

cadenas(n, c)

al conjunto de cadenas de longitud

usando las primeras

letras del alfabeto.

Como de costumbre, para encontrar la solucin a un problema recursivo pensaremos en el caso mas simple. El caso en el que

a,

o dicho de otra

c = 1. En ese caso solamente manera, cadenas(1, 1) = {a}


y 61

n=1

hay que imprimir

62

CAPTULO 5. BSQUEDA EXHAUSTIVA


En este momento podramos pensar en dos opciones para continuar el

razonamiento cmo de costumbre:

n=1

c>1

n>1
que imprimir las primeras que imprimir

c=1

Si pensramos en la segunda opcin, veramos que simplemente habra

letras del abecedario.

Si pensramos en la segunda opcin, veramos que simplemente habra

veces a y posteriormente un salto de lnea.

n > 1 y c > 1, para alguna n y alguna c, sera posible obtener cadenas(n, c) si ya se tiene cadenas(n 1, c) cadenas(n, c 1)?
Ahora vamos a suponer que Si nos ponemos a pensar un rato, veremos que no hay una relacin muy obvia entre por

cadenas(n, c) cadenas(n 1, c).

cadenas(n, c 1),

as que buscaremos la relacin

Hay que hacer notar aqu que si se busca una solucin en base a una pregunta y sta parece complicarse, es mejor entonces buscar la solucin de otra forma y slo irse por el camino complicado si no hay otra opcin. Volviendo al tema, buscaremos la relacin de

cadenas(n, c) con cadenas(n

1, c).
A veces algunos ejemplos sencillos pueden dar ideas. Observamos que

cadenas(1, 3) = {a, b, c, }

cadenas(2, 3) = {aa, ab, ac, ba, bb, bc, ca, cb, cc }

cadenas(3, 3) = {aaa, aab, aac, aba, abb, abc, aca, acb, acc, baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc, caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc, }

5.1. CADENAS

63

Luego de observar esto, es posible prestar mas atencin a la siguiente propiedad: Toda cadena de

caracteres puede ser formada por una cadena de

n1

caracteres seguida de otro caracter. Esto quiere decir que para cada caracter que pueda tener la cadena hay que generar todas las cadenas de dicho caracter al nal. Partiendo de esta idea, que puede parecer un tanto complicada de implementar, se puede sustituir por la idea de primero colocar el ultimo caracter de la cadena, y luego generar todas las cadenas de guardada en un arreglo global de tipo char. Cdigo 5.1: Generador de Cadenas de Caracteres 1 2 3 4 5 6 7 8 9 10 11 } } }

n1

caracteres y a cada cadena colocarle

n1

caracteres posibles.

Para evitar meter muchos datos en la pila, es mejor tener la cadena

char C [ 2 1 ] ; void c a d e n a s ( int int i ; i f ( n==0){ else {

n,

int

c){

printf (" % s \ n " , C) ;

for ( i = ' a ' ; i < ' a '+c ; i ++){


C [ n ]= i ; c a d e n a s ( n 1 , c) ;

Problema Resuelto 5.1.2.


solamente las primeras

Cuntas cadenas de longitud

que utilizan

letras del abecedario(todas minsculas) existen? O

dicho de otra forma Cuntas lneas imprime el cdigo 5.1?

Solucin
lo tanto

Llammosle

11. Es obvio que Ntese que

cad(n, c) al nmero de lneas que imprime el cdigo cad(1, c) imprime c lneas. si n > 1, cadenas(n, c) llama c veces a cadenas(n 1, c). Por cad(1, c) = c cad(n, c) = cad(n 1, c)c
para

n>1

Ahora por induccin es fcil darnos cuenta que

cad(n, c) = cn

64

CAPTULO 5. BSQUEDA EXHAUSTIVA


Un algoritmo de cambio mnimo para generar cadenas binarias(de 0s y

1s) es aquel que genera todas las cadenas binarias de determinada longitud en un orden tal que cada cadena diera de su predecesor en solamente un caracter. mnimo que genera las El siguiente cdigo es una implementacin de un algoritmo de cambio 2n cadenas binarias. Cdigo 5.2: Generador de cadenas binarias con cambio mnimo 1 2 3 4 5 6 7 8 9 } El cdigo anterior genera las cadenas en un arreglo llamado } }

void g e n e r a ( int i f ( n==0){ else {

n){

imprime_cadena ( ) ; g e n e r a ( n 1) ;

C [ i ] = !C [ i ] ; g e n e r a ( n 1) ;

C,

y asume

que siempre ser lo sucientemente grande para generar todas las cadenas, la lnea 3 llama a una funcin para imprimir la cadena generada, dicha funcin no se muestra porque ocupara demasiado espacio y no tiene relevancia en el algoritmo. Puede parecer un tanto confusa la lnea 6, pero hay que recordar que !0 devuelve 1 y !1 devuelve 0.

Problema Resuelto 5.1.3.


mente un dgito.

Demuestra que el cdigo 5.2 genera todas las

cadenas binarias y que cada cadena que genera diere de la anterior en sola-

Solucin
Si

Este problema requiere que se comprueben 2 cosas: una es que

el algoritmo genera todas las cadenas binarias de longitud las genera con cambio mnimo.

n,

y otra es que

n = 1

basta con echar un vistazo al cdigo para darse cuenta que

funciona en ambas cosas. Podemos observar tambin, que luego de ejecutarse la lnea 6 se llama a

genera(n 1)

y se sigue llamando recursivamente a la funcin genera sin

pasar por la lnea 6 hasta que cambio mnimo.

toma el valor de 0 y se imprime la cadena

en la lnea 3; de esta forma podemos asegurar que genera las cadenas con Para probar el hecho de que se imprimen todas las cadenas binarias de longitud

n,

hay que hacer la siguiente observacin que no es muy obvia:

5.2. CONJUNTOS Y SUBCONJUNTOS


No importa si el arreglo binaria de longitud binaria de

65

no est inicializado en 0s, siempre y cuando

contenga nicamente 0s y 1s. La prueba de esto es que dada una cadena

n, se puede generar a partir de ella cualquier otra cadena longitud n, simplemente cambiando algunos de sus 1s por 0s y/o

algunos de sus 0s por 1s. Si el algoritmo produce todas los conjuntos de cambios que existen entonces generar todas las cadenas sin importar la cadena inicial ya que a partir de una cadena se puede formar cualquier otra luego de aplicar una serie de cambios. La capacidad de poder hacer este tipo de razonamientos tanto es muy importante, tan importante es poder reducir un problema como dividirlo. De esa forma, suponiendo que para algn las cadenas de

n1

la funcin produce todas

n1

caracteres, las producir para

n?.

Dado que la funcin no hace asignaciones sino solamente cambios(lnea 6), podemos estar seguros que si se producen todas las cadenas llamando a la funcin con

n1

signica que el algoritmo hace todos los cambios para

n 1. genera(n 1) con C [n] = a para alguna 0 a 1, y la lnea 7 llama a genera(n 1) con C [n] =!a (si a = 0 entonces C [n] = 1 y si a = 1 entonces C [n] = 0). Como C [n] est tomando todos los valores posibles y para cada uno de sus valores esta generando todas las cadenas de tamao n 1 podemos concluir
Tambin se puede observar que la lnea 5 llama a que el algoritmo genera todas las cadenas binarias.

Hay que destacar una propiedad que se hay en la solucin:

longitud n con un alfabeto de c letras.

Teorema 3. Sea cad(n, c) el nmero de cadenas que se pueden formar de


cad(n, c) = cn

5.2. Conjuntos y Subconjuntos


Los conjuntos son estructuras que estan presentes en todas las reas de las matemticas, y juegan un papel central en las matemticas discretas y por ende en las ciencias de la computacin. La idea bsica de un conjunto es una coleccin de elementos, para los cuales todo objeto debe pertenecer o no pertenecer a la coleccin. Hay muchos ejemplos de la vida cotidiana de conjuntos, por ejemplo, el conjunto de los libros de una biblioteca, el conjunto de muebles de la casa, el conjunto de personas entre 25 y 30 aos, etc.

66

CAPTULO 5. BSQUEDA EXHAUSTIVA


As como existen conjuntos tan concretos, las matemticas tratan por lo

general con conjuntos ms abstractos. Por ejemplo el conjunto de los nmeros entre 0 y 10, el conjunto de los nmeros pares, el conjunto de los polgonos regulares, entre otros. De hecho casi todos los objetos matemticos son conjuntos. Los conjuntos nitos pueden describirse con una lista de sus elementos separados por comas, por ejemplo, el conjunto de las vocales:

{a, e, i, o, u}
El conjunto de los nmeros pares positivos de un solo dgito:

{2, 4, 6, 8}
Dado que un conjunto es una agrupacin de elementos, no importa el orden en el que se escriban los elementos en la lista. Por ejemplo:

{1, 5, 4, 3} = {4, 5, 1, 3}
Los conjuntos suelen representarse con letras maysculas y elementos de los conjuntos con letras minsculas. Si un objeto pertenece a

es un elemento de un conjunto

P,

entonces se dice que

y se denota de la siguiente manera:

aP
y si un objeto

no pertenece a

se denota de la siguiente manera

a /P
Por ejemplo

1 3, 1, 5
En resumen:

pero

1 / 3, 4, 2

Denicin 5.2.1
Si un objeto pertenece a

(Conjunto)

Un conjunto es una coleccin o agrupacin

de objetos, a los que se les llama elementos. Los conjuntos nitos se pueden describir con una lista de sus elementos separados por comas.

es un elemento de un conjunto

P,

entonces se dice que

y se denota de la siguiente manera:

aP
y si un objeto

no pertenece a

se denota de la siguiente manera

a /P

5.2. CONJUNTOS Y SUBCONJUNTOS


Algunos conjuntos muy renombrados son: El conjunto de los nmeros reales

67

R Z = {..., 3, 2, 1, 0, 1, 2, 3, ...} N = {0, 1, 2, 3, 4, 5, ...}

El conjunto de los nmeros enteros

El conjunto de los nmeros naturales

Respecto a este ltimo conjunto, hay algunos matemticos que aceptan el 0 como parte de los nmeros naturales y hay matemticos que no lo aceptan. Por lo general los que no aceptan el 0 como parte de los nmeros naturales es porque en teora de nmeros no se comporta como el resto de ellos, y adems, el 0 fue un nmero desconocido para muchas civilizaciones. Pero en las ciencias de la computacin se suele aceptar al 0 como nmero natural, y no es por mero capricho, esto tiene una razn importante: tanto en el manejo de conjuntos como en la induccin, el 0 se comporta como nmero natural. Se dice que un conjunto de

es subconjunto de

B,

si todos los elementos

tambin son elementos de

B.

O dicho de otra manera:

Denicin 5.2.2 (Subconjunto).


todo elemento como

Se dice que

tal que

c A

se cumple

A es subconjunto de B si para que tambin c B y se denota

A B.

Por ejemplo: El conjunto de las vocales es un subconjunto del conjunto del alfabeto. El conjunto de los nmeros pares es un subconjunto del conjunto de los nmeros enteros.

{a, b} {b, c, d, a, x} NZ ZR
Los subconjuntos son muy utilizados en la bsqueda exhaustiva, ya que muchos problemas pueden ser reducidos a encontrar un subconjunto que cumpla con ciertas caractersticas. Ya sea para poder resolver un problema con bsqueda exhaustiva o para poder encontrar una mejor solucin partiendo de una bsqueda exhaustiva es preciso saber cmo generar todos los subconjuntos.

Problema Resuelto 5.2.1.


arreglo

Supongamos que hay un arreglo global

que

es de tipo entero. Escribe una funcin que reciba un entero

e imprima todos los subconjuntos del conjunto de primeros

n como parmetro n elementos del

C.

68

CAPTULO 5. BSQUEDA EXHAUSTIVA


Este problema de generar todos los subconjuntos se puede trans-

Solucin

formar en el problema de generar cadenas de caracteres de una manera bastante sencilla: Cada elemento de determinado. Vamos a crear entonces un decir

puede estar presente o ausente en un subconjunto

arreglo de presencias
C [i]

al que llamaremos est presente.

P,

es

P [i]

ser 0 si

C [i] n
en

est ausente, y ser 1 si

El problema entonces se reduce a generar todas las posibles cadenas binarias de longitud

P.

La tcnica de expresar un problema en trminos

de otro es indispensable para resolver este tipo de problemas. Cdigo 5.3: Generacin de subconjuntos imprime \ _subconjuntos (

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

int

int i ; i f (m<n ) {

int

n,

int

m=1){

i f ( n==0){ for ( i =0; i <m; i ++) i f (P [ i ]==1)


printf (" % d " , C[ i ] ) ;

m =n ;

else {

p r i n t f ( " \n" ) ;

P [ n 1]=0; i m p r i m e \ _ s u b c o n j u n t o s ( n 1 , m) ; P [ n 1]=1; i m p r i m e \ _ s u b c o n j u n t o s ( n 1 , m) ; } } El cdigo anterior imprime todos los subconjuntos de declaracin de

C,

se omite la

para no gastar espacio y se utiliza el parmetro

co-

mo el nmero de elementos de los que hay que imprimir subconjuntos para que no se confunda con el parmetro

que es el nmero de elementos de los

que hay que generar los subconjuntos. Ntese que si se llama a la funcin dejando el parmetro predeterminado para

m,

posteriormente

tomar el valor de

n.

Problema Resuelto 5.2.2. Considera la misma situacin que en el ejemplo


5.2.1, escribe una funcin que reciba como parmetros cada subconjunto contenga exactamente m elementos.

m,

e imprima

todos los subconjuntos de los primeros n elementos del arreglo C tal que

5.2. CONJUNTOS Y SUBCONJUNTOS

69

Solucin
denir

Como ya vimos en el ejemplo anterior, este problema se puede como 1 si

reducir a un problema de generacin de cadenas binarias. As que vamos a

P [i]

C [i]

esta presente y como 0 si

C [i]

esta ausente del

subconjunto. Sea adems

S (n, m)

los subconjuntos de los primeros

elementos del

arreglo tal que cada subconjunto tenga exactamente longitud

elementos.

Por ello el problema se reduce a encontrar todas las cadenas binarias de

que contengan exactamente

1s.

Antes de encontrar el algoritmo para resolver esto es necesario hacer notar las siguientes propiedades: Si

m > n

no hay cadena binaria que cumpla con los requisitos, ya

que requiere tener exactamente corta(menor que Si Si

1s, pero su longitud es demasiado

m).

m=0

solo hay un subconjunto: el conjunto vaco.

n > 0 y n > m entonces S (n, m) esta compuesto nicamente por S (n 1, m) y por todos elementos de S (n 1, m 1) aadindoles C [n]
a cada uno(vase Ejemplo 11).

Con estas propiedades ya se puede deducir el algoritmo. Al igual que en el ejemplo anterior, utilizaremos un parmetro auxiliar al que llamaremos

que indicar de qu longitud era la cadena inicial. La

intencin es que a medida que se vayan haciendo las llamadas recursivas, el programa vaya formando una cadena que describa el subconjunto en Dicho de otra manera deniremos una funcin llamada

m, l), que generar todos los subconjuntos de tamao n elementos del arreglo C e imprimir cada subconjunto representado en P [0..n 1] junto con los elementos denidos en P [n..l 1](los que ya se denieron en llamadas recursivas anteriores), es decir, imprimir P [0..l 1]
para cada subconjunto. Si Si

P. imprime_subconjuntos(n, m de los primeros

m > n m=0

entonces simplemente hay que terminar la ejecucin de esa

funcin pues no hay nada que imprimir. solamente queda el conjunto vaco, entonces solo hay que im-

P [0..l 1]. En otro caso hay que asegurarse de que C [n 1] este ausente en el subconjunto y llamar a imprime_subconjuntos(n-1, m, l), esto generar todos los subconjuntos de los primeros n elementos del arreglo C , con exactamente m elementos, donde C [n 1] no esta incluido. Posteriormente, hay que poner a C [n] como presente y llamar a imprime_subconjuntos(n-1, m-1, l) para generar los subconjuntos donde C [n] esta incluido.
primir el subconjunto representado en

70

CAPTULO 5. BSQUEDA EXHAUSTIVA

Cdigo 5.4: Generacin de todos los subconjuntos de C con m elementos 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 } } }

void

imprime_subconjuntos (

int int i ; i f (m>n ) { return ; } i f (m==0){ for ( i =0; i < l ; i ++) i f (P [ i ]==1) else {
p r i n t f ( " \n" ) ; P [ n 1]=0;

n,

int

m,

int

l ){

printf (" % d

" , C[ i ] ) ;

i m p r i m e _ s u b c o n j u n t o s ( n 1 , m, P [ n 1]=1;

l); l);

i m p r i m e _ s u b c o n j u n t o s ( n 1 , m 1 ,

5.3. Permutaciones
As como a veces un problema requiere encontrar un subconjunto que cumpla con ciertas caractersticas, otras veces un problema requiere encontrar una secuencia de objetos que cumplan con ciertas caractersticas sin que ningn objeto se repita; es decir, una permutacin. Una permutacin se puede denir como una cadena que no utiliza 2 veces un mismo elemento. Las permutaciones, al igual que los conjuntos, suelen representarse como una lista de los elementos separada por comas pero delimitada por parntesis. Sin embargo, a diferencia de los conjuntos, en las permutaciones el orden en el que se listan los elementos si importa. y diferente a la permutacin

(3, 2, 5) es diferente a la permutacin (5, 3, 2) (2, 3, 5). Si se tiene un conjunto A, una permutacin de A es una cadena que utiliza cada elemento de A una sola vez y no utiliza elementos que no pertenezcan a A. Por ejemplo, sea A el conjunto (a, b, c, d), una permutacin de A es (a, c, d, b) otra permutacin es (d, a, c, b).
Por ejemplo, la permutacin

5.3. PERMUTACIONES

71

Teorema 4. Sea A un conjunto con n elementos, existen n! permutaciones


de A. Demostracin.
de Un conjunto con un solo elemento o ningn elemento tiene

solamente una permutacin. Si para algn

n se sabe el nmero de permutaciones que tiene un conjunto


elementos?

elementos, es posible averiguar el nmero de permutaciones con un

conjunto de

n+1

Consideremos una permutacin de un conjunto con representa el

elementos(aqui

ai

i-simo

nmero de la permutacin):

(a1 , a2 , a3 , a4 , ..., an )
Suponiendo que quisiramos insertar un nuevo elemento en esa permutacin, lo podramos poner al principio, lo podramos poner entre

a1

a2 , entre a2

a3 ,

entre

a3

insertar en existen

a4 , n+1
y

... , entre

an1

an ,

o bien, al nal. Es decir, lo podramos

posiciones diferentes.

Ntese entonces que por cada permutacin de un conjunto de

n elementos,

n+1

permutaciones de un conjunto de

n+1

elementos conservando

el orden de los elementos que pertenecen al conjunto de Y tambin, si a una permutacin de un conjunto elementos. Sea

n elementos. con n + 1 elementos

se

le quita un elemento, entonces queda una permutacin de un conjunto con

p(n)

el numero de permutaciones de un conjunto con

elementos,

podemos concluir entonces que

p(0) = 1 y p(n) = p(n 1)n, esta recurrencia n


elementos

es exactamente igual a la funcin factorial. Por ello el nmero de permutaciones de un conjunto con diferentes es

n!.

Problema Resuelto 5.3.1.


tivos.

Escribe un programa que dado

n,

imprima to-

das las permutaciones del conjunto de los primeros

nmeros enteros posi-

Solucin
Si

Antes que nada tendremos un arreglo p[] = {1, 2, 3, ..., n} n = 0 entonces solamente hay una permutacin. Si n > 0, hay que generar todas las permutaciones que empiecen con p[0], las que empiecen con p[1], las que empiecen con p[2], las que empiecen con p[3], ... , las que empiecen con p[n 1](sobra decir a estas alturas que una permutacin de un conjunto con n nmeros es un solo nmero seguido de una permutacin de un conjunto con n 1 nmeros).

72

CAPTULO 5. BSQUEDA EXHAUSTIVA


Esto se puede hacer recursivamente, ya que la funcin generar todas las

permutaciones con n elementos, sin importar qu elementos sean. Es decir, si el programa intercambia el valor de llamar a la funcin con los primeros para toda

p[i]

con el valor de

p[n 1]

y manda a

n 1, la funcin generar todas las permutaciones de n 1 elementos con el antiguo valor de p[i] al nal. As que hay n 1.

que repetir este procedimiento de intercambiar y mandar llamar a la funcin

entre 0 y

As que el programa queda de esta manera: Cdigo 5.5: Generacin de permutaciones 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 } } } }

#include < s t d i o . h> int p ; int N ; void p e r m u t a c i o n e s ( int n ) { int i , aux ; i f ( n==0){ // Si n=0 imprime l a permutacion for ( i =0; i <N ; i ++)
printf (" % d p r i n t f ( " \n" ) ; ", p[ i ]) ;

else { // Sino , r e a l i z a l o s i n t e r c a m b i o s for ( i =0; i <n ; i ++){


aux=p [ i ] ;

c o r r e s p o n d i e n t e s y e n t r a en r e c u r s i o n // I n t e r c a m b i a l o s v a l o r e s de p [ n 1] y p [ i ] //Hace l a

p [ i ]= p [ n 1 ] ; p [ n 1]=aux ; p e r m u t a c i o n e s ( n 1) ; aux=p [ i ] ;

llamada r e c u r s i v a //Pone l o s v a l o r e s de p [ n 1] y p [ i ] en su l u g a r

p [ i ]= p [ n 1 ] ; p [ n 1]=aux ;

int

main ( ) {

int

i ;

scanf (" % d" , p=

new int [ N ] ; for ( i =0; i <N ; i ++) // I n i c i a l i z a e l c o n j u n t o

de l a en tra da

\&N) ;

// Lee e l tamao d e l c o n j u n t o

5.3. PERMUTACIONES
27 28 29 30 } p [ i ]= i +1;

73

return

p e r m u t a c i o n e s (N) ; 0;

// E j e c u t a l a r e c u r s i o n

Nuevamente el cdigo de la funcin es bastante corto aunque el resto del programa hace el cdigo mas largo.

74

CAPTULO 5. BSQUEDA EXHAUSTIVA

Parte II Anlisis de Complejidad

75

77

Durante la parte I se vieron muchas formas de aplicar la recursin en la resolucin de problemas y algunas consecuencias matemticas tiles de la recursin. Sin embargo, los problemas mas interesantes en el diseo de algoritmos surgen al tomar en cuenta el anlisis de complejidad, a estas alturas resulta muy difcil seguir avanzando sin contar con esta herramienta, y a diferencia de la recursin, el anlisis de complejidad si requiere conocimientos previos para entenderse adecuadamente. Durante un par de captulos se hablar muy poco de programacin, luego la programacin volver, pero tendremos que esperar para regresar al diseo de algoritmos ya que nos concentraremos en el anlisis de algoritmos. Quiz est curva de aprendizaje pudiera parecer desmotivante puesto que el objetivo es dominar el diseo de algoritmos, pero no hay que olvidar que los fundamentos no slo sirven para entender el anlisis de complejidad, tambin pueden servir en el momento de resolver problemas. Otra cosa que hay que tomar en cuenta, es que despus de esta prolongada ausencia del diseo de algoritmos, el diseo de algoritmos regresar con una innidad de nuevos problemas planteados y ser posible avanzar mucho mas rpido.

78

Cap tulo

Tcnicas Bsicas de Conteo


Como el nombre del captulo lo indica, el tema a tratar son tcnicas de conteo, las cuales son estudiadas por la combinatoria enumerativa; dichas tcnicas, adems de servir para resolver problemas que involucran el conteo, sirven ampliamente para determinar cuantas veces se ejecutan algunos pasos en los algoritmos. A continuacin veremos algunos ejemplos donde se aplican las reglas bsicas de conteo para posteriormente identicarlas de manera objetiva.

Problema Resuelto 6.0.2.


a la ciudad de la ciudad a la ciudad

Hay 2 caminos que conducen de la ciudad

B,

3 caminos que conducen de la ciudad

a la ciudad

C.

Un

camino que conduce de la ciudad

B a la ciudad D y un camino que conduce C a la ciudad D De cuntas formas se puede ir de la ciudad A D? (vease la gura para tenerlo mas claro).

Solucin
la ciudad

Para llegar a la ciudad

solamente se puede llegar a travs de

o de la ciudad

B,

por cada forma de llegar a la ciudad

hay

B A C
Figura 6.1: Problema resuelto 6.0.2

79

80

CAPTULO 6. TCNICAS BSICAS DE CONTEO

C
Figura 6.2: Problema resuelto 6.0.3

una forma de llegar a la ciudad para llegar a la ciudad para llegar a la ciudad

y por cada forma de llegar a la ciudad

hay una forma de llegar a la ciudad

D. Por lo tanto, el nmero de D iniciando en la ciudad A es la suma de las B y a la ciudad D, es decir 2 + 3 = 5.

formas formas

Problema Resuelto 6.0.3.


A
a la ciudad

Hay cuatro caminos que conducen de la ciudad

B,

y tres caminos que conducen de la ciudad

a la ciudad

C.

Cuntos caminos que pasan por

conducen hasta

C?

v1 , v2 , v3 y v4 a los caminos que van desde A hasta B y w1 , w2 , w3 a los caminos que van desde la ciudad B hasta la ciudad C . Para ir desde A hasta C pasando por B es necesario llegar hasta B usando
Vamos a llamar alguno de los 6 caminos, y posteriormente elegir alguno de los tres caminos para ir desde

Solucin

hasta

C.

Es decir, existen los siguientes 12 caminos:

v1 , w1 v1 , w2 v1 , w3 v2 , w1 v2 , w2 v2 , w3 v3 , w1 v3 , w2 v3 , w3 v4 , w1 v4 , w2

81

v4 , w3 v1 hay algun w1 , alguno terminado en w2 y alguno terminado en w3 , por cada camino que empieza con v2 tambin hay 3 caminos terminados en w1 , w2 y w3 respectivamente; lo mismo para v3 y v4 . Por lo tanto hay (4)(3) = 12 caminos.
Aqu puede observarse que por cada camino que empieza con camino terminado en

Problema Resuelto 6.0.4.


a la ciudad

Hay

caminos que conducen de la ciudad

B,

caminos que conducen de la ciudad

a la ciudad

A C.

Cuntos caminos que pasan por

B x

conducen hasta

C?
la lo

Solucin
ciudad

Por cada una de las existen

formas para ir de la ciudad

formas para ir de la ciudad

tanto existen

xy

formas para ir desde

hasta

A hasta B hasta la ciudad C . Por C pasando por B .

Cdigo 6.1: Funcin f 1 2 3 4 5 6 7 8 9 } } }

int

f(

int A, int B) { int i , k , r =0; for ( i =0; i <A ; i ++){ for ( k =0; k<B ; k++){
r ++;

return

r;

Problema Resuelto 6.0.5.


el cdigo anterior?

Cul es el valor de retorno de la funcin

en

Solucin
la lnea 5.

Este problema se puede reducir en saber cuntas veces se ejecuta

Hay que hacer notar que la lnea 3 se ejecuta se ejecuta la lnea 3, la lnea 5 se ejecuta es

veces y por cada vez que

veces. Por lo tanto el nmero de

veces que se ejecuta la lnea 5(y por ende el valor de retorno de la funcin)

AB .

82

CAPTULO 6. TCNICAS BSICAS DE CONTEO

6.1. Reglas Bsicas de Conteo


A pesar de que los problemas de combinatoria enumerativa son bastante variados, la mayora pueden resolverse utilizando las llamadas

suma, regla del producto, biyeccion y recursin.

regla de la

Ya dedicamos batante tiempo a ver lo que es recursin, y adems a estas alturas ya deberas ser caps de utilizar la regla de la suma y el producto cuando te encuentres con un problema de combinatoria enumerativa, sin embargo, hay que identicarlas para poder justicar claramente nuestros razonamientos:

Teorema 5 (Regla de la Suma). Si cierto objeto x puede ser elegido de n maneras diferentes y otro objeto y puede ser elegido de n1 maneras diferentes, entonces el nmero de formas de elegir x o y es n + n1 . Una manera mas precisa de decirlo, es que para dos conjuntos disjuntos A y B , el nmero de elementos que pertenecen a A o a B es |A| + |B |.
Aunque la regla anterior es bastante evidente tanto en su denicin como en su aplicacin, la siguiente regla es menos obvia de ver y aplicar que la anterior.

maneras diferentes y otro objeto y puede ser elegido de m maneras diferentes, entonces el nmero de formas de elegir x y posteriormente elegir y es nm. Una manera mas precisa de decirlo, es que para dos conjuntos A y B , el nmero de pares ordenados (a, b) tales que a A y b B es |A||B |.
Por ejemplo, si queremos formar palabras de longitud 2 nicamente con las letras

Teorema 6

(Regla del Producto)

. Si cierto objeto x puede ser elegido de n

a, b y c el nmero de formas de hacerlo es 9: aa, ab, ac, ba, bb, bc, ca, cb y cc. Si se quisieran formar palabras de longitud 3 nicamente con las 3 letras a, b y c el nmero de formas de hacerlo sera 3 = 27.
Una aplicacin computacional es la siguiente: Alguna vez te has preguntado por qu en C y C++ los enteros con signo soportan valores desde

2147483648

hasta

2147483647

y qu tiene que ver eso con que sean tipos

de datos de 32 bits?. Cada bit es un dgito que puede ser solamente

y el conjunto

de dgitos binarios que sean iguales a 1 determina el nmero almacenado. Por ello hay 32 dgitos y cada uno puede ser elegido de dos maneras, por regla del producto la cantidad de nmeros que pueden ser almacenados en 32 32 bits es: 2 = 4294967296, y ahora, un int puede tener cualquiera de

2147483647 (2147483648) + 1 = 4294967296


de

valores diferentes y por ese

mismo motivo los enteros sin signo(tambin de 32 bits) soportan valores des-

hasta

4294967295.

6.2. CONJUNTOS, SUBCONJUNTOS, MULTICONJUNTOS

83

La unica regla que falta es la de las biyecciones; su uso tambin es bastante natural, sin embargo la forma de utilizarlo involucra bastante creatividad y es mas complicado de denir. La idea bsica de la biyeccin es la de contar algo distinto a lo que inicialmente se quiere contar y luego demostrar que lo que se cont es del mismo

tamao

que lo que inicialmente se quera contar.

Por ejemplo, para contar cuantos subconjuntos tiene un conjunto de elementos, contamos cuantas cadenas hay de longitud solamente 0s y 1s.

tales que contengan

La manera de demostrar que contar el nmero de subconjuntos equivale a contar el nmero de cadenas con 0s y 1s se basa en decir que a cada cadena le corresponde un nico subconjunto y viceversa. Es decir, dos conjuntos elemento en

tienen la misma cantidad de elementos, si

es posible asignarle a cada elemento de

una nica pareja en

B,

y a cada

asignarle una nica pareja en

A.

Al conjunto de todas las

parejas se le conoce como biyeccin. Siendo mas precisos:

Teorema 7 (Regla de la Biyeccin). Si para dos conjuntos A y B existe un


conjunto de pares ordenados P tal que para cada a A existe un nico b B tal que (a, b) P y para cada b B existe un nico a A tal que (a, b) P entonces se dice que P es una biyeccin entre A y B y adems |A| = |B |.

6.2. Conjuntos, Subconjuntos, Multiconjuntos


Ya en la seccin 5.2 habamos denido conjuntos y subconjuntos, aqu exploraremos algunas de sus propiedades y deniremos multiconjunto. La siguiente propiedad es fundamental en las tecnicas de conteo:

Teorema 8 (Nmero de Subconjuntos). Si un conjunto C tiene n elementos, el nmero de subconjuntos de C es exactamente 2n .


Demostracin.
T C
tal que est o bien en Cada uno de los

elementos puede estar dentro o fuera del

subconjunto, ntese que para todo subconjunto

SC

existe un subconjunto

S y T no S o en T . S

tienen elementos en comn y cada elemento de

De esta manera, todo elemento de formar parte de regla del producto se concluye que

puede ser elegido de dos formas: para

o para formar parte de

tiene n elementos, por n tiene exactamente 2 subconjuntos. y como

84

CAPTULO 6. TCNICAS BSICAS DE CONTEO


Ahora vamos a introducir un nuevo concepto, se trata del concepto de los

multiconjuntos. La idea de un multiconjunto es poder representar un conjunto con elementos repetidos, ya que para modelar ciertos sistemas se pueden tener elementos que compartan las mismas propiedades; por ejemplo en el ajedrez, todos sabemos que cada jugador cuenta inicialmente con 16 piezas, sin embargo algunas de esas piezas son idnticas y con ellas se podra hacer exactamente lo mismo; otro ejemplo interesante puede ser el dinero que se traiga en la cartera, puede haber varias monedas que en la prctica son exactamente iguales. Asi que de manera general, un multiconjunto es un

mentos que se pueden repetir.


denicin:

conjunto con ele-

Pero esta claro que esta denicin aunque es

bastante comprensible y dice mucho sobre la aplicacin de los multiconjuntos es inadmisible de manera formal, por lo que se ha construido esta otra

Denicin 6.2.1

(Multiconjunto)

Un multiconjunto se dene como el par funcin tal que a cada elemento

(C, f ) donde C es un conjunto y f es una de C le asigna un nmero entero positivo.


El proposito de

en sta denicin es decir cuntas veces aparece cada

elemento en el multiconjunto. Lo que ahora necesitamos tener es un anlogo a los subconjuntos pero en los multiconjuntos, el cual llamaremos submulticonjunto. Nuestro sentido comn nos dice que si queremos denir un submulticonjunto

de un multiconjunto

entonces todos los elementos de

deben de

aparecer en veces en

A,

y adems, es inadmisible que un elemento de

aparezca mas

que en

A.

Mas formalmente:

Denicin 6.2.2

(Submulticonjunto)

Sea

S = (D, g ) es un submulticonjunto de d D se cumple que g (d) f (d). Y se denota SA

A = (C, f ) A si D C y
como:

un multiconjunto, adems para todo

Lo siguiente que nos debemos preguntar es Cuntos submulticonjuntos tiene un multiconjunto nito Es decir, cada elemento

A = (C, f )?.

Para contarlos procederemos de

la misma manera que para contar los subconjuntos.

cC

puede estar desde 0 hasta

f ( c)

veces en el

submulticonjunto. De esta manera cada elemento de

puede ser elegido de

f ( c) + 1

formas.

6.3. PERMUTACIONES

85

Usando regla del producto concluimos que el nmero de submulticonjuntos de

A {a1 , ..., an }. A

es

(f (a1 ) + 1)(f (a2 ) + 1)(f (a3 ) + 1)...(f (an ) + 1)

donde

C =

Una observacin interesante es que segn esa de submulticonjuntos es los conjuntos.

frmula

si cada elemento de

aparece una sola vez, es decir si f (ai ) = 1 para toda i entonces el nmero 2n , lo cual coincide con nuestras observaciones en

6.3. Permutaciones
Ya se haban mencionado las permutaciones, sin embargo, solamente se denieron de manera que no pudiera haber elementos repetidos. Aqu se vern las permutaciones de una forma ms general.

Denicin 6.3.1 (Permutacin).

Una permutacin es un reacomodo de ob-

jetos o smbolos en secuencias diferentes. Las permutaciones, al igual que los conjuntos, suelen representarse como una lista de los elementos separada por comas pero delimitada por parntesis. Sin embargo, a diferencia de los conjuntos, en las permutaciones el orden en el que se listan los elementos si importa. Por ejemplo, hay 6 permutaciones del conjunto

C = {X, Y, Z }:

(X, Y, Z ) (X, Z, Y ) (Y, X, Z ) (Y, Z, X ) (Z, X, Y ) (Z, Y, X )


Pero solamente hay 3 permutaciones del multiconjunto

C = {A, A, B }:

(A, A, B ) (A, B, A) (B, A, A)

con repeticin.

Las permutaciones de multiconjuntos se conocen como

permutaciones

86

CAPTULO 6. TCNICAS BSICAS DE CONTEO


Ya habamos visto que un conjunto con

elementos tiene exactamente

n!

permutaciones, a continuacin descubriremos cmo explotar y extrapolar

esta propiedad.

Problema Resuelto 6.3.1. Para una empresa se requiere que se ocupen los
siguientes cargos: presiente, vicepresidente, secretario, vicesecretario, barrendero. Solamente puede(y debe) haber un presidente, un vicepresidente, un secretario, un vicesecretario pero no hay lmite de barrenderos. Si 6 personas van a ocupar cargos de cuntas formas pueden ocuparlos?.

Solucin

Dado que hay 5 cargos diferentes y 4 de ellos son nicos, el

nmero de barrenderos es

6 4 = 2. A
y a otro cargo como

Primero veremos de cuantas formas se pueden elegir a los barrenderos. Si etiquetamos a un cargo de barrendero como elegir de entre las 5 restantes al barrdendero formas de elegirlos, pero dado que si nos

B,

entonces podemos elegir de entre 6 personas al barrendero

A y luego podemos B , eso da un total de (6)(5) = 30 olvidamos de las etiquetas A y B ,

el rden en el que se eligen en realidad no importa, veremos que cada forma de elegirlos se est contando dos veces, por lo que el nmero de formas de elgir a los barrenderos es

(6)(5)/2 = 15.

Una vez elegidos a los barrenderos quedan 4 cargos diferentes y 4 personas diferentes, ntese que por cada permutacin de las personas existe una manera de asignarle los cargos(a la primer persona presidente, a la segunda vicepresidente, a la tercera secretario y a la cuarta vicesecretario). Por ello el nmero de formas de asignar 4 cargos diferentes a 4 personas diferentes es 4!=16. Por regla del producto el nmero total de formas de asignar los puestos de trabajo es

(15)(16)

o dicho de otra forma:

6! 6! )((6 2)!) = 4!2! 2!

Problema Resuelto 6.3.2. Para una empresa se requiere que se ocupen los
siguientes cargos: presiente, vicepresidente, secretario, vicesecretario, barrendero. Solamente puede(y debe) haber un presidente, un vicepresidente, un secretario, un vicesecretario pero no hay lmite de barrenderos. Si 8 personas van a ocupar cargos de cuntas formas pueden ocuparlos?.

6.3. PERMUTACIONES

87

Solucin

El problema es exactamente el mismo que el del ejemplo anterior,

solamente que ahora el nmero de barrenderos es

8 4 = 4. 8!.

Si los 4 puestos de barrenderos fueran todos diferentes entre s entonces est claro que el nmero de formas de asignar los cargos sera Pero por cada manera de asignar 8 personas a 8 cargos diferentes existen

(8 4)! = 4!

maneras de asignar 8 personas a 4 cargos diferentes y un cargo

repetido 4 veces. Esto es porque las personas que estuvieran ocupando los cargos de barrenderos seran 4 y podran reordenar de De esta manera la solucin es:

4!

formas.

8! 4!

Problema Resuelto 6.3.3. Un restaurant requiere 3 meseros, 4 cocineros y


4 barrenderos, si 20 personas quieren trabajar y estan igualmente capacitadas para ocupar cualquiera de los cargos, de cuantas formas pueden ocuparlos?

Solucin

Primero que nada intentaremos transformar este problema a al-

go parecido al problema anterior, debido a que es mas fcil tratar con un problema previamente resuelto. El nmero de personas que ocuparn al menos uno de los cargos es un total de

3 + 4 + 4 = 11,

por lo que 9 personas se quedarn con las ganas

de trabajar(pero sin poder hacerlo). As que para resolver este problema imaginaremos un nuevo cargo en el cual se quedarn las 9 personas que no consigan ninguno de los otros. Si las 20 personas se acomodaran en una la sera posible asignarles a los primeros 3 el cargo de meseros, a los siguientes 4 el cargo de cocineros, a los siguientes 4 el cargo de barrenderos y a los ultimos 9 nuestro cargo imaginario; en efecto, si se quieren asignar cargos de alguna manera, sin importar la que sea, siempre es posible ordenar a las 20 personas en una la de tal forma que se asignen los cargos de manera deseada utilizando el criterio que ya se mencion. Pero an existen varias formas de ordenar a la la que producen la misma asignancin de empleos. Por ahora solo sabemos que el nmero de formas de ordenar la la es los meseros de

20!.
formas distintas, es posible reordenar a los cocieneros de

Ahora, para cualquier manera de ordenar la la, es posible reordenar a formas distintas, es posible reordenar a los barrenderos de

4! 4! formas distintas y adems es posible reordenar a los que ocupan el cargo imaginario de 9! for-

3!

mas diferentes y todo esto produciendo la misma asignacin de empleos; por

88

CAPTULO 6. TCNICAS BSICAS DE CONTEO

ejemplo, si se cambian de rden las primeras 3 personas de la la sin mover a las dems las 3 personas seguirn siendo meseros y las dems personas seguirn ocupando los mismos cargos. Por regla del producto, para cualquier manera de ordenar la la, es posible reordenarla sin cambiar la asignacin de empleos de distintas. Por lo que el nmero total de maneras de asignar los empleos es:

(3!)(4!)(4!)(9!)

formas

20! 3!4!4!9!
Viendo la solucin del ejemplo anterior, se puede encontrar fcilmente una demostracin anloga del siguiente teorema, el cual resulta obvio despus de ver la solucin del ejemplo anterior:

conjunto donde C = c1 , c2 , ..., cn son los elementos que posee y f (ci ) es el nmero de veces que se encuentra cualquier elemento ci en M . El nmero de permutaciones del multiconjunto M es:
n Pf (c1 )f (c2 )...f (n) =

Teorema 9

(Permutaciones con Repeticin)

. Sea M = (C, f ) un multi-

n! f (c1 )!f (c2 )!...f (n)!

6.4. Combinaciones
Como ya habamos visto en la Parte I, las combinaciones, o coecientes binomiales son el nmero de subconjuntos con exactamente cogidos de un conjunto con exactamente

elementos es-

elementos para una

y una

dadas. Y se denota de la siguiente manera:

n k
Ya habamos se haba visto en la Parte I cmo calcular las combinaciones utilizando el tringulo de Pascal. Pero es conveniente conocer una frmula cerrada para calcularlas y no solamente una funcin recursiva. Recordando los ejemplos de la seccin de permutaciones con repeticin, podemos transformar este problema a tener una empresa con 2 puestos de trabajo uno con

plazas y otro con

nk

plazas y encontrar el nmero de

formas en las que un tipo con

trabajadores pueden ocupar todos los puestos. O dicho

de otra manera, encontrar el nmero de formas de ordenar

elementos de

nk

elementos de otro tipo.

6.5. SEPARADORES

89

Teorema 10

(Combinaciones de

en

k ).

n k

n! (n k )!k !

6.5. Separadores
Muchos problemas de combinatoria se resuelven agregando al planteamiento unos objetos llamados

separadores, para los cuales existe una biyeccin

entre lo que se quiere contar y ellos. Es difcil ilustrar de manera general lo que son los separadores. As que se mostrar en ejemplos cmo usarlos.

Problema Resuelto 6.5.1.

Se tienen 20 canicas idnticas y se quieren

guardar en 3 frascos diferentes sin importar si uno o dos de los frascos quedan vacos. De cuntas formas se puede hacer esto?

Solucin

En lugar de imaginar 20 canicas, imagina 20 guiones alineados

en una recta de la siguiente manera: - - - - - - - - - - - - - - - - - - - Ahora, vamos a insertar 2 brras verticales entre los guiones. A dichas barras verticales les llamaremos separadores. Una posible manera de insertarlas sera as: - - - - - - - - - - | - - - | - - - - - - Lo anterior puede ser interpretado como poner 10 canicas en el primer frasco, 3 canicas en el segundo frasco y 7 canicas en el tercer frasco, dicho de otra manera, los guiones anteriores al primer separador se ponen en el primer frasco, los guiones entre el primero y segundo separador se ponen en el segundo frasco y los guiones posteriores al segundo separador se ponen en el tercer frasco. Asi que por cada manera de insertar dos separadores entre un total de 20 guiones existe una manera de guardar 20 canicas identicas en un total de 3 frascos diferentes y viceversa, es decir Hay una biyeccin entre el nmero de separadores y el nmero de formas de repartir las caincas!. Adems, el nmero de formas de poner los dos separadores, es el nmero de permutaciones con repeticin de 20 objetos de un tipo y 2 objetos de otro tipo, lo cual es equivalente a:

20+2 P20 ,2 =

22! = 20!2!

22 2

90

CAPTULO 6. TCNICAS BSICAS DE CONTEO

Problema Resuelto 6.5.2.

De cuntas formas se pueden formar en una

la 20 marcianos diferentes y 7 jupiterianos diferentes de manera que no haya ningn par de jupiterianos adyacentes? Consideremos primero el caso donde hay 20 marcianos idnticos y 7 jupiterianos idnticos. Queremos saber el nmero de formas de dividir a 20 marcianos en 8 grupos de manera que en cada uno de los grupos haya al menos un marciano. Y luego el primer grupo se puede colocar frente al primer jupiteriano, el segundo grupo frente al segundo jupiteriano, y asi sucesivamente, quedando el ltimo grupo detras del ltimo jupiteriano. Para hacer eso primero hay que asignarle un marciano a cada grupo, quedando 12 marcianos disponibles para repartirse en 8 grupos; el nmero de formas de hacerlo se puede calcular aadiendo 7 separadores a los 12 12+7 marcianos, con lo cual resultan formas de hacerlo. 7 Ya calculamos el nmero de maneras de ordenarlos si fueran identicos, pero dado que son diferentes, por cada manera de ordenarlos sin considerar las diferencias marciano-marciano y jupiteriano-jupiteriano existen

20!7!

maneras diferentes de reordenarlos sin cambiar el tamao de ninguno de los 8 grupos. Por lo tanto, el nmero de formas en las que se pueden acomodar 20 marcianos diferentes y 7 jupiterianos diferentes en una la sin que haya dos jupiterianos juntos es:

19 (20!)(7!) 7

Cap tulo

Funciones
Las funciones son un tema fundamental dentro de las ciencias de la computacin al igual que en el resto de las matemticas; son necesarias para entender correctamente la mayora de los algoritmos y en ellas se centra el anlisis de complejidad. El concepto de funcin muy comnmente es malentendido o usado de una manera incorrecta. A veces se tiene la creencia de que una funcin necesariamente se puede expresar como una ecuacin o que todos los valores de las funciones son conocidos; y para los programadores es mas comn pensar en las funciones como mquinas que reciben parmetros y devuelven valores. Para ciertas aplicaciones son tiles esas anlogas, pero para lo que viene despus se requiere entender perfectamente lo qu es una funcin y cmo aparecen de manera natural mientras se resuelven problemas. No es de sorprender entonces que se les dedique un captulo a las funciones, ya que en los prximos captulos estaremos trabajando con ellas y resulta razonable dedicar un tiempo a denir realmente con qu estamos trabajando.

7.1. Las Funciones como Reglas


Como se deca unos prrafos atrs, las funciones muchas veces son vistas como mquinas que reciben parmetros y devuelven valores; aunque esta forma de verlas es til en la programacin, es muy limitada en la resolucin de problemas. Una forma parecida de ver las funciones es como conjunto

reglas,

es

decir, una funcin puede ser vista como una regla que a cada elemento de un

le asigna un elemento(y solo uno) de un conjunto

Y.

Los siguientes ejemplos sirven para ilustrar mejor esta manera de ver las

91

92

CAPTULO 7. FUNCIONES

funciones: 1. La regla que le asigna a cada nmero real su mitad. 2. La regla que le asigna a cada nmero entero positivo descrita por:

su sumatoria

(n)(n + 1) 2
3. La regla que le asigna una CURP a cada ciudadano. 4. La regla que le asigna a cada par de nmeros

a
3 . 4

su promedio

a+b . 2

5. La regla que le asigna

53,

1 a 2

42

6. La regla que le asigna un tiempo de ejecucin a cada conjunto de entradas en un programa. Con estos ejemplos debe de quedar claro que una funcin puede ser cualquier regla que le asigne elementos de un conjunto un conjunto

a los elementos de

X , y cualquier regla

se reere a que las reglas pueden no ser de-

scritas por alguna ecuacin o expresin algebrrica, pueden no estar denidas en todos los nmeros, pueden incluso no estar denidas en nmeros(como en el ejemplo 3), y es posible que en algunos casos(como en el ejemplo 5) no se conozca a qu nmeros u de un conjunto

objetos

se puede aplicar.

Si una funcin le asigna un elemento de un conjunto

a cada elemento

entonces se dice que el dominio de la funcin es de dominio

X f,
Si

y la codominio

Y. g
y

Aunque no es un requisito, las funciones se suelen designar por la letra y cuando hay mas de una funcin se suelen usar tambin las letras

es una funcin,

una variable tal que

asocia con

se expresa como

f (x)

y se lee

x X , entonces como  f de x.

el valor que

h. f

La funcin del primer ejemplo se puede expresar como:

f (x) =

x 2

para todo nmero real

La del segundo ejemplo se puede denir como

f (n) =

(n)(n + 1) 2

para todo entero positivo

El tercer ejemplo no se puede denir completamente con esta notacin, ya que no hay expresin algebrarica que represente una CURP o un ciudadano, y tampoco conocemos un procedimiento para encontrar la CURP;

7.1. LAS FUNCIONES COMO REGLAS

93

solamente sabemos que el dominio es el conjunto de ciudadanos y la imagen es el conjunto de las CURPs. Por este motivo es necesario denir mas notacin. Si su dominio y

es una funcin,

su codominio, se puede denotar de la siguiente manera:

f : D C
As ya podemos expresar el tercer ejemplo como una funcin:

f : D C

donde D es un conjunto de ciudadanos y C es un conjunto de CURPs

El cuarto ejemplo tambien puede resultar peculiar, ya que muchas veces se espera que el dominio de una funcin sea un solo nmero y no dos. Para ser precisos el dominio de la funcin del cuarto ejemplo es el conjunto de todos los pares rdenados de nmeros reales R2 .

(a, b), el cual se representa como

Asi que el cuarto ejemplo se puede describir as:

f (a, b) =
conocidos:

a+b 2

para todo par de nmeros (a, b)

R2

Para el quinto ejemplo no hay mas remedio que enumerar los valores

f (x) =

2, si x = 53 1 , si x = 42 = 2 3 = , si x = 4

El sexto ejemplo habla de una funcin que corresponde al tiempo que tarda en ejecutarse un programa con determinada entrada; esta funcin cobrar mucha importancia mas adelante y se le conoce como la funcin de tiempo. Despus de usar mucho tiempo las funciones, es natural comenzar a pensar = x para todo nmero 2 real n podra parecernos muy largo, as que muchas veces por practicidad en abreviaturas, como con el primer ejemplo  f (x) se omite la descripcin del dominio de la funcin y se deber asumir que el dominio es el conjunto de todos los nmeros reales para los cuales la funcin tiene sentido.

1 se sobreentiende que el dominio es x todos los nmeros reales excepto el 0. Sin embargo existen muchos intentos
Por ejemplo para la funcin

f ( x) =

por abreviar an ms esta notacin pero la mayora resultan inadecuados.

94

CAPTULO 7. FUNCIONES
Por ejemplo sustituir

x x por equivaldra a hablar de un nmero 2 2 real en lugar de una funcin; la nica manera aceptable de abreviar esto x an ms es como x , esto no representa una gran mejora en cuanto a 2 la abreviacin y en ocaciones puede parecer menos claro, la nica ventaja

f ( x) =

que parece tener esta notacin es que no hay que asignarle un nombre a la funcin y puede servir cuando se est trabajando con muchas funciones y no sea necesario nombrar a todas. Luego de ver estos ejemplos es clara la ventaja de ver las funciones como y no solamente como

reglas

mquinas,

ya que es mas claro imaginar una

regla implcita que una mquina implcita, y no queda claro que una mquina simpre devuelva la misma salida para una entrada dada.

7.2. El Concepto Formal de Funcin


A pesar de que se pueden inferir muchas cosas imaginando a las funciones como a cosas ya conocidas y con propiedades bien denidas. La palabra

reglas, el concepto de regla es subjetivo, es preferible denir algo en base regla


puede signicar distintas cosas en distintos contextos.

Algo que sabemos muy bien cmo se comportan son los conjuntos y los pares ordenados. Deniremos una funcin a partir de esto para que no quede duda de qu es cmo se comporta una funcin. Primero que nada necesitamos denir un concepto bastante importante en muchas reas de las matemticas, y este es el del elementos de dos conjuntos dados. Por ejemplo, el producto cartesiano de El producto cartesiano de ntese que

producto cartesiano, este

concepto se reere al conjunto de todos los pares ordenados formados por y es son todos los puntos del

plano cartesiano cuyas cordenadas son enteras.

{1, 2}

{3, 4}

(1, 3)

si es parte del producto cartesiano pero

ello decimos que el producto cartesiano es

El hecho de que el producto cartesiano sea

no conmutativo. no conmutativo

{(1, 3), (1, 4), (2, 3), (2, 4)}, (3, 1) no lo es, por
hace que

este concepto sea aplicable a gran cantidad de cosas y algunas de ellas no estan muy relacionadas con las ciencias de la computacin, como lo es el producto cartesiano de los nombres y los apellidos, con el cual se obtiene el conjunto de todos los nombres completos vlidos.

Denicin 7.2.1 (Producto Cartesiano). El producto cartesiano de dos conjuntos y

A y B es el conjunto de todos los pares ordenados (a, b) tal que a A b B , y se denota como: AB

7.2. EL CONCEPTO FORMAL DE FUNCIN


A cualquier subconjunto de

95

AB

se le denomina

relacin de A en B , el

concepto de relacin es muy importante, ya que casi todo lo que estudian las matemticas(independientemente de las otras propiedades que tengan) son relaciones. es multiplo de

(a, b) tal que b Z en Z y se le llama divisibilidad. El conjunto de los pares de nmeros (a, b) tales que a < b son la relacin menor
Por ejemplo el conjunto de todos los pares de nmeros

es una relacin de

que; incluso las funciones son relaciones.

Denicin 7.2.2
en

(Funcin)

Una funcin tal que: y

con dominio en

y codominio

es un subjunto de Si Si

AB

(a, b) f

(a, c) f

entonces tal que

b=c (a, b) f

aA

entonces existe

bB

Admitimos las siguiente notacin: Una funcin

con dominio en

y codominio en

se denota como

f : A B . (a, b) f f (a)
se denota como

f (a) = b. (a, f (a)) f . (x, f (x)) f.

es aquel nmero tal que

x f (x)
para todo

se reere al conjunto de todos los pares ordenados

en el dominio de

Muchas veces hay que tener presente la denicin anterior, sin embargo, sigue siendo mas til para resolver problemas el hecho de ver una funcin como una regla. En general este tipo de deniciones sirven para aclarar aspectos oscuros respecto a lo que puede ser y lo que no puede ser una funcin evitando as llegar a contradicciones.

96

CAPTULO 7. FUNCIONES

Cap tulo

Anlisis de Complejidad
Comunmente se suele pensar que la computadora efecta sus operaciones con una velocidad innitamente rpida y con una capacidad de almacenamiento innita. Y esto es natural, ya que una computadora puede realizar millones de operaciones cada segundo as como almacenar informacin que no cabra ni en 100 bibliotecas. Sin embargo, en las matemticas existen funciones cuyos valores crecen a x una velocidad impresionante; por ejemplo, f (x) = 2 , mientras valores como

f (8) = 256, f (9) = 512, f (10) = 1024 son relativamente pequeos, tenemos que f (30) = 1099511627776, y si decimos que f (x) representa el nmero de sumas que debe realizar un programa, nos encontramos con que si x = 29
el programa tardara medio segundo en correr(en una computadora con 1.5 Ghz), si

x = 30

el programa tardara un segundo, si

x = 31

el programa

tardara 2 segundos, si

x = 42

el programa tardara mas de una hora, si

x = 59 el programa tardara 182 aos en ejecutarse y si x = 100 el programa


tardara en ejecutarse casi 31 mil veces la edad del universo. Por todo esto, ni siquiera las computadoras se salvan de tener que limitar el nmero de operaciones que realizan; pero la manera en que las computadoras se comportan es muy diferente a la manera en la que nos comportamos los humanos. Mientras que un humano puede comenzar a resolver un problema de cierta manera y luego darse cuenta de que existen formas ms rpidas de hacerlo, una computadora no, la computadora ejecutar el algoritmo para el que fue programada de principio a n; es por eso que los humanos necesitamos saber qu tan rpidos son los algoritmos antes de pedirle a una computadora que los ejecute. La mayor parte del estudio de diseo de algoritmos est enfocado en encontrar algoritmos sucientemente rpidos, y para lograr eso se requiere

97

98

CAPTULO 8. ANLISIS DE COMPLEJIDAD

una gran cantidad de anlisis, pero sobre todo se requiere saber qu clase de algoritmo se est buscando, y para saber eso es indispensable poder medir la velocidad del algoritmo, o dicho de otra manera, su complejidad. Dado que las computadoras son rpidas, es muy difcil darse cuenta si un programa es rpido o no con entradas pequeas, as que el anlisis de complejidad se enfoca a las entradas

grandes.

El anlisis de complejidad es una tcnica para analizar

qu tan rpido

crecen las funciones, y nos centraremos en una funcin que mide el nmero mximo de operaciones que puede realizar un algoritmo, a dicha funcin le llamaremos

funcin de tiempo.

Pero antes de analizar la complejidad de las funciones daremos varios ejemplos, el primero de ellos corresponde a una aplicacin en la fsica.

8.1. Un ejemplo no muy computacional


Imagina que sobre una silla de madera tienes un recipiente lleno de agua con forma cilndrica. Las patas de la silla tienen una base cuadrada de otros

centmetros por

centmetros, el radio del recipiente es de 20 centmetros y su altura es de

20

centmetros. Lo cual hace que el recipiente con agua tenga un peso

de poco ms de 25 kilogramos. La silla resiste muy bien ese peso, pero ahora imagina que conseguimos un modelo a gran escala formado con los mismos materiales de la silla y el recipiente. Digamos que cada centmetro del viejo modelo equivale a un metro del nuevo modelo, es decir, es una silla recipiente

100

veces mas grande y un

100

veces mas grande.

La pregunta es el nuevo modelo resistir?. Y la respuesta es simple: no. 2 En el modelo original 20cm sostenan 25kg , es decir, cada centmetro del modelo original sostena

1,25kg ,

sin embargo en el nuevo modelo, el

area de las patas suma un total de

4(500cm)(500cm) = 1000000cm2 y el 2 26 3 volmen del recipiente es (2000cm) (2000cm) = 8,042496 10 cm por lo 23 que el peso del nuevo recipiente es 8,042496 10 kg , y de esa manera, cada centmetro cuadrado de las nuevas patas tiene que soportar un peso de

804249600000000000kg !.
En general si un cuerpo que se encuentra sobre el suelo aumenta su tamao, se dice que el rea que esta en contacto con el suelo crece de manera cuadrtica, mientras que su peso aumenta de manera cbica. Eso mismo se aplica a los animales, durante muchos aos las pelculas nos han mostrado insectos gigantes y lo seguirn haciendo, sin embargo, sabiendo algo de anlisis de complejidad podemos darnos cuenta que eso es imposible,

8.2. ALGUNOS EJEMPLOS DE COMPLEJIDAD EN PROGRAMAS

99

ya que el rea de las patas aumenta de manera cuadrtica y su peso de manera cbica.

8.2. Algunos Ejemplos de Complejidad en Programas


Al igual que las reas y los volmenes que se mencionan en la seccin anterior, el tiempo de ejecucin de los programas tambin puede crecer de maneras cuadrticas, cbicas, lineales, logartmicas, etc., en esta seccin se analizar el tiempo de ejecucin de varios programas. Supn que tienes la siguiente funcin: Cdigo 8.1: Funcin fnc 1 2 3 4 5 6 7 8 9 } Suponiendo que tu computadora tardara un segundo en procesar Cunto tiempo crees que tardara en procesar } }

int

fnc (

int n ) { int i , k , r =0; for ( i =0; i <n ; i ++){ for ( k =0; k<n ; k++){
r+=i

k ;

return

r;

f nc(12000),

f nc(24000)

Los que estn manejando por primera vez el anlisis de complejidad posiblemente contestaran 2 segundos, pero la realidad es otra: se tardara 4 segundos. Si miramos bien el cdigo a la variable de cada par ordenado

0 k < n, por regla del 2 producto podemos concluir que se estan realizando n multiplicaciones. Tambin nos podemos percatar de que el bucle del centro itera n veces (i, k )
donde y por cada iteracin del bucle exterior, y esa es otra manera en la que podemos 2 ver que el bucle interior itera un total de n veces. Por simplicidad ignoraremos las sumas y las comparaciones (esas toman mucho menos tiempo que las multiplicaciones) y nos concentraremos en las multiplicaciones, si cada multiplicacin tarda en ejecutarse un tiempo tonces al llamar a al si

r se 0 i < n

le est sumando el producto

t, enf nc(12000) el tiempo total de ejecucin sera (12000)2 t y 2 llamar a f nc(24000) el tiempo total de ejecucin sera (24000) t. As que (12000)2 t = 1s, cunto ser (24000)2 t ? Una forma de calcularlo sera

100

CAPTULO 8. ANLISIS DE COMPLEJIDAD

obtener el valor de siguiente manera: 2

t,

pero para este propsito nos dice ms resolverlo de la

(24000) t = (2 12000)2 t = 4(12000)2 t 2 Sustituyendo 12000 t por 1s obtenemos: 4(1s) = 4s


Hemos comprobado que el tiempo de ejecucin seran 4 segundos, pero

ahora intentemos generalizar un poco.

Problema Resuelto 8.2.1.


misma computadora?

Si en una computadora

gundos en ejecutarse, cunto tiempo tardar

f nc(a) tarda m sef nc(2 a) en ejecutarse en la

f nc(a) tarda un tiempo de a2 t en ejecutarse. Y f (2 a) tarda un tiempo de 4a2 t en ejecutarse. Sustituyendo a2 t por m tenemos que tardara 4m segundos. Si repitiramos el ejemplo anterior pero con f nc(10 a) veramos que el tiempo de ejecucin ascendera a 100m segundos !
Tenemos que

Solucin

Ahora considera la siguiente funcin: Cdigo 8.2: Funcin cubo 1 2 3 } Aqu sin importar qu tan grande sea el valor de anterior,

double

cubo (

double x ) { return x x x ;
x, cubo(x)
siempre

tomar mas o menos el mismo tiempo en ejecutarse, a diferencia de la funcin

cubo(a)

tardara el mismo tiempo en ejecutarse que

cubo(10 a).

Un mismo algoritmo puede ser implementado de muchas maneras en un mismo lenguaje, y dependiendo de la implementacin, del compilador y del hardware en el que corra el programa, el tiempo de ejecucin sera mayor o menor, sin embargo, sin importar todo eso, siempre

velocidad.

aumentar con la misma

Es decir, si vamos a tratar con algoritmos, nos vamos a concentrar en encontrar algoritmos cuyo tiempo de ejecucin aumente lo menos posible a medida que la entrada se hace ms y ms grande. Por ello, cuando se trata de entradas grandes, lo mas importante es identicar qu tan rpido crecen los tiempos de ejecucin y no cul es el nmero exacto de operaciones o el tiempo exacto de ejecucin.

8.3. FUNCIN DE TIEMPO

101

8.3. Funcin de Tiempo


Si tuviramos todo el tiempo y las energas del mundo para resolver un problema sera posible implementar una solucin, generar casos de prueba y luego ver si su tiempo de ejecucin no es excede del lmite; y en caso de que se exceda pensar en otra solucin y empezar desde el principio. Sin embargo, no tenemos todo el tiempo del mundo y seguramente nadie tendra paciencia y energas ilimitadas para implementar muchos algoritmos sin saber de antemano cual va a funcionar en tiempo.Esto hace necesario saber qu tan rpido funcionara un algoritmo desde antes de implementarlo. El tiempo de ejecucin de un algoritmo no es algo fcil de medir, sobre todo antes de implementarlo; ya que depende directamente de todas las operaciones que se vayan a hacer en el cdigo y de la velocidad de la computadora en cada tipo de operacin en especco. El solo hecho de pensar en el nmero exacto de sumas que realizar una computadora en determinado algoritmo puede ser ms tardado que implementar el mismo algoritmo. Y adems de todo esto, un mismo algoritmo puede tardarse diferente cantidad de tiempo con diferentes entradas. Y a veces las variaciones son con entradas del mismo tamao. Est claro que es imposible lidiar con todos estos datos cada que se quiera resolver un problema, y es necesario ignorar algunos de ellos para poder analizar de alguna forma la rapidez de un algoritmo. Como se haba mencionado unos captulos atrs, vamos a considerar una funcin

E T1 (E ) donde E

es el conjunto de los datos de entrada y

T1 (E ) es

el nmero de operaciones que realiza el algoritmo con esos datos de entrada. Sera algo deseable poder trabajar con una funcin con dominio en los enteros y codominio en los enteros, ya que resulta muy difcil trabajar con conjuntos de datos de entrada. Para solucionar esto utilizaremos una funcin de

cota superior funcin pesimista.


La idea es denir una nueva funcin

T2 : N N de manera que T1 (E ) T2 (n) cuando n |E | para todo conjunto de datos de entrada E . Otra cosa que hay que denir es que T2 (n) T2 (n + 1), eso har que el
anlisis se vuelva signicativamente mas simple, no es difcil comprobar que siempre existir una funcin que cumpla con estas caractersticas.

T2

es una cota superior ya que para cualquier entrada

podemos estar

seguros que el nmero de operaciones que realizar el programa ser igual o menor a

T2 (|E |)

y es pesimista porque lo que menos quisieramos(el peor

caso) para una entrada

es que

T1 (E ) = T2 (|E |).

Esta aproximacin del pesimismo puede parecer inapropiada en la prctica, pero se hablar de ella por varios motivos:

102

CAPTULO 8. ANLISIS DE COMPLEJIDAD


Cuando se resuelve un problema, la solucin debe de funcionar en todos los casos que describa el problema, si en lugar de analizar el peor caso, analizaramos un caso problema. Existen varias maneras de analizar la complejidad, y la que se trata en este libro es la mas simple de todas, y es bueno conocerla como introduccin a las otras. Hay gran cantidad de algoritmos donde para casi cualquier

promedio ; no estaramos realmente resolviendo el

T1 (E ) es proporcional a T2 (|E |)

E.

Algunas veces los usuarios de las aplicaciones que programemos pueden llegar a intentar darle entradas diseadas especcamente para que nuestro algoritmo se vuelva lento(por ejemplo en un concurso de programacin, o en una intrusin a un sistema). De ahora en adelante llamaremos a representaremos simplemente como

T2

como la funcin de tiempo y la

T.

8.4. La Necesidad del Smbolo O


Deniendo la funcin de tiempo hemos podido reducir una gran cantidad de datos a unos pocos con un enfoque ligeramente pesimista. Pero an as medir el tiempo sigue siendo muy difcil. Por ejemplo en la siguiente funcin: Cdigo 8.3: maximoSecuencial 1 2 3 4 5 6 7 8 9 10 } Podemos ver que ese cdigo realiza al menos una asignacin y a lo mas asignaciones a la variable } }

void

maximoSecuencial (

int

int

v[] ,

int

n){

i ,

r ;

for ( i =1; i <n ; i ++){ i f ( v [ i ]> r ) {


r=v [ i ] ;

r=v [ 0 ] ;

return

r;

r,

as que siguiendo la lnea del pesimismo vamos

a asumir que siempre se realizan

asignaciones a la variable

r.

Adems se

8.4. LA NECESIDAD DEL SMBOLO


realizan y

O
comparaciones entre

103

n1

comparaciones entre

n, n 1

v [i]

n1

incrementos, asi que:

T (n) = n + (n 1) + (n 1) + (n 1) = 4n 3
El conteo realizado, aunque nos condujo a una expresin algebrrica bastante simple y en poco tiempo, habra sido muy difcil de realizar si solamente conocieramos el algoritmo y no la implementacin, por lo que nuevamente caeramos en la necesidad de tener que implementar el algoritmo antes de saber si es eciente. Adems, mltiples implementaciones del mismo algoritmo pueden tener funciones de tiempo diferentes. Una cosa que podramos hacer es seguir con la lnea del pesimismo y asumir que la bsqueda secuencial va a iterar

veces, que en cada iteracin

va a realizar 100 operaciones(se est eligiendo un nmero arbitriariamente grande para el nmero de operaciones dentro de un ciclo) y que lo que est fuera del ciclo va a realizar otras 100 operaciones, as que antes de implementarlo sera posible denir la siguiente funcin de tiempo:

T (n) = 100n + 100


Bien podramos elegir repetidas veces el nmero 100 para buscar cotas superiores. Pero nos meteramos en algunas complicaciones con la aritmtica. Por ejemplo con el siguiente cdigo: Cdigo 8.4: Funcin cosa 1 2 3 4 5 6 7 8 9 } Aqu podramos pensar que por cada vez que se ejecuta el bucle exterior, el bucle interior se ejecuta 77 veces, que el bucle exterior se ejecuta que la funcin de tiempo sera: } }

void

cosa (

int v [ ] , int n ) { int i , k , r ; for ( i =0; i <n ; i ++){ for ( k =0; k<v [ i ] % 7 7 ; k++){
r+=v [ i ] %( k +1) ;

return

r;

veces,

y que cada vez que se ejecuta el bucle interior gasta 100 operaciones. Por lo

104

CAPTULO 8. ANLISIS DE COMPLEJIDAD

T (n) = n(77)(100) = 7700n


Sabemos que solamente exageramos al elegir el 100 como el nmero de operaciones que se hace el bucle interior cada que itera, as que el como

7700n

no

tiene ningn signicado real para nosotros, mas bien sera mas til dejarlo

100(77)n.

El siguiente cdigo, muy parecido al anterior puede causarnos mas molestias. Cdigo 8.5: Funcin cosas 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 } Despus de ser pesimistas llegaramos a la conclusin de que } } } } }

void

cosas (

int i , k , r =0 , h ; for ( i =0; i <n ; i ++){ for ( k =0; k<v [ i ] % 7 7 ; k++){


r+=v [ i ] %( k +1) ;

int

v[] ,

int

n){

for ( h =0; h<n ; h++){ for ( k =0; k<v [ i ] % 8 8 ; k++){


r+=v [ i ] %( k +1) ;

for ( k =0; k<v [ i ] % 5 5 ; k++){


r+=v [ i ] %( k +1) ; r;

return

T (n) = (100)(n)(77 + 88n) + (100)(55) = (100)(88n2 + 77n + 55)


Observamos que el 100 ahora multiplica a todos los trminos. Y si nos ponemos a pensar un poco, el 100 siempre multiplicar a todos los trminos si mantenemos la estrategia que hemos seguido hasta ahora. As que en lugar de utilizar el 100, puede resultar mas cmodo utilizar otro smbolo, por ejemplo

O,

para representar un nmero mayor a lo que

cualquier cdigo sin un bucle anidado puede tardar en ejecutarse.

8.5. DEFINICIN DE LA NOTACIN

O-MAYSCULA

105

Por lo que las funciones de tiempo en estos ejemplos se pueden expresar como:

T (n) = O(n + 1) para maximoSecuencial T (n) = O(77n) para la funcin cosa T (n) = O(88n2 + 77n + 55) para la funcin

cosas

8.5. Denicin de la notacin O-mayscula


A pesar de que adoptando el uso de ejemplo: Cdigo 8.6: Funcin funcion1 1 2 3 4 5 6 7 8 9 } Usando el anlisis aprendido en la seccin anterior podemos concluir que } }

O como una constante de cota superi-

or, an se puede hacer todava mas sencillo el anlisis. Considera el siguiente

void

funcion1 (

int i , k , r , h ; for ( i =0; i <n ; i ++){ for ( k =0; k <5; k++){


r ++;

int

n){

return

r;

T (n) = O(5n).

Pero a continuacin se muestra una funcin que obviamente

hace el mismo nmero de operaciones: Cdigo 8.7: Funcin funcion2 1 2 3 4 5 6 7 8 9 10 11

void

funcion2 (

int i , k , r , h ; for ( i =0; i <n ; i ++){


k =0; r ++; k++; k <5; r ++; k++; k <5; r ++; k++; k <5; r ++;

int

n){

106

CAPTULO 8. ANLISIS DE COMPLEJIDAD


k++; k <5; r ++; k++; k <5; }

12 13 14 15 16 17 }

return

r;

Y aqu, basndonos en la misma manera de analizar obtendramos la siguiente cota superior:

T (n) = O(n) = O(5n).

Esto no quiere decir que

debamos deshacernos de la idea de usar el smbolo una sola vez.

O, sino que los bucles que

iteran un nmero constante de veces pueden contabilizarse como si iteraran

Parece algo descabellado hacer esto en un inicio, pero el ejemplo anterior lo prueba, es lo mismo un ciclo corto que itera un nmero constante de veces que un cdigo largo que itera solo una vez. El objetivo de analizar de esta manera los algoritmos se trata de conocer qu tan rpido crece el nmero de operaciones a medida que la entrada se hace mas grande. Por ejemplo, considerense 3 funciones de tiempo T, T y T tales que: 2 T (n) = O(n), T (n) = O(100n) y T (n) = O(n ). Tenemos que T (2n) =

2T (n), T (2n) = 2T (n)


Aqu podemos ver

T (2n) = 4T (n). que T y T crecen al


y

mismo ritmo, mientras que

crece a un ritmo mucho mayor. Por lo tanto, decir que

T (n) = O(100n)

es equivalente a decir

T (n) =

O(n),

en ambos casos nos estamos reriendo a una cota superior que

crece,

al mismo ritmo que

n.

Un ejemplo nos servir para convencernos de que el ritmo de crecimiento es mas importante que el funcionamiento en casos pequeos: un algoritmo

algoritmo

que realiza exactamente 100n operaciones ser mucho mas rpido que un B que realiza n2 operaciones, donde n es el tamao de la entrada. Aunque para valores pequeos, como por ejemplo

n = 3

n = 10,

el

algoritmo

es mas rpido, cuando n = 10000, por ejemplo, las cosas cam6 2 8 bian, ya que 100(10000) = 10 y (10000) = 10 , eso signicara que con una entrada de tamao 10 mil, el algoritmo algoritmo

sera 100 veces mas rpido que el

B.

Luego de justicar por qu ignorar las constantes, hemos llegado a un punto donde debemos redenir nuestra notacin, ya que si decimos que

T (n) = O(100n) y T (n) = O(n) concluiramos que O(100n) = O(n) y que 100 = 1; as que no podemos seguir tratando a O como si fuera un nmero
real.

8.5. DEFINICIN DE LA NOTACIN


En lugar de decir que

O-MAYSCULA

107

T (n) = O(n) diremos que  T (n) es de rden O(n), y la notacin O (f (n)), mas que indicarnos que un nmero real O multiplica al valor de la funcin f evaluado en n, signica que es posible elegir una constante K de manera que n Kf (n) sea una cota superior de la funcin
de tiempo. Luego de haber reformulado conceptos, es necesario formalizarlo en la siguiente denicin:

Denicin 8.5.1.
que:

Sea

complejidad o de rden

f una funcin con dominio en N se dice que es de O(g (n)) si y solo s existe alguna constante K tal

f (n) < Kg (n)


Ntese que al hablar de

para todo

nN n

O(g (n))

no es necesario expresarlo como

O(g (n)) para decir que se trata de la funcin g y no de el valor de g evaluada en n, esto es porque dentro de esta notacin no aparecen constantes, solamente variables; salvo que se trate de una funcin constante en cuyo caso, siempre se expresa como

O(1).
Encuentra la complejidad de

Problema Resuelto 8.5.1.


notacin

f nc,

utiliza la

O-mayscula.
Si supusiramos que

Solucin
realiza

f nc

es de orden

O(n),

entonces debera de

k tal que kn siempre sea mayor al nmero de operaciones que f nc(n), pero podemos darnos cuenta que para cualquier valor entero de k que elijamos, el tiempo de ejecucin de f nc(k + 1) siempre ser mayor que k (k + 1), as que f nc no puede ser de orden O (n). 2 Ahora supongamos que fnc es de complejidad O (n ) y sea m el numero de operaciones que realiza f nc(n), entonces f nc(2 n) realizar 4m operaciones, f nc(4 n) realizar 16m segundos en ejecutarse. Si a k le asignamos un valor 2 mayor que m, por ejemplo (m +1), entonces podemos ver que (m +1)n siempre ser mayor que f nc(n), con esto queda demostrado que la complejidad 2 de fnc es O (n ).
existir alguna Cdigo 8.8: Funcin suma_modular 1 2 3 4 5 6 } }

int

suma_modular (

int n ) { int i , r =0; for ( i =0; i <n %100; i ++){


r+=i ;

108

CAPTULO 8. ANLISIS DE COMPLEJIDAD

Problema Resuelto 8.5.2. Analiza la complejidad de suma_modular. Utiliza la notacin

O-mayscula.

Solucin
Sea

Como podemos ver, el ciclo

for

siempre itera menos de 100 veces,

y en cada iteracin se realizan siempre el mismo nmero de operaciones sin importar el valor de

n. D
el

el nmero de operaciones que se realizan en cada iteracin,

nmero de operaciones requeridas para llamar a la funcin y para regresar un valor, podemos darnos cuenta que

CD + 1 siempre ser mayor que el numero de operaciones que realiza suma_modular , y adems CD + 1 es constante, por lo tanto el tiempo de ejecucin de suma_modular crece en O (1).

8.6. Mltiples Complejidades en Notacin OMayscula


Podras estarte preguntando, f nc no ser tambin de orden 2 4 orden O (n + n) o de orden O (n ), etc... ?

O(n3 )

o de

La respuesta a esta pregunta es que s, si un algoritmo tiene una complejidad

O(g (n))

tambin se puede decir que ese algoritmo es de cualquier

complejidad mayor a

O(g (n)) solo que por practicidad se suele tomar la fun-

cin de crecimiento ms pequea que se conozca del algoritmo.

8.7. Cundo Un Algoritmo es Factible y Cundo Buscar Otro


Ya vimos lo qu es la notacin

O-mayscula pero an no se habla del tema


en un segundo realiza poco mas de 200

de cmo saber si un algoritmo de cierta complejidad resuelve el problema. Una computadora con

1,5Ghz

millones de sumas. Puede ser un buen punto de referencia saber eso, pero est claro que una iteracin por lo general consiste de mas operaciones que una sola suma por este motivo es necesario conocer en qu tamaos de las entradas suelen funcionar los algoritmos con diferentes complejidades. En la siguiente tabla se muestran varias complejidades y valores mximos de

que suelen ser los indicados en la mayora de los problemas de

programacin si se requiere que el programa funcione en menos de un segundo(esto se puede extrapolar para tiempos mayores con sencillos clculos algebrricos).

8.7. CUNDO UN ALGORITMO ES FACTIBLE Y CUNDO BUSCAR OTRO109


Tambin existen algoritmos en los que cada iteracin requiere de muchas operaciones y el valor mximo de

debe de ser ms pequeo; pero con esos

casos especiales solamente se aprende a lidiar por medio de la experiencia.

Tabla de Complejidades
Complejidad
O(1) O(logn) O ( n) O(n) O(nlogn) O(n2 ) O(n3 ) O(n4 ) O(2n ) O(n!)

Valor Mximo de n
250000000 1015 50000000 5000000 5000 500 80 20 11

110

CAPTULO 8. ANLISIS DE COMPLEJIDAD

Cap tulo

9
A es de complejidad O(f (n)) nos refen
entonces la complejidad de la

Reglas para Medir la Complejidad


Aunque comience a parecer algo tedioso calcular las complejidades de los algoritmos, es fcil en la mayora de los casos, ya que existen ciertas tcnicas usadas para calcular la complejidad de los algoritmos, las cuales descubriremos en las prximas pginas. Cuando decimos que un algoritmo rimos a que si algoritmo

T (n)

es la funcin del tiempo que tarda en ejecutarse el de tamao la de la funcin

A con una entrada funcin T (n) es la misma que O(T (n)) = O(f (n)).

f (n),

o dicho de otra forma

9.1. Regla de la Suma


La primera de las reglas para calcular complejidad es la regla de la suma(no debe confundirse con la regla combinatoria de la suma). Esta regla implica que al ejecutar dos algoritmos diferentes, uno despus del otro, la complejidad de ejecutar ambos algoritmos ser igual a la complejidad de ejecutar el algoritmo mas lento de ellos. De una manera mas precisa, si el tiempo para ejecutar un algoritmo es

n T (n) + T (n) entonces su complejidad O(T (n) + T (n)) = O(max(T (n), T (n))).

es

O(max(T (n), T (n))),

o bien

Esta regla a primera vista puede causar escepticismo y a veces negacin 3 3 a usarse, pero basta con ver que los valores de f (x) = x + 3x + 100 y de 3 g (x) = x se vuelven casi iguales conforme crece x. Por ejemplo:

f (1000) = 1003000100 g (1000) = 1000000000


111

112

CAPTULO 9. REGLAS PARA MEDIR LA COMPLEJIDAD


Por ello, si un algoritmo que realiza

g (n)

operaciones tarda

10

segundos

en ejecutarse cuando tardara

n = 1000,

un algoritmo que realize

f (n)

operaciones

10,03

segundos.

Aunque despus de haber visto el ejemplo anterior puedas entender un poco la razn de la regla de la suma, es conveniente conocer su demostracin para estar seguros que realmente se aplica a la notacin que denimos como

mayscula.

Teorema 11 (Regla de la suma). Si una funcin a es de complejidad O(f (n))


Demostracin.
alguna

y una funcin b es de complejidad O(g (n)) la complejidad de a+b es O(max(f (n), g (n))). O dicho de otra forma O(f (n) + g (n)) = O(max(f (n), g (n)))
Sin perder la generalidad supongamos que

f (m) g (m) para

m. J
y

Por denicin existen dos constantes

tal que

Jf (n) > a(n) Kg (n) > b(n) y J > K para


En consecuencia tenemos que

toda

Jf (m) + Jf (m) Jf (m) + Kg (m) > a(m) + b(m) 2Jf (m) > a(m) + b(m)
Anlogamente, si g (m) f (m) entonces existe J tal que 2Jg (m) > a(m) + b(m). Como J es constante, entonces podemos concluir que O (f (n) + g (n)) = O(max(f (n), g (n))).

Dado que trabajamos con funciones de tiempo creciente, lo mas frecuente sera encontrarnos con que Es decir,

O(f (n) + g (n)) es o bien O(f (n)) o bien O(g (n)). n f (n) g (n) para toda n. Pero no siempre es as, puede haber valores de n para los cuales f (n) > g (n) y valores de n para los cuales f (n) < g (n). Por lo tanto, no siempre nos salvaremos de expresar una complejidad de la forma O (f (n) + g (n)), f (n) g (n)
para toda un ejemplo de esto son los algoritmos de bsqueda que se analizarn al nal de la parte IV.

9.1. REGLA DE LA SUMA


Cdigo 9.1: Funcin burbuja

113

1 2 3 4 5 6 7 8 9 10 11

Problema Resuelto 9.1.1 (Complejidad del Algoritmo Burbuja). void b u r b u j a ( int a r r e g l o [ ] , int n ) { int i , k ; for ( i =0; i <n ; i ++){ for ( k =0; k+1<n ; k++) i f ( a r r e g l o [ k]< a r r e g l o [ k +1])
swap ( a r r e g l o [ k ] , a r r e g l o [ k +1]) ; }

for ( i =0 ,

k=n ; i >k ; i ++,k) swap ( a r r e g l o [ i ] , arreglo [ k ]) ;

return ;
}

Mide la complejidad de la funcin burbuja implementada en el cdigo anterior. Utiliza la notacin

O-mayscula.

Solucin

la lnea 3, el

for de la lnea 3 itera n veces, por cada iteracin del for de for de la lnea 4 itera n 1 veces, por lo tanto, el nmero de veces que se ejecuta la lnea 5 es (n)(n 1) y el nmero de intercambios que se realizan en la lnea 6 nunca es mayor a (n)(n 1).
El Luego, el nmero de operaciones cada vez que itera

el for

de la lnea 4 es

menor o igual que:

2(n)(n 1) = 2n2 2n
positivo, por lo que 2 el tiempo de ejecucin desde la lnea 3 hasta la lnea 7 es de orden O (n ), o dicho de otra manera, cuadrtico. n veces, y el nmero de intercambios que se El de la lnea 8 itera 2 n n iteraciones realiza realizan tambin es , eso signica que cada una de las 2 2 el mismo nmero de operaciones, dicho nmero de operaciones lo vamos a Ntese que

2n2 > 2n2 2n

para cualquier valor de

for

denotar como es:

m.

Por ello, el nmero de operaciones que se ejecutan entre las lneas 8 y 9

m
Dado que

n m = n 2 2

m es una constante(es fcil ver que existe una cantidad de tiem2 po constante que siempre ser mayor que el tiempo que tarden en ejecutarse

114

CAPTULO 9. REGLAS PARA MEDIR LA COMPLEJIDAD

m operaciones), el tiempo de ejecucin de las lneas 8 y 9 es de complejidad 2 O(n). 2 Como primero se ejecuta un algoritmo de O (n ) (lneas 1 a 7) y posteriormente se ejecuta un algoritmo de el tiempo de ejecucin es de

O(n) (lneas 8 y 9), por regla de la suma, 2 2 complejidad O (max(n, n )) = O (n ).

9.2. Producto por Constante


La siguiente regla es un tanto obvia, pero no por eso se debe despreciar. La regla dice que si dos funciones para alguna constante

son tales que

T (n) = Kt(n)

K,

entonces su complejidad es la misma, es decir

O(T (n)) = O(t(n)).

Teorema 12. Si una funcin f (n) = Kt(n) donde K es una constante, la


Demostracin.

complejidad de f es la misma que la de t, dicho de otra manera O(Kf (n)) = O(f (n)) para cualquier constante K .
por denicin existe una constante

t es O(f (n)), n, multiplicando ambos lados de la desigualdad por la constante no negativa K , tenemos que (K )(K )f (n) > (K )t(n). Como (K )(K ) es constante, entonces O (Kf (n)) = O (f (n)).
Sean funciones tales que la complejidad de

tyf

tal que

K f (n) > t(n)

para toda

9.3. Regla del Producto


Considera el siguiente cdigo: Cdigo 9.2: f y g 1 2 3 4 5 6 7 8 9 10 11 } }

int

f(

int n ) { int i , r =0; for ( i =1; i <=n ; i ++){


r+=n ;

return

r;

int

g(

int n ) { int i , k , r =0; for ( k =1; k<=n ; k++){ for ( i =1; i <=n ; i ++){

9.4. COMPLEJIDAD EN POLINOMIOS


12 13 14 15 16 } } } r+=f ( k ) f ( i ) ;

115

return

r;

Intentar calcular el nmero exacto de iteraciones que se realizan en la funcin anterior es algo muy difcil, pero a la vez si analizaramos el nmero de veces que la funcin

manda a llamar a la funcin

en lugar del tiempo

que tarda en ejecutarse la funcin g , nos daremos cuenta que ese nmero se 2 2 calcula con la funcin v (n) = 2n la cual es una funcin de rden O (n ). Vamos a denotar complejidad de

como la entrada de

recordando que el nmero de llamadas a

desde

n g

es de rden

como la entrada de g , O(n 2 ) y la

es

O(n),

por denicin es posible elegir 2 constantes y

tales que:

Kn2 > 2n2

Kn >n

(9.1)

Por la ecuacin anterior podemos concluir que:

Kn2 K n > 2n2 n


Recordando que

(9.2)

n n,

y por la ecuacin anterior

Kn2 (K n) Kn2 K n > 2n2 n (K )(K )n3 Kn2 K n > 2n2 n (K )(K )n3 > 2n2 n
De sto se puede concluir que

funciona en un tiempo de orden

O(n3 ).

De manera anloga se puede demostrar que si

g (n) = f (n)f (n)

entonces

es de rden

O(f (n)f (n)).

Teorema 13 (Regla del Producto). Sea g(x) = f (x)f (x), la complejidad de


g es igual al producto de las complejidades de f y f .

9.4. Complejidad en Polinomios


Considera el siguiente cdigo: 1 2 3 4

for ( i =N ; i >=0; i ) { } for ( i =0; i <N ; i ++){ for ( k =2; k<N ; k++){ for ( h =0; h<N ; h+=5) ;

116

CAPTULO 9. REGLAS PARA MEDIR LA COMPLEJIDAD


}

5 6 7 }

for ( k =0; k<N %128; k++)


for
de la lnea 3 es

el for de la lnea 2 es N , el nmero de veces N (N 2), el nmero de veces que itera el for de la lnea 4 es (N )(N 2)(N/5), el nmero de veces que itera el for de la lnea 6 es N mod128, y el nmero de veces que itera el for de la lnea 1esN .
El nmero de veces que itera que itera el Por tanto el nmero total de iteraciones es:

N +N (N 2)+N (N 2)(N/5)+N mod128+N = (1/5)N 3 +(3/5)N 2 +N +N mod128


Aplicando la regla de la suma sabemos que:

O((1/5)N 3 +(3/5)N 2 +N +N mod128) = O(max(O((1/5)N 3 ), O((3/5)N 2 ), O(N ), O(N mod128


Aplicando la regla del producto por constante tenemos que:

O((1/5)N 3 + (3/5)N 2 + N + N mod128) = O(max(N 3 , N 2 , N, 1)) = O(N 3 )


Como te habrs dado cuenta, en cualquier algoritmo cuyo tiempo de ejecucin se pueda representar mediante un polinomio, lo nico que hay que hacer es tomar el trmino con mayor exponente de la variable y olvidarse del coeciente. Y si eres un poco ms observador, te podrs dar cuenta que ni siquiera es necesario expandir el polinomio. Es decir:

O(N +N (N 2)+N (N 2)(N/5)+N mod128+N ) = O(N +N (N )+N (N )(N )+1+N )


Las constantes se pueden ignorar, y de esa manera se vuelve trivial dado un polinomio encontrar su complejidad. Se deja como ejercicio para el lector comprobar que esto se puede hacer con cualquier polinomio.

9.5. Medir antes de implementar


En el captulo anterior se plante el objetivo de cmo saber si un algoritmo es rpido sin necesidad de implementarlo. Y hasta este momento solamente nos hemos dedicado a analizar la complejidad de implementaciones. Sin embargo, ya tenemos las herramientas sucientes para poder analizar la complejidad de muchos algoritmos sin necesidad de su implementacin;

9.5. MEDIR ANTES DE IMPLEMENTAR

117

para lograr esto simplemente hay que examinar qu ciclos posee el algoritmo y cules de esos ciclos estan uno dentro de el otro. Una vez identicados los ciclos, hay que estimar cual es el nmero mximo de veces que se puede ejecutar cada uno, ignorando las constantes, obteniendo as un polinomio. Luego de eso simplemente hay que tomar el trmino del polinomio con exponente mayor. Los siguientes ejemplos ilustran un poco esta tcnica

Problema Resuelto 9.5.1.


manera: Para cada elemento demas elementos de

Analiza la complejidad del algoritmo para en-

contrar el mximo elemento de un arreglo

con

elementos de la siguiente

a,

si se cumple

i del arreglo A, verica que i sea mayor que todos los i ser el mximo.

Solucin

i es el mximo, el algoritmo verica que a > k para todo k A tal que k = i, como hay n valores posibles de A, entonces esta operacin hay que realizarla n 1 veces, eso es complejidad O(n). Y como hay que vericar n elementos distintos y cada elemento toma un 2 tiempo de O (n) vericarlo, la complejidad es O (n ).
Para saber si un elemento

Problema Resuelto 9.5.2.


manera:

Analiza la complejidad del algoritmo para en-

contrar el mximo elemento de un arreglo

con

elementos de la siguiente

el

maximo con valor Para cada elemento i del arreglo A, si el valor de maximo es menor valor de i entonces se sustituye el valor de maximo por el valor de i.
Primero se inicializa una variable

que

Solucin

maximo toma tiempo O(1), comparar el valor de maximo con el valor de alguna i tambien toma tiempo O (1), pero hay que hacer dicha comparacin n veces, por lo tanto el algoritmo es O (n).
Inicializar el valor de A pesar de que ya no se requiere un cdigo fuente para analizar los algoritmos, en este libro se seguirn usando, debido a que describir los algoritmos en espaol puede resultar confuso, y no vale la pena explicar un pseudocdigo.

118

CAPTULO 9. REGLAS PARA MEDIR LA COMPLEJIDAD

9.6. Bsqueda de Cotas Mayores


Aunque la mayora de veces este proceso es sencillo hay veces en las que no es fcil ver cuantas veces se ejecuta un ciclo an si se ignoran las constantes, pero para analizar esos casos lo mas conveniente a hacer es elegir una cota mxima y obtener cierta complejidad an cuando no sea la complejidad menor del algoritmo encontrar una propiedad en el algoritmo que permita determinar cuntas veces se ejecuta(para lo cual no hay una regla general). Cdigo 9.3: Funcin cuatro_cuadrados 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 } Por ejemplo en el cdigo 9.3 resulta extremadamente difcil darse cuenta cuantas veces itera cada ciclo, excepto el exterior que itera veces pero la mayora de ocaciones iteran menos. 2 En este caso se puede decir que n es una ciones en la funcin cuatro_cuadrados. Asi que, por regla del producto podemos saber que la funcin cuatro_cuadrados 4 itera menos de n = n2 veces, lo que signica que si T (n) es el nmero total de iteraciones. } } } } }

int

cuatro_cuadrados (

int n ) { int a , b , c , d ; int r =0; for ( a =0; a a<=n ; a++){ for ( b=a ; a a+b b<=n ; b++){ for ( c=b ; a a+b b+c c<=n ; c++){ for ( d=c ; a a+b b+c c+d d
<=n ; d++){

i f ( a a+b b+c c+d


d==n ) {
r ++;

return

r;

veces, en

los siguientes ciclos solamente es posible darse cuenta que pueden iterar

cota mayor al total de itera-

n2 > T (n)

9.6. BSQUEDA DE COTAS MAYORES


Sea

119

una consante tal que cada iteracin realiza menos de

opera-

ciones, entonces:

T (n) < K (n2 )


Por lo tanto, podemos decir que la funcin cuatro_cuadrados corre en 2 2 tiempo O (n ) ntese que n es una cota superior, por lo cual puede ser 2 posible que O (n ) no sea la menor complejidad de T que se puede hayar. Al medir complejidades muchas veces es til usar cotas mayores, ya que esto asegura que el algoritmo va a funcionar en un tiempo menor al calculado usando la cota mayor y eso a veces es suciente para saber que el algoritmo va a funcionar dentro de las condiciones que se piden en el problema.

120

CAPTULO 9. REGLAS PARA MEDIR LA COMPLEJIDAD

Cap tulo

10
O(log n),

Complejidades Logartmicas
En el captulo anterior se estudiaron mltiples complejidades de la forma O(nk ) y se mostr como gran cantidad de algoritmos corren en tiempos que se pueden expresar como un polinomio y como toda funcin polinomial tiene k una complejidad de la forma O (n ) donde k es una constante. Otro tipo de complejidades importantes son las complejidades aunque suene extrao usar logaritmos en el conteo del nmero de operaciones, este tipo de complejidades aparecen de una manera muy frecuente. Cabe mencionar que en los algoritmos, suelen ser mas importantes los logaritmos base 2 que los logaritmos naturales.

10.1. Anlisis de la Bsqueda Binaria


El primer algoritmo que veremos con esta complejidad es uno que ya se mencion en la Parte I, sin embargo nunca se analiz su complejidad. 1 2 3 4 5 6 7 8 9 10 11 12 } }

int

Busqueda_Binaria (

int v [ ] , int a , int while ( a<b ) i f ( v [ ( a+b ) /2]==x ) { return ( a+b ) / 2 ; } else i f ( v [ ( a+b ) /2] < x ) { else {
a=(a+b ) / 2 + 1 ; b=(a+b ) /2 1;

b,

int

x){

i f ( v [ a]==x ) { return } else {

a;

121

122

CAPTULO 10. COMPLEJIDADES LOGARTMICAS

13 14 15 } }

return 1; Problema Resuelto 10.1.1. Analiza la complejidad de la bsqueda binaria,


utiliza la notacin O mayscula.

Solucin

Todas las iteraciones de este algoritmo ejecutan la lnea 3, y las (a+b) ] = x y iteraciones se ejecutan mientras no se haya encontrado un v [ 2 mientras a sea menor que b. Para conocer el nmero de iteraciones primero debemos estar seguros de que este cdigo realmente termina y no se queda en un bucle innito. Es posible que en

no exista ningn valor igual a

ble que se mantenga indenidamente la condicin mostraremos que esto ltimo no es posible: Si

x, pero, Ser posia < b? en seguida de-

a<b

entonces

Vamos a llamar

ba>0 a y b a los

valores que tomarn

en la siguiente

iteracin. En cada una de las iteraciones ocurre alguna de estas 2 cosas: b b + 1 bien b = a+ 1 a = a+ 2 2 Por tanto:

b a =b b a =b

a+b 1 2

a b 1 (a + b)mod2 2 2 a b 1 (a + b)mod2 b a = 2 2 b a ba 1 (a + b)mod2 < 2 2 2 ba b a < 2


o bien

b a = b a =

a+b 1a 2

a b + + amod2 1 a 2 2 a b ba + + amod2 1 a < 2 2 2 ba b a < 2

10.2. BASES DE LOGARITMOS


Con lo anterior podemos darnos cuenta que luego de cada iteracin

123

a b se

reduce a la mitad o menos. Cuando se trata de nmeros reales, sta situacin puede continuar por un tiempo indenido, pero cuando se trata de nmeros enteros, despus de cierto nmero de operaciones se llegar a la situacin inevitable de que

a = b. b a = 2n ,
entonces el algo-

Es fcil de darse cuenta que si inicialmente ritmo terminar en el peor caso despus de

n iteraciones, es decir despus de

log2 (n)

iteraciones.

x no est en v , busqueda_binaria(v, a, b, x) busqueda_binaria(v, a, b + 1, x). Por lo tanto, busqueda_binaria(v, 0, n, x) no tardar mas que busqueda_binaria(v, 0, m, x) donde m es la menor potencia de 2 tal que n m. Con esto concluimos que la complejidad de la bsqueda binaria es O (log n).
Tambin es fcil probar por induccin que si tarda igual o menos tiempo que

10.2. Bases de logaritmos


Resulta curioso nunca ver las bases de los logaritmos en las complejidades, esto se debe a que todos los logartimos tienen exactamente la misma complejidad.

lesquiera dos nmeros reales k y h. Demostracin.


Sea

Teorema 14. O(logk f (n)) = O(logh f (n)) para cualquier funcin f y cuaUsando las leyes de los logaritmos sabemos que ln x para cualquier x y que logk x = para cualquier x. ln h

logk x =

ln x ln k

K = max(ln h, ln k )

tenemos que:

K (ln f (n)) logk f (n)


y adems

K (ln f (n)) logh f (n)


Con esto probamos que sea el valor de complejidad.

O(logm f (n)) = O(ln f (n))

sin importar cual

m,

esto quiere decir que todos los logaritmos tienen la misma

124

CAPTULO 10. COMPLEJIDADES LOGARTMICAS

10.3. Complejidades O(N logN )


A diferencia de la bsqueda binaria, la siguiente funcin no tiene una aplicacin practica, sin embargo, pronto veremos que su complejidad aparece de una manera muy natural el gran cantidad de algoritmos: 1 2 3 4 5 6 7 } }

void

funcion_nlogn (

int i =n , k ; while ( i >0) {

int

n){

for ( k =0; k<n ; k++){

i /=2;

Problema Resuelto 10.3.1.


Utiliza la notacin

Analiza la complejidad de

f uncion_nlogn.

mayscula.

Solucin
for
itera

Tenemos que el

while

itera no mas de

logn + 1

veces, y que el

veces por cada iteracin del

while. f uncion_nlogn es

Por regla del producto tenemos que la complejidad de

O((logn + 1)(n)) = O(O(logn)O(n)) = O(nlogn). . Puede parecer extraa la complejidad O (nlogn) pero mas tarde nos daremos cuenta que es bastante comn en el ordenamiento.

Cap tulo

11
cuntos ciclos se anidan.

Complejidades en Funciones Recursivas


Las funciones recursivas presentan mas problemas para analizarse que las funciones iterativas, ya que, a diferencia de las funciones iterativas, no es posible saber exactamente

En la Parte I se muestra la bsqueda binaria recursiva y unas pginas atrs se demostr que la complejidad de la bsqueda binaria es logartmica, ese puede ser un ejemplo de cmo un algoritmo recursivo puede ser notablemente rpido, sin embargo, en la Parte I se mostr que la funcin recursiva de bonacci crece muy rpido, por lo que los algoritmos recursivos tambin pueden ser notablemente lentos. Pero al usar recursin con memoria, vimos como se puede mejorar de manera considerable la velocidad del algoritmo recursivo para calcular los nmeros de bonacci. Por otro lado, resultara casi imposible simular mentalmente un algoritmo recursivo con el n de determinar qu tan rpido es.

11.1. Estados
El concepto de estado es un concepto que cobrar mucha importancia mas adelante, pero por el momento lo veremos de una manera supercial. A medida que se ejecuta un algoritmo, los valores de las variables o de los arreglos pueden variar, vamos a llamar estado a un conjunto de valores que pueden tener determinadas variables. Puede haber muchos puntos de referencia para determinar los estados. Por ejemplo, en el cdigo 9.3, podemos decir que los estados son todos los conjuntos de valores de las variables

a, b , c

d,

o bien tambien podriamos

decir que los estados son todos los conjuntos de valores de las variables

a, b

125

126 CAPTULO 11.

COMPLEJIDADES EN FUNCIONES RECURSIVAS

r,

etc.

Para medir adecuadamente la complejidad es necesario elegir un conjunto de estados de manera que el programa tarde un tiempo acotable en cambiar de un estado a otro. La idea es medir la complejidad del algoritmo en un estado, y luego multiplicar esa complejidad por el nmero de estados; y as no ser necesario simular mentalmente la recursin. El siguiente ejemplo ayudar a comprender mejor lo que se pretende hacer con denir estados: 1 2 3 4 5 6 7 8 9 10 11 } } }

int A r r e g l o [ 3 0 ] ; void b i n a r i o ( int n ) { i f ( n==0){ else {


procesa ( Arreglo ) ; Arreglo [ n]=0; b i n a r i o ( n 1) ; Arreglo [ n]=1; b i n a r i o ( n 1) ;

Problema Resuelto 11.1.1. Analiza la complejidad de binario en el cdigo


anterior.

Solucin
n

Vamos a tomar como los estados a todos los posibles valores de

y los primeros

nmeros del arreglo

Arreglo.

De lo cual nos resulta que el nmero de estados es la cantidad de cadenas de 1 bit ms la cantidad de cadenas de 2 bits, ms la cantidad de cadenas de 3 bits, ..., ms la cantidad de cadenas de

bits.

Por regla del producto sabemos que esto es:

21 + 22 + ... + 2n
Lo cual, por la ecuacin 4.1 sabemos que es:

(11.1)

2n+1 1
De esta manera hay un total de

(11.2)

2n+1 1 estados, y el programa tarda un

tiempo constante en cambiar de un estado a otro, por lo tanto la complejidad n es O (2 ).

11.2. CORTES
1 2 3 4 5 6 7 8 9 10 11 } } }

127

void

dfs (

int a ) { int i ; i f ( a<0 | | a>=N) return ; for ( i =0; i <n ; i ++){ i f ( Mat [ a ] [ i ]
&& ! V [ i ] ) {

V [ a ]= t r u e ;

d f s ( Mat [ a ] [ i ] ) ;

Problema Resuelto 11.1.2.


anterior.

Analiza la complejidad de

dfs

en el cdigo

Solucin
y

Podemos darnos cuenta que si

a<0

an

entonces el progra-

ma terminara en un nmero constante de pasos. Retomando nuestra actitud pesimista vamos a asumir que los estados. Debido a que

a solamente puede tener valores entre 0 n 1, y adems, vamos a utilizar el conjunto de posibles valores de a como df s(x)
es llamado a lo mas una vez para cada

x,

podemos

alegremente ignorar la recursin y solo contar el tiempo utilizado por el programa durante la ejecucin de para cada

df s(x)(sin

contar las llamadas recursivas) sin contar las tener hasta

x. n

df s(x) para alguna x y llamadas recursivas es de complejidad O (n); y como x puede 2 valores diferentes la complejidad es O (n ).
El tiempo que tarda en ejecutarse

11.2. Cortes
Muchas veces, al hacer llamadas recursivas un problema se divide en problemas mas pequeos, tal como se vi en el captulo de Divide y Vencers, cuando esto sucede, frecuentemente un estado rpido que un

estado mas grande

mas pequeo

se procesa ms

y las cosas se complican bastante.

Considere, por ejemplo, el siguiente cdigo. 1 2 3 4 5

int

suma_dummy (

int i , r =0; i f ( a<=b ) return 0 ; for ( i =a ; i <b ; i ++){

int

a,

int

b){

128 CAPTULO 11.

COMPLEJIDADES EN FUNCIONES RECURSIVAS


r ++;

6 7 8 9 10 11 } }

r+= suma_dummy ( a ,

( a+b ) / 2 ) ) ; b) ) ;

return

r+= suma_dummy ( ( a+b ) / 2 , r;

Problema Resuelto 11.2.1. Solucin


Vamos a denotar

Analiza la complejidad de

suma_dummy

suma_dummy (a, b)

como el tiempo en que

tarda en ejecutarse dicha funcin.

b )+ O(suma_dummy (a, b)) = O(b a + sumad ummy (a, a+ 2 a+b suma_dummy ( 2 , b)) cuando a > b y que O(suma_dummy (a, b)) = O(1) cuando a <= b, sin embargo, expresar la complejidad de manera recursiva
Es obvio que no parece ser una buena idea. Para resolver esta problemtica hay que tomar en cuenta el hecho de

O(suma_dummy (a, b)) = O(suma_dummy (a + c, b + c)) a, b y c.


nmero de iteraciones del ciclo

para cualesquiera

Esto no es muy fcil de admitir, sin embargo, se justica al ver que el

for

solamente depende de la diferencia entre

a
1 2

b,

lo mismo la condicin de retorno.

Sin embargo, hay un par de lneas donde si parece afectar: r+= suma_dummy ( a , ( a+b ) / 2 ) ) ; b) ) ;

r+= suma_dummy ( ( a+b ) / 2 ,

Pero, esta problematica se resuelve rpidamente:

a+b a+b a + b + 2c (a + c) = +cac= a 2 2 2 Analogamente b a + b = b + c a + b + 2c . Una vez admitido que O (suma_dummy (a, b)) = O (suma_dummy (a + c, b+c)), vamos a utilizar la expresin T (n) como el tiempo que tarda en ejecutarse suma_dummy (0, n), de esa manera O (T (ba)) = O (suma_dummy (a, b)). n Lo interesante aqu es que O (T (n)) = O (n + 2T ( )). 2
Para un anlisis supericial, tomemos las potencias de 2 y veamos que sucede con

O(T (n))

cuando

es una potencia de 2.

grande.

Adems utilizaremos la letra

para designar una constante de tiempo

T (1) k T (2) 2k + 2k = 2(2k ) = 4k

11.2. CORTES

129

T (4) 4k + 2(2k + 2k ) = 3(4k ) = 12k T (8) 8k + 2(12k ) = 4(8k ) = 32k T (16) 16k + 2(32k ) = 5(16k ) = 80k
n Utilizando induccin es fcil conrmar que T (2 ) n n que T (1) k y T (2 ) k (2 )(n + 1) implica que n n+1 n+1 n+1

k (2n )(n + 1). Puesto T (2n+1 ) 2n+1 k +


tenemos que:

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

Ahora, recordando

+ 2 (n + 1) = k (2 que T (n) T (n + 1)

)(n + 2) para todo n,

T (n) T (2 log2 n ) K (2 log2 n )( log2 n + 1) K (n + 1)( log2 n + 1)


Por lo tanto, la complejidad de

(11.3) (11.4) (11.5)

suma_dummy

es

O(nlog2 n).

Hay que hacer notar que en el anlisis de complejidad no importa la lnb . Por lo que decir que un algoritbase del logaritmo, ya que loga b = lna mo es de complejidad O (nlog2 n) es lo mismo que decir que es de comple1 jidad O ( (nlog2 n)). Y como ln2 es una constante entonces O(nlog2 n) = ln2 O(n ln n). Nuevamente nos encontramos con una complejidad

O(n log n).

Como se

haba dicho anteriormente, esta complejidad aparece mucho en los algoritmos de ordenamiento. Pero no solamente aparece en esos algoritmos, sino que aparece por lo general en algoritmos que requieren realizar Generalizando el ejemplo anterior, si una entrada de tamao tamao es

cortes. O(1) procesar

son constantes, un algoritmo

basado en la estrategia divide y vencers requiere un tiempo

n > K en M O(n logM n).

entradas de tamao

y requiere un tiempo O (n) dividir una entrada n , la complejidad del algoritmo M

La demostracin de esta propiedad es anloga al anlisis de complejidad de la funcin

suma_dummy .

Se deja como ejercicio para el lector.

130 CAPTULO 11.

COMPLEJIDADES EN FUNCIONES RECURSIVAS

Parte III Algoritmos de Ordenamiento I

131

133

Los algoritmos de ordenamiento son quiz los ejemplos mas citados de algoritmos donde la eciencia juega un papel muy importante y tambin sirven para realizar una de las tareas mas comunes de una computadora, que es la de ordenar datos. Incluso un sinnimo de la palabra computadora es ordenador. Podemos ver con frecuencia que aplicaciones como hojas de clculo o navegadores de archivos tienen la opcin de ordenar los datos para mostrarlos de una manera mas presentable. Sin embargo el ordenamiento tiene un propsito mucho mas importante que el de mostrar datos organizados. Para entender la importancia del ordenamiento recordemos que en la Parte I se mostr un algoritmo llamado Busqueda Binaria el cual serva para encontrar valores en secuencias ordenadas de manera no descendente y pareca ser bastante rpido. Y en la Parte III se demostr que la complejidad en tiempo de la Bsqueda Binaria es

O(log2 N ).

Es decir, encontrar datos en

una secuencia ordenada de forma no descendente es mucho mas rpido que encontrar datos en una secuencia cualquiera. Por este y otros motivos los algoritmos de ordenamiento juegan un papel escencial en las ciencias de la computacin.

134

Cap tulo

12

Ordenamiento
Antes de comenzar a tratar los algoritmos de ordenamiento es necesario saber cul es exactamente el objetivo de los algoritmos de ordenamiento y qu reglas deben de seguir. Podramos pensar en los algoritmos de ordenamiento simplemente como algoritmos para ordenar nmeros, pero muchos algoritmos(por ejemplo la bsqueda binaria) resultan tiles para trabajar con datos que pueden no ser numricos. Por tanto lo primero que haremos ser buscar una forma de denir lo qu es un rden, de tal manera que sea consistente con el rden de los nmeros pero que tambin se pueda aplicar a otras cosas. Si quisiramos ordenar nmeros, el sentido comn nos dice que dada una secuencia tal que

A de nmeros, queremos encontrar una secuencia B = (b1 , b2 , b3 , ..., bn ) B es un reordenamiento de los elementos de A y adems: b1 b2 b3 ... bn

Teniendo esta secuencia ordenada, podemos hacer uso de la propiedad de que si

i<k

entonces

bi < b k .

Esta propiedad a pesar de ser tan simple es

muy poderosa. Por ejemplo, si se diera el caso que

1 < b1 < bn < 10

entonces ya po-

dramos saber que todos los nmeros de la secuencia son mayores que 1 y menores que 10 sin necesidad de recorrerla toda; otro ejemplo es que si

b1 x < b
trar

n 2

bn

entonces

no se encuentra en la segunda mitad de la

secuencia(la bsqueda binaria usa esta estrategia repetidas veces para encon-

x). <
tal que nos permi-

Lo que se desea es denir una funcin llamada

ta ordenar los elementos y conservar estas propiedades; examinaremos esta

135

136

CAPTULO 12. ORDENAMIENTO

denicin a continuacin, y en seguida exploraremos los algoritmos de ordenamiento usando unicamente funciones de comparacin, de esta manera los algoritmos de ordenamiento servirn para ordenar cualquier cosa con funcin de comparacin, no slo nmeros.

12.1. Funcin de comparacin


Muy frecuentemente usamos expresiones tales como Cuando se trata de nmeros reales sabemos que o dicho de otra manera, nuestra

funcin de comparacin es:


si

a < b , a > b a b. a < b si y solo s a b / R+ 0

f (a, b) = 1 0
elemento es menor que otro.

ab / R+ 0

en otro caso

Es decir, una funcin de comparacin es una funcin que indica si un Muchos lenguajes de programacin traen implementados previamente algoritmos de ordenamiento y permiten que el programador dena sus propias funciones de comparacin. A continuacin se dene de una manera formal lo que es una funcin de comparacin.

Denicin 12.1.1 (Funcin de Comparacin). Una funcin de comparacin


es una funcin

f : A A {0, 1} para algun conjunto A, tal que se cumplen


(antisimetra) y

las siguientes propiedades:

f (a, a) = 0
Si Si

f (a, b) = 1 f (a, b) = 1

f (b, c) = 1

entonces

f (a, c) = 1

(transitividad)

entonces

f (b, a) = 0

(totalidad o

completitud) f
es una funcin que

La denicin anterior se puede interpretar como que hace comparaciones cumpliendo las siguientes reglas: Todos los pares ordenadados comparar haciendo uso de

(a, b)

tales que

aA

bA

se pueden

f.

Ningn elemento es menor que si mismo. Si Si

a a

es menor que es menor que

b b

es menor que

entonces

a a.

es menor que

c.

entonces

no es menor que

12.2. CONJUNTO CON UN RDEN TOTAL

137

12.2. Conjunto con un rden Total


Un conjunto con un rden total, como su nombre lo indica, es aquel que se puede ordenar de manera no descendente con una funcin de comparacin dada. Podrmos vernos tentados a decir que un conjunto en base a la existencia de una permutacin

de

A tiene un rden total A tal que P = (p1 , ..., pn ) y

f (pi , pi1 ) = 0

para todo

0 < i n. (A, f )

Pero eso no es necesario

Denicin 12.2.1 (conjunto con un rden total). Un conjunto con un rden


total es un par ordenado deniciones: donde de comparacin con dominio en

A A A.

es un conjunto y

es una funcin

Adems se aceptan las siguientes

a<b a>b a=b ab ab

si y slo si si y slo si si y slo si si y slo si si y slo si

f (a, b) = 1 f (b, a) = 1 f (a, b) = 0 a<b a>b


y se lee a menor que y se lee a mayor que y

b b b

f (b, a) = 0

y se lee a equivale a

a=b a=b

y se lee a menor o igual que y se lee a mayor o igual que

b b

Es un buen momento para hacer una aclaracin, las matemticas actuales en lugar de denir una funcin de comparacin, denen algo muy parecido llamado relacin de rden total. As que la denicin que vamos a manejar de conjunto con un rden total es ligeramente diferente a la mas usada. Si algn lector se siente ofendido por esto, puede consultar lo que es una relacin de orden total y convencerse de que todas las propiedades que analizaremos en los conjuntos con rden total tambin se cumplirn con la denicin tradicional. Estamos usando funciones de comparacin ya que en este contexto nos resultar mas familiar abordar los algoritmos de ordenamiento. Al principio del captulo se dijo que no se iba a denir un conjunto con un rden total en base a la existencia de una permutacin

de

tal que

P =

(p1 , ..., pn )

pi 1 pi

para todo

0 < i n;

y el motivo de esto es que dadas

las propiedades de una funcin de comparacin es posible comprobar que dicha permutacin existe; de hecho luego de estudiar el siguiente algoritmo ser fcil de convencerse de que es capz de ordenar cualquier conjunto con un rden total. Es imporante mencionar que las implementaciones que aparecen aqu de algoritmos de ordenamiento son para aplicarse en el caso mas comn: donde

138

CAPTULO 12. ORDENAMIENTO

se tienen todos los elementos en un arreglo. Pero como se ver mas adelante, las cosas no siempre son tan simples.

12.3. Algoritmo de Seleccin


El ordenamiento por seleccin es quiz el primer algoritmo de ordenamiento que a la mayora de la gente se le puede ocurrir y se basa en un hecho bastante simple:

Teorema 15. Para todo conjunto con un rden total C = (A, f ) tal que A
Demostracin.

es un conjunto nito, existe al menos un elemento n A tal que n a para todo a A, al cual llamaremos mnimo y se denota por min(C ) .
n en A tal que n a para todo a A.Esto implicara que para todo elemento m A existe un elemento m1 A tal que m1 < A. Como m1 A entonces existira otro elemento m2 A tal que m2 < m1 , y existira otro elemento m3 A tal que m3 < m2 , por induccin podemos concluir que para cualquier elemento m A y cualquier entero k existiran k elementos m1 , ..., mk A tales que mk < mk1 < ... < m2 < m1 < m.
Supongamos lo contrario que no exista un elemento Ahora, usando la conclusin del prrafo anterior, sabemos que para cualquier

m A deberan existir tambien |A| elementos m1 , ..., m|A| tales que m|A| < ... < m1 < m. Por la propiedad de totalidad, tenemos que, todos los elementos m|A| , ..., m1 , m son distintos, por lo que el conjunto A debera tener mas de |A| elementos,
elemento lo cual es una contradiccin.

mnimo elemento de

S , encontrar el (s1 , s2 , ..., sn ) e intercambiarlo con s1 , despus encontrar el mnimo elemento de (s2 , s3 , ..., sn ) e intercambiarlo con s2 . No es dicil darse cuenta que luego de hacer k intercambios, los primeros k elementos de la sucesin estarn ordenados. Por lo tanto, luego de hacer n intercambios se tendr toda la sucesin S ordenada.
La idea del algoritmo de seleccin es, dada una sucesin El siguiente cdigo muestra una implementacin del algoritmo de seleccin con enteros. Cdigo 12.1: Seleccion con Enteros

1 2 3

void

seleccion (

int i , k , minimo ; for ( i =0; i <n ; i ++){

int

S[] ,

int

n){

12.4. ALGORITMO DE INSERCIN


4 5 6 7 8 9 10 11 } } } intercambia (S [ i ] , S [ minimo ] ) ; minimo= i ;

139

for ( k= i ; k<n ; k++){ i f ( S [ k ] <S [ minimo ] )


minimo=k ;

La implementacin en C con enteros resulta bastante clara; C++ tiene la capacidad de denir funciones de comparacin que se llamen al usar los operadores <y >, pero se muestra una implementacin en C para ilustrar el funcionamiento del algoritmo de manera general: Cdigo 12.2: Seleccion en General 1 2 3 4 5 6 7 8 9 10 11 } Como se puede ver, la implementacin general cambi muy poco, y eso es lo que hace tiles a las funciones de comparacin en los algoritmos de ordenamiento. En general un algoritmo de ordenamiento slamente puede manipular los datos de dos formas: dados dos datos } } intercambia (S [ i ] , S [ minimo ] ) ;

void

s e l e c c i o n ( tipoDatos

int i , k , minimo ; for ( i =0; i <n ; i ++){


minimo= i ;

S[] ,

int

n){

for ( k= i ; k<n ; k++){ i f ( f (S [ k ] ,

S [ minimo ] ) ==1)

minimo=k ;

b,

compararlos intercambiarlos.

12.4. Algoritmo de Insercin


Este algoritmo es otro que a la gente se le ocurre de manera natural, se trata del algoritmo usado para ordenar las cartas de una baraja: A lo largo del ordenamiento, en la mano izquierda se tiene un conjunto de cartas ordenado, y luego, para cada carta de la mano derecha, se mueve la carta hacia el lugar que le corresponda en la mano izquierda. Por ejemplo, si en la mano izquierda se tienen 4 cartas numeradas como

1, 3, 8

10

y en la mano derecha se tiene una carta con el nmero 5, esta

140

CAPTULO 12. ORDENAMIENTO

carta se deber colocar entre las cartas 3 y 8, para asi obtener el conjunto ordenado de cartas

1, 3, 5, 8

10.

Inicialmente en la mano izquierda se coloca una sola carta, y es trivial que ese conjunto ya est ordenado, posteriormente se van insertando las cartas de una por una con el procedimiento antes descrito hasta tener todas las cartas en la mano izquierda. Una manera mas general de describir este procedimiento es inicialmente tener una sucesin vaca de elementos ordenados que la sucesin

y un conjunto

elementos no ordenados, despus insertar cada elemento de de esa manera hasta haber insertado todo los elementos.

B con n B en A de manera

A se mantenga ordenada luego de cada insercin, y continuar

En seguida se muestra una implementacin del algoritmo de insercin: Cdigo 12.3: Insercion 1 2 3 4 5 6 7 8 } } }

void

s e l e c c i o n ( tipoDatos

int i , k ; for ( i =0; i <n ; i ++){ for ( k= i ; k>0

S[] ,

int

n){

&& f ( S [ k ] ,

S [ k 1 ] ) ==1;k){ S [ k 1]) ;

intercambia (S [ k ] ,

12.5. Anlisis de los Algoritmos de Seleccin e Insercin


Recordando lo que hace cada algoritmo, el algoritmo de seleccin busca repetidas veces el mnimo de un conjunto de datos no ordenados y cada que encuentra un mnimo, lo quita del conjunto no ordenado y lo pone al nal del conjunto ordenado. Si hay veces. Mientras tanto, el algoritmo de seleccin mantiene una sucesin ordenada de datos y realiza

elementos, este procedimiento se repite

inserciones en esa sucesin.

Podramos mirar ingnuamente las implementaciones y decir que ambos 2 algoritmos son O (n ), pero hay que recordar que los algoritmos de ordenamiento son algo mucho mas general que una sla implementacin. Vamos a llamar

consulta

a la operacin que realiza el algoritmo de selec-

cin que consiste en encontrar el menor elemento que an no se ha ordenado y colocarlo al nal de los elementos ya ordenados.

12.6. EL ODIADO BURBUJA

141

Teorema 16. Con una sucesin de n datos, el algoritmo de seleccin realiza


n consultas y el algoritmo de insercin realiza n inserciones. O(n)
En las implementaciones que se mostraron, una consulta requiere tiempo y una insercin requiere tiempo O (n), por lo que ambos algoritmos 2 funcionan en tiempo O (n ) en estos casos. Sin embargo, existen algunas situaciones e implementaciones donde tanto las consultas como las inserciones son de complejidad estos algoritmos tengan una complejidad El caso de que una insercin tome tome

O(logn),

haciendo que

O(nlogn). O(logn) sucede

en la vida real cuando

se quiere ordenar una pila de cartas y el caso de que una consulta de seleccin

O(logn)

se estudiar mas adelante con los montculos.

Pero por el momento consideraremos la complejidad de estos algoritmos 2 como O (n ).

12.6. El Odiado Burbuja


Este algoritmo ya se haba mencionado en algunos ejemplos, pero no est por dems mencionarlo aqu. Burbuja tiene la peculiaridad de ser conocido como el peor algoritmo de ordenamiento, sin embargo es posible disear a propsito otros peores y al igual que insercin y seleccin, hay ocasiones en las cuales Burbuja puede ser el algoritmo adecuado. Se trata de recorrer varias veces el arreglo, y en cada iteracin intercambiar aquellos valores para los cuales algoritmo termina. Antes de analizar el algoritmo para evitar las posibles ambigedades del lengaje se mostrar una implementacin: Cdigo 12.4: Burbuja 1 2 3 4 5 6 7 8 9 10 } } } }

Sk > Sk+1 ;

luego de

iteraciones el

void

burbuja ( tipoDatos

int i , k ; for ( i =0; i <n ; i ++){ for ( k =0; k<n 1; k++) i f ( f ( S [ k ] , S [ k + 1 ] ) ==1){
intercambia (S [ k ] , S [ k +1]) ;

S[] ,

int

n){

142

CAPTULO 12. ORDENAMIENTO


Como se puede ver, al igual que en los dos algoritmos anteriores el

exterior recorre los enteros de 0 a

n pero el for interior siempre recorre todo

for

el arreglo(a diferencia de insercin y seleccin que en promedio recorren la mitad del arreglo), es por eso que burbuja tiene tan mala fama. No parece muy difcil converserse de que Burbuja realmente funciona, pero tampoco est por de ms encontrar un buen argumento para estar seguros. El argumento que demuestra sto hace uso de la siguiente denicin:

Denicin 12.6.1 (Inversin).


(i, j )
donde

Sea

S = (s1 , s2 , ..., sn )

una secuencia de ele-

mentos de un conjunto con un rden total, una inversin es un par ordenado

si > sj

s1 s2 ... sn , tenemos en ese caso por la propiedad de transitividad que para todo i, k {1, 2, ..., n} con i < k se tiene que si < sk , es decir, esa secuencia no tiene inversiones.
Ahora, supongamos que Tambin cada interacambio en burbuja reduce en 1 el nmero de inversiones, as que si se corre el

for interno el suciente nmero de veces entonces


n
iteraciones del

la secuencia se quedar sin inversiones y nalmente ser ordenada. Sin embargo, eso no es razn suciente para pensar que

for

exterior son sucientes. Observemos que en cada iteracin de burbuja

la ltima posicin de

del arreglo donde se hizo un itercambio cumple algo

peculiar, y es que no hay inversin de una inversin.

(i, j ) para la cual i = p, por tanto despus n interaciones ya no habr ningna posicin posible para primer elemento
Hemos comprobado que burbuja funciona, pero tanto su prueba de que

funciona como su cantidad de operaciones resultan notablemente peores que las de insercin y seleccin; as que llegamos a la siguiente pregunta obligatoria por qu la gente no se ha olvidado de este algoritmo que parece ser tan malo?, la respuesta puede ser un tanto sorprendente: este algoritmo slamente intercambia elementos adyacentes en el arreglo. La propiedad de slamente intercambiar elementos adyacentes en el arreglo hace que este algoritmo resulte adecuado en situaciones en las cuales resulta costoso intercambiar que se encuentran en posiciones alejadas e incluso tiene una consecuencia terica muy interesante: si en una secuencia se pueden intercambiar elementos adyacentes entonces la secuencia se puede ordenar.

12.7. Ordenamiento en O(n log n) por Mezcla


Los dos algoritmos que acabamos de ver son de complejidad

O(n2 )

generalmente los algoritmos que a la gente se le ocurren de manera natural

12.7. ORDENAMIENTO EN
son de esa complejidad.

O(N LOG N )

POR MEZCLA

143

Existen tambien muchos algoritmos de ordenamiento de complejidad

O(n log n)

(de hecho es posible comprobar que no hay algoritmo basado en una funcin de comparacin que sea mas rpido), por el momento slo nos concentraremos en uno de ellos llamado mezcla. Para poder conocer el algoritmo de mezcla primero hay que resolver el siguiente ejemplo:

Problema Resuelto 12.7.1.


y los elementos de

Encuentra un algoritmo que dadas dos suce-

siones ordenadas de manera no descendente ordenada de manera no descendente

B,

encuentre una sucesin

tal que conste de los elementos de

B,

el algoritmo deber funcionar en tiempo

O(n)

donde

es el tamao de la sucesin

C.

Solucin

Siguiendo la idea del algoritmo de seleccin, es posible que nos

venga a la mente buscar en ambas sucesiones cual es el elemento mas pequeo. Pero recordando que concluir que

a1 a2 a3 a4 ... y b1 b2 b3 b4 ..., podemos min(a1 , a2 , a3 , ..., b1 , b2 , b3 , ...) = min(a1 , a2 ).

Por lo cual, podemos encontrar el mnimo de ambas sucesiones en tiempo constante. La idea del algoritmo es encontrar el minimo de ambas sucesiones, quitarlo de la sucesin original e insertarlo al nal de Por cada insercin en

C , y luego repetir los

pasos anteriores hasta que ambas sucesiones esten vacas.

hay que encontrar el menor elemento de ambas

sucesiones, lo cual toma tiempo Asi que en total hay

O(1)

y posteriormente insertarlo al nal de

la sucesin, lo cual toma tambin tiempo

O(1).
bsquedas del mnimo, que nos da

inserciones y

como resultado un algoritmo que funciona en las cosas. Los prmetros nmero de elementos de

O(n).
de elementos de

A continuacin se incluye la implementacin del algoritmo para claricar

na y nb indican el nmero B respectivamente.

y el

Cdigo 12.5: Funcin que mezcla dos sucesiones ordenadas en otra sucesion ordenada 1 2 3 4 5

void

mezclar ( tipoDatos A[ ] ,

[] ,

int na , int nb ) { int a =0 , b =0 , c =0; while ( a<na | | b<nb ) { // Mientras A t e n g a

tipoDatos B[ ] ,

tipoDatos C

elementos B tenga elementos i f ( a<na && b<nb ) { // Si t a n t o A como B tienen elementos

i f (A [ a ] <B [ b ] ) {

144

CAPTULO 12. ORDENAMIENTO


C [ c++]=A [ a ++]; C [ c++]=B [ b ++]; }

6 7 8 9 10 11 12 13 14 15 16 } } } } } }

else {

else i f ( a<na ) { // Si s o l o A t i e n e
elementos

else { // Si s o l o B t i e n e e l e m e n t o s
C [ c++]=B [ b ++];

C [ c++]=A [ a ++];

n elementos de La idea del algoritmo de mezcla es ordenar los primeros 2 n la sucesin y los ltimos elementos de la sucesin por separado. Y pos2 teriormente ambas de la sucesin para obtener la sucesin

mezclar

mitades

ordenada. Y obviamente, para ordenar cada procede recursivamente.

mitad

de la sucesin por separado se

Mezcla,
inicio

De manera mas especca, se implementar una funcin llamada que recibir como argumentos dos arreglos,

orden-

colocar en

f in. La funcin ordenar Dest[inicio..f in].

todos los elementos

Dest y dos enteros de S [inicio..f in] y los


y

A continuacin se muestra una implementacin de la funcin Cdigo 12.6: Ordenamiento por Mezcla 1 2 3 4 5 6 7 8 9 10 11 }

ordenMezcla.

void

ordenMezcla ( tipoDatos

inicio ,

int f i n ) { int p i v , i ; i f ( i n i c i o >=f i n ) return ;

S[] ,

tipoDatos

Dest [ ] ,

int

for ( i = i n i c i o ; i <=f i n ; i ++)


D e s t [ i ]= S [ i ] ; o r d e n M e z c l a ( Dest , o r d e n M e z c l a ( Dest , p i v i n i c i o +1 , S, S, inicio , p i v +1 , piv ) ; fin ) ;

p i v =( i n i c i o + f i n ) / 2 ;

m e z c l a r (&S [ i n i c i o ] ,

&S [ p i v + 1 ] , &D e s t [ i n i c i o ] ,

f i n p i v ) ;

12.7. ORDENAMIENTO EN

O(N LOG N )

POR MEZCLA

145

Utilizando induccin es muy fcil darse cuenta de que este algoritmo funciona, pero el anlisis de complejidad de este algoritmo no luce tan sencillo.

T (n) es una conta superior para el tiempo que puede tardar en ejecutarse ordenM ezcla(S, Dest, a, a + n) para a, n N. n )) + O(T ( n )) + n). Por Tenemos que T es de complejidad O (O (T ( 2 2 n regla de la suma esto se puede simplicar a O (T ( ) + n), podriamos vernos 2 tentados a decir que cuando n = 2 la complejidad es O (n) y cuando n > 2 la complejidad sigue siendo O (n); pero eso no sera el camino correcto ya que
Vamos a llamarle a una funcin tal que 

n  en esta notacin representa una funcin y no solamente un argumento. 2 Al llegar a este punto parece difcil proceder con las reglas conocidas para

calcular complejidad, as que habr que volver a denir el valor de a una constante

en base

H: T (n) = H
cuando n=1

n H (2T ( ) + n + 1) 2
El siguiente arreglo muestra algunos valores de de 2:

evaluado en potencias

T (1) T (2) T (4) T (8) T (16) ...


Estos valores sugieren que:

= = = = =

H (1) H (3 + n) H (7 + 3n) H (15 + 7n) H (31 + 15n)

T (2x ) = H (2x+1 + 2x 1)
ra, vamos a denir una variable La ecuacin anterior se puede comprobar fcilmente con induccin. Ahon tal que n = 2x , la ecuacin anterior se

expresara de la siguiente manera:

T (n) = H (n(x + 1) + n 1) H (n(log2 (n) + 1) + n 1) H (nlog2 n + 2n 1)

146

CAPTULO 12. ORDENAMIENTO

O0 (n)
=

n
n 2 n 2
=

O1 (n)
+

n 22

n 22

n 22

n 22

O2 (n)
+

. . .

. . .

. . .

. . .

. . .

. . . +

n 2k

n 2k

n 2x

n 2x

Ox=lg n (n)
=

O
i=0

n 2 i 2
i

O
i=0

= O(x n) O(n lg n)

Figura 12.1: Rendimiento del ordenamiento por mezcla

Y ahora, como que

es una constante y por regla de la suma es fcil de ver

es de complejidad

O(n log n).

La gura 12.1 ilustra este anlisis que se acaba de realizar. Cabe mencionar que la complejidad solamente fu comprobada para poa a+1 tencias de 2, sin embargo, es fcil darse cuenta que si 2 n 2 entonces a a+1 T (2 ) T (n) T (2 ), lo que prueba que T es de complejidad O(nlogn) para cualquier entero

n.

Existe tambin un algoritmo muy popular de ordenamiento llamado Quicksort que en la mayora de los casos suele ser tan rpido como mezcla y requiere menos memoria. Pero no se hablar de ese algoritmo en este libro debido a la dicultad de analizar su tiempo de ejecucin, si el lector quiere aprender a usarlo puede consultar otro libro y usarlo bajo su propio riesgo.

12.8. PROBLEMAS

147

12.8. Problemas
12.8.1. Mediana
Tiempo Lmite: 1 segundo Un nuevo experimento espacial involucra a 1 hasta

objetos etiquetados desde

Se sabe de antemano que nmero natural.

es impar.

Cada objeto tiene una fuerza distinta(aunque desconocida) expresada por un El objeto con la fuerza mediana es el objeto fuerza mayor que

tantos objetos con una fuerza mas pequea que

tal que hay exactamente como objetos con una

Desafortunadamente, la unica manera de comparar las fuerzas es por un dispositivo que, dados 3 objetos distintos determina el objeto con fuerza mediana tomando en cuenta solamente esos 3 objetos.

Problema
Escribe un programa que determine el objeto con la fuerza mediana.

Libreria
Dispones de una biblioteca llamada

device con estas tres operaciones:

GetN, deber ser llamado una vez al principio sin parmetros; regresa
el valor de

Med3, deber ser llamado con tres etiquetas de objetos como parmetros; regresa la etiqueta del objeto con la fuerza media.

Answer, deber ser llamado una sola vez al nal, con una etiqueta de
objeto como argumento; reporta la etiqueta del objeto Puedes experimentar con una biblioteca de diseo propio

Ejemplo

Secuencia
2 5 4 3 1

Interaccin

148

CAPTULO 12. ORDENAMIENTO

1. 2. 3. 4. 5.

GetN() regresa 5. Med3(1, 2, 3) regresa 3. Med3(3, 4, 1) regresa 4. Med3(4, 2, 5) regresa 4. Answer(4)

Consideraciones
0<

N <1000

No esta permitido hacer mas de 7777 llamadas a la funcion ejecucin de tu programa.

Med3

por cada

Referencias
Este problema apareci por primera vez en la IOI del 2000, fue traducido por Luis Enrique Vargas Azcona durante el 2008 con propsitos de entrenamiento. El valor mximo de

fu modicado debido a que la solucin ocial no

es determinista pero hay soluciones deterministas que funcionan con

N <

1000(actualmente
IOI).

las soluciones no deterministas no son de inters para la

Parte IV Estructuras de Datos

149

151

En lo que se reere a la resolucin de problemas, muchas veces para plantear el problema imaginamos objetos y acciones que se relacionan entre si. Cualquier lenguaje de programacin tiene ya implementados tipos de datos bsicos como lo son enteros cortos, enteros largos, punto otante, caractres, arreglos y matrices. Sin embargo, a medida que nos adentramos ms y ms en la programacin, esos tipos de datos dejan de ser sucientes, podemos plantear problemas que traten de cosas mas all de los nmeros y arreglos. No debemos de desanimarnos y pensar que la computadora solo nos servir para problemas de nmeros y arreglos, ya que con un poco de creatividad podremos manejar una innidad de objetos distintos. Si bien una computadora solamente cuenta con herramientas para manejar nmeros, caractres y arreglos, es posible hacer una analoga

tando

represen-

ciertos objetos abstractos con arreglos y nmeros e implementando

funciones que

simulen

las acciones de estos objetos.

Las representaciones de estos

objetos abstractos

constan de una serie de

datos y funciones para manipular esos datos, a estas dos cosas juntas se les llama estructuras de datos. A continuacin conoceremos las estructuras de uso mas frecuente.

152

Cap tulo

13

Pilas, Colas, Listas


Iniciaremos nuestro estudio de las estructuras de datos con estas tres estructuras que son por mucho, las mas sencillas de todas: pilas, colas y listas.

13.1. Pilas
Imaginemos este sencillo escenario: un mesero tiene platos de colores apilados; de vez en cuando el que lava los platos coloca un plato recin lavado sobre la pila de platos; y en otras ocaciones el mesero toma el plato que esta hasta arriba y sirve ah la comida que ha sido preparada por el cocinero para posteriormente llevarla a su destino. Si sabemos de qu color es el primer plato de la pila, en qu momentos el que lava los platos coloc platos sobre la pila(tambien sabemos el color de los que se van aadiendo), y en qu momentos el mesero retir cada plato que se encontraba hasta arriba; podemos saber de qu color ser el plato que le toca a cada cliente. Una manera de saberlo podra ser, hacer una representacin dramatica de los hechos; pero esto no es necesario, ya que tambien podramos tomar un lapiz y un papel, y escribir una lista de los colores de los platos, posteriormente, ir escribiendo los colores de los platos que se pusieron en la pila al nal de la lista, y borrar el ultimo color de la lista cada que un plato se retire. No se necesita ser un gran matemtico para pensar en hacer eso, sin embargo, en el momento de querer implementar un programa en C que lo reprodusca, nos encontramos con que no tenemos ninguna lista donde se coloquen y se quiten cosas del nal, tenemos solamente arreglos, variables,

153

154

CAPTULO 13. PILAS, COLAS, LISTAS

estructuras, apuntadores, etc. Claro que podemos simular esta lista con las herramientas que nos proporciona C++ o incluso C, asi pues, este es un ejemplo de objetos(como la pila de platos) ligados a operaciones(como poner un nuevo plato o quitar un plato) que modican al objeto, es decir, una estructura de datos. Una pila, es la estructura de datos mencionada en este ejemplo, es decir, un altero de objetos. O mas objetivamente:

Denicin 13.1.1

(Pila)

Estructura de datos que simula una lista en la

cual solo se pueden realizar 2 operaciones: colocar un elemento al nal, o quitar un elemento del nal. Lo unico que se puede hacer en una pila es colocar un objeto hasta arriba, o quitar el objeto que esta arriba, en el ejemplo anterior si se quita un objeto de abajo o del centro(lo mismo que si se intenta aadir uno), la pila colapsara. Si queremos programar algo similar, lo mas obvio es guardar la informacin de la pila en un arreglo, adems en este ejemplo usaremos nmeros para denotar los colores. Imaginemos que el restaurant tiene en total 10 platos(un restaurant bastante pobre), ello nos indicara que un arreglo de tamao 10 podra guardar todos los platos sin temor a que el tamao del arreglo no alcance. Suponiendo que inicialmente hay 2 platos, uno de color 1, y otro de color 2, el arreglo debera lucir algo asi:

2 1 0 0 0 0 0 0 0 0
Si repentinamente el que lava los platos pone un plato hasta arriba de color 2, luego de ello el arreglo debera de lucir asi:

2 1 2 0 0 0 0 0 0 0
Si luego pone hasta arriba un plato de color 3, entonces el arreglo debera de quedar asi:

2 1 2 3 0 0 0 0 0 0
Pero si el mesero toma un plato de arriba, el arreglo estar de nuevo de esta manera:

2 1 2 0 0 0 0 0 0 0
Si el mesero vuelve a tomar un plato de arriba, el arreglo quedar de esta manera:

13.1. PILAS

155

2 1 0 0 0 0 0 0 0 0
Para lograr esto, basta con declarar un arreglo y una variable de tal manera que el arreglo diga especicamente qu platos hay en la pila, y la variable cuntos platos hay. Entonces, podemos representar los datos de una pila de la siguiente manera: 1 2

int int

p i l a [ tamao maximo ] ; p =0;

En esta implementacin supusimos que la pila consta nicamente de nmeros enteros, sin embargo en la prctica cualquier tipo de datos es vlido, incluso el funcionamiento es el mismo si se declaran varios arreglos para guardar elementos en la pila que consten de varias variables, por ejemplo, pares ordenados de nmeros. Cada que queramos aadir un elemento en la parte superior de la pila, es suciente con esta lnea de cdigo: 1 p i l a [ p++]= o b j e t o ; Y cuando queramos retirar el elemento que este en la parte superior. 1 p i l a [p ] = 0 ; Hay que hacer notar casi siempre es suciente decrementar al variable ma. Por ltimo, como cultura general, en ingles a la pila se le llama la operacin de poner un elemento en la parte superior se le llama de quitar un elemento se le llama

para retirar un elemento de la parte superior, pero todo depende del proble-

pop.

stack, a push, y la

Asi mismo, si la pila sobrepasa su tamao mximo, el error devuelto es stack overow o desbordamiento de pila(ahora ya sabemos qu quieren decir algunos errores comunes en aplicaciones inestables). Vale la pena recordar que cada que el sistema operativo maneja tambin una pila para cada programa que se est ejecutando, en dicha pila se aaden las variables locales cada que se llama a una funcin y se retiran cuando se regresa de la funcin. La implementacin de dicha pila es muy parecida a la que se muestra aqu, esto es un indicio de que a pesar de ser una implementacin muy sencilla tiene sus ventajas.

156

CAPTULO 13. PILAS, COLAS, LISTAS

13.2. Colas
Imagina una conversacin de chat entre 2 personas, aunque los conversantes no se den cuenta, existe algo llamado Dependiendo de la conexin, el gundo o incluso mas de un minuto. Si por un momento, por falla del servidor, una de las 2 computadoras pierde la conexin, y en ese momento un usuario est intentando mandar varios mensajes, el programa de chat guardar los mensajes que el usuario est tratando de mandar, y cuando se recupere la conexin, el programa de chat mandar los mensajes en el mismo orden que el usuario los escribi. Obviamente, el programa de chat no usa una pila para eso, ya que si usara una pila, el receptor leera los mensajes en el orden inverso que el emisor los escribi. Es decir, en una pila el ultimo que entra es el primero que sale. De ah que a las pilas se les conozca como estructuras LIFO(Last In, First Out). Existen otras estructuras llamadas colas, en una cola el primero que entra es el primero que sale. Su nombre deriva de las las que se hacen en los supermercados, cines, bancos, etc. Donde el primero que llega, es el primero en ser atendido, y el ltimo que llega es el ltimo en ser atendido(suponiendo que no haya preferencias burocrticas en dicho establecimiento). Las colas, son conocidas como estructuras FIFO(First In, First Out).

lag, es decir, el tiempo que tardan

las 2 computadoras en mandarse y recibir los mensajes.

lag

puede variar entre menos de un se-

Denicin 13.2.1

(Cola)

Una cola es una estructura de datos que simula

una lista, en la cual slo se pueden aplicar estas dos operaciones: colocar un elemento al nal, o quitar un elemento del principio. Para representar una cola obviamente necesitamos tambien un arreglo. Nuevamente para ilustrar cmo utilizar el espacio del arreglo imaginaremos una situacin divertida. Supongamos que hay varios participantes en la cola para el registro de la OMI(Olimpiada Mexicana de Informtica). Cada participante tiene un nombre que consiste de un nmero entero mayor o igual que 1(Son bastante populares esos nombres en nuestros das). Ccomo faltan muchos estados a la OMI, solo habr 10 participantes. Entonces el arreglo podra lucir algo asi...

0 0 0 0 0 0 0 0 0 0
En ese momento llega 3 y se forma

3 0 0 0 0 0 0 0 0 0

13.2. COLAS
Luego llega 5 y se forma

157

3 5 0 0 0 0 0 0 0 0
Despues llega 4 y se forma

3 4 5 0 0 0 0 0 0 0
Luego llega 9, y despues de eso llega 2

3 4 5 9 2 0 0 0 0 0
Entonces al encargado del registro se le ocurre comenzar a atender, y atiende a 3. En la vida real, los participantes daran un paso hacia delante, pero en una computadora, para simular eso, sera necesario recorrer todo el arreglo, lo cual es muy lento; por ello, es mas practico dejar el primer espacio de la cola en blanco.

0 4 5 9 2 0 0 0 0 0
Luego se atiende a 4

0 0 5 9 2 0 0 0 0 0
En ese momento llega 1 corriendo y se forma

0 0 5 9 2 1 0 0 0 0
Y as contina... Ya para este momento, te debes de estar imaginando que para implementar una cola, unicamente se requiere un arreglo y dos variables, donde las variables indican donde inicia y donde termina la cola. La mayora de las veces podremos saber por adelantado cuantos elementos se formarn en la cola. Sin embargo, suponiendo que se forma alguien e inmediatamente es atendido, se vuelve a formar y vuelve a ser atendido inmediatamente, y asi 1 000 veces, entonces se requerira un arreglo de tamao 1 000, cuando nunca hay mas de un elemento dentro de la cola. Para evitar eso, podemos aadir elementos al principio de la cola si ya no hay espacio al nal. Por ejemplo, si luego de que se atiendi a muchos participantes, la cola est de esta forma:

158

CAPTULO 13. PILAS, COLAS, LISTAS

0 0 0 0 0 0 0 0 7 3
Y para recticar algo 5 vuelve a formarse, podemos colocar a 5 al principio

5 0 0 0 0 0 0 0 7 3
Luego si 4 vuelve a formarse

5 4 0 0 0 0 0 0 7 3
Puede parecer extrao esto, pero el programa sabr que la cola empieza donde est 7 y termina donde est 4. Asi que si el organizador atiende al siguiente, atender a 7, y la cola quedar de esta manera

5 4 0 0 0 0 0 0 0 3
Luego atender a 3

5 4 0 0 0 0 0 0 0 0
Despues atender a 5

0 4 0 0 0 0 0 0 0 0
Y asi sucesivamente... Implementar la operacin de meter un elemento a la cola es muy sencillo: 1 2 3 c o l a [ f i n ++]=e l e m e n t o ;

i f ( f i n >=tamao
f i n =0;

de

la

cola )

Y cas lo mismo es sacar un elemento de la cola 1 2 3 i n i c i o ++;

i f ( i n i c i o >=tamao
i n i c i o =0;

de

la

cola )

13.3. LISTAS

159

Dato 1

Dato 2

Vaco

Figura 13.1: Representacin grca de una lista enlazada

13.3. Listas
Frecuentemente necesitamos tener almacenadas unas

listas de datos en

memoria, sabiendo que el nmero total de datos en memoria no sobrepasa Si disponemos de suciente memoria, podemos guardar en arreglos de tamao

n. memoria n

o una matriz de tamao

nk ,

pero no siempre dispon-

dremos de tanta memoria. Tambien hay veces que se requieren tener listas de nmeros y agregar nmeros a dichas listas pero no al nal ni al principio, sino en medio. Para solucionar estos y otros problemas, existen las listas enlazadas. Las listas enlazadas son estructuras de datos compuestas por una sucesin de elementos llamados nodos; en la que cada nodo contiene un dato y la direccin del proximo nodo, en caso de que haya prximo. La gura 13.3 muestra una representacin grca de una lista enlazada.

Denicin 13.3.1
cosas:

(Nodo)

Se considera un nodo a cualquiera de estas dos

Una estructura vaca Un elemento de informacin y un enlace a otro nodo. La tarea de implementar una lista enlazada puede hacerse ecazmente con 2 arreglos: uno para guardar los datos y otro para guardar los enlaces, adems se requiere una variable que diga el tamao de la lista de la siguiente manera: 1 2 3 Tipo

int int

d a t o [ tamao maximo t a m _ l i s t a =1;

de

la de

lista ]; la lista ];

p r o x i m o [ tamao maximo

//Tamao de l a l i s t a

Lo nico que falta denir es el elemento vaco, para ello, podemos denir que el dato 0 es el elemento vaco, y en el momento que nos encontremos con l, sabemos que la lista ya habr terminado. La ventaja de usar el 0 para representar el elemento vaco radica en que muchos arreglos se inicializan automticamente en 0 y no es necesario inicializarlos despus. Si se mira con atencin las tres lneas sugeridas para declarar la lista enlazada, es posible darse cuenta que el tamao inicial de la lista es 1, a

160

CAPTULO 13. PILAS, COLAS, LISTAS

pesar de que inicialmente la lista no tiene elementos. Esto se debe a que esa implementacin est pensada para reservar el nodo 0 como el nodo vaco, por lo que inicialmente la lista consiste nicamente del nodo vaco. Como toda estructura de datos, las listas tienen operaciones para manipular los datos, aqu contemplaremos un par de operaciones: insertar y recorrer. Como el lector puede imaginar, usaremos una estrategia similar a la de la pila para determinar qu partes de los arreglos ir utilizando conforme se vayan insertando nuevos elementos. Mas precisamente, una vez insertados

elementos, las primeras

n+1
justo

posiciones de los arreglos habrn sido usadas. Una vez considerado todo esto, insertar un nodo con un dato despues de otro nodo 1 2 3 4 5 } Lo que hace este cdigo es colocar tro del arreglo

x,

k,

se puede hacer facilmente en tiempo constante: x,

void

i n s e r t a r ( Tipo

int

k){

d a t o [ t a m _ l i s t a ]= x ; p r o x i m o [ t a m _ l i s t a ]= p r o x i m o [ k ] ; p r o x i m o [ k ]= t a m _ l i s t a ++;

datos,

luego colocar un enlace al sucesor de

espacio en blanco dentro del arreglo que se acaba de crear. De esa forma que apuntaba

x en el siguiente espacio en blanco denk en el siguiente proximo, y hacer que k apunte al nodo

k apuntar al nuevo nodo, y el nuevo nodo apuntar al nodo

k.

Si observamos bien, esta implementacin solo funciona cuando ya se tiene un nodo inicial en la lista, por lo cual es necesario crear el primer nodo mediante otro. La siguiente funcin crea un primer nodo de una lista asignndole el valor 1 2 3 4 5 } Una vez que tenemos creada una lista necesitamos consultar los datos de alguna manera, en una lista lo nico que podemos hacer es consultar sus datos en rden, desafortunadamente tenemos que pagar el precio de no poder acceder directamente a los datos, pero por los motivos antes expresados, a veces vale la pena.

y devuelve el ndice del nodo:

int

primer_nodo ( Tipo

x){

d a t o [ t a m _ l i s t a ]= x ;

return

proximo [ t a m _ l i s t a ] = 0 ; t a m _ l i s t a ++;

13.3. LISTAS

161

Al recorrer la lista se pueden hacer muchas cosas, aqu mostraremos simplemente cmo imprimir los elementos de la lista. El siguiente cdigo imprime todos los datos contenidos en una lista, asumiendo que 1 es el primer elemento de dicha lista. 1 2

for ( i =1; i ! = 0 ; i =p r o x i m o [ i ] )
printf (" % d" , dato [ i ] ) ; Estas implementaciones estan algo lmitadas, ya que se le da un trato especial al primer elemento de la listas y no se pueden borrar nodos entre otras cosas. Muchos lenguajes de programacin(por ejemplo C++) ya tienen previamente implementadas listas enlazadas, con implementaciones bastante mas completas que las mostradas aqu. El objetivo de mostrar las listas y las implementaciones es simplemente mostrar la idea de las listas enlazadas y una implementacin sencilla, ya que juega un papel muy importante en el estudio de los algoritmos y sirven de base para entender otras estructuras de datos. Tambin hay que hacer notar que tanto las pilas como las colas pueden ser implementadas con listas enlazadas, pero su implementacin es mas tediosa, su rendimiento es mas lento y adems los enlaces hacia el siguiente elemento requieren cierta memoria adicional; la eleccin sobre qu implementaciones usar depende del problema en especco.

162

CAPTULO 13. PILAS, COLAS, LISTAS

13.4. Problemas
13.4.1. Equilibrando Smbolos
Jorge est estudiando para ser un desarrollador de software y como tarea le han encargado elaborar un compilador. Para hacerlo requiere de un algoritmo que verique que la sintaxis del cdigo fuente sea la correcta. Una de las cosas que se requieren para que un cdigo tenga una sintaxis correcta es que todos los parntesis, corchetes y llaves estn correctamente emparejados, es decir, que por cada parntesis, corchete o llave que abra exista una pareja correspondiente que cierre, y adems que no se traslapen. Jorge requiere implementar un algoritmo que reciba un programa como entrada y devuelva la posicin del primer error sintctico, considerando nicamente las posiciones de parntesis (  (,  ) ), corchetes (  [,  ] ) y llaves (  {,  } ). El cdigo fuente consiste de una sola lnea con segundo en la posicin dos y as sucesivamente. Algunos de los caracteres pueden ser;  (,  ),  [,  ],  { o  }. Dos smbolos son correspondientes si son del mismo tipo y uno de ellos el otro casos:

caracteres (incluyen-

do espacios en blanco) donde el primer carcter est en la posicin uno, el

cierra

abre

mientras

(ej.  ( y  ) son smbolos correspondientes).

Un cdigo contiene un error sintctico si se cumple al menos uno de tres

Para alguno de los smbolos que abren no existe un smbolo correspondiente que cierre. (ej.

(a + b) + (c [)).

Cuando se encuentre un smbolo que cierre sin estar antes su correspondiente que abra. (ej. ([a+b]) + c] * (2 + 2)). Cuando Entre dos smbolos correspondientes (uno que abra y otro que cierre del mismo tipo) exista un tercer smbolo de tipo diferente que abra y que no tenga su correspondiente en ese rango, es decir, cuando los smbolos se traslapen. (ej.

a(b + c {d/h)}).

Problema
Escribir un programa que dados

(el nmero de caracteres del cdigo

fuente a revisar incluyendo espacios en blanco), y el cdigo en s, determine si ste es correcto sintcticamente o no.

13.4. PROBLEMAS
Entrada
Linea 1: Un solo entero

163

N
Salida

caracteres que representan el cdigo.

La palabra SI si el cdigo es sintcticamente correcto, la palabra NO en caso contrario.

Ejemplo de Entrada

20 a(b + c * { d / h )}
Ejemplo de Salida

NO
Referencias
El problema fue escrito por Nephtali Garrido y utilizado en uno de los primeros examenes preselectivos de la Olimpiada Mexicana de Informtica a nales del 2007.

164

CAPTULO 13. PILAS, COLAS, LISTAS


Pirmides Relevantes
pirmides alineadas en lnea recta.

13.4.2.
Hay

Las pirmides estan numeradas de 1 a Cada pirmide

N,

de izquierda a derecha.

i tiene un nivel de relevancia Ri y tambien tiene una altura

Hi .
Desde la parte mas alta de cualquiera de las pirmides, es posible ver todas las pirmides hacia la izquierda o hacia la derecha, siempre y cuando cuando no exista una piramide mas alta que obstruya la vista. Es decir, una pirmide pirmide para

i, s y Hk Hi .

solo s

NO

se puede ver desde la parte mas alta de una

existe alguna

tal que

i<k <j

j <k <i

Por ejemplo, si hay 6 pirmides con alturas 1, 3, 2, 1, 5, 2(de izquierda a derecha en ese orden) desde la parte mas alta de la pirmide 3(que tiene altura 2), se pueden ver las pirmides 2, 3, y 4 y 5 y las pirmides 1 y 6 no pueden ser vistas desde ah ya que las pirmides 2(de altura 3) y 5(de altura 5) obstruyen la vista. En cambio, desde la parte mas alta de la pirmide 5 se pueden ver todas las pirmides, mientras que desde la parte mas alta de la pirmide 6, solamente se puede ver la pirmide 5. Un gua de turistas ha pedido tu ayuda.

Problema
Escribe un programa que dadas las caractersticas de las pirmides, determine, para la parte mas alta de cada pirmide, cual es la pirmide mas importante que puede ser vista desde ah.

Entrada
Lnea 1: Un solo nmero entero

N.
contiene los dos enteros

Siguientes

lneas: La lnea

i+1

Hi

Ri

separados por un espacio.

Salida

nmeros enteros, donde el

isimo

entero representa la relevancia

de la pirmide mas relevante que se pueda ver desde la parte mas alta de la pirmide

i.

13.4. PROBLEMAS
Entrada de Ejemplo

165

6 1 3 2 1 5 2

10 5 4 3 1 2

Salida de Ejemplo

10 10 5 4 10 2
Consideraciones
Puedes asumir que

2 N 1000000.

Referencias
El problema fue escrito por Luis Enrique Vargas Azcona y usado en uno de los primeros exmenes preselectivos de la Olimpiada Mexicana de Informtica a nales del 2007.

166

CAPTULO 13. PILAS, COLAS, LISTAS

Cap tulo

14

rboles Binarios
Seguramente despues de haber visto las listas enlazadas te lleg a la mente la idea de colocar mas de un enlace en cada nodo. No fuiste el primero en tener esa idea, pues los rboles binarios son estructuras de datos parecidas a las listas enlazadas, con la diferencia de que cada nodo puede tener hasta 2 enlaces(de ah el nombre de binario). Estas estructuras de datos tienen muchas aplicaciones, las mas frecuentes involucran ordenamiento. Al igual que a las listas enlazadas, es posible ver a los rboles binarios como un conjunto de informacin. La gura 14.1 muestra una representacin grca de un rbol binario, como se puede ver, es igual que una lista enlazada excepto porque cada nodo puede tener hasta dos enlaces y no solamente uno. Antes de seguir entrando en materia, ser conveniente dar unas cuantas deniciones. Por ahora nos reservaremos la denicin general de rbol, pues requiere de teora de grafos para comprenderla, sin embargo, un rbol binario se puede denir de la siguiente manera, sin recurrir a teora de grafos:

nodos

iterconectados y a cada nodo se le puede asignar

Denicin 14.0.1
elementos llamados Para todo

nodos o vrtices, tal que:


donde:

(rbol Binario)

Un rbol binario es un conjunto

de

v V , v = (A, B )

A B

es el conjunto vaco o un enlace a otro vrtice

decimos que

v v

apunta a

a), A recibe el nombre de hijo b), B


recibe el nombre de

a V (en b V (en

izquierdo de v

este caso

es el conjunto vaco o un enlace a otro vrtice apunta a

decimos que

hijo derecho de v.

este caso

167

168

CAPTULO 14. RBOLES BINARIOS

11

Figura 14.1: Representacin grca de un rbol binario

Hay un nico elemento decimos que Para todo a

es la

r V tal raz de V . v=r

que ningn otro vrtice apunta a l;

vV v V

tal que

al cual llamaremos

padre de v.

existe un nico

wV

tal que

apunta

v = r existe un camino desde la raiz hasta v (mas objetivamente, existen w1 , ..., wk V tales que w1 = r, wk = v y wi apunta a wi+1 para todo entero 1 i < k ).
Para todo tal que La denicin anterior no es la que se utiliza en teora de grafos para los rboles binarios y en cierto sentido tiene algunas propiedades distintas, sin embargo, los rboles binarios utilizados en programacin son exactamente los que se acaban de denir. El lector puede considerar esto una ambigedad, y realmente lo es, pero puede estar tranquilo de que en este libro se hablar solo de los rboles binarios que se usan en programacin evitando as toda ambigedad. Esto no quiere decir que nos vayamos a limitar en decir cmo programar los rboles binarios, sino que tambin exploraremos sus propiedades matemticas. Por lo general a cada nodo se le suele asociar un contenido llamado puede carecer de claves y an as sigue siendo rbol binario. Existen tambin otras deniciones que es conveniente conocer: Los enlaces que unen los nodos reciben el nombre de aristas.

clave,

sin embargo, esto no forma parte de la escencia de un rbol binario, ya que

14.1. IMPLEMENTACIN
Se dice que un nodo va desde

169

es hijo de un nodo

A, si existe alguna arista que

hasta

B.

Por ejemplo, en la gura, 7 es hijo de 2, 4 es hijo

de 9, 11 es hijo de 6, etc. Se dice que un nodo va desde

es padre de un nodo

si existe una arista que

hasta

B.

Ej. 9 es padre de 4, 6 es padre de 5 y de 11, etc.

Se dice que un nodo es hoja, si no tiene hijos. Ej. 11 y 4 son hojas, pero 6, 7, y 9 no lo son. La rama izquierda de un nodo es el rbol que tiene como raz el hijo izquierdo de tal nodo, por ejemplo 7, 2, 6, 5, 11 son los nodos de la rama izquierda de 2. La rama derecha de un nodo es el rbol que tiene como raz el hijo derecho de tal nodo. La altura de un rbol es la distancia(en ristas) de la hoja mas lejana a la raz.

14.1. Implementacin
Los rboles binarios tienen muchas formas de implementarse, cada una con sus ventajas y desventajas, por ahora solo trataremos una. Pueden ser implementados utilizando 3 arreglos: clave(los datos que guarda cada nodo), hijo izquierdo e hijo derecho. Y por supuesto otra variable entera: el nmero de nodos en el rbol. 1 2 3 4 Tipo

int int int

c l a v e [ maximo i z q [ maximo d e r [ maximo n o d o s =0; de de

de

nodos ] ;

nodos ] ; nodos ] ;

// Dato que guarda e l nodo // Hijo i z q u i e r d o // Hijo derecho

En seguida se muestran los datos que podran contener los arreglos para obtener el rbol que se muestra el la gura 14.1 : nodo clave izq der 0 2 1 4 1 7 2 3 2 2 -1 -1 3 6 6 5 4 5 -1 7 5 11 -1 -1 6 5 -1 -1 7 9 8 -1 8 4 -1 -1

Como se observa en la tabla, se est utillizando el nodo 0 como raz, y el -1 para representar el vaco. Se puede crear un nuevo nodo facilmente con la siguiente funcin:

170

CAPTULO 14. RBOLES BINARIOS

1 2 3 4 5 6

int

crear_nodo ( Tipo

dato ) {

c l a v e [ n o d o s ]= d a t o ; i z q [ n o d o s ]= 1; d e r [ n o d o s ]= 1; n o d o s ++;

return
}

Como se puede ver, la funcin recibe como parmetro el dato que va a tener el nuevo nodo y regresa el lugar en el arreglo donde est ubicado el nuevo nodo, depende del programador saber dnde poner el enlace para accesar a dicho nodo.

14.2. Propiedades Bsicas


Los rboles binarios tienen muchas propiedades que pueden resultar tiles. Exploraremos varias de ellas.

Teorema 17. Todo rbol binario con n nodos contiene exactamente n 1


aristas. Demostracin.
Para todo es hijo de es decir, hay Sea

un rbol binario con

nodos.

Toda arista une a un nodo padre con un nodo hijo, es decir, el nmero de aristas es igual al nmero de nodos que tienen un padre.

vA

tal que

no es la raz, existe un nico nodo

tal que

w. Por lo tanto, hay tantas aristas como nodos distintos de la raz, n 1 aristas.

Teorema 18. Un rbol binario de altura n tiene a lo mas 2n+1 1 nodos.


Demostracin.
raz. Si para algun Existe un solo nodo a distancia 0 de la raz, existen a lo ms 2 nodos a distancia 1 de la raz, existen a lo ms 4 nodos a distancia 3 de la

existen a lo ms

2k

nodos a distancia

de la raz, como

cada uno de esos nodos puede tener a lo ms 2 hijos, entonces existen a lo k+1 ms 2 nodos a distancia k + 1 de la raiz. k Por induccin, existen a lo ms 2 nodos a distancia k de la raz para todo

k N. 20 + ... + 2n ,
lo que por la ecuacin

Por lo tanto, el nmero de nodos es n+1 4.1 es igual a 2 1.

Un rbol estrictamente binario es aquel en el que todo nodo tiene 2 hijos o bien no tiene hijos.

14.3. RECORRIDOS

171

jas.

Teorema 19. Un rbol estrictamente binario A tiene exactamente


Sea

|A|+1 2

hoel

Demostracin.

el nmero de hojas que hay en el rbol, y sea

nmero de nodos que no son hojas, es decir Por cada nodo

m = |A| h.

vA

tal que

no es una hoja, existen 2 aristas, por lo

tanto el nmero de aristas es igual a

2m. |A| 1
aristas. Es decir

Por el teorema 17, el rbol tiene exactamente

|A| 1 = 2m = 2|A| 2h

y as se concluye que:

h=

|A| + 1 2

(14.1)

14.3. Recorridos
Aunque nos hemos dedicado a demostrar propiedades de los rboles binarios e incluso ya vimos cmo representarlos en un programa, an no hemos visto ni una sola cosa til que se pueda realizar con ellos. Desafortunadamente el lector tendr que esperar si quiere encontrar cosas El tema de los recorridos en los rboles binarios suele parecer muy aburrido en un inicio, ya que su explicacin carece de motivacin. Por el momento el lector deber de creer que dichos recorridos nos servirn muy pronto. An as se explicarn supercialmente ejemplos de dnde se usa cada recorrido. Existen 3 formas recursivas distintas de visitar los nodos de un rbol binario:

tiles, pues antes tenemos que estudiar los recorridos en rboles binarios.

Recorrido en Preorden. Tambien llamado Bsqueda en Profundidad


en la teora de grafos, consiste en visitar primero el nodo actual, luego el hijo izquierdo y despues el hijo derecho. Este recorrido visita los nodos del rbol binario de la gura 14.1 en el siguiente rden: 2 7 2 6 5 11 5 9 4. Puede ser implementado de la siguiente manera: 1 2 3 4 5 6 7 }

void

preorden (

i f ( nodo==1) return ;

int

nodo ) {

v i s i t a ( nodo ) ; p r e o r d e n ( i z q [ nodo ] ) ; p r e o r d e n ( d e r [ nodo ] ) ;

172

CAPTULO 14. RBOLES BINARIOS


Entre otras cosas, este recorrido es til para saber cmo recorrera una persona, un robot, o culquier cosa que pueda trasladarse pero no teletransportarse una estructura similar a un rbol binario de bsqueda; ya que para llegar a los nodos hijos hay que pasar primero por el nodo padre. Dicho de otra manera, estos recorridos son

caminables.

Recorrido en Orden. En algunos lugares se le menciona como inorden, consiste en visitar primero el hijo izquierdo, luego el nodo actual, y nalmente el hijo derecho. Este recorrido visita los nodos del rbol binario de la gura 14.1 en el siguiente rden: 2 7 5 6 11 2 5 4 9. El siguiente cdigo lo ilustra: 1 2 3 4 5 6 7 } La principal aplicacin de este recorrido es en el ordenamiento, dicha aplicacin se tratar despus.

void

orden (

i f ( nodo==1) return ;

int

nodo ) {

o r d e n ( i z q [ nodo ] ) ; v i s i t a ( nodo ) ; o r d e n ( d e r [ nodo ] ) ;

Recorrido en Postorden. Como ya te imaginars, consiste en visitar


primero los nodos hijos y despus el nodo actual. Este recorrido visita los nodos del rbol binario de la gura 14.1 en el siguiente rden: 2 5 11 6 7 4 9 5 2. 1 2 3 4 5 6 7 } A veces es necesario borrar los nodos al visitarlos, en caso de que fuera as, este recorrido tendra la caracterstica de que solamente desapareceran hojas del rbol y en ningun momento habra vrtices aislados. Es decir, este recorrido es indispensable cuando se requiere liberar la memoria de un rbol binario(aunque estas implementaciones no reser-

void

postorden (

i f ( nodo==1) return ;

int

nodo ) {

p o s t o r d e n ( i z q [ nodo ] ) ; p o s t o r d e n ( d e r [ nodo ] ) ; v i s i t a ( nodo ) ;

14.4. RBOLES BINARIOS DE BSQUEDA

173

ven y liberen dinmicamente la memoria, es muy probable que el lector lo necesite realizar en algun momento de su vida).

14.4. rboles Binarios de Bsqueda


Los rboles binarios son bellos, pero no demuestran todo su potencial hasta que se utilizan como rboles binarios de bsqueda; los rboles binarios de bsqueda aparecieron con el propsito de mantener conjuntos de datos ordenados. Ya hemos visto como ordenar conjuntos de datos, pero si en algun momento dado quicieramos agregar mas datos al conjunto, con lo que sabemos hasta ahora tendramos que repetir todo el ordenamiento, o en el mejor de los casos usar insercin, como ya hemos visto, si una insercin toma tiempo lineal entonces inserciones en

inserciones tomaran tiempo cuadrtico. lamentablemente los rboles que estudiaremos no

El principal objetivo de los rboles binarios de bsqueda es poder hacer

O(logN );

alcanzan ese objetivo, pero al menos se

acercan

de cierta manera.

Denicin 14.4.1 (Arbol Binario de Bsqueda). Un rbol binario de bsqueda es un rbol binario Adems, para todo Si

A con una funcin de costo w : A C v A: i)

donde

es un

conjunto con un rden total.

v tiene w(i) v tiene w(d)

algn descendiente izquierdo(llammosle

entonces

w(v ) w(v )

Si

algn descendiente derecho(llammosle

d)

entonces

Al nmero

w(v )

le vamos a llamar el

peso de v o la clave de v.

La principal aplicacin de estos rboles en este captulo ser la de guardar elementos en el rbol y saber si ya se haban insertado de manera eciente. Hasta este momento hemos visto cmo recorrer rboles binarios y cmo guardarlos en memoria, sin embargo, sin una manera de construirlos, esto resultara intil. No vale la pena entrar en detalle respecto a cmo construir un rbol binario de bsqueda de un solo nodo. Una vez teniendo un rbol binario de bsqueda, es necesario poder agregarle mas nodos, ya que de otra manera de nada nos servira que el recorrido en rden procesara las claves de un rbol binario de bsqueda en rden no descendente.

174

CAPTULO 14. RBOLES BINARIOS


La insercin de un dato en un rbol binario de bsqueda, adems de que

implica crear un nodo, la parte mas nuevo nodo.

difcil

radica en saber dnde insertar el

Esa dicultad se supera sin mucho problema adoptando un procedimiento recursivo, llamaremos Si

a la clave que tendr el nuevo nodo:

x x

es mayor que la clave de la raz y no hay rama derecha, entonces

hay que crear un nodo con clave Si

x que haga las veces de rama derecha.

es mayor que la clave de la raz y hay rama derecha, entonces hay

que insertar Si

en la rama derecha.

x es x

menor que la clave de la raz y no hay rama izquierda, entonces

hay que crear un nodo con clave Si

x que haga las veces de rama izquierda.

es menor que la clave de la raz y hay rama izquierda, entonces

hay que insertar

en la rama izquierda.

Si se diera el caso que binario de bsqueda.

fuera igual a la clave de la raz,

puede ser

insertado en cualquiera de las dos ramas sin alterar la condicin de rbol La gura 14.2 muestra una serie de inserciones utilizando este algoritmo. A continuacin se muestra una implementacin de dicho algoritmo: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 } } } } } } }

void

insertar (

int nodo , T i p o d a t o ) { i f ( c l a v e [ nodo ] > d a t o ) { i f ( i z q [ nodo ]==1){


i z q [ nodo ]= c r e a r _ n o d o (

else {

dato ) ; i n s e r t a r ( i z q [ nodo ] , d a t o );

else {

i f ( d e r [ nodo ]==1){
d e r [ nodo ]= c r e a r _ n o d o (

else {

dato ) ; i n s e r t a r ( d e r [ nodo ] , dato ) ;

14.4. RBOLES BINARIOS DE BSQUEDA

175

8 4 8 8

5 8

5 8

10

10

Figura 14.2: Insercin de la secuencia de enteros 8 4 5 9 10 3 en un rbol binario de bsqueda.

176

CAPTULO 14. RBOLES BINARIOS


La funcin anterior, crea un nuevo nodo con clave

dato

y lo inserta en un

rbol(o subrbol) que tiene raiz en nodo. Asi que si queremos generar un rbol de bsqueda con las claves 8, 4, 5, 9, 10, 3 lo unico que tenemos que hacer es: 1 2 3 4 5 6 r a i z =c r e a r _ n o d o ( 8 ) ; insertar ( raiz , insertar ( raiz , insertar ( raiz , insertar ( raiz , insertar ( raiz , 4) ; 5) ; 9) ; 10) ; 3) ;

Ahora podemos ver que un rbol binario de bsqueda permite mantener un conjunto de datos ordenado e insertar nuevos datos; pero el objetivo de los rboles binarios de bsqueda es poder hacer inserciones en tiempo logartmico, lo cual an no hemos logrado.

14.5. Encontrar un Elemento en el rbol Binario de Bsqueda


Otra aplicacin de los rboles binarios de bsqueda, es el hecho de saber si en el rbol existe un nodo con una determinada clave. Nuevamente la recursin resuelve fcilmente este problema. Llammosle

al valor que se est buscando: Si la clave de la raiz es

entonces

est en el rbol. entonces

Si la clave de la raz es menor que solo s

x, x,

x x

est en el rbol si y

x x

est en la rama derecha entonces est en el rbol si y

Si la clave de la raiz es mayor que solo s est en la rama izquierda

Estas 3 observaciones conducen instantanemente a la siguiente implementacin recursiva: 1 2 3 4 5 6

bool

esta_en_el_arbol (

int nodo , T i p o i f ( nodo==1) return f a l s e ; i f ( c l a v e [ nodo]==x ) { return true ; } else i f ( c l a v e [ nodo ] < x ) {

x){

14.6. COMPLEJIDAD
7 8 9 10 11 } Como se podr notar, si el rbol tiene altura } }

177

else {

return return

e s t a _ e n _ e l _ a r b o l ( d e r [ nodo ] , e s t a _ e n _ e l _ a r b o l ( i z q [ nodo ] ,

x) ; x) ;

h,

se visitarn a lo ms

nodos para encontrar el nodo deseado, o bien, darse cuenta que no existe.

14.6. Complejidad
Respecto a la complejidad de la insercin en rboles binarios de bsqueda, hay mucho que decir. Ya que si se insertan los datos en rden ascendente o descendente, insertar un nuevo nodo requerira

O(n); pero tambin es posible

insertar los datos en otro rden de manera que cada insercin tome a lo ms

log2 n

llamadas recursivas.

Consideremos el ejemplo de insertar los nmeros enteros desde 1 hasta 7. Si se insertaran de la siguiente manera no obtendramos algo no muy diferente a una lista enlazada: 1 2 3

for ( i =2; i <=7; i ++)

r a i z =c r e a r _ n o d o ( 1 ) ; insertar ( raiz , i);

De hecho, el rbol binario tendra una altura de 6 aristas. Pero si se insertaran los nodos de la siguiente manera, obtendramos algo muy diferente: 1 2 3 4 5 6 7 r a i z =c r e a r _ n o d o ( 4 ) ; insertar ( raiz , insertar ( raiz , insertar ( raiz , insertar ( raiz , insertar ( raiz , insertar ( raiz , 2) ; 1) ; 3) ; 6) ; 5) ; 7) ;

En este segundo caso, obtendramos un rbol con altura de 2 aristas. De esta manera, es posible que una insercin tome

O(log n) tome O(n),

dependiendo de cmo se haya construido el rbol, pero esa no es toda la dicultad, ya que aqu solamente presentamos 2 formas de construir un rbol, cuando en realidad hay

n!

formas de construirlo, y adems, puede que un

mismo rbol se pueda construir de dos o mas maneras diferentes.

178

CAPTULO 14. RBOLES BINARIOS


A estas alturas, obviamente nos gustara saber,

en promedio, qu altura

tiene un rbol binario de bsqueda?. Prestando atencin a lo que signica en promedio, podemos denirlo como la suma de las alturas de los sertando los nmeros de 1 a Vamos a llamar

n!

rboles(dichos rboles se generan in-

en todos los rdenes posibles) entre

n!.

a una funcin

NQ

tal que

la altura al generar un rbol binario de bsqueda Por denicin, si

S (n) sea el de n nodos.

promedio de

n > 1: S (n) =
n! i=1

H (P (i)) n!

(14.2)

Donde

H (P (i)) n!).

representa la altura del rbol generado por una per-

mutacin numerada como i(suponiendo que se numeraran todas las permutaciones de 1 a rboles con rboles con Es necesario tomar en cuenta que

F (n + 1) F (n),

ya que todos los cada uno de los

n + 1 nodos se obtienen generando primero n nodos y aadiendoles otro nodo al nal. b

Con lo anterior, es fcil demostrar que la altura de un rbol con en la rama izquierda y de

nodos

nodos en la rama derecha tiene en promedio altura

S (max(a, b)) + 1.
Tambin hay que hacer la observacin ahora de que se generan

(n 1)!

rboles con cada una de las claves como raz. Por lo tanto, si

n>1

entonces:

S (n) =
Si

n1 i=0

S (max(i, n i 1)) + 1 n
n1 i= n 2

(14.3)

es par:

2 S (n) =
Si

S (i) +1
(14.4)

es impar

2 S (n) =
Sabemos de antemano que adivinar:

n1 i= n 2

S (i) +
y

S(

n 2

n1 S (1) = 1

2 S (0) = 0.

+1

(14.5)

Nuevamente buscaremos evaluaremos algunos valores de

n e intentaremos

1 S (2) = 2 2 +1=2 2 S (3) = 2 3 +1 +1=2+ 3 2 3

14.7. EL BARRIDO, UNA TCNICA TIL

179

S (4) = 2

4+ 2 3 4

+1=3+

1 3 4 5

+2 +1=3+ S (5) = 2 6 5 5 S (6) = 2


9+ 4 5 6

+1=4+
2,67 7

4 15

,4 S (7) 2 11 + 7

+ 1 = 4,64

S (8) 5,00 S (9) 5,30 S (10) 5,60 S (11) 5,85 S (12) 6,11 S (13) 6,33 S (14) 6,55 S (15) 6,74 S (16) 6,94
Aqu es mas difcil encontrar un patrn, el nico patrn que parece apareEsto sugiere que la complejidad es S (16) S (8) S (4) , y parece S (8) S (4) S (2) haber una conrmacin de dicha sospecha. logaritmica. Y al examinar los valores de las razones Por el momento no disponemos de sucientes herramientas para demostrar esto para cualquier para todas las cer es que

S crece cada vez mas lento.

n,

sin embargo, con un programa es posible demostrarlo

menores o iguales 50 millones, lo cual debe ser suciente

para casi cualquier aplicacin.

14.7. El Barrido, una Tcnica til


El clculo de los valores de

tampoco es algo trivial, ya que para alguna

est

denida a travs de una sumatoria, y si se calcula esa sumatoria cada vez que se quiera calcular el valor de antes

S (i)

i,

requerira en total un

tiempo cuadratico, ya que para calcular

S (i)

es necesario haber calculado

S (1), S (2), ..., S (i 1). S

Para poder calcular los valores de

tcnica que comunmente la denominan

barrido

en tiempo o

ventana deslizante.

O(n)

haremos uso de una

180

CAPTULO 14. RBOLES BINARIOS


Un barrido normalmente consiste en obtener el valor de una funcin eval-

uada en un intervalo a partir del valor de la misma funcin evaluada en un intervalo parecido. No existe una regla general de cundo es conveniente aplicar un barrido, pero en el cluculo de

S,

como lo veremos a continuacin, si es conveniente.

Otra forma de denir

es como:

S (i) =
Cuando

F (i) +1 i

(14.6)

es par y

S (i) =
Cuando

i ) F (i) S ( 2 + +1 i i

(14.7)

es impar.

S en tiempo lineal es que si i 1 F (2i + 1) = F (2i) S (i) + S (2i). Es decir, si se sabe de antemano cuales son los valores de F (1), ..., F (i) y el valor de S (i), calcular los valores de S (i + 1) y F (i + 1) se puede hacer en
La observacin clave para lograr calcular entonces

En las dos ecuaciones anteriores se est utilizando n1 S (i). i= n 2

F (i)

para denotar

F (2i) = F (2i 1) + S (2i 1)

tiempo constante. Por lo cual, el algoritmo consistira en llenar un arreglo que se vayan encontrando de la funcin el valor de calcular

con los valores

y mantener una variable

num

con

F (i). S (1), luego


despus calcular

Para que este algoritmo funcione es necesario primero calcular

S (2),

S (3),

etc. Esta forma de calcular valores de

funcines recursivas de abajo hacia arriba es conocida como Programacin Dinmica. En seguida se muestra una implementacin del algoritmo: 1 2 3 4 5 6 7 8 9 10 11

double double int

S[50000001]; num = 0 . 0 ;

main ( ) {

double

i ;

S[0]=0; S[1]=1;

for ( i =2; i <=50000000; i ++){ num +=S [ int ( i ) 1 ] ; i f ( int ( i ) %2==0){ S [ int ( i ) ] = ( 2 . 0 num ) / i + 1 . 0 ;

14.7. EL BARRIDO, UNA TCNICA TIL


12 13 14 15 16 17 18 } Utilizando la ascersin } } }

181

else {
S[

num =S [

int ( i / 2 ) ] ; int ( i ) ] = ( 2 . 0 num ) / i +S [ int ( i


/2) ] / i +1.0;

return

0;

anterior, es posible comprobar que

assert(S [int(i)] <= 3,2 log (i)) en el programa S (i) 3,2ln i para toda i entre 1 y 50

millones. De ah llegamos al siguiente teorema

Teorema 20 (Altura promedio de un rbol binario de bsqueda). El valor esperado de la altura de un rbol binario de bsqueda construido al azar es menor o igual a 3,2log (n) donde n es el nmero de nodos del rbol.
Ahora hemos conrmado como los rboles binarios de bsqueda se acercan bastante a su objetivo inicial de hacer inserciones en tiempo logartmico, ya que un rbol binario de bsqueda construido al azar con menos de 50 millones de vertices probablemente tendr una altura Como podr intuir el lector,

razonable.

S es una funcin cuya complejidad es O(logN ),

sin embargo la demostracin de dicho resultado va mas all de todo lo que se pueda ver en este libro, y para sentirnos bien con nuestra conciencia, no utilizaremos ese resultado sino el teorema 20.

182

CAPTULO 14. RBOLES BINARIOS

14.8. Problemas
14.8.1. Ancho de un Arbol
Tiempo Lmite: 1 segundo

Descripcion
Supon que deseas dibujar un arbol binario en una cuadricula cuyas columnas estan numeradas de acuerdo a las siguientes reglas: Todos los nodos en un mismo nivel deberan estar en la misma la Cada columna de la cuadricula puede tener solamente un nodo Los nodos en el subarbol izquierdo de un nodo deberan ser dibujados en una columna a la izquierda del mismo, al igual los nodos del subarbol derecho deberan ser dibujados en columnas a la derecha del mismo Al dibujar el arbol no debe quedar ninguna columna sin nodo entre la columna mas a la izquierda y mas a la derecha del dibujo. El ancho de un nivel se puede obtener restando el numero de la columna derecha menos la columna izquierda del mismo mas uno. La raiz del arbol se considera el nivel 1. La siguiente gura muestra un arbol binario dibujado de acuerdo a la siguiente regla. El ancho del primer nivel es uno mientras que el ancho del segundo es 13, el tercer, cuarto, quindo y sexto nivel tienen los anchos 18, 18, 13 y 12 respectivamente.

Escribe un programa que al dibujar un arbol de esta forma calcule cual es el nivel mas ancho del arbol, si dos niveles tienen el ancho maximo, como en el caso del ejemplo el nivel 3 y el 4, entonces debes tomar el nivel con menor numero.

14.8. PROBLEMAS
Entrada

183

Tu programa debera leer del teclado los siguientes datos, la primera linea contendra un numero arbol. Cada una de las siguientes

entre 1 y 1,000 que indica el numero de nodos del

lineas contiene 3 enteros, denotando 3 nodos,

donde el primer numero indica un nodo, el segundo y tercer numeros de la linea indican el nodo izquierdo y derecho del nodo respectivamente. Cada nodo esta numerado del 1 al

N.

Si hay un nodo que no tenga hijos,

entonces su hijo tendra el numero -1. El nodo raiz tiene el numero 1.

Salida
Tu programa debera escribir a la pantalla dos numeros en una linea separados por un espacio, el primer numero indica el nivel con el ancho maximo, mientras que el segundo numero indica el ancho del nivel. Si hay mas de un nivel con el ancho maximo imprime el nivel de menor numero.

Ejemplo

Entrada
19 1 2 3 2 4 5 3 6 7 4 8 -1 5 9 10 6 11 12 7 13 -1 8 -1 -1 9 14 15 10 -1 -1 11 16 -1 12 -1 -1 13 17 -1 14 -1 -1 15 18 -1 16 -1 -1 17 -1 19 18 -1 -1 19 -1 -1

184

CAPTULO 14. RBOLES BINARIOS

Salida
19 1 2 3 2 4 5 3 6 7 4 8 -1 5 9 10 6 11 12 7 13 -1 8 -1 -1 9 14 15 10 -1 -1 11 16 -1 12 -1 -1 13 17 -1 14 -1 -1 15 18 -1 16 -1 -1 17 -1 19 18 -1 -1 19 -1 -1
Limites

siempre estara entre 1 y 1,000

Referencias
Este problema apareci en el concurso VU atrankinis turas 2005 m. ACM ICPC var?yboms Posteriormente fue utilizado en los preselectivos 2004-2005, que fueron organizados donde Cesar Arturo Cepeda Garca y Francisco Javier Zaragoza Martnez se encargaron de elegir y traducir los problemas.

14.8. PROBLEMAS
14.8.2. Cuenta rboles

185

Tiempo Lmite: 1 segundo

Problema
Escribe un programa que dado pueden formar con exactamente

N,

determine cuantos rboles binarios se

nodos.

Entrada
Una lnea con un nico nmero:

N.

Salida
La primera y nica lnea deber contener un solo entero: el nmero de rboles que se pueden formar mod

1000000.

Entrada de Ejemplo
3

Salida de Ejemplo
5

Consideraciones

1 N 1000
Referencias
Este es un problema clsico de combinatoria de conteo, esta redaccin fue utilizada por primera vez para un examen preselectivo de la Olimpiada Mexicana de Informtica a nales del 2005.

186

CAPTULO 14. RBOLES BINARIOS

Cap tulo

15
O(log N ).

Montculos
Ya vimos en el captulo anterior lo tiles que son los rboles binarios de bsqueda, y que para la mayora de conjuntos de datos los podemos usar como si soportaran insercin y bsqueda de elementos en tiempo

Sin embargo, estamos conscientes de que eso no es suciente, ya que puede suceder que para un conjunto de entradas en especco tanto la insercin como la bsqueda de un elemento tomen tiempo

O(n)

Dichos casos no son difciles de encontrar. Basta con dar como entrada algo que ya est ordenado!, cosa que no sera muy raro en la prctica que sucediera. Incluso los rboles binarios de bsqueda pueden resultar muy lentos si la entrada est

casi ordenada.

En este captulo nos vamos a concentrar en una estructura de datos que resuelve parcialmente el problema: los montculos. El montculo es una estructura de datos que soporta slamente 3 operaciones: conocer el mayor elemento que tiene guardado, agregar un nuevo elemento y eliminar el mayor elemento. Puede sonar limitante el hecho de que no se puede saber si un elemento est o no est dentro de los datos guardados y muchas veces s lo es, pero hay algunos problemas donde lo nico que se requiere es saber cul es el elemento mayor, o en otras ocaciones se requiere conocer el menor; por tanto los montculos a veces funcionan mejor que los rboles binarios de bsqueda pero no constituyen un reemplazo.

15.1. Qu son los Montculos?


Los montculos tambin son rboles binarios, pero no son rboles binarios de bsqueda.

187

188

CAPTULO 15. MONTCULOS


Para poder explicar de manera precisa lo que son los montculos debemos

denir otra cosa, la cual llamaremos posicin horizontal de un nodo, supercialmente hablando la posicin horizontal de un nodo es cuntos nodos de su mismo nivel(misma distancia a la raz) se pueden dibujar a la izquierda de l. Como en el primer nivel hay un solo nodo, que es la raz, diremos que su posicin horizontal es 0. Si la posicin horizontal de un hijo izquierdo es posicin horizontal del hijo derecho claramente debe de ser Adems si la posicin horizontal de un nodo es a su izquierda, por lo cual se van a dibujar

la

k + 1. p signica que hay p nodos 2p


y

2p

nodos a la izquierda de sus

hijos y por ende la posicin horizontal de sus hijos es zontal de un nodo:

2p + 1.

Con esto ya tenemos sucientes elementos para denir la posicin hori-

Denicin 15.1.1 (Posicion Horizontal). La posicin horizontal en un rbol


A
es una funcin Si Si Si

p:AZ

tal que para cada vrtice

v A:

v v v

es la raz entonces

p(v ) = 0. w
es su padre entonces

es un hijo izquierdo y es un hijo derecho y

p(v ) = 2p(w)

es su padre entonces

p(v ) = 2p(w) + 1.

Volviendo al tema de los montculos, la idea de un montculo es que cada nodo tiene una clave mayor a la de cualquiera de sus hijos y adems son rboles muy equilibrados, de manera que todos los niveles del rbol estn llenos excepto tal vez el ltimo. Y el ltimo nivel tiene la caracterstica de que todos sus nodos estan

lo mas a la izquierda posible.

Dicho de una manera mas precisa:

Denicin 15.1.2

(Montculo)

Un montculo

es un rbol binario que

cumple con las siguientes propiedades: La clave de todo nodo de sus hijos. Para todo entero no negativo

v A es mayor o igual que la clave de cualquiera

n se cumple una de estas dos propiedades: el nmero de nodos que estan a distancia n de la raiz es exactamente 2n no hay nodos cuya distancia a la raz sea mayor que n. v,
si su posicin horizontal es

Para cualquier nodo al menos

entonces existen

p+1

nodos que se encuentran en el mismo nivel que

v.

15.2. REPRESENTACIN EN MEMORIA

189

Figura 15.1: Dibujo de un montculo

La primera condicin para que un rbol binario sea considerado montculo tiene el propsito de que la raz tenga siempre la mayor clave. El propsito de la segunda propiedad es que sea un rbol completamente equilibrado. Y la tercera propiedad simplemente es una manera formal de decir que los nodos del ltimo nivel se encuentran

lo mas a la izquierda posible.

La gura 15.1 muestra un dibujo de un montculo, como se puede observar, aunque la clave de cada nodo es mayor o igual que las claves de sus hijos, puede darse el caso de que un nodo de un nivel inferior tenga mayor clave que un nodo de un nivel superior. Como el lector puede imaginar, la propiedad de ser un rbol muy equilibrado es lo que garantiza eciencia en un montculo, sin embargo an no hemos visto cmo implementar el montculo ni tampoco cmo implementar las operaciones de quitar el mayor elemento e insertar un nuevo elemento. La buena noticia es que estas implementaciones son mas simples de lo que se esperan.

15.2. Representacin en Memoria


Podramos vernos tentados a implementar un montculo de la misma manera que cualquier otro rbol binario, pero las 2 ltimas condiciones que debe cumplir un rbol binario para ser montculo nos sern de especial utilidad. La idea es guardar las claves de los nodos en un arreglo de manera que primero aparezcan las claves que estan mas arriba y luego aparezcan las claves que estan mas abajo, y adems las claves del mismo nivel esten ordenadas de izquierda a derecha. Por ejemplo, el montculo de la gura 15.1 se puede representar como

(8, 5, 1, 3, 2). Una ventaja de dicha representacin es que no se requiere memoria para guardar los enlances de hijo izquierdo e hijo derecho.

190

CAPTULO 15. MONTCULOS

Figura 15.2: ndices de Montculo que corresponden a cada nodo

La gura 15.2 muestra los ndices de montculo de cada nodo cuando se tiene un montculo de 7 elementos. Nuestro propsito ser ahora entender cmo se relaciona esta representacin esbozada con la estructura del rbol. Si un montculo tiene n + 1 niveles, por el teorema 20 entonces hay un n+1 total de 2 1 nodos en los primeros n niveles, por lo que dichos 2n+1 1 nodos aparecern primero en el arreglo, el resto de los nodos todos estan en un mismo nivel, y estn ordenados de izquierda a derecha, es decir, por su posicin horizontal. As que vamos a denir la posicin en el arreglo de la siguiente manera:

Denicin 15.2.1 (ndice de Montculo).


un nodo cualquiera, sea

Sea

su distancia a la raz, y d Entonces el ndice de montculo de v es 2 1

A un rbol binario, sea v A h su posicin horizontal. + h.

Puede parecer extrao que esta propiedad de ndice de montculo se haya denido para cualquier rbol binario y no slo para los montculos, existen varios motivos, uno es que resulta til para implementar cualquier rbol binario que se sabe de antemano que est equilibrado, el otro lo revisaremos al ver cmo realizar operaciones en un montculo. Como ya vimos, el ndice de montculo corresponde a la posicin del arreglo en el cual est ubicado un nodo, sabiendo eso es obvio que un montculo con

nodos va a requerir exactamente

posiciones del arreglo sin que haya

huecos. O dicho de otra manera:

Teorema 21. Sea A un montculo, y n el nmero de nodos de A, tenemos que para todo v A, el ndice del montculo de v es menor que n y no hay dos nodos con el mismo ndice de montculo; es decir, hay una biyeccin entre los nodos y los ndices de montculo menores que n.

15.2. REPRESENTACIN EN MEMORIA

191

El teorema anterior slo nos dice qu un ndice de montculo identica de manera nica cada uno de los nodos, sin embargo, probablemente necesitaremos saber dado el ndice de montculo de un nodo, cules son los ndices de montculo de su padre y sus hijos. El siguiente teorema nos da una respuesta increblemente sencilla a la pregunta anterior:

hijos y un padre, entonces se cumple lo siguiente:

Teorema 22. Sea A un montculo, y v el ndice de un nodo que tiene dos


El ndice de montculo de los hijos es 2v + 1 y 2v + 2 para hijo izquierdo y derecho respectivamente. El ndice de montculo de el padre es Demostracin.
tculo Sea

v 1 2

h la posicin horizontal del nodo que tiene ndice de mon2k 1 + h = v , tenemos que su hijo izquierdo tendr una posicin horizontal de 2h y estar a distancia k + 1 k+1 de la raz, por lo tanto la posicin horizontal del hijo izquierdo es 2 1+2h k y notamos que esto es igual a 2(2 1 + h) + 1. v
y sea

su distancia a la raz. tal que

Dado que la posicin horizontal del hijo derecho es igual a la posicin horizontal del hijo izquierdo ms uno, tenemos que el ndice de montculo del hijo derecho es Sea

2v + 1 + 1 = 2v + 2.

m el ndice de montculo de un nodo que no es la raz y sea p el ndice de montculo de su padre, tenemos que m = 2p + 1 o bien m = 2p + 2, en m1 . ambos casos p = 2
Con esta informacin ya podemos

movernos libremente en el rbol usando

solamente los ndices del montculo. Y para guardar un montculo en memoria solamente necesitaremos un arreglo y una variable. 1 2

int int

M o n t i c u l o [T A M A O _ M A X I M O] ; N=0;

Aqu el arreglo a cada ndice de

Monticulo guarda las claves de los nodos correspondientes montculo y N guarda el nmero de nodos del montculo. int.

No est por dems mencionar que un montculo puede guardar cualquier conjunto con un rden total, no es necesario que sea de tipo

Sin embargo, esta implementacin nos resultar intil hasta averigar como insertar y borrar elementos.

192

CAPTULO 15. MONTCULOS

15.3. Insercin
Para insertar un elemento en el montculo es necesario que temporalmente este rbol binario deje de ser montculo. La idea es colocar la clave del nuevo elemento en el primer lugar disponible(de izquierda a derecha) del ltimo nivel, y si el ltimo nivel est lleno, colocar la clave del nuevo elemento en el primer nodo de un nuevo nivel. Esto se puede hacer simplemente colocando la clave en posteriormente incrementando

N,

Monticulo[N]

una vez hecho esto, nuestro rbol binario

podra dejar temporalmente de ser montculo. A continuacin hay que intercambiar la clave que se acaba de insertar con la clave del padre y repetir esta operacin hasta que el rbol binario vuelva a ser montculo, es decir, hay que

subir

la clave insertada mientras se encuentre

en un nodo cuyo su padre tenga una clave menor. Este algoritmo se puede implementar con el siguiente cdigo: 1 2 3 4 5 6 7 8 9 10 11 } }

void

int

insertar ( i , k;

int

Monticulo [ ] ,

int

clave ){

i= N++;

while ( i >0

M o n t i c u l o [ i ]= c l a v e ; && M o n t i c u l o [ ( i

1) /2] < M o n t i c u l o [
i ];

i ]){

k=M o n t i c u l o [ ( i Monticulo [ ( i i =( i

1) / 2 ] ;

1) / 2 ] = M o n t i c u l o [

M o n t i c u l o [ i ]= k ;

1) / 2 ;

15.4. Eliminacin
Como se haba dicho anteriormente, no se puede eliminar cualquier elemento del montculo, sino que en lugar de eso solamente se puede eliminar el mayor elemento. Para eliminar la mayor clave se puede seguir un procedimiento parecido a insertar una nueva; la idea es quitar la clave que est en la raz, luego mover la clave que est hasta el nal del montculo(ltimo nivel

lo ms a la derecha

posible) colocndola en la raz. Y nalmente intercambiar esta clave con la clave del mayor de los hijos del nodo donde se encuentre hasta que la clave sea mayor que las claves de todos los hijos. Al igual que la insercin, la eliminacin tambin es un procedimiento muy sencillo de implementar:

15.4. ELIMINACIN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 } } } } } }

193

int

int

quitar ( i ,

int
k,

Monticulo [ ] ) { r;

r=M o n t i c u l o [ 0 ] ; N; Monticulo [ 0 ] = Monticulo [N ] ; i =0;

while ( 2 i +1<N) { i f ( 2 i +2<N &&


+1]) {

M o n t i c u l o [ 2 i +2]> M o n t i c u l o [ 2 i

else {

k=2 i +2;

k=2 i +1;

i f ( M o n t i c u l o [ k]> M o n t i c u l o [ i ] ) {
i =k ; k=M o n t i c u l o [ ( i Monticulo [ ( i

1) / 2 ] ;
i ];

1) / 2 ] = M o n t i c u l o [

else {

M o n t i c u l o [ i ]= k ;

break ;

return

r;

194

CAPTULO 15. MONTCULOS

Cap tulo

16
grafos.

Grafos
Muchos problemas, nos presentan algun conjunto, en el cual algunos pares de elementos de dicho conjunto se relacionan entre s. Ese tipo de problemas son muy estudiados en la teora de grcas o teora de Estrictamente hablando, estas estructuras se llaman grcas, sin embargo la palabra puede generar confusin con la grca de una funcin. Por tal motivo en este libro nos referiremos a estas estructuras como

grafos.

El primer ejemplo de grafos se utilizar en algo no muy relacionado con la programacin, esto se hace para evitar que el lector se vea tentado a confundir el concepto de grafo con el de la implementacin de un grafo; pues hay problemas donde es necesario utilizar grafos en el razonamiento y no en la implementacin.

Problema Resuelto 16.0.1. En una reunin hay n invitados, se asume que


si un invitado

a conoce a un invitado b, b tambien conoce a a, demuestra que

en la reunin hay al menos 2 invitados que conocen al mismo nmero de invitados. Asmase tambien que todos conocen por lo menos a alguien de la reunin.

Solucin

En el ejemplo anterior, tenemos un conjunto de

personas, y

como vemos, algunos pares de personas se conocen entre s. Para resolver este tipo de problemas es muy til usar grafos, tambien llamados gracas por algunos, sin embargo, aqu se utilizar el trmino grafo, para evitar que se confunda con el concepto de la grca de una funcin. Una forma un tanto pobre de denir un grafo(poco despus trataremos la denicin formal) es:

Denicin 16.0.1

(Denicin Provisional de Grafo)

Conjunto de puntos

195

196

CAPTULO 16. GRAFOS

5 1

2
Figura 16.1: Dibujo de un grafo

llamados vrtices, en el cual algunos vertices pueden estar unidos por lneas llamadas aristas Volviendo al ejemplo, es posible dibujar un punto por cada persona de la reunin, y entre cada par de personas que se conozca entre s, colocar una lnea. Por ejemplo si 6 conoce a 4, 4 conoce a 5, 5 conoce a 1, 5 conoce a 2, 2 conoce a 1, 3 conoce a 2 y 4 conoce a 3, se podra dibujar un grafo similar al de la gura 16 Ahora, una vez que tenemos visualizada de cierta manera la solucin, podemos ver que cada nodo puede estar conectado con al menos otro nodo(cada invitado conoce por lo menos a otro invitado) y a lo mas nodos. Es decir, el nmero de conocidos de cada persona va desde 1 hasta

n1 n 1.

Como cada persona puede tener n 1 nmeros distintos de conocidos y hay n personas, por principio de las casillas al menos dos personas debern tener el mismo nmero de conocidos.

16.1. Qu son los grafos?


Por si alguien no ley la solucin del ejemplo. Hay que mencionar que un grafo puede imaginarse como un conjunto de puntos llamados vrtices, en el cual algunos vertices pueden estar unidos por lneas llamadas aristas. Los grafos son muy tiles en problemas que involucran estructuras tales como carreteras, circuitos electricos, tuberas de agua y redes entre otras

16.1. QU SON LOS GRAFOS?

197

B D

Figura 16.2: Dos dibujos de un mismo grafo conocido como

K4

cosas, y como lo acabamos de ver, con los grafos incluso se pueden representar las relaciones en una sociedad. Si eres observador, tal vez ya te diste cuenta que tanto las listas enlazadas como los rboles son grafos. Hay que hacer notar que no importa cmo se dibujen los grafos, si las aristas estan curvas o rectas, o si los vertices estan en diferente posicin no es relevante, lo unico que importa qu pares de vertices estan unidos. Por ejemplo, la gura 16.1 muestra dos maneras distintas de dibujar un mismo grafo. Es decir, un grafo, mas que un conjunto de puntos unidos por lneas, es un conjunto de objetos en el cual algunos pares de estos se relacionan entre s, mas formalmente:

Denicin 16.1.1
de

(Grafo)

Par ordenado

G = (V, A),

donde los elementos

son llamados vertices, y

es un conjunto de pares no ordenados de

vrtices llamadas aristas. Aunque hay quienes se dedican a investigar grafos innitos, en este libro nos dedicaremos exclusivamente a grafos nitos. De esa forma, el grafo que se dibuja arriba se puede expresar como:

G = ({A, B, C, D}, {{A, B }, {A, D}, {D, B }, {D, C }, {B, C }, {A, C }})
Esto no quiere decir que para manejar grafos hay que olvidarse completamente de dibujarlos como puntos unidos por lneas, ya que en un dibujo

198

CAPTULO 16. GRAFOS

a simple vista se pueden distinguir detalles del grafo que no seran tan facil distinguirlos sin el dibujo. Antes de proseguir es conveniente ver algunas deniciones relacionadas con los grafos. La primera que veremos ser la de con l, es decir:

grado,

valencia,

al hablar del grado

de un vrtice, nos referimos al nmero de extremos de arista conectados

Denicin 16.1.2 (Grado). Para un grafo G = (V, A) el grado de un vertice


vV
es el nmero de aristas de la forma dos veces el nmero de aristas de la forma El grado de

{v, w} A {v, v } A.

tales que

v=w

ms

se denota como

(v ).

Por ejemplo, en la gura 16 el nodo etiquetado como 1 tiene grado 2, y el nodo etiquetado como 4 tiene grado 3, mientras que en la gura 16.1 todos los nodos tienen grado 3. Un grafo dirigido(o digrafo) informalmente hablando es un grafo en el cual las aristas tienen direccin, es decir una arista entre una arista entre

vi

vj

no implica

vj

vi .

La nica diferencia es que en lugar de que las aristas sean conjuntos de uno o dos elementos, las aristas sern pares ordenados.

Denicin 16.1.3 (Digrafo).


digrafos son nitos.

Par ordenado

G = (V, A)

donde

AV V.

Nuevamente cabe aclarar que en este libro asumiremos tambin que los

16.2. Propiedades Elementales de los Grafos


Los grafos tambin tienen muchas propiedades interesantes, la primera de ellas se demostr en el ejemplo 16.0.1, por lo cual solamente la vamos a enunciar con propsitos de referencia:

Teorema 23. Sea G = (V, A) un grafo tal que {a, a} / A para toda a

V (no hay aristas que unan a un vrtice consigo mismo), entonces existen dos vrtices con el mismo grado.
La siguiente propiedad tambin tiene que ver con los grados de los vrtices un grafo:

Teorema 24. Sea G = (V, A) un grafo se tiene que la suma de los grados
de todos los vrtices en V es exactamente 2|A|.

16.3. IMPLEMENTACIN

199

Demostracin.
doble.

El grado de un vrtice es el nmero de aristas incidentes a

l, donde las aristas cuyo origen y destino son el mismo vrtice se cuentan

Como cada arista est conectada a lo ms con dos vrtices entonces, si est conectada con dos vrtices aporta la cantidad de 1 al grado de cada vrtice, aportando en total 2 a la suma de todos los grados; si est conectada con un solo vrtice, entonces aporta 2 al grado de ese vrtice y por tanto 2 a la suma de todos los grados. Por lo tanto la suma de todos los grados es el doble del nmero de aristas(2|A|).

Teorema 25. Sea G = (V, A) un grafo, el nmero de vrtices de grado impar


es par. Demostracin.
entonces Usando el teorema anterior, sea

z = 2|A|, adems notamos que V = {v1 , ..., vn }. Como z es par entonces solamente puede ser obtenida como suma de una
de grado impar es par.

z la suma de los grados de G z = (v1 ) + (v2 ) + ... + (vn ) donde

cantidad par de impares y 0 o ms pares, por lo tanto, el nmero de vrtices

16.3. Implementacin
Luego de haber visto la naturaleza de los grafos, seguramente estas pensando en cmo implementar una estructura de datos similar a un grafo. Hay muchas maneras de implementar grafos, pero solo se tratarn las 2 mas usadas: matriz de adyacencia y listas de adyacencia.

Matriz de Adyacencia
Esta es quiz la forma mas comn de representar un grafo en un lengua2 je de programacin debido a su sencillez, sin embargo, requiere |V | de memoria, y en la mayora de los casos es un poco lenta. Un grafo

|V | |V |, vi y vj .

G = (V, A) puede ser representado como una matriz M de donde Mi,j = 0 si existe una arista en A que une a los nodos

Por ejemplo, el grafo de la Figura G1 puede ser representado como:

200

CAPTULO 16. GRAFOS

M = { 010010 101000 010100 001010 110100 000100 }


La implementacion de un grafo por matriz de adyacencia es bastante simple: 1

int

m[ n m e r o _ d e _ v e r t i c e s ] [ n m e r o _ d e _ v e r t i c e s ] ;

Listas De Adyacencia Consisten en vertice

|V | listas enlazadas, es decir, una lista enlazada para cada v V , dicha lista consta de los nodos que estan unidos a v

por medio de alguna arista. La ventaja fundamental de las listas de adyacencia sobre la matriz de adyacencia es que las listas de adyacencia solo requieren

|A| + |V |

de memoria.

Sin embargo, la representacin de un grafo por listas de adyacencia es un poco mas compleja y muchos se ven tentados a usar memoria dinamica para estos casos. Nuevamente se presentar una implementacin con memoria esttica por los motivos que se mencionan en el prefacio. Si tenemos un grafo de 100 000 nodos, y a lo mas un milln de aristas, es obvio que no podemos crear 100 mil listas reservando espacio para 100 mil nodos en cada una, ya que requerira una gran cantidad de memoria y es muy probable que carezca de ella la computadora donde se vaya a ejecutar el programa. La solucin para esto, es guardar los datos de todas las listas en un solo arreglo, y guardar cual es el primer elemento de cada lista en otro arreglo. Adems, ser necesario tener una variable que indique el nmero de aristas de la lista. En el siguiente cdigo ilustra cmo implementar un grafo con a lo mas 100 000 nodos y a lo mas un milln de aristas:

16.4. RECORRIDOS EN GRAFOS


1 2 3 4

201

int int int

la arista la l i s t a

dato [ 1 0 0 0 0 0 1 ] ;

// El nodo h a c i a e l c u a l apunta // El s i g u i e n t e elemento de

proximo [ 1 0 0 0 0 0 1 ] ;

p r i m e r o [ 1 0 0 0 0 1 ] ; // El primer elemento de l a l i s t a de cada nodo int a r i s t a s =1; // El nmero de a r i s t a s Aqu se declar como una arista inicialmente para dejar libre el 0 para expresar el elemento vaco que indica el nal de toda lista. El procedimiento de insertar una arista entre 2 nodos tambin es algo tedioso con listas de adyacencia comparandolo a lo simple que es con matriz de adyacencia, la siguiente funcin inserta una arista entre los nodos v y w en un grafo dirigido.

1 2 3 4 5

void

insertar_arista (

int

v,

int

w) {

d a t o [ a r i s t a s ]=w ; p r o x i m o [ a r i s t a s ]= p r i m e r o [ v ] ; p r i m e r o [ v ]= a r i s t a s ++; } Si se quiere que el grafo sea no dirigido simplemente hay que llamar a la funcin anterior 2 veces Como se ve en el cdigo, la arista siempre se inserta al principio de la lista de adyacencia, para que de esa forma, el conjunto de todas las aristas se inserte en tiempo lineal; ya que si se recorriera la lista de adyacencia cada que se quisiera insertar una nueva arista, insertar todo el conjunto de aristas se realizara en tiempo cuadratico, queda como tarea para el lector demostrar por qu.

16.4. Recorridos en Grafos


Ahora que ya conocemos los grafos y cmo representarlos en una computadra comenzaremos con una de sus aplicaciones mas bsicas: los recorridos en grafos o bsquedas. La idea de recorrer un grafo se desprende de imaginar los nodos como cuartos y las aristas como pasillos que conectan cuartos, y el recorrido es visitar caminando todos los cuartos. A estos recorridos les llamaremos caminos, los cuales se formalizan mediante la siguiente denicin:

202

CAPTULO 16. GRAFOS


Un camino

Denicin 16.4.1 (Camino).


(v1 , v2 , v3 , ..., vn ), vi con vi + 1.

tal que para toda

C es una sucesin de vertices C = 0 < i n existe una arista que conecta

Si es posible iniciar en un cuarto y visitar todos los dems entonces se dice que grafo es conexo. O dicho de una manera mas precisa:

Denicin 16.4.2
cada par de vertices

(Grafo conexo)

Un grafo

G = (V, A)

es conexo si para

v, w V

existe un camino que inicia en

y termina en

w.
Bsicamente las bsquedas sirven para visitar todos los vertices de un grafo conexo, o bien de un subgrafo conexo de manera sistemtica. Es decir, iniciamos en el vertice

A,

y queremos saber desde ah, a cules

vertices se puede llegar, y la manera de hacerlo es... Buscando!. El primer recorrido que veremos ser la busqueda en profundidad. La busqueda en profundidad es un recorrido de grafos de naturaleza recursiva(aunque se puede implementar de manera no recursiva). Recuerdas la antigua leyenda del minotauro? En caso de que no la recuerdes aqu va la parte que interesa: Haba un minotauro(un hombre con cabeza de toro) dentro de un laberinto, un valiente joven, llamado Teseo se dispuso a matar al minotauro(el minotauro mataba gente, asi que no tengas lastima del minotauro), pero para no perderse en el laberinto, tom una cuerda para ir marcando por donde ya haba pasado, y saber qu recorrido haba seguido, para as poder regresar. Luego cuando mat al minotauro regres siguiendo su rastro, como lo haba planeado, y el resto ya no tiene que ver con el tema. Qu moraleja nos deja esta historia? Basicamente que si nos encontramos en un laberinto(o en un grafo), es necesario ir marcando el camino por el que ya pasamos y saber por dnde venamos. La idea de la bsqueda en profundidad es dejar una marca en el nodo que se esta visitando; y despues visitar recursivamente los vecinos de dicho nodo que no hayan sido visitados. El siguiente codigo es una implementacion de la bsqueda en profundidad en un grafo con 1 2 3 4 5

nodos y con matrz de adyacencia nodo ) {

g:

void

visitar (

int

for ( i =1; i <=N ; i ++) i f ( g [ nodo ] [ i ] ! = 0

v i s i t a d o [ nodo ] = 1 ;

&& v i s i t a d o [ i ]==0) {

visitar ( i ) ;

16.4. RECORRIDOS EN GRAFOS


6 7 } }

203

G = (V, A), la bsqueda en profun2 didad funciona con matriz de adyacencia en O (|V | ) mientras que con listas de adyacencia funciona en O (|V | + |A|).
Hay que mencionar que en un grafo El otro recorrido que se va a tratar aqu es la bsqueda en amplitud(algunos lo llaman bsqueda a lo ancho). Lamentablemente la bsqueda en amplitud no fue idea de Teseo, y como te imaginars, es un poco mas difcil que a alguien se le ocurra hacerla si no la conoce de antemano. Para comenzar a adentrarnos en la bsqueda en amplitud es conveniente que imagines la siguiente situacin: Eres miembro de una banda de bndalos(valga la redundancia), y la banda tiene como centro de reunin una esquina de la ciudad. Los miembros de la banda quieren que toda la ciudad sepa a cuntas cuadras se encuentran en cada momento de su terrirtorio, asi que deciden dejar pintados mensajes en la paredes de cada esquina de la ciudad diciendo a cuntas cuadras de encuentran del territorio prohibido. Ahora, el jefe de la banda te ha encargado a ti determinar qu nmero se debe de pintar en cada esquina de la ciudad. La tarea no es facil, ya que si haces rodeos, podras llegar a creer que estas mas lejos de lo que realmente te encuentras, y la ciudad no es muy rectangular que digamos. Conviene al lector detenerse unos momentos a reexionar sobre este problema y pensar en una posible solucin antes de leerla. Lo que deberas hacer en ese caso, es pintar una advertencia Alejate, este es nuestro territorio en la esquina donde la banda tiene su centro de reunin, luego caminar una cuadra en cualquier direccin y pintar algo asi como estas a una cuadra de territorio prohibido, despus regresar al punto de encuentro y caminar una cuadra en otra direccin y otra vez pintar lo mismo, repitiendo esa operacin hasta que ya se haya caminado una cuadra en todas las direcciones. Despus, para cada esquina donde hayas escrito estas a una cuadra de terrirtorio prohibido, caminas una cuadra en todas las direcciones y escribes estas a dos cuadras de territorio prohibido(claro, si no habas pintado nada ah anteriormente). Y continas as hasta haber pintado toda la ciudad o hasta ser atrapado por la polica. Es claro que el divertido ejemplo anterior solo pretende ensear la idea de la bsqueda en amplitud y no se espera que el lector realize tales actos.

204

CAPTULO 16. GRAFOS


La idea de la bsqueda en amplitud es visitar un nodo inicial

v,

y luego

visitar los nodos que se encuentren a una arista de encuentren a 2 aristas de

v,

despus los que se

y asi sucesivamente.

De esa forma, la bsqueda en amplitud visita todos los nodos que pueden ser alcanzados desde nmero de aristas)

v , y para cada uno de ellos, calcula su distancia(mnimo con v .

En la situacin que imaginamos del bndalo pintando paredes, el bndalo tena que caminar mucho despues de pintar cada esquina, sin embargo, como nosotros usaremos una computadora para realizar un proceso similar, no necesitamos estar caminando, o recorriendo todo el grafo en busca del siguiente vertice que hay que marcar. Una forma mas rpida de obtener los mismos resultados es teniendo una cola, y metiendo a

en la cola, luego. mientras la cola no este vaca, sacar

un vertice de la cola, visitarlo, y meter a los vecinos no visitados a la cola. Una manera de hacerlo(asumiendo que la cola nunca se llenar) es la siguiente:

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

c o l a [ f i n ++]=v ; v i s i t a d o [ v ]=1; d i s t a n c i a [ v ]=0;

// Meter v a l a c o l a // I n i c i a l i z a r

while ( i n i c i o != f i n ) { // Mientras l a c o l a no e s t e
vaca
a=c o l a [ i n i c i o ++];

for ( i =1; i <=N ; i ++) // Meter v e c i n o s a l a


cola

// Sacar de l a c o l a

if (g [ a ] [ i ]

&& v i s i t a d o [ i ]==0) {

v i s i t a d o [ i ]=1; d i s t a n c i a [ i ]= d i s t a n c i a [ a ]+1; c o l a [ f i n ++]= i ; } }

El cdigo anterior usa una matriz de adyacencia Como nodo se visita una vez y cada arista se visita

g,

con

nodos.

1 o 2 veces dependiendo

de si el grafo es dirigido o no dirigido, este algoritmo funciona en tiempo O(|V |2 ) con matriz de adyacencia y con listas de adyacencia funciona en tiempo

O(|V | + |A|).

16.5. CONECTIVIDAD

205

16.5. Conectividad
Al inicio de la seccin anterior se deni que era un grafo conexo y se vi como recorrer un grafo conexo. Sin embargo no hemos analizado muy a fondo todo lo que esto implica.

Denicin 16.5.1

(Conectividad)

Decimos que dos vrtices

conectados si existe un camino que los una. Y lo denotaremos

v, w como v

estan

Por facilidad vamos a pensar que todo vrtice est conectado consigo mismo, hay algunos problemas donde no es prudente pensar as, pero en general es mucho mas manejable la conectividad si pensamos de esta manera. Esta primera propiedad la llamaremos tambin

reexiva.

Hay que notar que para todo par de vrtices

tales que

v w

w
con

v;

eso es fcil de ver ya que solamente hay que recorrer el

camino que une a

al revs para encontrar el camino que une a

v.
y

Esta propiedad la llamaremos

simtrica.
u

Otra propiedad interesante de la conectividad es que para 3 vrtices

u, v

transitiva.

w,

si

entonces

w.

Esta propiedad la llamaremos

Una vez observadas estas 3 propiedades vamos a denir algo que llamaremos compontente conexa.

Denicin 16.5.2 (Componente Conexa). Sea G = (V, A) un grafo y v V ,


la componente conexa de que

es el conjunto de todos los vrtices

uV

tales

y se denota como

[v ].

Esta denicin tiene una ntima relacin con las 3 propiedades que acabamos de enunciar. Como veremos a continuacin con este teorema:

Teorema 26. Sea G = (V, A) un grafo y sean u, v V dos vrtices cualesquiera. Tenemos que u Demostracin.
v se cumple s y solo s [u] = [v ]. u v,
sea

x [u] y sea y [v ]. Sabemos por denicin que v y y que u x. Por la propiedad transitiva u y , por lo tanto y [u]. Por la propiedad simtrica v u y por la transitiva v x por lo tanto x [v ]. Es decir todo elemento de [u] tambin est en [v ] y todo elemento de [v ] tambin est en [u] por lo tanto [u] = [v ]. Ahora nos vamos a olvidar de que u y v estn conectados y vamos a suponer solamente que [u] = [v ]. Por la propiedad reexiva u [u] pero como [u] = [v ] entonces u [v ] es decir v u y por la propiedad simtrica u v.
Supongamos que

206

CAPTULO 16. GRAFOS

Teorema 27. Sea G = (V, A) y sean [u], [v] dos componentes conexas, tenemos que se cumple una de estas dos cosas:
[u] = [v ] o bien [u] [v ] =

Demostracin.

u, v V vrtices tales que u [u] y v [v ], tenemos que si u v entonces [u] = [v ]. En caso contrario, para todo x [v ] tenemos que u no est conectado con x dado que x v , y anlogamente para todo y [u] tenemos que v no est conectado con y . Es decir, [u] y [v ] no tienen
Sean elementos en comn. Una vez vistos estos dos teoremas podemos apreciar como el problema

de determinar la conectividad entre todos los pares de vrtices se reduce a particionar el grafo en varias componentes conexas. Una vez que se tiene las componentes conexas, para saber si dos vrtices estan conectados solamente hay que ver si pertenecen a la misma componente conexa. Adems, para encontrar todas las componentes conexas lo nico que hay que hacer es recorrer cada una de ellas una sola vez. Eso es relacin tiene el nombre de

O(|V | + |A|)!.

En general esta forma de particionar un conjunto con respecto a una

clases de equivalencia

y se puede hacer con

cualquier relacin que sea reexiva, simtrica y transitiva (por ejemplo, la relacin del paralelismo entre rectas en el plano). Observa nuevamente las demostraciones de los dos teoremas y te dars cuenta que solamente se usaron las 3 propiedades enunciadas y no se us en absoluto el hecho de que se estuviera hablando de un grafo.

16.6. rboles
Ya anteriormente se mencionaron los rboles binarios como estructura de datos, sin embargo, la denicin que se di se aleja mucho de lo que es un rbol. Antes de poder entender los rboles necesitamos denir los ciclos, un ciclo es un camino que inicia y termina en el mismo vrtice, es decir:

realmente

Denicin 16.6.1
donde

(Ciclo)

Un ciclo es un camino

C = (v1 , v2 , v3 , ..., vn )

v1 = vn .
y en la cual cada

ramicaciones,

La idea emprica de los rboles se basa en tener un grafo compuesto por

ramicacin

se puede dividirse en otras

16.6. RBOLES

207

ramicaciones
ramas.

y as sucesivamente, tal como si se tuviera un tronco del cual

se desprenden ramas, y luego cada rama puede subdividirse a la vez en otras Esta idea de los rboles se formaliza con la siguiente denicin:

Denicin 16.6.2
Es conexo

(rbol)

Se dice que un grafo

G = (V, A)

es un rbol si

cumple con las siguientes dos condiciones:

No contiene ciclos Los rboles adems aparecen de manera natural en muchas situaciones, como se demuestra con el siguiente par de teoremas:

Teorema 28. Sea G = (V, A) un grafo conexo, las siguientes tres proposiciones son equivalentes:
G es un rbol.

Entre cada par de vrtices existe un solo camino que no repite vrtices que los une.
|A| = |V | 1

Demostracin.

G es un rbol, al ser un rbol(y por v y w existe al menos un camino que los une, vamos a denotarlo como A = (a1 , ..., an ) donde a1 = v y an = w .
Primero supongamos que tanto conexo) sabemos que entre cada par de vrtices Ahora, para probar que este camino es nico vamos a suponer que existe otro, el cual denotaremos como Como

tal que

B = (b1 , ..., bm ) con b1 = v y bm = w. A y B no son el mismo camino entonces existe al menos un ak = bk , tambin hay que notar que a1 = b1 y an = bm .

entero

comn,

A lo que se intenta llegar con esto es que los dos caminos tienen un

prejo

el cual sea tal vez de un solo nodo, y luego se vuelven a juntar en

otro nodo. Dicho de otra manera podemos hablar del mayor entero

b1 , a2 = b2 , ...ai = bi ,

en este caso decimos que

i tal que a1 = (a1 , ..., ai ) = (b1 , ..., bi ) es el

prejo que tienen en comn Tomando en cuenta que elemento de

A y B. an = b m

podemos estar seguros que al menos un

{ai+1 , an } ax ,

es igual a un elemento de

{bi+1 , bm },

ahora, de entre

todos esos elementos, vamos a elegir aquel que aparezca antes en a denominarlo elegir alguna

A, y vamos

adems, como por denicin aparece en ambos, vamos a

tal que

ax = b y .

208

CAPTULO 16. GRAFOS


Consideramos ahora el camino

(ai , ai+1 , ..., ax , by1 , by2 , ..., bi ) este camino G


es un rbol, entre cualquier par de

es un ciclo, por lo tanto un rbol no puede tener mas de un camino uniendo al mismo par de vrtices. Es decir, si vrtices existe un solo camino. Para seguir probano que las tres proposiciones son equivalentes, ahora nos vamosa olvidar de que que

G = (V, A)

es un rbol y solo vamos a suponer que

entre cada par de nodos existe un nico camino, e intentaremos demostrar

|A| = |V | 1.

Usaremos induccin, es tentador usar como caso base un grafo con un solo vrtice(en el cual es evidente que se cumple); sin embargo, resulta mas cmodo usar como caso base un grafo con dos vrtices. Si solamente hay dos vrtices, entonces solo puede haber una arista(existen estructuras llamadas multigrafos que soportan varias aristas entre los mismos dos vrtices, pero no estamos hablando de ellas), y entre ese nico par de vrtices hay un solo camino. Ahora, si se tiene un grafo con arista entre dos vrtices

vrtices(para

n2

),

n1

aristas, y

entre cada par de vrtices hay un solo camino, entonces, si se aade una nueva

w,

entonces habra dos caminos que conectan a

con

w,

uno sera el que ya se tena antes, y el otro sera un camino de una

sola arista

(v, w).

Aqu acabamos de demostrar una propiedad interesante, y es que si se aade una arista a un grafo donde entre cada par de vrtices hay un solo camino, entonces, luego de ello, existir al menos un par de vrtices conectados por mas de un camino. Continuado con la induccin, si le aadimos un vrtice al grafo, llam-

w, tenemos que para cualquier otro vrtice u V existe un nico camino que une a u con w , llammosle C , y por tanto existe un nico camino entre u y v , el cual consiste de C seguido de v .
mosle y lo conectamos con otro vrtice Por lo tanto, por induccin, si entre cada par de vrtices existe un solo camino que los une, entonces

|A| = |V | 1. |V | 1|
aristas, entonces el grafo es un rbol.

El tercer paso de nuestra prueba es demostrar que si un grafo conexo

G = (V, A)

tiene exactamente

Para demostrar eso vamos a recurrir a un concepto parecido al de rbol y es el de bosque, un bosque es un grafo sin ciclos, el cual puede o no ser conexo. Como se puede ver, un rbol es un bosque pero un bosque no necesariamente es un rbol. Un grafo con

vrtices y ningn arista es un bosque y tiene

nentes conexas, adems es el nico grafo de

n compon vrtices que tiene exactamente n


vrtices,

componentes conexas. Esto es un buen caso base para induccin! Supongamos que para un

k > 0

todo grafo con

aristas y

16.6. RBOLES

209

nk

componentes conexas es un bosque. Si a cualquiera de esos grafos le

aadiramos una arista entre dos vrtices de la misma componente conexa, obtendramos un grafo con conexas. Sin embargo, si aadimos una arista entre dos vrtices de dos componentes conexas distintas obtenemos un grafo con

vrtices,

k+1

aristas y

nk

componentes

n vrtices, k + 1 aristas y n k 1

componentes conexas. Este grafo ser un bosque, puesto que conectar dos componentes conexas distintas con una sola arista nunca crea un ciclo. Es decir, todo grafo con ser un bosque. Por induccin un grafo con

n vrtices, k +1 aristas y n k 1 componentes conexas n


vrtices,

n1

aristas ser un bosque con

n (n 1) = 1

componentes conexas, es decir, un rbol.

Observamos que la primera proposicin implica la segunda, la segunda implica la tercera y la tercera implica la primera, de esta manera queda demostrado que las tres proposiciones son equivalentes.

Un elemento muy interesante que poseen los rboles son las

hojas, en los

rboles binarios las hojas son aquellos vrtices que no tienen hijos, aqu son algo parecido, son aquellos vrtices que estan unidos solamente con vrtice, podemos pensar en ese vrtice como si fuera el

padre

de la hoja.

Por lo tanto nuestra denicin de hoja queda de la siguiente manera:

Denicin 16.6.3 (Hoja).

Una hoja es un vrtice con grado 1.

Las hojas son un elemento interesante ya que son el punto de partida para las soluciones de algunos problemas y adems siempre estn presentes en los rboles, o mas objetivamente:

Teorema 29. Sea G = (V, A) un rbol, si |V | 2 entonces G tiene al menos


2 hojas. Demostracin.
grados de Es evidente que no hay vrtices con grado 0, puesto que el

grafo no sera conexo y por tanto no sera un rbol. Como la suma de los grados de un grafo

2|A|

entonces la suma de los pero como esto no es

es

2|V | 2,

si todos los vrtices tuvieran grado mayor o igual a

2, entonces la suma de los grados sera al menos cierto, entonces existe un nodo de grado Si un solo nodo tuviera grado grado

2|V |,

1.

y el resto de los nodos tuviera al menos

2,

suma es

entonces la suma de los grados sera al menos 2|V | 1, como la 2|V | 2 entonces al menos 2 nodos tienen grado 1, es decir, el rbol

tiene al menos 2 hojas.

210

CAPTULO 16. GRAFOS


Ya vimos que todo grafo

G = (V, A)

con

|V | 1

aristas es un rbol, si

pensamos en un grafo conexo que tenga tantos vrtices como aristas inmediatamente se puede imaginar a un rbol con una arista adicional. Lo cual hace pensar que es un grafo con un solo ciclo. En este caso el sentido comn no nos traiciona, ya que realmente el grafo es un rbol con una arista adicional que posee un solo ciclo, sin embargo, an debemos de demostrarlo, pues debemos de considerar la posibilidad de que haya mas de un ciclo o de que el grafo deje de ser conexo si se quita alguna arista.

Teorema 30. Sea G = (V, A) un grafo conexo tal que |V | = |A| entonces G
contiene un solo ciclo. Demostracin.
cara Si G no contuviera ciclos entonces sera un rbol y eso impli|A| = |V | 1, lo cual es falso.

Ya demostramos que hay al menos un ciclo, ahora demostraremos que hay un solo ciclo, sea

C = (v1 , ..., vn )

un ciclo en por tanto

G,

tenemos que para

v1 , v2

existen al menos dos caminos que los unen por estar en un mismo ciclo. Pero ya sabemos que une a

{v1 , v2 } A

v1

con

v2 ,

ahora veremos que sucede si le

quitamos

(v1 , v2 )

es un camino que la arista al grafo,

es decir, consideraremos el grafo

H = (V, A {v1 , v2 }).

Tenemos que H es conexo, ya que an existe un camino que une a v1 con v2 , ya que para cada par de vrtices u, w V existe un camino que los une en G, si la arista {v1 , v2 } no est presente en el camino, entonces dicho camino tambin existe en H , y si est presente, entonces es posible reemplazarla por este camino (v2 , ..., vn , v1 ). Como el nmero de aristas en H es |A| 1 entonces H es un rbol y no tiene ciclos. Es decir, todos los ciclos en G tienen la arista {v1 , v2 }. Ahora, tomemos dos ciclos cualesquiera en H , si quitamos a {v1 , v2 }, luego de eso habr un nico camino entre v1 y v2 , el cual es (v2 , ..., vn , v1 ), y ambos ciclos unen a v1 y a v2 con 2 caminos incluyendo el camino de una sola arista, por lo tanto ambos ciclos pasan por ese camino y por la arista {v1 , v2 }, es decir, son el mismo ciclo. Los rboles estan ntimamente ligados a la conectividad, como se ve en la demostracin del teorema 28. A continuacin observaremos algunas propiedades de los rboles que los convierten en un objeto importante de estudio dentro de la conectividad. Como ya habamos visto, un rbol se dene como un grafo conexo sin ciclos, el hecho de que no tenga ciclos hace suponer que tiene pocas aristas, por ello, para encontrar un grafo conexo con pocas aristas, suena razonable buscar un rbol.

16.6. RBOLES

211

La propiedad que estudiaremos ahora nos dice que no solo es razonable buscar un rbol, sino que es lo mejor. Es decir, no hay un grafo conexo que tenga menos aristas que un rbol, dicha propiedad se resume en el siguiente teorema:

Teorema 31. Sea G = (V, A) un grafo tal que |A| < |V | 1 entonces, el
grafo no es conexo. Demostracin. Tomemos un grafo H0 = (V, ), es decir un grafo con los misV
pero sin aristas, mos vrtices que conexas. Vamos a denir

H0

tiene exactamente

|V |

componentes

Hn

como un subgrafo de

Supongamos que para cualquier entero

G con k , Hk

exactamente

aristas.

tiene al menos

componentes conexas. Si se aade una arista entre 2 vrtices

[w]

el nmero de componentes conexas disminuye en

nmero de componentes conexas se mantiene, por lo menos

|V | k v, w, si [v ] = 1, y si [v ] = [w ] el cual Hk+1 tendr al

|V | (k + 1) componentes conexas. Por induccin Hn tiene al menos |V | n componentes conexas. Notamos que G = H|A| , por lo tanto, el grafo tiene al menos |V | |A| componentes conexas, pero |V | |A| > 1, por lo tanto G tiene mas de una componente
conexa, es decir, no es conexo. A veces lo que se necesita hacer es encontrar un grafo conexo, y ya vimos que si se quieren tener pocas aristas lo mejor es tener un rbol. Sin embargo, si se quiere trabajar con un subgrafo conexo de un grafo conexo, hasta el momento nada nos dice qu subgrafo tomar si queremos simplicar las cosas usando pocas aristas. El lector puede estar pensando en tomar un rbol, este rbol es llamado rbol de expansin, ya vimos en la demostracin del teorema 30 que este arbol siempre existe con un grafo conexo que tiene tantos vrtices como aristas, veamos un caso mas general:

Teorema 32. Sea G = (V, A) un grafo conexo, entonces existe un rbol


T = (V, A ) tal que A A, el cual es llamado rbol de expansin de G.

Demostracin.
Si expansin de

Como

es conexo

|A| |V | 1.

|A| = |V | 1 entonces G es un rbol, en este caso existe un rbol de G el cual es G. Ahora, si |A| > |V | 1 entonces G al ser conexo y no ser un rbol tiene al |A| (|V | 1) veces, el grafo seguir siendo

menos un ciclo, si quitamos cualquier arista del ciclo, el grafo seguir siendo conexo, si se repite esta operacin conexo y ser un rbol. Dicho rbol est contenido en el grafo original, y es el mencionado rbol de expansin.

212

CAPTULO 16. GRAFOS

16.7. Unin y Pertenencia


Ya vimos en la seccin anterior como los rboles se relacionan de una manera natural con la conectividad, ahora vamos a aprovechar todo esto para resolver el problema de unin-pertenencia. Este problema es un problema de diseo de estructura de datos, es decir, ocupamos mantener un conjunto de datos y estarlo continuamente actualizando. En este problema hay que disear una estructura de datos con la cual podamos representar estas dos operaciones en un grafo:

Unin. Aadir una arista entre dos vrtices. Pertenencia. Saber si un par de vrtices estan conectados.
Pueden sonar extraos los nombres de las operaciones. Se llaman as porque se puede pensar en cada componente conexa como un conjunto. Aadir una arista entre dos vrtices es equivalente a unir dos conjuntos. Saber si dos vrtices se encuentran en la misma componente conexa se puede interpretar como saber que dos vrtices se encuentran en el mismo conjunto. A pesar de esta notacin sugestiva, aqu seguiremos tratando este problema como un problema de conectividad. Hasta el momento, con lo que hemos visto la nica manera de atacar este problema sera realizando mltiples bsquedas. Podramos realizar la unin aadiendo una arista en tiempo constante si representamos el grafo con listas de adyacencia, pero si usamos una bsqueda para la operacin de pertenencia nos tomara tiempo lineal; y en este problema hay que estar preparados para poder ejecutar muchas veces ambas operaciones. Otra cosa que podramos hacer es numerar las componentes conexas y mantener un arreglo diciendo a qu componente pertenece cada uno de los vrtices. Con esto podramos lograr la pertenencia en tiempo constante, pero la unin en el peor de los casos seguira funcionando en tiempo lineal ya que habra que reasignar la componente conexa a varios vertices. En estos momentos parece claro que si seguimos pensando en optimizaciones menores para las bsquedas no llegaremos a mucho, necesitamos profundizar mas al respecto. Si recordamos la seccin anterior, todo grafo conexo tiene un rbol de expansin, por lo tanto, toda componente conexa tambin tiene un rbol de expansin. Hay que observar que si dos rboles son unidos por una arista, entonces el resultado ser otro rbol sin importar qu vertice se elija en cada rbol.

16.7. UNIN Y PERTENENCIA

213

Tambien hay que recordar que solo nos interesan las operaciones de unin y pertenencia. Con todo esto ya podemos estar seguros que no es necesario guardar todas las aristas, es suciente con guardar un rbol de expansin de cada componente conexa. Pero esta propiedad no basta para disear una estructura de datos eciente. Recordemos que en un rbol entre todo par de vrtices hay un nico camino que los une(claro que descartando los caminos que repiten vrtices).

T = (V, A), vamos a elegir arbitrariamente un r, al cual llamaremos raz. Est claro que para cualquier vrtice v V existe un nico camino que lo une con r , vamos a llamar P [r ] al segundo vrtice de dicho camino(el primer vrtice del camino claramente es v ), si v = r entonces el camino consta solamente de un vrtice y por conveniencia diremos que P [r ] = r . As que para llegar v a la raiz simplemente hay que seguir este algoritmo
Consideremos un rbol vrtice recursivo: 1 2 3 4 5 6 7 } Volviendo al problema original, si en lugar de guardar todo el grafo simplemente guardamos un rbol de expansin del grafo original entonces podemos a cada componente conexa asignarle una para cada vrtice

int

encuentra_raiz (

i f (P [ v]==v ) { return } else { return


}

int

v){ v; e n c u e n t r a _ r a i z (P [ v ] ) ;

raz, si hiciramos esto ya no necesi-

taramos usar listas de adyacencia ni bsquedas, simplemente guardaramos

el valor de

P [v ], y de esta manera desde cualquier vrtice

podramos llegar a la raz de su componente conexa. Si dos vrtices estan conectados, entonces estan en la misma componente conexa. Si estan en la misma componente conexa entonces estan conectados con la misma raz. Anlogamente si estan conectados con la misma raz entonces estn en la misma componente conexa y por tanto estan conectados. Bajo este esquema debemos de notar tambin que solamente necesitamos tener un rbol para cada componente conexa y cada rbol debe de contener a los vrtices de su respectiva componente. Pero no nos importa la estructura del rbol, solamente qu vertices tiene, por lo tanto no es necesario que sea un rbol de expansin. Si queremos unir dos componentes conexas raz de

A y B , basta con hacer que la B.

apunte a la raz de

B (o

viceversa), de esa manera todos los nodos

estarn conectados con la misma raz que todos los nodos de

214

CAPTULO 16. GRAFOS


Nuestro procedimiento de unin queda de la siguiente manera:

1 2 3 4 5 6

void union ( int int r a ,

a, rb ;

int

b){

r a=e n c u e n t r a _ r a i z ( a ) ; r b=e n c u e n t r a _ r a i z ( b ) ; P [ r a ]= r b ; } La gura 16.3 muestra un ejemplo de la ejecucin de la funcion cuando es llamada varias veces. Y como dos vrtices estan conectados s y solo s estan conectados con la misma raz entonces nuestro procedimiento de pertenencia queda de la siguiente manera:

union

1 2 3 4 5 6 7

bool

pertenencia (

int a , int b ) { i f ( e n c u e n t r a _ r a i z ( a )==e n c u e n t r a _ r a i z ( b ) ) { return true ; } else { return f a l s e ;


}

} No hay que olvidarse de que tambin el grafo necesita ser inicializado. Para este problema normalmente se considera que al inicio el grafo contiene solo vrtices y no tiene aristas, por lo cual debe de haber cdigo inicializa un grafo con

|V |

componentes

conexas y cada vrtice es la raz de su propia componente conexa. El siguiente

vrtices, como ya lo debes imaginar, este

cdigo debe de ser llamado antes de usar unin o pertenencia. 1 2 3 4 5 6 } Es claro que este algoritmo es bastante mas rpido que hacer bsquedas, sin embargo, an no lo analizamos adecuadamente. }

void

inicializa (

int n ) { int i ; for ( i =0; i <n ; i ++){


P [ i ]= i ;

16.8. Mejorando el Rendimiento de Unin-Pertenencia


Cmo recordar el lector, el algoritmo para resolver el problema de uninpertenencia se basa en tener varios rboles con races asignadas, donde cada

16.8. MEJORANDO EL RENDIMIENTO DE UNIN-PERTENENCIA215

0 Conguracin inicial 0 0

1 1

2 2

3 3

4 4

5 5

union(0, 2)

union(4, 3)

union(1, 3)

union(5, 0)

union(0, 4)

Figura 16.3: Ejecucin del algoritmo de unin-pertenencia

216

CAPTULO 16. GRAFOS

vrtice apunta a su

padre, y para unir dos rboles hacemos que la raz de uno

apunte a la raz del otro. Ya debemos de acordarnos de los rboles binarios de bsqueda y cmo a veces los rboles que se crean pueden tener alturas terriblemente grandes, de manera que son similares a listas enlazadas. En estos rboles tambien puede pasar lo mismo. Vamos a denir la altura de un rbol de la siguiente manera:

Denicin 16.8.1
r V,

(Altura)

La altura de un rbol y

T = (V, A)

con raz

es la distancia entre

donde

vV

es el vrtice mas lejano a

r.

h1 y h2 se unen con el procedimiento de la seccin anterior, el rbol resultante puede tener altura igual a h1 + 1 o bien altura igual a h2 . Lo primero sucedera en el caso de que h1 h2 y lo segundo sucedera en el caso de que h1 < h2 .
Si dos rboles de alturas Esto es una fuerte sugerencia de que al realizar uniones entre rboles es mejor hacer que la raz de un rbol con altura mnima apunte al otro rbol, si hacemos esto para obtener un rbol de altura 1 se necesitaran 2 de altura 0, para obtener uno de altura 2 se necesitaran 2 de altura 1, para obtener un rbol de altura 3 se necesitaran 2 de altura 2, o de manera mas general: para obtener un rbol de altura de altura

n > 0 seran necesarios al menos dos rboles

n 1. h(n) como
el nmero mnimo de vrtices que se pueden usar

Si denimos

para construir un rbol de altura

2h(n 1),

por lo tanto si tenemos

n, tenemos que h(0) = 1, y que h(n) = 2n vrtices el rbol mas alto que se puede

construir tendra altura

n. O(log N ), donde N
es el nmero de vrtices. Esto

Por lo tanto, de esta manera tanto la unin como la pertenencia tendran un tiempo de ejecucin de es bastante razonable. Sin embargo todo esto se puede simplicar an ms si cada vez que se encuentra una raz se borran todas las aristas que se recorrieron y se colocan aristas que apunten directamente a la raz. Si hacemos esto, cada arista sera recorrida a lo ms una vez, salvo que apunte directamente a la raz. Implementar esto es batante sencillo, basta con modicar el procedimiento para encontrar raz de la siguiente manera: 1 2 3 4 5 6

int

encuentra_raiz (

int i f (P [ v]==v ) { return } else { return

v){ v;

P [ v ]= e n c u e n t r a _ r a i z (P [ v ] ) ; P[ v ] ;

16.8. MEJORANDO EL RENDIMIENTO DE UNIN-PERTENENCIA217


7 8 } Como cada arista que no apunta directamente a la raz se recorre a lo ms una vez y todas las aristas son creadas mediante una operacin de unin, entonces el algoritmo toma un tiempo de llamadas a la operacin unin, y pertenencia. Esto no quiere decir que el procedimiento unin y el procedimiento pertenencia tomen tiempo constante, ya que el tiempo de ejecucin del algoritmo no se distribuye uniformemente entre todas las llamadas a estos procedimientos. }

O(u + p)

donde

es el nmero de

es el nmero de llamadas a la operacin

218

CAPTULO 16. GRAFOS

16.9. Problemas
16.9.1. Una Ciudad Unida
Tiempo Lmite: 1 segundo Unos ambiciosos ingenieros tienen pensado construir una gran ciudad a partir de unos planos que elaboraron. El plano de la ciudad consta de

esquinas y

calles bidireccionales.

Cada calle une a lo mas a dos equinas. Se dice que existe un camino que une a dos esquinas de esquinas unida

w,

si

v = w,

si hay

una calle conectando directamente a las dos esquinas, o si existe una secuencia

a1 , a2 , a3 , ..., ak , tal que a1 = v , ak = w, y toda ai (menos ak ) est directamente con ai+1 por una calle.

Los ingenieros se preguntan si habrn planicado mal algo, como por ejemplo, que si una persona vive en una esquina A y quiere ir a visitar a un amigo que vive en una esquina B, no exista un camino que le permita llegar a la esquina B.

Problema
Debes hacer un programa que dado un plano de la ciudad, determine cuantos pares de esquinas hay tales que no exista un camino que las una.

Entrada

Descripcin
Lnea 1: 2 nmeros enteros Siguientes

separados por un espacio.

lneas: Cada lnea representa una calle y contiene 2 nmeros

enteros representando los nmeros de las esquinas que une la calle.

Ejemplo
5 3 1 2 2 4 3 5

16.9. PROBLEMAS
Salida

219

Descripcin
Lnea 1: Un solo nmero entero, el nmero de pares de esquinas para las cuales no existen

Ejemplo
6

Consideraciones
0<2000<N 0<100000<M

Referencias
El problema fue escrito por Luis Enrique Vargas Azcona y fu utilizado por primera vez en un examen preselectivo de la Olimpiada de Informtica durante el 2007.

220

CAPTULO 16. GRAFOS


Abba

16.9.2.

Tiempo Lmite: 1 segundo

Descripcin
reemplaza(a, b) cambia cada una de las ocurrencias del caracter a por el caracter b. Por ejemplo, si S = abracadabra entonces reemplaza(b, c) produce la cadena acracadacra.
Dada una cadena de caracteres la operacin Un palndrome es una cadena de caracteres que se lee de la misma forma de izquierda a derecha que de derecha a izquierda. Por ejemplo, son palndromes.

S,

abba

dad

Problema
S y que encuentre el nmero mnimo r de aplicaciones de la operacin reemplaza que transforman a S en un palndrome.
Escribe un programa que lea una cadena de caracteres

Entrada
Un solo rengln que contiene una cadena letras minsculas del alfabeto ingls.

formada exclusivamente por

Ejemplo

croacia

Salida
El valor de

r.

Ejemplo

Consideraciones
La cadena

tendr una longitud entre 1 y 1,000,000.

16.9. PROBLEMAS

221

Referencias
La idea original del problema fue de Sergio Murgua, el problema fu redactado por Francisco Javier Zaragoza Marinez y fue utilizado por primera vez en un examen selectivo de la Olimpiada Mexicana de Informtica durante Julio del 2007.

222

CAPTULO 16. GRAFOS


Cdigos de Prfer

16.9.3.

Tiempo Lmite:1 Segundo El matemtico Heinz Prfer invent una forma ingeniosa de nombrar rboles cuyos vertices esten etiquetados de manera nica con los nmeros enteros de 1 a

n n2
nmeros

La forma de nombrar el rbol consiste de un cdigo con goritmo: 1. Localiza la hoja

llamado cdigo de Prfer. Dicho cdigo se obtiene mediante el siguiente al-

que est etiquetada con el nmero mas pequeo.

2. Agrega al cdigo la etiqueta del vrtice al cual est unido 3. Borra

4. Si quedan 3 vrtices o ms, regresa al paso 1 Por ejemplo, el cdigo de Prfer asociado al rbol de la siguiente gura es 9 7 7 7 9 6 6

8 3 4 2 9 1 7 6

Problema
Dado un cdigo de Prfer encuentra el rbol que genera ese cdigo.

Entrada
Lnea 1: Un entero Lnea 2: Prfer.

n2

enteros separados por espacios representando el cdigo de

16.9. PROBLEMAS
Ejemplo de Entrada

223

9 9 7 7 7 9 6 6

Salida
La salida deber constar de

lneas, donde para cada

1 i n,

la

isima

lnea deber indicar los nmeros de los vrtices unidos al vrtice

en orden ascendente y separados por espacios. Si hay mltiples soluciones simplemente imprime una sola lnea con la palabra

AMBIGUO.

Si no hay solucin imprime una sola lnea con la palabra

IMPOSIBLE

Ejemplo de Salida

9 7 7 7 9 7 9 2 3 4 6 7 8 9
Lmites

2 n 100000

Referencias
Este es un problema clsico ingeniado por el mismo Prfer para demostrar n2 que el nmero de rboles con exactamente n nodos es n . El problema fue redactado por Luis Enrique Vargas Azcona en Junio de 2009 para entrenar a la preseleccin nacional en la Olimpiada Mexicana de Informtica.

224

CAPTULO 16. GRAFOS

16.10. Sugerencias
Una Ciudad Unida
Considera hayar las componentes conexas en lugar de hacer una bsqueda independiente con cada vrtice.

Abba

G = (V, A) donde V consta de los 26 caracteres del alfabeto ingls y {v, w } A s y solo s en algn momento hay que cambiar alguna letra v alguna letra w (ntese que al decir letra v y letra w esta sugerencia se reere a cualquier letra en V , no a los caracteres v y w).
Piensa en un grafo Qu sucede con ese grafo si se reemplazan todas las coincidencias de una letra por otra?

Cdigos de Prfer
Qu sucede con las hojas de un rbol en su cdigo de Prfer correspondiente?. Realmente es posible que la salida sea

AMBIGO

IMPOSIBLE?

Parte V Algoritmos de Ordenamiento II

225

227

La parte de ordenamiento fue necesaria dividirla debido a que existen algoritmos de ordenamiento que hacen uso de estructuras de datos. En efecto, en general los rboles estn diseados parar organizar elementos con una funcin de comparacin, as que no suena descabellado que se puedan usar para ordenar. Pero los rboles no slo servirn para organizar los datos que se quieran ordenar, tambin jugarn un papel importante cuando descubramos que no existe ningn algoritmo de ordenamiento basado en comparaciones cuya complejidad sea menor que O(N log N).

228

Cap tulo

17
O(N ), en este corto captulo volveremos a aplicar esa

rboles y Ordenamiento
Cuando revisamos el algoritmo de seleccin optamos por elegir la estrategia de encontrar el menor y colocarlo al principio. Sin embargo la forma de encontrar el menor era estrategia pero los rboles nos servirn para realizarla mas rpido.

17.1. Ordenamiento por rboles Binarios de Bsqueda


Los rboles binarios de bsqueda sern la primera estructura de datos que usaremos para ordenar. Nos basaremos en la implementacin que se mencion en la seccin 14.1: 1 2 3 4 Tipo

int int int

c l a v e [ maximo i z q [ maximo d e r [ maximo n o d o s =0; de de

de

nodos ] ;

nodos ] ; nodos ] ;

Ya vimos en la seccin 14.4 cmo insertar nodos en el rbol, pero an no hemos visto cmo usarlo para ordenar. Recurriremos a uno de los tres recorridos, el llamado recorrido en rden. El siguiente teorema revelar por qu el libro no deja de pregonar que los rboles binarios de bsqueda son tiles para el ordenamiento.

Teorema 33. Un recorrido en rden en un rbol binario de bsqueda procesa los nodos en rden no descendente con respecto a sus claves.
Demostracin.
Utilizando induccin, en un rbol binario de bsqueda con un solo nodo, solamente se visita un nodo, por lo cual el rden en que los nodos se visitan es no descendente.

229

230

CAPTULO 17. RBOLES Y ORDENAMIENTO


Supongamos que para alguna

todos los rboles binarios de bsqueda

con

tamente

k < k nodos. k nodos.

Imaginemos un rbol binario de bsqueda que tiene exac-

Tanto su rama izquierda como su rama derecha son rboles binarios de bsqueda, y adems, tienen menos de

nodos.

Al recorrer su rama izquierda, todos sus nodos se visitarn en orden no descendente, al visitar la raz, por la denicin de rbol binario de bsqueda, su clave ser mayor o igual que todas las claves de los nodos ya visitados. Finalmente, al recorrer la rama derecha, todos los nodos tendrn claves mayores o iguales que los nodos que ya se haban visitado, adems, como la rama derecha es un rbol binario de bsqueda con menos de se visitarn dichos nodos en rden. Asi que la forma de ordenar con un rbol binario de bsqueda es simplemente insertar todos los elementos y luego hacer un recorrido en rden. El recorrido en rden toma tiempo lineal, sin embargo la insercin puede tomar tiempo cuadrtico. Como aqu se supone que sabemos de antemano cual es la secuencia de nodos que se va a ordenar, lo que se puede hacer es insertarlos en un rden aleatorio y por el teorema 20 sabemos que en promedio este algoritmo funcionar como si fuera O(N log N), al menos con

k nodos, tambin

N 50000000.
A pesar de que mezcla es tericamente mas rpido(y probablemente en la prctica tambin lo sea), los rboles binarios de bsqueda tienen la ventaja de que es posible obtener ordenamientos de los primeros

elementos insertados

en tiempo lineal, y con mezcla sera necesario reordenar cada vez que se quiera saber el rden de los elementos que ya se insertaron. Dicho de otra manera, los rboles binarios de bsqueda sirven para aplicaciones mo

interactivas.

A pesar de que se mencion que en este libro no iba a ensear el algorit-

QuickSort,

este ordenamiento con rboles binarios de bsqueda es

QuickSort(pero este ltimo pierde la ventaja de la

interactividad ).

casi

17.2. Ordenamiento por Montculo


Hasta el momento el nico algoritmo que se ha mostrado que realmente funciona en bsqueda es

O(N log N ) es el de mezcla, ya que el uso de rboles binarios de casi O(N log N ) pero estrictamente hablando es O(N 2 ); adems,

ambos algoritmos requieren guardar en memoria una copia del arreglo inicial.

sort )

El algoritmo de ordenamiento por montculo(muchas veces llamado funciona en

heap-

O(N log N )

y adems no necesita guardar otra copia del

arreglo para ordenarlo.

17.2. ORDENAMIENTO POR MONTCULO

231

La idea del ordenamiento por montculo es similar a la de ordenamiento por seleccin. En cada iteracin del ordenamiento por seleccin se encuentra el menor elemento y se coloca al principio; en el ordenamiento por montculo se encuentra el mayor elemento y se coloca al nal. Un montculo se puede usar para insertar datos en mayor tambin en

O(log N ),

y quitar el

O(log N ),

as que lo nico que hay que hacer es insertar

todos los elementos en un montculo y posteriormente quitar el mayor hasta que el montculo quede vaco. Aunque la idea base sea sencilla, aplicarla de manera que no se desperdicie memoria complica las cosas. La estrategia para lograr esto ser una idea extraamente parecida a insercin: Inicialmente el primer elemento del arreglo puede representar un montculo, as que inicialmente el arreglo representa un montculo de tamao 1 y

n1

datos independientes del montculo. Luego tomamos el valor de la se-

gunda posicin y lo insertamos en el montculo, de esa manera los primeros 2 elementos del arreglo representarn un montculo, despus tomamos el tercer elemento y lo insertamos y as sucesivamente. Los montculos y su representacin en memoria estn diseados de tal manera que si hay

n nodos nunca se requieran mas de n posiciones de memo-

ria consecutivas; as que ser posible

convertir

el arreglo en un montculo sin

necesidad de crear otro arreglo y sin perder informacin. Posteriormente se inicia la etapa de seleccin, en la posicin del arreglo(la cual corresponde a la posicin slo ocupa las primeras

se quita el elemento mayor del montculo y se coloca en la

i sima iteracin n i + 1sima n i en C y C++),

ntese que para el momento en que se termina de quitar el mayor, el montculo

ni

posiciones del arreglo.

A continuacin se muestra una implementacin del ordenamiento por montculo: 1 2 3 4 5 6 7 8 9 10 11 } } }

void

heapSort (

int

int
k,

Arreglo [ ] , h;

int

n){

i ,

N=0;

for ( i =0; i <n ; i ++){


i n s e r t a r ( Arreglo , Arreglo [ i ] ) ;

for ( i =0; i <n ; i ++){


k=q u i t a r ( A r r e g l o ) ; A r r e g l o [ n1 i ]= k ;

232

CAPTULO 17. RBOLES Y ORDENAMIENTO


Como se puede ver el cdigo es bastante corto pero usa un par de fun-

ciones no mas cortas, las cuales son respectivamente.

insertar

quitar,

las implementa-

ciobnes de dichas funciones se pueden encontrar en las secciones 15.3 y 15.4 Nuevamente se hace incapi en que estas implementaciones son un mero ejemplo y puede haber muchas maneras de implementar los mismos algoritmos, de hecho se est usando el tipo de datos

<,

int y la funcin de comparacin

pero cualquier rden total es vlido.

Cap tulo

18
O(N log N );
pero no hemos dado la prueba de ello

Mas rpido que O(N log N)?


Ya se dijo varias veces en este libro que los algoritmos de ordenamiento mas rpidos son de y adems es posible romper esta restriccion si nos restringimos a ordenar nmeros enteros en lugar de ordenar cualquier conjunto con un rden total. El siguiente algoritmo es importante conocerlo, las ltimas dos secciones de este captulo resultan interesantes para aquellos que gustan de conocer algoritmos pero no presentan utilidad alguna en los concursos de programacin y tampoco tienen muchas aplicaciones as que esas dos secciones son opcionales.

18.1. Count-Sort
Consideremos que tenemos una sucesin nita de la cual denotaremos por

enteros entre

0 y M,

S = (s1 , s2 , ..., sN ).

Existe una manera relativamente sencilla de ordenar estos enteros que hemos pasado por alto hasta ahora: se trata de crear un arreglo tamao

M,

tal que

Cont[k]

Cont

de

indique cuntas veces aparece

en la secuencia

S.
La forma de crear este arreglo es mucho mas sencilla que los algoritmos de mezcla y los de ordenamiento usando rboles. Lo nico que hay que hacer es inicializar el arreglo el arreglo 1 2 3

Cont

en

Una vez creado el arreglo

0, recorrer S e incrementar el contador. Cont con las caractersticas deseadas, obtener


,

ordenado se vuelve trivial:

void c o u n t S o r t ( int S [ ] int i , k ; for ( i =0; i <= M; i ++){

int

N,

int

M) {

233

234

CAPTULO 18. MAS RPIDO QUE O(N LOG N)?


Cont [ i ] = 0 ;

4 5 6 7 8 9 10 11 12 13 14 15 16 } } } }

for ( i =0; i <N ; i ++){


Cont [ S [ i ] ] + + ;

for ( i =0 , k =0; i <= M; i ++){ while ( Cont [ i ] > 0 ) {


S [ k ]= Cont [ i ] ; Cont [ i ] ; k++; }

Este algoritmo puede aparentar ser el sueo mas maravilloso imaginado entre los algoritmos de ordenamiento: muy sencillo de implementar y funciona en tiempo lineal. Pero las cosas no son tan maravillosas como parecen: en primer lugar su complejidad no es de rden que si

es grande(por

O(N ) sino de rden O(N + M ), lo cual signica 3 ejemplo M = 2 1 1) este algoritmo puede resultar

ser mucho mas lento que todos los dems que hemos visto(incluso mas lento que burbuja! ) y en segundo lugar slamente sirve para ordenar nmeros enteros no negativos, lo cual es muy diferente a poder rdenar cualquier conjunto con un rden total. Este algoritmo puede ser adaptado para ordenar tambin nmeros negativos sumando una constante a todo el arreglo y al nal restndolo, tambin se pueden ordenar nmeros racionales, pero para eso es necesario multiplicar todos los datos por una constante e incluso multiplicar

por esa constante!.

Como conclusin, este algoritmo slo es til en casos muy especcos.

18.2. *Bucket-Sort
Ya vimos que el Count-Sort tiene la terrible desventaja de que el tamao de

importa demasiado en el rendimiento, esto se puede corregir de una

manera muy sencilla: Hacemos count-sort con cada dgito, es decir, si a la primera todos aquellos nmeros cuyo da) sea 0, a la segunda todos aquellos as sucesivamente.

es el nmero de dgitos

del mayor nmero de la secuencia entonces creamos 10 secuencias, agregamos

c-simo dgito(de derecha a izquiernmeros cuyo c-simo dgito sea 1, y

18.2. *BUCKET-SORT

235

Una vez separada la secuencia en 10 secuencias, es posible proceder recursivamente y al nal simplemente concatenar las secuencias ordenadas. Como se puede apreciar, este algoritmo tambin est pensado para ordenar nicamente nmeros enteros no negativos, pero puede ser extendido para ordenar otras cosas. Por ejemplo, se pueden ordenar nmeros con expansin decimal nita siguiendo el mismo principio, tambin se pueden representar enteros simulando el digno con un dgito extra; otra cosa que se puede hacer es representar vectores ordenando primero los dgitos de una componente y luego los de la otra. En resmen: este algoritmo puede ordenar todo aquello cuyo rden total sea representado a travs de nmeros enteros no negativos. Cualquier conjunto nito con un rden total puede ser representado as, pero no siempre resulta prctico(es posible que para saber cmo representar los elementos sea necesario primero ordenarlos). Otra cosa que hay que hacer notar, es que el algoritmo se ilustr con base 10 pero funciona con cualquier base, debido a que los procesadores trabajan mejor con potencias bits, es preferible usar una base que sea una potencia de 2. La complejidad de este algoritmo es rpido que los tradicionales cuando

O(N log M ),

asi que como se puede

apreciar, de manera parecida a count-sort, este algoritmo slo resulta mas

es lo sucientemente pequeo.

Un detalle interesante es que si la base de numeracin usada es igual a

entonces obtendremos el count-sort. Debido a que este algoritmo puede presentar tantas implementaciones

distintas segn sea el caso, aqu slo se mostrar una que ordena enteros y funciona usando base 2, por lo que slo divide la secuencia original en 2 secuencias. Se asume que se tiene una funcin llamada devuelve el 1 2 3 4 5 6 7 8 9

BIT(a, b)

la cual

bsimo

bit de

a.

void

ordena (

int i , k , m; i f ( i n i c i o >f i n ) return ; for ( i = i n i c i o ; BIT ( S [ i ] ,


}

int

S[] ,

int

inicio ,

int

fin ,

int

bits ){

b i t s )==0 && i <=f i n ; i ++){

for ( k= i +1; k<=f i n ; i ++){ for ( ; BIT ( S [ k ] , i f ( k<=f i n )


{ }

b i t s ) !=0 && k<=f i n ; k++)

intercambia (S [ k ] ,

S[ i ]) ;

236

CAPTULO 18. MAS RPIDO QUE O(N LOG N)?


}

10 11 12 13 14 15 16 17 18 }

while ( BIT ( S [ i ] , i f ( b i t s >0) {


} i

i =min ( i ,

fin ) ;

b i t s ) !=0 && i >=i n i c i o )

;
inicio , i +1 , i ,

ordena (S , ordena (S ,

fin ,

1) ; b i t s 1) ;
bits

18.3. *La Inexistencia de Mejores Algoritmos


A pesar de que esta seccin est marcada con (*) se advierte nuevamente que el lector puede optar por saltarse esta seccin y se asegura que no se volver a utilizar en el libro. De hecho a lo largo del libro las nicas matemticas que se dan por conocidas han sido matemticas muy elementales, pero este captulo es diferente, pues usaremos una ideantidad llamada la

frmula de Stirling.

La demostracin de esta identidad requiere de conocimientos de clculo y lo cual se aleja de los propsitos de este libro. Si lector quiere conocer la demostracin de la frmula puede consultarla el apndice de bibliografa recomendada.

Teorema 34

(Frmula de Stirling)

. Se cumple la siguiente igaldad.


(18.1)

log (n!) = n log (n) n + f (n)

Donde n es un entero positivo y f es de rden O(log (n))


Para obtener una cota inferior respecto a la complejidad de un algoritmo de ordenamiento, recordemos qu es lo que tenemos permitido hacer. Cuando decimos algoritmo de ordenamiento normalmente nos resferimos a un ordenamiento sobre un elementos que tienen un rden total; es decir lo nico que podemos hacer es comparar pares de elementos, ya que en un rden total slo estamos seguros que los elementos Tambin tenemos

saben

cmo compararse.

elementos, por simplicidad asumiremos que dichos

elementos son todos distintos. Ordenar dichos elementos consiste en encontrar una permutacin de ellos que cumpla con ciertas caractersticas, dicha permutacin es nica; y con cada comparacin que se realiza, nos vamos haciendo la idea de cul puede ser la permutacin correcta.

18.3. *LA INEXISTENCIA DE MEJORES ALGORITMOS

237

De una manera mas precisa, inicialmente hay n! permutaciones que po(n)(n1) posibles comparaciones; cada dran ser la correcta y podemos hacer 2 vez que realicemos una comparacin obtendremos un 0 o un 1, y la intencin es que la cantidad de permutaciones que podran ser la correcta disminuya. Vamos a imaginar un rbol binario, en el cual a cada hoja corresponde a una permutacin nica y a cada nodo permutaciones posibles entonces

corresponde a un conjunto de

C (v ) C (v ) = C (a) C (b)

de tal manera que si los hijos de y adems

son

a b = . r
representa el estado inicial y

Este rbol representa un algoritmo, cada nodo representa un estado posible del algoritmo, de tal manera que la raiz

C (r )

es el conjunto de todas las permutaciones de los

elementos.

Como el algoritmo est bien denido entonces no hay duda que la primera comparacin es siempre la misma, esa comparacin puede regresar 0 o 1, el hijo izquierdo representa el estado del algoritmo si la primera comparacin regresa 0 y el hijo derecho representa el estado del algoritmo si la primera comparacin regresa 1. De tal manera que si entonces

son los hijos izquierdo y derecho de la raiz

C (a)

C ( b)

representan los conjuntos de posibles permutaciones

vlidas si la primera comparacin regresa 0 y si regresa 1 respectivamente. De manera anloga todas las comparaciones realizadas(y los valores devueltos) por el algoritmo en un estado quedan determinadas por el camino a la raz del nodo que representa dicho estado; los hijos izquierdo y derecho representarn lo que sucede si la siguiente comparacin regresa 0 y si regresa 1 respectivamente. Una vez convencidos de que cada rbol puede representar de manera nica todas las posibles comparaciones que realiza un algoritmo de ordenamiento con una entrada de tamao

n, procederemos a medir la complejidad en cuanto

a nmero de comparaciones. Descender un nivel en el rbol equivale a realizar una comparacin, de esta manera al alcanzar una hoja, el nmero de comparaciones realizadas ser su distancia a la raz. Como se explico antes, a la raz, es decir, respecto a la altura del rbol. El nmero de hojas del rbol binario es y bien sabemos que un rbol binario

somos pesimistas,

as que

vamos a medir la complejidad del algoritmo con respecto a la hoja mas lejana

n!(una hoja por cada permutacin), con n! hojas tiene altura de al menos

log2 (n!), por lo tanto por la frmula de Stirling la altura del rbol es de rden O(nlogn).
De este resultado obtenemos el siguiente teorema:

Teorema 35. Cualquier algoritmo de ordenamiento(sobre un rden total)


tiene complejidad de al menos O(n log n).

238

CAPTULO 18. MAS RPIDO QUE O(N LOG N)?

Parte VI Optimizacin Combinatoria

239

Cap tulo

19

Estructura de la Solucin y Espacio de Bsqueda


Gran parte de los problemas tratados en la algoritmia son de optimizacin combinatoria; es decir, dentro de un conjunto nito, elegir el mejor elemento o encontrar aquellos elementos que cumplan con una caracterstica especial. Esto suena como si fuera algo realmente sencillo, pues todo lo que hay que hacer es evaluar uno por uno y quedarse con el mejor. Lo interesante de estos problemas radica en encontrar el mejor mas rapidamente o en reducir el nmero de posibles soluciones. Al procedimiento de evaluar

candidato

que pudiera ser una solucin le

llamamos bsqueda(rerindonos a que se est espacio de bsqueda.

buscando

una solucin), y al

conjunto dentro del cual tenemos que encontrar la solucin le llamaremos Aqu hay que aclarar que no se est abusando de la palabra escencia un espacio de bsqueda. Por lo general la denicin de espacio de bsqueda es simplemente el conjunto de todos los posibles

bsqueda, la

cual es usada para referirse a los recorridos en grafos, ya que un grafo es en

candidatos

a ser una solucin de un problema de

optimizacin. Sin embargo, para nuestros propsitos usaremos una denicin algo diferente, la cual requiere de un poco mas de texto para entender tanto su signicado como su motivacin. Por el momento nos quedaremos con la siguiente denicin provicional:

Denicin 19.0.1 (Espacio de Bsqueda(denicin temporal)). Un conjunto que contiene a todos los de optimizacin. Es decir es un conjunto al cual pertenece la solucin del problema, pudiendo tener otros elementos que no sean la solucin.

candidatos

para ser una solucin de un problema

241

242CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

Lo primero que se necesita para hacer uso ecaz de una bsqueda es denir un espacio de bsqueda, este paso es obvio, antes de buscar, requerimos saber en donde buscar. Pero an cuando parezca obvio, es el paso ms difcil de todos y el que menos programadores son capaces de tomar. Como buen programador es indispensable que puedas denir un espacio de bsqueda para cualquier problema que se te presente, de otra forma tendrs serios problemas para poder encontrar soluciones a problemas que no sean triviales. Para poder denir el espacio de bsqueda, es necesario conocer la estructura de la solucin, es decir, Qu es la solucin que me piden? Qu estructura tiene? Cmo se puede representar?

19.1. Estructura de la Solucin


Normalmente la estructura de la solucin de un problema de optimizacin combinatoria se puede simplicar a una sucesin de decisiones, cada decicin representada mediante un nmero. Por ejemplo en el siguiente problema:

Problema Resuelto 19.1.1.


de 2 cuesta $5.

En una tienda venden paquetes de crayones,

el paquete de 11 crayones cuesta $15, el paquete de 20 cuesta $25, el paquete

Se cuenta con exactamente $30, cul es la mayor cantidad de crayones que se pueden comprar?

Solucin

Este problema se puede reducir a tomar estas 3 deciciones:

Cuntos paquetes de 11 crayones comprar? Cuntos paquetes de 15 crayones comprar? Cuntos paquetes de 2 crayones comprar?

Se pueden comprar a lo mas 2 paquetes de 11, a lo ms un paquete de 15 y a lo ms 6 paquetes de 2. Nuestra estructura de la solucin puede ser una terna ordenada de enteros indicando cuntos paquetes se van a comprar de cada tipo. Por lo tanto nuestro espacio de bsqueda es el siguiente:

19.1. ESTRUCTURA DE LA SOLUCIN

243

(0)

(1)

(0, 0)

(0, 1)

(1, 0)

(1, 1)

(0, 0, 0)

(0, 0, 1)

...

...

(1, 1, 0)

(1, 1, 1)

Figura 19.1: rbol de Decisiones

{(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4), (0, 0, 5), (0, 0, 6) (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 1, 5), (0, 1, 6) (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 0, 3), (1, 0, 4), (1, 0, 5), (1, 0, 6) (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4), (1, 1, 5), (1, 1, 6) (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 0, 3), (2, 0, 4), (2, 0, 5), (2, 0, 6) (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 1, 4), (2, 1, 5), (2, 1, 6)}
Muchas veces resulta conveniente visualizar esta sucesin de decisiones como rboles enraizados(con raz), donde cada nodo representa un prejo de uno o mas

candidatos

a soluciones, como te podrs imaginar un nodo

padre es prejo de toda su descendencia y las hojas representan los elementos

decisiones.

del espacio de bsqueda. Estos rboles a menudo son llamados

rboles de

Denicin 19.1.1 (rbol de Decisiones). Si un espacio de bsqueda S posee


como elementos solamente sucesiones nitas, entonces decisiones. El rbol de decisiones de

posee un rbol de con raz

es un rbol enraizado

T = (V, A)

en el cual se cumplen las siguientes condiciones:

Para todo nodo elemento de

v V

existe

S.

Llamaremos

p(v ) tal que p(v ) es prejo de p(v ) la subsolucin asociada a v .

algun

244CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

Para todo prejo que

de todo elemento

sS v

existe un nodo

vV

tal

p(v ) = x. u, v V , u
es padre de entonces

Si para dos nodos

p(u)

es prejo de

p(v ).
La gura 19.1 muestra el rbol correspondiente al espacio de bsqueda que denimos. Obviamente solo muestra algunos nodos, ya que ocupara demasiado espacio en la pgina. Notamos como la raz est vaca, los hijos directos de la raz constan de un solo nmero, los nodos a distancia 2 de la raz son pares ordenados y las hojas, que estan a distancia 3 de la raz, son ternas ordenadas. Aqu debemos de notar que no todas las ternas del espacio de bsqueda son aceptables, puesto que algunas se exceden del presupuesto, por ejemplo, la terna

(2, 1, 6)

no es aceptable, ya que comprar 2 paquetes de 11 crayones,

1 de 15 y 6 de 2 cuesta

dispone de la cantidad de

30 + 15 + 30 = 75 30.

y en este problema solamente se

El objetivo del espacio de bsqueda no es ser una solucin al problema, ni ser un conjunto de soluciones aceptables, de hecho puede haber muchos espacios de bsqueda vlidos para un mismo problema. El objetivo real de un espacio de bsqueda es ser un conjunto nito donde seguramente se encuentre la solucin, muchas veces es preferible que este conjunto sea pequeo, sin embargo a veces es mas sencillo trabajar con espacios mas grandes. Volviendo al problema, si vericamos todas las ternas, una por una, llegaremos a la conclusin de que

(2, 0, 0)

representa la solucin al problema,

es decir, la solucin es comprar 2 paquetes de 11. El ejemplo anterior fue algo burdo y muy probablemente el lector lleg a la conclusin de los 2 paquetes de 11 sin necesidad de vericar cada terna en el espacio de bsqueda. No hay que subestimar el ejemplo, se incluy para ilustrar como la estructura de la solucin de un problema de optimizacin combinatoria se puede reducir a tomar una sucesin de decisiones(en este caso 3 decisiones). El identicar correctamente la estructura de la solucin y visualizar el espacio de bsqueda como un rbol enraizado ya es un gran paso, y ahora podemos entender las bsquedas que ya se presentaron anteriormente de la siguiente manera:

Bsqueda en Profundidad.

Procedimiento recursivo que trata de

llegar siempre lo ms profundo que pueda, en cada paso, si an no ha

19.2. JUEGO DE NMEROS

245

encontrado la solucin, trata de bajar un nivel en el rbol, si no es posible bajar ms, entonces regresa un nivel y trata de bajar por la siguiente rama que todava no haya recorrido.

Bsqueda en Amplitud. Recorre el rbol nivel por nivel, es decir, en


el primer paso busca la solucin entre todos los nodos del primer nivel del rbol, si no encuentra la solucin, entonces baja un nivel y la busca entre todos los nodos del segundo nivel, y de esa manera recorre cada uno de los niveles hasta encontrar la solucin. Aqu hay que hacer notar que las bsquedas en amplitud son especialmente tiles cuando lo que se desea es la solucin que este ms cercana a la raz del rbol, ya que al ir recorriendo nivel por nivel, la primera solucin que se encuentra es aquella que esta ms cercana a la raz. Tambin es agradable notar que no hemos dejado de ver las bsquedas como recorridos de grafos, ya que un rbol enraizado tambin es un grafo. Dedicaremos las siguientes secciones completamente a ejemplicar el uso de bsquedas identicando los rboles de decisiones correspondientes as como a notar las deciencias que tiene este modelo y motivar otra forma mas renada de ver los espacios de bsqueda. Es importante leer estas secciones ya que adems de ilustrar como usar las bsquedas en problemas no triviales, nos llevarn a nuestra nueva concepcin de espacio de bsqueda y en el camino se darn mas deniciones.

19.2. Juego de Nmeros


Esta seccin se dedica exclusivamente a un problema, el cual llamaremos Juego de Nmeros no sobra decir que este fu uno de los primeros problemas utilizados para entrenar y elegir a la seleccin mexicana para la Olimpiada Internacional de Informtica del 2005. El contexto del problema es el siguiente: Hay un juego, que se juega sobre un tablero de N xN casillas, en cada 2 casilla hay un nmero distinto entre 1 y N , por lo que todas las casillas del tablero estan llenas. El objetivo del juego consiste en llevar la conguracin inicial del tablero a 2 una conguracin en la que los nmeros del 1 al N se encuentren ordenados comenzando de izquierda a derecha y de arriba a abajo. Es decir, si el tablero inicialmente estuviera como se muestra en la gura 19.2 el objetivo del juego sera llevarlo a la conguracin de la gura 19.2. Para mover nmeros de su lugar te puedes posicionar en cualquier casilla

(i, j )

tal que

1i<N

1 j < N.

246CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

2 1 7

5 8 9

3 4 6

Figura 19.2: Estado inicial del tablero 1 4 7 2 5 8 3 6 9

Figura 19.3: Estado nal del tablero

Una vez en la casilla

(i, j )

se puede hacer una rotacion, ya sea en sentido

horario o antihorario utilizando las casillas la

(i + 1, j ), (i + 1, j + 1) y (i, j + 1).

Por ejemplo, para el de la gura 19.2 si nos posicionaramos en la casil-

(2, 2) podemos hacer una rotacion(horario o antihoraria) con las casillas (3, 2), (2, 3) y (3, 3). Las guras 19.2 y 19.2 muestran ambas rotaciones.

Problema Resuelto 19.2.1.

Escribe un programa que dada la congura-

cion inicial de un tablero de 3 x 3 determine cual es el numero minimo de movimientos que se requieren para ordenarlo. Se considera un movimiento cualquier giro que se haga, ya sea en sentido horario o antihorario. El programa deber de funcionar en menos de un segundo.

Estructura de la Solucin
Primero que nada identicaremos la estructura de la solucin, y para eso debemos de responder esta pregunta qu nos est pidiendo el problema? La respuesta es sencilla: Nos est solicitando el nmero mnimo de movimientos para llegar de una permutacin inicial de las casillas a una permutacin nal. Es conveniente ver el estado del tablero como una permutacin de 9 nmeros enteros, donde los primeros 3 corresponden a la primera la, los

1 4 7

8 5 9 6
(2, 2)

Figura 19.4: Rotacin horaria en

19.2. JUEGO DE NMEROS


1 4 7 2 3

247

6 9 5 8
(2, 2)

Figura 19.5: Rotacin antihoraria en

siguientes 3 a la segunda la y los ltimos 3 a la ltima la. Siendo as la permutacin nal es

(1, 2, 3, 4, 5, 6, 7, 8, 9).

Aunque este problema consiste en encontrar el nmero mnimo de movimientos para acomodar el tablero, si vemos la respuesta como un nmero es claro que no avanzaremos mucho. Vamos a cambiar un poco el problema con el n de poder denir subsoluciones: en lugar de buscar nicamente el mnimo nmero de movimientos para acomodar el tablero por el momento supondremos que tambin necesitamos saber qu movimientos hacer para acomodar el tablero. Ahora podemos ver el espacio de bsqueda como un rbol enraizado, donde cada nodo representa una sucesin de movimientos, la raz representa la ausencia de movimientos, los 8 nodos a distancia 1 de la raiz representan las 8 rotaciones(2 rotaciones diferentes en cada uno de los 4 lugares diferentes) que se pueden realizar, los 64 nodos a distancia 2 de la raz representan los 64 pares de rotaciones que se pueden realizar, etc. Debido a que hay 8 rotaciones vlidas en este juego, de cada estado surgen 8 nuevos estados, lo que ocasiona que el rbol de bsqueda crezca demasiado rpido. Hay un nodo a distancia 0 de la raz, 8 nodos a distancia 1, 64 nodos a distancia 2, 512 nodos a distancia 3, etc.

n Es decir, a distancia n hay 0+8+64+ ... +8 nodos, lo cual es equivalente 8n+1 1 nodos. Si buscaramos una solucin de tamao 10 tendramos que a 7 revisar un total de 153391689 nodos!.
Esto pudiera parecer un callejn sin salida, ya que realmente no podemos revisar todas las subsoluciones (sucesiones de movimientos, en este caso) que se pueden realizar. Sin embargo hay algo que nos puede sacar de este apuro: Hay solamente

9! = 362880
si

acomodos del tablero posibles. Tambin debemos de notar que

para dos subsoluciones

A = (a1 , a2 , ..., an )

llegan al mismo acomodo del

B = (b1 , b2 , ..., bm ) con n < m, tablero, entonces A puede ser prejo


y

de una solucin pero

no puede ser prejo de una solucin.

Recordamos que nuestra denicin de solucin es aquella sucesin nita de movimientos que lleva el tablero del estado inicial al nal y que adems dicha sucesin tiene longitud mnima. Si

fuera prejo de una solucin

S = (s1 , s2 , ..., sm ),

signicara que

248CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

b1 = s1 , b2 = s2 , ..., bm = sm , es decir, que una solucin sera realizar los m movimientos de B llevando al tablero a un acomodo P y luego realizar m m movimientos llevando al tablero al acomodo nal.
Pero si eso sucediera entonces sera posible llevar al tablero al al mismo acomodo

P realizando n < m movimientos y luego realizar los movimientos sm+1 , sm+2 , ..., sm llevando al tablero al estado nal. Es decir la subsolucin T = (a1 , a2 , ..., an , sm+1 , sm+2 , ..., sm ) llevara al tablero del acomodo inicial al acomodo nal, y adems la longitud de T sera n + m m lo cual es menor que m + m m = m y contradice la suposicin de que S es una solucin.

si hay varias formas de llegar a un mismo acomodo del tablero, solamente hay que tomar en cuenta la mas corta.
En resumen, lo que probamos en estos prrafos fue que basta con tomar cualquiera de ellas y no perderemos nuestra oportunidad de encontrar una solucin ya que al llegar al mismo acomodo del tablero, lo que se puede hacer a partir de ah es exactamente lo mismo y lleva a los mismos resultados. As que lo que podemos hacer para resolver el problema, sin necesidad de abandonar an nuestro modelo del rbol de decisiones, es realizar una bsqueda en amplitud teniendo en cuenta qu acomodo de tablero genera cada subsolucin; y si dicho acomodo de tablero ya fu visitado antes, entonces ignorar dicho nodo. Podemos hacer eso ya que en la bsqueda en amplitud la primera vez que encontremos cada acomodo de tablero tendremos la certeza de que lo

Si varias de esas subsoluciones usan el mismo nmero de movimientos,

habremos encontrado con la menor cantidad de movimientos(todos los nodos que se recorran despus estarn igual o mas lejos de la raz). Finalmente, para implementar esta solucin lo que necesitamos es una cola(para la bsqueda en amplitud), una manera de representar cada nodo, y una manera de marcar los acomodos o permutaciones ya visitadas. La informacin relevante de cada nodo viene dada por dos cosas: la subsolucin(los movimientos realizados) y la permutacin de casillas a la cual se lleg. Aunque la primera cosa implique la segunda, recordemos que el problema solamente nos pide el nmero de movimientos realizados, entonces basta con que guardemos la permutacin de casillas y el nmero de movimientos realizados. Es tentador representar cada permutacin por un arreglo de 9 enteros, sin embargo, para marcar la permutacin como visitada resultara mucho mas prctico el

nmero de permutacin,

es decir, si se ordenaran todas las

permutaciones lexicogrcamente, cuntas permutaciones seran menores que una permutacin en particular?.

19.2. JUEGO DE NMEROS


Nmero de Permutacin

249

Averigar el nmero de permutacin es un problema interesante, el cual sugiere el uso de diversas estructuras de datos para permutaciones de muchos elementos, sin embargo como tenemos solamente 9 elementos, difcilmente una estructura de datos mejorara el tiempo de ejecucin de dicho algoritmo. cul es el nmero de permutacin de dicho cuntas permutaciones hay menores que

P = (7, 3, 2, 6, 9, 5, 8, 1, 4)? P?

o mejor

Los siguientes dos prrafos describen algo verdaderamente sencillo de pensar pero difcil de redactar de manera precisa, la idea trata de dada una permutacin notar que profundo. Vamos a llamar C (p) al mayor prejo comn entre p y P . Por ejemplo C (7, 1, 2, 3, 4, 5, 6) = (7), C (7, 3, 2, 1, 4, 5, 6) = (7, 3, 2). Y sea C1 (p) el prejo de p cuya longitud excede en 1 a la longitud de C (p)(notamos que no tiene sentido hablar de C1 (P )). Por ejemplo C (7, 3, 2, 1, 4, 5, 6) = (7, 3, 2, 1) Ahora, consideremos que p es una permutacin cualquiera de los nmeros de 1, ..., 9 tal que p = P . Vamos a llamar m a la longitud de C1 (p). Tenemos que p es menor que P s y solo s. C1 (p) es menor que el prejo de tamao m de P . Notemos que si una permutacin p empieza con 1, 2, 3, 4, 5 6 entonces la permutacin p es menor que P , y adems hay 8! permutaciones que inician con cada uno de estos nmeros. Si una permutacin empieza con mutacin es menor que

p,

tomar el menor prejo de

que no sea prejo de

y luego

es menor que

s y solo s estos dos prejos son distintos. El

lector deber leer con cuidado lo siguiente pero sin miedo ya que no es nada

y adems hay

(7, 1) 7!

o con

(7, 2)

tambin esa per-

permutaciones que inician con

cada uno de estos prejos. Continuando con este razonamiento, la permutacin tambin ser menor si inicia con

(7, 3, 1) 4

y hay

6!

permutaciones que cumplen esto.

El siguiente nmero en la permutacin es prejos de tamao

6,

parece que encontraremos

tales que sus primeros 3 elementos sean

sean prejos de puras permutaciones menores que ya que solamente existen 3 prejos: Lo que sucede ahora es que los

7, 3 y 2 y que P . Pero la realidad es otra, (7, 3, 2, 1), (7, 3, 2, 4) y (7, 3, 2, 5). prejos no pueden terminar en 2 ni en

ya que repetiran nmeros en la permutacin, as que debemos cuidarnos Es decir, si una permutacin

tambin de no contar los nmeros que se repiten.

p es menor que P y m es la longitud de C1 (p), C1 (p) no puede estar en C (p) ni tampoco ser mayor o igual que el msimo nmero de P , es decir, los nmeros aceptables son aquellos que no estan en C (p) y son menores que el msimo nmero de
entonces, el ltimo nmero de

250CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

P.
Para contar aquellos nmeros aceptables para una longitud de prejo determinada, vamos a denir una funcin y 9,

donde par un entero

entre 1

M (n)

representa la cantidad de nmeros que no aparencen en el prejo

de tamao

de

y que son menores que el

Por ejemplo, ya vimos que Por lo tanto el nmero de

nsimo nmero de P . M (1) = 6, M (2) = 2, M (3) = 1 y M (4) = 3. permtuaciones menores que P es:

M (9)0! + M (8)1! + M (7)2! + M (6)3! + M (5)4! +M (4)5! + M (3)6! + M (2)7! + M (1)8!


Es interesante notar que si bien nos motivamos en el valor de la permutacin de

para obtener esta frmula, la frmula no depende realmente de los

valores de la permutacin, solamente estamos suponiendo que mutacin de los nmeros enteros de 1 a 9. de

es una per-

Si aplicaramos un algoritmo anlogo para una permutacin de los enteros 2 a N , el algoritmo sera O (N ), puesto que evaluar la funcin M sera

lineal y habra que evaluarla

veces. Sin embargo, como aqu

siempre

vale 9, esta particularizacin del algoritmo es

O(1).

Para representar una permutacin usaremos un arreglo de 9 caractres, ya que los caracteres gastan menos memoria que los enteros. A continuacin se muestra una implementacin del algoritmo que encuentra el nmero de permutacin en una funcin llamada 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 } } } r+= M f a c t o r i a l ; }

menores:

int

menores (

int int

char

perm [ ] ) { k, r;

i , M,

f a c t o r i a l =1;

for ( i =0; i <=8; i ++){ i f ( i !=0)


f a c t o r i a l = i ; M =perm [9 i

r =0;

for ( k =0; k<9 i ; k++){ i f ( perm [ k]<=perm [9 i 1 ] ) {


M ;

1];

return

r;

19.2. JUEGO DE NMEROS


Implementacin

251

Volviendo al problema original, nos disponemos a usar una bsqueda en amplitud, donde cada elemento de la cola constar de una permutacin representando un acomodo del tablero y el tamao de la subsolucin mas corta que llega a dicho acomodo, tambin ser necesario un arreglo con elementos para marcar los tableros que ya se lograron formar. En la bsqueda en amplitud se visitaran solo aquellos nodos del rbol de deciciones que formen un acomodo del tablero que no se haya podido formar anteriormente. Dado que nunca habr mas de

9!

9!

elementos en la cola, podemos imple-

mentar la cola con un tamao mximo de mentar de la siguiente manera: 1 2 3 4

9!, cada elemento de la cola consta

de un arreglo de 9 caracteres y un entero. Por lo tanto la cola se pude imple-

char ColaPerm [ 9 8 7 6 5 4 3 2 1 ] [ 9 ] ; int ColaMov [ 9 8 7 6 5 4 3 2 1 ] ; int C o l a I n i c i o =0; int C o l a F i n =0;


El arreglo para marcar los tableros ya formados puede ser usado tambin para guardar cuntos movimientos se necesitaron para formarlo, puede ser declarado asi:

int

MinMov [ 9 8 7 6 5 4 3 2 1 ] ; valdr 0 cuando la permutacin no se haya visitado, y valdr

MinMov

el nmero de movimientos ms uno cuando la permutacin se haya visitado. Esto de sumarle 1 se debe a que la permutacin inicial se forma con 0 movimientos. Resulta conveniente marcar el arreglo MinMov dentro de la funcin para encolar: 1 2 3 4

void

encolar (

int m=m e n o r e s ( a c t u a l ) ; i f ( MinMov [m]==0) {

char

perm [ ] ,

int

movs ) {

memcpy ( ColaPerm [ C o l a F i n ] ,

ColaFin ] e l c o n t e n i d o de perm
5 6 7 8 9 } } ColaMov [ C o l a F i n ]= movs ; C o l a F i n ++; MinMov [m]=mov ;

sizeof ( int ) ) ; // Copiar a ColaPerm [

perm ,

252CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

Y para desencolar as: 1 2 3 4 } La funcin para desencolar regresa el nmero de movimientos y copia la permutacin a un arreglo dado. Notamos tambin que una rotacin en sentido antihorario es equivalente a 3 rotaciones en sentido horario, por lo tanto solamente es necesario implementar la rotacin en sentido horario: 1 2

int

desencolar ( ));

char

perm [ ] ) { ColaPerm [ C o l a I n i c i o ] , 9

memcpy ( perm ,

sizeof ( int

return

ColaMov [ C o l a I n i c i o ++];

void

rota (

char perm [ ] , int f i l a , int c o l ) { char a , b , c , d ; // Estas v a r i a b l e s

4 5 6 7 8 9 10 11 }

apuntaran a l a s p o s i c i o n e s de l a s 4 c a s i l l a s que se van a r o t a r char t a , tb , t c , t d ; // Estas v a r i a b l e s guardarn l o s v a l o r e s i n i c i a l e s de l a s 4 casillas f i l a ; c o l ; //Cambia l a f i l a y columna de i n d i c e s basados en 1 a i n d i c e s basados en 0
a=&perm [ f i l a

b=&perm [ f i l a

3+ c o l ] ; 3+ c o l + 1 ] ; c=&perm [ ( f i l a +1) 3+ c o l ] ; d=&perm [ ( f i l a +1) 3+ c o l + 1 ] ; t a = a ; t b=b ; t c = c ; t d=d ; //Guarda e l e s t a d o a=t c ; b=t a ; c=t d ; d=t b ; // Rota en s e n t i d o

i n i c i a l en v a r i a b l e s t e m p o r a l e s

horario

Una vez implementadas todas estas funciones la bsqueda en amplitud es algo muy sencillo: 1 2 3 4 5 6 7

while ( MinMov [ 0 ] = = 0 ) {
movs=d e s e n c o l a r ( a c t u a l ) ;

encolar ( actual ,

1) ;

for ( i =1; i <=2; i ++){ for ( k =1; k<=2;k++){


rota ( actual , i , k) ; movs+1) ; encolar ( actual ,

Rotacion h o r a r i a

//

19.3. EMPUJANDO CAJAS


8 9 10 11 rota ( actual , rota ( actual , i , i , k) ; k) ; movs+1) ;

253

Rotacin a n t i h o r a r i a r o t a ( a c t u a l , i , k ) ; // Despues de 4 r o t a c i o n e s l a permutacin i n i c i a l r e g r e s a a como e s t a b a a l i n i c i o


} } } El cdigo completo de esta solucin(incluyendo la cabecera, la lectura y la escritura) mide menos de 80 lneas.

encolar ( actual ,

//

12 13 14

19.3. Empujando Cajas


El siguiente problema se us en el 2005 en la Competencia Iberoamericana de Informtica por Correspondencia. Seguramente ya conoces un videojuego clsico que trata de que hacer que un obrero empuje cajas para colocarlas sobre una marcas dibujadas en el suelo. Considera un juego de similar en el cual el obrero debe empujar solamente una caja a traves de un cuarto hacia una ubicacin determinada. El cuarto est representado por una cuadricula de 5 x 5 rodeada de una pared perimetral, dentro de la cuadricula hay dos tipos de casillas: las casillas libres y las casillas con pared. Adems de esto, dentro del cuarto se encuentra el obrero y la caja, siempre en casillas diferentes. El obrero puede moverse una casilla al norte, sur, este u oeste, siempre y cuando no ocupe la posicin de una pared ni la posicin de la caja. En ciertas ocaciones el obrero puede empujar la caja pero no tiene sucientes fuerzas para tirar de ella. El acto de empujar la caja se puede ejercer cuando el obrero est en una casilla vecina(horizontal o verticalmente pero NO en diagonal) a la casilla de la caja que empuja con la restriccin de que la caja nunca puede ocupar una casilla con pared. Luego de empujar la caja, el obrero pasa a ocupar la casilla que antes ocupaba la caja y la caja se mueve una casilla en la misma direccin que el obrero.

254CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

00

01

10

11

000

001

010

011 ...

100

101

110

111

Figura 19.6: Los primeros 4 dgitos del rbol de decisiones de Empujando Cajas

Problema Resuelto 19.3.1.

Escribe un programa que dado el mapa del

cuarto, determine el nmero mnimo de pasos para que el obrero coloque la caja en la ubicacin dada realizando el mnimo nmero de pasos. Donde un paso se dene como la accin de moverse de una casilla a una casilla vecina. El mapa del cuarto se representa por una cuadrcula de 7 por 7 caracteres donde: El caracter X indica pared El caracter O(no cero) indica la ubicacin del obrero El caracter C la ubicacin de la caja. El caracter D el lugar de destino de la caja. El caracter .(ASCII 46) casilla libre. El programa deber funcionar en menos de un segundo.

Estructura de la Solucin
Nuevamente nos debemos de preguntar: qu es lo que nos est pidiendo el problema?. La respuesta es el nmero mnimo de pasos. Procederemos como en la seccin anterior, pensando en la solucin como una secuencia nita de pasos, cada paso puede ser representado solamente por un entero del 0 al 3 indicando la direccin.

19.3. EMPUJANDO CAJAS

255

As que diremos que las soluciones de este problema son aquellas secuencias nitas de pasos(representadas por enteros del 0 al 3) tales que hacen que el obrero empuje la caja hasta su posicin de destino y que tienen longitud mnima. En este problema cada subsolucin ser tambin una secuencia nita de pasos pero puede no llevar la caja a su lugar de destino. Por lo tanto el rbol de decisiones lo podemos ver como un rbol enraizado en el cual la raz es una cadena vaca y cada nodo representa una sucesin nita con los enteros del 0 al 3. La gura 19.3 muestra los primeros 4 niveles(incluyendo la raz) de este rbol de decisiones. Nuevamente podemos darnos cuenta de que el rbol de decisiones crece bastante rpido, con tan solo 10 niveles hay mas de un milln de nodos y en 15 niveles hay mas de mil millones de nodos.

Mejorando la Solucin
En la seccin anterior se resolvi el problema del juego de dgitos, en el cual salimos del apuro de tener un espacio de bsqueda demasiado grande dndonos cuenta que haba varias manera de llegar a un mismo acomodo del tablero y una vez que se llega a un acomodo del tablero no importan qu movimientos se hicieron para llegar ah sino cuntos movimientos se hicieron. Trataremos de aplicar un razonamiento parecido aqu. Nuestro acomodo no es simplemente el lugar donde se encuentra el obrero, ya que el obrero puede mover la caja, pasar por su posicin original y volver a mover la caja despus. Dos subsoluciones pueden ser escencialmente distintas an cuando el obrero se encuentre en la misma posicin. Sin embargo, si para dos casillas cualesquiera colocar la caja en

(a, b)

hay varias formas de

y que el obrero se mueva despus a

b,

sin importar cual

de todas esas formas us el obrero para hacer eso. Las maneras de llevar la caja a su destino no cambian. Es decir, para cualquier par de casillas tales que

(a, b)

si dos subsoluciones

a,

se

|x| < |y |, que llevan tiene que y no es prejo

al obrero a la casilla

y a la caja a la casilla

de ninguna solucin, puesto que si es posible

llevar la caja a su destino, no la llevara en el mnimo nmero de pasos. Vamos a denir entonces un estado como un par de casillas pasos de la subsolucin, la caja termina en la casilla en la casilla

(a, b) y vamos

a decir que una subsolucin pertenece a dicho estado si luego de ejecutar los

y el obrero termina

b.

Asi que haremos una bsqueda en amplitud en el rbol de decisiones, cada que se visite una subsolucin, revisaremos si el estado al cual pertenece no haba sido generado antes antes, y si haba sido generado no visitaremos

256CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

esa subsolucin,de esta manera todos los estados se generarn unicamente en el nodo mas cercano a la raz, es decir, para cada estado encontraremos una de sus subsoluciones mas cortas, y solamente una. La solucin podra ser cualquiera de las subsoluciones la posicin de destino de la caja.

(a, D)

donde

es

Implementacin
Como se haba mencionado anteriormente, vamos a hacer una bsqueda en amplitud en el rbol de decisiones. El problema solo nos pide la longitud de la subsolucin y no la solucin completa, entonces no es necesario guardar en la cola todo sino solamente el tamao de la subsolucin encontrada y el estado que genera dicha subsolucin. Como habamos dicho antes, una subsolucin es un par de casillas. Pero necesitamos representar las casillas de alguna manera. Es posible representarlas por un par de enteros indicando la la y la columna, pero como sabemos que el mapa es de 7x7 entonces sera mas sencillo numerar las casillas de 0 a 48 y representar cada casilla por un solo entero. Digamos que las casillas de la primera la tienen nmeros del 0 al 6, de la segunda la del 7 al 13, etc. Si en cada la a cada casilla le asignamos los nmeros en forma creciente de izquierda a derecha tendremos que a una casilla ubicada en

f -sima la y la c-sima columa, le correspondera el nmero 7(f 1)+(c 1). Esto sugiere que es conveniente tanto en las como columnas
Por lo tanto, dado el nmero de casilla, obtener la la y la columna es particularmente fcil:

asignarles nmeros de 0 a 6.

1 2 3 4 5 6

int
}

obtenFila (

return return

int

casilla ){

c a s i l l a /7;

int

obtenColumna (

int

casilla ){

c a s i l l a %7;

} Y dada la la y la columna, obtener el nmero de casilla tambin es fcil:

1 2 3

int
}

obtenCasilla (

return

int

fila ,

int

co lumn a ) {

7 f i l a +col umn a ;

Usaremos un arreglo llamado

Mejor

para guardar la longitud de la sub-

solucin mas corta para generar cada estado. La cola, como ya se haba

19.3. EMPUJANDO CAJAS

257

mencionado, debe de guardar un estado y la longitud de la subsolucin, es decir, tres enteros. Para representar los movimientos al norte, sur, este y oeste, vamos a usar un par de

vectores de direccin, es decir, dos arreglos Df


f
y columna

alguna casilla en la 0 a 4.

casilla vecina a la casilla original, indicando

c, f + Df [i] y i la direccin

Dc tales que para c + Dc[i] representen una


y con un entero de

As que estas son las estructuras que se denirn en el problema: 1 2 3 4 5 6 7 8 9 10

int int int int int int int int

Mejor [ 4 9 ] [ 4 9 ] ; ColaA [ 4 9 4 9 ] ; ColaB [ 4 9 4 9 ] ; ColaTam [ 4 9 4 9 ] ; ColaInicio ; ColaFin ; Df [ ] = { 0 , Dc [ ] = { 1 , 1, 0, 0,

1};
0};

1 ,

Para encolar un estado, en lugar de usar como parmetros las dos casillas y la longitud de la subsolucin, resulta mas cmodo pasar la la y la columna de cada una de las casillas. 1 2 3 4 5 6 7 8 9 10 11 12 } Al nal la bsqueda en amplitud queda de esta manera: 1 2 3 4 }

void

encolar (

int

int
b;

f1 ,

int

c1 ,

int

f2 ,

int

c2 ,

int

len ){

a,

a=o b t e n C a s i l l a ( f 1 , b=o b t e n C a s i l l a ( f 2 ,

c1 ) ; c2 ) ;

i f ( M e j o r [ a ] [ b ]==0) { // Si e l e s t a d o no h a b a
s i d o encontrado a n t e s
M e j o r [ a ] [ b ]= l e n ; ColaA [ C o l a F i n ]= a ; ColaB [ C o l a F i n ]= b ; ColaTam [ C o l a F i n ]= l e n +1; C o l a F i n ++;

void

amplitud (

int int int

int
c; i ;

O,

int
f2 ,

C) { c2 , len ;

f1 , f , a,

c1 ,

258CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

5 6 7 8 9 10 11 12 13 14 15 16

e n c o l a r ( o b t e n F i l a (O) , o b t e n F i l a (C) ,

obtenColumna (O) , 0) ;

while ( C o l a I n i c i o != C o l a F i n ) {

obtenColumna (C) ,

f 1=o b t e n F i l a ( ColaA [ C o l a I n i c i o ] ) ; c 1=obtenColumna ( ColaA [ C o l a I n i c i o ] ) ; f 2=o b t e n F i l a ( ColaB [ C o l a I n i c i o ] ) ; c 2=obtenColumna ( ColaB [ C o l a I n i c i o ] ) ; l e n=ColaTam [ C o l a I n i c i o ] ; C o l a I n i c i o ++;

for ( i =0; i <4; i ++){


f=f 1+Df [ i ] ; c=c 1+Dc [ i ] ;

i f ( Mapa [ f ] [ c ] ! = 'X ' ) { // Si e l

17 18

o b r e r o se mueve a una p o s i c i n donde no hay pared i f ( f==f 2 && c==c 2 ) { // Si empuja l a c a j a


] ] [ c 2+Dc [ i

i f ( Mapa [ f 2+Df [ i
] ] ! = 'X ' ) { // Si l a c a j a se mueve a una p o s i c i n donde no hay pared

19

encolar (f , c, f 2+ Df [ i ] , c 2+ Dc [ i ] , len +1) ;

20 21 22 }

else { // Si se mueve a
suelo l i b r e
encolar ( f , f2 , c2 , c, len

19.4. CAMINO ESCONDIDO


+1) ; 23 24 25 26 27 } } } } }

259

El programa completo que resuelve este problema posee poco menos de 100 lneas.

19.4. Camino Escondido


Este problema apareci en un examen preselectivo para la IOI del ao 2007, el contexto del problema es el siguiente: Imagina que estas a punto de entrar en la pirmide que antecede a la sala donde se encuentran los legendarios tesoros de los mayas, y en la puerta esta escrito un extrao jeroglico el cual se traduce de la siguiente manera:

Aqui no pueden entrar seres que no sean descendientes de nuestra gloriosa civilizacion, para demostrar que por tus venas corre sangre maya debers demostrar tus habilidades para rastrear el camino siguiendo un patron que se repite hasta llegar a la salida
El mapa de la piramide tiene forma de cuadricula y mide

N,

donde

N 50,

los movimientos pueden ser de manera vertical u horizontal, pero

no estan permitidos movimientos en diagonal. La entrada a la cmara esta representada por un 1 y se encuentra en la parte superior, la salida tambien esta representada con un 1 pero se encuentra en la parte inferior, el patron que debes de seguir debe de coincidir con los numeros que estan dibujados en el suelo Por ejemplo considera el siguiente mapa:

2 8 2 8 8

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

5 2 9 6 5

Si el patron a seguir fuera negritas).

(3, 5, 8) el nmero mnimo de pasos para llegar

de la entrada a la salida seria 8 (en la gura el camino est resaltado con

260CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

Problema Resuelto 19.4.1.


llegar a la salida.

Escribe un programa que dado un mapa de la

pirmide encuentre el nmero mnimo de pasos para desde la entrada hasta El programa deber funcionar en menos de un segundo.

Estructura de la Solucin
De igual manera que en los problemas anteriores, la pregunta que todo lector se debe hacer es:

qu estoy buscando?.

Para contestar correctamente la pregunta es necesario haber ledo con atencin el problema y comprenderlo correctamente. En este caso especco, estamos buscando el camino vlido ms corto entre la celda A y la celda B. Ya sabemos lo que estamos bscando. Cul es el siguiente paso? Por supuesto, algo que parece obvio y sin embargo poca gente hace; preguntarse Cmo es lo que estoy buscando? Para cuestiones de problemas de informtica esto se traduce: Cmo se representa lo que estoy buscando? Cmo se representa lo que estoy buscando? Para este problema, un camino se representa como una secuencia de celdas contiguas, ya sea de manera horizontal o vertical, de la matriz, que inicia en la celda en la celda

A y termina

B.

Una vez especicado el objeto que buscamos, surgen varias otras preguntas interesantes sobre las caractersticas del mismo. Estas preguntas se aplican a casos particulares y no es sencillo generalizarlas como las dos anteriores, sin embargo, basta decir que una vez especicado el objeto de la bsqueda deben intentar conocerse la mayor cantidad de caractersticas posible del mismo. Un punto importante para el problema que estamos investigando es: Cul es el largo mximo que puede tener un camino? Inicialmente podramos pensar que el largo mximo posible es el nmero de celdas en la matriz, es decir,

N.

Pero leyendo el problema con atencin podremos observar que nunca se dijo que el camino no poda pasar 2 veces por la misma celda. Eliminada esta limitante vemos entonces que el largo mximo de un camino puede ser mayor que el nmero total de celdas de la matriz. De hecho, el largo mximo de un camino puede ser MUUUY grande. Muy bien, ya sabemos que estamos buscando, tambin sabemos como es. Sigamos adelante, aqui viene la pregunta ms importante hasta el momento:

en donde debemos buscarlo?

En donde debemos buscar? Esta es la pregunta clave en cualquier problema de bsqueda y su correcta respuesta es lo que nos denir el espacio de bsqueda. Volvamos a nuestro problema, la primera opcin que se ocurre es

19.4. CAMINO ESCONDIDO

261

construir todos los caminos posibles (vlidos e invlidos) y entre ellos buscar el que queremos. Dado que no denimos una cota superior para el largo mximo del camino, la cantidad de caminos que podemos construir es hasta el momento innita, y aunque las computadoras son muy rpidas, eso es insuciente para buscar entre un nmero innito de caminos.

Equivalencia de Subsoluciones
Una vez denido un primer espacio de bsqueda, el siguiente paso es recortarlo lo ms posible hasta llegar a una cardinalidad manejable. Para nuestro problema hay dos recortes inmediatos: En vez de construir todos los caminos, construyamos nicamente los que sean vlidos. Este puede parecer un buen recorte, sin embargo, al no conocer los datos de entrada de antemano, no podemos estar seguros de que sirva de algo. Un caso de prueba planeado cuidadosamente puede hacer que el nmero de caminos vlidos sea tan grande que para nes prcticos buscar entre ellos sea como buscar en un nmero innito de opciones. Dado que queremos el camino ms corto, hay que buscar entre todos los caminos vlidos empezando por los de menor longitud. Este recorte, a menos que el nico camino sea el ms largo posible, obviamente nos ahorra algo. Pero de nuevo, al no saber que tan largo es el camino que buscamos, no podemos estar seguros de que lo encontraremos en un tiempo corto. Hemos avanzado algo, sin embargo, an no llegamos a un punto en donde podamos asegurar una correcta solucin en tiempo para cualquier instancia posible del problema. Es muy importante siempre calcular el tamao del espacio de bsqueda y pensar que en el peor de los casos nuestro programa va a tener que buscar en la totalidad del espacio, piensen en el caso en el que no haya solucin, para estar seguros tenemos que buscar entre todas las opciones, de modo que requerimos que el tamao del espacio sea tal que an examinndolo completamente nuestro programa termine en el tiempo establecido. Para nuestro problema, dado que un camino puede ser una solucin, una subsolucin sera un prejo del camino. En los dos problemas anteriores hemos logrado exitosamente encontrar cierta alguna de ellas es sujo vlido para la otra).

redundancia

en las subsolu-

ciones (es decir, hay pares de soluciones tales que no todo sujo vlido para

262CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

A esta

redundancia

de subsoluciones la llamaremos equivalencia. La idea

es que dos subsoluciones son equivalentes si la forma de llegar desde ellas a la solucin bscada es exactamente la misma. Para denir la equivalencia de subsoluciones de una manera mas precisa, vamos a cambiar nuestro concepto de solucin. Diremos que una solucin es aquella secuencia de decisiones que lleve al resultado deseado, sin importar el nmero de decisiones que se tomen, es decir, solamente descartamos que una solucin es necesariamente el camino mas corto; en este problema vamos a aceptar como solucin cualquier camino. De esa manera lo que buscamos ahora es una solucin tal que su longitud sea la mas corta. Una vez aclarado esto estamos listos para denir la equivalencia de subsoluciones:

Denicin 19.4.1 (Equivalencia de Subsoluciones).


ciones, se dice que

Sean

a y b dos subsolu-

son equivalentes si y solo s:

S tal que S resulta de la concatenacin de a con alguna c(es decir, S = (a1 , a2 , ..., an , c1 , c2 , ..., cm ) ), se tiene que la concatenacin de b con c tambin es una solucin.
Para cualquier solucin Para cualquier solucin

tal que

es la concatenacin de

b con alguna

c,

se tiene que la concatenacin de

con

tambin es una solucin.

De qu nos sirve tener sub-soluciones equivalentes? En realidad eso depende un poco del problema especco, sin embargo, en la mayora de los casos el benecio viene de que dos sub-soluciones se pueden comparar de manera directa y en base a eso decidir cual de las dos es ptima dados los criterios que buscamos. Notamos que si

son dos subsoluciones equivalentes, tambin

tambin son equivalentes. Si con

a es equivalente con b, y b es equivalente con c, entonces a es equivalente


demostracin surge directamente de la denicin anterior).

c(la

Adems, toda subsolucin es equivalente consigo misma. Estas 3 propiedades fueron las que utilizamos en la conectividad de los grafos para ver cmo un grafo se poda particionar en componentes conexas. Nos vemos tentados a denir algo totalmente anlogo a las componentes conexas pero sustituyendo las aristas con las equivalencias. Sin embargo esto puede complicar el problema, deniremos algo parecido, lo cual llamaremos estados.

19.4. CAMINO ESCONDIDO


Estados

263

La idea de los estados es poder particionar adecuadamente el espacio de bsqueda. Es decir, cada estado es un conjunto de subsoluciones y ninguna subsolucin est en mas de un estado, todo esto de manera que cada decisin que se tome, o cada paso que se d, o como se le quiera llamar al hecho de descender un nivel en el rbol de decisiones sea equivalente a cambiar de un estado a otro. Tambin se requiere que todas las subsoluciones que pertenezcan a un mismo estado sean equivalentes(obsrvese que puede que subsoluciones asociadas a estados distintos tambin sean equivalentes). Hay que aclarar que existen muchas formas vlidas de denir los estados, pero todas deben de cumplir con esas condiciones que se acaban de mencionar. Aterrizando un poco, en el problema de camino escondido, Cules seran dos sub-soluciones equivalentes? Dijimos anteriormente que una subsolucin es cualquier prejo de un camino solucin, como un camino es una sucesin de celdas de la matriz, entonces una sub-solucin se denira como una sucesin de celdas en la matriz que va de la celda

a la celda

Q. S, Q
adems se encuentra en

La solucin debe de seguir los nmeros especicados por la secuencia de modo que nuestra subsolucin que termina en una cierta posicin de la secuencia

a la que llamaremos

Supongamos ahora que conocemos la solucin desde tonces todas las subsoluciones que terminen en su camino hacia

hasta B , en(Q, p) son equivalentes ya que

p. (Q, p)

es el mismo sin importar la manera en la que llegaron a

(Q, p).
Para nuestro problema, lo mas natural es identicar los estados por el par ordenado

(Q, p).

Aunque haya estados para los cuales no hay subsoluciones

asociadas(es decir, estados vacos), eso no representa ninguna dicultad. Queremos el camino ms corto. Si tenemos dos subsoluciones que llevan al estado

(Q, p),

su camino ms corto hacia la celda

es el mismo, por lo

tanto, podemos decir que la subsolucin cuya longitud sea menor entre las dos es mejor para nuestros propositos, si ambas son de la misma longitud, entonces podemos simplemente tomar cualquiera de las dos, ya que, a n de cuentas, son equivalentes. Esto nos sugiere hacer una bsqueda amplitud en los estados. Si el lector regresa a las secciones anteriores y mira los cdigos de las bsquedas en amplitud, se dar cuenta que realmente lo que se hizo fue una bsqueda en amplitud con los estados. Habiendo visto lo que son los estados y las subsoluciones equivalentes

264CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

estamos listos para llegar a nuestra denicin nal de espacio de bsqueda:

Denicin 19.4.2 (Espacio de Bsqueda).


donde a los elementos de siguientes propiedades:

Es un par ordenado

los llamaremos estados y los

E = (S, T ), elementos de T los

llamaremos cambios o transiciones de estado. Adems se deben cumplir las

Todo estado es un conjunto de subsoluciones(prejos de solucin). Para todo par de estados disjuntos. Para todo estado y

candidatos
a
y

tales que

a=b

se tiene que

son

e,

si tomamos cualesquiera

s1 , s2 e,

se tiene que

s1

s2

son equivalentes.

Los elementos de

son pares ordenados de los elementos de existen

S,

(e1 , e2 ) T s y solo s de t2 y |t1 | = |t2 | 1.

t1 e1

t2 e2

tales que

t1

es prejo

De las tres propiedades de esta denicin, solo la cuarta propiedad parece perderse un poco en detalles sin mostrar una motivacin adecuada. Esta propiedad se puede recordar simplemente como que dos estados estan conectados s y solo s es posible moverse de uno a otro en solamente un paso. Tambin es conveniente notar que los espacios de bsqueda son grafos dirigidos. As que el proceso de resolver un problema de optimizacin combinatoria se reduce a identicar un espacio de bsqueda de tamao razonable, identicar en ese espacio de bsqueda que es lo que se quiere optimizar(en este problema sera el nmero de transiciones) y dadas esas dos condiciones, elegir cmo recorrer el espacio de bsqueda. Como lo que queremos minimizar es el nmero de transiciones de estado, la bsqueda en amplitud nuevamente nos va a servir. No tenemos que buscar dos veces en un mismo estado, de modo que nuestra bsqueda se limita a la cantidad de estados que existan. Para nuestro caso todas los pares Dado que

(Q, p)

posibles identican los estados.

es una celda de la matriz, existen

posibles valores para

Q. Asi mismo, p representa una posicin en la secuencia S as que la cantidad de valores posibles para p es igual al largo de la secuencia S .
Sustituyendo por los valores mximos posibles tenemos que en el peor caso nuestra bsqueda abarcara un total de

(300)(300)(50) = 4500000

estados, lo

cual funcionar fcilmente en un segundo.

19.4. CAMINO ESCONDIDO


Implementacin

265

Aunque vamos a hacer una bsqueda en un grafo dirigido y anteriormente se habl de la implementacin de grafos con matriz de adyacencia y listas de adyacencia, esta vez nos conviene representar los vrtices como ternas ordenadas de enteros. Segun este planteamiento, una terna contiene los caminos que terminan en la patrn. Para marcar los estados ya visitados y al mismo tiempo guardar el tamao del camino mas corto para llegar a cada estado usaremos un arreglo de 3 dimensiones llamado camino. Por lo tanto hay que declarar el siguiente arreglo: 1 mas corto para llegar al estado

(p, f, c) representar el estado que casilla (f, c) y en la posicin p del

Marca, de manera que Marca[p][f][c] indique el camino (p, f, c), o -1 si no se ha encontrado dicho

int

Marca [ 5 0 ] [ 5 2 ] [ 5 2 ] ;

El motivo para declarar el arreglo de tamao

50

50 x 50 x 50 es para usar la N + 1 como una frontera.

columna 0, la la 0, la

52 en lugar de columna N + 1 y la la


x x

52

Si se utilizan esta nocin de la frontera en el arreglo para marcar los estados, tambin hay que usarla en el arreglo que guarda el mapa: 1

int

Mapa [ 5 0 ] [ 5 2 ] [ 5 2 ] ;

Tambin hay que recalcar que en alguna parte del cdigo hay que inicializar todas las posiciones del arreglo con asignaremos un tamao mximo de 1 2 3 4

1.

Para implementar la bsqueda en amplitud es necesaria una cola, le 503 . ColaInicio , ColaFin ;

int int int int

ColaP [ 5 0 5 0 5 0 ] ; ColaF [ 5 0 5 0 5 0 ] ; ColaC [ 5 0 5 0 5 0 ] ;

Tambin es necesario implementar una funcin para meter un estado a la cola; como nunca se deben de encolar estados ya visitados, suena razonable que la funcin de meter a la cola verique que el estado no haya sido visitado y una vez metido a la cola lo marque como visitado: 1 2 3 4

void

encolar (

int p , int f , int c , int i f ( Marca [ p ] [ f ] [ c ]==1){


ColaP [ C o l a F i n ]= p ; ColaF [ C o l a F i n ]= f ;

w) {

266CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

5 6 7 8 9 } }

ColaC [ C o l a F i n ]= c ; Marca [ p ] [ f ] [ c ]=w ; C o l a F i n ++;

El patrn lo guardaremos en un arreglo llamado 1

Patron:

int

Patron [ 5 0 ] ;

Como se puede apreciar, se pretende guardar datos a partir de la posicin 0 y no a partir de la posicin 1. Los ndices basados en 0 son adecuados para este momento ya que si el ndice de una posicin del patrn fuera el ndice de la siguiente posicin sera

entonces

(p+1) %P.

Al igual que en el ejemplo anterior, este algoritmo habla de movimientos entre cuadros vecinos de una cuadrcula, por lo que es conveniente usar los vectores de direccin aqu tambin: 1 2

int int

Df [ ] = { 0 , Dc [ ] = { 1 ,

1 ,
0,

0,

1}; 0};

1 ,

En este problema en particular, el procedimiento de meter en la cola simplifca las cosas si se implementa en una funcin aislada, pero el procedimiento de sacar de la cola puede implementarse dentro de la bsqueda sin que esto haga el cdigo difcil de manejar. Como queremos que luego de dar el primer paso el estado est ubicado en la primera posicin del patrn, entonces antes de dar el primer paso vamos a considerar que el estado est ubicado en la ltima posicin del patrn. As que la bsqueda en amplitud se puede implementar de la siguiente manera: 1 2 3 4 5 6 7 8 9 10

while ( C o l a I n i c i o != C o l a F i n ) {

e n c o l a r ( P 1 ,

1,

inicioC ,

0) ;

f=ColaF [ C o l a I n i c i o ] ; c=ColaC [ C o l a I n i c i o ] ; p=ColaP [ C o l a I n i c i o ] ; C o l a I n i c i o ++;

for ( i =0; i <4; i ++){


k=Mapa [ f+Df [ i ] ] [ c+Dc [ i ] ] ;

i f ( k==1

||

P a t r o n [ ( p+1) % P]==k ) { f+Df [ i ] , c+Dc [ i ] , Marca [ p

e n c o l a r ( ( p+1) % P, ] [ f ] [ c ]+1) ;

11

19.4. CAMINO ESCONDIDO


12 13 } Las variables }

267

inicioC

indican el tamao del patrn y el nmero de

columna inicial respectivamente. Con esto la solucin del problema est prcticamente completa, solo falta imprimir la salida y leer la entrada; esta parte, debido a que es poco interesante se deja como ejercicio al lector para que verique si comprende bien esta implementacin.

268CAPTULO 19. ESTRUCTURA DE LA SOLUCIN Y ESPACIO DE BSQUEDA

Cap tulo

20

Programacin Dinmica en Los Espacios de Bsqueda


El nombre Programacin Dinmica es un trmino elegante que no tiene mucho sentido en nuestro contexto, bsicamente consiste en guardar en una tabla lo mejor que se puede hacer en cada estado, o dicho de otra manera es aplicar la estrategia Divide y Vencers con una tabla. Generalmente la programacin dinmica se implementa utilizando

while

for

en lugar de recursin.

Al igual que en captulos anteriores, abordaremos el tema explorando ejemplos para posteriormente generalizarlos en un mtodo efectivo.

20.1. Clculo de Fibonacci


En la seccin 2.3 abordamos el problema de los conejos de Fibonacci y se mencion una solucin recursiva de complejidad exponencial, luego en la seccin 3.1 vimos como evitar redudandancia para obtener una solucin de complejidad

O(n).

A pesar de que la complejidad no est mal, sucede que la implementacin se puede volver mucho mas simple y un poco mas rpida, la siguiente funcin calcula el 1 2 3 4 5 6

n + 1-simo

trmino de la sucesin de Fibonacci:

int

F(

int n ) { int F i b [ n + 1 ] ; for ( i =2; i <=n ; i ++){


F i b [ i ]= F i b [ i

Fib [ 0 ] = 0 ; Fib [ 1 ] = 1 ;

1]+ F i b [ i 2 ] ;
269

270CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

7 8 9 }

return

Fib [ n ] ;

Quiz para algunos resulte obvio que este cdigo funciona, pero tal vez para otros no, as que no est por dems mencionar una prueba corta por induccin de que funciona.

n 1 veces. Cuando se ejecuta por primera vez la lnea 6, resulta que F ib[0] y F ib[1] son los primeros 2 trminos de la sucesin de Fibonacci y adems i = 2. Supongamos que est por ejecutarse por k -sima vez la lnea 6 y que F ib[0], F ib[1], ..., F ib[k ] son los primeros k + 1 trminos de la sucesin de Fibonacci, entonces i tendr el valor de k +1 y luego de ejecutarse la lnea 6 tendremos que F ib[k +1] = F ib[k ]+ F ib[k 1] = F (k )+ F (k 1) = F (k ), posteriormente i tomar el valor de k +2 y tendremos que F ib[0], F ib[1], ..., F ib[k +1] sern los primeros k + 2 trminos de la sucesin de Fibonacci. Por induccin, luego de iterar n veces F ib[n] ser el n + 1-simo trmino de la sucesin de
Sabemos que el itera Fibonacci. Esta implementacin elimina aparentemente la recursin. Sin embargo, resultara muy difcil saber qu est haciendo sin conocer la recursin, ya que para llegar a esta implementacin usamos la idea de la implementacin recursiva.

for

Informalmente hablando la programacin dinmica consta de 2 pasos: 1. Encontrar una frmula recursiva que resuelva el problema, comunmente esta frmula es llamada la

subestructura ptima.

2. Calcular la solucin llenando una tabla desde abajo hacia arriba. Algunos autores hablan de un tercer paso llamado reconstruir la solucin, que consiste en encontrar la mejor solucin una vez que se tiene calculada la tabla. El problema con este tercer paso es que funciona bien cuando se trata de problemas de optimizacin combinatoria, pero cuando se trata de problemas de conteo o de probabilidad no tiene mucho sentido hablar de reconstruir la solucin. De hecho con los conejos de Fibonacci algo anlogo a reconstruir la solucin sera imprimir la ascendencia de todos los conejos que se tienen al nal!, lo cual es mucho mas tardado que contarlos. Y por supuesto, el paso de encontrar la subestructura ptima suele ser tremendamente mas difcil que el de llenar la tabla, as que este mtodo de dos pasos no nos ayuda mucho y tenemos que encontrar uno mejor.

20.2. EL LADRN Y LA MOCHILA

271

20.2. El Ladrn y la Mochila


A continuacin abordaremos otro problema que comunmente se toma como ejemplo de programacin dinmica, se le conoce como el problema de la Mochila o

knapsack.
Pi

El problema habla de un ladrn que lleva una mochila con una capacidad

y se encuentra en un lugar con y un volmen

tipos de objetos, donde cada objeto

tiene un precio

Vi ,

tanto el precio como el volmen de cada

objeto son enteros.

Problema Resuelto 20.2.1.


todos los volmenes no exceda

Escribir un programa que dados

C, N , P

determine la suma mxima de precios que puede obtener el ladrn robando cualquier cantidad de objetos de cada tipo de tal manera que la suma de

C. O(CN ).

El programa debe de funcionar en tiempo

Solucin

Al toparse por primera vez con este problema, muchos(incluido

el autor) caen en la ingenuidad de pensar que lo nico que tienen que hacer es precio para cada tipo de objeto y posteriormente tomar calcular una razn volumen aquellos objetos que puedan caber en la mochila y que su razn sea mayor. Este algoritmo, a pesar de que suena coherente, es completamente errneo, por ejemplo, se puede tener una mochila de capacidad objeto con volmen precio

6,

un tipo de

y precio

11,

y otro tipo de objeto con volmen

4.

11 y los de segundo tipo Los objetos del primer tipo tienen una razn de 5 4 de , por lo tanto el algoritmo descrito tomara un objeto del primer tipo 2 y terminara obteniendo una suma de precios de 11, mientras que se puede
hacer algo mejor: tomar 3 objetos del segundo tipo obteniendo un precio total de

12.

Luego de ver este contraejemplo es posible que muchos intenten

parchar

el algoritmo para que pueda funcionar con ese caso; pero la recomendacin del autor es la siguiente: no se deben de intentar parchar algoritmos que esten incorrectos, puede que la idea resulte til para resolver un problema parecido o para acotar, pero intentar generar un algoritmo correcto a partir de casos particulares es algo que generalmente est destinado al fracaso. Habindonos olvidado de el tentador algoritmo incorrecto, abordaremos este problema de una manera perecida a los otros. Podemos observar que si elegimos un objeto de tipo

arreglar

i,

nos quedar un

problema equivalente a llenar una mochila con capacidad de

C Vi .

No necesitamos considerar la opcin de sacar objetos que ya estan dentro de la mochila, puesto que introducir un objeto y luego quitarlo nos deja con

272CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

el mismo resultado que si nunca se hubiera introducido. Si recordamos el tipo de observaciones tiles que se vieron en el captulo anterior, veremos que si tenemos una capacidad restante portar cmo se hayan llenado los primeros estados!. Tambin hay un conjunto de cambios de estado, y es fcil ver cul es, ya que la capacidad restante cambia s y solo s se mete un nuevo objeto a la mochila. As que el conjunto de estados son los objetos que se pueden meter a una mochila de una capacidad dada. Una consideracin que simplica un poco el problema es tomar en cuenta la opcin de desperdiciar espacio. Esto es prctico ya que puede que la mejor solucin no llene toda la mochila. As que otro cambio de estado es desperdiciar una unidad de espacio, si se quiere desperdiciar mas de una unidad basta con desperdiciar varias veces una unidad. Algo que quiz pasamos por alto pero al nal resultar importante es que una mochila de capacidad 0 otorga siempre una suma de precios de 0. Resumiendo y precisando, si llamamos

k,

entonces sin im-

Ck

espacios de la mochila, la

solucin es la misma. Las posibles capacidades restantes son un conjunto de

G(k )

a la mayor suma de precios

que se puede obtener con una mochila de capacidad

tenemos que:

G(k ) = 0 si k = 0 = max({G(k Vi ) + Pi
nos daremos cuenta que

(20.1) con

Vi k }, G(k 1))

si k>0 (20.2)

Lo cual nos deja con una funcin recursiva. Si miramos bien la frmula,

G(k ) est denido solamente en base a los valores de la entrada, a G(k 1) y a G(k Vi ), estas dos expresiones tienen en comn que hacen referencia a G en ndices menores que k. Y tiene sentido,
puesto que en cada que se mete un elemento a la mochila disminuye el espacio restante, y tambin el espacio disminuye cuando se decide desperdiciar. Por tanto una forma de calcular los valores de la funcin calcular valores

es primero

G(0),

luego calcular

G(1),

despus

G(2)

y as sucesivamente. Esto

se puede hacer ya que para calcular

G(k )

solamente se necesitan calcular los

G(h)

con

h < k.

Finalmente obtenemos esta sencilla implementacin: 1 2 3 4 5 6 G[ 0 ] = 0 ;

for ( k =1; k<=C ; i ++){


G [ k ]=G [ k 1 ] ;

for ( i =1; i <=N ; i ++){ i f (V [ i ] > k && G [ k ] <G [ kV [ i ] ] + P [ i ] ) {


G [ k ]=G [ k V[ i ] ] +P [ i ] ;

20.3. PRIMER TEOREMA DEL CMPUTO DE ABAJO HACIA ARRIBA273


7 8 9 } Viendo que el cdigo solamente contiene dos bcles anidados y tomando en cuenta que uno de ellos itera que su complejidad es } }

veces y otro

veces podemos concluir

O(CN ).

De este ejemplo podemos notar que se repiti el hecho de contar con una funcin recursiva con dominio en los enteros de tal manera que el valor de una funcin en determinado entero en enteros menores que

solo dependa de valores de la funcin

k.

Una vez teniendo eso se pudo usar la propiedad de que si se llenaba un arreglo de izquierda a derecha guardando los valores de la funcin, se podan reemplazar las llamadas recursivas por accesos al arreglo.

20.3. Primer Teorema del Cmputo de Abajo hacia Arriba


Despus de haber visto que tanto el problema de Fibonacci y como el de la mochila tienen la propiedad de que una funcin denida de manera recursiva se puede calcular con un ciclo

for,

sera deseable poder saber de

manera precisa cundo se puede hacer esto. Podriamos decir ingnuamente: se aplica en las funciones recursivas tales que el valor de la funcin slo depende de los enteros anteriores. Pero aqu tropezaramos con un error de conceptos: en el captulo 2 se dijo lo que es una denicin recursiva, y el captulo 7 se deni una funcin como un conjunto de pares ordenados, as que no tiene sentido decir que los valores de una funcin dependen de otros ni que la funcin sea recursiva. Claro que una funcin implementada en un lenguaje de programacin puede o no ser recursiva, pero una funcin matemtica se comporta mas como un arreglo que como un proceso. Por ejemplo, podramos terjiversar las cosas y decir que la funcin un arreglo desde

de Fibonacci, no se puede calcular llenando

hasta

ya que

los valores de la funcin en un Para evitar este

caos terico,

F (n) = F (n + 1) F (n 1) por entero n dependen del valor en n + 1.

lo que

vamos a hablar de frmulas. Una frmula

es simplemente una expresin matemtica, a diferencia de las funciones, las frmulas si dependen de la forma en la que son descritas. Por ejemplo, la

274CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

f (x) = x + x el la misma funcin frmulas x + x y 2x no son las mismas.


funcin

que

g (x) = 2x

sin embargo, las

Es posible tambin denir parmetros a las frmulas de una manera parecida a como se hace con las funciones, por ejemplo se puede hablar de la 2 frmula w (x) = x , y cada que se mencione en el texto a w (x) se deber de 2 reemplazar w (x) por x . Siendo estrictos no pueden existir frmulas recursivas, ya que tendramos una expresin innitamente grande con la cual no tendra sentido trabajar. Sin embargo, podemos decir que una funcin est denida de manera recursiva si

f (n)

est bien denido mediante una frmula que contenga a

f.

Por ejemplo sin embargo

H (x) = x para x 1, ..., 10 no H (0) = 0 y H (x) = H (x 1) + 1

es una denicin recursiva, para

x 1, ..., 10

si es una

denicin recursiva. Por tanto hay funciones que pueden ser denidas de maneras recursivas y no recursivas. Tambin es conveniente hablar de una subfrmula, decimos que si una frmula,

es subfrmula de

f , si w

es una subcadena de

f es f . Por ejemplo,

para la frmula

a + b + c + d,

tenemos que

+c

es una subfrmula.

Volviendo al tema, para el cual introducimos las frmulas, queremos generalizar la propiedad encontrada en el problema de Fibonacci y en el problema de la mochila. Si nos jamos bien, los rdenes en los cuales se pueden calcular los valores de la funcin en el arreglo dependen de la frmula que se est usando para calcular la funcin; y lo que tienen en comn ambos problemas, es que el valor de la funcin en un entero

solamente se calcula usando valores de la

funcin en enteros mas pequeos.

k se puede calcular mediante una frmula f (k ) calculando los valores de F (0), F (1), ..., F (k ) en ese rden s y solo s f (k ) realmente caracteriza a F (k ) y para cada subfrmula de f (k ) de la forma F (w ) se tiene que w < k .
As que tenemos que una funcin evaluada en Esta forma de calcular las funciones se llamar clculo cmputo de abajo hacia arriba, el cul se puede denir de la siguiente manera:

Denicin 20.3.1 (Computo de abajo hacia arriba unidimensional).


culo de abajo hacia arriba con una frmula siguiente algoritmo:

El cl-

en un arreglo

se reere al

for(i=0;i<n;i++){ A[i]=f(i); }
Sobra decir que el cdigo escrito solamente representa una manera de muchas de implementar un algoritmo, ya que el algoritmo de clculo de abajo

20.4. TRINGULO DE PASCAL

275

hacia arriba ya se implement de forma distinta en los dos ejemplos anteriores. Ahora, lo nico que queda por decir en esta seccin es cundo se puede aplicar el clculo de abajo hacia arriba unidimensional:

Teorema 36 (Primer Teorema del Cmputo de Abajo Hacia Arriba). Consideremos un conjunto C Z, una funcin F : C R que se quiere calcular y una frmula f que calcula F (es decir F (n) = f (n) para cualquier n C ). Si para toda subfrmula de f (n) de la forma F (w(n)), se tiene que w(n) < n entonces F se puede calcular de abajo hacia arriba con la frmula f .
Puede que muchos lectores sientan que esta seccin les hizo perder mucho tiempo deniendo cosas, sin embargo, adems de precisar el clculo de abajo hacia arriba en arreglos unidimensionales tambin nos dimos cuenta de una importante propiedad: una manera diferente de denir una misma funcin puede cambiar muchas cosas.

20.4. Tringulo de Pascal


Ya en la seccin 3.3 se deni el tringulo de Pascal como una funcin tal que:

P (i, 0) = 1 para i N P (i, i) = 1 para i N P (i, k ) = P (i 1, k 1) + P (i 1, k )

para

i, k N

con

0<k<i

Otra manera de describir este tringulo es como un arreglo triangular de nmeros de manera que cada el nmero de arriba es arriba); es decir:

y cada nmero es la

suma de los dos que tiene arriba(o de uno solo en caso de que solo tenga uno

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

5 10 10 ..........

6 15 20 15 6

276CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

Cualquier persona que entienda cmo est formado este tringulo, lo podra dibujar a mano sin ningn problema y empezara dibujando primero la primer la, luego la segunda la, y as sucesivamente. Esto nos sugiere que el tringulo de Pascal se puede calcular mediante programacin dinmica. Y ciertamente lo nico que tenemos que hacer es calcular las las superiores antes que las las inferiores. Es decir, para calcular una la la la

i 1.

Tomando en cuenta que al calcular una la

i es necesario haber calculado previamente i solamente se est

haciendo referencia a la la

i1

entonces ni siquiera importa el rden en el

cual se procesan las columnas. Por cmodidad procesaremos las columnas en rden creciente: 1 2 3 4 5 6 7 8 } Como resultado obtuvimos nuevamente un cdigo bastante corto. a Este cdigo no debe de ser subestimado, puesto que P (a, b) = , esto b indica que es un excelente algoritmo para calcular coecientes binomiales! ya (N )(N +1) 2 coecientes binomiales en O (N ), el cdigo es que calcula un total de 2 bastante simple y solamente se desborda cuando el resultado nal no cabe en el tipo de datos(si se intenta calcular con factoriales las cosas se complicaran por el hecho de que primero se multiplica y luego se divide). } P[ i ] [ i ]=1; P[ 0] [ 0] = 1 ;

for ( i =1; i <=N ; i ++){


P[ i ] [ 0 ] = 1 ;

for ( k =1; k< i ; k++){


P [ i ] [ k ]=P [ i

1 ] [ k ]+P [ i 1 ] [ k 1 ] ;

20.5. La Mochila (0, 1)


Este otro problema es muy parecido al anterior, lo nico que cambia es que en lugar de exista una cantidad ilimitada de cada tipo de objeto, cada objeto est presente una sola vez. En pocas palabras, hay que decidir, para cada objeto, si dejarlo fuera o dentro de la mochila(de ah el nombre (0, 1)). Siendo mas precisos, y sin hacer referencia al problema anterior, este problema habla de un ladrn que lleva una mochila con una capacidad se encuentra en un lugar con un precio

objetos diferentes, donde cada objeto

C y i tiene

Pi

y un volmen

Vi , tanto el precio como el volmen de cada objeto


Escribir un programa que dados

son enteros.

Problema Resuelto 20.5.1.

C, N , P

20.5. LA MOCHILA (0, 1)

277

determine qu objetos debe robar el ladrn de tal manera que la suma de

todos los volmenes no exceda

y que maximize la suma de los precios.

El programa debe de funcionar en tiempo

O(CN ).

Solucin

Si tomamos en cuenta el captulo anterior podemos concluir que

nos encontramos frente a otro problema de optimizacin combinatoria. Como el nombre del problema lo indica, a cada objeto le podemos asignar un nmero, ya sea 0 o 1, para indicar si ese objeto entra o no entra a la mochila. Adems, no importa el rden en el cual se meten los objetos a la mochila, as que podemos modelar este problema como una secuencia de decisiones: primero decidir si tomar o no tomar el primer objeto, luego decidir si tomar o no tomar el segundo objeto, etc. Como cada decisin la representamos como un 0 o un 1 entonces nuestro conjunto de posibles soluciones son todas las cadenas binarias de longitud

y en nuestro rbol de decisiones cada nodo representa un prejo de la cadena. El siguiente paso es identicar cules subrboles de nuestro rbol de deciciones son idnticos. En estos momentos podramos pensar en varias posibilidades: aquellos subrboles de la misma altura, aquellos que representen un prejo con la misma cantidad de 1s, aquellos en los cuales se hayan elegido objetos con el mismo precio, etc. Hay que hacer notar que todos estos ejemplos mencionados son incorrectos, sin embargo, no es malo examinar cada una de esas posibilidades y darse cuenta por qu son incorrectas. Despus de todo antes de llegar a una idea correcta es natural pasar por muchas incorrectas. Sin entrar en detalle de por qu esas reducciones son incorrectas, simplemente nos concentraremos en una reduccin que si funciona: Si subsoluciones representadas por prejos de la misma longitud toman objetos tales que sumen el mismo volmen entonces sus subrboles correspondientes son idnticos. La prueba de esto es bastante directa: El volumen que le queda disponible a la mochila es el mismo en ambos casos y los objetos que an se pueden tomar son los mismos, por tanto cualquier posible conjunto de objetos que se pueda meter a la mochila en una conguracin tambin es posible meterlo en la otra. Por lo tanto los estados quedan determinados por un par ordenado de enteros

(a, b)

representando la mayor suma de costos posibles utilizando un

subconjunto de los primeros

a objetos y ocupando un volumen b. Tenemos por tanto que si a > 0 hay a lo ms 2 formas posibles de llegar al estado (a, b): desde el estado (a 1, b), si se le asigna 0 al a-simo elemento;

278CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

desde el estado Cuando motivo?, si

(a 1, b Va ), si se le asigna 1 al a-simo elemento. a = 0 se tiene que o bien b = 0 o el estado es inalcanzable, el b > 0 equivaldra a decir que la suma de los volmenes de 0

objetos es mayor que 0. Por lo tanto llegamos a esta frmula:

F (a, b) = = F (0, 0) = F (0, b) =


Donde

max(F (a 1, b Va ) + Pa , F (a 1, b)) F (a 1, b) si a > 0 y b < Va 0 si b > 0

si

a>0

b Va

F (a, b) representa la mejor solucin utilizando un subconjunto de los primeros a objetos y usando un volumen total de b. El hecho de que F (0, b) = cuando b > 0 indica que ese estado no tiene ningn valor
aceptable puesto que es un estado inalcanzable, es decir, indica que cualquier otra opcin es preferible que

pasar

por este estado.

Claro que para objeto de implementacin basta con elegir un nmero negativo muy pequeo para que haga las veces de

Si recordamos cmo se implement el clculo del Tringulo de Pascal en la seccin anterior, podemos ver que estamos en una situacin anloga. Una vez calculados los valores de da la informacin necesaria para calcular los valores de

F (a, 0), F (a, 1), ..., F (a, C ) se tiene toF (a + 1, 0), F (a + F (a, b)
en un arreglo de la forma

1, 1), ..., F (a + 1, C ).
Por tanto si calculamos cada valor de

F [a][b],

basta con ir calculando las las en rden creciente, aqu nos encon-

tramos con una observacin interesante: no importa en qu orden se calculen las columnas, lo nico importante es calcular las las en el rden correcto(creciente). El cdigo mas natural para la solucin es el siguiente: 1 2 3 4 5 6 7 8 9 10 } } F[0][0]=1;

for ( i =1; i <=C ; i ++){


F [ 0 ] [ i ]=INF ;

for ( i =1; i <=N ; i ++){ for ( k =0; k<=C ; k++){ i f (V [ i ]<=k ) { else {
F [ i ] [ k ]=max ( F [ i

1][ k ]

F[ i

1 ] [ kV [

i ]]) ;

F [ i ] [ k ]=F [ i

1][ k ] ;

20.5. LA MOCHILA (0, 1)


11 12 13 } Claro que bien podramos hacer algo como esto: 1 2 3 4 5 6 7 8 9 10 11 12 13 } O peor an: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 } } } } } F[0][0]=1; } } } } F[0][0]=1; } }

279

for ( i =1; i <=C ; i ++){


F [ 0 ] [ i ]=INF ;

for ( i =1; i <=N ; i ++){ for ( k=C ; k>=0;k){ i f (V [ i ]<=k ) { else {


F [ i ] [ k ]=max ( F [ i

1][ k ]

F[ i

1 ] [ kV [

i ]]) ;

F [ i ] [ k ]=F [ i

1][ k ] ;

for ( i =1; i <=C ; i ++){


F [ 0 ] [ i ]=INF ;

for ( i =1; i <=N ; i ++){ do {


k= C/ i ;

i f (V [ i ]<=k ) { else {
F [ i ] [ k ]=max ( F [ i

1][ k ]

F[ i

1 ] [ kV [

i ]]) ;

F [ i ] [ k ]=F [ i k=(k +1) % C;

1][ k ] ;

while ( k !=C/ i ) ;

Pero el primero de estos cdigos es mas natural y mas claro que los ltimos dos.

280CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

20.6. Segundo Teorema del Cmputo de Abajo Hacia Arriba


El Primer Teorema del Cmputo de Abajo Hacia Arriba menciona cmo calcular iterativamente un arreglo con los valores de una recurrencia de un slo parmetro. En las dos secciones anteriores se analizaron problemas que se resolvan llenando una tabla bidimensional inspirandonos en una recurrencia de dos parmetros. El lector podra intentar adivinar que a continuacin formularemos un teorema que nos diga qu hacer cuando se tienen recurrencias de dos parmetros, sin embargo eso no es una buena idea. No podemos formular un teorema para cada cantidad distinta de variables en la recurrencia, necesitamos hacer algo mas general que nos permita atacar problemas con cualquier cantidad de variables. Una manera de hacerlo es transformar un problema de problema de

variables en un

n1

variables, esto nos permitira aplicar el teorema varias

veces para nalmente resolver un problema de una sola variable.

Denicin 20.6.1 (Computo de abajo hacia arriba multidimensional). Supongamos que tenemos una frmula de tamao

que recibe un entero y devuelve un arreglo

n 1. n
dimensiones llamado

Adems tenemos un arreglo de de la siguiente manera:

A. A se dene

El clculo de abajo hacia arriba de la frmula

con el arreglo

for(i=1;i<=n;i++){ A[i]=f(i); }
Recordamos que si de

es un arreglo de

dimensiones

A[i]

es un arreglo

n1

dimensiones.

Esta denicin es algo

extraa,

puesto que en los problemas de progra-

macin dinmica rara vez se utiliza un cdigo as. Nuevamente hay que recalcar que ese cdigo es slo una forma de describir un algoritmo, no es un cdigo que vaya a aparecer en un programa. Cuando se calcul el tringulo de Pascal con programacin dinmica, podemos decir que la frmula

f (i)

era el siguiente cdigo:

P[i][0]=1; for(k=1;k<i;k++){

20.6. SEGUNDO TEOREMA DEL CMPUTO DE ABAJO HACIA ARRIBA281

P[i][k]=P[i-1][k]+P[i-1][k-1]; } P[i][i]=1;
Y que

P [i]

representa

calculando una la del arreglo

A[i], de P, y

esta manera, en cada iteracin se est el cdigo quedara de esta manera:

for(i=1;i<=n;i++){ P[i][0]=1; for(k=1;k<i;k++){ P[i][k]=P[i-1][k]+P[i-1][k-1]; } P[i][i]=1; }


De hecho, siendo realistas, al terminar este captulo no volveremos a usar esta notacin de frmulas y de los teoremas del calculo de abajo hacia arriba. Qu es lo que tienen de especial que hace que valga la pena verlos?, la repuesta es que siempre se usan estos teoremas, pero la gente preere nunca mencionarlos en la explicacin de una solucin para no alargar las cosas y preere utilizarlos para uso propio sin decir explcitamente que los utiliz; al no tener que contarselos a otras personas no necesita denir una notacin como lo hacemos aqu. Volviendo al tema, ocupamos saber cundo podemos calcular una funcin

de varios parmetros de abajo hacia arriba. Si tenemos una frmula

tal que dado el primer parmetro calcula todos

los valores de la funcin con los razonable usar

n1

parmetros restantes, entonces suena

f (i)

dentro de un bucle.

Sin embargo, ocupamos que

g (i) > i,

es decir, que una vez

f (i) nunca haga referencia a F (g (i), ...) donde calculado F (i, ...) pueda calcular F (i + 1, ...).

Todo esto se menciona con precisin en el siguiente teorema:

sideremos un conjunto C Z, una funcin F : C n R que se quiere calcular y una frmula f (i) que calcula F (i, a1 , ..., an1 ) en una tabla A de tal manera que A[i][a1 ]...[an1 ] = F (i, a1 , ..., an1 ) luego de aplicar f (i). Si para toda subfrmula de f de la forma F (g (i), ...) se tiene que g (i) i entonces F se puede calcular de abajo hacia arriba utilizando f .
Con este teorema terminamos de decir con precisin como funciona el clculo de abajo hacia arriba. Pero no tiene mucho sentido aprenderse este teorema sino que hay que verlo como algo bastante natural. Para lograr este objetivo las siguientes secciones mencionarn ejemplos que harn ver que utilizar este teorema es mas fcil que enunciarlo.

Teorema 37 (Segundo Teorema del Cmputo de Abajo hacia Arriba). Con-

282CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

20.7. Robot Recolector


Supongamos que tenemos un robot en la posicin tridimensional de hay

por

por

N.

En cada celda

(1, 1, 1) de (a, b, c) de la

una rejilla cuadrcula

W (a, b, c)

monedas.

Si un robot pasa por una celda, automticamente toma todas las monedas que hay ah. El robot slo se puede mover hacia arriba, hacia la derecha y hacia atrs, es decir, si el robot se encuentra en una celda

(a, b, c) slamente se puede mover hacia las celdas (a + 1, b, c), (a, b + 1, c) y (a, b, c + 1). Luego de varios pasos, el robot debe de llegar a la posicin (N, N, N ).

Problema Resuelto 20.7.1. Solucin


1,
y

Escribe un programa que dados

W,

en-

cuentre la mayor cantidad de monedas que el robot puede tomar.

Si seguimos los pasos del captulo anterior, podemos plantear

primeramente el problema como encontrar una cadena con los caracteres

0,

indicando cada una de las 3 direcciones posibles y luego llegaramos a

la conclusin de que cada estado es una posicin en la rejilla.

(a, b, c) se debe de ingresar directamente desde (a 1, b, c), (a, b 1, c) (a, b, c 1) entonces si F (a, b, c) es la mayor cantidad de monedas que se pueden reunir al llegar a la posicin (a, b, c)
Como para llegar a una posicin tenemos que:

F (a, b, c) = max(F (a 1, b, c), F (a, b 1, c), F (a, b, c 1)) +W (a, b, c) si (a, b, c) = (1, 1, 1) y a, b, c 1 = W (a, b, c) si (a, b, c) = (1, 1, 1) = si a = 0 o b = 0 o c = 0
arriba? y posteriormente cmo se puede lograr eso?

(20.3) (20.4) (20.5) (20.6)

Ahora lo que sigue es preguntarnos: se puede calcular de abajo hacia

A, nuestra intencin es lograr que luego de ejecutar el algoritmo suceda que A[a][b][c] = F (a, b, c), imaginemos que en algn momento durante la ejecucin ya se tiene calculado A[a][b][c] para toda a i, b k y c < h entonces fcilmente podramos calcular A[i][k ][h] de la siguiente manera:
Consideremos un arreglo tridimensional 1 2 3 4 }

i f ( i ==1

&& k==1 && h==1){ || k==0 || h==0){

else i f ( i ==0

A [ i ] [ k ] [ h ]= W[ i ] [ k ] [ h ] ; A [ i ] [ k ] [ h]=INF ;

20.7. ROBOT RECOLECTOR


5 6 7 } }

283

else {
A [ i ] [ k ] [ h ]=max (A [ i ] [ k ] [ h 1 ] , A [ i ] [ k 1 ] [ h ] , ] [ h ] )+ W[ i ] [ k ] [ h ] ; A[ i

1][ k

A[i][k ][g (h)] en el cdigo anterior se menciona de la siguiente manera A[i][k ][h 1] entonces A[i][k ] se puede calcular de abajo hacia arriba as:
Ahora, en el nico lugar donde se menciona algo de la forma 1 2 3 4 5 6 7 8 9 } Como este cdigo calcula de la forma entonces 1 2 3 4 5 6 7 8 9 10 11 } Nuevamente expresiones de la forma como } } } } } } }

for ( h =0; h<=N ; h++){ i f ( i ==1 && k==1 else i f ( i ==0 else {
||

&& h==1){ k==0 || h==0){

A [ i ] [ k ] [ h ]= W[ i ] [ k ] [ h ] ; A [ i ] [ k ] [ h]=INF ; A [ i ] [ k ] [ h ]=max (A [ i ] [ k ] [ h 1 ] , A [ i ] [ k 1 ] [ h ] , A[ i

1][ k ] [ h ] )+ W[

i ][k][h];

A[i][k ]

y la nica parte donde menciona algo

A[i][g (k )] es de la siguiente manera A[i][k ][h 1] A[i] se puede calcular de abajo hacia arriba as:

A[i][k 1][h]

for ( k =0; k<=N ; k++){ for ( h =0; h<=N ; h++){ i f ( i ==1 && k==1 else i f ( i ==0 else {
||

&& h==1){ k==0 || h==0){

A [ i ] [ k ] [ h ]= W[ i ] [ k ] [ h ] ; A [ i ] [ k ] [ h]=INF ;

A [ i ] [ k ] [ h ]=max (A [ i ] [ k ] [ h 1 ] , A [ i ] [ k 1 ] [ h ] , A[ i

1][ k ] [ h ] )+ W[

i ][k][h];

A[g (i)] slo se mencionan en el cdigo

A[i]

A[i 1]

entonces

se puede calcular de abajo hacia arriba de

esta forma: 1 2 3

for ( i =0; i <=N ; i ++){ for ( k =0; k<=N ; k++){ for ( h =0; h<=N ; h++){

284CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

4 5 6 7 8 9 10 11 12 13 } } }

i f ( i ==1
} }

&& k==1 && h==1){ || k==0 || h==0){

else i f ( i ==0 else {

A [ i ] [ k ] [ h ]= W[ i ] [ k ] [ h ] ; A [ i ] [ k ] [ h]=INF ; A [ i ] [ k ] [ h ]=max (A [ i ] [ k ] [ h 1 ] , A [ i ] [ k 1 ] [ h] , A[ i

1][ k ] [ h ] )+ W[

i ][k][h];

Finalmente, luego de ejecutar ese algoritmo la respuesta ser simplemente

A[N ][N ][N ].

Algo interesante con este problema es que no importa en qu rden se acomoden los

en el

for, el cdigo sigue funcionando; por ejemplo, si iteramos sobre bucle exterior y sobre i en el bucle intermedio, el algoritmo sigue

funcionando. Se deja al lector reexionar sobre este hecho y en qu otros casos esto sucede.

20.8. Subsecuencia Comn Mxima


Se dice que una cadena obtener

es subsecuencia de una cadena

si se puede

borrando 0 o ms caracteres de

Por ejemplo

omi

es subsecuencia de

B. olimpiada.

Hay que hacer notar que una cadena vaca tambin se considera una subsecuencia. Una cadena

es subsecuencia comn de

si

es subsecuencia de

tambin es subsecuencia de

b.
Escribe un programa que dadas dos cadenas

Problema Resuelto 20.8.1.


AyB B.

encuentre la longitud de la subsecuencia comn mas grande de

El programa debe funcionar en

O(|A||B |).

20.8. SUBSECUENCIA COMN MXIMA

285

Solucin

Este ejemplo es mas complicado de modelar de la manera tradi-

cional como secuencia de decisiones, sin embargo tambin es posible hacerlo. Supongamos que tenemos una subsecuencia comn a lo que resulta de borrarle el ltimo caractr a es una subsecuencia comn de Vamos a llamar

c,

vamos a llamar

c1

c. m

Tenemos que

c1

tambin

y de

B.
de una cadena

P (s, m)

al prejo de tamao

s.

n es el menor entero tal que c1 es P (A, n) y m es el menor entero tal que c1 es subsecuencia comn de P (B, m) entonces c es subsecuencia comn de P (A, n1 ) y de P (A, m1 ) con n1 > n y m1 > m.
Otra propiedad importante es que si subsecuencia comn de Por tanto podemos pensar en los estados como pares de enteros indicando la longitud de cada prejo de los cuales la cadena es subsecuencia comn. Asi que el problema se puede replantear de esta manera: Creamos una cuadrcula

de tamao

|A| por |B |, y marcamos las casillas

(i, j )

cuando

A[i] = B [j ].

Hay que encontrar la sucesin mas grande de

casillas marcadas que sea creciente en ambas coordenadas. Este problema es similar al de la seccin anterior, es decir, hay un robot en una cuadrcula que slo se puede mover hacia abajo o hacia la derecha, cada casilla tiene un 0 o un 1 y debe de pasar por el mayor nmero de 1s. Sin embargo, cada que el robot pasa por un 1, el robot debe de moverse en diagonal hacia abajo y hacia la derecha al mismo tiempo. Resumiendo:

LCS (i, j ) = LCS (i 1, j 1) + 1 si M [i][j ] = 1 LCS (i, j ) = max(LCS (i 1, j ), LCS (i, j 1)) si M [i][j ] = 0 y min(i, j ) > 0 LCS (i, 0) = 0 LCS (0, i) = 0
Si recordamos de dnde viene evitar tener que calcularlo:

(20.7) (20.8) (20.9) (20.10) (20.11)

podemos sustituirlo en la frmula para

LCS (i, j ) = LCS (i 1, j 1) + 1 si A[i] = B [j ] = max(LCS (i 1, j ), LCS (i, j 1)) si A[i] = B [j ] y min(i, j ) > 0 LCS (i, 0) = 0 LCS (0, i) = 0

(20.12) (20.13) (20.14) (20.15) (20.16)

Ahora lo que hay que notar es que en la frmula anterior, slamente

286CAPTULO 20. PROGRAMACIN DINMICA EN LOS ESPACIOS DE BSQUEDA

aparece una vez una expresin de la forma

LCS (i, g (j )), i

y aparece como

LCS (i, j 1),


1 2 3 4 5 6 7 8 9 } } } }

por tanto cada la

se puede calcular de abajo hacia arriba. es la siguiente:

Una posible implementacin para calcular una la

for ( j =0; j <=l e n B ; j ++){ i f ( i ==0 | | j ==0){ else i f (A [ i ]==B [ j ] ) { else {


LCS [ i ] [ j ]=LCS [ i LCS [ i ] [ j ] = 0 ;

1 ] [ j 1]+1; 1][ j
] , LCS [ i ] [ j

LCS [ i ] [ j ]=max ( LCS [ i

1]) ;

Mirando el cdigo anterior como si fuera una frmula, las formas en las que aparece

LCS [g (i)]...

son las siguientes:

LCS [i 1]

LCS [i],

por tanto

se puede calcular de abajo hacia arriba toda la tabla de la siguiente manera: 1 2 3 4 5 6 7 8 9 10 11 } } } } }

for ( i =0; i <=l e n A ; i ++){ for ( j =0; j <=l e n B ; j ++){ i f ( i ==0 | | j ==0){ else i f (A [ i ]==B [ j ] ) { else {
LCS [ i ] [ j ]=LCS [ i LCS [ i ] [ j ] = 0 ;

1 ] [ j 1]+1; 1][ j
] , LCS [ i ] [ j

LCS [ i ] [ j ]=max ( LCS [ i

1]) ;

LCS [lenA][lenB ]. El lector observador se habr dado cuenta que las cadenas A y B se estn tomando como si los caractres empezaran en A[1] y B [1] en lugar de A[0] y B [0]. Esto no es lo mas comn, pero se escribi as con propsitos de claridad.
Luego, longitud de la subsecuencia comn mas grande ser

Cap tulo

21

Programacin Dinmica en Cortes


En el captulo anterior vimos cmo utilizar la programacin dinmica para resolver problemas que se podan plantear como un camino mas corto(o mas largo) en un grafo dirigido y usamos tablas para poder calcular de manera eciente el mejor camino hacia cada vrtice. Es decir, cada vrtice(al cual llamamos estado) estaba representado por una celda en la tabla, y la operacin de moverse de un vrtice a otro poda ser vista como recorrer una arista en el grafo. Muchos problemas de programacin dinmica se pueden plantear de esta manera, sin embargo no es una receta que funcione siempre que un problema se pueda resolver con programacin dinmica. Existen otra clase de formas de aplicar la programacin dinmica, si el lector recuerda el principio divide y vencers en su forma recursiva, se basa en dividir un problema en copias mas pequeas de s mismo, resolver las copias mas pequeas y nalmente combinar sus soluciones para obtener la solucin al problema original. Un ejemplo de un algoritmo que usa de manera directa este principio es el algoritmo de ordenamiento llamado mezcla(o conoce. El algoritmo de mezcla parte el problema original en dos subproblemas, soluciona los dos subproblemas por separado y posteriormente los une para obtener la solucin al problema original. Este algoritmo tiene la caracterstica de que puede fcilmente dividirse en dos subproblemas sin perder generalidad. Sin embargo, existen problemas que se pueden dividir de varias maneras en copias mas pequeas de s mismo y cada forma de dividirlo produce una solucin distinta. En esos casos no queda otra opcin mas que dividir el problema de todas

mergesort

para muchos); dicho

algoritmo ya se mencion en este libro as que se asumir que el lector ya lo

287

288

CAPTULO 21. PROGRAMACIN DINMICA EN CORTES

las formas posibles, lo cual nos podra llevar a generar una cantidad exponencial de subproblemas, pero afortunadamente en muchas ocaciones aunque haya muchas formas de didividr un problema en copias mas pequeas de s mismo, muchos subproblemas se repiten!. Aqu la programacin dinmica ayuda para calcular en una tabla las soluciones a los subproblemas ya resueltos. A este tipo de formas de aplicar la programacin dinmica le llamaremos informalmente cortes. El primer ejemplo de cortes que veremos ser el problema de cortar cadenas.

21.1. Cortando Cadenas


Supongamos que tenemos una cadena de caractres

que tiene longitud

n.

Vamos a llamar

s1 , s2 ,

...,

sn

a los caractres correspondientes a cada una

de las posiciones de la cadena, es decir, LLamaremos subcadena

si

es el

i-simo

caractr.

[a, b]

a la cadena compuesta por los caractres

sa , sa+1 , ..., sb en ese rden, obviamente para que tenga sentido hablar de una subcadena [a, b] es necesario que a, b {1, 2, ..., n} y que a b.
Consideremos que hay una librera que contiene una funcin llamada

cortaCadena que recibe una cadena de caractres c, un entero m y devuelve dos cadenas de caractres, la primera corresponde a los primeros m caractres de c y la segunda corresponde al resto de los caractres. Dicha funcin usa exactamente m operaciones.
Vamos a imaginar que en algn lenguaje de programacin no se tiene acceso directo a los caractres de las cadenas pero se posee la funcin varias formas de hacerlo. Por ejemplo, si la cadena Olimpiada se quiere partir en 4 partes Olim, pi y a y da esto se podra hacer de varias formas, y puede que el costo computacional(nmero de operaciones), sea diferente, por ejemplo, estas dos formas tienen un costo distinto: Se manda a llamar

cortaCadena,

siendo este el caso si se quiere partir una cadena en mas de dos partes hay

cortaCadena(Olimpiada, 4) y se obtienen Olim y piada; luego se manda a llamar a cortaCadena(piada, 2) y se obtienen pi y ada. Despus se manda a llamar cortaCadena(ada, 1) y se obtienen a y da. El costo computacional de hacer esto es de la logitud de Olimpiada ms la longitud de piada, es decir 9+5+3 = 17.

cortaCadena(Olimpiada, 7) y se obtenen Olimpia y da; luego se manda a llamar a cortaCadena(Olimpia, 4) y se obtienen Olim y pia. Finalmente se manda a llamar cortaCadena(pia,
Se manda a llamar

21.1. CORTANDO CADENAS

289

2) obteniendo pi 18.

y a. El costo computacional total es de

9+6+3 =

Llegando a este punto la pregunta obligatoria es: Cul es el menor costo para cortar la cadena

en una secuencia de subcadenas dadas?

Problema Resuelto 21.1.1. Escribir un prgrama que dadas S y a1 , a2 , ..., ak


con

1 a1 a2 ... ak < n

determine cul es el menor costo com-

putacional (menor nmero de operaciones) para cortar

en las subcadenas

[1, a1 ], [a1 + 1, a2 ], ..., [ak1 + 1, ak ]?.

Solucin

Si procediramos como en el captulo anterior y tratramos de

reducir el problema a una sucesin de decisiones no llegaramos muy lejos, puesto que la nica sucesin de decisiones que es posible imaginarse es, dadas las cadenas que se han cortado hasta ese momento, cul es el siguiente corte?. Esta forma de modelar el programa es muy ineciente, puesto que el conjunto m de posibles cortes que pueden ya haberse realizado es de O (2 ) donde m es el nmero de cadenas que deben de quedar al nal. Hay otra forma de abordar este problema, y es usando el principio recursivo de Divide y Vencers abordado en el captulo

4.

Si de alguna manera sabemos que el primer corte de la cadena se debe realizar en la posicin de manera ptima para

p entonces lo que hay que hacer es resolver el problema [1, p] y para [p + 1, n], y la solucin ser n ms la

suma de las soluciones a cada subproblema. Sin embargo esto no es suciente para resolver el problema, ya que supusimos que ya sabamos en que posicin se debe de hacer el primer corte y eso no tiene por qu ser cierto. Hay algo que se debe hacer cuando se tienen varias opciones posibles y no se sabe cul es la correcta: inspeccionar cada una de ellas y luego elegir la mejor. La pregunta natural es este algoritmo se vuelve exponencial? y la respuesta es bastante graticante: no. Vamos a llamar la subcadenas

M (i, f, c) al menor costo de cortar la subcadena [a, b] en [i, c1 ], ..., [c|c| + 1, f ]. Tenemos que si c = entonces M (i, f, c) =
hay nada que cortar). La respuesta que estamos buscando

0(en ese caso no es M (1, m, a).

El siguiente paso es encontrar una expresin para

M (i, f, c),

la cual debe

de decir incluir la posibilidad de cortar en todos las posiciones indicadas en

c,

encontrar el ptimo de cada una y tomar el mnimo. Es decir, si

c =:

290

CAPTULO 21. PROGRAMACIN DINMICA EN CORTES

M (i, f, c) = min(M (i, c1 , ) + M (c1 + 1, f, {c2 , ..., c|c| }), M (i, c2 , {c1 }) + M (c2 + 1, f, {c3 , ..., c|c| }), ... M (i, c|c| , {c1 , c2 , ..., c|c|1 }) + M (c|c| + 1, f, )) + f i+1
En efecto, la mejor solucin que existe cortando primero en ci es las posibilidades. Si se implementa en una funcin recursiva tendriamos un tiempo de ejecucin de rden exponencial, pero tal vez podamos usar el truco llamado memorizacin que se describe en el captulo 3 para mejorar el algoritmo. Lo primero que hay que observar para darse cuenta que ese truco es realmente efectivo es que slamente nos interesa calcular el valor de la funcin de la forma o

M (i, ci , {c1 , c2 , ..., ci1 })+

M (ci + 1, f, {ci+1 , ..., c|c| }) + f i, as que la expresin anterior incluye todas

M (ai , af , {ai+1 , ..., af 1 }), M (1, af , {a1 , ..., af 1 }), M (ai , n, {ai+1 , ..., ak }) M (1, n, {ai , ..., ak }). Conviene pensar que a1 = 1 y ak = n de esa manera podemos de-

cir que slo nos interesan calcular los valores de la funcin en la forma

M (ai , af , {ai+1 , ..., af 1 }).


Con esto sabemos que el nmero de subproblemas que vamos a resolver (n)(n1) . As que est acotado por el nmero de parejas (ai , af ), es decir, por 2 podemos pensar en una implementacin como una funcin llamada Mejor, donde

Mejor(i, f)

regresa

M (ai , af , {ai+1 , ..., af 1 }).

Para marcar los subproblemas ya calculados podemos usar un arreglo de dos dimensiones llamado

Calculado

y para guardar las soluciones a los

subproblemas un arreglo de dos dimensiones llamado Una posible implementacin es la siguiente: 1 2 3 4 5 6 7 8 9 10 11 } M[ i n i c i o ] [ f i n ]=INFINITO ;

M.

int

int i , aux ; i f ( Calculado [ i n i c i o return M[ i n i c i o


}

Mejor (

int

inicio ,

int

fin ){

][ fin ]) { ] [ fin ] ;

C a l c u l a d o [ i n i c i o ] [ f i n ]=

i f ( f i n i n i c i o <=1){ return

true ;

M[ i n i c i o ] [ f i n ] = 0 ; M[ i n i c i o ] [ f i n ] ;

21.1. CORTANDO CADENAS


12 13 14 15 16 17 18 } }

291

for ( i = i n i c i o +1; i < f i n ; i ++){


aux= M[ i n i c i o ] [ i ]+M[ i + 1 ] [ f i n ]+ a [ f i n ] a [ i n i c i o

i f (M[ i n i c i o return

]+1;

] [ f i n ] > aux )

M[ i n i c i o ] [ f i n ]= aux ; M[ i n i c i o ] [ f i n ] ;

En el captulo anterior vimos que algunas funciones denidas de manera recursiva se pueden calcular iterativamente en una tabla, si queremos aplicar eso en este problema debemos de notar que para calcular tener calculado previamente

M [i][f ] M [i][1], M [i][2], ..., M [i][f 1].

se necesita

Cualquier rden que que respete esa restriccin es un buen candidato para calcular la tabla iterativamente. El rden mas natural es primero calcular aquellas posiciones donde 1 2 3 4 5 6 7 8 9 10 11 12 13 14 } Observemos que el cdigo anterior tiene la ventaja de que no necesita un arreglo para guardar cules posiciones fueron ya calculadas, es mas corto que usa memorizacin y a la larga es mas fcil de implementar. } } } }

M [i][f ]

f i

sea menor, es decir:

for ( l e n =1; l e n <=k ; l e n ++){ for ( i n i c i o =1; i n i c i o +l e n 1<=k ; i n i c i o ++){


f i n = i n i c i o +l e n

i f ( f i n i n i c i o <=1){ else { for (m= i n i c i o +1;m< f i n ;m++){


aux= M[ i n i c i o ] [ m]+M[m+ 1 ] [ f i n ]+ a [ f i n ] a [ M[ i n i c i o ] [ f i n ] = 0 ;

1;

i f (M[ i n i c i o

i n i c i o ]+1; ] [ f i n ] > aux ) M[ i n i c i o ] [ f i n ]= aux ;

Como nota cultural, este problema es muy parecido a un problema clsico llamado Multiplicacin ptima de Matrices, se incluy este problema en su lugar para evitar introducir una operacin llamada Multiplicacin de Matrices, la cual se aleja de los temas centrales de este libro.

292

CAPTULO 21. PROGRAMACIN DINMICA EN CORTES

21.2. Cortando en Cuadrados


Supongamos que tenemos una barra de chocolate cuadrculada de

por

cuadros, y queremos cortarla usando slamente cortes horizontales y verti-

cales en posiciones enteras de manera que al nal todas las piezas que queden sean cuadradas y adems queden la menor cantidad de piezas.

Problema Resuelto 21.2.1.


menor cantidad de piezas.

Escribe un programa que dados

en-

cuentre la manera de cortar una barra de chocolate de

por

de manera

que se obtengan slamente piezas cuadradas y adems que se obtengan la

Solucin
hay

Si

F =C

entonces la menor cantidad de piezas es

1, en otro caso

F 2

cortes horizontales psoibles y

C 2

cortes verticales posibles.

Suponiendo que sabemos por algn motivo que el primer corte es horizontal y se hace en la posicin

p, entonces lo nico que tendramos que hacer p


por

es minimizar el nmero de cuadrados en las dos piezas que quedan, es decir, resolver el problema para una barra de una barra de

y resolver el problema para

F p

por

C. p, F
por

Anlogamente si sabemos que el primer corte es vertical en la posicin hay que resolver el problema para una barra de

y para una barra de

por

C p. M (a, b)
a la solucin para una barra de

Por tanto, si llamamos entonces:

por

M (a, b) = 1 si a = b (21.1) = min(M (1, b) + M (a 1, b), ..., M (a 1, b) + M (1, b) (21.2) , M (a, 1) + M (a, b 1), ..., M (a, b 1) + M (a, 1)) (21.3) si a = b (21.4)
Usando los teoremas del clculo de abajo hacia arriba se deduce que la frmula anterior puede ser calculada de abajo hacia arriba de la siguiente manera: 1 2 3 4 5 6 7 }

for ( i =1; i <=F ; i ++){ for ( k =1; k<=C ; k++){ i f ( i==k ) { else {
M[ i ] [ k ] = 1 ; M[ i ] [ k ]=INFINITO ;

for (m=1;m< i ;m++){ // Cortes h o r i z o n t a l e s

21.3. POLGONO
8 9 10 11 12 13 14 15 } } } } }

293

M[ i ] [ k ]= min (M[ i ] [ k ] , M[m ] [ k ]+M[ i m] [ k ] ) ;

for (m=1;m< i ;m++){ // Cortes v e r t i c a l e s


M[ i ] [ k ]= min (M[ i ] [ k ] , M[ i ] [ m]+M[ i ] [ k m] ) ;

Para propsitos del tema de programacin dinmica en cortes ya encontramos la solucin al problema, sin embargo esta solucin est llena de cosas que se pueden mejorar, se invita al lector a buscar cmo mejorarla.

21.3. Polgono
para acceder a una descripcin de la solucin.

Seccin pendiente, consltese http://olympiads.win.tue.nl/ioi/ioi98/contest/day2/polygon/polygo

para acceder a la descripcin del problema y http://olympiads.win.tue.nl/ioi/ioi98/contest/day2/polyg

294

CAPTULO 21. PROGRAMACIN DINMICA EN CORTES

Parte VII Cmo Resolver Problemas

295

297

A lo largo de este libro hemos explicado herramientas muy poderosasn para poder resolver problemas por medio de la programacin. Pero, an no hemos visto ninguna manera para saber cul herramienta usar en cada situacin; y por si fuera poco tambin es cierto que no hemos visto todas las herramientas que existen (y menos las que llegarn a existir) que nos pueden ayudar a cumplir con nuestro objetivo. Existe una noticia desalentadora para este momento, y es que no existe un mtodo mediante el cul se puedan resolver absolutamente todos los problemas; la buena noticia es que eso hace a los probolemas ms interesantes y adems existe una lnea de pensamiento por medio de la cual se simplica bastante la resolucin de problemas. Esta lnea de pensamiento se resume a gran escala en 4 pasos: 1. Entender el problema 2. Encontrar la solucin 3. Implementar la solucin 4. Revisar Como bien se podr dar cuenta el lector, estos 4 pasos no son un buen resmen de cmo resolver problemas, principalmente porque la parte de encontrar la solucin suele ser mucho mas complicada que las otras 3 juntas. Sin embargo, eso no debe de provocar que les prestemos menos atencin a los otros 3 pasos en el momento de resolver un problema, cada uno de estos 4 pasos es igualmente importante a pesar de que algunos de ellos son

fciles.

298

Cap tulo

22

Entendiendo el Problema
Lo primero que hay que hacer para resolver un problema es entenderlo, suena como algo bastante obvio, y realmente es obvio que antes de resolver un problema primero hay que entenderlo. Sin embargo es necesario tener claro cundo un problema ya se entendi adecuadamente. La clave es la siguiente: un problema ya se entendi adecuadamente cuando es posible construir un modelo matemtico del problema, adems de decir objetivamente y sin rodeos qu es lo que se sabe de antemano(los datos) y qu es lo que se quiere saber(las incgnitas) en trminos del modelo matemtico(no en terminos de la descripcin del problema original). Para dejarlo mas claro: alguien que no haya leido el problema debera de ser capz de resolverlo sabiendo los datos, las incgnitas y el modelo matemtico. A pesar de que este paso es de vital importancia y aparentemente sencillo, muchas veces es subestimado y es omitido errneamente. Tal es el caso cotidiano del estudiante al cual se le presenta un problema y despus de leerlo sin hacer ningn intento se declara incapz de resolverlo porque no sabe qu hacer. Ahora bien, si al lector alguna vez le sucede algo similar, que luego de leer un problema no sepa por donde empezar, lo primero que debe de hacer es

empezar por identicar los datos y las incgnitas, eso incluye decir

en trminos objetivos(y de preferencia matemticos) qu es lo que se est buscando y qu es lo que se sabe de antemano adems de eliminar informacin supercial. Vale la pena subrayar que la respuesta a la pregunta Por dnde empiezo? siempre es:  por identicar los datos y las incgnitas. Vamos a resolver un ejemplo sencillo, de algo que para el lector le resultara un ejercicio mas que un problema pero para nios de educacin primeria

299

300

CAPTULO 22. ENTENDIENDO EL PROBLEMA

les podra resultar un problema, el objetivo es ilustrar cmo un problema al enterlo se transforma en un modelo matemtico.

Problema Resuelto 22.0.1.


compr?

Una naranja cuesta 2 pesos, una pera cuesta

5 pesos. Suponga que usted gast 18 pesos y la cantidad de naranjas que compr es el doble de la cantidad de peras. Cuntas peras y cuntas naranjas

Solucin

Sin prestar mucha atencin a los detalles rpidamente llegamos

a este sistema de ecuaciones:

2x + 5y = 18 2y = x
Y posteriormente se llegar a la respuesta: 2 paras y 4 naranjas.

(22.1) (22.2)

Si prestamos algo de atencin, en el momento que al nmero de naranjas les llamamos

x, que al nmero de peras les llamamos y y que planteamos las 2

ecuaciones, estamos olvidndonos de que hablabamos de peras y de naranjas. Incluso nos estamos olvidando de la moneda con la cual se realiza la compra y una vez que planteamos la primera ecuacin ya no es necesario ni siquiera recordar los precios.

De esta manera el proceso de entender un problema es el mismo que el de convertir el enunciado del problema en variables con un comportamiento bien denido. Tambin hay que cuidarse de evitar asumir demasiadas cosas en el momento de entender un problema, y qu mejor manera de mostrar que esto puede suceder a travs de un ejemplo:

Problema Resuelto 22.0.2.

Dos padres y sus respectivos dos hijos se

comieron un pastel, y nadie mas comi de l Es posible que en total hayan comido de ese pastel 3 personas?

Solucin

Es posible que el lector se vea tentado a asumir que los 2 padres

son personas distintas de los 2 hijos y que por tanto hay 4 personas. Revisemos nuevamente el texto, nos dice que hay 3 personas en total, las cuales las podemos representar por 3 vrtices llamados A, B y C. Podemos representar este problema como un grafo, donde lo que se quiere encontrar es una manera de encontrar 2 aristas dirigidas de manera que 2

301

vrtices tengan grado de salida 1, 2 vrtices tengan grado de entrada 1 y que no haya ciclos. Los vrtices con grado de salida 1 son los padres, los que tienen grado de entrada 1 son los hijos y

es hijo de

si existe una arista desde

hasta

a.

Claramente no es necesario tener en cuenta todo este tecnicismo para resolver el problema, simplemente se est haciendo notar que ya no importa si lo que estamos imaginando son personas o animales, ya nos olvidamos del pastel, etc. El problema nos dice adems que entre ellos hay 2 padres, sin prdida de generalidad diremos que son A y B. Y adems hay 2 hijos, dichos 2 hijos no pueden ser tambin A y B puesto que para que sucediera alguno debera de ser padre de s mismo o padre de su hijo. Por principio de las casillas entre A y B hay al menos un hijo de A o de B, nuevamente sin prdida de generalidad diremos que

es hijo de

A,

como

no puede ser hijo de

y hay en total 2 hijos entonces

es hijo de

B.

Por lo tanto si es posible.

El ejemplo anterior, debido a que se presta a malos entendidos, podra ser considerado una adivinanza en lugar de un problema. Otra cosa que puede suceder es que no se sepa qu datos tomar en cuenta y qu datos son supericiales, en el primer ejemplo(el de peras y manzanas) al plantear las ecuaciones nunca se tom en cuenta que las variables eran enteros no negativos, ni tampoco se acotaron considerando cul poda ser el mximo de peras o manzanas que pudiera haber en una tienda. Una estrategia til para esos casos es tomar los datos que aparentemente sean importantes, y si resultan insucientes regresar a la descripcin del problema y buscar mas datos. El siguiente ejemplo es un problema que no se presta a malinterpretar pero plantear el modelo matemtico no es trivial y ser necesario hacer uso de la estrategia de abstraer datos y luego volver por ellos.

Problema Resuelto 22.0.3.


decide platicar sobre su vida: -Tengo 3 hijas- dijo A.

Dos amigos(a los cuales llamaremos A y B)

se reencontraron despus de mucho tiempo sin verse, luego de saludarse, A

-Muy bien, Qu edades tienen?- pregunt B. -Para hacerlo mas interesante -continu A-, te lo voy a plantear como un acertijo: Si multiplicas sus edades obtendrs 36 y si las sumas obtendrs el nmero de ventanas de este edicio.

302

CAPTULO 22. ENTENDIENDO EL PROBLEMA


B cont con mucho cuidado el nmero de ventanas y despus de pensar

un rato le dijo a A: -Estoy seguro que me falta un dato. -Es cierto!, lo olvidaba -contest A-, la mayor de ellas toca el piano. Luego de eso, B logr saber qu edades tenan las hijas de A. Ahora te toca averigarlo a ti. Nota: Todas las edades estan expresadas como cuntos cumpleaos ha tenido cada hija y ninguna cumple el 29 de Febrero.

Solucin

Como se ha hecho incapi a lo largo de este captulo, para en-

tender el problema es necesario poder decir cules son los datos y cules las incgnitas, as que empezaremos por ah. Necesitamos encontrar 3 enteros no negativos Respecto a los datos, sabemos que son difciles de abstraer. Sabemos que

a, b

c.

abc = 36,

pero el resto de los datos

a+b+c

es el nmero de ventanas de un edicio pero no

sabemos de qu edicio as que parecera un dato intil. Adems sabemos que la hija mayor toca el piano, nuevamente parece un dato intil ya que no nos importa mucho saber que toca el piano para averigar su edad (este problema no trata de usar cul es la edad mnima para tocar el piano), pero un dato importante que podramos pasar por alto es que existe una hija mayor(en otro caso podra suceder que dos gemelas fueran mayores que una tercera hermana y adems ). Dada la dicultad para saber qu datos pueden ser tiles y qu datos no lo son; vericaremos si los datos que hemos identicado son sucientes para resolver el problema y luego volveremos a la descripcin en caso de que no sean sucientes, ya que para poder avanzar en la solucin del problema hay que trabajar con un modelo matemtico y no con una descripcin parecida a un cuento.

a = 0, b = 0 y c = 0. Sin prdida de generalidad diremos que a = b = c. 2 2 La factorizacin en primos de 36 es 36 = 2 3 .


Dado que y sabemos que Usando la factorizacin en primos podemos encontrar todas las ternas que cumplen estas condiciones: (1, 1, 36), (1, 2, 18), (1, 3, 12), (1, 4, 9), (1, 6, 6), (2, 2, 9), (2, 3, 6), (3, 3, 4). El otro dato que tenemos es que hay una hija mayor, es decir

abc = 0

a=b<

c(ntese

la desigaldad estricta), eso slo nos hace descartar la terna (1, 6,

6), y an hay 7 soluciones posibles. En este momento ya podemos asegurar que si el problema est bien planteado entonces no tomamos en cuenta todos los datos.

303

Volviendo a revisar la descripcin, nos damos cuenta que B saba cunto vala

a + b + c y an as le faltaba un dato. Es decir, sea z = a + b + c, sabemos a b c tales que abc = 36 y a+b+c = z .

que el siguiente problema tiene mas de una solucin: Encontrar enteros no negativos nico posible valor de y las ternas son (1, As que Observando las ternas que encontramos, podemos darnos cuenta que el

z con el cual podran haber 2 ternas distintas es z = 13, 6, 6) y (2, 2, 9), para cualquier otro valor de z hay una ab<c
entonces

nica terna o no hay ternas.

z = 13,

y como

a = 2, b = 2

c = 9.

Por ltimo no sobra hacer notar que hay que tener mayores precausiones al enteder el problema que al resolverlo; el motivo es que una mala interpretacin del problema puede conducir a perder mucho mas tiempo que cualquier otro error. Es decir, si se comete un error de implementacin es necesario volver a revisar el cdigo, si se comete un error al encontrar el algoritmo es necesario volver a implementar, y luego volver a revisar pero si se comete un error al entender el problema es necesario volver a encontrar la solucin, volver a implementar y volver a revisar. De todos los errores posibles, el no entender adecuadamente el problema es el que tiene consecuencias mas graves.

304

CAPTULO 22. ENTENDIENDO EL PROBLEMA

Cap tulo

23

Encontrando la solucin
Este sin duda es el paso mas difcil de todos, y al cual estn dedicadas casi todas las herramientas que se mencionan en este libro. En el captulo anterior se pusieron ejemplos de problemas que nada tenan que ver con programacin, eso fu adecuado ya que simnplic la tarea y el paso de entender un problema mediante la abstraccin es el mismo sin importar si se trata de un problema de programacin o no. Pero en este captulo vamos a restringirnos a problemas de programacin, principalmente porque ya hay literatura que habla de problemas en general y hay algunas tcnicas que son tiles slo en problemas de programacin.

23.1. Las Dimensiones del Problema


Informalmente hablando, la palabra dimensin corresponde a todo aquello que puede variar en el problema, en general se reere a los datos de entrada junto con los rangos donde pueden estar, los datos de salida junto con sus respectivos rangos y a veces es conveniente aumentar dimensiones implscitas, esto con el objetivo de hacer ms manejable el problema.

Problema Resuelto 23.1.1.


Tienes

Identicar las dimensiones de este problema:

0 N 10000 pares de coordenadas (xi , yi ), (ai , bi ) con 0 xi , ai , yi , bi 100000, dichas coordenadas representan las esquinas opuestas de N rectngulos paralelos a los ejes. Hay que encontrar el nmero de rectngulos que tienen la misma rea.

Solucin
100000,

Las dimensiones de entrada son

0 N 10000, 0 xi , yi , ai , bi

es decir, hay 5 dimensiones de entrada.

305

306

CAPTULO 23. ENCONTRANDO LA SOLUCIN


Slo hay una dimensin de salida, y esta consiste en un nico nmero

entero

0 n 100000.

Respecto a dimensiones implscitas, es probable que para resolver el problema necesitemos calcuolar el rea de cada rectngulo y por tanto tambin necesitaramos saber el ancho y el alto; estos datos son fciles de calcular a pesar de que no son parte de la entrada. Tenemos que tanto el ancho como el alto estn entre 0 y 100 000, sin 10 embargo el rea est entre 0 y 10 , esto podra causar un desbordamiento si no lo hubiramos tomado en cuenta.

Una vez identicadas todas las dimensiones del problema escrbelas y as podrs darte una idea aproximada de qu tan complicado es el problema.

23.2. Dibuja Una o Varias Figuras


A lo largo de este libro, se ha omitido este paso en todas las soluciones, principalmente porque plasmar las guras en un libro es mas complicado que hacerlo a mano y las guras rara vez sirven para justicar algo. Si bien deberas de ser capz de explicar la solucin nal del problema sin necesidad de una gura, algo que es cierto es que las guras son una buena fuente de ideas. La explicacin de por qu las guras resultan til no es muy sorprendente: se utilizan distintas reas del cerebro para procesar imgenes que para razonar verbalmente. Adems las guras muestran muchas propiedades del problema que no es fcil mantener en la memoria de acceso inmediato, as que mientras se mira una gura se pueden tener mas cosas en mente simultaneamente. Tambin hay que tener en cuenta que una sola gura normalmente no es suciente, casi siempre es necesario dibujar varias guras; lo que debes hacer es pensar en muchos posibles casos y dibujar varios de ellos, trata de que no sean casos muy parecidos para evitar que te den ideas falsas. Adems de todo eso, hay varias formas grcas de representar una misma idea, por ejemplo, si en un problema te piden encontrar una secuencia de nmeros enteros, podras escribir una secuencia, dibujar una grca de barras, dibujarlos como una secuencia de puntos sobre la recta numrica, etc. No te estanques en una sola representacin ya que muchas veces la solucin a un problema es fcil de ver con la representacin adecuada, y la nica

23.3. LIBRO INCOMPLETO Y DNDE CONTINUAR LEYENDO

307

manera de que sea probable que veas la representacin adecuada en cualquier problema es que dibujes los datos de muchas maneras posibles.

23.3. Libro Incompleto y Dnde Continuar Leyendo


Aqu termina el lo que he escrito del libro, pero no el contenido, espero continuar con este captulo y esta parte en el futuro; mientras tanto se puede leer un texto que habla de algo parecido:

http://www.mii.lt/olympiads_in_informatics/pdf/INFOL018.pdf

308

CAPTULO 23. ENCONTRANDO LA SOLUCIN

Parte VIII Apndices

309

Apendice

Otros Algoritmos y otras Estructuras de Datos


A quien est interesado en los concursos de programacin se le recomienda altamente conocer los siguientes algoritmos:

Caminos mas Cortos en Grafos. Dijkstra, Floyd y Bellman-Ford. rbol de Expansin Mnimo. Kruskal y Prim. Teora de Juegos. Nimbers. Estructuras de Datos. Segment Tree y Fedwick Tree. Teora de Nmeros. Mximo comn divisor(algoritmo de Euclides),
criba de Eratostenes, operaciones con enteros grandes.

Algoritmos Geomtricos. Par de puntos mas cercanos, obtencin del


cerco convexo, algoritmos bsicos con polgonos(obtener rea, saber si un punto est dentro, etc). Tambin se hace incapi en que por cada hora de teora se deben de dedicar al menos 9 horas en resolver problemas si se quiere llegar a tener una preparacin ecaz en los concursos. Estos algoritmos y estructuras de datos no se muestran en este libro debido a que la intencin de este libro no es la de ser un libro de referencia; consltese el apndice de bibliografa recomendada para tener una idea de dnde encontrarlos.

311

312APNDICE A. OTROS ALGORITMOS Y OTRAS ESTRUCTURAS DE DATOS

Apendice

Bibliografa Recomendada
Como se mencion en el apndice anterior, este libro es realmente una introduccin tanto a los algoritmos como a la resolucin de problemas e incluso es una introduccin a las matemticas a un nivel formal. Asi que este libro no es suciente para tener un conocimiento profundo de los algoritmos ni para resolver cualquier problema de algoritmos. A continuacin cito algunas recomendaciones bibliogrcas para diversos tpicos:

Introduction To Algorithms Thomas Cormen.

Excelente libro de

referencia de algoritmos en general. Contiene los algoritmos mas conocidos de cada rama y tiene un rigor matemtico bastante alto.

Algorithms S. Dasgupta, C.H. Papadimitriou, y U.V. Vazirani.


de manera gratuita.

Un

buen libro de algoritmos, no tan extenso como el de Thomas Cormen pero muy ameno pero de leer y sin perder el rigor. Se puede descargar

Problems on Algorithms Ian Parberry. Contiene muchos problemas


de caracter algortmico, tiene poca teora.

Graphs, Networks and Algorithms Dieter Jungnickel. Un libro de


tera y algoritmos de grafos bastante fuerte, adems de contener los algoritmos mas conocidos tambin contiene extensiones/generalizaciones de stos, muy til para quienes quieran ir mas all de las aplicaciones obvias.

Programming Challenges Steven S. Skiena y Miguel Revilla.

Li-

bro completamente dedicado al concurso de programacin ACM ICPC. Contiene problemas recomendados y un poco de teora de cada rea.

313

314

APNDICE B. BIBLIOGRAFA RECOMENDADA

Advanced Data Structures Peter Brass. Una extensa referencia de


estructuras de datos.

Problem Solving Strategies Arthur Engel. Libro ampliamente usado


para entrenamientos en la Olimpiada de Matemticas, no est centrado en el estudio de los algoritmos pero contiene muchas cosas tiles. Adems de estos libros, internet est lleno de contenido til, consultese la seccin de contenido educativo de

www.topcoder.com/tc.

Para aquellos interesados en la prueba de la frmula de Stirling, se requieren conocimientos de clculo, un buen libro de clculo es

Michael Spivak ;

Calculus

de

para quienes ya sepan clculo, la prueba de dicha frmu-

enciclopedia libre(en.wikipedia.org).

la se puede encontrar en muchos lugares, uno de ellos es

Wikipedia, la

Bibliografa

[1] Cesar Arturo Cepeda Garca,

Espacio de Bsqueda 2007 Problems on Algorithms 2002

[2] Ian Parberry y William Gasarch, [3] George Polya,

How to Solve It 1945

[4] Arthur Charguraud, Mathias Hiron,

Teaching Algorithmics for Informatics Olympiads: The French Method Problem Solving Strategies Springer 2000 Combinatoria 2005

[5] Arthur Engel,

[6] Maria Luisa Prez Segu

[7] Bernard Kolman, Robert C. Busby y Sharon Cutler Ross, Educacin 1995 [8] Francisco Javier Zaragoza Martnez

Estructuras de Matemticas Discretas para la Computacin Editorial Pearson Una Breve Introduccin a la

Recursin 2004

315