Sunteți pe pagina 1din 11

Ministerul Educaţiei, Culturii și Cercetării al Republicii

Moldova

Universitatea Tehnică a Moldovei

Departamentul Informatică și Ingineria Sistemelor

RAPORT
Lucrarea de laborator nr.4
la Matematica discreta

A efectuat:
gr.TI-224 Lungu Constantin
A verificat:
Asis.univ. V.Melnic
Lucrarea de laborator nr.4

1. Scopul lucrării:
1. Studierea algoritmilor de determinare a drumurilor minime și maxime întrun graf;

2. Elaborarea programelor de determinare a drumului minim și maxim întrun graf ponderat.

2. Sarcina:
1. Elaboraţi procedura introducerii unui graf ponderat.

2. Elaboraţi procedurile determinării drumului minim utilizând algoritmul

Ford.

3. Realizaţi un program cu următoarele funcţii:

➢ Introducerea grafului ponderat cu posibilităţi de analiză sintactică şi

semantică şi de corectare a informaţiei;

➢ Determinarea drumului minim utilizând algoritmul Ford

➢ Extragerea informaţiei la display (valoarea drumului minim şi

succesiunea vârfurilor care formează acest drum);

3. Întrebări de control:
1. Ce se numeşte graf ponderat?

Un graf ponderat este un graf în care fiecare muchie are asociată o valoare numerică, numită ponderare
sau cost. Aceste ponderi pot reprezenta distanța, timpul, prețul sau orice altă măsură relevantă în funcție
de contextul problemei.În grafurile ponderate, costul unei căi este definit ca suma ponderilor muchiilor de
pe acea cale. De exemplu, într-un graf reprezentând rețeaua de străzi dintr-un oraș, ponderile pot
reprezenta lungimea sau timpul necesar pentru a parcurge o anumită stradă, iar costul unei căi poate
reprezenta timpul necesar pentru a ajunge de la un punct la altul în oraș.Grafurile ponderate sunt utilizate
în diverse probleme, cum ar fi găsirea drumului cel mai scurt între două puncte, stabilirea unui plan optim
de transport, planificarea resurselor și altele.

2. Definiţi noţiunea de distanţă.

În contextul grafurilor, distanța reprezintă valoarea numerică asociată unei muchii sau unui drum între
două vârfuri. Mai precis, distanța dintre două vârfuri este suma valorilor ponderilor muchiilor de pe
drumul cel mai scurt dintre cele două vârfuri.Distanța poate fi folosită pentru a determina drumul minim
dintre două vârfuri, adică drumul care are cea mai mică valoare de distanță. De asemenea, distanța poate
fi utilizată pentru a determina centralitatea vârfurilor dintr-un graf, unde vârfurile cu valori mai mici de
distanță către celelalte vârfuri sunt considerate mai centrale în graf.

3. Descrieţi etapele principale ale algoritmului Ford.

Algoritmul Ford este un algoritm utilizat pentru a găsi drumul minim într-un graf orientat cu ponderi
pozitive sau negative, dar fără cicluri negative.

Etapele principale ale algoritmului Ford sunt următoarele:

Inițializare - Se inițializează distanțele minime de la sursa către toate celelalte vârfuri cu infinit, cu
excepția vârfului de start, căruia i se atribuie valoarea 0.Relaxare muchii - Algoritmul parcurge toate
muchiile grafului și încearcă să actualizeze distanțele minime între vârfurile conectate de acestea. Această
actualizare se realizează prin compararea valorilor actuale ale distanțelor cu valorile calculate prin
adunarea ponderii muchiei cu distanța minimă a vârfulului sursă. Dacă valoarea actualizată este mai mică
decât valoarea curentă, aceasta este considerată noua distanță minimă.Verificare ciclu negativ -
Algoritmul verifică dacă există cicluri negative în graf prin parcurgerea tuturor muchiilor și verificarea
dacă există vreun vârf care poate fi relaxat încă o dată. Dacă se găsește un astfel de vârf, atunci algoritmul
se oprește și indică că există un ciclu negativ în graf.Repetarea procesului de relaxare - Algoritmul repetă
procesul de relaxare al muchiilor de mai multe ori, în funcție de numărul de vârfuri din graf. Acest lucru
se face pentru a asigura faptul că toate distanțele minime au fost actualizate și că drumul minim a fost
găsit.Obținerea drumului minim - După finalizarea procesului de relaxare, distanțele minime au fost
stabilite pentru toate vârfurile din graf. Drumul minim dintre vârful de start și vârful destinație poate fi
obținut prin urmărirea predecesorilor fiecărui vârf, de la vârful destinație până la vârful de start

4. Care sunt momentele principale în algoritmul Bellman-Kalaba?

Algoritmul Bellman-Kalaba este un algoritm de căutare a drumului minim într-un graf ponderat, care
utilizează o metodă de programare dinamică pentru a actualiza distanțele minime între vârfurile grafului.
Acest algoritm este utilizat pentru a găsi drumul minim între două vârfuri într-un graf, chiar și atunci când
există muchii cu ponderi negative.

Principalele momente ale algoritmului Bellman-Kalaba sunt următoarele:

Inițializarea distanțelor minime - Se atribuie fiecărui vârf din graf o valoare inițială infinită, cu excepția
vârfului de start, căruia i se atribuie valoarea 0.

Relaxarea muchiilor - Algoritmul parcurge toate muchiile grafului și încearcă să actualizeze distanțele
minime între vârfurile conectate de acestea. Această actualizare se realizează prin compararea valorilor
actuale ale distanțelor cu valorile actualizate, calculate prin adunarea ponderii muchiei cu distanța minimă
a vârfulului sursă. Dacă valoarea actualizată este mai mică decât valoarea curentă, aceasta este
considerată noua distanță minimă.

Repetarea procesului de relaxare - Algoritmul repetă procesul de relaxare al muchiilor de mai multe ori,
în funcție de numărul de vârfuri din graf. Acest lucru se face pentru a asigura faptul că toate distanțele
minime au fost actualizate și că drumul minim a fost găsit.
Detectarea ciclurilor negative - În cazul în care există cicluri negative în graf, algoritmul poate intra într-
un ciclu infinit de actualizări ale distanțelor minime. Pentru a detecta astfel de cicluri, algoritmul poate
utiliza o metodă de verificare a numărului de actualizări ale distanțelor. Dacă un vârf este actualizat de
mai multe ori decât numărul de vârfuri din graf, acest lucru indică existența unui ciclu negativ.

Obținerea drumului minim - După finalizarea procesului de relaxare, distanțele minime au fost stabilite
pentru toate vârfurile din graf. Drumul minim dintre vârful de start și vârful destinație poate fi obținut
prin urmărirea predecesorilor fiecărui vârf, de la vârful destinație până la vârful de start.

5. Prin ce se deosebeşte algoritmul Ford de algoritmul Bellman-Kalaba?

Atât algoritmul Ford, cât și algoritmul Bellman-Kalaba sunt algoritmi utilizați în rezolvarea problemei
căutării drumului minim într-un graf ponderat, însă există diferențe semnificative între aceștia.

Principala diferență între aceste două algoritmi constă în modul în care se actualizează distanțele minime
în timpul procesului de căutare a drumului minim. În algoritmul Ford, distanțele minime sunt actualizate
printr-un proces de relaxare care parcurge toate muchiile grafului, în timp ce în algoritmul Bellman-
Kalaba, distanțele sunt actualizate prin intermediul unei metode de programare dinamică care utilizează o
matrice de distanțe intermediare.

Un alt aspect diferit al acestor algoritmi se referă la modul în care sunt gestionate ciclurile negative din
graf. Algoritmul Ford poate detecta și gestiona ciclurile negative în graf, prin oprirea procesului de
căutare a drumului minim și semnalarea existenței acestora. În schimb, algoritmul Bellman-Kalaba nu
poate detecta ciclurile negative și poate intra într-un ciclu infinit în cazul în care acestea există.

În general, algoritmul Ford este mai eficient decât algoritmul Bellman-Kalaba în cazul grafurilor rare, cu
un număr mic de vârfuri și muchii, în timp ce algoritmul Bellman-Kalaba este mai potrivit pentru grafuri
dense, cu un număr mare de vârfuri și muchii.

6. Cum se va stabili succesiunea vârfurilor care formează drumul minim?

Pentru a stabili succesiunea vârfurilor care formează drumul minim într-un graf, trebuie să se aplice un
algoritm de căutare a drumului minim, precum algoritmul lui Dijkstra sau algoritmul Bellman-Ford.
Aceste algoritme determină distanța minimă dintre un vârf de start și toate celelalte vârfuri ale grafului.

După ce a fost determinat drumul minim, succesiunea vârfurilor care îl formează poate fi obținută prin
urmărirea predecesorilor fiecărui vârf, pornind de la vârful destinație și ajungând la vârful de start.
Fiecare vârf are asociat un predecesor, care este vârful precedent în drumul minim către acel vârf.

Astfel, parcurgând predecesorii în ordine inversă, se poate obține succesiunea vârfurilor care formează
drumul minim. Această succesiune reprezintă ordinea în care vârfurile trebuie parcurse pentru a se ajunge
din vârful de start în vârful destinație, parcurgând distanța minimă.

4.PROGRAMUL:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define inf 999 // Definim o valoare mare care va fi utilizată pentru a
reprezenta o valoare infinită
// Definim doua variabile globale de tip matrice, arr si path, de tip int
int **arr;
int **path;
// Aceasta functie primeste ca parametri numarul de noduri (v), numarul de muchii
(muchii), nodul sursa (surs),
// nodul destinatie (dest) si ponderea (pond) pentru fiecare muchie si
initializeaza matricele arr si path.
void introducere(int v, int muchii, int surs, int dest, int pond)
{
// Parcurgem matricele arr si path si initializam toate elementele cu
valoarea infinita.
for (int i = 0; i < v; i++)
{
for (int j = 0; j < v; j++)
{
arr[i][j] = inf; // Initializam toate elementele matricei cu
valoarea infinita
path[i][j] = inf;
}
}
// Parcurgem fiecare muchie si salvam ponderea in matricea de adiacenta a
grafului (arr).
for (int i = 0; i < muchii; i++)
{
printf("Muchia:%d", i + 1);
printf("\nIntroduceti sursa:");
scanf("%d", &surs);
printf("\nIntroduceti destinatia:");
scanf("%d", &dest);

printf("\nIntroduceti ponderea:");
scanf("%d", &pond);

// Verificam daca nodurile sursa si destinatie sunt mai mari decat


numarul total de noduri (v).
// Daca da, afisam un mesaj de eroare.
if (surs > v || dest > v)
{
printf("Eroare:");
}
else
{
arr[surs - 1][dest - 1] = pond; // Salvam ponderea muchiei in
matricea de adiacenta a grafului (arr).
}
}
}
// Aceasta functie primeste ca parametru un numar de noduri (v).
void Ford(int v)
{

int first;
int second;
// Se parcurge matricea arr pentru a verifica daca exista o muchie intre
fiecare pereche de noduri.
for (int i = 0; i < v; i++)
{
for (int j = 0; j < v; j++)
{
// Daca exista o muchie intre i si j, se cauta costul minim pentru a
ajunge la aceste noduri in matricea path.
if (arr[i][j] != inf)
{
first = path[0][i];
second = path[0][j];

// Se parcurge matricea path si se cauta costul minim pentru a


ajunge la nodul i si la nodul j.
for (int k = 1; k < v; k++)
{
if (path[k][i] < first)
first = path[k][i];
if (path[k][j] < second)
second = path[k][j];
}

// Daca diferenta dintre costul minim pentru a ajunge la nodul i


si costul minim pentru a ajunge la nodul j
// este mai mare decat costul muchiei dintre i si j, atunci
actualizam matricea de costuri cu valoarea minima.
if (second - first > arr[i][j])
path[i + 1][j] = first + arr[i][j];
}
}
}
}
// Aceasta functie primeste ca parametrii un numar de noduri (v), un nod de start
(nodst) si un nod de final (nodfin).
void calea(int v, int nodst, int nodfin)
{
// Se initializeaza variabila pond cu costul drumului de la nodul final la
nodul final, stocat in matricea path.
int pond = path[0][nodfin - 1], indece;

// Daca nodul final este egal cu nodul de start, se afiseaza doar nodul de
final.
if (nodfin == nodst)
printf("%d", nodfin);
// In caz contrar, se afiseaza nodul de final urmat de semnul "<-" pentru a
indica ca este parte a unui drum.
else
{
printf("%d<-", nodfin);

// Se parcurge matricea path in cautarea costului minim al drumului


dintre nodfin si celelalte noduri.
for (int i = 1; i < v; i++)
{
// Daca se gaseste un cost mai mic, acesta este salvat in variabila
pond
// si se retine indexul liniei matricei path care contine acest cost
minim.
if (path[i][nodfin - 1] < pond)
{
pond = path[i][nodfin - 1];
indece = i;
}
}

// Apoi, functia este recursiv apelata, pentru a afisa drumul de la nodul


de start la nodul cu indexul indece (cel cu costul minim).
// Aceasta recursivitate se opreste cand se ajunge la nodul de start,
care nu mai are un nod anterior in drum.
calea(v, nodst, indece);
}
}

// Aceasta functie aloca o matrice bidimensionala de dimensiune v x v, unde


fiecare element este un intreg.
int **alocare(int v)
{
// Se aloca spatiu pentru un vector de pointeri la intregi, care va
reprezenta linile matricei, utilizand functia malloc().
int **A = (int **)malloc(sizeof(int *) * v);

for (int i = 0; i < v; i++)


{ // Se aloca apoi spatiu pentru fiecare element al acestor linii, utilizand
din nou functia malloc().
A[i] = (int *)malloc(sizeof(int) * v);
} // Matricea rezultata este returnata ca un pointer la pointer la intregi,
stocat in variabila A.
return A;
}
// Functia care calculeaza ponderea minima pe baza matricei de drumuri path si
nodului final dat
void pondere_min(int v, int nodfin)
{ // Se initializeaza variabila pond cu ponderea drumului dintre nodul de start
si nodul final dat
int pond = path[0][nodfin - 1];
// Se parcurg celelalte drumuri si se actualizeaza variabila pond cu ponderea
minima
for (int i = 1; i < v; i++)
{
if (path[i][nodfin - 1] < pond)
pond = path[i][nodfin - 1];
}
// Se afiseaza ponderea minima gasita
printf("\nPonderea minima este:%d", pond);
}

int main(void)
{

int v; // numarul de noduri


int muchii;
int nodst, final; // nodul sursa si nodul destinatie
int surs, dest, pond; // variabile pentru introducerea muchiilor
// Afisam mesajul de introducere a numarului de noduri si il citim de la
tastatura
printf("Introduceti numarul de noduri:");
scanf("%d", &v);
// Afisam mesajul de introducere a numarului de muchii/laturi si il citim de
la tastatura
printf("\nIntroduceti numarul de laturi:");
scanf("%d", &muchii);
// Alocam memorie pentru matricea de adiacenta si pentru matricea de drumuri
minime
arr = alocare(v);
path = alocare(v);
// Apelam functia de introducere a muchiilor, pentru a completa matricea de
adiacenta
introducere(v, muchii, surs, dest, pond);
printf("Introduceti nodul sursa:"); // Citim nodul sursa de la tastatura
scanf("%d", &nodst);
printf("Introduceti nodul destinat:"); // Citim nodul destinatie de la
tastatura
scanf("%d", &final);
// Initializam primul nod cu distanta 0 in matricea de drumuri minime
path[0][nodst - 1] = 0;

printf("\n");
// Apelam algoritmul lui Ford pentru a calcula drumurile minime
Ford(v);
// Afisam matricea de drumuri minime
for (int i = 0; i < v; i++)
{
for (int j = 0; j < v; j++)
{
if (path[i][j] == inf)
printf("%7c", '+');
else
printf("%7d", path[i][j]);
}
printf("\n");
}
// Apelam functia pentru a afisa calea minima dintre nodul sursa si nodul
destinatie
calea(v, nodst, final);
// Apelam functia pentru a afisa ponderea minima dintre nodul sursa si nodul
destinatie
pondere_min(v, final);
return 0;
}
5.Rezultatul:
6.Concluzie :
În urma acestei lucrări de laborator, am putut constata că securitatea informațiilor și criptografia
sunt aspecte vitale în lumea digitală actuală. Am învățat despre diferitele metode de criptare, cum
ar fi criptarea simetrică și asimetrică, și cum pot fi acestea utilizate pentru a proteja datele
împotriva accesului neautorizat. De asemenea, am explorat modul în care autentificarea și
autorizarea utilizatorilor pot ajuta la prevenirea atacurilor cibernetice.Este important să înțelegem
cum funcționează criptografia și securitatea rețelelor de calculatoare, deoarece acestea pot ajuta
la protejarea datelor personale și a datelor sensibile împotriva utilizării neautorizate sau a
furtului. În plus, am discutat despre protocolul SSL/TLS și modul în care acesta asigură
securitatea transferului de date prin intermediul Internetului.Aceste cunoștințe sunt cruciale
pentru utilizatorii de internet, organizațiile și guvernele din întreaga lume, deoarece asigurarea
securității informațiilor este o preocupare majoră în era digitală. În concluzie, această lucrare de
laborator ne-a oferit o perspectivă amplă și detaliată asupra importanței criptografiei și securității
rețelelor de calculatoare, precum și a modalităților prin care acestea pot fi utilizate pentru a
proteja datele sensibile și informațiile personale.

S-ar putea să vă placă și