PHP

PHP es el lenguaje de programación de mayor aceptación entre las aplicaciones web. La gran mayoría de los CMS orientados al mundo de la web están desarrollados sobre PHP.

Preparado para correr sobre un servidor web, aunque en mucha menor medida también se puede utilizar en entorno de terminal. Tiene una curva de aprendizaje muy baja, es muy permisivo con los errores, es interpretado, no compilado y orientado a objetos, entre otras cosas.

Aunque sea un lenguaje muy aceptado por los principiantes, es muy atacado por muchos experimientados en la programación. No obstante es uno de mis lenguajes preferidos. Y sobre todo, al desarrollar para la web, siendo prácticos, es una de las primeras y mejores soluciones balanceando entre el conjunto de lenguajes disponibles.

PrEDA: esquemas de programación genéricos avanzados

2018-02-03 - Categorías: PHP / PrEDA
PrEDA algoritmos genericos

Otro HOWTO, code-kata, a traer vengo hoy..

XDD vengo con este post cargado de esquemas algorítmicos para programar. Estos esquemas algorítmicos son los que nos ayudan a programar las mejores soluciones. Así conseguimos llegar a las soluciones que a veces tenemos que encontrar, pero quizá no vemos en un principio cómo. Quizá el problema lo tenemos en que tarda mucho, o necesitamos demasiado espacio de almacenamiento.

Continuar leyendo..

Magento 1: cómo sacar las ventas por método de pago

2018-01-26 - Categorías: Magento / PHP
Magento ventas por mes y método de pago

Hoy traigo otro code-kata, esta vez para Magento y hecho en PHP. Se trata de un simple script que se lanza en línea de comandos. Con este script obtendremos primero todos los métodos de pago que se han usado al hacer los pedidos. Después, haremos un listado, por método de pago, y entre ciertas fechas, del monto de pedidos completados por método de pago.

Es decir, supongamos que queremos saber cómo han ido las ventas con el método de pago por tarjeta de crédito. O quizá queremos saber si la financiación es un método de pago que funciona bien. O quizá simplemente queremos tener el total de ventas entre fechas.

Pues todo esto es bastante sencillo sabiendo donde está cada cosa.

Métodos de pago usados en pedidos

A saber, los pedidos en Magento 1 guardan parte de sus datos principales en la tabla sales_flat_order. La información del método de pago utilizado en cada pedido se guarda en la tabla sales_flat_order_payment.

Entonces, sacando la columna method de la tabla sales_flat_order_payment tendremos todos los métodos de pago utilizados en los pedidos. Esto en lenguaje SQL queda así:

SELECT distinct(sfop.method)
FROM sales_flat_order_payment sfop
ORDER BY sfop.method ASC;

Ventas de los pedidos enviados

El siguiente paso será saber cómo sacar las cantidades de los pedidos completados. A saber, los pedidos completados se quedan con un status=complete y state=complete. Sí, es raro, Magento tiene internamente dos estados en cada pedido. Uno llamado status y otro llamado state. Estos estados están respectivamente en sus columnas. Así la consulta a la base de datos en SQL para sacar las ventas de un método de pago queda así:

SELECT sum(sfo.grand_total) total_sold
FROM sales_flat_order sfo
JOIN sales_flat_order_payment sfop ON sfop.parent_id = sfo.entity_id
WHERE sfo.state = 'complete' AND sfo.state = 'complete' AND sfop.method LIKE '".$methodCode."'
AND sfo.created_at > '".$startDate->format('Y-m-d H:i:s')."'
AND sfo.created_at < '".$endDate->format('Y-m-d H:i:s')."'"

Fíjate que la tabla sales_flat_order_payment se relaciona con sales_flat_order de la forma que marco en negrita.

El código completo

Ahora bien, haciendo un poco de ejercicio con un par de bucles, programando el script completo, nos queda algo tal que así:

<php

require_once __DIR__.'/app/Mage.php';

Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);

// Loading arguments.
if ($argc == 3) {
    $yearFrom = $argv[1];
    $yearTo = $argv[2];
} else {
    echo 'ERROR: Incorrect number of arguments.'.PHP_EOL;
    exit;
}

$sqlConnection = Mage::getSingleton('core/resource')->getConnection('core_read');

// Loading available method codes.
$query = 'SELECT distinct(sfop.method)
    FROM sales_flat_order_payment sfop
    ORDER BY sfop.method ASC;';
$methods = $sqlConnection->fetchAll($query);
$methodCodes = array();
foreach ($methods as $method) {
    $methodCodes[] = $method['method'];
}
//var_dump($methodCodes);

// Printing table in CSV format from January to December..
echo 'PAYMENT_METHOD';
for ($year = $yearFrom; $year <= $yearTo; ++$year) {
    for ($month = 1; $month <= 12; ++$month) {
        echo ','.$year.$month;
    }
}
echo PHP_EOL;
foreach ($methodCodes as $methodCode) {
    echo $methodCode;
    for ($year = $yearFrom; $year <= $yearTo; ++$year) {
        for ($month = 1; $month <= 11; ++$month) { 
            $startDate = new DateTime($year.'-'.$month.'-1 00:00:00'); 
            //echo 'Date from: '.$startDate->format('Y-m-d H:i:s').PHP_EOL;
            $endDate = new DateTime($year.'-'.($month + 1).'-1 00:00:00');
            //echo 'Date to: '.$endDate->format('Y-m-d H:i:s').PHP_EOL;

            $query = "SELECT sum(sfo.grand_total) total_sold
            FROM sales_flat_order sfo
            JOIN sales_flat_order_payment sfop ON sfop.parent_id = sfo.entity_id
            WHERE sfo.state = 'complete' AND sfo.state = 'complete' AND sfop.method LIKE '".$methodCode."'
            AND sfo.created_at > '".$startDate->format('Y-m-d H:i:s')."'
            AND sfo.created_at < '".$endDate->format('Y-m-d H:i:s')."'";
            $amount = $sqlConnection->fetchOne($query);
            echo ','.$amount;
        }
        $startDate = new DateTime($year.'-12-1 00:00:00');
        //echo 'Date from: '.$startDate->format('Y-m-d H:i:s').PHP_EOL;
        $endDate = new DateTime($year.'-12-31 23:59:59');
        //echo 'Date to: '.$endDate->format('Y-m-d H:i:s').PHP_EOL;

        $query = "SELECT sum(sfo.grand_total) total_sold
        FROM sales_flat_order sfo
        JOIN sales_flat_order_payment sfop ON sfop.parent_id = sfo.entity_id
        WHERE sfo.state = 'complete' AND sfo.state = 'complete' AND sfop.method LIKE '".$methodCode."'
        AND sfo.created_at > '".$startDate->format('Y-m-d H:i:s')."'
        AND sfo.created_at < '".$endDate->format('Y-m-d H:i:s')."'";
        $amount = $sqlConnection->fetchOne($query);
        echo ','.$amount;
    }
    echo PHP_EOL;
}

Este script tendremos que ponerlo en el directorio raiz del proyecto Magento para que enganche al Magento. Fíjate que necesita de dos parámetros de entrada que son el año de inicio y el año de fin. Además he marcado un objeto que proporciona Magento para estas cosas, la conexión a la base de datos que se guarda en $sqlConnection.

Ejecutando

Guardamos el script anterior en un fichero, por ejemplo llamado script.php. Si queremos las ventas desde el año 2017 al 2017, lo ejecutamos así desde línea de comandos:

$ php script.php 2017 2017

Veremos la salida por pantalla. Ahora bien, si redireccionamos la salida del script a un fichero, luego podremos visualizarlo mejor. Para esto lo ejecutamos así:

$ php script.php 2017 2017 > ventas_por_metodo_de_pago.csv

Ahora abrimos el fichero CSV con nuestro programa de hojas de cálculo favorito para ver los datos. Si no tienes, es muy recomendable el LibreOffice que lo puedes descargar gratis haciendo click aquí.

Así jugando un poco con los datos resultantes, podemos tener una gráfica de evolución de los métodos de pago. Como la imagen de cabecera de arriba. Podemos ver que algo está pasando en abril y por Navidades del año 2017 con uno de los métodos de pago.

Espero que sirva.

Un saludo.


PHP: qué es un Singleton y para qué nos puede servir

2018-01-21 - Categorías: General / PHP / Principios y patrones
Coding

Este es uno de los patrónes de diseño de software más sencillos de implementar. Se trata del Singleton, que simplemente es un tipo de objeto de programación. En Programación Orientada a Objetos (POO), tenemos este tipo de objetos que se usan para sólo instanciar uno y exclusivamente uno en todo el programa.

No sabemos cuántas veces ni en cuántos lugares se va a usar el objeto. Pero sí que sabemos que necesitamos que sólo exista uno como máximo. De aquí que viene su nombre de Singleton.

Se dice que viola los principios SOLID del diseño de software porque en sí mismo controla el proceso de creación, pero de hecho, en patrones más avanzados como en las fábricas es necesario implementarlos.

Continuar leyendo..

PHP: cómo geolocalizar visitantes, IPs o nombres de dominio

2017-12-14 - Categorías: General / GNU/Linux / PHP
La Tierra

No quiero perder las buenas costumbres, y esto de escribir en mi blog es una de ellas. Así que aquí estoy de nuevo con otro pequeño HOWTO para geolocalizar ordenadores por IP o servidores por nombre de dominio. Es realmente sencillo, un juego de niños, pero por si lo necesitas en algún proyecto aquí que lo dejo.

A continuación tienes cómo instalar en PHP5.6 las librerías en un sistema operativo Linux. Luego para PHP7 y enlaces a información sobre bases de datos relacionadas. Para geolocalizar dispositivos en Internet basta con utilizar uno de estos servicios. Quizá lo que necesitas es dar una traducción de tu sitio según la localización del visitante, mostrar mensajes por país, o desplegar países, regiones y localidades. También puedes usar las coordenadas de este servicio, pero no es muy fiable localizar con este sistema a tan bajo nivel pero por tener tendrás con esto unos datos orientativos para tu web.

Abajo del post tienes enlaces a Bases de Datos con países del mundo, regiones y localidades actualizadas. Lo mejor de todo, la versión simple es gratis para que puedas usarlo en tu proyecto. Sólo con nombrar en tu proyecto a la empresa autora.

También hay empresas que ofrecen bajo pago este servicio..

En PHP5.6

Aquí tenemos que instalar unas de librerías y la base de datos pública de GeoIP. En un servidor Linux con Ubuntu, Debian o compatible hacemos lo siguiente:

$ sudo apt-get install php-geoip
$ wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
$ gunzip GeoLiteCity.dat.gz
$ sudo mkdir /usr/share/GeoIP
$ sudo mv GeoLiteCity.dat /usr/share/GeoIP/GeoIPCity.dat

Con esto ya podemos usarlo en cualquier script o programa hecho en PHP así:

<?php
var_dump(geoip_record_by_name('jnjsite.com'));

Tenemos que ver por pantalla algo parecido a:

Array
 (
 [continent_code] => EU
 [country_code] => IE
 [country_code3] => IRL
 [country_name] => Ireland
 [region] => 07
 [city] => Dublin
 [postal_code] =>
 [latitude] => 53.333099365234
 [longitude] => -6.248899936676
 [dma_code] => 0
 [area_code] => 0
 )

Tengo que decir que la latitud y longitud son bastante aproximadas. Y dependerá bastante de tu proveedor de servicios de Internet si está bien localizado en las bases de datos de MaxMind. Pero lo que es el país, región y ciudad son bastante fiables, sobretodo el país.

En PHP7

Ahora es más fácil incluso con PHP7. Sólo tendremos que instalar la siguiente librería y se nos instalará todo de una vez:

$ sudo apt-get install php-geoip

Ya está, simplemente con esto ya podemos usar en el ordenador la geolocalización mediante PHP. Hay mucha funciones disponibles.

Terminando

Sólo me queda dejar algunos enlaces interesantes:


Symfony: cómo montar una API REST de tu plataforma completa en menos de 1 hora

2017-11-27 - Categorías: PHP / Symfony
Symfony logo

Hoy traigo un pequeño HOWTO con una joya de Symfony. Se trata de un bundle, plugin o módulo (como prefieras llamarlo) para Symfony. Es una plataforma completa en origen, pero podemos sólo usar el núcleo de dicha plataforma en nuestro proyecto. Así en menos de 1 hora tendremos montada nuestra API en nuestro proyecto. Es decir, integrando el núcleo de este bundle en nuestro proyecto, se generarán automáticamente todas las funcionalidades clásicas de una API REST de todo lo que tengamos en nuestro proyecto.

Para los no duchos en el tema, una API es un punto de entrada a nuestro proyecto. Por ejemplo, si tenemos animales en nuestro proyecto, y dejamos en la API la gestión de estos animales, podremos listarlos, editarlos, crearlos o borrarlos.. Todo remotamente desde la API, desde una aplicación móvil, desde otra web, alimentando otros sistemas informáticos con esta información, cualquier cosa que se nos ocurra se puede hacer con una API.

Es decir, por excelencia las APIs son la mejor forma de interconectar sistemas informáticos. Y no es que ahora se hayan puesto de moda, sino que en el año 2000 se pusieron de moda. Y con Symfony mediante este proyecto se simplifica mucho su puesta en marcha.

Continuar leyendo..


PrEDA: minimizando tiempo en el sistema

2017-11-22 - Categorías: PHP / PrEDA
Gantt

La gestión de los proyectos es la clave: puede marcar la diferencia entre el éxito o el desastre. La gestión de las tareas no se puede hacer de cualquier forma. Por esto que se han estudiado y se han establecido muchas estrategias. No cualquier software puede ser un buen gestor de proyectos. Por esto que existen estudios que lo integran, técnicas de programación.. y sobretodo en informática, que existen multitud de estrategias, ya que dentro de los ordenadores esto se aplica constantemente.

Los pasos clásicos a seguir son los siguientes:

  • Se establecen las tareas, fijando especificaciones.
  • Se estudian bien los tiempos que va a llevar cada tarea.
  • Se establece la estrategia para proceder.
  • Se resuelve el órden, con lo cual, aquí se obtienen las fechas de finalización de cada tarea y el tiempo total.
  • Se ejecuta el proyecto.

Como es obvio, en el primer paso se cierran especificaciones y cualquier modificación posterior invalida el algoritmo, y hay que recalcular todo.

A modo de code-kata traigo hoy una estrategia voraz para organizar un proyecto con el objetivo de minimizar el tiempo de espera de las tareas en el sistema. Suponiendo que todas las tareas son igual de importantes, se trata de hacer que esperen en total lo mínimo posible. Se trata de una estregia voraz porque simplemente se elige la siguientes tareas que menos tiempo se tarde en terminar, sin rectificar ni volver atrás. Así se terminarán primero las que menos se tarde, y más rápido se irán entregando las tareas. Otra cosa es el beneficio o la importancia que pueda tener cada tarea, pero para esto hay otro algoritmo 😉

Continuar leyendo..

Symfony: cómo montar un programador de tareas en menos de 1 hora

2017-11-21 - Categorías: PHP / Symfony
Symfony logo

Cuando has valorado bien un proyecto, y es tal el nivel de personalización que se quiere alcanzar, no queda más remedio que hacer un desarrollo artesanal, o a la larga te arrepentirás. Ya que partir de un CMS prefabricado es, a veces, más que una ayuda un lastre. Cuando quieres la máxima flexibilidad, velocidad en las modificaciones.. y por supuesto, la máxima calidad.. tienes que ir al desarrollo sobre frameworks. Ya hablé sobre esto en otro post anterior..

Hoy vengo a empezar con una serie de plugins o bundles para Symfony, hoy con uno para programar tareas. Ya sea para enviar emails transaccionales, para programar tareas de marketing, crawleos de sitios web, generar sitemaps, o lo que sea que quieras automatizar.. Aquí verás como en un rato tienes montado un estupendo programador de tareas.

Con este programador de tareas ya tienes el panel de control hecho. Es decir, te evitas la construcción del panel, y podrás ir al siguiente paso en menos de una hora. Que será directamente el ir a crear las tareas tal y como las necesites para tu proyecto 🙂

Continuar leyendo..


PrEDA: grafos, el algoritmo de Dijkstra

2017-10-29 - Categorías: PHP / PrEDA
Grafos, mapas y matrices de adyacencia

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 de datos, vamos estudiando las aristas entre nodo y nodo. Así de esta manera, se van recalculando el vector de los costes y el de los predecesores. En este ejemplo se parte del nodo inicial 0. Y se van estudiando nodo a nodo, los caminos posibles hasta el siguiente nodo en estudio. Cuando ya hemos estudiado todos los nodos, tendremos el camino de menor coste a cualquier nodo desde el nodo origen.

Por ejemplo

En este caso está configurado el script para generar grafos dirigidos:

0 --> 1(3)
1 --> 3(4) --> 2(1)
2 --> 5(2) --> 3(1)
3 --> 1(1) --> 4(4)
4 --> 6(1)
5 --> 3(5)
6 --> 4(1)
INITIAL> Not used nodes: 1-2-3-4-5-6
INITIAL> Special: 3-INF-INF-INF-INF-INF
INITIAL> Predecessor: 0-#-#-#-#-#
>>>> Found min edge! $adjacentList[0][1]=3
>>>> Next min node to use is 1
Not used nodes: 2-3-4-5-6
Special: 3-4-7-INF-INF-INF
Predecessor: 0-1-1-#-#-#
>>>> Found min edge! $adjacentList[1][2]=1
>>>> Next min node to use is 2
Not used nodes: 3-4-5-6
Special: 3-4-5-INF-6-INF
Predecessor: 0-1-2-#-2-#
>>>> Found min edge! $adjacentList[1][3]=4
>>>> Found min edge! $adjacentList[2][3]=1
>>>> Next min node to use is 3
Not used nodes: 4-5-6
Special: 3-4-5-9-6-INF
Predecessor: 0-1-2-3-2-#
>>>> Found min edge! $adjacentList[2][5]=2
>>>> Next min node to use is 5
Not used nodes: 4-6
Special: 3-4-5-9-6-INF
Predecessor: 0-1-2-3-2-#
>>>> Found min edge! $adjacentList[3][4]=4
>>>> Next min node to use is 4
Not used nodes: 6
Special: 3-4-5-9-6-10
Predecessor: 0-1-2-3-2-4
FINAL> Special: 3-4-5-9-6-10
FINAL> Predecessor: 0-1-2-3-2-4

El resultado final son los dos vectores de las dos últimas líneas. El vector especial, nos indica el coste hasta el nodo i. Por ejemplo, veamos el camino desde el nodo 0 al 4. Tiene un coste de 9 unidades y su camino se construye de atrás a adelante 4-3-2-1-0, mediante el vector predecesor. Es decir, el camino de mínimo coste posible es el 0-1-2-3-4. Si estudiamos las opciones podemos ver que así es.

El código

<?php define('NUMBER_OF_NODES', 7);
define('NUMBER_OF_EDGES_PER_NODE', 2); 
define('IS_DIRECTED_GRAPH', true); 

$adjacentList = array(); 
fillRandomCosts($adjacentList); 
printToScreen($adjacentList); 

$special = $predecessor = array(); 
dijkstra($adjacentList, $special, $predecessor); 

echo 'FINAL> Special: '.implode('-', $special).PHP_EOL
    .'FINAL> Predecessor: '.implode('-', $predecessor).PHP_EOL;

function dijkstra($adjacentList, &$special, &$predecessor)
{
    // Fill C with not used nodes.
    $C = array();
    for ($i = 1; $i < NUMBER_OF_NODES; ++$i) {
        $C[] = $i;
    }

    // Fill special distances.
    for ($i = 1; $i < NUMBER_OF_NODES; ++$i) {
        $special[$i] = distanceFromTo($adjacentList, 0, $i);
        if ($special[$i] < INF) { 
            $predecessor[$i] = 0; 
        } else { 
            $predecessor[$i] = '#'; 
        } 
    } 
    
    echo 'INITIAL> Not used nodes: '.implode('-', $C).PHP_EOL
        .'INITIAL> Special: '.implode('-', $special).PHP_EOL
        .'INITIAL> Predecessor: '.implode('-', $predecessor).PHP_EOL;

    // Study nodes in C to update $special and predecessor vectors.
    while (count($C) > 1) {
        $v = selectNextNodeThatMinimizesSpecial($adjacentList, $C, $special);
        $C = array_diff($C, array($v));

        if ($v == -1) {
            echo 'IMPOSSIBLE TO FIND DIJKSTRA WITH ALL NODES! Cannot achieve all nodes!'.PHP_EOL;
            exit;
        }

        foreach ($C as $w) {
            if ($special[$w] > $special[$v] + distanceFromTo($adjacentList, $v, $w)) {
                $special[$w] = $special[$v] + distanceFromTo($adjacentList, $v, $w);
                $predecessor[$w] = $v;
            }
        }

        echo 'Not used nodes: '.implode('-', $C).PHP_EOL
            .'Special: '.implode('-', $special).PHP_EOL
            .'Predecessor: '.implode('-', $predecessor).PHP_EOL;
    }
}

function selectNextNodeThatMinimizesSpecial($adjacentList, &$C, &$special)
{
    $minCost = INF;
    $minNode = -1;

    for ($i = 0; $i < NUMBER_OF_NODES; ++$i) {
        foreach ($C as $node) {
            if (!in_array($i, $C)
            and isset($adjacentList[$i][$node])
            and $adjacentList[$i][$node] < $minCost) { 
                echo '>>>> Found min edge! $adjacentList['.$i.']['.$node.']='.$adjacentList[$i][$node].PHP_EOL;
                $minCost = $adjacentList[$i][$node];
                $minNode = $node;
            }
        }
    }

    echo '>>>> Next min node to use is '.$minNode.PHP_EOL;

    return $minNode;
}

function distanceFromTo($adjacentList, $from, $to)
{
    if (isset($adjacentList[$from][$to])) {
        return $adjacentList[$from][$to];
    } else {
        return INF;
    }
}

function fillRandomCosts(&$adjacentList)
{
    for ($i = 0; $i < NUMBER_OF_NODES; ++$i) {
        $added = false;
        while (!$added) {
            for ($j = 0; $j < NUMBER_OF_EDGES_PER_NODE; ++$j) {
                $adjacentNode = rand(0, NUMBER_OF_NODES - 1);
                if ($adjacentNode != $i and $adjacentNode != $j) {
                    $adjacentNodeCost = rand(1, 5);
                    $adjacentList[$i][$adjacentNode] = $adjacentNodeCost;
                    if (!IS_DIRECTED_GRAPH) {
                        $adjacentList[$adjacentNode][$i] = $adjacentNodeCost;
                    }
                    $added = true;
                }
            }
        }
    }
}
function printToScreen($adjacentList)
{
    for ($i = 0; $i < NUMBER_OF_NODES; ++$i) { echo $i; foreach ($adjacentList[$i] as $key => $value) {
            echo ' --> '.$key.'('.$value.')';
        }
        echo PHP_EOL;
    }
}


PrEDA: grafos, el algoritmo de Prim

2017-10-26 - Categorías: PHP / PrEDA
Grafos, mapas y matrices de adyacencia

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.

Continuar leyendo..

© 2024 JnjSite.com - MIT license

Sitio hecho con WordPress, diseño y programación del tema por Jnj.