Grafo : contare gli archi tra due nodi

slevyn
Salve ragazzi..vorrei un aiuto su come realizzare un metodo che mi faccia contare gli archi tra due nodi di un grafo passati come parametro , sto impazzendo e non riesco proprio ..
questa è la mia realizzazione di grafo, realizzato con matrice di adiacenza
static const int maxnodi = 50;
int const NULLO = -1;



template<class T>
class grafo
{
  public:
  typedef int peso;
  typedef int nodo ;
  typedef T tipoelem;
  grafo();
 ~grafo();
 void       creagrafo();
 bool       grafovuoto();
 void       insnodo(nodo);
 void       insarco(nodo,nodo,peso);
 bool       esistenodo(nodo);
 bool       esistearco(nodo,nodo);
 peso       pesoArco(nodo,nodo);
 lista<int> adiacenti(nodo);
 void       cancnodo(nodo);
 void       cancarco(nodo,nodo);
 void       scrivinodo(tipoelem,nodo);
 tipoelem   legginodo(nodo);
 void       setvisited(nodo, bool); //introduco questi 2 operatori per settare
 bool       getvisited(nodo ); //e recuperare il flag visited dei nodi
//necessario per le visite del grafo
 void       falsa_visited(); //reimposta il flag visited a tutti false
  void      DFS(nodo); // esplorazione del grafo in profondità
  int       find(tipoelem);
  int       contaNodiEtichettati(tipoelem);
  int       archiUscenti(nodo);
  int       archiEntranti(nodo);
  void      popola();
  int       LunghezzaCammino(nodo,nodo);

  private:
  tipoelem label[maxnodi]; //etichetta del nodo
  bool mark[maxnodi]; //indica se un nodo e' attivo o meno
  bool visited[maxnodi]; //utile per le visite
  int arc[maxnodi][maxnodi]; //matrice di adiacenza
  int totale_nodi;
  int totale_archi;
};



template<class T>
grafo<T>::grafo() {  creagrafo(); }

template<class T>
grafo<T>::~grafo() {}


template<class T>
void grafo<T>::creagrafo()
{
    totale_archi=0;
    totale_nodi=0;
    for(int i = 0 ; i<maxnodi ; i++ ){
      label[i] = '\0' ;
       mark[i] = false ;
        visited[i] = false ;
          for( int j = 0 ; j < maxnodi ; j++) {
             arc[i][j] = NULLO ;
          }
    }
}




template<class T>
bool grafo<T>::grafovuoto()
{

    for(int i = 0 ; i <maxnodi; i++) {
          if( mark[i]) return false ;
    }
      return true;
}




template<class T>
void grafo<T>::insnodo(grafo<T>::nodo u)
{
    totale_nodi++;
    if(u>=0 && u<maxnodi )
       if(!mark) mark = true;
}



template<class T>
void grafo<T>::insarco(grafo<T>::nodo u, grafo<T>::nodo v, grafo<T>::peso p)
{
   totale_archi++;
    if(u>=0 && u<maxnodi && v>=0 && v<maxnodi ) {
        if( mark && mark[v] && !esistearco(u,v)  &&  !esistearco(v,u) ) {
           arc[v] = p; // indicando così che sussiste l'arco
        }
    }
}


template<class T>
typename grafo<T>::peso grafo<T>::pesoArco(grafo<T>::nodo n , grafo<T>::nodo m)
{
    if(esistearco(n,m))
     return arc[n][m];
}



template<class T>
bool grafo<T>::esistenodo(grafo<T>::nodo u)
{
    if(u>=0 && u<maxnodi)
      return mark;
}



template<class T>
bool grafo<T>::esistearco(grafo<T>::nodo n ,grafo<T>:: nodo v)
{
    if(n>0 && n<=maxnodi && v>0 && v<=maxnodi)
    if(arc[n][v]==NULLO)
      return false;
       else return true;
}



template<class T>
lista<int> grafo<T>::adiacenti(grafo<T>::nodo u)
{

    lista<int> L;
    cella<int>* p = L.primoLista();

    for(int i = 0 ; i<maxnodi; i++) {
         if(arc[i] != NULLO) {
             L.insLista(i,p);
               p = L.succLista(p);
         }
    }
    return L;
}


template<class T>
void grafo<T>::cancnodo(grafo<T>::nodo u)
{

    if(u>=0 && u<=maxnodi && esistenodo(u) && adiacenti(u).listaVuota() )
       mark = false;
       totale_nodi--;
}



template<class T>
void grafo<T>::cancarco(grafo<T>::nodo u ,grafo<T>:: nodo n)
{
    if( u>=0 && u<=maxnodi && n>=0 && n<=maxnodi && esistearco(u,n) && esistearco(n,u)){
      arc[n] = NULLO;
      arc[n] = NULLO;
    }
    totale_archi--;
}



template<class T>
void grafo<T>::scrivinodo(grafo<T>::tipoelem el , grafo<T>::nodo n)
{

    if(n>=0&&n<=maxnodi && esistenodo(n))
       label[n] = el;
}



template<class T>
typename grafo<T>::tipoelem grafo<T>::legginodo(grafo<T>::nodo n)
{

    if(n>=0 && n<= maxnodi && esistenodo(n))
     return(label[n]);
}




template<class T>
void grafo<T>::setvisited(grafo<T>::nodo n , bool vis)
{
    if(n>=0 && n<maxnodi && esistenodo(n))
     visited[n] = vis;
}



template<class T>
bool grafo<T>::getvisited(grafo<T>::nodo n)
{
    if(n>=0 && n<maxnodi && esistenodo(n))
     return visited[n];
}



template<class T>
void grafo<T>::DFS(grafo<T>::nodo n)
{

    if(esistenodo(n)){
      setvisited(n,true);
        cout<<"["<<legginodo(n)<<"]"<<"-->";
          lista<int> L  =  adiacenti(n) ;
            if(!L.listaVuota()) {
                 cella<int> * p = L.primoLista();
                     while(!L.fineLista(p)) {
                       nodo v= L.leggiLista(p);
                          if(esistearco(n,v)){
                             cout << "(" << n << "," << v <<")";
                             if (getvisited(v)==false)
                                  DFS(v);

                        }
                                   p = L.succLista(p);
            }
     }
   }
}


template<class T>
int grafo<T>::find(grafo<T>::tipoelem el)
{
  int trovato = 0 ;
  int i = 0 ;
  while(label[i]!=el && i< maxnodi-1){
  i++;
  if(label[i]==el)
  trovato = 1;
  }
  return trovato;

}


template<class T>
int grafo<T>::contaNodiEtichettati(grafo<T>::tipoelem el)
{

  int n = 0 ;


  for(int i = 0 ; i<maxnodi-1 ; i++){
       if(label[i]==el)
         n++ ;
  }
  return n;

}



template<class T>
int grafo<T>::archiUscenti(grafo<T>::nodo n)
{
   int L = 0 ;
        for(int j =1 ; j<maxnodi-1;j++){
            if(esistearco(n,j)){
                 L++;

            }
        }
 return L;

}


template<class T>
int grafo<T>::archiEntranti(grafo<T>::nodo n)
{
       int L = 0 ;
        for(int j =1 ; j<maxnodi-1;j++){
            if(esistearco(j,n)){
                 L++;
            }
        }
 return L;
}

Risposte
yoshiharu
"slevyn":
Salve ragazzi..vorrei un aiuto su come realizzare un metodo che mi faccia contare gli archi tra due nodi di un grafo passati come parametro , sto impazzendo e non riesco proprio ..
questa è la mia realizzazione di grafo, realizzato con matrice di adiacenza


Scusa, ma se hai la matrice di adiacenza non basta prendere la entry relativa?
O forse intendi i cammini (quindi composti da piu' archi)? (In questo caso devi prendere le potenze della matrice di adiacenza).
Nella entry dovrebbe esserci il numero di cammini da-a, se non mi sbaglio, no?

slevyn
Se voglio calcolare il cammino dal nodo i al nodo j , devo effettuare la potenza della matrice e prendere l'elemento i,j. L'elemento i,j sarà il valore del cammino . Ma come faccio ad effettuare la potenza della matrice ..e soprattutto a cosa và elevata ?

slevyn
ho fatto così :
template<class T>
int grafo<T>::LunghezzaCammino(grafo<T>::nodo n , grafo<T>::nodo m)
{

    int i ,j;
    double arc2[maxnodi][maxnodi];


    for(int i=1;i<maxnodi-1;i++){
       for(int j=1;j<maxnodi-1;j++){
           arc2[i][j]= pow(arc[i][j],maxnodi);
       }
    }


   return arc2[n][m];
}

yoshiharu
"slevyn":
ho fatto così :
template<class T>
int grafo<T>::LunghezzaCammino(grafo<T>::nodo n , grafo<T>::nodo m)
{

    int i ,j;
    double arc2[maxnodi][maxnodi];


    for(int i=1;i<maxnodi-1;i++){
       for(int j=1;j<maxnodi-1;j++){
           arc2[i][j]= pow(arc[i][j],maxnodi);
       }
    }


   return arc2[n][m];
}


Ferma le macchine! :-)
Anzitutto avevo capito male la domanda (my fault): tu vuoi sapere quanto e' lungo il cammino tra un nodo e l'altro, io avevo capito tutt'altro (in effetti mi sembrava troppo semplice...).
Allora, per potenza di una matrice si intende la matrice moltiplicata per se stessa, non la potenza di ogni singola entry come fai tu nello stralcio qui sopra.
Ma ad ogni modo, per risolvere il tuo problema ci sono algoritmi migliori computazionalmente parlando, per esempio puoi implementare un algoritmo tipo A*, che costa sicuramente molto meno di una potenza di matrice, e soprattutto scala sicuramente meglio per grafi grandi.

slevyn
E come effettuo il prodotto di una matrice per se stessa? E poi non ho capito quante volte si dovrebbe moltiplicare per se stessa la matrice...io non ho mai visto un prodotto di una matrice per se stessa...io ho elevato apposta le entry...

hamming_burst
Scusa ma il tuo problema è:
avendo due nodi, contare gli archi passanti da tutti i cammini validi (togliendo gli archi uguali)?

slevyn
Il mio problema è trovare la lunghezza del cammino tra due nodi (ovvero quanti sono gli archi che li separano)

hamming_burst
"slevyn":
Il mio problema è trovare la lunghezza del cammino tra due nodi (ovvero quanti sono gli archi che li separano)

Quale cammino, l'articolo "del" è il fucro:
uno qualsiasi? il minimo, il massimo?

Penso tu sappia che un cammino non è unico (dipende dal grafo ovviamente), devi specificare che cammino cerchi, se uno qualunque o una tipologia in particolare. :-)

slevyn
Un cammino qualsiasi

yoshiharu
"slevyn":
Un cammino qualsiasi


Devi specificare un cammino, perche' altrimenti l'output della funzione non e' univocamente determinato.
In genere si cerca quello piu' corto. Per questo consigliavo A*, nel post prima.

yoshiharu
"slevyn":
E come effettuo il prodotto di una matrice per se stessa?


Scusa, avevo dato per scontato che conoscessi il prodotto righe per colonne.
Si tratta di questo:
[tex](a\cdot b)_{mn} = \sum_i a_{mi} b_{in}[/tex]
laddove ho indicato il prodotto delle matrici $a$ e $b$ con $a\cdot b$.

E poi non ho capito quante volte si dovrebbe moltiplicare per se stessa la matrice...


A questo punto ho capito di cosa hai bisogno, quindi non ti serve questo "trucco".
Comunque la faccenda e' questa:
data la matrice di adiacenza $A$, il numero di cammini di lunghezza $n$ che partono dal nodo $i$-esimo e arrivano al nodo $j$-esimo e' la componente [tex](A^n)_{ij}[/tex], cioe' la componente relativa ai due nodi, della potenza $n$-esima della matrice di adiacenza.
Comunque ripeto: non hai bisogno di elevare a potenza la matrice di adiacenza per quello che devi fare, devi semplicemente implementare un algoritmo di ricerca sul grafo. Ma prima devi decidere di che tipo di cammino hai bisogno.

hamming_burst
Solo per: A* è un algoritmo piuttosto complesso da implementare, utilizza euristiche, e anche il metodo delle potenze è costoso (anche se si va di iterazioni).

"yoshiharu":
Comunque ripeto: non hai bisogno di elevare a potenza la matrice di adiacenza per quello che devi fare, devi semplicemente implementare un algoritmo di ricerca sul grafo. Ma prima devi decidere di che tipo di cammino hai bisogno.


Come dici gli basta un algoritmo di visita (DFS o BFS) o un semplice MST (tipo Prim). :-)
un cammino trovato vale l'altro.

apatriarca
Se un cammino trovato vale l'altro allora direi uno tra DFS o BFS. Difficilmente si riesce infatti a scrivere un codice più semplice. Alternativamente, se si vuole il cammino minimo ma non si hanno abbastanza informazioni per scegliere una euristica, si può usare Dijkstra.

slevyn
Questa è la mia DFS..come andrebbe modificata affinchè conti gli archi tra DUE nodi ?

template<class T>
void grafo<T>::DFS(grafo<T>::nodo n)
{

    if(esistenodo(n)){
      setvisited(n,true);
        cout<<"["<<legginodo(n)<<"]"<<"-->";
          lista<int> L  =  adiacenti(n) ;
            if(!L.listaVuota()) {
                 cella<int> * p = L.primoLista();
                     while(!L.fineLista(p)) {
                       nodo v= L.leggiLista(p);
                          if(esistearco(n,v)){
                             cout << "(" << n << "," << v <<")";
                             if (getvisited(v)==false)
                                  DFS(v);

                        }
                                   p = L.succLista(p);
            }
     }
   }
}


apatriarca
È sufficiente sommare il costo di ogni arco visitato nel percorso che ti ha portato al nodo.

slevyn
Ho fatto così..penso stia bene però non mi ritorna la giusta somma degli archi attraversati :
template<class T>
typename  grafo<T>::peso grafo<T>::cammino(grafo<T>::nodo n,grafo<T>::nodo m)
{
    peso costo=0;
  while(getvisited(m)==false){
    if(esistenodo(n)){
      setvisited(n,true);
        cout<<"["<<legginodo(n)<<"]"<<"-->";
          lista<int> L  =  adiacenti(n) ;
            if(!L.listaVuota()) {
                 cella<int> * p = L.primoLista();
                     while(!L.fineLista(p)) {
                       nodo v= L.leggiLista(p);
                          if(esistearco(n,v)){
                             cout << "(" << n << "," << v <<")";
                              costo=costo + pesoArco(n,v);
                             if (getvisited(v)==false)
                                 costo = cammino(v,m);

                        }
                                   p = L.succLista(p);
         }
       }
     }
   }
   return costo;
}


apatriarca
Probabilmente perché quello che cerchi non è un cammino qualsiasi, ma uno particolare. Come ti abbiamo già detto, non esiste, in generale, un singolo cammino che collega due nodi, ma tanti.

slevyn
Mettiamo il caso che io voglia trovare il cammino minimo , riuscirei a trovarlo con questa DFS ?...l'algoritmo di Dijkstra non l'ho studiato purtroppo

hamming_burst
"slevyn":
Mettiamo il caso che io voglia trovare il cammino minimo , riuscirei a trovarlo con questa DFS ?...l'algoritmo di Dijkstra non l'ho studiato purtroppo

non disturbiamo Dijkstra :)
No, con la DFS non trovi il minimo, trovi UN cammino, che può essere il minimo, dipende da come viene visitato il grafo e con che ordine è scelto il nodo successivo di visita.

Se vuoi il minimo puoi usare la BFS.
Un'applicazione simpatica della BFS, che risolve il tuo problema (con cammini minimi), è "il numero di Erdòs" prova a cercarlo con guugle, vedrai è molto semplice :-)

apatriarca
@hamming_burst: Ma il grafo sembrerebbe pesato per cui il percorso con peso minimo non è necessariamente quello con il numero inferiore di archi. Per cui non sono certo che la ricerca in ampiezza sia sufficiente. Vista l'importanza degli algoritmi come Dijkstra o A* io gli farei implementare uno di questi algoritmi. Anche perché, una volta scelte le strutture dati adatte, non è complicato implementare questi algoritmi (sono anzi praticamente tutti identici). Ma forse BFS è sufficiente e io mi sto complicando la vita.

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.