|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: Dec 2007
Messaggi: 642
|
[C] Alberi n-ari: quale implementazione?
Salve a tutti... gradirei dalle vostre menti geniali un suggerimento: devo calcolare il diametro (il più lungo cammino minimo tra due nodi) di un albero generico. Più che un consiglio sull'algoritmo (che comunque non mi dispiacerebbe
![]() |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Oct 2006
Città: Roma
Messaggi: 1383
|
esiste una nota biezione tra l'insieme degli alberi N-ari e quello degli alberi binari. per rappresentare un albero N-ario basta che ogni nodo sia rappresentato dalla seguente struct:
Codice:
typedef struct _NODE { struct _NODE *m_pFirstChild; struct _NODE *m_pNextSibling; } NODE, *PNODE; |
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: Dec 2007
Messaggi: 642
|
Grazie mille, ma la struttura "figlio a sinistra - fratello a destra" permette un accesso sequenziale e non conserva esplicitamente i legami di parentela; come faccio poi a calcolare il diametro? A me - ammetto di essere molto disabituato alla programmazione - non è venuto in mente niente
![]() |
![]() |
![]() |
![]() |
#4 | |
Senior Member
Iscritto dal: Mar 2005
Città: Morimondo city
Messaggi: 5491
|
Quote:
__________________
Khelidan |
|
![]() |
![]() |
![]() |
#5 |
Senior Member
Iscritto dal: Dec 2007
Messaggi: 642
|
Che da un singolo nodo non è possibile risalire alla paternità del nodo stesso; e - ripeto, sono io scarso - non mi viene in mente come calcolare il diametro senza sfruttare la paternità.
|
![]() |
![]() |
![]() |
#6 |
Senior Member
Iscritto dal: Nov 2005
Messaggi: 2773
|
Riguardo la definizione di diametro, ho trovato una definizione differente: definito C(l) la cardinalità dei nodi del livello l si definisce diametro il massimo valore di C(l).
Per risolvere il problema credo che il modo migliore sia fare una visita per livelli contando di volta in volta i nodi del livello corrente ed aggiornando eventualmente il massimo. |
![]() |
![]() |
![]() |
#7 |
Senior Member
Iscritto dal: Dec 2007
Messaggi: 642
|
Può darsi che la definizione comunemente intesa di diametro sia quella - io non ne ho idea - ma purtroppo io devo trovare il diametro secondo la definizione che ho dato io
![]() |
![]() |
![]() |
![]() |
#8 | |
Senior Member
Iscritto dal: Oct 2006
Città: Roma
Messaggi: 1383
|
Quote:
Codice:
typedef struct _NODE { struct _NODE *m_pParent; struct _NODE *m_pFirstChild; struct _NODE *m_pNextSibling; } NODE, *PNODE; |
|
![]() |
![]() |
![]() |
#9 | |
Senior Member
Iscritto dal: Oct 2006
Città: Roma
Messaggi: 1383
|
Quote:
caso base, albero N-ario generato dalla funzione banale: ammettiamo che la tesi abbia senso e diamola per vera. caso induttivo, albero generato da una generica funzione fk: ci sono tre casi distinti: 1) entrambi gli estremi del cammino si trovano dentro uno dei k alberi in input - teorema verificato per ipotesi induttiva. 2) uno degli estremi del cammino é la nuova radice e l'altro si trova dentro uno dei k alberi, diciamo l'i-esimo albero; per costruzione il cammino tra i due nodi deve passare per la radice dell'albero i-esimo (chiamiamola radice i-esima), quindi il cammino in questione é formato dall'arco che unisce la radice i-esima alla nuova radice comune piu il cammino minimo tra la radice i-esima e l'estremo che sta dentro l'albero i-esimo; quest'ultimo cammino é minimo e unico per ipotesi induttiva, quindi tutto il cammino é minimo e unico. 3) i due estremi del cammino si trovano in due diversi alberi in input, diciamo l'i-esimo e il j-esimo; per costruzione il cammino passa per la nuova radice comune, quindi semplicemente vale lo stesso l'argomento del caso precedente applicato due volte ai due alberi, i-esimo e j-esimo. Ultima modifica di fero86 : 06-01-2010 alle 12:16. |
|
![]() |
![]() |
![]() |
#10 |
Senior Member
Iscritto dal: Oct 2006
Città: Roma
Messaggi: 1383
|
altra dimostrazione, molto piu semplice: se tra due nodi di un albero esistessero piu cammini distinti almeno in parte, l'albero conterrebbe cicli e non sarebbe un albero. é detta in maniera solo intuitiva ma si puó approfondire.
|
![]() |
![]() |
![]() |
#11 | |
Senior Member
Iscritto dal: Dec 2005
Città: Istanbul
Messaggi: 1817
|
Quote:
In ogni caso il puntatore al padre non ti serve perche' puoi ottenere il risultato che cerchi con una visita ricorsiva dell'albero. L'osservazione di base da fare e' che il diametro massimo per un albero e' uno dei seguenti: - Il diametro massimo di uno dei figli (ovvero un cammino che non passa per la radice) - Presi i due figli di altezza maggiore, sommi le due altezze + 1 (ovvero un cammino che passa per la radice). Questo solo nel caso ci siano almeno due figli. Procedi ricorsivamente con questo concetto ed ottieni la soluzione. Come vedi non e' affatto necessario sapere chi e' il padre.
__________________
One of the conclusions that we reached was that the "object" need not be a primitive notion in a programming language; one can build objects and their behaviour from little more than assignable value cells and good old lambda expressions. —Guy Steele Ultima modifica di marco.r : 06-01-2010 alle 13:53. |
|
![]() |
![]() |
![]() |
#12 |
Member
Iscritto dal: May 2006
Messaggi: 86
|
Con diametro, solitamente si intende:
Codice:
max(min(d(x,y))) Ora, per risolvere il problema, quello che ti consiglio di fare è usare http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm con questo, per ogni nodo trovi il costo minimo per andare dal nodo x a tutti gli altri nodi dell'albero. Se cicli l'algoritmo su tutti i nodi, e prendi il massimo dei valori che ottieni, hai il diametro cercato. Poi ci sono casi particolari: se i pesi fossero tutti non negativi, allora ti potresti limitare a controllare i costi delle foglie dell'albero ... tutto sta nello stabilire cosa intendi per generico ![]() PS: se i costi degli archi fossero tutti unari, il problema si converte nel trovare la profondità dell'albero, problema che, come han detto sopra, si risolve in modo elementare con una funzione ricorsiva: Codice:
h(padre) = 1+max(h(figli)) Ultima modifica di BlackAuron : 06-01-2010 alle 13:56. |
![]() |
![]() |
![]() |
#13 | |||
Senior Member
Iscritto dal: Oct 2006
Città: Roma
Messaggi: 1383
|
Quote:
Quote:
![]() Quote:
|
|||
![]() |
![]() |
![]() |
#14 |
Member
Iscritto dal: May 2006
Messaggi: 86
|
d è la distanza tra due nodi.
min{d(x,y)} è la distanza minima tra due nodi x e y max{min{d(x,y)}} è il massimo dell'insieme delle distanze minime tra due nodi dell'albero. Solitamente quello che si cerca è il diametro di un grafo, ma dal momento che un albero altro non è che un grafo non orientato e privo di cicli ... la definizione si può riutilizzare. Quella funzione ricorsiva lo so pure io che calcola la profondità di un albero, e infatti ho specificato che è da utilizzarsi nel caso in cui il costo degli archi dell'albero sia 1. In caso contrario ( nessuno vieta che gli archi che compongono l'albero abbiano un costo non unitario), bisogna ricorrere ad altri escamotages. PS: peso = costo di un arco |
![]() |
![]() |
![]() |
#15 |
Senior Member
Iscritto dal: Dec 2007
Messaggi: 642
|
Allora intanto grazie infinite per i vostri post che vi hanno portato via tempo (un giorno sarò abbastanza esperto da rendere il favore al forum, lo prometto) e le mie scuse per l'imprecisione dei miei: allora ovviamente sì, esiste un solo cammino tra due nodi perché per definizione l'albero è un grafo connesso e aciclico. Sorry per la fesseria, è che c'è proprio scritto così (la consegna dev'essere stata mutuata da un esercizio sui grafi generici) e ho copiato.
Allora, per "albero generico" intendo un albero coi rami non pesati (il costo è unitario) e dove ogni nodo può avere un numero qualsiasi di figli. Il diametro che devo trovare - chiedo scusa ancora per l'eventuale uso inappropriato del termine - è la distanza, espressa in numero di archi, tra i due nodi più lontani che si possono trovare nell'albero. Per esempio, nell'albero seguente ...O........ ./.|..\..... O.O...O... |....../.\.. O....O...O il diametro è 4. Ultima modifica di Red_Knight : 06-01-2010 alle 15:34. |
![]() |
![]() |
![]() |
#16 |
Member
Iscritto dal: May 2006
Messaggi: 86
|
a questo punto, direi che puoi affermare che: i due nodi che andranno a stabilire il diametro sono foglie.
Quindi, detto d(x,y) un algoritmo che torna il costo del cammino minimo tra due nodi, hai che il tuo programma sarà qualcosa del tipo: Codice:
pseudocodice: int m; for(x in F){ for(y in F){ m = max(m,d(x,y)); } } return m; PS: probabilmente quello che ho sopra descritto non è un algoritmo ottimo, visto che è dell'ordine di O(n^2) nel numero n delle foglie dell'albero, ma di certo è il più semplice ![]() |
![]() |
![]() |
![]() |
#17 |
Senior Member
Iscritto dal: Nov 2005
Messaggi: 2773
|
Secondo me il modo migliore è usare una variante della funzione altezza h(r) dove r è la radice dell'albero/sottoalbero
Codice:
PSEUDOCODICE: (h, d)diametroRec(r){ //Ritorna due valori, altezza di r e diametro di r Se r è una foglia: return 0,0 maxD,maxH1,maxH2 //maxD conterrà il massimo diametro tra i sottoalberi figli, maxH1 e maxH2 le due altezze maggiori tra i sotto alberi figli Richiamo diametroRec sui figli per assegnare le varie variabili return maxH1+1, maxD>maxH1+maxH2+2 ? maxD : maxH1+maxH2+2; } Ultima modifica di wingman87 : 06-01-2010 alle 20:47. |
![]() |
![]() |
![]() |
#18 |
Member
Iscritto dal: May 2006
Messaggi: 86
|
Quella che hai postato è la seconda idea che mi era venuta in mente... l'unico dubbio che però mi ha fatto propendere per l'altro algoritmo, oltre alla semplicità di scrittura, è che in quel modo ti trovi a fare una serie di ricerche di massimi tra i figli di ogni nodo ( per determinare maxh1 e maxh2) ... ora, son pigro e non voglio controllare, ma non ho la certezza che a livello computazionale la cosa convenga ( quantomeno non se si ha un albero con un fattore di diramazione alto).
Ma ripeto, non ho fatto alcun calcolo pratico ( e in effetti nell'algoritmo che ho scritto c'è da cercare in continuazione cammini minimi... ), quindi non saprei dire in fin dei conti cosa convenga ![]() |
![]() |
![]() |
![]() |
#19 | |
Senior Member
Iscritto dal: Dec 2005
Città: Istanbul
Messaggi: 1817
|
Quote:
__________________
One of the conclusions that we reached was that the "object" need not be a primitive notion in a programming language; one can build objects and their behaviour from little more than assignable value cells and good old lambda expressions. —Guy Steele |
|
![]() |
![]() |
![]() |
#20 | |
Member
Iscritto dal: May 2006
Messaggi: 86
|
Quote:
![]() mi resta comunque il dubbio che l'altro algoritmo non sia lineare nel tempo, ma piuttosto quadratico quantomeno nel caso pessimo, dal momento che per ogni nodo grossomodo c'è da fare una ricerca su tutti i figli per il massimo... |
|
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 10:21.