Bu kısımda örnek bir genetik algoritma kodlaması sunulacaktır ve temel sınıf GeneticAlgorithm sınıfı olacaktır. Bu sınıf veri elemanı olarak algoritmaya ilişkin parametreleri tutmalıdır:
public class GeneticAlgorithm {
private int populationSize;
private double mutationRate;
private double crossoverRate;
private int elitismCount;
//...
}
- mutationRate parametresi, bireysel bir genin rasgele bir biçimde mutasyona uğrama olasılığını ifade etmektedir.
- crossoverRate parametresi iki bireyin çiftleşme olasılığını, yani genlerini paylaşarak popülasyona yeni yavru kazandırma olasılıklarını göstermektedir.
- elitismCount parametresi popülasyondaki en güçlü bireylerin nesiller boyunca korunması ile ilgidir. Elit bir birey çiftleşmez ve mutasyona uğramaz.
İfade edilmesi gereken diğer önemli kavram popülasyondur:
public class Population {
private Individual population[];
private double populationFitness;
//..
}
Popülasyon, bireyler topluluğu olup bir uygunluk derecesine sahiptir. Birey demek burada bir aday çözüm olarak düşünülebilir:
public class Individual {
private int[] chromosome;
private double fitness;
Bireyin tuttuğu en önemli bilgi "kromozom" olup bir çözümün kodlanmış hali olarak düşünülmelidir. Kromozom bir karakter dizisi veya bir liste olabilir. Yukarıda tamsayı dizisi şeklinde bir kromozom kodlaması görülmektedir. Kromozomun en küçük yapıtaşı "gen" olarak ifade edilir. Yukarıda her bir tamsayı bir gendir ve mutasyon gibi operatörler tarafından değiştirilebilir. Yukarıda ifade edilen "fitness" ise problemden probleme değişen bir uygunluk derecesidir. Bireyin rastgele biçimde oluşturulması aşağıdaki şekilde yapılabilir:
public Individual(int chromosomeLength) {
this.chromosome = new int[chromosomeLength];
for (int gene = 0; gene < chromosomeLength; gene++) {
if (0.5 < Math.random()) {
this.setGene(gene, 1);
} else {
this.setGene(gene, 0);
}
}
}
Uygunluk derecesi hesaplamasını örnekleyebilmek için basit bir problemi ele alalım: Tüm rakamları 1'lerden oluşan n uzunluğunda bir sayı dizisi elde etmek amacında olalım. Bu durumda aday çözümler, ihtiva ettikleri 1 rakamı doğrultusunda uygunluk değerine sahip olacaktır:
public double calcFitness(Individual individual) {
int correctGenes = 0;
for (int geneIndex = 0; geneIndex < individual.getChromosomeLength(); geneIndex++) {
if (individual.getGene(geneIndex) == 1) {
correctGenes += 1;
}
}
double fitness = (double) correctGenes / individual.getChromosomeLength();
individual.setFitness(fitness);
return fitness;
}
Tüm popülasyonun uygunluk derecesi ise teker teker bireylerin uygunluk dereceleri üzerinden hesaplanacaktır:
public void evalPopulation(Population population) {
double populationFitness = 0;
for (Individual individual : population.getIndividuals()) {
populationFitness += calcFitness(individual);
}
population.setPopulationFitness(populationFitness);
}
Şimdi genel olarak genetik algoritmanın nasıl çalışacağı üzerinde duralım:
public static void main(String[] args) {
GeneticAlgorithm ga = new GeneticAlgorithm(100, 0.001, 0.95, 2);
Population population = ga.initPopulation(50);
ga.evalPopulation(population);
int generation = 1;
while (ga.isTerminationConditionMet(population) == false) {
System.out.println("Best solution: " + population.getFittest(0).toString());
population = ga.crossoverPopulation(population);
population = ga.mutatePopulation(population);
ga.evalPopulation(population);
generation++;
}
System.out.println("Found solution in " + generation + " generations");
System.out.println("Best solution: " + population.getFittest(0).toString());
}
Elimizdeki problemde mükemmel sonucun ne olduğunu bildiğimiz için döngüyü ne zaman sonlandırmamız gerektiğini biliyoruz fakat her zaman durum böyle olmayabilir. Döngüden çıkış koşulu basitçe aşağıdaki şekilde hesaplanabilir:
public boolean isTerminationConditionMet(Population population) {
for (Individual individual : population.getIndividuals()) {
if (individual.getFitness() == 1) {
return true;
}
}
return false;
}
Çiftleşme işlemi, "crossoverRate" parametresi ve elitlik özelliği değerlendirilerek gerçekleşmelidir. Eğer çiftleşme olacaksa her iki ebeveynin genlerinden %50 oranında içeren yeni bir birey ortaya çıkacaktır. Mevcut popülasyondan hareketle yeni bir popülasyon nesnesinin oluşturulması uygun olabilir. [9]
public Population crossoverPopulation(Population population) {
Population newPopulation = new Population(population.size());
for (int populationIndex = 0; populationIndex < population.size(); populationIndex++) {
Individual parent1 = population.getFittest(populationIndex);
if (this.crossoverRate > Math.random() && populationIndex >= this.elitismCount) {
Individual offspring = new Individual(parent1.getChromosomeLength());
Individual parent2 = selectParent(population);
for (int geneIndex = 0; geneIndex < parent1.getChromosomeLength(); geneIndex++) {
if (0.5 > Math.random()) {
offspring.setGene(geneIndex, parent1.getGene(geneIndex));
} else {
offspring.setGene(geneIndex, parent2.getGene(geneIndex));
}
}
newPopulation.setIndividual(populationIndex, offspring);
} else {
newPopulation.setIndividual(populationIndex, parent1);
}
}
return newPopulation;
}
Mutasyon işlemi de bireylerin genlerinin rasgele olarak değiştirilmesini içerecektir fakat bu da yine mutationRate parametresi göz önünde bulundurularak yapılmalıdır ve elit bireyler mutasyona uğramamalıdır:
public Population mutatePopulation(Population population) {
Population newPopulation = new Population(this.populationSize);
for (int populationIndex = 0; populationIndex < population.size(); populationIndex++) {
Individual individual = population.getFittest(populationIndex);
for (int geneIndex = 0; geneIndex < individual.getChromosomeLength(); geneIndex++) {
if (populationIndex > this.elitismCount) {
if (this.mutationRate > Math.random()) {
int newGene = 1;
if (individual.getGene(geneIndex) == 1) {
newGene = 0;
}
individual.setGene(geneIndex, newGene);
}
}
}
newPopulation.setIndividual(populationIndex, individual);
}
return newPopulation;
}
Bu kodu çalıştırdığımızda 100 tane 1 rakamından oluşan bir sayı dizisi elde etmek için her çalıştırmada farklı bir nesil sayısı ile en iyi sonucun bulunduğunu görüyoruz. Örneğin ilk çalıştırmamızda ekran çıktısı:
...
Best solution: 11111111111111111111111111111101111111111111111111
Best solution: 11111111111111111111111111111101111111111111111111
Found solution in 125 generations
Best solution: 11111111111111111111111111111111111111111111111111
Tekrar çalıştırdığımızda ortadaki rasgelelik durumundan ötürü tamamen farklı bir nesil sayısı ile sonucun elde edildiğini görüyoruz:
...
Best solution: 11111111111111111110111111111111111111111111111111
Best solution: 11111111111111111110111111111111111111111111111111
Found solution in 141 generations
Best solution: 11111111111111111111111111111111111111111111111111
Kaynaklar:
- Meffert,
Klaus, et al. "Jgap-java genetic algorithms and genetic
programming package." URL:
http://jgap. sf. net (2012).
- J
acobson, Lee, and Burak Kanber. "Introduction." Genetic
Algorithms in Java Basics.
Apress, 2015. 1-19.
Comments