14. Grafi
I grafi sono strutture dati che rappresentano relazioni tra nodi (vertici) collegati da archi. MALDA supporta sia grafi diretti sia grafi non diretti, con archi pesati e proprietà opzionali sugli archi.
14.1 Sintassi letterale Graph
I grafi si creano con la sintassi letterale graph directed { ... } o graph undirected { ... }:
// Create a directed graph
var g = graph directed {
nodes: ["A", "B", "C", "D"],
edges: [
{ from: "A", to: "B", weight: 4 },
{ from: "A", to: "C", weight: 2 },
{ from: "B", to: "D", weight: 5 },
{ from: "C", to: "D", weight: 1 }
]
};
// Create an undirected graph
var g2 = graph undirected {
nodes: ["X", "Y", "Z"],
edges: [
{ from: "X", to: "Y", weight: 10 },
{ from: "Y", to: "Z", weight: 5 }
]
};
// Empty graph
var g3 = graph directed {};
14.2 Operazioni sui grafi
I grafi offrono metodi per manipolare nodi e archi:
var g = graph directed {};
// Add nodes
g.addNode("A");
g.addNode("B", "data"); // Optional node data
// Add edges
g.addEdge("A", "B", 5); // from, to, weight
g.addEdge("A", "C", 3, dict { "label": "important" }); // with properties
// Query graph
print(g.hasNode("A")); // true
print(g.hasEdge("A", "B")); // true
print(g.getWeight("A", "B")); // 5
print(g.nodeCount()); // 2
print(g.edgeCount()); // 1
print(g.isDirected()); // true
// Get neighbors
var neighbors = g.getNeighbors("A"); // ["B", "C"]
// Get all nodes and edges
var nodes = g.nodes(); // ["A", "B", "C"]
var edges = g.edges(); // [[from, to, weight], ...]
// Remove nodes and edges
g.removeEdge("A", "B");
g.removeNode("C");
14.3 Algoritmi sui grafi
I grafi supportano gli algoritmi classici sui grafi:
var g = graph directed {
nodes: ["A", "B", "C", "D"],
edges: [
{ from: "A", to: "B", weight: 4 },
{ from: "A", to: "C", weight: 2 },
{ from: "B", to: "D", weight: 5 },
{ from: "C", to: "D", weight: 1 }
]
};
// Breadth-first search
var visited = g.bfs("A"); // ["A", "B", "C", "D"]
var pathResult = g.bfs("A", "D"); // { path: ["A", "B", "D"], found: true }
// Depth-first search
var dfsVisited = g.dfs("A"); // ["A", "B", "D", "C"]
var dfsPath = g.dfs("A", "D"); // { path: ["A", "B", "D"], found: true }
// Shortest path (Dijkstra's algorithm)
var shortest = g.shortestPath("A", "D");
print(shortest.path); // ["A", "C", "D"]
print(shortest.distance); // 3
print(shortest.found); // true
// Topological sort (directed graphs only)
var topo = g.topologicalSort();
print(topo.order); // ["A", "B", "C", "D"] or ["A", "C", "B", "D"]
print(topo.valid); // true
// Connected components
var components = g.connectedComponents();
// Returns array of arrays, each containing node IDs in a component
// Check for cycles
print(g.isCyclic()); // false
// Minimum spanning tree (undirected graphs only)
var gUndirected = graph undirected {
nodes: ["A", "B", "C"],
edges: [
{ from: "A", to: "B", weight: 1 },
{ from: "B", to: "C", weight: 2 },
{ from: "A", to: "C", weight: 4 }
]
};
var mst = gUndirected.minimumSpanningTree();
print(mst.edges); // [[from, to, weight], ...]
print(mst.totalWeight); // 3
14.4 Serializzazione dei grafi
I grafi si possono serializzare in formato JSON per l'archiviazione o la trasmissione, e deserializzare per ricostruire il grafo. Il formato di serializzazione memorizza i nodi una sola volta e li referenzia per ID negli archi, evitando la duplicazione dei dati.
var g = graph directed {
nodes: ["A", "B", "C"],
edges: [
{ from: "A", to: "B", weight: 5 },
{ from: "B", to: "C", weight: 3 }
]
};
// Serialize to JSON string
var json = g.serialize();
print(json);
// Serialize directly to file
g.serialize("graph.json");
// Deserialize from JSON string
var g2 = g.deserialize(json);
// Deserialize from file
var g3 = g.deserialize("graph.json");
// Verify the deserialized graph
print(g2.nodeCount()); // 3
print(g2.edgeCount()); // 2
print(g2.hasEdge("A", "B")); // true
Formato di serializzazione:
Il formato JSON memorizza i grafi in modo efficiente:
isDirected- Booleano che indica se il grafo è direttonodes- Array di oggetti nodo, ciascuno conidedataopzionaleedges- Array di oggetti arco, ciascuno confrom,to,weightepropertiesopzionali
{
"isDirected": true,
"nodes": [
{ "id": "A", "data": null },
{ "id": "B", "data": "node data" }
],
"edges": [
{ "from": "A", "to": "B", "weight": 5.0, "properties": null }
]
}
Dati dei nodi e proprietà degli archi:
I dati dei nodi e le proprietà degli archi vengono preservati integralmente durante la serializzazione:
var g = graph directed {};
g.addNode("A", "important data");
g.addNode("B", 42);
g.addEdge("A", "B", 5, dict { "label": "primary", "type": "connection" });
var json = g.serialize();
var g2 = g.deserialize(json);
print(g2.getNode("A")); // "important data"
print(g2.getNode("B")); // 42
// Edge properties are preserved internally
Grafi non diretti:
Per i grafi non diretti, gli archi vengono memorizzati una sola volta nel formato serializzato e gli archi inversi vengono creati automaticamente durante la deserializzazione:
var g1 = graph undirected {
nodes: ["X", "Y"],
edges: [
{ from: "X", to: "Y", weight: 10 }
]
};
var json = g1.serialize();
var g2 = g1.deserialize(json);
// Both directions work after deserialization
print(g2.hasEdge("X", "Y")); // true
print(g2.hasEdge("Y", "X")); // true
14.5 Riferimento dei metodi Graph
addNode(id, data?)- Aggiunge un nodo con dati opzionaliaddEdge(from, to, weight?, properties?)- Aggiunge un arco con peso e proprietà opzionaliremoveNode(id)- Rimuove un nodo e tutti i suoi archiremoveEdge(from, to)- Rimuove un arcogetNode(id)- Restituisce i dati del nodogetNeighbors(id)- Restituisce l'array degli ID dei nodi vicinigetEdges(from?, to?)- Restituisce gli archi (con filtro opzionale)hasNode(id)- Verifica se il nodo esistehasEdge(from, to)- Verifica se l'arco esistegetWeight(from, to)- Restituisce il peso dell'arconodeCount()- Restituisce il numero di nodiedgeCount()- Restituisce il numero di archiisDirected()- Verifica se il grafo è direttonodes()- Restituisce l'array di tutti gli ID dei nodiedges()- Restituisce l'array di tutti gli archi come tuple [from, to, weight]serialize(filePath?)- Serializza il grafo in una stringa JSON o in un filedeserialize(jsonOrFilePath)- Deserializza il grafo da una stringa JSON o da un filebfs(start, target?)- Breadth-first searchdfs(start, target?)- Depth-first searchshortestPath(from, to)- Cammino minimo con l'algoritmo di DijkstratopologicalSort()- Ordinamento topologico (solo grafi diretti)connectedComponents()- Trova tutte le componenti connesseisCyclic()- Verifica se il grafo contiene cicliminimumSpanningTree()- MST con l'algoritmo di Kruskal (solo grafi non diretti)
14.6 Applicazioni dei grafi
I grafi sono strutture dati versatili, con molte applicazioni pratiche. Ecco i casi d'uso più comuni, organizzati per categoria:
Ricerca di percorsi e routing
1. Pianificazione dei percorsi e navigazione
Trova i percorsi ottimali tra le località usando gli algoritmi di cammino minimo. I pesi degli archi possono rappresentare distanza, tempo o costo.
var roadNetwork = graph directed {
nodes: ["Home", "Work", "Store", "Park"],
edges: [
{ from: "Home", to: "Work", weight: 15 },
{ from: "Home", to: "Store", weight: 5 },
{ from: "Store", to: "Work", weight: 12 }
]
};
var route = roadNetwork.shortestPath("Home", "Work");
print("Optimal route: " + route.path);
print("Total distance: " + route.distance);
2. Ottimizzazione della supply chain e della logistica
Ottimizza le rotte di spedizione e riduce i tempi di consegna o i costi di trasporto nelle reti della supply chain.
var supplyChain = graph directed {
nodes: ["Factory", "Warehouse1", "Warehouse2", "Retailer"],
edges: [
{ from: "Factory", to: "Warehouse1", weight: 50 },
{ from: "Factory", to: "Warehouse2", weight: 60 },
{ from: "Warehouse1", to: "Retailer", weight: 20 },
{ from: "Warehouse2", to: "Retailer", weight: 15 }
]
};
var optimalRoute = supplyChain.shortestPath("Factory", "Retailer");
Gestione delle dipendenze
3. Gestione delle dipendenze tra task
Determina l'ordine di esecuzione corretto per i task con dipendenze usando l'ordinamento topologico. Utile per i sistemi di build, i prerequisiti dei corsi e la pianificazione dei progetti.
var buildGraph = graph directed {
nodes: ["compile", "test", "package", "deploy"],
edges: [
{ from: "compile", to: "test", weight: 1 },
{ from: "test", to: "package", weight: 1 },
{ from: "package", to: "deploy", weight: 1 }
]
};
var buildOrder = buildGraph.topologicalSort();
if (buildOrder.valid) {
print("Build order: " + buildOrder.order);
}
4. Risoluzione delle dipendenze del compilatore
Determina l'ordine di compilazione dei moduli e rileva le dipendenze circolari che impedirebbero la compilazione.
var moduleDeps = graph directed {
nodes: ["utils", "parser", "lexer", "compiler"],
edges: [
{ from: "lexer", to: "parser", weight: 1 },
{ from: "parser", to: "compiler", weight: 1 },
{ from: "utils", to: "parser", weight: 1 }
]
};
if (!moduleDeps.isCyclic()) {
var buildOrder = moduleDeps.topologicalSort();
print("Compilation order: " + buildOrder.order);
} else {
print("Circular dependency detected!");
}
Analisi di rete
5. Analisi dei social network
Identifica i gruppi di amici, analizza la diffusione dell'influenza e trova i cammini di connessione più brevi tra le persone.
var socialNetwork = graph undirected {
nodes: ["Alice", "Bob", "Charlie", "Diana", "Eve"],
edges: [
{ from: "Alice", to: "Bob", weight: 1 },
{ from: "Bob", to: "Charlie", weight: 1 },
{ from: "Diana", to: "Eve", weight: 1 }
]
};
var groups = socialNetwork.connectedComponents();
print("Friend groups: " + groups);
var connectionPath = socialNetwork.bfs("Alice", "Charlie");
6. Pianificazione delle infrastrutture di rete
Progetta reti convenienti (cavo, fibra, strade) che collegano tutti i nodi con il costo totale minimo, usando gli alberi di copertura minimi.
var cityNetwork = graph undirected {
nodes: ["CityA", "CityB", "CityC", "CityD"],
edges: [
{ from: "CityA", to: "CityB", weight: 100 },
{ from: "CityB", to: "CityC", weight: 150 },
{ from: "CityA", to: "CityC", weight: 200 },
{ from: "CityC", to: "CityD", weight: 80 }
]
};
var mst = cityNetwork.minimumSpanningTree();
print("Optimal network edges: " + mst.edges);
print("Total cost: " + mst.totalWeight);
7. Analisi della rete elettrica e della power grid
Progetta reti di distribuzione dell'energia efficienti e identifica i componenti isolati della rete.
var powerGrid = graph undirected {
nodes: ["PowerPlant", "Substation1", "Substation2", "CityA"],
edges: [
{ from: "PowerPlant", to: "Substation1", weight: 100 },
{ from: "Substation1", to: "CityA", weight: 50 },
{ from: "Substation1", to: "Substation2", weight: 80 }
]
};
var efficientGrid = powerGrid.minimumSpanningTree();
var gridComponents = powerGrid.connectedComponents();
Modellazione delle relazioni
8. Modellazione di workflow e processi
Modella i workflow, rileva i potenziali deadlock e valida i flussi di processo controllando la presenza di cicli.
var workflow = graph directed {
nodes: ["start", "process", "validate", "end"],
edges: [
{ from: "start", to: "process", weight: 1 },
{ from: "process", to: "validate", weight: 1 },
{ from: "validate", to: "end", weight: 1 }
]
};
if (workflow.isCyclic()) {
print("Workflow has cycles - potential deadlock!");
} else {
print("Workflow is valid");
}
9. Gerarchia organizzativa e struttura di reporting
Modella la struttura aziendale, trova le catene di reporting e identifica i livelli di management usando la ricerca in ampiezza.
var orgChart = graph directed {
nodes: ["CEO", "VP1", "VP2", "Manager1", "Employee1"],
edges: [
{ from: "CEO", to: "VP1", weight: 1 },
{ from: "CEO", to: "VP2", weight: 1 },
{ from: "VP1", to: "Manager1", weight: 1 },
{ from: "Manager1", to: "Employee1", weight: 1 }
]
};
var reportingChain = orgChart.bfs("CEO", "Employee1");
print("Reporting chain: " + reportingChain.path);
11. Sistemi di raccomandazione
Trova elementi simili, prodotti correlati o raccomandazioni di contenuti esplorando le relazioni tra vicini.
var productGraph = graph undirected {
nodes: ["ProductA", "ProductB", "ProductC", "ProductD"],
edges: [
{ from: "ProductA", to: "ProductB", weight: 0.9 },
{ from: "ProductB", to: "ProductC", weight: 0.7 },
{ from: "ProductC", to: "ProductD", weight: 0.5 }
]
};
var recommendations = productGraph.getNeighbors("ProductA");
print("Recommended products: " + recommendations);
var relatedCluster = productGraph.bfs("ProductA");
Vedi anche
- 4. Tipi di dati - Altre strutture dati in MALDA
- 6. Array - Strutture dati array
- 33. Esempi - Altri esempi completi