SIMULAREA UNUI ALGORITM GENETIC ELEMENTAR FOLOSIND PERL (algoritm bazat pe modelul propus de Prof. dr. Juan J.

Merelo Guervós, Univ. Granada, Spania)
use strict; my my my my my my $generatii = shift || 1000; $dimensiunePopulatie = 100; $solutiaOptima = 'valoareoptima'; $lungimeSir = length( $solutiaOptima ); @alfabet = ('a'..'z'); @populatie;

Comment [BIOFIZ1]: Scopul programului este să identifice cu ajutorul operatorilor genetici un număr de generaţii necesare evoluţiei unei populaţii, iniţializate aleatoriu, să atingă soluţia optimă reprezentată chiar prin şirul respectiv. Comment [BIOFIZ2]: Utilizarea modulului strict impune eliminarea utilizării variabilelor globale şi folosirea exclusivă a variabilelor cu caracter local sau privat. Variabilele şi subrutinele nu pot fi utilizate înainte de a fi declarate. Acest aspect impune următoarea regulă de existenţă a variabilelor. Programul principal nu poate opera decât numai cu variabilele private declarate în blocul principal, nici o variabilă privată declarată în subrutine nu poate fi accesibilă din programul principal. Subrutinele vor putea opera cu variabilele declarate atât în blocul principal cât şi în cadrul subrutinei. O sub-sub-rutină poate folosi variabilele din blocul principal, variabilele din subrutina în care a fost construită cât şi propriile variabile. Deci, „vizibilitatea” variabilelor se face întotdeauna de jos în sus. Altfel spus blocul principal nu poate „vedea” decât propriile lui variabile, o subrutină însă, fiind „mai jos”, le vede atât pe ale ei cât şi pe cele ale blocului principal ş.a.m.d. Comment [BIOFIZ3]: Dacă nu se specifică un anumit număr de generaţii la lansarea programului (parametru care iniţializează tabloul @ARGV şi este ulterior preluat prin shift) atunci se utilizează 1000 de generaţii. Comment [BIOFIZ4]: Dimensiunea populaţiei este fixată prin program la 100 de componente. Se poate extinde aici programul după preferinţă cu posibilitatea de a introduce utilizatorul o valoare la cerere. Comment [BIOFIZ5]: Variabila care deţine „soluţia optimă” spre care algoritmul genetic trebuie să se îndrepte parcurgând un număr variabil de generaţii. Şi aceasta poate fi la nevoie introdusă de către utilizator la cerere. Ea este însă restricţionată de utilizarea unui alfabet limitat doar la caracterele minuscule de la a la z, vezi variabila @alfabet. Comment [BIOFIZ6]: Conţine elementele populaţiei la un moment dat. Ea este iniţializată în mod aleatoriu cu ajutorul unei subrutine dedicate. Comment [BIOFIZ7]: Subrutina performanţă evaluează şi selectează cele mai bune rezultate care se apropie de $solutiaOptima. Detalii pentru funcţiile abs, ord, substr, vezi link-uri utile (documentaţia Perl). Funcţia ord are rolul de a extrage codul numeric corespunzător caracterului din şir, fiecare caracter de la tastatură are un cod numeric asignat printr-un standard. Funcţia substr extrage un subşir dintr-un şir de caractere existent într-o variabilă, se comportă oarecum similar cu splice asupra conţinutului tablourilor.

sub performanta { my $sir = shift; my $distanta = 0; my $i; for ($i = 0; $i < ($lungimeSir -1); $i++) { $distanta += abs( ord( substr( $sir, $i, 1)) - ord( substr( $solutiaOptima, $i, 1))); } return $distanta; } sub afisarePopulatie { foreach (@populatie) { # print "$_\n"; print "$_->{_str} : $_->{_performanta} \n"; } } sub mutatie { my $cromozom = shift; my $mutatiePunctiforma = rand( length( $cromozom->{_str})); my $caracter_rand = $alfabet[( rand( @alfabet))]; substr( $cromozom->{_secventa}, $mutatiePunctiforma, 1, $caracter_rand); } sub initializarePopulatie { for ( 1..$dimensiunePopulatie ) { my $cromozom = { _sir => '', _valoare => 0 }; for ( 1..$lungimeSir ) { $cromozom->{_str} .= $alfabet[( rand( @alfabet))]; } $cromozom->{_performanta} = performanta( $cromozom->{_str} ); push @populatie, $cromozom; } } initializarePopulatie(); afisarePopulatie(); @populatie = sort { $a->{_performanta} <=> $b->{_performanta} } @populatie; for ( 1..$generatii ) { my $cromozom = $populatie[ rand( @populatie)]; my $clone ={}; do { $clone = { _str => $cromozom->{_str}, _performanta => 0 }; mutatie( $clone ); $clone->{_performanta} = performanta( $clone->{_str} );

cât de departe este rezultatul obţinut faţă de . b). Funcţia rand aplicată pe array-ul alfabet va extrage la întâmplare un index (de ex. @populatie = sort { $a->{_performanta} <=> $b->{_performanta} } @populatie. astfel încât variabila locală my $sir preia prin funcţia shift valoarea şirului din @_. Aceasta conţine două structuri for condiţionate de $dimensiunePopulatie (o variabilă scalară fixă. prin intermediul variabilei $cromozom. locaţie ce conţine şirul care va forma un cromozom al populaţiei. printr-o expresie simplă._str. „distanţa” faţă de soluţia ideală de evoluţie. Deoarece această referinţă este apelată prin intermediul unui scalar. dacă $dimensiunePopulatie ar fi 2 atunci s-ar executa un ciclu for cu două iteraţii. 2). $cromozom -> . Vezi fişierul references. La fiecare iteraţie se mai adaugă câte o literă din alfabet._performanta. $cromozom. În cazul de faţă este creat un hash cu două componente care se iniţializează cu tipul de dată corespunzător. Cu alte cuvinte.=) câte un caracter obţinut în mod aleatoriu din expresia din partea dreaptă. last if $populatie[0]->{_performanta} == 0. Litera b se concatenează în locaţia de memorie care este referită prin asociaţia dintre numele $cromozom şi hash-ul cu structura indicată. hash. Accesul la aceste locaţii de memorie se face însă.se va concatena (._str. Scopul subrutinei performanta este să stabilească. Referinţa presupune o legătură la un array.pdf pentru detalii. După acest for imbricat se continuă instrucţiunile din for-ul anterior unde locaţia de memorie stabilită pentru valoare $cromozom->.} until ( $clone->{_performanta} > $cromozom->{_performanta}). Ex. acesta nefiind iniţializat (pentru început este un şir gol ’ ’) şi al doilea tip de dată. Următorul for are atâtea iteraţii cât de mare este $lungimeSir. În timpul execuţiei are loc următorul algoritm. se creează un hash anonim cu structură determinată (prima componentă fiind un şir de caractere. o valoare numerică care momentan e zero. sau la o subrutină prin asocierea unei variabile scalare (cazul de faţă $cromozom) cu un obiect (zona de memorie unde este stocată instanţa) de tipul datei (array sau hash). @populatie fiind o variabilă array este formată din mai mulţi cromozomi. În prima iteraţie s-ar iniţializa două locaţii de memorie _sir şi _valoare de tipurile indicate care aparţin unui hash ca structură de date. Dacă reducem şirul atribuit variabilei $solutieOptima la „actg” atunci $lungimeSir se iniţializează prin aplicarea funcţiei length cu valoarea 4.reprezintă accesarea locaţiei de memorie (construită anterior prin referinţă). ambele componente fiind identificare cu _ înaintea numelui). cu alte cuvinte. Variabila locală (privată) my $cromozom este o referinţă la un hash fără nume (anonim). Când se termină for-ul se încheie sinteza cromozomului din componentele impuse de alfabet._str-. La această locaţie desemnată $cromozom -> . Deci. } FUNCŢIONALITATEA PROGRAMULUI Blocul principal al programului porneşte de la apelarea subrutinei initializarePopulatie. înseamnă că în cadrul subrutinei se iniţializează array-ul @_ cu un singur component. $populatie[$#populatie] = $clone. adică cu şirul obţinut prin concatenarea randomizată. print "Cel mai performant: $populatie[0]->{_str} ==> $populatie[0]>{_performanta} \n". Acest lucru se realizează prin apelarea subrutinei performanta cu parametrul identificat cu ultima valoare existentă în locaţia memoriei referită de $cromozom->. se transferă către subrutina respectivă conţinutul existent la locaţia de memorie referită. deci acest ciclu for se va executa de 4 ori. valoarea ei fiind 100) şi de $lungimeSir (variabilă scalară a cărei dimensiune este obţinută în funcţie de cât de lung este şirul „valoareoptima” spre care trebuie să evolueze sistemul).este încărcată cu un rezultat numeric prin evaluarea performanţei combinaţiei obţinute. situaţie în care apelarea prin scalar $alfabet*2+ înseamnă a doua literă din alfabetul impus (ex.

aceasta urmează a fi afişată pentru a putea vizualiza ce fel de componente are şi ce performanţă are fiecare component. Din acest punct de vedere mutaţia este aleatorie folosind strict materialul disponibil în @alfabet. Deoarece adresele hexazecimale ale memoriei alocate de program pentru stocarea datelor sunt dificil de utilizat. la rândul lui. Mutaţia se efectuează pe număr de generaţii. din două componente (şirul de caractere care compune un cromozom şi valoarea numerică a performanţei acestuia) înseamnă că array-ul respectiv se va completa cu un număr de cromozomi. evoluţia se încheie deoarece şirul cromozomului comparat este identic cu valoarea optimă. Afişarea componentelor populaţiei se realizează prin parcurgerea @populatie cu ajutorul funcţiei foreach care ia fiecare cromozom din populaţie şi afişează prin intermediul print şirul din care e compus cromozomul şi performanţa acestuia. în care este utilizată ca referinţă către o locaţie de memorie. În final. Mecanismul de mutaţie este repetat în cadrul unei iteraţii do – until. ._str-. deci în urma aplicării funcţiei sort pe componentele @populatie. $clone ca parametru al subrutinei este tot un pointer pentru că prin intermediul lui se transferă către variabila locală $cromozom prin aplicarea lui shift pe array-ul @_ cele două componente ale cromozomului din populaţie. în al doilea caz valoarea performanţei corespunzătoare şir-ului. Index [0] Cromozom_1 Şir_1 Perfromanţă_1 Index [1] Cromozom_2 Şir_2 Perfromanţă_2 Index [2] Cromozom_3 Şir_3 Perfromanţă_3 Index [3] Cromozom_4 Şir_4 Perfromanţă_4 Index [n] Cromozom_n Şir_n Perfromanţă_n Odată populaţia iniţializată. Întrucât în spatele $cromozom se află un hash anonim.valoarea ideală. Este evident că dacă se atinge valoarea zero. Conţinutul locaţiilor de memorie odată alterat este disponibil în continuare doar în această variantă modificată. aceasta va conţine aceleaşi componente doar că ordinea lor este ascendentă în funcţie de valoarea performanţei. adaugă în array-ul populaţie o componentă de tip cromozom cu ajutorul funcţiei push. şirul şi performanţa. în primul caz şirul. compus. Printr-o simplă asociere cu o variabilă scalară se creează un pointer realizându-se o legătură între adresa de memorie şi conţinutul acesteia. Întrucât subrutina se bazează pe pointeri nu mai este necesară utilizarea instrucţiunii return. Dacă nu ar exista aşa ceva populaţia nu ar mai putea evolua şi s-ar opri la soluţiile aleatoare produse la iniţializare. În cadrul acestui mecanism se extrage aleatoriu un cromozom din @populatie şi se iniţializează o variabilă locală $clone care va prelua conţinutul cromozomului transferând-ul subrutinei mutatie. Din acest punct de vedere utilizarea pointer-ilor simplifică modul de exploatare a datelor şi a resurselor într-un program. populaţia astfel obţinută este sortată (vezi funcţia sort). În variabila $distanta se adună diferenţele în valoare absolută dintre codurile ASCII (vezi link-uri utile) ale caracterelor întâlnite în şirul din cromozom şi în şirul ideal spre care se tinde. Evaluarea condiţiei Comment [BIOFIZ8]: În programare. deci se obţine un array bidimensional conform schemei de mai jos. programul introduce şi un operator de mutaţie în populaţie. Scopul algoritmului este de a-l selecta pe cel cu valoarea cea mai mică. fiecare cromozom fiind de fapt o componentă bidimensională. ea va determina print-area conţinutului acelei locaţii. aceasta fiind asemănătoare cu while numai că este cu post-condiţie. Variabila $mutatiePunctiforma se iniţializează cu o valoare aleatoare între 0 şi lungimea şirului de caractere al cromozomului. tehnica pointer este folosită ca o soluţie elegantă pentru accesul direct la conţinutul locaţiilor de memorie fără ca programatorul să fie nevoit să ţină evidenţa acestora. această tehnică este cunoscută sub denumirea generică de exploatarea memoriei prin intermediul pointer-ilor. numai că în situaţia de faţă. Dacă eliminaţi simbolul # care comentează instrucţiunea print "$_\n". elimină nevoia de a utiliza foarte multe variabile pentru transferul datelor între subrutine. Mare atenţie la acest aspect. deci este condiţionată de un ciclu for a cărui număr de iteraţii este limitat de valoarea introdusă la $generatii. accesând locaţiile de memorie stabilite prin referinţele create anterior. Variabila internă $_ este exploatată similar ca şi array-ul _. Această valoare numerică aleatorie este folosită ulterior pentru a modifica un singur caracter prin apelarea substr pe şirul de caractere de la locaţia point-ată de $cromozom->. iar subrutina initializarePopulatie care încă nu s-a încheiat. După generarea aleatoare şi afişarea rezultatelor. Acest şir va fi alterat cu un caracter ales aleatoriu din @alfabet folosind expresia $alfabet[( rand( @alfabet))]. veţi observa afişată valoarea adresei de memorie la care se face referire. După execuţia performanţei se returnează valoarea obţinută prin diferenţă ca rezultat reprezentativ al acesteia.

TEMĂ a.se face după ce se trece obligatoriu măcar o dată prin ciclu.html http://ro. b. adică s-a atins valoarea ideală.comptechdoc. să se îmbunătăţească programul prin introducerea unei subrutine care să simuleze recombinarea prin crossing over.perl. Programul se încheie forţat (last) atunci când performanţă este zero. $populatie*$#populatie+ = $clone.edu/~moreland/courses/IntroPerl/docs/manual/pod/perlop. Ca şi condiţie de oprire este luată chiar valoarea performanţei. $solutiaOptima şi @alfabet.perl. în căsuţa Reference sunt reunite principalele capitole care descriu cu exemple detaliate toate funcţiile (în ordine alfabetică). În stânga. LINK-URI UTILE http://perldoc.wikipedia.sdsc.org/ http://www. Comment [BIOFIZ10]: Descriere amănunţită a operatorilor folosiţi în Perl. instrucţiunile şi operatorii limbajului. . Întrucât populaţia a fost deja sortată ascendent.html http://www.org/independent/web/cgi/perlmanual/index. practic mutaţia nu face altceva decât să modifice această valoare. $dimensiunePopulatie. Să se adapteze programul astfel încât utilizatorul să poată controla prin introducerea unor valori de la tastatură pentru $generatii. ultimul cromozom (cel mai îndepărtat de soluţia ideală) este înlocuit cu cel obţinut prin mutaţie. Întrucât algoritmul indicat este departe de un model real în ceea ce priveşte evoluţia populaţiei. Se resortează populaţia ascendent şi se afişează primul cromozom care este şi cel mai performant. Comment [BIOFIZ11]: Tutorial Perl cu descrieri concise şi exemple simple.org/wiki/ASCII Comment [BIOFIZ9]: Documentaţia oficială pentru Perl.org/ http://www.

Sign up to vote on this title
UsefulNot useful