PrEDA

19 entradas

Algoritmos de programación para resolver problemas informáticos: divide y vencerás, voraz, programación dinámica, vuelta atrás y ramificación y poda.

Grafos, mapas y matrices de adyacencia

PrEDA: grafos, el algoritmo de Dijkstra mejorado

Vengo a traer una revisión del post anterior. Sobre el algoritmo de Dijkstra para cálculo de caminos mínimos entre nodos de un grafo, a modo de code-kata. Son unas modificaciones para hacer que se pueda elegir el nodo inicial desde el que arrancamos. Y una sencilla ordenación de los nodos de la lista de adyacencia. Así de esta forma se puede poner en la variable global definida como INITIAL_NODE el nodo desde el que buscaremos los caminos mínimos. El código He marcado en negrita las modificaciones con respecto del anterior post. <?php define(‘NUMBER_OF_NODES’, 17); define(‘INITIAL_NODE’, 3); define(‘NUMBER_OF_EDGES_PER_NODE’, 3); define(‘IS_DIRECTED_GRAPH’, true); $adjacencyList = array(); fillRandomCosts($adjacencyList); printToScreen($adjacencyList); $special = $predecessor = array(); dijkstra($adjacencyList, $special, $predecessor); echo ‘FINAL> Special: ‘.implode(‘-‘, $special).PHP_EOL .’FINAL> Predecessor: ‘.implode(‘-‘, $predecessor).PHP_EOL; function dijkstra($adjacencyList, &$special, &$predecessor) { // Fill C with not used nodes. $C = array(); for ($i = 0; $i < NUMBER_OF_NODES; ++$i) { if ($i != INITIAL_NODE) { $C[] = $i; } } // Fill special distances. for ($i = 0; $i < NUMBER_OF_NODES; ++$i) { if ($i != INITIAL_NODE) { $special[$i] = distanceFromTo($adjacencyList, INITIAL_NODE, $i); if ($special[$i] < INF) { $predecessor[$i] = INITIAL_NODE; } else { $predecessor[$i] = ‘#’; } } else { $special[$i] = ‘I’; […]

Grafos, mapas y matrices de adyacencia

PrEDA: grafos, el algoritmo de Dijkstra

Después de haber repasado los fundamentos de los grafos: cómo se almacenan mediante matrices o listas de adyacencia, cómo se mantienen conectados mediante el algoritmo de Prim o Kruskal.. llegamos al algoritmo de Dijkstra. Con este algoritmo, que nos sirve tanto para grafos dirigidos como no dirigidos, podemos saber cuál es el camino de menor coste desde un nodo origen a todos los demás. Usa la estrategia de programación voraz, mediante la cual, vamos construyendo la solución sin tener que volver atrás en cada decisión que vamos tomando. Las aplicaciones de este algorimo son muchas más que los algoritmos predecesores. Por ejemplo, para calcular rutas en un mapa de carreteras, para conectar llamadas telefónicas mediante circuitos virtuales, enrutamiento de paquetes de red, coger varios autobuses/trenes/aviones minimizando coste o tiempo, buscar el mejor camino hasta el punto de recarga de un robot aspiradora, y un largo etcétera.. Estructura del algoritmo Se basa en la selección arbitraria de un nodo origen, en el uso de un conjunto de nodos pendientes de estudiar, un vector especial que almacena el coste de llegar a cada nodo, y un vector de predecesores que guarda el nodo anterior para llegar a cada posición. Mediante estas estructuras […]

Grafos, mapas y matrices de adyacencia

PrEDA: grafos, el algoritmo de Kruskal

Otro code-kata que vengo a traer.. Siguiendo con la serie de los relacionados con los grafos, el algoritmo de Kruskal. Es parecido al algoritmo de Prim del post anterior pero con distinto rendimiento según el tipo de grafo que tengamos. Si tenemos un grafo denso es más eficiente el algoritmo de Prim, pero si es un grafo disperso el de Kruskal. Este algoritmo busca conseguir el árbol de recubrimiento mínimo de un grafo. Es decir, cada arista que une vértices del grafo tiene un coste. Entonces, se busca el árbol de coste mínimo que conecte a todos los nodos. Este algoritmo se puede aplicar para construcción de redes con coste mínimo, por ejemplo: Internet, redes locales, eléctricas, de agua, etc.. Se utiliza la estrategia de programación voraz. Se van eligiendo las siguientes aristas de forma que no hay que volver atrás en la construcción de la solución. Se basa en el concepto de componente conexa y en el ordenamiento previo de las aristas. Vamos a ver.. Los pasos del algoritmo Se ordenan todas las aristas en orden creciente de coste. Se parte de un vector de componentes conexas que inicialmente es 1 componente por nodo. Cada vez que se añade […]

Grafos, mapas y matrices de adyacencia

PrEDA: grafos, el algoritmo de Prim

Hoy vuelvo a traer otro code-kata, siguiendo con la serie de algoritmos de programación relacionados con los grafos. Esta vez se trata del algoritmo de Prim, que sirve para calcular el árbol de recubrimiento de mínimo coste de un grafo. Es decir, tenemos un grafo que se compone de nodos, que se interconectan mediante aristas. Dichas conexiones entre nodo y nodo, tienen un coste. Entonces queremos hallar la forma de interconectar todo con el mínimo coste. Una aplicación directa de esto puede ser para una red de ordenadores, eléctrica, tuberías, carreteras, etcétera.. mediante este algoritmo puedes obtener la forma de mantener conectados todos los nodos del grafo con el mínimo coste. Este algoritmo usa la estrategia de programación voraz. Mediante esta se elige una forma de ir creando la solución sin rectificar volviendo atrás. Pasos del algoritmo de Prim Se divide en los siguientes: Se crean dos vectores, uno con los nodos que ya están en el árbol de recubrimiento mínimo (vector usados), y los que no (vector no usados). Se elige arbitrariamente el nodo raíz, se pone en el vector de los nodos del árbol usados. El resto de nodos en el vector de no usados. Se elige el […]

Grafos, mapas y matrices de adyacencia

PrEDA: grafos y mapas, ahora guardando en una lista de adyacencia..

Continuando con el post de ayer sobre cómo crear una matriz de adyacencia a partir de un mapa aleatorio, traigo otro code-kata. Es el mismo ejercicio de ayer, pero hoy para sacar en claro cómo almacenar un grafo en una lista de adyacencia. ¿Porqué una lista o matriz de adyacencia va a ser mejor o peor? Dependerá de cada grafo. Si el grafo es muy denso, porque tiene muchas aristas entre nodo y nodo, está demostrado que es mejor usar una matriz. Pero si el grafo es poco denso es mejor usar una lista de adyacencia. Igual que en el post de ayer, el escenario consiste en un mapa aleatorio, y nos podemos mover de casilla en casilla haciendo movimientos de peón de ajedrez. El coste de hacer cada movimiento, es el número que ponga en la casilla de destino. La interpretación de estos datos del mapa puede variar según lo que necesites. Al grano Un mapa generado aleatoriamente: | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ———————————————————————- 0 | 9 3 0 8 1 6 0 7 5 6 9 7 5 9 9 7 1 | 4 5 2 […]

Grafos, mapas y matrices de adyacencia

PrEDA: grafos, generando mapas aleatorios y su matriz de adyacencia..

Vuelvo de nuevo a traer otro code-kata. Esta vez estoy tratando de sacar en claro cómo generar matrices de adyacencia para trabajar con los grafos asociados a mapas. En este escenario tenemos un mapa, que se genera aleatoriamente. En cada casilla tenemos un coste de movernos a dicha casilla. Sólo podemos movernos a la casilla adyacente, como si se tratara del movimiento de un peón de ajedrez. Así pues, si estamos en la casilla (0,0) sólo podremos movernos a la (0,1), (1,1) y la de abajo, la (1,0). Así que ese coste de ese movimiento se representa por el valor del mapa generado. Y ese coste se guarda en la matriz de adyacencia como valor de la arista.

Números

PrEDA: el algoritmo de ordenamiento Mergesort

De nuevo con otro code-kata ya estoy por aquí de nuevo. Se trata ahora de un algoritmo más sencillo que los anteriores. El Heapsort se apoya en montículos para su ordenamiento. El Quicksort hace un pivotaje algo complejo para hacer la división del vector en dos más simples. En este caso, el Mergesort, hace un ordenamiento más simple. Mergesort aplica la estrategia de divide y vencerás para hacer el ordenamiento. Con esto, tenemos que un vector de números desordenado se divide en dos. Estos dos subvectores se ordenan por separado usando Mergesort recursivamente. Y al tenerlos ordenados, se fusionan en un vector final cogiendo elementos de uno u otro vector en orden creciente.

Números

PrEDA: el algoritmo de ordenamiento Heapsort

De nuevo traigo a modo de code-kata otro algoritmo de programación. En este caso no se usa ninguna técnica avanzada de programación. Sino que simplemente se usa el algoritmo de ordenamiento Heapsort, que como su nombre indica, se sirve de un heap (montículo) para hacer el ordenamiento. Como ya hablé en este otro post sobre los montículos, he reutilizado el código para hacer un par de clases genéricas que nos pueden servir para construir algoritmos más avanzados. PHP ya nos provee de una clase que implementa un montículo, el SplHeap. También tenemos en PHP una clase para un SplMinHeap y SplMaxHeap. Pero no podemos controlar cómo hacemos la valoración de mínimo o máximo para poner arriba del montículo los objetos ordenados. Así que no queda más remedio que extender la clase que nos provee PHP.

Números

PrEDA: el algoritmo de ordenamiento Quicksort

Vuelvo a la carga con los algoritmos de programación. Hoy traigo a modo de code-kata el algoritmo de ordenamiento Quicksort. En este algoritmo se utiliza la estrategia de programación divide y vencerás. Aplicándola, vamos dividiendo un vector inicial no ordenado, en subvectores que vamos a dividir una y otra vez recursivamente, hasta tener todos los elementos ordenados. Antes de dividir un vector en dos subvectores se aplica una técnica de pivotaje. Con este pivotaje de los elementos, obtendremos el vector dividido en 2 partes, más un elemento central llamado pivote. Este elemento pivote hará que en la parte izquierda del vector todos los elementos sean menores que el pivote. Y a su vez, en la parte derecha, todos los elementos serán mayores que el pivote.

Heap min max montículos

PrEDA: cómo programar montículos mínimos o máximos de cualquier tipo de objetos

Una de las mejores formas de ordenar objetos es haciendo montículos. Está demostrado matemáticamente cómo mejoran la eficiencia de muchos tipos de algoritmos. Así que aquí traigo a modo de code-kata, de nuevo, otro pequeño script con el que se puede implementar montículos mínimos o máximos de cualquier tipo de objetos. Un montículo es un tipo especial de árbol binario. Son binarios porque cada nodo tiene de máximo dos hijos. Además, éstos árboles están ordenados de arriba a abajo. Si es un montículo de mínimos entonces los elementos de arriba, los padres, siempre tienen un valor menor que los hijos. Si por el contrario es un montículo de máximos, entonces los elementos de arriba, los padres tienen un valor mayor. De esta forma nos aseguramos que siempre tengamos el elemento mayor o menor en el nodo raiz del árbol. Esto es muy útil para utilizarlos en todo tipo de algoritmos de programación. Punto de partida En PHP tenemos una clase, que implementa mediante un vector interno, un montículo. Se trata de la clase SplHeap. Entonces simplemente tenemos que hacer herencia de SplHeap extendiendo la clase SplHeap. El código es bien sencillo: <?php define(‘NUMBER_OF_NODES’, 30); class TNodo { public $cost; public […]

Gantt

PrEDA: priorizando tareas para obtener el máximo de beneficio

Hoy traigo a modo de code-kata otro problema resuelto. Se trata de resolver un problema muy muy común en informática, la gestión de tareas. En este caso, se trata de aplicar la gestión de tareas, o proyectos, con la condición de obtener el máximo de beneficio. Podríamos gestionar las tareas minimizando el tiempo en el sistema, distribuyendo tareas entre varios agentes.. O podríamos aplicarlo a otros ámbitos como la cosecha de cultivos, en donde habría que priorizar en función al tiempo de putrefacción y beneficio, teniendo en cuenta el tiempo necesario para cada cosecha. Es decir, en este caso, se trata de obtener el orden óptimo de tareas de forma que se maximize el beneficio. Tenemos entonces una duración de cada tarea, un beneficio, y un tiempo de entrega. Para resolver este problema, la mejor estrategia es la ramificación y poda. No existe una estrategia voraz, ni de divide y vencerás que podamos usar. Tampoco la vuelta atrás es la mejor solución, porque se trata de obtener la solución óptima en el menor tiempo posible. Entonces, tendremos que generar un árbol de posibilidades, un árbol de combinación de tareas. De forma que mientras que generamos el árbol tenemos que tener […]

php7-logo

PrEDA: algoritmo para encontrar todos los cuadrados latinos de tamaño NxN

Los cuadrados latinos son una aplicación matemática, utilizando combinatoria, para generar cuadrados con ciertos datos en cada casilla cumpliendo unas restricciones. Podemos encontrar cuadrados latinos por ejemplo en los famosos sudokus. También se puede aplicar a la forma de distribuir cultivos o fertilizantes en cuadrados de terrenos. También se pueden usar para generar puzzles, rompecabezas, o simplemente para practicar algorimos de programación.. Traigo entonces a modo de code-kata otro algoritmo, para resolver cuadrados latinos de tamaño NxN. Estos cuadrados latinos tienen la peculiaridad de que: en cada fila no se puede repetir el mismo dato, y en cada columna tampoco se puede repetir el mismo dato. En este caso, he usado números para llenar de datos el cuadrado, y estos números del 1 a N significan un color. Un cero significa que está sin llenar, y los números entonces que llenamos van desde 1 hasta N, siendo N el ancho y alto del cuadrado.