Anda di halaman 1dari 45

Editura Sfntul Ierarh Nicolae

ISBN 978-606-577-027-0

Cap.I Consideratii generale


1) Introducere
Algoritmii genetici sunt o parte a calculului evolutiv care, la rndul su, este o parte a
foarte actualului domeniu al inteligenei artificiale.
Dac algoritmii clasici cum sunt greedy, backtracking, divide et impera s.a. fac parte
din categoria algoritmilor deterministi (pentru aceleasi date de intrare dau intotdeauna
aceeasi solutie), algoritmii genetici pot fi caracteriziati ca fiind niste algoritmi
nedeterministi (pentru aceleasi date de intrare ofera solutii diferite). Din acest punct de
vedere ei pot fi usor incadrati in larga categorie a algoritmilor euristici.
Asa cum sugereaz si numele, acesti algoritmi sunt inspirati din legile geneticii,
avand insa ca punct de plecare teoriile lui Charles Darwin despre evolutie. Spus intr-un
mod foarte simplist, solutia unei probleme rezolvate de catre un algoritm genetic
evolueaza.
Ideea calculului evolutiv a fost introdusa pentru prima oara in anul 1960 de catre
I. Rechenberg in lucrarea sa Evolution strategies, ideea lui fiind dezvoltata apoi si de
catre alti cercetatori. Algoritmii genetici au fost inventati de catre John Holland. Acesta
si-a expus ideile in cartea sa Adaptation in Natural and Artificial Systems, publicata in
1975. In 1992 John Koza a folosit algoritmii genetici pentru a dezvolta programe
destinate anumitor scopuri precise. Metoda folosita de el a primit denumirea de
programare genetica.
In practica se intalnesc nenumarate probleme cu un volum foarte mare de date de
intrare cum ar fi: probleme de recunoastere (a vocii, a scrisului etc.), de coordonare a
miscarii robotilor, de previziune statistica in meteorologie etc. De cele mai multe ori
mintea umana abordeaza cu succes astfel de probleme, dar gasirea unor algoritmi
adecvati pentru rezolvarea lor, astfel incat ele sa poata fi abordate eficient (din punctul de
vedere al timpului de executie si al necesarului de memorie) cu ajutorul calculatorului,
este destul de dificil de realizat. Se pune problema, cum pot entitatile naturale (nu
neaparat inteligente) sa rezolve fara un efort prea mare aceste probleme combinatoriale
masive, un exemplu elocvent in acest sens fiind cel al albinei care poate sa urmeze rute
complexe, rute care chiar si pentru un supercalculator reprezinta o piatra de temelie in
incercarea de a le gasi in timp real. De aceea, pentru astfel de probleme, s-a incercat
imitarea inteligentei gasita in natura prin incercarea de a simula soft lumea reala,
copiind caile de rezolvare pe care le-a gasit natura pentru solutionarea lor, si de a prelucra
datele de o maniera cat mai naturala.
2) Notiuni de genetica
Toate organismele vii sunt alcatuite din celule. In nucleul fiecarei celule exista
aceeasi multime de cromozomi. Cromozomii sunt siruri de A.D.N. (acid
dezoxiribonucleic) si servesc ca model pentru intregul organism, A.D.N.-ul codificand
sub forma chimica informatiile genetice pentru toate caracterele ereditare. Un cromozom
este alcatuit din gene, fiecare gena codificand o caracteristica (spre exemplu culoarea
ochilor). Ea are o pozitie fixa in cromozom.

In timpul reproducerii apar doua tipuri de transformari: incrucisarea (engl.


crossover) si mutatia. Mult simplificat, procesul de incrucisare ar arata astfel: doi
cromozomi se lovesc unul de celalalt, isi schimba intre ei fragmente de informatii
genetice (printr-o recombinare a genelor, insa fara a-si schimba numarul de gene) si apoi
se indeparteaza. Mutatia reprezinta modificarea unor elemente dintr-un cromozom (apar
elemente care nu apartin nici unuia dintre cei doi cromozomi parinti), si apare de obicei
din cauza erorii copierii de gene de la parinti.
O specie se caracterizeaz prin: numrul de gene, ordinea genelor n cromozom i
semnificaia fiecrei gene.
Populaia este o mulime de n indivizi (in cazul algoritmilor genetici notiunea de
individ se va confunda cu cea de cromozom), din aceeai specie, care ocup un teritoriu
cu resurse limitate i care se reproduc ntre ei. Orice populatie evolueaza in timp prin
aplicarea transformarilor mai sus amintite.
Mediul cuprinde, in afara de teritoriu si resurse, totalitatea regulilor prin care se creeaz relaiile de concuren ntre indivizii din populaie.
Speranta de viata (engl. fitness) este o valoare care se ataseaza fiecarui individ
(cromozom), si care reprezinta in esenta valoarea adaptabilitatii la regulile impuse de
mediu.
3) Algoritmul general (schema logica si pseudocod)
Asa cum am mai spus, solutia pentru o problema rezolvata prin algoritmi genetici
evolueaza. Algoritmul incepe cu o multime de solutii (solutieindividcromozom),
numita populatie. Aceasta populatie (denumita uneori si generatia 0) se genereaza aleator.
Solutiile dintr-o populatie existenta la un anumit moment dat sunt luate si folosite
pentru a forma o noua populatie pe baza unor anumite criterii. Acest lucru este motivat de
faptul ca noua populatie are sanse foarte mari sa fie mai buna decat cea veche. Formarea
noii populatii are loc prin procesele de incrucisare (crossover) si mutatie. Cu cat speranta
de viata (fitness) a unui cromozom este mai mare, cu atat sansa ca acesta sa
supravietuiasca in populatia urmatoare este mai mare. Totodata, la incrucisare, sansa unui
cromozom de a fi ales pentru acest proces este direct proportionala cu fitness-ul lui.
Algoritmul de mai sus este repetat pana cand o conditie este satisfacuta. Conditia este
de obicei repetarea de un numar de ori al ciclului, imbunatatirea solutiei cu un anumit
grad etc.
In figura de mai jos se reprezinta schema logica care ilustreaza, la modul general,
mecanismele descrise mai sus.

P0

Pt

t = t+1

(populaia iniial)

Pt+1
Pt

inserie
P''t (urmai)

evaluare

reproducere
P't (prini)

Pt

selecie
criteriu
de oprire

nu

Pt

da
rezultat
In blocul de instructiuni evaluare se calculeaza fitness-ul fiecarui cromozom,
aceste valori ajutand mai apoi la selectia (blocul selectie) cromozomilor ce se vor
incrucisa, formand astfel noua populatie.
Blocul reproducere efectueaza cele 2 procese de crossover si mutatie.
In secventa insertie are loc inlocuirea populatiei vechi cu populatia noua obtinuta
prin reproduceri succesive.
Algoritmul se repeta pana cand se indeplineste criteriul de oprire, care reprezinta de
obicei creerea unui anumit numar de populatii (lucru contorizat de variabila t).
Sageata care duce la insertie fara a trece prin selectie si reproducere reprezinta
fenomenul de elitism, fenomen ce va fi explicat in capitolul urmator.
In continuare vom prezenta codificarea in pseudocod a modului de functionare a unui
algoritm genetic.
// incepe cu un timp initial(se porneste de la generatia 0)
t := 0;
// initializeaza o populatie aleatoare de n cromozomi (care
reprezinta solutii posibile pentru problema)
InitializeazaPopulatie();
// calculeaza fitness-ul fiecarui cromozom din populatia
initiala
CalculFitness ();
// testeaza pentru criteriul de oprire;

// cat timp nu este indeplinit criteriul de oprire creeaza o


populatie noua pe baza celei vechi;
// incrementeaza contorul de timp;
t := t + 1;
// selecteaza doi parinti cromozomi pe baza unor
prestabilite;
SelectieParinti();
// recombina genele cromozomilor selectati
prin crossover;
Crossover();
// modifica noii cromozomi formati prin mutatie;
Mutatie();
// insereaza cromozomii noi formati in populatia noua;
Insert();
// populatia noua o inlocuieste pe cea veche;
// daca se indeplineste conditia de final afiseaza solutia;
//sfarsit algoritm.

reguli

Cap.II Codificarea informaiilor


Precum se poate observa, algoritmul de mai sus (ilustrat prin schema logica si
pseudocod) este foarte general. Pentru fiecare problema particulara in parte trebuie sa se
raspunda la urmatoarele intrebari:
1. Cum codificam cromozomii, si cum este reprezentata o solutie in cromozom?
2. Cum initializam cromozomii? Care este populatia initiala? Cat de mare este ea?
3. Cine este fitness-ul unui cromozom?
4. Cum selectam parintii pentru incrucisare?
5. Cum se face incrucisarea?
6. Cum se face mutatia?
7. Daca noua populatie este formata doar prin crossover, atunci este posibil sa se piarda
cele mai bune solutii din actuala populatie. Cum evitam acest lucru?
8. Cat de des apare mutatia? Daca apare prea des, ar insemna ca facem o cautare
aleatoare in spatiul solutiilor. Daca nu apare nici o data, atunci s-ar putea sa cadem
intr-un extrem local.
9. Cand se termina algoritmul?
La toate aceste intrebari si la multe altele care se leaga de ele vom raspunde in
paragrafele care urmeaza.
1) Codificarea cromozomilor
Exista multe variante de codificare, totul depinzand de problema pe care dorim sa o
rezolvam. Trebuie avuta mare grija in aceasta etapa deoarece o codificare buna poate
duce la o implementare usoara a operatiilor de incrucisare si mutatie. Exemple:
a) Codificarea binara
Fiecare cromozom este reprezentat ca un sir de 1 si 0:
Cromozomul A
1001100101011101011010011
Cromozomul B
1111110011000000110101101
Problema clasica care foloseste acest tip de codare este problema discreta a
rucsacului. Aici fiecare obiect este codificat cu 1 daca este in rucsac si cu 0 daca nu este
in rucsac.
b) Codificarea sub forma de permutare
Fiecare cromozom este reprezentat sub forma unei permutari:
Cromozomul A
153264798
Cromozomul B
856723149
Aceasta codificare se face in cazul problemei comis voiajorului. O permutare
reprezinta ordinea in care comis-voiajorul viziteaza orasele.
c) Codificarea sub forma de valori
Fiecare cromzom este un sir de valori:
Cromozomul A
1.234 5.3243 0.4456 2.3293 2.4545
Cromozomul B
ABDJEIFJDHDHDGKDFLDPIRW
Cromozomul C
(back),(back),(right),(forward),(left)

2) Populatia initiala
Populatia este o multime de cromozomi care trebuie sa fie suficient de mare pentru a
se putea realiza un numar multumitor de combinatii intre cromozomii componenti, dar nu
prea mare, deoarece acest lucru va incetini algoritmul.
Populatia initiala este una aleatoare. Spre exemplu, pentru determinarea unui drum
hamiltonian de cost minim, se poate incepe cu generarea aleatoare a catorva permutari.
O parte din populatia initiala poate fi generata prin metode euristice, spre exemplu in
cazul problemei comis-voiajorului se pot genera cativa cromozomi prin metoda greedy.
In acest fel se poate ajunge la o solutie mai rapid.
3) Fitness-ul unui cromozom
Fitness-ul unui cromozom este speranta lui de viata. Pe noi ne intereseaza sa obtinem
cromozomi cu fitness-ul cat mai mare, sau cat mai mic, in orice caz, cu un fitness la o
extrema.
In cazul problemei comis-voiajorului, fitness-ul este costul drumului codificat de
respectivul cromozom. Aici ne intereseaza sa obtinem un cromozom cu un fitness cat mai
mic.
In cele ce urmeaza presupunem, pentru simplitate, ca dorim sa obtinem un
cromozom cu fitness-ul cat mai mare.
4) Selectarea cromozomilor parinti
Cromozomii sunt selectati din populatie pentru a fi parinti intr-o incrucisare
(crossover). Conform teoriei lui Darwin, cei mai buni parinti supravietuiesc si se
inmultesc creand noi urmasi.
Dintre metodele cele mai des intalnite de selectie amintim:
a) Selectia aleatoare
Doi parinti sunt alesi aleator din populatie. Acest lucru se face generand un numar
aleator intre 1 si dimensiunea populatiei.
b) Selectarea cu ajutorul ruletei ponderate
Parintii sunt selectati in conformitate cu fitness-ul lor. Cu cat sunt mai buni, cu atat
sansa lor de a fi alesi este mai mare. Imaginati-va o ruleta in care sunt dispusi toti
cromozomii din populatie. Fiecarui cromozom ii corespunde un sector al ruletei, direct
proportional, ca marime, cu fitness-ul cromozomului respectiv. In acest fel cromozomii
cu fitness mai mare au atasate sectoare mai mari iar cei cu fitness mic au atasate sectoare
mai mici. La aruncarea bilei pe ruleta exista mai multe sanse de alegere pentru
cromozomii cu fitness mare. Figura de mai jos reprezinta un exemplu de ruleta ponderata
pentru opt cromozomi.

8
6 7
5
4
3

1
2

Simularea rotii de ruleta se face in felul urmator:


se calculeaza suma tuturor fitness-urilor cromozomilor. Fie aceasta S;
se genereaza un numar aleator intre 1 si S. fie acesta r;
se parcurge populatia ordonata crescator dupa fitness si se calculeaza suma
fitness-urilor cromozomilor parcursi pana depasim valoarea r. Se alege
cromozomul la care s-a ajuns.
c) Selectia aranjata
Selectia anterioara nu este recomandata in cazul in care fitness-ul cromozomilor
difera foarte mult. De exemplu daca unul din cromozomi are fitness-ul de 100 de ori mai
mare decat suma fitness-urilor celorlalti cromozomi, sansele cromozomilor cu fitness mic
sunt aproape nule. Pentru a evita aceasta situatie, se ordoneaza cromozomii crescator
dupa fitness, apoi se renumeroteaza cu numere intregi din intervalul [1,..,dimensiunea
populatiei]. Cromozomul cu fitness-ul cel mai mic are numarul 1 etc., iar cromozomul cu
fitness-ul cel mai mare are numarul egal cu dimensiunea populatiei. Aceste numere se
considera fitness-uri si pe ele se aplica selectia rotii de ruleta. Observam ca cromozomii
cu fitness-ul cel mai mare au tot cele mai mari sanse de a fi alesi, dar de data aceasta nu
mai sunt asa mari in comparatie cu sansele celorlalti.
d) Selectia cu starea consolidata
Aceasta nu este o metoda particulara. Ideea esentiala este urmatoarea: cromozomii
nou generati vor inlocui, daca sunt mai buni, o parte din cromozomii vechii populatii. In
acest fel nu mai este necesar sa se lucreze cu mai multe populatii deodata, ci doar cu una
singura.
e) Elitism
Cand cream o noua populatie prin incrucisare si mutatii, exista o sansa mare de a
pierde cei mai buni cromozomi. De aceea se recomanda copierea celor mai buni
cromozomi in noua populatie, fara a-i schimba. Acest proces se numeste elitism si el
creste rapid performantele algoritmilor genetici.
5) Incrucisarea (crossover)
Incrucisarea depinde de tipul de codificare a cromozomilor si de problema
particulara pe care o rezolvam. Iata cateva exemple:

Codificarea binara
a) Incrucisare intr-un singur punct
Un punct de incrucisare este ales. Din primul parinte este copiata secventa de la
inceput pana la punctul de incrucisare, iar din al doilea parinte este copiata secventa de la
punctul de incrucisare pana la final.
11001011+110111111 110011111
b) Incrucisarea in doua puncte
Sunt alese doua puncte de incrucisare. Secventa dintre cele doua puncte este aleasa
dintr-unul din parinti, iar ce a ramas din celalalt parinte:
11001001+1101111111011101
c) Incrucisarea uniforma
Bitii sunt copiati aleator din primul si al doilea parinte.
11001011+1101110111011111

d) Incrucisarea aritmetica
Operatii aritmetice sunt efectuate pentru a crea noi urmasi.
11001011+1101111111001001 (AND)
Codificarea sub forma de permutare
Metodele folosite aici sunt asemanatoare cu cele de la codificarea binara. Trebuie
avuta grija sa se pastreze consistenta permutarii, adica sa nu apara un numar de doua ori,
iar altele nici o data.
(1 2 3 4 5 6 7 8 9)+(4 5 3 6 8 9 7 2 1)(1 2 3 4 5 6 8 9 7)
Codificarea sub forma de valoare
Metodele de la codificarea binara pot fi folosite si aici.
Observatii
Probabilitatea de incrucisare indica cat de des trebuie sa apara aceasta operatie intr-o
populatie. De obicei are valoare mare, cuprinsa intre 60% si 90%.
Metodele de incrucisare prezentate mai sus sunt destul de generale. De obicei sunt
alese metode specifice problemei care se doreste a fi rezolvata si care genereaza o
populatie mai buna. Spre exemplu in cazul problemei generarii unei componente intern
stabile maximale (submultime de noduri a unui graf neorientat cu un numar maxim de
elemente cu proprietatea ca oricare doua noduri din submultime nu sunt legate printr-o
muchie) se aleg doi cromozomi care codifica doua componente intern stabile. Aceste
componente sunt de fapt multimi, reprezentate ca un sir de biti. In acest caz se poate
folosi, de exemplu incrucisarea intr-un singur punct, sau cu doua puncte, dar s-ar putea ca

in acest caz componentele noi formate sa fie mai mici decat parintii lor. De aceea putem
lua unul din parinti si ii vom adauga noduri din al doilea parinte, rezultand in acest fel un
cromozom cu fitness-ul mai mare sau egal decat cel al parintilor. Este repetata aceeasi
operatie si pentru celalalt parinte. Acest tip de incrucisare se numeste incrucisare
inteligenta.
6) Mutatia
Mutatia apare in principal pentru a evita caderea solutiilor intr-un optim local.
Efectuarea de prea multe ori a acestei operatii va transforma algoritmul intr-o cautare
aleatoare in spatiul solutiilor posibile. De aceea probabilitatea de mutatie trebuie sa fie
mica, circa 0,5-1%.
Codificarea binara
a) Inversarea bitilor
Bitii selectati sunt inversati 10.
1100110001
b) Interschimbarea bitilor
Se aleg doi biti si se schimba valorile intre ei.
1100100110001101
Codificarea sub forma de permutare
a) Transpozitie
Este efectuata o transpozitie asupra permutarii respective. Acest lucru este echivalent
cu interschimbarea bitilor de la codificarea binara.
(1 2 3 4 5 6 8 9 7)(1 8 3 4 5 6 2 9 7)
Codificarea sub forma de valori
Un numar mic este adaugat sau scazut din valorile selectate.
(1.29 5.68 2.86 4.11 5.55)(1.29 5.68 2.73 4.22 5.55)
7) Cand se termina algoritmul?
Algoritmii genetici se termina, de obicei, dupa ce s-a creat un numar predefinit de
populatii noi. In alte cazuri se poate determina daca solutia s-a imbunatatit de la o
populatie la alta. In caz afirmativ se continua cu creerea de noi populatii, in caz negativ se
afiseaza cel mai bun cromozom din populatia curenta.

Cap.III Implementarea algoritmului


Dupa cele doua capitole pregatitoare suntem in masura de a da o forma generala a
programului ce rezolva o problema prin algoritmi genetici.
Vom incepe prin a da forma generala a programului principal, forma care sufera
foarte mici modificari de la o problema la alta.
Comentariile la nivel de cod sursa sunt suficiente pentru a intelege mecanismul
algoritmului.
void main()
{
int i;
Citire();
/*citeste datele de intrare ale problemei*/
randomize();
InitializeazaPopulatie();
/*genereaza populatia initiala*/
OrdoneazaDupaFitness();
/*ordoneaza cromozomii descrescator(crescator) dupa fitness*/
for(i=1;i<=MaxIteratii;i++)
/*i>MaxIteratii este conditia de iesire din program, i fiind
echivalentul lui t din pseudocod*/
{
NrPop=0;
/*variabila globala care contorizeaza numarul de cromozomi adaugati
la noua populatie*/
Elitism(MaxElitism);
/*copie cei mai buni MaxElitism cromozomi din populatia veche in cea
noua*/
for(co=1;co<=MaxCrossover;co++)
/*MaxCrossover este o constanta ce arata numarul de incrucisari care
au loc intr-o generatie*/
{
SelectieParinti(r1,r2);
/*selecteaza 2 cromozomi care se vor incrucisa*/
Crossover(PopulatieVeche[r1],PopulatieVeche[r2],
PopulatieNoua[++NrPop],PopulatieNoua[++NrPop]);
/*se incruciseaza cei doi cromozomi*/
}
MutaRestul(MaxPopulatie-NrPop);
/*completeaza populatia noua cu cromozomi din populatia veche pana
cand ele au acelasi numar de cromozomi(=MaxPopulatie)*/
for(co=1;co<=MaxPopulatie;co++)
PopulatieVeche[co]=PopulatieNoua[co];
/*inlocuieste populatia veche cu cea noua*/
for(co=1;co<=MaxMutatie;co++)
Mutatie(PopulatieNoua[1+random(MaxPopulatie)]);
/*modifica prin mutatie un numar de MaxMutatie cromozomi*/
CalculFitness();
/*calculeaza fitness-ul fiecarui cromozom din populatia noua*/
OrdoneazaDupaFitness();
}
Afiseaza();

10

/*afiseaza cea mai buna solutie gasita*/


}

Algoritmii genetici au o structura foarte generala, care permite folosirea lor pentru
majoritatea problemelor. Ceea ce se schimba sunt urmatoarele:
- codificarea cromozomilor;
- initializarea populatiei;
- incrucisarea;
- mutatia.
Vom da in continuare sursa completa ce implementeaza forma generala a
programului. Pentru o mai buna lizibilitate a codului, toate functiile care se folosesc in
acest program au fost mai intai declarate prin antetul lor inaintea programului principal, si
dezvoltate apoi dupa acesta. Inca o data, comentariile la nivelul codului sursa sunt
suficiente pentru a intelege ce se intampla.
#include<iostream.h>
#include<conio.h>
#include<stdlib.h>
/*aici isi au prototipul functiile randomize() si random(int)*/
const MaxPopulatie=100,
/*numarul de cromozomi in populatie*/
MaxIteratii=100,
/*numarul de iteratii ale algoritmului*/
MaxElitism=2,
/*cati cromozomi sunt copiati prin elitism*/
MaxCrossover=37,
/*de cate ori apare incrucisarea intr-o populatie*/
MaxMutatie=1;
/*de cate ori apare mutatia intr-o populatie*/
typedef struct{
int gena[22];
/*depinde de la problema la problema*/
float fitness;
}TCromozom;
/*tipul care codifica un cromozom*/
typedef TCromozom TPopulatie[MaxPopulatie+1];
/*tipul care codifica o populatie*/
TCromozom CelMaiBunCromozom;
/*cea mai buna solutie in orice moment*/
TPopulatie PopulatieVeche, PopulatieNoua;
/*se lucreaza cu doua populatii*/
int i,j,k,contor,co,r1,r2;
int NrPop;
void citire();
void CalculFitness();
void InitializeazaPopulatie();
void Elitism(int NrMutati);
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2);
void Mutatie(TCromozom &mutant);
void MutaRestul(int NrMutati);
void OrdoneazaDupaFitness();
void SelectieParinti(int &r1, int &r2);

11

void Afiseaza();
void main()
{
/*programul principal dat mai sus*/
}
void citire()
{
/*se citesc datele de intrare*/
}
void InitializeazaPopulatie()
{
int i,j;
for(i=1;i<=MaxPopulatie;i++)
/*sunt initializati cromozomii din populatie*/
CalculFitness();
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];
}
void CalculFitness()
{
/*fitness-ul poate fi calculat si in momentul in care se creeaza
populatia*/
}
void Elitism(int NrMutati)
{
int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=PopulatieVeche[MaxPopulatie-i+1];
}
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2)
{
/*incrucisarea cromozomilor parinti g1 si g2 din care rezulta doi
cromozomi fii rez1 si rez2*/
}
void Mutatie(TCromozom &mutant)
{
/*se efectueaza o mutatie asupra unui cromozom ales aleator si se
returneaza cromozomul mutant*/
}
void MutaRestul(int NrMutati)
{
/*dupa ce s-a efectuat incrucisarea, mutatia si elitismul mai raman
NrMutati pozitii libere in noua populatie care vor fi completate cu
cromozomi alesi aleator din vechea populatie*/
int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=
PopulatieVeche[1+random(MaxPopulatie-MaxElitism)];
}
void OrdoneazaDupaFitness()
{
/*ordonarea cromozomilor dintr-o populatie crescator dupa fitness prin
metoda bulelor*/
int i,sw=1;
TCromozom aux;
while (sw)
{

12

sw=0;
for(i=1;i<=MaxPopulatie-1;i++)
if (PopulatieVeche[i].fitness>PopulatieVeche[i+1].fitness)
{
aux=PopulatieVeche[i];
PopulatieVeche[i]=PopulatieVeche[i+1];
PopulatieVeche[i+1]=aux;
sw=1;
}
}
/*cautarea unei solutii mai bune si retinerea ei*/
if (PopulatieVeche[MaxPopulatie].fitness>
CelMaiBunCromozom.fitness)
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];
}
void SelectieParinti(int &r1, int &r2)
{
/*selecteaza doi parinti din populatie folosind una din metodele
descrise in capitolul II*/
}
void Afiseaza()
{
/*se va afisa CelMaiBunCromozom*/
}

Sa vedem cum implementam selectia. Metodele cele mai importante pe care le-am
prezentat mai sus sunt:
1. Selectia uniforma
void SelectieParintiUniforma(int &r1, int &r2)
{
r1=1+random(MaxPopulatie);
r2=1+random(MaxPopulatie);
}

2. Selectia folosind ruleta ponderata


void SelectieParintiAranjata(int &r1, int &r2)
{
int s=0,r;
for(i=1;i<=MaxPopulatie;i++)
s+=PopulatieVeche[i].fitness;
/*pentru primul parinte*/
r=1+random(s);
r1=0;
while (r>0)
{
r1++;
r-=PopulatieVeche[r1].fitness;
}
/*pentru al doilea parinte*/
r=1+random(s);
r2=0;
while (r>0)
{
r2++;

13

r-=PopulatieVeche[r2].fitness;
}
}

3. Selectia aranjata
void SelectieParintiAranjata(int &r1, int &r2)
{
int s,r;
s=MaxPopulatie*(MaxPopulatie+1)/2;
/*pentru primul parinte*/
r=1+random(s);
r1=0;
while (r>0)
{
r1++;
r-=r1;
}
/*pentru al doilea parinte*/
r=1+random(s);
r2=0;
while (r>0)
{
r2++;
r-=r2;
}
}

14

Cap.IV Aplicatii
1. Ecuatie diofanica
Se citeste de la tastatura un numar natural nenul n si coeficientii naturali a1,a2,,an si
b ai ecuatiei diofanice a1x1+a2x2++anxn=b. Se cere sa se gaseasca o solutie naturala a
ecuatiei date.
Analiza problemei
0) Datele de intrare
n int;
a vector de tip int;
b int.
1)

Codificarea cromozomilor
Vom folosi codificarea sub forma de valoare. Fiecare cromozom va contine n biti,
un bit oarecare i reprezentand valoarea lui xi.
Codificarea tipului cromozom este urmatoarea:
typedef struct{
int gena[15];
float fitness;
}TCromozom;

2)

Populatia initiala
Generarea populatiei initiale are o importanta foarte mare in cadrul acestui algoritm,
de aceea alegerea multimii din care se vor genera aleator cromozomii populatiei initiale
trebuie sa fie facuta cu atentie. In cadrul problemei de fata observam ca exista sanse mari
ca o solutie x1,x 2,,xn sa fie formata din numere ce se afla in apropierea valorii
b/(a1+a2++an). Pentru ca populatia initiala sa contina cromozomi formati din numere ce
se afla in jurul acestei valori, solutiile vor fi generate aleator din multimea
{0,1,,[2*b/(a1+a2++an)]}. Secventa care codifica generarea populatiei initiale este:
for(i=1;i<=MaxPopulatie;i++)
for(j=1;j<=MaxGena;j++)
PopulatieVeche[i].gena[j]=random(2*b/InitVar);

unde InitVar reprezinta suma coeficientilor necunoscutelor si se calculeaza la citirea lor.


3)

Fitness
Un cromozom codifica o solutie (x1,x 2,,xn). Fitness-ul lui va fi dat de expresia |Vb|, unde V reprezinta valoarea expresiei a1x1+a2x2++anxn. Evident, cromozomii mai
buni vor fi cei cu fitness-ul mai mic.

15

4)

Selectarea cromozomilor parinti


Cromozomii formati intr-o populatie se ordoneaza descrescator dupa fitness. Metoda
de selectie folosita este cea prin selectie aranjata.
5) Crossover-ul
Incrucisarea se realizeaza intr-un singur punct, acesta fiind ales aleator.
r=1+random(MaxGena-1);
for(i=1;i<=MaxGena;i++)
if (i<=r)
{
rez1.gena[i]=g1.gena[i];
rez2.gena[i]=g2.gena[i];
}
else
{
rez1.gena[i]=g2.gena[i];
rez2.gena[i]=g1.gena[i];
}

6)

Mutatia
Mutatia are o probabilitate de 1% si se realizeaza prin adaugarea sau scaderea unei
valori aleatoare mici la fiecare din genele cromozomului.
{
r1=1+random(b/(10*InitVar));
r2=random(2);
if (r2) mutant.gena[i]+=r1;
else mutant.gena[i]-=r1;
}

Valoarea b/(10*InitVar) a fost determinata in mod euristic.


7)

Cel mai bun cromozom


Se initializeaza in functia InitializeazaPopulatie() si intr-o generatie se calculeaza in
functia OrdoneazaDupaFitness(), dupa ordonare, astfel:
if (PopulatieVeche[MaxPopulatie].fitness<
CelMaiBunCromozom.fitness)
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];

In continuare vom da codul sursa complet care rezolva aceasta problema.


#include<iostream.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
const MaxPopulatie=100,
MaxIteratii=1000,
MaxElitism=3,
MaxCrossover=35,
MaxMutatie=1;

16

typedef struct{
long int gena[22];
float fitness;
}TCromozom;
typedef TCromozom TPopulatie[MaxPopulatie+1];
TCromozom CelMaiBunCromozom;
TPopulatie PopulatieVeche, PopulatieNoua;
int i,j,k,contor,co,r1,r2;
int NrPop, MaxGena;
int InitVar=0;
int a[50];
long int b;
void citire();
void CalculFitness();
void InitializeazaPopulatie();
void Elitism(int NrMutati);
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2);
void Mutatie(TCromozom &mutant);
void MutaRestul(int NrMutati);
void OrdoneazaDupaFitness();
void SelectieParintiAranjata(int &r1, int &r2);
void Afiseaza();
void main()
{
int i,sw=1;
citire();
randomize();
InitializeazaPopulatie();
OrdoneazaDupaFitness();
for(i=1;i<=MaxIteratii&&sw;i++)
{
NrPop=0;
Elitism(MaxElitism);
for(co=1;co<=MaxCrossover;co++)
{
SelectieParintiAranjata(r1,r2);
Crossover(PopulatieVeche[r1],PopulatieVeche[r2],
PopulatieNoua[++NrPop],PopulatieNoua[++NrPop]);
}
MutaRestul(MaxPopulatie-NrPop);
for(co=1;co<=MaxPopulatie;co++)
PopulatieVeche[co]=PopulatieNoua[co];
for(co=1;co<=MaxMutatie;co++)
Mutatie(PopulatieNoua[1+random(MaxPopulatie)]);
CalculFitness();
OrdoneazaDupaFitness();
if (CelMaiBunCromozom.fitness==0) sw=0;
}
Afiseaza();
}
void citire()
{
int i;
clrscr();
cout<<"Numarul de necunoscute:";
cin>>MaxGena;

17

for(i=1;i<=MaxGena;i++)
{
cout<<"a["<<i<<"]=";
cin>>a[i];
InitVar+=a[i];
}
cout<<"b=";
cin>>b;
}
void InitializeazaPopulatie()
{
int i,j;
for(i=1;i<=MaxPopulatie;i++)
for(j=1;j<=MaxGena;j++)
PopulatieVeche[i].gena[j]=random(2*(b+1)/InitVar);
CalculFitness();
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];
}
void CalculFitness()
{
int i,j;
float s;
for(i=1;i<=MaxPopulatie;i++)
{
s=0;
for(j=1;j<=MaxGena;j++)
s=s+a[j]*PopulatieVeche[i].gena[j];
PopulatieVeche[i].fitness=abs(s-b);
}
}
void Elitism(int NrMutati)
{
int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=PopulatieVeche[MaxPopulatie-i+1];
}
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2)
{
int i,r;
r=1+random(MaxGena-1);
for(i=1;i<=MaxGena;i++)
if (i<=r)
{
rez1.gena[i]=g1.gena[i];
rez2.gena[i]=g2.gena[i];
}
else
{
rez1.gena[i]=g2.gena[i];
rez2.gena[i]=g1.gena[i];
}
}
void Mutatie(TCromozom &mutant)
{
int r1,r2,i;
for(i=1;i<=MaxGena;i++)

18

{
r1=1+random(b/(10*InitVar));
r2=random(2);
if (r2) mutant.gena[i]+=r1;
else mutant.gena[i]-=r1;
}
}
void MutaRestul(int NrMutati)
{
int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=PopulatieVeche[1+random(MaxPopulatieMaxElitism)];
}
void OrdoneazaDupaFitness()
{
int i,sw=1;
TCromozom aux;
while (sw)
{
sw=0;
for(i=1;i<=MaxPopulatie-1;i++)
if (PopulatieVeche[i].fitness<PopulatieVeche[i+1].fitness)
{
aux=PopulatieVeche[i];
PopulatieVeche[i]=PopulatieVeche[i+1];
PopulatieVeche[i+1]=aux;
sw=1;
}
}
if (PopulatieVeche[MaxPopulatie].fitness<CelMaiBunCromozom.fitness)
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];
}
void SelectieParintiAranjata(int &r1, int &r2)
{
int s,r;
s=MaxPopulatie*(MaxPopulatie+1)/2;
r=1+random(s);
r1=0;
while (r>0)
{
r1++;
r-=r1;
}
r=1+random(s);
r2=0;
while (r>0)
{
r2++;
r-=r2;
}
}
void Afiseaza()
{
int i;
if (CelMaiBunCromozom.fitness==0)
cout<<"O solutie este:"<<endl;

19

else
cout<<"Cea mai apropiata solutie gasita are o eroare de "<<
(CelMaiBunCromozom.fitness/b)*100<<"%"<<endl<<"Aceasta este:";
for(i=1;i<=MaxGena;i++)
cout<<CelMaiBunCromozom.gena[i]<<" ";
getch();
}

Exemplu
Pentru n=10 si urmatoarele valori ale coeficientilor:
a1

a2

a3

a4

a5

A6

a7

a8

a9

a10

147

258

369

123

456

789

159

753

268

842

10000

programul afiseaza la trei apeluri succesive urmatoarele solutii:


-

2 3 3 3 3 2 3 2 0 3 cu o eroare de 0.01%;
2 3 2 1 3 1 2 3 3 3 cu o eroare de 0.07%;
1 0 3 2 2 3 3 3 3 2 cu o eroare de 0.03%.

2. Problema discreta a rucsacului


Cu ajutorul unui rucsac avand capacitatea maxim admisibila G trebuie sa se
transporte o serie de obiecte din n disponibile avand greutatile g1,g2,,gn si utilitatile
c1,c2,,cn. Presupunand ca obiectele nu se pot diviza, gasiti o metoda optima de umplere
a rucsacului (o solutie care maximizeaza utilitatea totala a obiectelor).
Analiza problemei
0) Datele de intrare
Capacitate float;
NrObiecte int;
GreutateObiect vector de tip float;
UtilitateObiect vector de tip float.
1) Codificarea cromozomilor
Cromozomii vor fi codificati binar. Fiecare cromozom va contine NrObiecte biti, un
bit oarecare i luand valoarea 1 daca obiectul i se gaseste in rucsac si 0 in caz contrar.
Codificarea tipului cromozom este urmatoarea:
typedef struct{
int gena[15];
float fitness;
}TCromozom;

20

2) Populatia initiala
Cromozomii fiind codificati binar, populatia initiala se va genera prin completarea
aleatoare a bitilor cromozomilor initiali cu valorile 0 sau 1. Secventa de instructiuni care
realizeaza acest lucru este urmatoarea:
for(i=1;i<=MaxPopulatie;i++)
for(j=1;j<=MaxGena;j++) //MaxGena=NrObiecte
PopulatieVeche[i].gena[j]=random(2);

3) Fitness
O solutie este cu atat mai buna cu cat utilitatea totala pe care o are este mai mare, deci
la prima vedere utilitatea totala ar trebui sa fie fitness-ul unui cromozom. In afara de
aceasta conditie insa, mai avem una: greutatea totala a obiectelor trebuie sa fie mai mica
decat capacitatea rucsacului. Notand cu Ut utilitatea totala a obiectelor unei solutii si cu
Gt greutatea totala a lor, vom cauta sa avem o solutie cu Ut cat mai mare si cu
|Capacitate-Gt| cat mai apropiat de 0.
Vom lucra cu 3 fitness-uri definite astfel:
- fitness1 = Ut;
- fitness2 = Gt;
- fitness = |Capacitate-Gt|/Ut.
Primele doua fitness-uri vor folosi la aflarea celui mai bun cromozom dintr-o
generatie, iar cel de-al treilea este cel dupa care facem ordonarea (vom cauta sa obtinem
cromozomi cu fitness-ul cat mai mic). Din aceasta cauza tipul cromozom va trebui
modificat prin inlocuirea liniei float fitness; cu float fitness1, fitness2,
fitness;. In continuare vom da secventa ce codifica calcularea fitness-urilor
cromozomilor dintr-o populatie.
for(i=1;i<=MaxPopulatie;i++)
{
f1=f2=0;
for(j=1;j<=MaxGena;j++)
{
f1=f1+PopulatieVeche[i].gena[j]*UtilitateObiect[j];
f2=f2+PopulatieVeche[i].gena[j]*GreutateObiect[j];
}
PopulatieVeche[i].fitness1=f1;
PopulatieVeche[i].fitness2=f2;
f2=abs(Capacitate-f2);
if (f1) PopulatieVeche[i].fitness=f2/f1;
else PopulatieVeche[i].fitness=100;
}

Conditia de final apare pentru ca exista posibilitatea ca la initializare sau prin mutatii
sa se formeze cromozomi cu toate genele egale cu 0, avand astfel fitness1=0.
4) Selectarea cromozomilor parinti
Cromozomii formati intr-o populatie se ordoneaza descrescator dupa fitness. Metoda
de selectie folosita este cea prin selectie aranjata.

21

5) Crossover-ul
Incrucisarea se realizeaza intr-un singur punct, acesta fiind ales aleator.
r=1+random(MaxGena-1);
for(i=1;i<=MaxGena;i++)
if (i<=r)
{
rez1.gena[i]=g1.gena[i];
rez2.gena[i]=g2.gena[i];
}
else
{
rez1.gena[i]=g2.gena[i];
rez2.gena[i]=g1.gena[i];
}

6) Mutatia
Mutatia are o probabilitate de 1% si se realizeaza prin inversarea bitilor.
for(i=1;i<=MaxGena;i++)
if (mutant.gena[i]==1) mutant.gena[i]=0;
else mutant.gena[i]=1;

7) Cel mai bun cromozom


Se initializeaza in functia InitializeazaPopulatie() si intr-o generatie se calculeaza in
functia OrdoneazaDupaFitness(), dupa ordonare, astfel:
for(i=1;i<=MaxPopulatie;i++)
if
((PopulatieVeche[i].fitness1>CelMaiBunCromozom.fitness1)&&(PopulatieVech
e[i].fitness2<=Capacitate))
CelMaiBunCromozom=PopulatieVeche[i];

In continuare vom da codul sursa complet care rezolva aceasta problema.


#include<iostream.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
const MaxPopulatie=100,
MaxIteratii=1000,
MaxElitism=2,
MaxCrossover=20,
MaxMutatie=1;
typedef struct{
int gena[15];
float fitness1, fitness2, fitness;
}TCromozom;
typedef TCromozom TPopulatie[MaxPopulatie+1];
TCromozom CelMaiBunCromozom;
TPopulatie PopulatieVeche, PopulatieNoua;
int i,j,k,contor,co,r1,r2;
int NrPop, NrObiecte, MaxGena;
float Capacitate, GreutateObiect[100], UtilitateObiect[100];
void citire();

22

void CalculFitness();
void InitializeazaPopulatie();
void Elitism(int NrMutati);
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2);
void Mutatie(TCromozom &mutant);
void MutaRestul(int NrMutati);
void OrdoneazaDupaFitness();
void SelectieParintiAranjata(int &r1, int &r2);
void Afiseaza();
void main()
{
int i;
citire();
randomize();
InitializeazaPopulatie();
OrdoneazaDupaFitness();
for(i=1;i<=MaxIteratii;i++)
{
NrPop=0;
Elitism(MaxElitism);
for(co=1;co<=MaxCrossover;co++)
{
SelectieParintiAranjata(r1,r2);
Crossover(PopulatieVeche[r1],PopulatieVeche[r2],
PopulatieNoua[++NrPop],PopulatieNoua[++NrPop]);
}
MutaRestul(MaxPopulatie-NrPop);
for(co=1;co<=MaxPopulatie;co++)
PopulatieVeche[co]=PopulatieNoua[co];
for(co=1;co<=MaxMutatie;co++)
Mutatie(PopulatieNoua[1+random(MaxPopulatie)]);
CalculFitness();
OrdoneazaDupaFitness();
}
Afiseaza();
}
void citire()
{
int i;
clrscr();
cout<<"Capacitatea rucsacului:";
cin>>Capacitate;
cout<<"Numarul de obiecte:";
cin>>NrObiecte;
MaxGena=NrObiecte;
for(i=1;i<=NrObiecte;i++)
{
cout<<"Greutate obiect "<<i<<":";
cin>>GreutateObiect[i];
cout<<"Utilitate obiect "<<i<<":";
cin>>UtilitateObiect[i];
}
}
void InitializeazaPopulatie()
{
int i,j;

23

for(i=1;i<=MaxPopulatie;i++)
for(j=1;j<=MaxGena;j++)
PopulatieVeche[i].gena[j]=random(2);
CalculFitness();
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];
}
void CalculFitness()
{
int i,j;
float f1,f2;
for(i=1;i<=MaxPopulatie;i++)
{
f1=f2=0;
for(j=1;j<=MaxGena;j++)
{
f1=f1+PopulatieVeche[i].gena[j]*UtilitateObiect[j];
f2=f2+PopulatieVeche[i].gena[j]*GreutateObiect[j];
}
PopulatieVeche[i].fitness1=f1;
PopulatieVeche[i].fitness2=f2;
f2=abs(Capacitate-f2);
if (f1) PopulatieVeche[i].fitness=f2/f1;
else PopulatieVeche[i].fitness=100;
}
}
void Elitism(int NrMutati)
{
int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=PopulatieVeche[MaxPopulatie-i+1];
}
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2)
{
int i,r;
r=1+random(MaxGena-1);
for(i=1;i<=MaxGena;i++)
if (i<=r)
{
rez1.gena[i]=g1.gena[i];
rez2.gena[i]=g2.gena[i];
}
else
{
rez1.gena[i]=g2.gena[i];
rez2.gena[i]=g1.gena[i];
}
}
void Mutatie(TCromozom &mutant)
{
int i;
for(i=1;i<=MaxGena;i++)
if (mutant.gena[i]==1) mutant.gena[i]=0;
else mutant.gena[i]=1;
}
void MutaRestul(int NrMutati)
{

24

int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=PopulatieVeche[1+random(MaxPopulatieMaxElitism)];
}
void OrdoneazaDupaFitness()
{
int i,sw=1;
TCromozom aux;
while (sw)
{
sw=0;
for(i=1;i<=MaxPopulatie-1;i++)
if (PopulatieVeche[i].fitness<PopulatieVeche[i+1].fitness)
{
aux=PopulatieVeche[i];
PopulatieVeche[i]=PopulatieVeche[i+1];
PopulatieVeche[i+1]=aux;
sw=1;
}
}
for(i=1;i<=MaxPopulatie;i++)
if ((PopulatieVeche[i].fitness1>CelMaiBunCromozom.fitness1)
&&(PopulatieVeche[i].fitness2<=Capacitate))
CelMaiBunCromozom=PopulatieVeche[i];
}
void SelectieParintiAranjata(int &r1, int &r2)
{
int s,r;
s=MaxPopulatie*(MaxPopulatie+1)/2;
r=1+random(s);
r1=0;
while (r>0)
{
r1++;
r-=r1;
}
r=1+random(s);
r2=0;
while (r>0)
{
r2++;
r-=r2;
}
}
void Afiseaza()
{
int i;
cout<<"O umplere optima a rucsacului este formata din obiectele:";
for(i=1;i<=MaxGena;i++)
if (CelMaiBunCromozom.gena[i]) cout<<i<<" ";
cout<<endl;
cout<<"Ele au greutatea "<<CelMaiBunCromozom.fitness2<<
" si utilitatea "<<CelMaiBunCromozom.fitness1;
getch();
}

25

Exemplu
Pentru urmatoarele date de intrare:
Capacitate=22;
NrObiecte=6;
Obiect
Utilitate
Greutate

1
500
5

2
770
7

3
690
6

4
1000
8

4
480
4

6
630
6

Programul va afisa:
O umplere optima a rucsacului este formata din obiectele:2 3 4.
Ele au greutatea 21 si utilitatea 2460.
De remarcat faptul ca pentru aceleasi date de intrare un algoritm greedy care pune
obiectele in rucsac in ordinea descrescatoare a raportului utilitate/greutate va afisa:
O umplere optima a rucsacului este formata din obiectele:3 4 5.
Ele au greutatea 18 si utilitatea 2170.
3. Problema comis-voiajorului
Un comis-voiajor are de vizitat un numar de n orase. Oricare doua orase sunt legate
intre ele si costul deplasarii de la orasul i la orasul j este citit in elemetul ai,j al matricei
Anxn. Comis-voiajorul pleaca din orasul 1 si trebuie sa treaca prin toate celelalte orase o
singura data, intorcandu-se in final in orasul 1. Gasiti un drum care minimizeaza costul
total al deplasarii.
Analiza problemei
0)

Datele de intrare
n numarul de orase;
a matricea costurilor.

1)

Codificarea cromozomilor
Un cromozom va fi reprezentat printr-o permutare de tipul
1 (2) (3) (n) 1.
Codificarea tipului cromozom este urmatoarea:

typedef struct{
int gena[15];
int fitness;
}TCromozom;

Orice cromozom va avea gena[1]=gena[MaxGena]=1.

26

2)

Populatia initiala
Dificultatea in aceasta etapa consta in generarea unor permutari in mod aleator.
Pentru aceasta ne vom folosi de o functie care genereaza prin backtracking o permutare.
Functia are prototipul void GenereazaPermutare(int i, int j), unde i va reprezenta
indicele cromozomului care este generat iar j este un parametru care sporeste caracterul
aleatoriu al permutarii.
Secventa care initializeaza populatia este:
for(i=1;i<=MaxPopulatie;i++)
GenereazaPermutare(i, 1+random(50));

unde functia GenereazaPermutare(i,j) este:


void GenereazaPermutare(int i, int j)
{
int k=2;
st[1]=1;
st[2]=random(MaxGena-1);
while(j&&(k>1))
{
if (k==MaxGena)
{
CopieSolutie(i);
j--;k--;
}
else if (st[k]<MaxGena-1)
{
st[k]++;
if (Valid(k)) st[++k]=0;
}
else k--;
}
}

Functia CopieSolutie(int i) retine o posibila permutare.


3)

Fitness
Fitness-ul unui cromozom va reprezenta costul total al drumului pe care il codifica.
Astfel, vom cauta sa obtinem cromozomi cu un fitness cat mai mic.
for(i=1;i<=MaxPopulatie;i++)
{
PopulatieVeche[i].fitness=0;
for(j=1;j<MaxGena;j++)
PopulatieVeche[i].fitness+=
a[PopulatieVeche[i].gena[j]][PopulatieVeche[i].gena[j+1]];
}

4)

Selectarea cromozomilor parinti


Cromozomii formati intr-o populatie se ordoneaza descrescator dupa fitness. Metoda
de selectie folosita este cea prin selectie aranjata.

27

5) Crossover-ul
O alta dificultate a acestei probleme este gasirea unui mod de incrucisare care
pastreaza consistenta permutarii dupa transformare. Acest lucru poate fi realizat prin
urmatoarea forma de incrucisare: cei doi cromozomi parinti ( si ) se inmultesc intre ei
prin regula de inmultire dintre permutari, mai intai primul cu al doilea si apoi invers. Se
vor obtine doi cromozomi copii x si x. Avand in vedere faptul ca inmultirea
permutarilor este in general necomutativa, cei doi cromozomi rezultati vor fi diferiti.
for(i=1;i<=MaxGena;i++)
{
rez1.gena[i]=g1.gena[g2.gena[i]];
rez2.gena[i]=g2.gena[g1.gena[i]];
}

6)

Mutatia
Mutatia are o probabilitate de 1-3% si se realizeaza printr-o transpozitie (i,j) asupra
cromozomului dat, unde i si j sunt doua numere diferite generate aleator si cuprinse intre
2 si n (MaxGena=n+1).
{
r1=1+random(MaxGena-1);
do
r2=1+random(MaxGena-1);
while (r1==r2);
aux=mutant.gena[r1];
mutant.gena[r1]=mutant.gena[r2];
mutant.gena[r2]=aux;
}

7) Cel mai bun cromozom


Se initializeaza in functia InitializeazaPopulatie() si intr-o generatie se calculeaza in
functia OrdoneazaDupaFitness().
In continuare vom da codul sursa complet.
#include<iostream.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
const MaxPopulatie=100,
MaxIteratii=200,
MaxElitism=4,
MaxCrossover=36,
MaxMutatie=3;
typedef struct{
int gena[33];
float fitness;
}TCromozom;
typedef TCromozom TPopulatie[MaxPopulatie+1];
TCromozom CelMaiBunCromozom;
TPopulatie PopulatieVeche, PopulatieNoua;
int i,j,k,co,r1,r2, st[100];
int NrPop, MaxGena, a[30][30];

28

void Citire();
void CalculFitness();
void GenereazaPermutare(int i, int j);
int Valid(int k);
void CopieSolutie(int i);
void InitializeazaPopulatie();
void Elitism(int NrMutati);
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2);
void Mutatie(TCromozom &mutant);
void MutaRestul(int NrMutati);
void OrdoneazaDupaFitness();
void SelectieParintiAranjata(int &r1, int &r2);
void Afiseaza();
void main()
{
int i;
Citire();
randomize();
InitializeazaPopulatie();
OrdoneazaDupaFitness();
for(i=1;i<=MaxIteratii;i++)
{
NrPop=0;
Elitism(MaxElitism);
for(co=1;co<=MaxCrossover;co++)
{
SelectieParintiAranjata(r1,r2);
Crossover(PopulatieVeche[r1],PopulatieVeche[r2],
PopulatieNoua[++NrPop],PopulatieNoua[++NrPop]);
}
MutaRestul(MaxPopulatie-NrPop);
for(co=1;co<=MaxPopulatie;co++)
PopulatieVeche[co]=PopulatieNoua[co];
for(co=1;co<=MaxMutatie;co++)
Mutatie(PopulatieNoua[1+random(MaxPopulatie)]);
CalculFitness();
OrdoneazaDupaFitness();
}
Afiseaza();
}
void Citire()
{
int i,j,n;
clrscr();
cout<<"Numarul de orase:";
cin>>n;
MaxGena=n+1;
for(i=1;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
cout<<"Costul deplasarii "<<i<<"<->"<<j<<" :";
cin>>a[i][j];
a[j][i]=a[i][j];
}
a[i][i]=0;

29

}
}
void GenereazaPermutare(int i, int j)
{
int k=2;
st[1]=1;
st[2]=random(MaxGena-1);
while(j&&(k>1))
{
if (k==MaxGena)
{
CopieSolutie(i);
j--;k--;
}
else if (st[k]<MaxGena-1)
{
st[k]++;
if (Valid(k)) st[++k]=0;
}
else k--;
}
}
void CopieSolutie(int i)
{
int j;
for(j=2;j<MaxGena;j++)
PopulatieVeche[i].gena[j]=st[j];
PopulatieVeche[i].gena[1]=PopulatieVeche[i].gena[MaxGena]=1;
}
int Valid(int k)
{
int i,v=1;
for(i=1;i<k&&v;i++)
if (st[i]==st[k]) v=0;
return v;
}
void InitializeazaPopulatie()
{
int i;
for(i=1;i<=MaxPopulatie;i++)
GenereazaPermutare(i, 1+random(50));
CalculFitness();
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];
}
void CalculFitness()
{
int i,j;
for(i=1;i<=MaxPopulatie;i++)
{
PopulatieVeche[i].fitness=0;
for(j=1;j<MaxGena;j++)
PopulatieVeche[i].fitness+=
a[PopulatieVeche[i].gena[j]][PopulatieVeche[i].gena[j+1]];
}
}
void Elitism(int NrMutati)
{

30

int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=PopulatieVeche[MaxPopulatie-i+1];
}
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2)
{
int i;
for(i=1;i<=MaxGena;i++)
{
rez1.gena[i]=g1.gena[g2.gena[i]];
rez2.gena[i]=g2.gena[g1.gena[i]];
}
}
void Mutatie(TCromozom &mutant)
{
int r1,r2,aux;
r1=1+random(MaxGena-1);
do
r2=1+random(MaxGena-1);
while (r1==r2);
aux=mutant.gena[r1];
mutant.gena[r1]=mutant.gena[r2];
mutant.gena[r2]=aux;
}
void MutaRestul(int NrMutati)
{
int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=PopulatieVeche[1+random(MaxPopulatieMaxElitism)];
}
void OrdoneazaDupaFitness()
{
int i,sw=1;
TCromozom aux;
while (sw)
{
sw=0;
for(i=1;i<=MaxPopulatie-1;i++)
if (PopulatieVeche[i].fitness<PopulatieVeche[i+1].fitness)
{
aux=PopulatieVeche[i];
PopulatieVeche[i]=PopulatieVeche[i+1];
PopulatieVeche[i+1]=aux;
sw=1;
}
}
if ((PopulatieVeche[MaxPopulatie].fitness<CelMaiBunCromozom.fitness)
&&(PopulatieVeche[MaxPopulatie].fitness))
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];
}
void SelectieParintiAranjata(int &r1, int &r2)
{
int s,r;
s=MaxPopulatie*(MaxPopulatie+1)/2;
r=1+random(s);

31

r1=0;
while (r>0)
{
r1++;
r-=r1;
}
r=1+random(s);
r2=0;
while (r>0)
{
r2++;
r-=r2;
}
}
void Afiseaza()
{
int i;
cout<<"Un drum optim este:";
for(i=1;i<=MaxGena;i++)
cout<<CelMaiBunCromozom.gena[i]<<" ";
cout<<endl;
cout<<"Drumul are costul total de: "<<CelMaiBunCromozom.fitness;
getch();
}

Exemplu
Pentru urmatoarele date de intrare:
n=12;
- matricea costurilor 0
1
2
3
4
5
6
7
8
9
1
2

1
0
3
4
5
6
7
8
9
1
2
3

2
3
0
4
5
6
7
8
9
1
2
3

3
4
4
0
4
5
6
7
8
9
1
2

4
5
5
4
0
3
4
5
6
7
8
9

5
6
6
5
3
0
1
2
3
4
5
6

6
7
7
6
4
1
0
7
8
9
1
2

7
8
8
7
5
2
7
0
3
4
5
6

8
9
9
8
6
3
8
3
0
7
8
9

9
1
1
9
7
4
9
4
7
0
1
2

1
2
2
1
8
5
1
5
8
1
0
3

2
3
3
2
9
6
2
6
9
2
3
0

programul ce rezolva problema prin backtracking va afisa dupa aproximativ 10 min


drumul 1 2 10 3 5 8 9 6 7 11 4 12 1 de cost total 26.
Pentru aceleasi date de intrare programul scris mai sus afiseaza la 3 apeluri succesive
urmatoarele drumuri:
- 1 11 2 10 3 4 12 7 6 9 8 5 1 de cost 29;
- 1 2 3 10 6 8 9 5 4 12 7 11 1 de cost 30;
- 1 2 11 3 10 8 9 6 7 12 4 5 1 de cost 29.
Timpul mediu de rulare pentru un exemplu este de 2 s.

32

4. Maximul unei functii n-dimensionale


Considerandu-se o functie in n nedeterminate definita pe un produs cartezian
[a,b]x[a,b]x..x[a,b] de n intervale identice se cere sa se afle maximul ei si un set de valori
din domeniul de definitie pentru care acest maxim este atins.
Analiza problemei
0) Datele de intrare
Dat fiind gradul ridicat de dificultate pe care il necesita citirea expresiei functiei de la
tastatura, aceasta va fi scrisa intr-o functie care va returna valoarea ei intr-un set de
puncte (x1,x2,,x n).
Singurele date care se citesc de la tastatura sunt capetele intervalului [a,b].
1) Codificarea cromozomilor
Dificultatea acestei probleme consta in faptul ca punctele (x1,x2,,xn) iau valori
reale, si deci trebuie sa gasim o modalitate eficienta de codificare a cromozomilor. Daca
vom incerca sa codificam o gena a unui cromozom printr-un numar real (un cromozom
va contine n gene), vom observa ca prin transformarile cunoscute (crossover, mutatie) nu
se obtine un numar multumitor de combinatii, spatiul solutiilor fiind foarte slab acoperit.
Codificarea binara ridica iarasi probleme la transformarea unui numar real in baza 2 si
reciproc. Cea mai buna solutie in acest caz o reprezinta o imbinare a celor doua metode.
O gena va consta dintr-un vector de lungime NrBiti format din cifrele 0 si 1. Astfel o
gena va putea codifica (2^NrBiti)-1 numere naturale prin transformarea numarului format
din elementele binare ale vectorului intr-un numar din baza 10. Decodificarea unei gene
va fi realizata in modul urmator: intervalul [a,b] este impartit in (2^NrBiti)-1 diviziuni. O
gena i ce contine un numar y in baza 2 va fi transformata in numarul xi astfel: y
xi=a+(b-a)*[y]10/[(2^NrBiti)-1], unde [y]10 reprezinta numarul y in baza 10.
2) Populatia initiala
Va fi creata prin generarea aleatoare de valori binare pentru fiecare vector al fiecarei
gene al fiecarui cromozom al populatiei initiale.
3) Fitness
Fitness-ul unui cromozom va reprezenta valoarea functiei in punctele (x1,x2,,xn)
codificate de acesta. Petru a-l calcula vom avea nevoie de o functie float transf(int
i, int j) care realizeaza decodificarea genei j a cromozomului i.
4) Selectarea cromozomilor parinti
Cromozomii formati intr-o populatie se ordoneaza crescator dupa fitness. Metoda de
selectie folosita este cea prin selectie aranjata.
5) Crossover-ul
Incrucisarea se realizeaza intr-un singur punct, acesta fiind ales aleator.

33

6) Mutatia
Mutatia are o probabilitate de 1% si se realizeaza prin inversarea valorilor bitilor
tuturor genelor unui cromozom ales in mod aleator.
7) Cel mai bun cromozom
Se initializeaza in functia InitializeazaPopulatie() si intr-o generatie se calculeaza in
functia OrdoneazaDupaFitness().
In continuare vom da codul sursa complet al problemei care afla maximul unei functii
in 6 nedeterminate, expresia ei aflandu-se in functia float Functia(float
Nedet[MaxGena+1]).
#include<iostream.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
const MaxPopulatie=100,
MaxIteratii=200,
MaxElitism=3,
MaxCrossover=30,
MaxMutatie=1,
MaxGena=6,
NrBiti=15;
typedef struct{
int gena[MaxGena+1][NrBiti+1];
float fitness;
}TCromozom;
typedef TCromozom TPopulatie[MaxPopulatie+1];
TCromozom CelMaiBunCromozom;
TPopulatie PopulatieVeche, PopulatieNoua;
int i,j,k,contor,co,r1,r2;
float LungDiv=0;
int NrPop,a,b;
void Citire();
float Functia(float Nedet[MaxGena+2]);
float Transf(TCromozom g,int j);
void CalculFitness();
void InitializeazaPopulatie();
void Elitism(int NrMutati);
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2);
void Mutatie(TCromozom &mutant);
void MutaRestul(int NrMutati);
void OrdoneazaDupaFitness();
void SelectieParintiAranjata(int &r1, int &r2);
void Afiseaza();
void main()
{
int i,j;
Citire();
randomize();
InitializeazaPopulatie();
CelMaiBunCromozom.fitness=0;
OrdoneazaDupaFitness();
for(i=1;i<=MaxIteratii;i++)

34

{
NrPop=0;
Elitism(MaxElitism);
for(co=1;co<=MaxCrossover;co++)
{
SelectieParintiAranjata(r1,r2);
Crossover(PopulatieVeche[r1],PopulatieVeche[r2],
PopulatieNoua[++NrPop],PopulatieNoua[++NrPop]);
}
MutaRestul(MaxPopulatie-NrPop);
for(co=1;co<=MaxPopulatie;co++)
PopulatieVeche[co]=PopulatieNoua[co];
for(co=1;co<=MaxMutatie;co++)
Mutatie(PopulatieNoua[1+random(MaxPopulatie)]);
CalculFitness();
OrdoneazaDupaFitness();
}
Afiseaza();
}
void Citire()
{
int i;
clrscr();
cout<<"a=";
cin>>a;
cout<<"b=";
cin>>b;
for(i=1;i<=NrBiti;i++)
LungDiv=2*LungDiv+1;
}
float Functia(float Nedet[MaxGena+2])
{
float x1,x2,x3,x4,x5,x6;
x1=Nedet[1]-1.123;
x2=Nedet[2]-1.234;
x3=Nedet[3]-1.345;
x4=Nedet[4]-1.456;
x5=Nedet[5]-1.567;
x6=Nedet[6]-1.678;
return (1/(1+x1*x1+x2*x2+x3*x3+x4*x4+x5*x5+x6*x6));
}
void InitializeazaPopulatie()
{
int i,j,k;
for(i=1;i<=MaxPopulatie;i++)
for(j=1;j<=MaxGena;j++)
for(k=1;k<=NrBiti;k++)
PopulatieVeche[i].gena[j][k]=random(2);
CalculFitness();
}
float Transf(TCromozom g, int j)
{
int k;
float nr=0,x;
for(k=1;k<=NrBiti;k++)
nr=2*nr+g.gena[j][k];
x=a+(b-a)*(nr/LungDiv);

35

return x;
}
void CalculFitness()
{
int i,j,co;
float Nedet[MaxGena+2];
for(i=1;i<=MaxPopulatie;i++)
{
co=0;
for(j=1;j<=MaxGena;j++)
{
Nedet[++co]=Transf(PopulatieVeche[i],j);
}
PopulatieVeche[i].fitness=Functia(Nedet);
}
}
void Elitism(int NrMutati)
{
int i;
for(i=1;i<=NrMutati;i++)
PopulatieNoua[++NrPop]=PopulatieVeche[MaxPopulatie-i+1];
}
void Crossover(TCromozom g1, TCromozom g2, TCromozom &rez1, TCromozom
&rez2)
{
int r,j,k;
for(j=1;j<=MaxGena;j++)
{
r=1+random(NrBiti-1);
for(k=1;k<=NrBiti;k++)
if (k<=r)
{
rez1.gena[j][k]=g1.gena[j][k];
rez2.gena[j][k]=g2.gena[j][k];
}
else
{
rez1.gena[j][k]=g2.gena[j][k];
rez2.gena[j][k]=g1.gena[j][k];
}
}
}
void Mutatie(TCromozom &mutant)
{
int r,j;
for(j=1;j<=MaxGena;j++)
{
r=1+random(NrBiti-1);
if (mutant.gena[j][r]) mutant.gena[j][r]=0;
else mutant.gena[j][r]=1;
}
}
void MutaRestul(int NrMutati)
{
int i;
for(i=1;i<=NrMutati;i++)

36

PopulatieNoua[++NrPop]=PopulatieVeche[1+random(MaxPopulatieMaxElitism)];
}
void OrdoneazaDupaFitness()
{
int i,sw=1;
TCromozom aux;
while (sw)
{
sw=0;
for(i=1;i<=MaxPopulatie-1;i++)
if (PopulatieVeche[i].fitness>PopulatieVeche[i+1].fitness)
{
aux=PopulatieVeche[i];
PopulatieVeche[i]=PopulatieVeche[i+1];
PopulatieVeche[i+1]=aux;
sw=1;
}
}
if (PopulatieVeche[MaxPopulatie].fitness>CelMaiBunCromozom.fitness)
CelMaiBunCromozom=PopulatieVeche[MaxPopulatie];
}
void SelectieParintiAranjata(int &r1, int &r2)
{
int s,r;
s=MaxPopulatie*(MaxPopulatie+1)/2;
r=1+random(s);
r1=0;
while (r>0)
{
r1++;
r-=r1;
}
r=1+random(s);
r2=0;
while (r>0)
{
r2++;
r-=r2;
}
}
void Afiseaza()
{
int i;
cout<<"Maximul gasit este:"<<CelMaiBunCromozom.fitness;
cout<<endl<<"El este atins pentru valorile:";
for(i=1;i<=MaxGena;i++)
{
cout<<endl<<"X"<<i<<"=";
cout<<Transf(CelMaiBunCromozom,i);
}
getch();
}

37

Exemplu
Pentru a=1, b=2 programul va afisa la trei apeluri succesive:
Apel
1)
2)
3)

X1
X2
X3
X4
X5
X6
1.125034 1.233894 1.375286 1.456343 1.562487 1.681814
1.125248 1.251106 1.341746 1.477798 1.578448 1.753929
1.122288 1.234077 1.376080 1.501999 1.499985 1.624378

38

f
0.999044
0.993365
0.989659

Cap.V Consideratii finale


Urmarind exemplele de aplicatii care s-au dat in capitolul anterior putem da niste
caracteristici generale ale problemelor pe care le poate rezolva un algoritm genetic. Este
recomandat astfel ca problemele date sa aiba un volum mare de date de prelucrat.
Din punctul de vedere al spatiului solutiilor, problemele se impart in 2 categorii:
1. Probleme cu spatiul solutiilor finit.
Aici sunt incluse problema discreta a rucsacului si cea a comis-voiajorului. In general,
orice problema care se poate rezolva prin backtracking are si o rezolvare prin algoritmi
genetici. Cele doua probleme mentionate mai sus, impreuna cu altele de acest gen, fac
parte din categoria problemelor NP-complete, adica a acelor probleme pentru care nu
exista la ora actuala algoritmi polinomiali deterministi de rezolvare a lor. Algoritmii
genetici reprezinta o alternativa viabila celor deterministi exponentiali care rezolva aceste
probleme, pentru ca, spre exemplu, in cazul problemei comis voiajorului, testarea tuturor
drumurilor posibile care leaga n orase ar presupune n! operatii matematice. Un tur ce
cuprinde 30 de orase ar presupune aproximativ 2.65 X10 32 operatii. Presupunand ca un
procesor ar realiza un miliard de operatii pe secunda, timpul de rezolvare a problemei ar
fi in jur de 8,000,000,000,000,000 de ani! Adaugarea unui singur oras creste numarul de
operatii de 31 de ori.
2. Probleme cu spatiul solutiilor infinit.
In aceasta categorie se include problema maximului unei functii n-dimensionale. De
remarcat ca in cazul maximului unei functii unidimensionale exista metode clasice de
rezolvare (metoda coardei, cea a tangentei), dar care se pot aplica numai functiilor care
indeplinesc anumite conditii (continuitate, derivabilitate). Aceste metode insa se limiteaza
la functii intr-o singura nedeterminata.
Desi problema rezolvarii unei ecuatii diofanice ar putea fi inclusa foarte bine in prima
categorie, am preferat sa o mentionez aici, deoarece ea reprezina prin caracterul ei o
probema aflata la granita dintre cele doua tipuri de probleme.
Optimizari
Pentru o mai buna intelegere a algoritmilor genetici, s-a incercat in lucrarea de fata o
prezentare cat mai fixa a lor, variatiile de la o aplicatie la alta limitandu-se la strictul
necesar.
In realitate insa, se poate spune fara exagerare ca numarul lor este egal cu cel al
problemelor care implementeaza un algoritm genetic. Cauzele diversitatii lor sunt
urmatoarele:
Proiectarea unui algoritm genetic incepe cu alegerea caracteristicilor individului. Deci
doua probleme de optimizare conduc la doua programe diferite;
Deciziile pe care le ia proiectantul de programe la implementarea unui algoritm
genetic sunt in mare masura echivalente intre ele, situatie n care intervine stilul
programatorului;
Rezultatul rularii unui algoritm genetic depinde de alegerea parametrilor de control ai
algoritmului genetic, deci depinde de optiunile experimentatorului.

39

In continuare vom da cateva optimizari care se pot face algoritmilor prezentati mai
sus.
In primul rand, prin numarul parametrilor de control si prin operatiile aleatoare care se
fac in cadrul programului (generarea populatiei initiale, alegerea punctului de incrucisare,
mutatia, alegerea supravietuitorilor), acesti algoritmi au un caracter semialeator. De
aceea pentru o problema data se recomanda schimbarea valorilor parametrilor de control
si observarea diferentelor dintre solutiile afisate. Principalii parametri care pot fi
schimbati (se pot considera chiar date de intrare) sunt urmatorii:
- numarul cromozomilor unei populatii;
- numarul de generatii create;
- numarul de cromozomi copiati prin elitism;
- numarul de incrucisari pe populatie;
- numarul de mutatii pe populatie.
Pentru ca majoritatea lucrarilor din literatura de specialitate recomanda rularea de mai
multe ori a unui algoritm genetic si apoi notarea celei mai bune solutii gasite, sugerez
introducerea unui alt parametru fata de cei prezentati mai sus. Acesta va ajuta la
simularea rularii multiple a programului. In programul dat mai jos parametrul este
denumit MaxReIteratii.
void main()
{
int i,j;
Citire();
/*aici se va initializa CelMaiBunCromozom*/
for(j=1;j<=MaxReIteratii;j++)
{
randomize();
InitializeazaPopulatie();
/*din aceasta functie se va scoate initializarea
celui mai bun cromozom*/
OrdoneazaDupaFitness();
for(i=1;i<=MaxIteratii;i++)
{
/*aici nu se modifica nimic*/
}
}
Afiseaza();
}

Dupa cum se observa, modificarile care intervin sunt minore.


Alte optimizari care se pot face au legatura cu functiile care alcatuiesc programul,
dupa cum urmeaza:
Selectia realizata in aplicatiile de la capitolul precedent s-a bazat numai pe metoda
selectiei aranjate. Se pot folosi si celelalte metode sau chiar se pot realiza unele noi;
Pentru o intelegere mai usoara a algoritmului, am preferat folosirea celei mai simple
metode de ordonare, si anume cea a bulelor. Avand in vedere faptul ca ordonarea
dupa fitness se face de un numar mare de ori, si ca populatia are un numar relativ
mare de indivizi recomand o varianta mai buna de ordonare, cum ar fi quicksort-ul;
Generarea populatiei initiale se poate face si prin metode euristice, multe din solutiile
generate aleator fiind departe de optimul cautat.

40

In sfarsit, un al treilea tip de optimizare care se poate face este cel legat de conditia de
oprire a algoritmului. Este posibil ca in cadrul unor probleme solutia sa evolueze rapid in
primele generatii si apoi evolutia ei sa fie nesemnificativa. De aceea, in cadrul acestui tip
de probleme, se recomanda monitorizarea evolutiei solutiei pe parcursul rularii
algoritmului si terminarea lui in momentul in care aceasta devine lenta.
In finalul acestui capitol vom introduce doua notiuni care permit compararea
performantelor a doi algoritmi genetici.

Timpul de stabilizare a populaiei i viteza de convergen

Pentru a compara performanele a doi algoritmi genetici care rezolva aceeai problema,
sau pentru a evalua influenta parametrilor de control asupra unui algoritm genetic, se folosesc cativa indicatori de performanta cum sunt: timpul de stabilizare a populatiei, viteza
de convergenta, etc..
Timpul de stabilizare a populatiei este timpul necesar aglomerarii indivizilor n
vecinatatea punctului optim.
Viteza de convergenta a algoritmului genetic este cresterea valorii medii a fitnessului f f P ( t ) f P ( t 1) la trecerea de la o generatie la alta.
In figura de mai jos s-a reprezentat grafic evolutia in timp a trei functii: f opt ( t ) , valoarea curenta a maximului fitness-ului, f P ( t ) , valoarea curenta a mediei fitness-ului si
f ( t ) viteza de convergenta.
Pe figura se observa ca din generatia a 20-a valoarea medie a fitness-ului, f P ( t ) , nu
sufera variatii importante. Viteza de convergenta a algoritmului genetic este panta valorii
medii a fitness-ului, f P ( t ) . Se observa ca pana in generatia a 15-a viteza de convergenta
este semnificativa, iar dupa aceea viteza de convergenta scade si ramane oscilanta in jurul
valorii f 0 .

41

Evolutia in timp a functiilor f opt ( t ) , f P ( t ) si f ( t ) .

In principiu, urmarirea evolutiei in timp a valorii curente a maximului fitness-ului nu


este un indicator de calitate decat daca algoritmul genetic rezolva o problema test
cunoscuta. Timpul de stabilizare a populatiei si viteza de convergenta a algoritmului
genetic sunt doua criterii in functie de care experimentatorul poate aprecia tendintele care
se manifesta in evolutia populatiei.

42

BIBLIOGRAFIE

Oltean, M. Proiectarea si implementarea algoritmilor, Editura Computer Libris


Agora, Cluj, 1999;
Aspru, O. Tehnici de programare, Editura Adias, Rm. Valcea, 1997;
Andonie, R., Garbacea, I. Algoritmi fundamentali, o perspectiva C++, Editura
Libris, Cluj, 1995;
Johnson, G. Computers and Itractability; A Guide to the Theory of
NPCompleteness, W. H. Freeman & co., 1979;
Colectia Gazetei de informatica;

43

CUPRINS

Capitolul I.

Consideraii generale 1
1. Introducere.... 1
2. Notiuni de genetica... 1
3. Algoritmul general (schema logica si pseudocod)... 2

Capitolul II.

Codificarea informatiei

1. Codificarea cromozomilor...
2. Populatia initiala.
3. Fitness-ul unui cromozom
4. Selectarea cromozomilor parinti...
5. Incrucisarea (crossover)
6. Mutatia..
7. Cand se termina algoritmul?.

5
6
6
6
7
9
9

Capitolul III.

Implementarea algoritmului 10

Capitolul IV.

Aplicatii..

15

1.
2.
3.
4.

15
20
26
33

Capitolul V.

Ecuatia diofanica...
Problema discreta a rucsacului..
Problema comis-voiajorului..
Maximul unei functii n-dimensionale...

Consideratii finale.. 39

Bibliografie. 43

44

Anda mungkin juga menyukai