Manuel López Michelone https://sg.com.mx/ en Cuadrados Mágicos y Recursión https://sg.com.mx/revista/49/cuadrados-magicos-y-recursion <span class="field field--name-title field--type-string field--label-hidden">Cuadrados Mágicos y Recursión</span> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-hidden field__item"> <img src="/sites/default/files/images/melancholia_I.jpg" width="3146" height="1842" alt="" loading="lazy" typeof="foaf:Image" /> </div> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/1" lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="" class="username">sg</a></span> <span class="field field--name-created field--type-created field--label-hidden">Tue, 12/22/2015 - 20:53</span> <div class="field field--name-field-numrevista field--type-entity-reference field--label-inline field--entity-reference-target-type-taxonomy-term clearfix"> <h3 class="field__label inline">Publicado en</h3> <ul class='links field__items'> <li><a href="/revista/49" hreflang="und">SG #49</a></li> </ul> </div> <div class="field field--name-field-seccion field--type-entity-reference field--label-hidden field--entity-reference-target-type-taxonomy-term clearfix"> <ul class='links field__items'> <li><a href="/revista/secciones/programacion" hreflang="und">Programación</a></li> </ul> </div> <div class="field field--name-field-autor field--type-entity-reference field--label-inline field--entity-reference-target-type-taxonomy-term clearfix"> <h3 class="field__label inline">Autor</h3> <ul class='links field__items'> <li><a href="/buzz/autores-sg/manuel-lopez-michelone" hreflang="und">Manuel López Michelone</a></li> </ul> </div> <div class="text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p dir="ltr">En las matemáticas hay muchos problemas que son básicamente recreativos. Uno de ellos es el de los cuadrados mágicos, en donde se trata de colocar números en una matriz de celdas de n x n, de forma tal que la suma de los números por columnas, filas y diagonales principales sea la misma, la constante mágica. Usualmente los números empleados para rellenar las casillas son consecutivos, de 1 a n², siendo n el número de columnas y filas del cuadrado mágico.</p><p dir="ltr">Estos cuadrados se conocen ya en la antigua China, desde el III milenio A.C. Hay también referencias a los mismos en las culturas india, egipcia, árabe y griega. Y aunque muchas veces se han asociado con cuestiones astrológicas o de índole mágico, la realidad es que es una simple recreación matemática que es interesante por sí misma.</p><p dir="ltr">El artista renacentista Alberto Durero, en su obra Melancolía I, incluye un cuadrado mágico de orden 4 (4x4 celdas), en donde la constante mágica es 34. La figura 1 tiene los valores de dicho cuadrado.</p><p><img src="/sites/default/files/images/stories/sg49/cuadrado.jpg" alt="" width="250" /></p><p>Figura 1. El cuadrado mágico de Durero.</p><p>Existen una serie de procedimientos para crear cuadrados mágicos de orden par o impar. Un ejercicio interesante es escribir un programa que siga estos procedimientos para generar cualquier cuadrado mágico. No obstante esto, la pregunta que surge es si se puede generar un cuadrado mágico a prueba y error, sin necesidad de conocer algoritmo alguno. La respuesta es: sí, y para ello lo mejor que podemos hacer es usar un lenguaje de programación al que se le den los datos correspondientes y el programa resuelva automáticamente lo que buscamos. Este tipo de lenguajes declarativos forman un paradigma diferente al de los lenguajes imperativos y Prolog es uno de los mejores ejemplos del mismo.</p><p>Prolog nació en los años 70 del siglo pasado, en la Universidad de Aix-Marseille I (Francia), ideado por dos estudiantes, Alain Colmerauer y Philippe Roussel. La primera versión del lenguaje se escribió en Algol 60 y era interpretado. Más tarde (1983), llegaría David H.D. Warren, quien habría desarrollado un compilador que traducía Prolog en una serie de instrucciones de una máquina virtual, la cual se denominó a la postre la Máquina Abstracta de Warren (también conocida como WAM - Warren Abstract Machine).</p><p>Los programas en Prolog se componen de cláusulas de Horn que constituyen reglas del tipo “modus ponendo ponens”, es decir, “si es verdad el antecedente, entonces es verdad el consecuente”. Básicamente Prolog usa hechos y reglas. Los hechos son verdades que son ciertas en el entorno de un programa y las reglas definen verdades cuando se cumplen una serie de hechos. Prolog se basa así, en inferencias lógicas. A manera de ejemplo, si me presentan a alguien que se llama Jorge Flores y yo encuentro un parecido con un amigo mío llamado Luis Flores, quizá mi cerebro haga automáticamente una inferencia y me nazca preguntarle a Jorge: “¿no tienes un hermano llamado Luis?”. Ese tipo de inferencias las puede hacer Prolog, como en el siguiente ejemplo:</p><p class="code">padre(juan, manuel). /* el padre de juan es manuel */<br />padre(pedro, manuel). /* el padre de pedro es manuel */<br />hermanos(X,Y) :- /* X y Y son hermanos si tienen el mismo padre */<br />&nbsp; padre(X,Z),<br />&nbsp; padre(Y,Z),<br />&nbsp; X&lt;&gt;Y,<br />&nbsp; write(X, “es hermano de “, Y).</p><p>La regla se lee de la siguiente manera: “X y Y son hermanos si: el padre de X es Z, y el padre de Y es Z, y X no es Y”. Nótese que no se dice en ninguna parte que pedro y juan son hermanos. Prolog, a través de la regla definida, llega a esa conclusión.</p><p>Es importante hacer la aclaración de que X no es Y, sino el programa reportaría que X es hermano de sí mismo.</p><p>Prolog resuelve todo a través de un motor de inferencias, también reconocido como algoritmo de Robinson (en honor al autor que publicó por primera vez el mecanismo en 1968). De esta manera y abreviando el asunto, en Prolog describimos el problema y el lenguaje nos da las soluciones a través de dos mecanismos: la recursión y el backtrack.</p><p>La recursión es simplemente llamarse a sí mismo. En términos de programación significa que una rutina se llame a sí misma hasta que cierta condición impida que se cicle eternamente.</p><p>El backtrack consiste en regresar sobre nuestros pasos si resulta que la solución hallada no cumple con nuestras expectativas. Un ejemplo simple de backtrack puede verse en el recorrer un laberinto. Caminamos dentro de éste hasta que topamos con pared. Si esto ocurre, entonces retrocedemos sobre nuestros pasos hasta encontrar la primera bifurcación posible. Seguimos este procedimiento hasta hallar la salida.</p><p>Regresando a los cuadrados mágicos, veamos la creación de un cuadrado mágico de orden 3. El listado 1 muestra código en Prolog que resuelve este problema. Todo consiste en describir las condiciones iniciales y las de frontera. Luego le pedimos a Prolog que nos dé las soluciones a través de probar todas las posibilidades exhaustivamente (el árbol solución).</p><script type="text/javascript" src="https://gist.github.com/pedrogk/d048d13308dc47a623a8.js"></script><p>Listado 1. Encontrar cuadrados mágicos con Prolog.</p><p>El código del listado 1 básicamente hace lo siguiente:</p><ul><li>Elegir 9 números (todos diferentes).</li><li>Hacer las combinaciones de dichos números —en las ecuaciones correspondientes— que entreguen un resultado R. Este resultado debe ser igual en todas las ecuaciones.</li><li>Si se logra esto, escríbase la solución.</li><li>Búsquese una nueva solución a través de backtrack (aplicando la instrucción fail).</li></ul><p>El algoritmo es no determinista, es decir, no sabemos de antemano cuál será el resultado de la ejecución y el sistema, a las mismas entradas, puede dar muchos resultados posibles.</p><p>La forma más sencilla de ejecutar este código es por medio de SWISH, que es un ambiente de ejecución de Prolog que funciona en el navegador web. Simplemente entra a&nbsp;<a href="http://swish.swi-prolog.org">http://swish.swi-prolog.org</a>, crea un programa nuevo, pega el código fuente y en la sección donde pones tu query teclea “cuadrado” y da clic a “Run”. El sistema comenzará a calcular e irá desplegando matrices conforme las encuentre.</p><p>Como verás, el programa en Prolog es relativamente compacto y simple de entender. Intenta hacer esto en otro lenguaje y verás que tendrás código más grande y complejo. Por otro lado, un inconveniente es que Prolog dista de ser un lenguaje eficiente. En este caso hicimos el cálculo de un cuadrado de un orden relativamente pequeño (3), pero conforme vayamos aumentando el orden, el consumo de memoria aumentará significativamente y podríamos agotarla. Sin embargo, lo importante en este caso es la posibilidad de que, dando solamente los hechos y reglas relevantes, el programa busque con el algoritmo de Robinson, todas las posibles soluciones.</p></div> <div class="text-formatted field field--name-field-autor-bio field--type-text-long field--label-above"> <div class="field__label">Bio</div> <div class="field__item"><p>Manuel López Michelone (@morsa) es Físico por la UNAM y maestro en Ciencias por la Universidad de Essex en el tema de Inteligencia Artificial. Ha sido columnista por muchos años en publicaciones de la industria del cómputo y ávido programador. Actualmente realiza su doctorado en ciencias de la computación en la UNAM. morsa@la-morsa.com</p></div> </div> <section class="field field--name-comment field--type-comment field--label-above comment-wrapper"> </section> Wed, 23 Dec 2015 02:53:05 +0000 sg 6213 at https://sg.com.mx ¿Cuáles son las Palabras Diferentes en el Quijote? Un ejercicio con árboles binarios https://sg.com.mx/revista/48/cuales-son-las-palabras-diferentes-el-quijote-un-ejercicio-arboles-binarios <span class="field field--name-title field--type-string field--label-hidden">¿Cuáles son las Palabras Diferentes en el Quijote? Un ejercicio con árboles binarios</span> <div class="images-container clearfix"> <div class="image-preview clearfix"> <div class="image-wrapper clearfix"> <div class="field field--name-field-image field--type-image field--label-hidden field__item"> <img src="/sites/default/files/images/fundamentos-fig1.png" width="906" height="835" alt="" loading="lazy" typeof="foaf:Image" /> </div> </div> </div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><a title="View user profile." href="/user/1" lang="" about="/user/1" typeof="schema:Person" property="schema:name" datatype="" class="username">sg</a></span> <span class="field field--name-created field--type-created field--label-hidden">Mon, 11/16/2015 - 23:12</span> <div class="field field--name-field-numrevista field--type-entity-reference field--label-inline field--entity-reference-target-type-taxonomy-term clearfix"> <h3 class="field__label inline">Publicado en</h3> <ul class='links field__items'> <li><a href="/revista/48" hreflang="und">SG #48</a></li> </ul> </div> <div class="field field--name-field-seccion field--type-entity-reference field--label-hidden field--entity-reference-target-type-taxonomy-term clearfix"> <ul class='links field__items'> <li><a href="/seccion-revista/fundamentos" hreflang="und">Fundamentos</a></li> </ul> </div> <div class="field field--name-field-autor field--type-entity-reference field--label-inline field--entity-reference-target-type-taxonomy-term clearfix"> <h3 class="field__label inline">Autor</h3> <ul class='links field__items'> <li><a href="/buzz/autores-sg/manuel-lopez-michelone" hreflang="und">Manuel López Michelone</a></li> </ul> </div> <div class="text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>El Quijote, de Cervantes Saavedra, es uno de los libros más importantes de la literatura mundial. Pensando sobre el Quijote recientemente, se me ocurrió preguntarme ¿cuántas palabras distintas tendrá y cuáles son? El reto por sí mismo es interesante y en el camino se pueden aprender muchas cosas. Veamos a continuación cómo podríamos resolverlo.</p><p>La problemática se centra en la estructura de datos a usar. Por ejemplo, imaginemos que queremos usar un arreglo de palabras para irlas acomodando conforme las vamos leyendo. El primero problema con esto es que no sabemos el total de palabras que hay por lo cual ¿de qué tamaño haremos el arreglo? Bueno, si el Quijote (como está en el Proyecto Gutenberg) tiene unas 2 millones de letras, es razonable pensar que un 20% máximo serán palabras, más o menos 400 mil palabras. Podemos crear un arreglo de 400,000 palabras y esperar que esto sea suficiente, pero<br />definitivamente esta no es una solución ideal.</p><p>Una mejor opción es usar estructuras de datos dinámicas, que van creciendo conforme lo vamos requiriendo. Dicho de otra manera, si necesitamos una nueva variable para alojar un dato, creamos esa variable en tiempo de ejecución.</p><p>Una estructura de datos dinámica muy poderosa, es la de los árboles binarios. En una estructura de esta naturaleza tenemos un nodo y dos ramas. En el nodo tenemos la información y en las ramas tenemos apuntadores a otros nodos o bien a nulo.</p><p>Para explicar cómo funciona una estructura de árbol binario, imaginemos que tenemos la siguiente lista de números desordenada: 60, 41, 74, 16, 53, 65, 25, 46, 55, 63, 70.</p><p>Comenzamos tomando el primer valor (60) y usándolo como nodo raíz. Ahora tomamos el siguiente número (41) y lo comparamos al raíz. Dado que es menor que el raíz, y el raíz todavía no tiene rama izquierda, colocamos el 41 en la rama izquierda del 60 (si hubiera sido mayor lo colocaríamos en la rama derecha). El siguiente número es el 74, así que lo ponemos en la rama derecha del 60. A continuación tenemos el 16, dado que el nodo 60 ya tiene rama izquierda (41), entonces ahora bajamos al nodo 41 y comparamos contra este. 16 es menor que 41, y 41 todavía no tiene rama izquierda así que ahí ponemos al 16. De esta forma generamos nuestro árbol, como se muestra en la figura 1.</p><p><img src="/sites/default/files/images/stories/sg48/fundamentos-fig1.png" alt="" width="500" /></p><p><em>Figura 1. Ejemplo de un árbol binario.</em></p><p>Nótese que un árbol de esta naturaleza además nos da una manera intrínseca de ordenar información. Si queremos desplegar los números que están en este árbol de manera que queden ordenados de menor a mayor, lo que hacemos es muy simple: Partimos del nodo raíz y nos movemos hasta la última rama izquierda posible.</p><p>Esto nos dará, de acuerdo a la imagen que hemos puesto, en el número 16. Entonces vemos si este nodo tiene rama derecha. Sí, la tiene, y es el 25. Entonces ponemos como primer número el 16 y después el 25. Nos hacemos un nodo atrás y hallamos el 41. Lo ponemos. Vemos si tiene rama derecha. Sí, la tiene, es el 53, pero éste tiene una rama izquierda, que es el 46 y este nodo a su vez tiene la rama izquierda con el valor 42. Entonces ponemos el 42, 46, 53 y ahora revisamos la rama derecha y hallamos que hay un 55. Por lo tanto la lista va quedando así: 16, 25, 41, 42, 46, 53, 55... Y ahora ya no hay más ramas izquierdas que revisar. Pasamos a la rama derecha del nodo raíz y hallamos que hay un 74. Pero éste tiene un nodo izquierdo, el 65, que a su vez tiene otro, el 63, que a su vez tiene otro más, el 62, por lo que pondremos, 62, 63 y ahora vemos si hay nodo derecho. Y sí, lo hay, el 64. Ponemos ahora el 65 y como tiene rama derecha, ponemos el 70 y finalmente el 74.</p><p>Si queremos ordenar el árbol de los números mayores a los menores, lo que tenemos que hacer es recorrer el árbol al revés, primero empezar por la rama derecha. Esto es increíble. La misma estructura nos permite ya dejar ordenados literalmente los datos sin necesidad de otras estructuras.</p><p>Pues bien, una vez que tenemos esto aclarado, podemos crear un árbol binario con las palabras del Quijote. Si es una nueva palabra, creamos el primer nodo. Si es una palabra que es menor que la palabra que está en el nodo raíz, la ponemos en un nuevo nodo, que cuelga de la rama de la izquierda, en caso contrario, de la rama derecha.</p><p>Pero ¿cómo puedo crear una estructura de un árbol binario? La manera más simple es crear una estructura dinámica, que crece en la medida que lo necesitamos. El listado 1 muestra el código para hacerlo. El código está en Delphi (Pascal) que yo sé que ya no es común ver, pero sirve bien para este propósito.</p><p><img src="/sites/default/files/images/stories/sg48/fundamentos-1-nodo.png" alt="" width="500" /></p><p><em>Listado 1. Estructura de un árbol binario.</em></p><p>Esta definición siempre parece costar trabajo leerla, y la razón es que es la única vez que Pascal permite usar algo que aún no hemos definido. Así, nodoptr = ^nodo; pero node no ha sido aún definido. De hecho, se define inmediatamente después.</p><p>Una vez teniendo la estructura dinámica, Delphi (Pascal), nos permite crear una nueva instancia de la misma si la necesitamos. Por ejemplo, definamos</p><p><code>var<br />&nbsp;&nbsp;&nbsp; arbol : nodoptr;<br /></code></p><p>Esto es precisamente el árbol en donde iremos colocando las palabras que vayamos leyendo del Quijote. Necesitamos para ello, un procedimiento para insertar nuevos nodos y poner los valores en los mismos.</p><p><img src="/sites/default/files/images/stories/sg48/fundamentos-2-insertaarbol.png" alt="" width="500" /></p><p><em>Listado 2. Rutina para insertar nodos en el arbol</em></p><p>Esa es la rutina imprescindible para ir creando los nodos (con sus respectivas ramas izquierda y derecha). Ahora hagamos la tarea: leamos el archivo del Quijote. Cada línea puede contener un número finito de palabras. El listado 3 tiene el código que abre el archivo de texto a analizar e itera sobre su contenido, línea por línea.</p><p><img src="/sites/default/files/images/stories/sg48/fundamentos-3-buttonclick.png" alt="" width="500" /></p><p><em>Listado 3. Leemos el archivo e insertamos palabras al arbol</em></p><p>Ejecutar este programa nos arroja que el Quijote tiene 384,123 palabras en total, aunque muchas de ellas se repiten a lo largo de la obra (¿diferentes? unas 30 mil). El programa crea un archivo con todas las palabras (una por línea) y las despliega en orden alfabético. La rutina que crea este archivo se muestra en el listado 4.</p><p><img src="/sites/default/files/images/stories/sg48/fundamentos-4-writeouttree.png" alt="" width="500" /></p><p><em>Listado 4. Desplegar contenidos del árbol.</em></p><p>Nótese que esta es una rutina recursiva (se llama a sí misma), que permite imprimir todas las palabras diferentes del Quijote en un archivo.</p><h3>Siguientes pasos</h3><p>No basta con saber cuáles son las palabras diferentes en total que tiene el Quijote, sino también su frecuencia. Aquí necesitamos entonces hacer dos cosas. La primera es que el nodo contenga no sólo la palabra del texto, sino las veces que la vamos encontrando, algo así como una variable que vaya incrementándose cada vez que encontremos dicha palabra. Por ello, requerimos de un procedimiento más: uno que busque un nodo y pueda modificar los valores del mismo. No lo voy a hacer aquí. Si el amable lector me ha seguido hasta este punto, seguro puede hacer fácilmente esta rutina ¿verdad?</p><p>&nbsp;</p></div> <div class="text-formatted field field--name-field-autor-bio field--type-text-long field--label-above"> <div class="field__label">Bio</div> <div class="field__item"><p>Manuel López Michelone (<a href="http://twitter.com/morsa" target="_blank">@morsa</a>) es Físico por la UNAM y Maestro en Ciencias por la Universidad de Essex en el tema de Inteligencia Artificial. Ha sido columnista por muchos años en publicaciones de la industria del cómputo y ávido programador. Actualmente realiza su doctorado en ciencias de la computación en la UNAM. morsa@la-morsa.com</p></div> </div> <section class="field field--name-comment field--type-comment field--label-above comment-wrapper"> </section> Tue, 17 Nov 2015 05:12:36 +0000 sg 6187 at https://sg.com.mx