Curs 5 Prelucrarea informaţiei În acest modul se vor trece în revistă noţiuni privind modul în care se specifică diverse operaţiuni efectuate asupra datelor şi modul în care se specifică desfăşurarea acestor operaţiuni – conform prelucrărilor vizate. Abordarea sistematică a operaţiunilor şi desfăşurării lor este impusă de exprimarea concisă şi completă a acestora printr-un limbaj de programare; fiindcă limbajele de programare moderne reprezintă de fapt codificări optime ale operaţiunilor necesare în oricăror categorii de prelucrări, se utilizează pe parcursul modulului formalismul specific acestor codificări. În scopul înţelegerii şi asimilării lui, se prezintă în continuare un model intuitiv care foloseşte termeni ai acestui formalism, pe exemplul asamblării unor motoare de automobil la o secţie de profil: (I) Piesele motorului se află în cutii aşezate pe raft, cu etichete – fiecare cutie este o variabilă cu identificator (numele pe etichetă), având valoare (piesa) sau fiind liberă („aşteptând” o piesă ce va fi asamblată şi plasată apoi în cutie); piesele în sine sunt date, iar categoria de piese (de exemplu „carburatoare”) reprezintă un tip de date. (II) Fiecare tip de piesă prezintă modalităţi specifice de îmbinare cu alte piese (şuruburi de dimensiuni stabilite, cleme, etc.) – fiecare modalitate este un operator specific tipului de date. (III) Asamblarea pieselor are loc după un plan dinainte stabilit, pe paşi – după un algoritm de rezultat în urma unei analize şi folosind cunoştinţele în domeniu. (IV) Îmbinarea pieselor are loc prin mai multe şuruburi (cleme, etc.) combinate corespunzător – adică prin expresii în care piesele sunt operanzi şi şuruburile operatori. (V) Dacă o piesă nu se potriveşte la îmbinare se ia decizia de o repara sau arunca, iar la asamblarea mai multor piese de acelaşi fel acţiunile pentru una se repetă la celelalte – conform unor instrucţiuni de control al fluxului de operaţiuni. (VI) În final, motorul este ambalat şi trimis la magazin – pentru a fi prezentat utilizatorilor. Noţiunile care intervin în modelul de mai sus vor fi explicate şi descrise – cu specificul lor pentru domeniul TIC, în cele ce urmează, însoţite de exemple sau de informaţii tehnice specifice unor limbaje de programare uzuale (cum sunt Pascal, C, Java).
Expresii Numele calculator duce gândul la calcule – ca fiind prelucrările pe care acest instrument le poate executa dar, aşa cum s-a arătat la, posibilităţile de prelucrare ale calculatoarelor sunt mult mai largi. Expresia este descrierea formală a unui set de acţiuni efectuate cu un anumit tip de date. Semnul = are rolul de a „încărca” în variabila din stânga sa, valoarea rezultatului obţinut în dreapta sa. Astfel, semnul = poate fi privit ca un operator de atribuire, iar întreaga construcţie F = m·a ca o expresie cu o singură valoare – anume valoarea obţinută pentru forţa F. Operatori Operatorii sunt simboluri ale unor acţiuni cu semnificaţie stabilită prin tipul de date operat. Am folosit deja simbolul + ca operator de concatenare (deci simbol al operaţiei de concatenare). Operatorul poate fi format din unul sau mai multe caractere. Entitatea asupra căreia se aplică operatorul se numeşte operand. După numărul de operanzi, operatorii pot fi:
operatori unari, care se aplica unui singur operand, de ex. operatorul - in expresia -x; utilizarea operatorului unar se face, de regula, sub forma "prefix" operator operand (operatorul este pus în faţa operandului) iar uneori sub forma "postfix" operand operator, în care operatorul este pus după operand;
operatori binari, care se aplică asupra a doi operanzi, fiind situat între aceştia; de exemplu operatorul de concatenare + in expresia "acesta este"+" un exemplu" sau operatorul de adunare a doua numere + in expresia 17+28. Operatorii binari se folosesc sub forma "infix" operand1 operator operand2 - în care operatorul este situat între cei doi operanzi; operatori ternari, care se aplica asupra a trei operanzi; de exemplu în limbajele C şi Java există operatorul ternar (? : ) folosit în expresiile condiţionale. Operatorul ternar se scrie sub forma (operand1 ? operand2 : operand3 ) în care operand1 are o valoare logica (true sau false), iar ceilalţi doi operanzi reprezintă acţiuni. Pentru o privire completă asupra domeniului, se prezintă în continuare categorii de operatori, cu simboluri uzuale pentru limbajele C şi Java. Operatori aritmetici sunt operatorii prezentaţi la Error! Reference source not found. şi Error! Reference source not found., pentru calcule numerice, care se aplică funcţie de tipul operanzilor (întregi sau reali). Există o ordine de precedenţă a operatorilor aritmetici: primii se aplică operatorii unari apoi cei de înmulţire şi împărţire, ultimii de sumare algebrică; această ordine poate fi schimbată prin grupări între paranteze „(‟ şi „)‟ ale operanzilor şi operaţiilor în expresie. Operatori relaţionali sunt operatori (binari) pentru comparaţie sau incluziune: <, >, = =, != (simbol pentru ), <= şi >= (simboluri pentru şi ), in (pentru apartenenţă). Operatori logici sunt operatorii prezentaţi la şi produc un rezultat logic („adevărat ” sau „fals”). Ordinea de precedenţă este: primul negarea ! (NU), apoi conjuncţia && (ŞI), ultimul disjuncţia || (SAU); ordinea de precedenţă se poate schimba prin grupare cu paranteze. Tipuri de expresii Prin rolul său în program, o expresie este o linie de cod care poate fi redusă la o singură valoare. Se prezintă în continuare tipuri de expresii, prin exemple, pentru cazuri de operatori şi precedenţă care prezintă interes. Expresii matematice Fie declaraţiile de variabile: int a=3, b=7, c=11, d;
care iniţializate cu valorile de mai sus, utilizate în expresii ca mai jos, dau rezultatele specificate în coloana din mijloc şi se execută în ordinea de precedenţă explicată în dreapta: Expresii logice Aceste expresii au rezultat logic (adevărat - true, sau fals - false). Operatorii relaţionali dau ca rezultat o valoare de tip logic fiindcă în urma relaţiei (comparaţiei) situaţia poate fi adevărată sau falsă. Comparaţiile realizate cu operatori relaţionali precum şi agregările de operatori logici sunt privite ca întrebări „este adevărat că … ?” Instrucţiuni Pentru prelucrările simple – de genul calculelor matematice, este suficient un calculator de buzunar. Dacă aceste calcule sunt combinate pentru a determina un rezultat complex – de exemplu soluţionarea generală a unei ecuaţii de gradul întâi, atunci mai sunt necesare acţiuni de decizie: de exemplu „este coeficientul necunoscutei nul?” – atunci ecuaţia este degenerată; asemenea acţiuni de control al desfăşurării prelucrărilor propriu-zise trebuie realizate prin programul de calculator, pe baza unor comenzi specifice limbajului de programare ales. De fapt, pe lângă prelucrările variabilelor (care se realizează prin expresii), mai sunt necesare două tipuri generice de acţiuni: de decizie şi de repetiţie. Rezultă că un limbaj de programare trebuie să asigure comenzi de:
a) Atribuire – pentru de obţinere a unui rezultat pentru o expresie şi de încărcare a valorii acestui rezultat în variabila ţintă (din stânga semnului =). b) Decizie – pentru alegerea unei secvenţe de comenzi din mai multe variante (uzual două) urmare a rezultatului unei expresii logice – condiţia de decizie. c) Repetiţie – pentru reluare a unei secvenţe de comenzi de mai multe ori, până la îndeplinirea unei condiţii de oprire. Între aceste acţiuni doar atribuirea este cea care priveşte formularea expresiilor, rezolvarea şi păstrarea rezultatelor. Decizia şi repetiţia modifică doar cursul de execuţie al atribuirilor. Tabloul comenzilor posibile se completează cu: d) Salturi şi reveniri – pentru întreruperea unei secvenţe de comenzi şi preluarea controlului de către altă secvenţă de comenzi în cadrul aceluiaşi bloc program sau într-un subprogram. e) Operaţii de intrare / ieşire – pentru interacţiunea cu exteriorul, prin periferice de intrare / ieşire (către om sau instalaţii, pentru stocarea sau transferul datelor). Comenzile din categoriile a) şi e) modifică valoarea sau suportul datelor, iar comenzile din categoriile b) c) şi d) modifică controlul asupra fluxul de comenzi. Uzual, operaţiile de intrare / ieşire nu fac parte din limbajul de programare ci din biblioteci de funcţii ataşate acestuia. Cuvintele cheie care exprimă categoriile de acţiuni de mai sus precum şi modul corect de exprimare al lor (adică sintaxa) constituie setul de instrucţiuni al unui limbaj de programare. Dacă se cunosc modurile de exprimare ale aceste acţiuni de bază într-un limbaj de programare, se poate considera limbajul cunoscut în proporţie de 50%. Trebuie remarcat că, în limbajele de programare moderne, operaţiile de intrare ieşire nu constituie instrucţiuni de limbaj ci sunt realizate prin subprograme specializate – în afara limbajului. Un program este un text ce cuprinde o secvenţă de instrucţiuni din categoriile de mai sus, care sunt executate în ordinea în care apar în text. Procesorul asigură preluarea şi executarea comenzilor în secvenţă, aceasta fiind ideea centrală de funcţionare a maşinilor von Neumann. După cum se poate imagina, instrucţiunile de decizie şi de repetiţie au o exprimare mai complexă – de exemplu pentru decizie trebuie indicate acţiunile pentru ramura în care condiţia de decizie are rezultat „fals” şi acţiunile pentru ramura cu rezultat „adevărat”. Instrucţiunile pentru decizii şi repetiţii sunt mai complexe şi au nevoie de mai multe linii de text pentru a le descrie, pe când pentru instrucţiunile de atribuire este suficientă o linie de text ce descrie expresia vizată. De aceea instrucţiunile din prima categorie se consideră „instrucţiuni simple”, iar cele din ultimele două categorii „instrucţiuni structurate”. Instrucţiuni simple Într-un program acţiunile sunt descrise prin linii de text, care apoi pot fi interpretate şi executate de către calculator – linie cu linie sau pe loturi de linii. Descrierea unei acţiuni complete se încheie cu ; ca simbol uzual indicând „sfârşitul acţiunii”. Asemenea instrucţiuni se prezintă în continuare. Instrucţiunea de atribuire Prelucrarea efectivă a datelor are loc în instrucţiunea de atribuire: una sau mai multe valori intră într-o expresie al cărei rezultat se atribuie variabilei stânga simbolului de atribuire = (în limbajele C şi Java) sau := (în limbajul Pascal). Pentru exemplul de calcul al forţei: F=m*a;
// în C, respectiv
F:=m*a;
/* în Pascal
În expresie intervin numai variabile ce au primit valori, iar variabila stânga trebuie să aibă tipul de date al rezultatului expresiei.
Instrucţiuni de salt Întreruperea forţată a executării unei secvenţe de instrucţiuni (şi saltul la începutul altei secvenţe) se poate face condiţionat (dacă a fost îndeplinită o condiţie logică) sau necondiţionat. a) Saltul necondiţionat – provoacă părăsirea execuţiei secvenţiale a comenzilor. „Ruperea” secvenţelor de comenzi din program prin salturi necondiţionate, vădesc o proiectare defectuoasă, nesistematizată, a descrierii prelucrărilor; ele fac programul greu inteligibil şi dificil de depanat sau dezvoltat şi de aceea trebuie eliminate. În limbajele Pascal şi C saltul necondiţionat există doar pentru caracterul său istoric: GOTO eticheta; Secvenţei curentă este întreruptă şi execuţia se continuă de la eticheta. Există instrucţiuni de salt care provoacă părăsirea secvenţei de comenzi doar în condiţii bine precizate, impuse direct de prelucrări. b) Saltul de revenire din subprogram la programul apelant: return [expresie]; unde expresie produce ca rezultat o valoare ce este „întoarsă” (returnată) programului apelant de către subprogram, fiind folosită direct în expresii. Parantezele drepte [ şi ] indică o parte opţională – adică expresie poate lipsi în situaţia când nu este necesară o valoare returnată. Saltul de la programul apelant la programul apelat se face prin însăşi numele subprogramului . Salturile de terminare abruptă a instrucţiunilor structurate sunt impuse de prelucrări (în cazul repetiţiilor sau deciziilor multiple): c) Întrereupere – încheie instrucţiunea curentă şi trece la următoarea: break [eticheta]; d) Continuare – reia secvenţa curentă de la începutul ei înainte de final: continue [eticheta]; Cazuri de utilizare sunt la întreruperea unei bucle de repetiţie (şi saltul în afara sa la secvenţa ce începe de la eticheta), respectiv reluarea unei bucle de repetiţie de la început fără a se parcurge întreaga secvenţă din buclă. Instrucţiuni structurate Controlul fluxului de instrucţiuni este necesar pentru soluţionarea unei probleme complexe, iar algoritmul este de fapt o reţetă de control al fluxului de instrucţiuni prin care se caută soluţia la problema dată. Fie exemplul banal de „algoritm pentru rucsacul de vacanţă”: 1) dacă (este vacanţa de vară) atunci 2) repetă aşează în rucsac tricou; 3) repetă aşează în rucsac obiect de plajă; 1‟) altfel 4) repetă aşează în rucsac pulover; 5) repetă aşează în rucsac obiect pentru schi; 6) închide rucsacul;
În acest exemplu se observă „decizia” 1) – 1‟) şi „repetiţia” 2), 3) sau 4), 5). Acestea se vor regăsi ca instrucţiuni într-un limbaj de programare ca instrucţiuni structurate – remarcaţi structura deciziei dacă .. atunci .. altfel, subliniată prin numerotarea cu apostrof. Cuvintele cu litere înclinate (cursive) indică modul cum se vor efectua operaţiunile (sunt „instrucţiuni”) iar cuvintele scrise normal sunt operaţiunile înseşi (de fapt instrucţiuni simple). Astfel, la pasul 2) se repetă aşezarea în valiză a tricourilor – unul la un moment dat (cel roşu, apoi cel verde, etc.), iar la pasul 3) similar, aşezarea fiecărui obiect de plajă (umbrelă, ulei de plajă, etc.). Omul execută operaţiunile înscrise mai sus una după alta, fără a fi necesară indicarea ordinii lor prin numere – în acest exemplu au fost numerotate doar pentru a fi referite. Similar, procesorul (construit după principiile enunţate de von Neumann) execută operaţiile una după alta, în ordinea apariţiei lor în textul programului; în exemplul nostru, după încheierea repetiţiei 3) se execută direct 6) – în cazul vacanţei de vară şi în cazul vacanţei de iarnă. Instrucţiunea de decizie binară Pentru indicarea a două alternative în desfăşurarea acţiunilor unui program se foloseşte decizia sau ramificaţia binară „dacă .. atunci”, a cărei format general (în limbajul C) este: if (expresie) instrucţiune1; else instrucţiune2; unde instrucţiune1 se execută atunci când condiţia expresie are valoare logică „adevărat” (sau în limbajul C, are o valoare diferită de 0), altfel (când condiţia este falsă sau 0) se execută instrucţiune2. Când pe una din ramuri sunt mai multe instrucţiuni de executat, atunci se foloseşte pe acea ramură instrucţiunea compusă. Se observă că, prin modul de scriere a textului, ies în evidenţă prin „indentare” (adâncire spre dreapta) ce se execută pe ramura „adevărat” (imediat după if) şi ce se execută pe ramura „fals” (imediat după else). În cazul în care nu există o instrucţiune2, atunci ramura else lipseşte şi se continuă cu secvenţa ce urmează după simbolul de sfârşit ;. În diferite limbaje de programare (inclusiv în limbaje „script”) se foloseşte pentru decizia binară construcţia de mai sus, cu diferenţe minore de scriere (sintaxă): lipsesc parantezele după condiţiei şi apare cuvântul then (ca în Pascal) sau în loc de ; este folosit endif. Instrucţiunea de decizie multiplă Decizia binară – prezentată mai sus, priveşte situaţii simple, cu două alternative: Alb/Negru, Da/Nu, Adevărat/Fals. Pentru situaţii în care decizia priveşte mai mult de două alternative, este dificil de aplicat mai multe instrucţiuni de decizie binară. O asemenea situaţie apare când expresia de selecţie nu are valori binare ci multiple – cum ar fi cazul selecţiei opţiunilor unui meniu; fiecare opţiune devine un caz selectat printr-un număr sau prin poziţia indicatorului pe ecran („mouse”). Ca exemplu, se prezintă instrucţiunea switch în limbajul C, într-o secvenţă de program în care se alege o opţiune din trei posibile la alegerile prezidenţiale, prin numărul acesteia – furnizat prin NumarOptiune: switch (NumarOptiune) { case ‘1’: { “Candidat de stânga”}; break; case ‘2’: { “Candidat de centru”}; break; case ‘3’: { “Candidat de dreapta”}; break; default: { „Exprimaţi-vă opţiunea”}; }
Se observă că pentru un alegător indecis (care alege NumarOptiune diferit de 1, 2 sau 3) există posibilitatea de a fi atenţionat că doar aceste opţiuni sunt disponibile – prin secţiunea default (care înseamnă „implicit” în engleză); textele dintre {} la fiecare caz, apar în loc de blocuri program prin care se afişează – de exemplu, sigla şi numele candidatului. Instrucţiunile de salt break, sunt utilizate pentru a încheia posibilitatea de selecţie după ce s-a exprimat o opţiune, în scopul continuării cu secvenţa program ce urmează instrucţiunii switch, adică după }. Instrucţiunea de repetiţie după număr cunoscut de paşi Deseori, prelucrările pentru care este util calculatorul sunt cele în care se repetă anumite operaţii de foarte multe ori; omul ar obosi (apoi greşi) la repetiţii îndelungate ale aceloraşi operaţii, dar echipamentul electronic le execută precis, rapid şi fără complicaţii sociale. Atunci când se cunoaşte numărul de iteraţii (cuvânt ce indică „repetiţii numerotate”), este utilă instrucţiunea for – ce apare în diverse limbaje, dar se va prezenta cu sintaxa uzuală în limbajul C. Trebuie remarcat că, pentru a efectua numărul de iteraţii dorit, este necesar un contor pentru care se indică o valoare de start, o valoare de final şi o modalitate de avans a contorului (în această ordine) prin expresiile ce apar între () şi sunt separate cu ; ca mai jos. For (NrStud=1,NrBilet=35;NrStud<=30;NrStud++,NrBilet--) { „Prezintă legitimaţie şi primeşte bilet de examen” }; Exemplul priveşte verificarea legitimaţiei de student şi primirea de către acesta a biletului de examen, la o grupă de 30 studenţi. Contorul NrStud reţine al câtelea student a intrat în sala de examen iar contorul NrBilet reţine câte bilete au mai rămas examinatorului – din totalul de 35; expresiile NrStud++ şi NrBilet—adună şi respectiv scad o unitate din contoarele respective (aceasta înseamnă ++ şi respectiv --). Instrucţiunea de repetiţie după condiţie Atunci când nu se cunoaşte numărul de iteraţii, terminarea repetiţiilor poate fi indicată de o condiţie cunoscută – care se aplică înainte sau după efectuarea operaţiei în bucla de repetiţie. Ca urmare, se folosesc una din cele două tipuri de instrucţiuni: a) „cât timp (condiţie) adevărată – repetă {operaţie}” – prin care este posibil ca operaţia să nu se execute nici măcar o dată, dacă de la început condiţia nu este îndeplinită; b) „repetă {operaţie} – cât timp (condiţie) adevărată” – prin care operaţia se execută cel puţin o dată, indiferent dacă este adevărată condiţia la intrarea în buclă. Pentru exemplul de mai sus reluat, dar în care nu se cunoaşte numărul de studenţi ce se prezintă la examen, descrierea în limbajul C a cele două cazuri ar fi: (a) while (NrStud>0) { „Prezintă legitimaţie şi primeşte bilet de examen” }; (b) do { „Prezintă legitimaţie şi primeşte bilet de examen” }; while (NrStud>0); Lăsăm cititorul să decidă care dintre formele (a) sau (b) este adecvată exemplului ales. (Indicaţie – este posibil ca la un examen să nu se prezinte nici un student). Dacă există cazuri în care bucla de repetiţie trebuie întreruptă în desfăşurarea ei (de exemplu în cazul când unele operaţii nu se execută dacă nu este îndeplinită o condiţie), atunci apar în interiorul buclei instrucţiuni de salt de tipul break (întrerupe repetiţia şi părăseşte bucla) sau continue (reia bucla de la început fără executarea operaţiunilor care urmează acestei instrucţiuni) – vezi c) şi d).
Programe şi subprograme Secvenţele de instrucţiuni sunt organizate în programe şi subprograme, fiecare având un nume care – în principiu, indică rolul prelucrărilor acestuia. Program este denumirea generică a unei înşiruiri de comenzi care execută prelucrări într-un scop dat; comenzile pot fi exprimate prin cuvinte cheie specifice unui limbaj de programare (în programarea „clasică”) sau poate fi o structură de reprezentări grafice ale comenzilor, plasate pe o suprafaţă de lucru (în programarea „vizuală”). În cazul programelor scrise într-un limbaj de programare, structura textului depinde de modul de programare (structurată sau obiectuală), în principiu, pentru un program fiind specificate: nume_program (lista parametri) { declaratii variabile corpul programului } unde nume_program este un identificator al programului (prin care poate fi apelat spre a executa acţiunile înscrise în el), lista parametri este setul de date care se furnizează programului (ca „materie primă”) şi asupra cărora se vor executa acţiunile, declaratii variabile indică variabilele (locale) necesare stocării rezultatelor intermediare, corpul programului este secvenţa efectivă de comenzi pentru acţiunile vizate, iar acoladele {} încadrează şi delimitează programul propriu-zis. Subprograme În cazul unor prelucrări complexe, care sunt necesare repetat, este indicată folosirea subprogramelor; acestea sunt secţiuni de cod înscrie o dată şi folosite de mai multe ori, prin apelarea lor ori de câte ori este nevoie, separat sau în cadrul expresiilor. Apelarea subprogramului se face prin intermediul identificatorului său (numele), similar cu referirea unei variabile. Dacă un subprogram este inclus unei biblioteci de subprograme el poate fi apelat de către oricare alt program scris în limbajul respectiv sau în alte limbaje (dacă la apelare se fac precizările de rigoare). Subprogramul este o prelucrare care se poate efectua asupra unui set de date ale căror valori sunt specificate la „apelarea” subprogramului. Subprogramul este un program apelat (prin nume) în cadrul altui program (programul apelant) pentru a executa acţiunile sale specifice. Deci subprogramele nu sunt „de sine stătătoare”, adică nu pot fi lansate direct de către utilizator din sistemul de operare ). Principial, există două categorii de subprograme: Subprogramul care, după execuţia acţiunilor sale, revine în programul apelant cu o singură valoare (valoare „returnată”) ce poate fi folosită direct în calculul unei expresii; acest tip de subprogram se numeşte funcţie. Un exemplu clasic este funcţia sin(x), care poate fi apelată într-o expresie – de exemplu a+b+sin(x, sumele fiind realizate doar după calculul sinusului pentru valoarea x furnizată. Structura textului de definire a unei funcţii adaugă, la structura de principiu din deschiderea, specificarea tipului valorii returnate şi comanda de revenire în programul apelant: tip_valoare_returnata nume_functie (lista parametri) { declaratii variabile corpul programului return expresie; //nu apare obligatoriu la final } unde tip_valoare_returnata specifică tipul de date al valorii rezultate în urma calculelor din expresie şi care va fi adusă la revenirea în programul apelant. Execuţia programelor se face de către procesor, care poate funcţiona doar secvenţial şi pentru o singură sarcină (un singur program) la un moment dat. La apelul unui subprogram de către programul apelant, acesta din urmă trebuie „părăsit” de către procesor iar contextul de lucru (adică setul rezultatele
parţiale) din memoria locală a procesorului trebuie salvat în memoria internă – RAM. La revenirea din subprogram, acest context se încarcă din memoria RAM în memoria locală a procesorului iar programul apelant reia lucrul – exact din punctul în care a fost întrerupt pentru execuţia subprogramului, folosind valoarea „întoarsă” ca rezultat de către subprogram (vezi instrucţiunea return.b), şi valoare rezultat pentru expresie). Programul principal Execuţia acţiunilor înscrise într-un program trebuie iniţiată la comanda utilizatorului. Pentru aceasta sistemul de operare interacţionează cu omul, primeşte comanda (care de obicei este chiar numele programului) şi „lansează” execuţia acestuia. Partea din textul unui program care poate fi lansată nemijlocit de sistemul de operare (deci care poate funcţiona de sine stătător) se numeşte program principal. Acesta este, în general, o succesiune de acţiuni grupate în corpul programului pe trei secţiuni: Introducerea datelor Prelucrarea datelor Afişarea rezultatelor Pentru execuţia acestora, se face apel la subprograme din biblioteca de subprograme sau din setul subprogramelor declarate în textul sursă al programului ca ansamblu; în acest ultim caz, programul va fi specificat astfel: nume_program_principal (lista parametri) { declaratii variabile declaratii subprograme definire subprograme { corpul programului principal } } unde declaratii subprograme reprezintă secţiunea în care se inventariază numele şi lista parametrilor specifice tuturor subprogramelor apelate în textul programul. Secţiunea definire subprograme reia declararea subprogramelor, de această dată cu descrierea acţiunilor din fiecare subprogram aşa cum este prezentat.
Curs 6 Algoritmi Modalitatea în care are loc prelucrarea efectivă a unor date se exprimă prin algoritm – ca paşi succesivi, repetiţii şi ramificaţii prin care se execută operaţiile vizate. Rezolvarea oricărei probleme presupune o metodă şi o execuţie în paşi a acesteia, spre soluţia dorită. De exemplu, rezolvarea unei ecuaţii de gradul I de forma a·x + b = 0 presupune paşii: i) se verifică dacă a = 0; dacă da – ecuaţia este degenerată; ii) se verifică dacă b = 0; dacă da x = 0; iii) altfel, x = -b/a. Pentru prelucrări mai complexe, trebuie găsită o reprezentare explicită, în amănunt, a operaţiunilor şi paşilor efectuaţi pentru a rezolva problema. De exemplu, în cazul de mai sus, al ecuaţiei de gradul I, nu s-a specificat nimic privind introducerea datelor (valorile pentru a şi b) dar şi privind afişarea rezultatului x (care pentru un calculator nu sunt subînţelese – aşa cum sunt ele pentru un om obişnuit cu rezolvarea problemelor de matematică). Algoritmii sunt modalităţi prin care se exprimă succesiunea de operaţii prin care un program pe calculator poate ajunge la datele de intrare furnizate la rezultatele dorite. Trebuie remarcat că un algoritm nu exprimă doar operaţii cu numere ci orice fel de prelucrări (cu texte, imagini, etc.) aşa cum se prezintă intuitiv în „algoritm pentru rucsacul de vacanţă”. Exprimarea algoritmilor În general, descrierea prelucrărilor prin care se obţine soluţia unei probleme date se face fără a se specifica tipurile de date utilizate, ca şi cum acestea ar fi subînţelese. Un algoritm este o înşiruire de operaţiuni în care se utilizează variabile şi (eventual) alte prelucrări, indicate prin identificatori inventariaţi într-un dicţionar sau nomenclator ce specifică rolul variabilelor sau prelucrărilor respective. Istoric, prima modalitate de exprimare a algoritmilor au fost organigramele – cu avantajul descrierii grafice, intuitive şi uşor de urmărit. Evoluţia către programarea şi proiectarea structurată a impus exprimarea prin pseudocod – cu avantajele descrierii concise, modularizate şi apropiate limbajelor de programare. Organigrame (Scheme logice) Succesiunea de operaţiuni ale unei prelucrări se poate descrie grafic prin blocuri standard. Chiar dacă are mai mult o valoare istorică, organigrama este un bun start în specificarea unui algoritm, fiind intuitivă şi uşor de constituit. START
(a) Terminator iniţial
Date de intrare
b, c, d
a = b+c+sin(d)
i=i+1
(b) Bloc de prelucrare
Subprogram “Maxim”
Da
Nu i<=n
(c) Bloc de decizie binară
STOP
(d) Bloc de (e) Apel de (f) Terminator final intrare/ieşire Figura 1 Blocuri graficesubprogram asociate prelucrărilor elementare reprezentate în organigrame.
START Citeşte valori pentru a, şi b (de la tastatură) c Da
Afişează „Ecuaţie degenerată”
a=0
Nu
Nu
Da b=0 x = 0
x = -b/a
Afişează „Soluţia este:” x
STOP Figura 2 Organigrama pentru algoritmul de rezolvare a ecuaţiei de gradul I. Blocurile grafice generice sunt prezentate în Figura 1, cu simbolurile şi semnificaţiile lor: terminatorii (a) şi (f) indică începutul şi sfârşitul prelucrărilor – delimitând astfel algoritmul descris; blocurile (b) şi (e) indică efectiv prelucrările vizate prin algoritm – descrise prin formule sau printr-un subprogram predefinit ; blocul (d) indică o intrare sau o ieşire de date (citire sau afişare); blocul (c) este necesar pentru specificarea ramificaţiilor de decizie – eventual, implicate în operaţiuni repetate. Succesiunea operaţiilor se indică prin linii şi săgeţi care leagă blocurile grafice prezentate, astfel constituind aşa-numita organigramă. Ca exemplu, se prezintă în Figura 2 organigrama (schema logică) pentru rezolvarea ecuaţiei de gradul întâi. Pseudocod Atunci când se doreşte nu doar descrierea algoritmului ci şi structurarea etapelor de rezolvare a problemei (prin modularizare), este indicată utilizarea unui limbaj codificat care exprimă (în limba maternă) operaţiunile de executat şi cele de control al fluxului de comenzi (cum sunt decizia binară, repetiţia). Modularizarea (adică separarea operaţiunilor pe secţiuni, fiecare cu un scop restrâns şi specific) este singura modalitate de abordare a rezolvării problemelor complexe şi cu soluţie puţin sau deloc cunoscută Pe de altă parte, descrierea algoritmilor prin pseudocod este mult mai compactă decât organigrama (care necesită mult spaţiu pe foaia de scris), este apropiat de un limbaj de programare, fiind o replică exprimată în limba maternă a programului structurat.
Programul principal: #1 citeşte valorile pentru a şi b; #2 soluţionarea ecuaţiei de gradul I; #3 afişează soluţia. Detalierea modulelor #1, #2, #3: #1: afişează pe rând nou: „Introduceţi valoarea pentru a: ”; citeşte de la tastatură: a; afişează pe rând nou: „Introduceţi valoarea pentru b: ”; citeşte de la tastatură: b. #2: Dacă a=0 atunci afişează pe rând nou: „Ecuaţie degenerată”; altfel x = - b/a. #3: afişează pe rând nou: „Soluţia este x = ”, x; afişează pe rând nou: „Pentru reluare lansaţi din nou programul”. Exprimarea acţiunilor prin pseudocod este o modalitate eficientă de a descrie operaţiunile ce urmează a fi implementate într-un program dar poate fi folosită şi spre a descrie orice acţiune complexă ce apare în activitatea umană. Pentru familiarizarea cititorului cu codificarea specifică în informatică (prin limbaje de programare sau macrocomenzi), algoritmii prezentaţi în continuare se vor exprima în continuare în limba engleză şi nu în limba română, folosind instrucţiuni specifice limbajelor C şi Java. Sperăm că dificultatea de înţelegere a limbajului în engleză nu trebuie considerată ca dificultate de înţelegere a algoritmilor în sine, acestea fiind uşor de disociat dacă se face traducerea mentală conform convenţiilor prezentate la. Elaborarea algoritmilor Rezolvarea diferitor probleme din lumea reală cu ajutorul calculatorului se poate face doar dacă soluţia se cunoaşte dar trebuie căutată într-un set existent sau dacă există o metodă de găsire a soluţiei. Algoritmul este o asemenea metodă, dar elaborarea sa este de cele mai multe ori dificilă, considerată uneori (îndeosebi la începuturile programării) o artă. Este evident că folosirea unor metode sistematice de elaborare a algoritmilor este mai eficientă decât o căutare „la ureche” sau prin încercări; aceste metode sunt ele însele algoritmi generici ce vor fi concretizaţi apoi pentru problema reală dată. Între metodele de elaborare a algoritmilor se amintesc cele mai importante (numele fiind indicat în engleză spre a fi uşor de recunoscut), cu o scurtă descriere a specificului lor: Greedy – pentru crearea de submulţimi optimale cu elementele preluate dintr-o mulţime dată şi cu respectarea unor restricţii impuse individual elementelor. Backtracking – pentru crearea de submulţimi optimale cu elementele preluate dintr-o mulţime dată, cu respectarea unor restricţii impuse elementelor dar şi setului (există relaţii între elementele submulţimii soluţie). Divide et impera – pentru probleme ce conţin secvenţe sau piese discrete ordonate, care pot fi divizate în subprobleme care se rezolvă separat şi apoi se obţine soluţia prin combinarea soluţiile parţiale. Branch and Bound – pentru probleme în care soluţiile se pot reprezenta ca noduri în arbore iar căutarea soluţiei se face prin parcurgerea arborelui urmărind totodată o funcţie de cost pentru a limita adâncimea de căutare. Programare dinamica Metode euristice
Algoritmii generali pot sta la baza elaborării de algoritmi particulari, pentru rezolvarea problemelor concrete, după stabilirea metodei celei mai adecvate situaţiei de fapt. Exemple de algoritmi În continuare, se prezintă câteva exemple de algoritmi, cu descrierea lor folosind instrucţiunile prezentate pentru controlul fluxului de operaţiuni iar pentru comunicaţia cu omul (adică pentru afişare) se folosesc exprimări în cuvinte încadrate de { şi }. Acest paragraf are ca scop exemplificarea unor algoritmi concreţi dintre cei mai utilizaţi în prelucrări uzuale, pentru fiecare exemplu prelucrările fiind grupate într-un subprogram cu nume sugestiv ce poate apelat în alte programe. Ambele exemple sunt aplicaţii ale metodei „Divide et impera”. Căutare (logaritmică) binară Cea mai uzuală prelucrare efectuată cu un sistem de calcul este căutarea unei piese de date într-o mulţime oarecare – de exemplu date aflate pe unul din discurile calculatorului sau date aflate în Internet. După cum s-a arătat, cea mai eficientă căutare are loc atunci când datele sunt organizate arborescent. O metodă de căutare eficientă – similară celei în arbore, se poate însă executa şi pe date ordonate secvenţial (după un criteriu de ordine – alfabetic, numeric), adică date ordonate într-o listă. Fie exemplul următor: se dă o listă (un tabel) cu date despre studenţi (număr legitimaţie, nume, prenume, adresă, etc.) ordonate crescător după numărul de legitimaţie; se doresc informaţii despre un student cu număr de legitimaţie cunoscut (denumit Tinta). Trebuie remarcat că înşiruirea de numere de legitimaţie în listă nu este contiguă – unii studenţi au rămas repetenţi, alţii s-au retras, deci secvenţa de numere are „goluri”. Metoda de lucru constă în compararea numărului Tinta cu numărul de la jumătatea listei (element pivot), apoi selectează pentru căutare în continuare jumătatea superioară sau inferioară – funcţie de rezultatul comparaţiei. Căutarea se repetă prin înjumătăţire succesivă până la găsirea liniei numărului Tinta (şi afişarea informaţiilor) sau se constată inexistenţa lui în listă. Pentru descrierea algoritmului se consideră Lista (ca depozitar al informaţiilor despre studenţi) apoi Tinta (ca variabilă ce conţine numărul de legitimaţie căutat) şi elementul Pivot (valoare aflată la mijlocul listei). Subprogramul CautaLogaritmic se bazează pe alte subprograme (funcţii) necesare prelucrărilor: Vida(Lista) care indică dacă lista nu mai are elemente, adică este vidă; Jumate(Lista) indică numărul aflat la jumătatea listei; JumateInf(Lista) extrage jumătatea inferioară iar JumateSup(Lista) extrage jumătatea superioară al listei. <Subprogram> CautaLogaritmic(Lista, Tinta) { if (Vida(Lista)) {Afiseaza „Studentul căutat nu există în listă”; return} else { Pivot = Jumate(Lista); if (Pivot == Tinta) {Afiseaza „Informaţii despre studentul căutat ...” ; return} if (Pivot < Tinta) {Lista = JumateInf(Lista); CautaLogaritmic(Lista, Tinta)} if (Pivot > Tinta) {Lista = JumateSup(Lista); CautaLogaritmic(Lista, Tinta)} }
}
// final CautaLogaritmic
Subprogramul CautaLogaritmic este recursiv (v. 0); simbolurile ==, <, >, sunt operatori relaţionali ”egal”, „mai mare”, „mai mic”, cu rezultat logic (adevărat/fals). Sortare rapidă În multe cazuri piesele de date trebuie aranjate în ordine crescătoare (sortate ascendent) sau descrescătoare (sortate descendent) – vezi cazul listei din, funcţie de un criteriu de ordine (numeric sau alfabetic - lexicografic). Astfel, sortarea este o prelucrare des întâlnită şi, ca atare, necesită metode rapide de execuţie. Există o serie de metode de sortare, între care amintim: „metoda bulelor”, „metoda inserţiei”, „metoda selecţiei”. În cele ce urmează, se prezintă o „metoda rapidă” de sortare (denumită „quicksort”), care este mult mai rapidă faţă de alte metode (cum sunt metoda selecţiei, metoda inserţiei, faţă de care este de 100, respectiv de 20 de ori mai rapidă). Sortarea rapidă se bazează pe înjumătăţire, rearanjând elementele prin comparaţii între mijlocul listei şi capetele acesteia, apoi procedând similar cu jumătăţile obţinute până la ordonarea completă a listei. Algoritmul poate fi realizat recursiv, deci permite o scriere compactă şi elegantă. Se consideră ca variabile: Lista (ca vector conţinând elementele de sortat – în care un element în poziţia i este referit prin indexul său, adică Lista[i], apoi în lista curentă: indicele inferior (primul element) Iinf, indicele superior (ultimul element) Isup, doi indici de lucru i şi j, apoi elementul pivot (elementul de la mijlocul listei) Pivot. Funcţia Inverseaza() inversează elementele de pe poziţiile specificate ca parametri. <Subprogram> SortareRapida(Iinf, Isup) { i=Iinf; j=Isup; Pivot=Lista[(Iinf+Isup)/2]; do { while(Lista[i]<Pivot) i=i+1; while(Lista[j]>Pivot) j=j-1; if (i<j) { Inverseaza(j, i); i=i+1; j=j-1; } while(j>i); if (Iinf<j) SortareRapida(Iinf, j); if (i<Isup) SortareRapida(i, Isup); } // final SortareRrapida Trebuie remarcat că împărţirea din calculul poziţiei pivotului (Iinf+Isup)/2 este împărţire întreagă. Analiza algoritmilor şi complexitate După cum a rezultat din prezentarea de mai sus a unor algoritmi şi a metodelor de elaborare a acestora, rezolvarea unei probleme se poate face prin mai multe căi – deci prin algoritmi diferiţi, şi este utilă, chiar necesară uneori, compararea acestor căi pentru a răspunde la următoarele întrebări: Soluţia obţinută este corectă? Dacă da este ea optimă?
Rezolvarea problemei este inteligibilă şi uşor de modificat? Execuţia algoritmului este eficientă - în sensul timpului de calcul şi a resurselor necesare?
Pe lângă aceste probleme fundamentale din punct de vedere practic apar şi chestiuni teoretice prin care algoritmii se pot analiza şi compara: Durata de execuţie este predictibilă? Cât de complexă este rezolvarea problemei relativ la timpul de calcul necesar? Complexitatea algoritmului În acest paragraf se vor stabile mărimi de evaluare a complexităţii algoritmilor. Analiza algoritmilor priveşte în principal timpul de execuţie, dar acesta depinde puternic de sistemul de calcul şi de contextul de rezolvare. O modalitate mai generală este calculul numărului de operaţii executate de algoritm pentru rezolvarea problemei – aceasta fiind dependentă doar de metoda de rezolvare şi de numărul n al pieselor de date de intrare. În acest sens, compararea programelor se poate face urmărind în principal numărul de operaţiuni costisitoare ca timp de execuţie, cum sunt: apeluri de funcţii, înmulţiri şi împărţiri, atribuiri, comparaţiile (de exemplu pentru sortări – v. metode amintite la . Pentru un număr n de piese de date la intrare, interesează: complexitatea temporală – notată T(n), care reprezintă numărul de operaţii executate de algoritm asupra setului de intrare (considerând o operaţie în unitatea de timp); complexitatea spaţială – notată S(n), care reprezintă numărul de locaţii de memorie folosite de algoritm (memoria de date, stiva, regiştrii); unde T(n) şi S(n) nu sunt funcţii ci relaţii, fiindcă pot avea pentru acelaşi n diferite rezultate (dependente de modul în care algoritmi diferiţi rezolvă o problemă). Analiza unui algoritm se face pentru un număr generic de n date în cele trei cazuri de interes: cel mai defavorabil – indicând maximul numărului de operaţii efectuate, mediu – indicând media numărului de operaţii efectuate, cel mai favorabil - indicând minimul numărului de operaţii efectuate. Uzual, se analizează cazul cel mai defavorabil, apreciat prin Ordinul algoritmului, notat O (O mare), acesta corespunzând timpului cel mai lung (sau spaţiului cel mai mare) necesar algoritmului pentru a prelucra toate piesele n de date. Complexitatea algoritmului este indicată de ordinul său. În urma analizei unui algoritm rezultă un ordin al acestuia, care poate fi încadrat în una din clasele de complexitate de mai jos în scopul comparării la un nivel mai general a algoritmilor: Clasa P pentru probleme polinomiale, ce se pot rezolva într-un număr de operaţii exprimabil printr-un polinom de n – numărul de piese de date, într-un mod determinist (adică cunoscut şi stabilit perfect privind operaţiile şi rezultatul lor). Clasa NP pentru probleme nedeterminist polinomiale, ce se pot rezolva într-un timp polinomial într-un mod nedeterminst (de exemplu prin „ghicirea” soluţiei folosind algoritmi genetici – v. §Error! Reference source not found.), apoi se verifică dacă soluţia este corectă. În clasa NP există o subclasă de probleme numite NP-complete care nu se pot rezolva în timp polinomial dar măcar se poate verifica în timp polinomial o eventuală soluţie. Soluţia propriu-zisă necesită un timp de rezolvare cuprins între unul exprimat polinomial şi unul exprimat exponenţial.
Categorii de prelucrare şi prezentare a informaţiilor În acest paragraf se vor trece în revistă o serie de prelucrări uzuale realizate de sisteme de calcul, cu caracteristicile lor. Scopul paragrafului este de a se completa noţiunile prezentate în paragrafele anterioare, cu locul şi rolul prelucrărilor de date şi modului cum acestea sunt utilizate de către om. Se va începe cu prelucrări matematice (cele care dau şi numele instrumentului „calculator”) dar se vor trece în revistă şi categorii de prelucrări care emulează modul de a raţiona sau reacţiona al omului, precum şi prelucrări care prezintă informaţia către acesta. Nu se vor trece în revistă domenii de
utilizare a calculatorului în viaţa omului ci caracteristicile diferitor prelucrări pe care le poate (şi trebuie) să le facă în susţinerea unei utilităţi umane. Calcule matematice De la începuturi, maşinile de calcul au fost gândite pentru a efectua mai rapid calcule laborioase şi în volum mare. Caracteristicile acestor prelucrări sunt: (1) determinismul – prelucrările execută operaţii şi paşi bine determinaţi, spre rezultate repetabile (adică pentru aceleaşi date de intrare se obţin aceleaşi rezultate); (2) exprimarea numerică – prelucrările se referă la cantităţi ce se exprimă prin numere şi sunt reprezentate direct în sistemul de calcul în format binar. Este deja evident că, prin caracteristicile lor, aceste prelucrări se rezolvă algoritmic, pe baza unor modele matematice elaborate anterior şi a căror validitate este demonstrată şi verificată. Prelucrările matematice se întâlnesc în orice aplicaţie, fiindcă în orice domeniu al realităţii de utilizează mărimi cantitative. Tocmai de aceea, în general, limbajele de programare oferă – pe lângă operaţiile generale de atribuire, decizie şi repetiţie (denumite instrucţiuni de programare), un set de funcţii pentru prelucrări matematice uzuale (cum sunt funcţiile trigonometrice, cele de aflare a părţii întregi sau fracţionare a numerelor reale) – grupate în biblioteci de funcţii matematice. Pentru efectuarea de calcule foarte laborioase – cum sunt calcule pentru modelarea fenomenelor în mecanica fluidelor sau pentru volume foarte mari de date (în economie, administraţie, transport), precum şi pentru calcule a căror rezultat este aşteptat imediat („cu timp mic de răspuns”), se folosesc pentru prelucrarea datelor algoritmi paraleli. Cu aceşti algoritmi se realizează programe care vor rula pe „maşini paralele”, adică maşini de calcul cu mai multe procesoare în care fiecare prelucrează o anumită parte a problemei; rezultatele parţiale se preiau de la aceste procesoare şi se combină în rezultatul final ce va fi prezentat omului. Algoritmii paraleli sunt elaboraţi pe baza unor modele deterministe, rolurile lor fiind în principal de a distribui sarcinile de calcul şi a obţine o încărcare echilibrată a procesoarelor. Astăzi, prelucrarea paralelă se poate efectua în reţele de calculatoare obişnuite, de exemplu folosind calculatoarele PC din reţeaua locală a departamentului şi programe specializate care distribuie sarcinile de calcul pe acestea. Prelucrări de birou În această categorie se pot încadra toate prelucrările care susţin omul în prelucrarea informaţiilor care, în mod tradiţional, foloseau ca suport hârtia sau mijloacele de comunicaţie de tip telefon sau poştă. Aceste prelucrări şi aplicaţiile care le oferă privesc utilizatorul obişnuit, prin acesta înţelegând orice persoană care are de prelucrat documente „la masa sa de lucru” – fie acesta un funcţionar, om de ştiinţă sau casnic. Vom face mai jos o enumerare a prelucrărilor, domeniilor de lucru şi instrumentelor utilizate pentru acestea, dar asupra lor se va reveni în detaliu la cap. 5 şi 6: Prelucrări de documente „scrise” – folosesc procesoare de texte, ce oferă utilizatorului instrumente de editare, formatare şi corectură automată a textului şi altor elemente de exprimare a informaţiilor (tabele, imagini). Calcule diverse şi ilustrarea seriilor cantitative prin histograme – folosesc foi de calcul electronice, ce permit utilizatorului calcule rapide pe volume relativ mari de date şi într-o modalitate simplă şi eficientă (fără a scrie programe speciale pentru acestea). Pregătirea şi expunerea materialelor de prezentare – folosesc aplicaţii de prezentare către auditoriu, ce permit expuneri multimedia către grupuri de oameni (folii de prezentare, sunet şi imagini animate). Gestiune şi prelucrare a datelor structurate (de tip articol) – folosesc aplicaţii de baze de date, ce permit crearea de tabele cu informaţii de diverse tipuri şi manipularea sau prelucrarea acestora.
Comunicaţii în interiorul şi exteriorul organizaţiei – folosesc aplicaţii de transfer date în intranet (de la şi spre colegi) sau în Internet (de la şi spre lumea largă), care emulează serviciile poştale (poşta electronică – „e-mail”) sau consultarea de „pagini cu informaţii” (web – în mod pasiv sau interactiv). Există apoi aplicaţii de lucru la distanţă (de „acasă” se lucrează pe un calculator distant ca şi cum ar fi pe masa de lucru) şi vizionare interactivă de produse massmedia.
Prelucrările din această categorie sunt foarte variate, aproape în majoritate fiind prelucrări algoritmice deterministe. Pentru faptul că eficienţa şi aplicabilitatea lor devine tot mai largă, tehnicile de Inteligenţă Artificială pătrund în aplicaţiile obişnuite, deci astăzi ele folosesc şi algoritmi nedeterminişti (reţele neuronale artificiale, logică fuzzy, algoritmi genetici). Prelucrări prin metode de Inteligenţă Artificială (IA) În situaţiile din viaţa curentă, omul trebuie să le rezolve mai rar probleme cantitative şi precise; adesea sunt de rezolvat probleme calitative într-un mod aproximativ. De exemplu, la traversarea unei străzi, în plin trafic, un om nu face calculul vitezelor maşinilor, a distanţelor şi stării drumului (etc., etc.) spre a aprecia apoi viteza cu care se va deplasa pentru a nu intra în coliziuni, ci procedează la un raţionament aproximativ, bazat pe informaţii puţine, dar eficient: traversează fără nici o zgârietură. De asemenea, nu efectuează calcule spre a recunoaşte o altă persoană sau o marcă de maşină ci, pe baza caracteristicilor lor cunoscute şi sesizate, decide spontan cine sau ce este. Domeniul care se ocupă cu simularea comportamentului fiinţelor vii se numeşte Inteligenţă Artificială (IA). Implementarea tehnicilor IA se poate face fizic (prin sisteme electronice) sau logic (prin programe pe sisteme de calcul). Sunt vizate, în principal, tehnici pentru: (1) Emularea raţionamentului simbolic – prin Sisteme Bazate pe Cunoştinţe, în care se parcurge un set de cunoştinţe (exprimate ca propoziţii) spre a obţine noi cunoştinţe. (2) Reprezentarea şi inferenţa pentru informaţii aproximative – prin Logică Vagă (Fuzzy), în care exprimări lingvistice aproximative (relative la cantităţi) intră în reguli „dacă .. atunci”, spre a obţine noi valori – aproximative sau precise, necesare unei decizii. (3) Recunoaşterea formelor – prin Reţele Neuronale Artificiale, în care se pun în relaţie un set de caracteristici ale unor obiecte (reale sau abstracte) cu un set de decizii, pe baza unui model conexionist. (4) Soluţionarea problemelor de optim cu tehnici evolutive – prin Algoritmi Genetici, în care mulţimea soluţiilor este privită ca o populaţie de cromozomi în care au loc mutaţii şi selecţii spre adaptarea optimă la un set de restricţii date. Cercetarea şi aplicaţiile Inteligenţei Artificiale se dezvoltă în principal către următoarele arii de interes: Demonstrarea teoremelor – ca modalitate de a valida un adevăr (o teoremă) prin găsirea unei secvenţe deductive de alte adevăruri de la cel iniţial nevalidat (al teoremei) la altul final validat anterior sau prezentat axiomatic. Utilitatea demonstrării automate a teoremelor este legată de evitarea rezolvării unor probleme dificile doar pentru contexte sau date particulare – prin algoritmi realizaţi ad-hoc de programatori „inteligenţi” sau prin păreri exprimate de experţi umani. Jocuri ale minţii – şah sau alte jocuri în care „campioni” umani sunt provocaţi de maşini „inteligente” care de fapt rulează un program dedicat jocului respectiv. Analiză de tip Expert şi consultanţă în domenii aplicative – prin care se prelucrează informaţii de tip simbolic şi se emulează raţionamente în contexte complexe, cu cunoştinţe incomplete sau variabile: diagnoză medicală şi tehnică, predicţie. Planificarea comportării prin analiză scop-mijloc – pentru a crea şi planifica secvenţe de acţiuni, privite ca mijloace, care servesc unor scopuri direct legate de mijloace, de exemplu pentru mişcarea şi comportarea roboţilor.
Înţelegerea şi utilizarea limbajului natural vorbit şi scris – necesar sistemelor de traduce automată, comandă şi exprimarea vocală în interacţiunea cu maşina, înţelesului conţinutului unui mesaj. Percepţie acustică şi vizuală, recunoaşterea formelor – pentru identificare de către maşină a obiectelor şi fenomenelor pentru a le comunica omului sau pentru asistarea sa în luarea deciziilor. Auto-învăţare şi auto-reproducere – pentru comunicarea maşină-mediu şi pentru replicarea unor obiecte (reale sau virtuale) în scopul susţinerii unei utilităţi umane.
În continuare, se vor descrie pe scurt modalităţile de reprezentare şi prelucrare a informaţiilor prin metode de lucru specifice celor patru domenii IA, prezentate mai sus. Totuşi, înainte de a descrie în mod tehnic domeniul Inteligenţei Artificiale, este indicat să lămurim termenul de inteligenţă şi modul în care calculatorul se poate comporta „inteligent”. Nu vom da o definiţie a inteligenţei (care de fapt este foarte controversată) ci, în susţinerea teoriilor inteligenţei artificiale, vom aminti că dualism-raţionalismul Cartezian şi monismul materialist consideră inteligenţa oarecum diferit, dar în esenţă simulabilă cu calculatorul. Iniţial, inteligenţa era considerată capacitatea de a emite judecăţi, iar de aici materialismul computaţional a emis următoarea :secvenţă practică: (A) inteligenţă raţionament prelucrare simbolică calculabilitate, care fundamentează „Sistemele Bazate pe Cunoştinţe” (în engleză Knowledge Based Systems - KBS). Totuşi, acţiunile „inteligente” ale lumii vii nu se reduc toate la raţionament (improprie, după bunul simţ, melcilor sau chiar pisicii spre exemplu). Fiinţele vii în principal reacţionează la stimuli din mediu: (B) inteligenţă reacţie prelucrare subsimbolică calculabilitate, care fundamentează prelucrările de „Soft-computing”, adică prin tehnici Fuzzy, Reţele Neuronale Artificiale şi Algoritmi Genetici. Am putea considera prima abordare (A) ca judecata emisă de om prin „conştient” iar a doua abordare (B) ca reacţia instinctivă sau intuitivă a omului prin „subconştient”.
Rezumat În acest modul s-au trecut în revistă noţiuni privind modul în care se specifică diverse operaţiuni efectuate asupra datelor şi modul în care se specifică desfăşurarea acestor operaţiuni – conform prelucrărilor dorite. Abordarea sistematică a operaţiunilor şi desfăşurării lor este impusă de exprimarea concisă şi completă a acestora printr-un limbaj de programare; fiindcă limbajele de programare moderne reprezintă de fapt codificări optime ale operaţiunilor necesare în oricăror categorii de prelucrări, se utilizează pe parcursul modulului formalismul specific acestor codificări. Expresia este descrierea formală a unui set de acţiuni efectuate cu un anumit tip de date. Operatorii sunt simboluri ale unor acţiuni cu semnificaţie stabilită prin tipul de date operat. Instrucţiunile reprezintă exprimarea într-un limbaj de programare a prelucrărilor ce se doresc a fi efectuate cu datele problemei respective. Instrucţiunile sunt de două tipuri mari: instrucţiuni simple şi instrucţiuni structurate. Program este denumirea generică a unei înşiruiri de comenzi care execută prelucrări întrun scop dat; comenzile pot fi exprimate prin cuvinte cheie specifice unui limbaj de programare (în programarea „clasică”) sau poate fi o structură de reprezentări grafice ale comenzilor, plasate pe o suprafaţă de lucru (în programarea „vizuală”). În cazul unor prelucrări complexe, care sunt necesare repetat, este indicată folosirea subprogramelor; acestea sunt secţiuni de cod înscrie o dată şi folosite de mai multe ori, prin apelarea lor ori de câte ori este nevoie, separat sau în cadrul expresiilor. Apelarea subprogramului se face prin intermediul identificatorului său (numele), similar cu referirea unei variabile. Dacă un subprogram este inclus unei biblioteci de subprograme el poate fi apelat de către oricare alt program scris în limbajul respectiv sau în alte limbaje (dacă la apelare se fac precizările de rigoare). Subprogramul este o prelucrare care se poate efectua asupra unui set de date ale căror valori sunt specificate la „apelarea” subprogramului. Subprogramul este un program apelat (prin nume) în cadrul altui program (programul apelant) pentru a executa acţiunile sale specifice. Deci subprogramele nu sunt „de sine stătătoare”, adică nu pot fi lansate direct de către utilizator din sistemul de operare. Algoritmii sunt modalităţi prin care se exprimă succesiunea de operaţii prin care un program pe calculator poate ajunge la datele de intrare furnizate la rezultatele dorite. Exprimarea algoritmilor sau descrierea acestora se poate realiza în două mari modalităţi: cu ajutorul unor grafuri orientate(scheme logice), cu ajutorul unui pseudocod. Categoriile mari de prelucrare şi prezentare a datelor sunt: calculele matematice, prelucrările de birou, prelucrări prin metode de Inteligenţă Artificială. Concluzii Prelucrarea informaţiilor se face cu ajutorul unor algoritmi printr-o diversitate de modalităţi de prelucrare. Recomandări bibliografice Brookshear J. G., Introducere în Informatică, Ed. Teora, 1998. Burdescu D. D., Algoritmi şi structuri de date, Ed. Mirton, 1992 Jacobson I., Ericsson M. and Jacobson A., The Object Advantage: business process engineering with object technology, Addison-Wesley, 2001. Odăgescu I., Smeurean I., Ştefănescu I., Programarea avansată a calculatoarelor personale, Ed. Militară 1993. Rumbaugh J., et al., Object Oriented modeling and design, Prentice Hall Int., Englewood Cliffs, 1991. Stroustrup B., The annotated C++ Reference manual, Addison Wesley, 1991.
Tardieu H., et al., La methode MERISE. Principes et outils, Les editions dâ&#x20AC;&#x;organisation, 1986. Wilson D. A., Managing Information, Butterworth-Heinemann, 1998. Zadeh L. A., Fuzzy sets, Information and Control, 8:338-353, 1965. Zorkoczy P., Heap N., Information Technology â&#x20AC;&#x201C; an introduction, Pitman Publishing, 1995.