You are on page 1of 6

<?php interface IObserver { public function receiveEvent($source, $message, $params); } interface IObservable { public function attachObserver(IObserver $observer); } ?

> <?php abstract class Chromosome { protected $state; public function __construct($initialState = null) { if (is_null($initialState)) $this->setState($this->getDe faultState()); else $this->setState($initialState); } protected function validateState($state) { return !is_null($state); } protected function getDefaultState() { return $this->getRandomState(); } abstract protected function getRandomState(); protected function setState($state) { if (!$this->validateState($state)) throw new Exception(' Invalid state.'); $this->state = $state; } public function getState() { return $this->state; } //Must return at least 1 abstract public function getFitness(); abstract public function mutate(); abstract public function breed(Chromosome $partner); abstract public function getMaxFitness(); public function __toString() { return $this->getState(); } } ?> <?php abstract class Selector { protected $population; protected $matingPopulation;

public function __construct($matingPopulation) { $this->resetPopulation(); $this->setMatingPopulation($matingPopulation); } public function setMatingPopulation($matingPopulation) { $this->matingPopulation = $matingPopulation; } public function getMatingPopulation() { return $this->matingPopu lation; } public function resetPopulation() { $this->population = array(); } abstract public function addChromosome(Chromosome $chromosome); abstract public function select(); } ?> <?php class RouletteWheelSelection extends Selector { protected $totalFitness; public function resetPopulation() { $this->totalFitness = 0; parent::resetPopulation(); } public function addChromosome(Chromosome $chromosome) { $fitness = $chromosome->getFitness(); $this->totalFitness += $fitness; $this->population[] = array('fitness' => $fitness, 'chro mosome' => $chromosome); } public function select() { $selection = array(); for ($i = 0; $i < $this->matingPopulation; ++$i) { $selection[] = $this->selectIndividual(); } return $selection; } protected function selectIndividual() { $target = mt_rand(0, $this->totalFitness - 1); $fitnessMin = 0; $n = count($this->population); for ($i = 0; $i < $n; ++$i) { $fitness = $this->population[$i]['fitness']; if ($target >= $fitnessMin && $target < $fitness Min + $fitness) { return $this->population[$i]['chromosome ']; } else { $fitnessMin += $fitness; } }

throw new Exception('Could not select individual!'); } } ?> <?php class Planet implements IObservable { protected $chromosomeDomain; protected $chromosomes; protected $observers; protected $solutions; protected $hasSolution; public function __construct($chromosomeDomain) { $this->setChromosomeDomain($chromosomeDomain); $this->chromosomes = array(); $this->observers = array(); $this->solutions = array(); $this->hasSolution = false; } public function attachObserver(IObserver $observer) { $this->observers[] = $observer; } protected function sendEvent($message, $params) { reset($this->observers); while ($value = each($this->observers)) { $value['value']->receiveEvent($this, $message, $ params); } } protected function setChromosomeDomain($chromosomeDomain) { $this->chromosomeDomain = $chromosomeDomain; } public function plantChromosome(Chromosome $chromosome) { if (get_class($chromosome) != $this->chromosomeDomain) t hrow new Exception('Chromosomes must be of type '.$this->chromosomeDomain.' to b e added to this planet.'); $this->chromosomes[(string)$chromosome] = $chromosome; $this->sendEvent('plantChromosome', $chromosome); } public function deleteChromosome(Chromosome $chromosome) { if (!isset($this->chromosomes[(string)$chromosome])) ret urn; unset($this->chromosomes[(string)$chromosome]); $this->sendEvent('deleteChromosome', $chromosome); } public function breedGeneration(Selector $selector) { if (count($this->chromosomes) < 2) throw new Exception(' The planet must have at least two chromosomes.'); $selector->resetPopulation(); foreach ($this->chromosomes as $chromosome) { $selector->addChromosome($chromosome);

} $selection = $selector->select(); $children = array(); $n = count($this->chromosomes); for ($i = 0; $i < $n; ++$i) { $parent1 = mt_rand(0, count($selection) - 1); $parent2 = $parent1; while ($parent1 == $parent2) { $parent2 = mt_rand(0, count($selection) - 1); } $parent1 = $selection[$parent1]; $parent2 = $selection[$parent2]; $child = $parent1->breed($parent2); $child->mutate(); if (isset($children[(string)$child])) { --$i; continue; } $fitness = $child->getFitness(); if ($fitness >= $child->getMaxFitness()) { if (!isset($this->solutions[(string)$chi ld])) $this->solutions[(string)$child] = $child; $this->hasSolution = true; $this->sendEvent('foundSolution', $child ); } $children[(string)$child] = $child; } $this->chromosomes = $children; $this->sendEvent('breedGeneration', $children); } public function getSolutions() { return $this->solutions; } public function isSolved() { return $this->hasSolution; } } ?> <?php class NumberSequence extends Chromosome { protected $length; protected $perfect; public function __construct($length, $initialState = null) { if ($length < 3 || $length > 10) throw new Exception('Th e number sequence must be 3-10 characters long.'); if (!is_null($initialState) && strlen($initialState) != $length) throw new Exception('Initial states must match length.'); $this->setLength($length); $this->perfect = $this->getPerfectSolution(); parent::__construct($initialState); } protected function setLength($length) { $this->length = $length; }

public function getLength() { return $this->length; } protected function getPerfectSolution() { $string = ''; for ($i = 0; $i < $this->length; ++$i) $string .= $i; return $string; } protected function getRandomState() { $string = ''; for ($i = 0; $i < $this->length; ++$i) $string .= mt_ran d(0, 9); return $string; } protected function validateState($state) { return ctype_digit($state) && strlen($state) == $this->l ength; } public function getFitness() { $n = strlen($this->state); $fitness = $n + 1; for ($i = 0; $i < $n; ++$i) { if ($this->state{$i} != $this->perfect{$i}) --$f itness; } return $fitness; } public function getMaxFitness() { return strlen($this->perfect) + 1; } public function mutate() { if (mt_rand(0, 1000) > 5) return; $split = str_split($this->getState()); $replacePosition = mt_rand(0, count($split) - 1); $split[$replacePosition] = (string)mt_rand(0,9); $this->setState(implode('',$split)); } public function breed(Chromosome $partner) { if (mt_rand(0, 100) > 70) return $this; if (mt_rand(0, 100) < 50) { $baseSplit = str_split($this->getState()); $replaceSplit = str_split($partner->getState()); } else { $replaceSplit = str_split($this->getState()); $baseSplit = str_split($partner->getState()); } $onePoint = mt_rand(-1, count($baseSplit) - 1); $twoPoint = $onePoint; while ($twoPoint == $onePoint) $twoPoint = mt_rand(-1, c ount($baseSplit) - 1); if ($onePoint > $twoPoint) { $temp = $onePoint; $onePoint = $twoPoint; $twoPoint = $temp; }

$childSplit = array(); $n = $this->getLength(); for ($i = 0; $i < $n; ++$i) { if ($i > $onePoint && $i <= $twoPoint) { $childSplit[] = $replaceSplit[$i]; } else { $childSplit[] = $baseSplit[$i]; } } return new NumberSequence($this->getLength(), implode('' ,$childSplit)); } } ?> <?php class SolutionPrinter implements IObserver { public function receiveEvent($source, $message, $params) { if ($message == 'foundSolution') { echo $params; } } } ?> <?php $wheel = new RouletteWheelSelection(24); while (true) { $planet = new Planet('NumberSequence'); $planet->attachObserver(new SolutionPrinter()); for ($i = 0; $i < 70; ++$i) $planet->plantChromosome(new NumberS equence(10)); for ($gen = 0; $gen < 1000; ++$gen) { $planet->breedGeneration($wheel); if ($planet->isSolved()) break 2; } } ?>

You might also like