Documente Academic
Documente Profesional
Documente Cultură
La Constructia produselor program deseori putem intilni cazuri problematice, cind exista mai multe solutii posibile,
iar nici o solutie nu este “corecta” sau “gresita”, ci numai “mai buna” sau “mai rea” luindu-se in seama gradul curent
de intelegere a problemei. Adaugator fiecare astfel de caz problematic este esential unic, fara reguli explicite de
solutionare, iar proiectantul va fi responsabil pentru consecintele solutiei alese.
Cu acest model de gindire despre proiectarea program vor exista 2 variabile care vor determina calea spre solutia de
proiectare:
- Gradul de intelegere a domeniului problemei
- Gradul de intelegere a spatiului solutiei?
In lumea produselor program, intelegerea domeniului problemei inseamna gindirea despre constringeri tehnice,
cerintele functionale, atribute de calitate si constringeri de afacere. Intelegerea constringerilor este critic importanta
fiindca ele vor defini unele limite a spatiului de solutii. Cerintele functionale vor avea de a face cu ce va efectua
sistemul, iar atributele de calitate vor avea de a face cu comportamentul sistemului la la executarea unor functii date.
In final constringerile de afacere sunt niste lucruri pe care clientul le cere si care necesita o solutie pur si simplu din
considerente de afacere. De exemplu acestea pot fi bugetul si data de expediere.
Spatiul de solutii este un teren multi-dimensional care este ocupat de posibilitati practic nelimitate. Ca un proiectant
lucrul vostru consta in navigarea in acest spatiu in cautarea solutiilor problemei. Intelegerea curenta a problemei
limiteaza abilitatea de a vedea solutiile si deseori corespondenta solutiei poate fi inteleasa doar in referinta cu alta
solutie posibila. Aceasta implica ca intelegerea curenta a problemei dicteaza intelegerea solutiilor potentiale, iar
intelegerea solutiei va aproviziona informatii adaugatoare despre problema. Aceasta este o pricina de ce este deseori
bun de a avea produs program gata care lucreaza in pentru prezentarea rapida in fata utilizatorilor.
Figura 1 – Graficul cunostintelor despre proiectare fata de cunostintele din domeniul problemei
La proiectarea a oricaror sisteme avem o tendinta naturala de a prefera sa facem decizii rationale de proeictare, acolo
unde dupa examinare a tutoror informatiilor noi alegem cea mai buna sau mai optimizata solutie. Efectuarea alegerei
rationale necesita nu doar cunoasterea a tutoror informatiilor din domeniul problemei, dar si a tuturor solutiilor
posibile. Atit constringerile de afacere, prin constringeri financiare si temporare, si deficientele de limita a creierului
uman limiteaza capacitatea noastra de a cauta aceste cunostinte prin crearea limitelor in jurul solutiei cautate.
Cautarea solutiei nu consta in optimizare ci in corespondenta solutiei maxim de bune cu informatia curent
disponibila. Lucrurile pot fi complicate deoarece indivizii pot intelege domeniul problemei si spatiul de solutii in
mod diferit, depinzind de experienta si capacitatile individualului, marimea problemei si complexitatea solutiei.
La proiectarea produselor program este natural de a dori sa ne miscam cit mai departe pe curba cunostintelor, pentru
a obtine proiectul cit mai mult de rational si optimal. Aceasta tendinta reiese din preferinta instinctuala de a evita
pierderi. In alte cuvinte, proiectantii asuma ca solutia optimala exista si sunt mai dispusi sa rejecteze solutiile mai
putin optimale.
Alegerea abordarii de proiectare produselor program: Aplicare
In proiectarea produselor program exista 4 tipuri de baza a strategiilor de proiectare:
- Proiectare planificata: proiectarea este completata inainte de a incepe implementarea. Asumptiile
esentiale consta in faptul ca este posibil de proiecta un sistem complet inainte de a incepe constructia.
- Proectare cu planificare minima: o parte din proiectare este completata inainte de a incepe
implementarea. Folosind aceasta strategie, noi intelegem ca numaidecit vor aparea schimbari, dar dorim sa
schitam directiile de baza.
- Proiectare evolutionara: Proiectul sistemului creste pe masura implementarii sistemului, dar cresterea este
sporadica si este controlata de ingineri cu experienta cu ajutorul practicilor cunoscute ca proiectare de
referinta si refactorizare. Cel putin o parte din cerintele sunt definite la inceput dar nu asteptate a fi
complete.
- Proiectare emergenta: proiectarea problemei este admisa de a avea loc in mod organic in timp ce sistemul
este implementata fara intentii specifice.
Este adevarat ca ultima etapa în rezolvarea unei probleme - implementarea - este decisiva
si doveditoare, dar primele doua etape au o importanta capitala. Ele sînt singurele ce pot oferi
raspunsuri corecte la urmatoarele întrebari dificile: Avem certitudinea ca solutia gasita este
corecta ? Avem certitudinea ca problema este complet rezolvata ? Cît de eficienta este solutia
gasita ? Cît de departe este solutia aleasa de o solutie optima ?
Daca ar fi sa sintetizam în cîte un cuvînt efortul asupra caruia se concentreaza fiecare din
cele trei etape - analiza, proiectarea si implementarea- cele trei cuvinte ar fi: corectitudine,
eficienta si impecabilitate. Etapa de analiza este singura care permite dovedirea cu argumente
riguroase a corectitudinii solutiei, iar etapa de proiectare este singura care poate oferi argumente
precise în favoarea eficientei solutiei propuse.
În general problemele concrete din informatica au în forma lor initiala sau în enunt o
caracteristica pragmatica, fiind foarte ancorate în realitatea imediata. Totusi ele contin în
formularea lor initiala un grad mare de eterogenitate, diversitate si lipsa de rigoare. Fiecare
dintre aceste "defecte" este un obstacol major pentru demonstrarea corectitudinii solutiei. Rolul
esential al etapei de analiza este acela de a transfera problema "de pe nisipurile miscatoare" ale
realitatii imediate de unde ea provine într-un plan abstract, adica de a o modela. Acest "univers
paralel abstract" este dotat cu mai multa rigoare si disciplina interna, avînd legi precise, si poate
oferi instrumentele logice si formale necesare pentru demonstrarea riguroasa a corectitudinii
solutiei problemei. Planul abstract în care sînt "transportate" toate problemele de informatica este
planul sau universul obiectelor matematice iar corespondentul problemei în acest plan va
fi modelul matematic abstract asociat problemei. Demonstrarea corectitudinii proprietatilor ce
leaga obiectele universului matematic a fost si este sarcina matematicienilor. Celui ce
analizeaza problema din punct de vedere informatic îi revine sarcina (nu tocmai usoara) de a
dovedi printr-o demonstratie constructiva ca exista o corespondenta precisa (o bijectie !) între
partile componente ale problemei reale, "dezasamblata" în timpul analizei, si partile componente
ale modelului abstract asociat. Odata descoperita, formulata precis si dovedita, aceasta "perfecta
oglindire" a problemei reale în planul obiectelor matematice ofera certitudinea ca toate
proprietatile si legaturile ce exista între subansamblele modelului abstract se vor regasii precis
(prin reflectare) între partile interne ale problemei reale, si invers. Atunci, solutiei abstracte
descoperite cu ajutorul modelului matematic abstract îi va corespunde o solutie reala concretizata
printr-un algoritm ce poate fi implementat într-un program executabil.
Aceasta este calea generala de rezolvare a problemelor si oricine poate verifica acest fapt.
De exemplu, ca si exercitiu, încercati sa demonstrati corectitudinea (adica sa se aduca argumente
precise, clare si convingatoare în favoarea corectitudinii) metodei de extragere a radicalului
învatata înca din scoala primara (cu grupare cifrelor numarului în grupuri cîte doua, etc.) sau a
algoritmului lui Euclid de determinare a celui mai mare divizor comun a doua numere prin
împartiri întregi repetate. Desigur nu pot fi acceptate argumente copilaresti de forma:
"Algoritmul este corect pentru ca asa l-am învatat!" sau "Este corect pentru ca asa face toata
lumea !" din moment ce nu se ofera o argumentatie matematica riguroasa.
Ideea centrala a etapei a doua - proiectarea unui algoritm de solutionare eficient poate fi
formulata astfel: din studiul proprietatilor si limitelor modelului matematic abstract asociat
problemei se deduc limitele inferioare ale complexitatii minimale ("efortului minimal
obligatoriu")inerente oricarui algoritm ce va solutiona problema în cauza. Complexitatea
interna a modelului abstract si complexitatea solutiei abstracte se va reflecta imediat
asupracomplexitatii reale a algoritmului, adica asupra eficientei de solutionare a problemei.
Acest fapt permite prognosticarea înca din aceasta faza - faza de proiectare a algoritmului de
solutionare - a eficientei practice, masurabila ca durata de executie, a programului.
Noţiunea de corectitudine
Definiţia 5.1.1. Spunem că cele două predicate, predicatul de intrare φ(X) şi predicatul de ieşire
ψ(X,Y), constituie specificaţia problemei. Cele două predicate se mai numesc precondiţie,
respectiv postcondiţie. Se subînţelege că pentru a cunoaşte această specificaţie trebuiesc
cunoscuţi şi vectorii X şi Z. Astfel, dacă în problemă se cere să se calculeze radicalul de ordinul
2 (notat prin r) dintr-un număr real pozitiv x, specificaţia problemei este dată prin predicatele:
Subliniem că între două puncte de tăietură pot exista mai multe drumuri directe distincte.
Floyd demonstrează următoarea teoremă:
Teorema 1. Dacă toate condiţiile de verificat sunt adevărate atunci programul P este parţial
corect în raport cu specificaţiile φ(X) şi ψ(X,Z). Ca exemplu de demonstrare a corectitudinii
unui program să considerăm următorul algoritm de calcul al celui mai mare divizor comun a
două numere date:
ALGORITMUL CMMDC1(n1,n2,d) ESTE:
DATE n1,n2; { A: φ(X)::= n1∈N, n2∈N}
FIE d:=n1;
FIE i:=n2;
CÂTTIMP (d≠i) ŞI (i>0) EXECUTĂ { B: PB::= (d,i)=(n1,n2)}
DACĂ d>i ATUNCI d:=d-i
ALTFEL i:=i-d
SFDACĂ
SFCÂT { C: PC= ψ(X,Z)::= d=(n1,n2)}
REZULTATE d;
SFALGORITM
În cazul în care s-a demonstrat parţial corectitudinea şi Pi(X,Y) a fost predicatul invariant în
punctul i, condiţia de terminare pentru drumul α care duce de la punctul i la punctul j se poate
lua: ∀X∀Y (Pi(X,Y)∧Rα(X,Y) → (ui(X,Y) > uj(X,rα(X,Y)) )
Pentru algoritmul CMMDC1 avem trei puncte de tăietură, pentru care alegem:
u1(X,Y) = 2*n1 + 2*n2;
u2(X,Y) = d + i;
u3(X,Y) = 0,
iar condiţiile de terminare corespunzătoare acestor drumuri sunt:
Tα1 = (n1≠n2)∧(n2>0) → (2*n1+2*n2 >n1+n2) ;
Tα2 = (PB∧(d>i)∧(i>0) → d+i>d) sau (PB∧(d<i)∧(i>0)→d+i>i) ;
Tα3 = True .
Se poate verifica că toate cele trei condiţii sunt adevărate pentru n1>0, dar Tα2 este falsă dacă
n1=0, întrucât d ia valoarea 0 şi inegalitatea i>i este falsă. În acest caz drumul α2 este parcurs de
oinfinitate de ori, deoarece variabilele d şi i nu-şi modifică valorile la parcurgerea acestui drum.
Pentru a obţine un algoritm total corect este necesară modificarea algoritmului CMMDC1 astfel
încât să nu se intre în drumul α2 dacă d=0. Obţinem următorul algoritm total corect:
Demonstrarea corectitudinii algoritmilor poate fi făcută după elaborarea lor, sau în timp ce sunt
concepuţi. Am văzut că ea presupune scrierea unor predicate invariante corespunzătoare
punctelor de tăietură, deci ea impune proiectantului respectarea unei discipline în programare.
Foarte probabil că aceste predicate invariante sunt sugerate de semnificaţiile variabilelor. Deci
este necesar ca fiecare variabilă să aibă semnificaţia ei şi să nu fie folosită în scopuri diferite.
Gries spunea: Proiectarea programului şi demonstrarea corectitudinii lui trebuie făcute în paralel.
Într-o astfel de proiectare convingerea programatorului că a conceput un algoritm corect creşte,
iar scrierea predicatelor invariante reduce numărul erorilor în programare. De asemenea, dacă s-
ar încerca demonstrarea corectitudinii algoritmilor descrişi, multe erori ar fi descoperite şi
eliminate în faza de proiectare.
Pornind de la această afirmaţie vom exemplifica în secţiunea următoare cum putem obţine
algoritmi corecţi prin rafinări succesive, pornind din specificaţii. Rezultă din exemplele date că
în prim plan este demonstraţia corectitudinii, propoziţiile care compun algoritmul fiind o
consecinţă a acestei demonstraţii.
Verificarea modelelor
Verificarea modelelor este o tehnică de verificare formală (semi) automată bazată pe modele şi
având o abordare orientată pe verificarea proprietăţilor. Explorează toate execuţiile posibile ale
sistemului, aşadar este mai “puternică” decât testarea sau tehnicile de analiză bazate pe simulari.
Această tehnică de verificare este alcatuită din trei parţi distincte:
Analiza si verificare
Concluzie
1.2. 1.2.
1 2 1.3.
1
1.3.
1.1.
4 2
1.1.
3
1.2. De
folosire
1.1. 1.3. Auto- 2.5.
2 documentare 4
e
2.5.
1.1. 1.1 De
3
1 realizare
1.Documentar 2.5.
2.5. 2
e
Validare
2.5.
2.1. 1
1
2.1. Corectitudinea
2 2.1. Algoritmului
Inspectare 2.4.
Programului 5
2.1.
3
2.4.
2.4. 4
2.Programare
Verificare
2.4.
2.2. 3
1
2.4.
2.2. 2
2.2.
Testare
2 2.4.
2.3. 1
2.2. Depanare
3
2.2.
4 2.3.
3
2.3. 2.3.
1 2
Orice strategie va fi realizata conform unor anumite tactice/tehnici. Fiecare punct strategic, la rindul
sau, are anumite tactici, pe care le voi descrie mai jos.
1. Documentarea
1.1. De realizare
1.2. De folosire
1.3. Auto-documentare
2. Programarea
2.1. Inspectarea
2.2. Testarea
2.3. Depanarea
2.4. Verificarea
2.5. Validarea
Orice tactica isi are anumite obiective, care sunt necesare pentru realizarea sarcinilor puse.
In asa mod, eu am ajuns la urmatoarele obiective:
1. Documentarea :
1.1. De realizare
1.1.1. Enuntul initial al problemei
1.1.2. Specificatiile
1.1.3. Documentarea de proiectare
1.1.4. Documentarea de programare
1.2. De folosire
1.2.1. Regulile de utilizare
1.2.2. Instructiunile
1.3. Auto-documentare
1.3.1. Ajutorul necesar utilizatorului, fc. Help
1.3.2. Autoverificarea
2. Programarea
2.1. Inspectarea
2.1.1. Specificarea
2.1.2. Proiectarea
2.1.3. Codificarea
2.2. Testarea
2.2.1. Dupa specificatia problemei
2.2.2. Dupa textul programului
2.2.3. Testarea robustetei programului
2.2.4. Testarea de integrare
2.3. Depanarea
2.3.1. Executarea pas cu pas a programului
2.3.2. Observarea valorilor unei variabile
2.3.3. Modificarea valorilor
2.4. Verificarea
2.4.1. Analiza specificatiilor
2.4.2. Inspectarea proiectarii, codificarii, documentatiei
2.4.3. Executia simbolica a algoritmelor
2.4.4. Testarea produsului
2.4.5. Citirea documentatiei
2.5. Validarea
2.5.1. Testare cu date reale
2.5.2. Testarea robustitiei programului
2.5.3. Verificarea timpului in care s-au obtinut rezultatele
2.5.4. Forma aparitiei rezultatelor
#include "stdio.h"
#include "conio.h"
#include "math.h"
clock_t begin, end;
double time_spent;
begin = clock();
// linia(nivelul) linie.
end = clock();
time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
}
#define __STDC_WANT_LIB_EXT1__ 1
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t t = time(NULL);
printf("UTC: %s", asctime(gmtime(&t)));
printf("local: %s", asctime(localtime(&t)));
#ifdef __STDC_LIB_EXT1__
struct tm buf;
printf("UTC: %s", asctime(gmtime_s(&t, &buf)));
printf("local: %s", asctime(localtime_s(&t, &buf)));
#endif
}
Programul va afisa :
UTC: Tue May 07 18:12:09 2015
local: Tue May 07 13:12:09 2015
UTC: Tue May 07 18:12:09 2015
local: Tue May 07 13:12:09 2015
Algoritmul in C :
#include <stdio.h>
#include<time.h>
double start,stop; //variabile care vor determina timpulde executie a programului
main()
{int i,j,n,k;
start=clock(); //inceputul calcului timpului
printf("Introdu dimendiunea matriciilor:\n");
scanf("%d",&n); //citim dimensiunea matricilor
printf("\n");
int a[n][n], b[n][n], c[n][n]; // declalarea matricilor 1,2 si matricea care se
formeaza
printf("Introdu valorile tabloului a[]:\n"); //citim valolire pentru tabloul 1
for (i=0;i<n;i++){ //parcurge pina la numarul de elemente
for (j=0;j<n;j++){ //contorul parcurge pina la numarul de elemente
printf("a[%d][%d]=",i,j);
scanf("%d",&a[i][j]);}} //citim valoarea pentru fiecare element al tabloului
printf("\n");
printf("Introdu valorile tabloului b[]:\n"); //citim valolire pentru tabloul 2
for (i=0;i<n;i++){ //parcurge pina la numarul de elemente
for (j=0;j<n;j++){ //contorul parcurge pina la numarul de elemente
printf("b[%d][%d]=",i,j);
scanf("%d",&b[i][j]); //citim valoarea pentru fiecare element al tabloului b
}}
for (i=0;i<n;i++){ //parcurge pina la numarul de elemente
for (j=0;j<n;j++){ //contorul parcurge pina la numarul de elemente
c[i][j] = 0; //initial tabloul c este declarat ca liber, 0
for (k=0;k<n;k++){ ////contorul parcurge pina la numarul de elemente
c[i][j] += a[i][k] * b[k][j]; //formeza elementele tabloului C pe baza tabloului 1 si 2
}}
}
printf("\nMatricea C are forma:\n"); //afisarea matricei C
for (i=0;i<n;i++){ //contorul parcurge pina la numarul de elemente
printf("\n\n\n"); //afiseza doua rinduri libere , formind astfel o forma de matrice
for (j=0;j<n;j++){ //parcurge pina la numarul de elemente
printf("\t%3d",c[i][j]); //afiseaza fiecare element al tabloului format
}}
stop=clock(); printf("\n\nTimpul de executie a programului=%lf\n",(stop-start)/CLOCKS_PER_SEC);
} //afiseaza timpul de executie a programului
Algoritmul:
1. Înregistrarea corespunzătoare configuraţiei iniţiale este încărcată în open cu g=0 şi f=h
2. Atât timp cât nu s-a selectat spre expandare nodul corespunzător configuraţiei finale şi lista
open este nevidă, se execută următoarele
3. Se selectează din lista open nodul t cu f minim
4. Se expandează acest nod obţinând o listă liniară simplu înlănţuită cu succesorii săi
5. Pentru fiecare succesor din această listă se execută
6. Se ataşează noul g, obţinut ca sumă între valoarea lui g a configuraţiei t şi costul expandării
7. Se testează dacă acest succesor aparţine listei open sau close şi în caz afirmativ, se verifică
dacă valoarea lui g este mai mică decât cea a configuraţiei găsite în listă
8. În caz afirmativ, nodul găsit este direcţionat către actualul părinte (prin fixarea legăturii tată) şi
se ataşează noul g, iar dacă acest nod se găseşte în close, este trecut în open
9. În caz că acest nod nu se găseşte în open sau close, este introdus în lista open
10. Dacă s-a selectat pentru expandare nodul corespunzător configuraţiei finale atunci se trasează
folosind o procedură recursivă drumul de la configuraţia iniţială la cea finală utilizând legătura
tată
11. Dacă ciclul se încheie deoarece lista open este vidă înseamnă că problema nu are soluţie
Obs: Algoritmul se poate implementa folosind o singură listă iar diferenţierea dintre nodurile
expandate şi cele neexpandate se va face după un câmp special. Apare însă dezavantajul că
operaţiile de căutare se fac într-o listă mai lungă. Acest dezavantaj se poate elimina folosind o
structură specială numită tabelă hash. În unele cazuri se poate folosi un arbore binar.
Dacă programul care se scrie trebuie rulat de un număr mic de ori, prima cerinţă este mai
importantă; în această situaţie, timpul de punere la punct a programului e mai important decât
timpul lui de rulare, deci trebuie aleasă varianta cea mai simplă a programului.
Daca programul urmează a fi rulat de un număr mare de ori, având şi un număr mare de date
de prelucrat, trebuie ales algoritmul care duce la o execuţie mai rapidă. Chiar în această situaţie,
ar trebui implementat mai înainte algoritmul mai simplu şi calculată reducerea de timp de
execuţie pe care ar aduce-o implementarea algoritmului complex.
Timpul de rulare al unui program depinde de următorii factori:
- datele de intrare;
- calitatea codului generat de compilator;
- natura şi viteza de execuţie a instrucţiunilor programului;
- complexitatea algoritmului care stă la baza programului.
Deci timpul de rulare e o funcţie de intrarea sa, de cele mai multe ori, nedepinzând de valorile
de la intrare, ci de numărul de date şi operaţii.
In continuare vom nota cu T(n) timpul de execuţie al unui algoritm destinat rezolvării unei
probleme de dimensiune n. Pentru a estima timpul de execuţie trebuie stabilit un model de calcul
şi o unitate de măsură.
Vom consideră un model de calcul (numit şi maşină de calcul cu acces aleator) caracterizat
prin:
• Prelucrările se efectuează în mod secvenţial.
• Operaţiile elementare sunt efectuate în timp constant indiferent de valoarea operanzilor.
• Timpul de acces la informaţie nu depinde de poziţia .
• *Timpul de execuţie al întregului algoritm se obţine însumând timpii de execuţie a
prelucrărilor componente.
Importanţa celui mai defavorabil caz
In aprecierea şi compararea algoritmilor interesează în special cel mai defavorabil caz deoarece
furnizează cel mai mare timp de execuţie relativ la orice date de intrare de dimensiune fixă. Pe
de altă parte pentru anumiţi algoritmi cazul cel mai defavorabil este relativ frecvent.
În ceea ce priveşte analiza celui mai favorabil caz, aceasta furnizează o margine inferioară a
timpului de execuţie şi poate fi utilă pentru a identifica algoritmi ineficienţi (dacă un algoritm are
un cost mare în cel mai favorabil caz, atunci el nu poate fi considerat o soluţie acceptabilă).
- Pentru instrucţiunea if , timpul este suma dintre cel pentru evaluarea condiţiei, O(1) şi cel
al secvenţei ce se execută la condiţie adevărată, tot O(1), calculat mai sus, deci O(1),
acesta fiind deci şi timpul pentru o iteraţie a instrucţiunii for. Pentru cele n - i iteraţii ale
lui for , timpul de execuţie este O((n-i)).
-
- Modalitatea de evaluare a timpului de execuţie al unei proceduri recursive, este ilustrată
prin cea a funcţiei de calcul al factorialului:
- Algoritmul in C:
- int fact(int n) //funcția care calculeza factorialul unui întreg
- {
- if (n==0) //dacă elemental introdus este 0
- return 1; //eroare, ieșirea forțată din program
- else //dacă nu
- return n*fact(n-1); //returnează factoriarul numarului
- }
- int main(void)
- {
- int nr,i,n,f, sum=0; //suma inițială este 0
- printf("\t\tProgramul calculeaza factorialul unui numarul");
- printf("\nIntroduceri numarul: ");
- scanf("%d",&nr); //citim numarul care trebuie calculate
factoriarul
- if (nr==0){ //dacă numarul introdus este 0
- printf("Nu exista!\n"); //eroare
- return 0;} //ieșirea din program
- n=nr; //atribuie lui n numarul citit
- f=fact(nr); //apel la funcția pentru calcula factorialul
- printf("\nFactorialul lui %d este %d\n",nr,f); //afișează factorialul
numarului n
- return 0;
- }
-
-
-
- Dimensiunea intrării este aici n, valoarea numărului al cărui factorial se calculează şi se
notează cu T(n), timpul de rulare al funcţiei main().
- Timpul de rulare pentru liniile de citire este O(1), iar pentru linia for este O(1) + T(n - 1),
deci pentru variabilele c si d neprecizate:
- T(n) = c + T(n - 1), pentru n > 1 sau T(n) = d , pentru n 1.Pentru n > 2, se obţine T(n) =
2c + T(n - 2). Pentru n > 3, expandând pe T(n - 2), se obţine T(n) = 3c + T(n - 3). Deci, în
general T(n) = ic + T(n - i), pentru n > i.
- În final, când i = n - 1, se obţine T(n) = (n - 1)c + T(1) = (n - 1)c + d.
-
-
- 4. Compararea Metodelor de Programare
- Se consideră un triunghi format din n linii, fiecare linie i conţinând i numere întregi. Să
se determine cea mai mare sumă de numere aflate pe un drum între numărul de pe prima
linie şi un număr de pe ultima linie. Fiecare număr din acest drum este situat sub
precedentul, la stânga sau la dreapta acestuia.
-
-
- 4.1. Metoda Divide et Impera
- #include <stdio.h>
- #include<time.h>
-
- double start , stop; //declaram variabilele start si stop
-
- int n; int a[30][30];
- void citire() { int i,j; //funcția pentru citirea datelor
- printf("Introduceti numarul de elemente=");
- scanf("%d",&n); //citim numarul de elemente
- for (i=1;i<=n;i++) //parcurge pina la n
- for (j=1;j<=i;j++) //parcurge pina la i
- {printf("a[%d,%d]=",i,j);
- scanf("%d",&a[i][j]); //citimvaloarea pentru elementul i,j
- }}
-
- void afisare() { int i,j; //funția pentru afișarea datelor
- for (i=1;i<=n;i++) { //parcurge pina la n
- printf("\n");for (j=1;j<=i;j++) printf("%5d",a[i][j]); printf("\n");} //afișează toate
elementele
- }
- int suma (int i, int j) { int s,d,S; if (i<=n) { s=suma(i+1,j);
d=suma(i+1,j+1);
- if (s>d) S=a[i][j]+s; else S=a[i][j]+d; } else S=0; return S; }
-
- void main()
- { start=clock();
- citire(); printf("\n"); afisare(); printf("\n"); printf("\nSuma maxima=
%d\n\n",suma(1,1));
- stop=clock();printf("%Timpul de executie=%lf s", (stop-start)/CLOCKS_PER_SEC);
getch(); }
-
-
-
-
- 4.2 Metoda Greedy
- #include<time.h>
- #include <stdio.h>
- double start , stop;
- int n,S; int a[50][50];
-
- void citire() { int i,j; printf("Introduceti numarul de elemente="); //fucncția pentru
citirea datelor
- scanf("%d",&n); //citim numarul de elemente
- for (i=1;i<=n;i++) for (j=1;j<=i;j++) { printf("a[%d,%d]=",i,j);
- scanf("%d",&a[i][j]); //citim valoarea elementului de pe poziția i,j }
}
- void afisare() { int i,j; //funcția pentru afișarea datelor
- for (i=1;i<=n;i++) { for (j=1;j<=i;j++) printf("%5d",a[i][j]); printf("\n"); } }
- void suma () { int i,j; S=a[1][1]; j=1;
- for (i=2;i<=n;i++) { if (a[i][j]>a[i][j+1]) S+=a[i][j];
- else { S+=a[i][j+1]; j++; } } }
- void main()
- { start=clock(); //incepe calculul timpului de execuție
- citire(); printf("\n"); afisare(); printf("\n"); suma(); printf("\nsuma
maxima==%d\n\n",S); //maxim devine egal cu S
- stop=clock();printf("Timpul de executie=%lf s", (stop-start)/CLOCKS_PER_SEC);
getch();
- } //afișează timpul de executie a programului
-
-
-
-
- 4.3. Metoda Backtracking
- #include<stdio.h>
- #include <time.h>
- double start,stop;
- for (i=1;i<=n;i++) {
- printf("\n");
- printf("\n"); } }
- if (S>Smax) Smax=S; }
- else cod=1; return cod; //cod devine 1 și returneaza valoarea lui cod
- }
-
- void back(int k) { for (int i=1;i<=k;i++) { x[k]=i; if (valid(k)) if (k==n) afis(); else
back(k+1); } }
- }
-
-
-
-
-
-
- Compararea metodelor dupa timpul de executie:
-
Enunt problema Date Timp de executie (secunde)
de test Bactracking Greedy Programare dina Divide et im
Suma maxima in N=5 4,906 3,920 5,492 5,017
triunghi N=10 14,183 11,700 21,467 11,474
N=20 42,424 41,230 45,488 39,480
-
- Exemplu(1): Se dă o multime de numere pozitive, P şi un număr M. Se cere determinarea
unui subset a lui P a cărui sumă a elementelor să fie cel mult M.
- Listing-ul:
- #include<time.h>
- #include <stdio.h>
-
- int main() {
- double start,stop; //variabile care determină timpul de execuție
aprogramului
- int n, p[200], rez[200], k, M, i, j, sum, temp, x;
- start=clock() ; //începe calculul timpului de executie a programului
- printf("introduceti n: ");
- scanf("%d", &n); //citimnumarulde elemente a tringhiiului
- for (i=0; i<n; i++) //parcurge pina la ultimul elementul al tabloului
- { printf("p[%d]=", i);
- scanf("%d", &p[i]); } //citim valoarea fiecarui element al tabloului
- printf("Introduceti suma: ");
- scanf("%d", &M); //citim numarul M
- for (i=0; i<n-1; i++) //parcurge pin al penultimul element al
tabloului
- for (j=i+1; j<n; j++) //parcurge pina la ultimul element al tabloului
- if (p[i]>p[j]) { temp = p[i]; p[i] = p[j]; p[j] = temp; }; //compara
elementele tabloului
- k = 0; sum = 0; //atribuie variabeor valoarea 0
- for (i=0; i<n; i++) { //parcurge pina la ultimul element al
tabloului
- x = p[i]; /* alege(A,i,x) */
- if ( sum + x <= M ) { //dacă suma nu depășeste suma
introduse
- rez[k++] = x; //atribuie lui rez valoarea stocat in x
- sum += x; //la suma se adaga
valoarea lui x
- }
- else //dacă nu
- break; } //se termina iterația și iese din contor
- printf("submultimea rezultat: ");
- for (i=0; i<k; i++) printf(" %d", rez[i]); printf("\n"); //afișează rezultatul
- stop=clock(); //se termina calculu timpului de execuție a programului
- printf("Timpul de executie=%lf s", (stop-start)/CLOCKS_PER_SEC);
- getch(); //așteaptă pina la tastarea unei taste
- }
-
-
- Exemplu(2): Problema damelor de şah. Să se găsească toate soluţiile posibile de aşezare
pe tabla de şah de n linii şi n coloane a n dame, astfel încât să nu existe două dame pe
aceaşi linie, coloană sau diagonală.
- Listing-ul:
- #include <stdio.h>
- #include <math.h>
- int main()
- { int n,i,k,v;
- int x[20]; //x[i] va reprezenta coloana pe care se afla dama de pe linia i
- printf("Introduceti n:");
- scanf("%d",&n); //citim numarul de elemente
- k = 1; x[1] = 0;
- while (k > 0) { //cit timp k este mai mare ca 0
- v=0; //atribuie lui v valoarea 0
- while ((x[k] <= n-1) && (v==0)) {x[k]++; //daca conditia este adevarata
- for (v=1,i=1; (i<=k-1) && (v==1);i++)
- if ((x[k]==x[i])||(k-i == abs(x[k]-x[i]))) //verifica daca k este egal cu i
- v=0; //daca da v din nou devine 0
- }
- if (v==0) k--; //daca v nu sa modificat , decrementeaza k
- else if (k==n) { //daca nu, verifica daca k este egal cu n
- for (i=1;i<=n;i++) //parcurge pina la ultimul element al
tabloului
- printf("%d ",x[i]); printf("\n"); } //afiseaza toate elementele tabloului
- else { k++; //daca ca nu este egal cu n
- x[k] = 0; } } //fiecare elementul k al tabloului devine 0
- }
-
-
-