www.agora.ro

Principiul optimalității

Rolul deciziei în programarea DINAMICĂ

Ilie Vieru

În literatura de specialitate programarea dinamică este recomandată pentru acele probleme pentru care se cere să se afle soluția optimă dintr-o mulțime finită de soluții posibile. Metodele euristice, de obicei, nu conduc la o soluție optimă, iar metoda backtracking este foarte lentă.

Principiile generale ale programării dinamice sunt foarte bine precizate în toate lucrările de referință, cunoscându-se trei strategii de rezolvare a problemelor folosind această metodă (înainte, înapoi și mixtă).

Dificultățile apar însă la alegerea uneia din cele trei strategii, la stabilirea relațiilor de recurență corespunzătoare formei în care este verificat principiul optimalității.

Etapa asupra căreia vom insista este rolul deciziilor în trecerea sistemului dintr-o stare în alta, dar mai ales, în valorificarea rezultatelor puse la dispoziție de metodă. În continuare vom arăta, prin câteva exemple cunoscute, că memorând deciziile în fiecare etapă (lucru care este ineficient doar din punct de vedere al consumului de memorie) putem alege atât strategia înainte cât și strategia înapoi, iar evidențierea unei soluții care conduce sistemul din starea inițială în cea finală în condiții de optim, este foarte simplă.

Cel mai lung subșir crescător

Se consideră un vector V cu n numere întregi. Se cere să se afișeze cel mai lung subșir crescător al acestuia.[1]

Soluție

Notăm cu F(k) lungimea celui mai lung subșir crescător care se poate forma începând cu elementul k al vectorului și cu d(k) decizia în poziția k, adică rangul termenului din șir care va urma în subșirul construit imediat după elementul V(k). Obținem astfel relațiile următoare pentru strategia înainte:

Șirurile obținute ne permit să determinăm atât max{F(k) | k = 1..n} cât și termenii care compun un astfel de subșir de dimensiune maximă, folosind valorile date de k, d(k), d(d(k)), ...

Informațiile sunt suficiente ca să putem obține foarte ușor toate subșirurile de lungime maximă cu această proprietate.

Strategia înapoi presupune mici modificări de forma:

Triunghiul

Se consideră un triunghi de numere în care pe prima linie apare un număr, pe a doua linie apar două numere etc. Scrieți un program care calculează cea mai mare dintre sumele numerelor care apar pe drumurile care pleacă din vârf și ajung la bază, astfel încât în fiecare drum succesorul unui număr se află pe următorul rând, fie sub acesta, fie pe diagonală, la dreapta. Se citește numărul n al liniilor care formează triunghiul precum și elementele acestuia, numere întregi din intervalul [0, 99].

(OII, Suedia, 1994)

Soluție

Notăm cu aij numărul situat în triunghi pe linia i și coloana j, cu Fij suma maximă a numerelor care apar pe drumurile care pleacă din aij și ajung la bază și cu dij poziția elementului din linia i+1 care urmează elementului aij pe drumul de sumă maximă.

În aceste condiții relațiile de recurență devin:

Folosind aceste relații și aplicând strategia înainte a programării dinamice, se determină F11 cât și drumul pe care se obține F11.

Strategia înapoi poate fi folosită la fel de simplu, dacă vom considera Fij ca fiind suma maximă a numerelor care apar pe drumurile care pleacă din vârful triunghiului (a11) și ajung în aij. Atunci,

Drumuri minime

Se consideră un graf orientat și matricea A a costurilor atașate arcelor grafului. Se cere ca, pentru fiecare pereche de vârfuri (i, j), să se tipărească lungimea drumului minim de la i la j.

În această problemă se aplică principiul optimalității în forma mixtă, în care funcția obiectiv are forma:

și este asociată drumului de cost minim de la i la j , drum care poate trece numai prin vârfurile 1, 2, ..., k.

Pentru fiecare etapă k, decizia are forma:

Inițial avem d(i, j) = 0, Un rezultat interesant se poate formula astfel:

pentru oricare două vârfuri i, j din graf putem determina atât costul minim, cât și drumul de cost minim cu extremitățile în i, respectiv în j, folosind metoda divide et impera în matricea deciziilor, astfel:

procedure afis(i,j:Integer);

begin

if d(i,j)=0

then

Write(i,' ')

else

begin

afis(i,d[i,j]);

afis(d[i,j],j)

end

end;

Nu vom încheia articolul înainte de a prezenta o problemă celebră care a preocupat mulți matematicieni și anume problema partițiilor unui număr întreg [3].

Partiționare

Orice număr întreg se poate descompune în termeni; astfel, numărul 4 se poate descompune în 5 moduri: 4=3+1=2+2=2+1+1=1+1+1+1. Dacă notăm cu n numărul de descompus și cu p(n) numărul de moduri în care se poate descompune, putem scrie: p(1)=1, p(2)=2, p(3)=3, p(4)=5, p(5)=7, p(6)=11, etc. Să se determine p(n), "nÎN, n>1.

Problema a fost abordată de Euler, apoi de matematicianul MacMahon care a calculat p(100)=190569292. Mai târziu s-au calculat și alte valori pentru p(n).

Pentru rezolvarea problemei vom introduce notația p(n, k) reprezentând numărul de moduri în care se poate partiționa numărul n în așa fel încât fiecare partiție să conțină cel puțin un termen cu valoarea k (1ŁkŁn), iar restul termenilor având valori mai mici sau egale cu k.

Convenim să reprezentăm prin p(n, 0) numărul total al partițiilor lui n (adică p(n)).

De exemplu, avem p(6, 1)=1; p(6, 2)=3, p(6, 3)=3, p(6, 4)=2, p(6, 5)=1, p(6, 6)=1, iar p(6, 0)=11.

Un alt exemplu pe care-l apreciem ca suficient este p(7, 1)=1; p(7, 2)=p(5, 2) + p(5, 1); p(7, 3) = p(4, 3) + p(4, 2) + p(4, 1); p(7, 4) = p(3, 0); p(7, 5) = p(2, 0); p(7, 6) = p(1, 0); p(7, 7)=1.

Vă propunem în continuare următorul algoritm de programare dinamică (vezi și [4]):

(2)

unde p(1, 0) = p(1, 1) = 1. (3)

Folosind relațiile (1) și (2) și ținând cont de condițiile inițiale (3) programul se implementează ușor, obținând valoarea lui p(n) într-un timp excelent, algoritmul având un ordin de complexitate polinomial.

Se observă și următoarea recurență:

p(n, 1) = p(n, 2) = p(n, 3) + ... + p(n, k) = p(n+k, k) (4)

care se regăsește și în [3], unde p(n, k) are o altă semnificație și anume p(n, m) reprezintă numărul de partiții ale numărului n care sunt formate din m termeni:

n=a1+a2+...+an cu proprietatea: a1 ł a2 ł ... ł

an.

Programul care implementează acest algoritm este prezentat în continuare.

Listing NUMERE.PAS

Bibliografie

1. Tudor Sorin, Tehnici de programare, 1994

2. Doina Rancea, Algoritmi fundamentali, 1999.

3. Ioan Tomescu, Combinatorica și teoria grafurilor, 1978.

4. Ilie Vieru, Câteva aspecte legate de metodica predării tehnicii de programare "Programarea dinamică" - Revista "Să înțelegem știința", nr. 2-3/1999.

Domnul Ilie Vieru este cadru didactic la Liceul "Gh.Vrânceanu" din Bacău și poate fi contactat prin e-mail la adresa dimex@cybernet.ro

[cuprins]