Logo dell'Università di Catania: Siciliae Studium Generale 1434 Logo del Centro IPPARI, Programmazione
matite e gomma
Loghi istituzionali: Siciliae Studium Generale 1434, Centro ricerche IPPARI, Università di Catania, Facoltà di Scienze Matematiche, Fisiche, Naturali, Insegnamento di Programmazione

Algoritmi e strutture dati in C++

Introduzione

Lezione 1 di Programmazione 2

Docente: Giuseppe Scollo

Università di Catania, sede di Comiso (RG)
Facoltà di Scienze Matematiche, Fisiche e Naturali
Corso di Studi in Informatica applicata, AA 2006-7

Logo di Conformità WCAG-1 di Livello Tripla A, W3C-WAI Web Content Accessibility Guidelines 1.0 Validazione XHTML 1.0 Validazione CSS 2

Indice

  1. Algoritmi e strutture dati in C++
  2. algoritmi: correttezza, semplicità, efficienza
  3. algoritmi e programmi: efficienza o semplicità?
  4. un esempio: il problema della connettività
  5. il problema della connettività: precisazione
  6. problemi di connettività: variazioni sul tema
  7. progetto di soluzioni: operazioni astratte union, find
  8. algoritmo quick-find
  9. un programma C++ per quick-find
  10. algoritmo quick-union
  11. un programma C++ per quick-union
  12. algoritmo quick-union pesata
  13. un programma C++ per quick-union pesata
  14. varianti dell'algoritmo quick-union pesata
  15. valutazione empirica delle prestazioni

algoritmi:
correttezza, semplicità, efficienza

algoritmi e programmi:
efficienza o semplicità?

un esempio:
il problema della connettività

il problema della connettività:
precisazione

il problema proposto in (Sedgewick, 2003, Cap. 1) è un po' diverso:

  • input: una sequenza di coppie
  • si vuole un algoritmo che, per ogni coppia cn, decida se i suoi elementi sono connessi da un cammino nel grafo (non orientato) costruito dalle coppie precedenti {cm | m<n}:
    • se lo sono, si passa alla coppia successiva
    • altrimenti si produce in output cn
      (e si passa alla coppia successiva)
esempio
 in       out   
1-3 1-3
2-4 2-4
3-0 3-0
1-0
4-2
0-2 0-2
2-1

problemi di connettività:
variazioni sul tema

varianti del problema proposto in (Sedgewick, 2003):

  • più complessa: se la coppia p-q è connessa, produrre in output uno dei (o tutti i) modi in cui è connessa
    • esempio: 4o, 5o, 7o input del precedente   
esempio
 in       out           
1-0 (1-3-0)
4-2 (2-4)
2-1 (1-3-0-2)

progetto di soluzioni:
operazioni astratte union, find

algoritmo quick-find

un algoritmo semplice (non troppo: non richiede memorizzazione di tutte le coppie in input)

si abbiano N nodi

struttura dati di supporto all'algoritmo: un array id[N] che soddisfi l'invariante:

se cn = p-q è la n-sima coppia in input, allora id[p] = id[q] sse Cp = Cq nel grafo costruito dalle coppie precedenti {cm | m<n}

un programma C++ per quick-find

problema della connettività: soluzione 1

 
#include <iostream.h>
static const int N = 10000;
int main()
{ int i, p, q, id[N];
  for (i = 0; i < N; i++) id[i] = i ;
  while (cin >> p >> q)
  {
    if (id[p] == id[q]) continue;
    int t = id[p];
    for (i = 0; i < N; i++)
      if (id[i] == t) id[i] = id[q];
    cout << " " << p << " " << q << endl;
  }
}

algoritmo quick-union

  • strutture ad albero nell'array id[N] nell'esecuzione di quick-find:
  • possiamo modificarle per rendere più efficiente l'implementazione di union

input: 3-4, 4-1, 5-2, 0-2

alberi iniziali input 3 input 4 alberi dopo input 3-4 input 4 input 1 alberi dopo input 4-1 input 5 input 2 alberi dopo input 5-2 input 0 input 2 alberi dopo input 0-2 alberi finali
  • nuovo invariante:

Cp = Cq nel grafo costruito dalle coppie precedenti p-q sse idωp = idωq

  • inizializzazione: come in quick-find
  • implementazione di find
    si complica: i ← idωp, j ← idωq
  • implementazione di union(Cp,Cq) immediata: id[i] ← id[j]

input: 3-4, 4-1, 5-2, 3-5

alberi iniziali input 3 input 4 alberi dopo input 3-4 input 4 input 1 alberi dopo input 4-1 input 5 input 2 alberi dopo input 5-2 input 3 input 5 alberi dopo input 3-5 alberi finali

efficienza dell'algoritmo quick-union : non sempre buona, può dover eseguire più di MN/2 istruzioni (per N nodi e M coppie in input), se M>N

un programma C++ per quick-union

problema della connettività: soluzione 2

si ottiene con semplici modifiche del programma C++ già visto per l'algoritmo quick-find:

algoritmo quick-union pesata

  • inizializzazione: come in quick-union, ma estesa all'array ausiliario sz
  • implementazione di find : invariata
  • implementazione di union(Cp,Cq) : si complica un po' per tener conto di, e aggiornare, sz

input: 3-4, 4-1, 5-2, 3-5

alberi iniziali input 3 input 4 alberi dopo input 3-4 input 4 input 1 alberi dopo input 4-1 input 5 input 2 alberi dopo input 5-2 input 3 input 5 alberi dopo input 3-5 alberi finali

efficienza di quick-union pesata : molto migliore della precedente, per la riduzione della lunghezza dei cammini da O(N) a O(lg N)

programma C++ per quick-union pesata

problema della connettività: soluzione 3

 
#include <iostream.h>
static const int N = 10000;
int main()
{ int i, j, p, q, id[N], sz[N];
  for (i = 0; i < N; i++) { id[i] = i ; sz[i] = 1; }
  while (cin >> p >> q)
  {
    for (i = p; i != id[i]; i = id[i]) ;
    for (j = q; j != id[j]; j = id[j]) ;
    if (i == j) continue;
    if (sz[i] > sz[j])
         { id[j] = i; sz[i] += sz[j]; }
    else { id[i] = j; sz[j] += sz[i]; }
    cout << " " << p << " " << q << endl;
  }
}

varianti dell'algoritmo quick-union pesata

ci si può attendere che ulteriori miglioramenti dell'efficienza si possano ottenere riducendo la lunghezza dei cammini

sequenza di visita di una find albero ottenuto da essa per dimezzamento dei cammini
  • prestazioni:
    • quanto sono vantaggiose queste tecniche?
    • ripagano il loro costo computazionale ?
    • come possiamo valutarne la convenienza nel caso medio e nel caso peggiore ?

valutazione empirica delle prestazioni

test: generazione casuale di M coppie per N nodi, fino a connetterli tutti

Confronto empirico delle prestazioni per diversi valori di M e N

Confronto empirico delle prestazioni per diversi valori di M e N