我实施GAHelloWorld时逻辑有什么问题?

时间:2013-10-12 12:34:53

标签: php arrays algorithm oop genetic-algorithm

我有一个计划生成任务,根据this question,可以借助遗传算法解决。

我经历了谷歌搜索,发现了许多非常有用的文献和两个“Hello World!”例子。到目前为止,我已经尝试将它们翻译成php,并重新封装以使代码可以重用于我未来的任务。

以下是C++Java上的示例链接(抱歉,最后一个是俄语版,但仍然可以使用此代码)。

这是我的实施:

<?php    
abstract class Creature
    {
        protected $fitness;

        public function __construct()
            {
                $this->fitness = 0;
            }

        public function getFitness()
            {
                return $this->fitness;
            }

        abstract public function calculateFitness();

        public function compareTo($creature)
            {
                return $this->fitness - $creature->fitness;
            }

        abstract public function mateWith($creature);

        abstract public function mutate();
    }

abstract class Population
    {
        protected $creatures;
        protected $generation;

        public function __construct()
            {
                $this->creatures    = array();
                $this->generation   = 1;

                $this->populate();
            }

        public function __destruct()
            {
                unset($this->creatures);
            }

        public function get($index)
            {
                return isset($this->creatures[$index]) ? $this->creatures[$index] : null;
            }

        public function getCount()
            {
                return count($this->creatures);
            }

        public function getGeneration()
            {
                return $this->generation;
            }

        abstract protected function populate();

        public function sort($order = SORT_ASC)
            {
                switch($order)
                    {
                        case SORT_ASC:
                            $fn = function($c1, $c2){ return $c1->compareTo($c2); };
                        break;

                        case SORT_DESC:
                            $fn = function($c1, $c2){ return $c2->compareTo($c1); };
                        break;

                        default: return false;
                    }

                return usort($this->creatures, $fn);
            }

        public function select(array $params)
            {
                $result = false;

                if(isset($params['top']))
                    {
                        $length = round(abs($this->getCount() * $params['top']) / 100);

                        $this->creatures = array_slice($this->creatures, 0, $length);

                        $result = true;
                    }

                if(isset($params['fn']) && is_callable($params['fn']))
                    {
                        $this->creatures = array_filter($this->creatures, $params['fn']);

                        $result = true;
                    }

                return $result;
            }

        public function breed()
            {
                $candidates = $this->creatures;

                shuffle($candidates);

                $candidates = array_chunk($candidates, 2);
                $result     = 0;

                foreach($candidates as &$pair)
                    {
                        if(count($pair) < 2)continue;

                        list($mother, $father) = $pair;

                        $children = $mother->mateWith($father);

                        $result += count($children);

                        $this->creatures = array_merge($this->creatures, $children);
                    }

                $this->generation++;

                return $result;
            }
    }

class HWCreature extends Creature
    {
        protected $string;

        protected function randChar()
            {
                return chr(rand(0, 255));
            }

        protected function fill()
            {
                $length = strlen(Algorithm::TARGET);

                for($i = 0; $i < $length; $i++)
                    {
                        $this->string .= $this->randChar();
                    }
            }

        public function __construct($fill = true)
            {
                parent::__construct();

                $this->string = '';

                if(!$fill)return;

                $this->fill();
                $this->calculateFitness();
            }

        public function __toString()
            {
                return $this->string;
            }

        public function calculateFitness()
            {
                $length = strlen($this->string);
                $target = Algorithm::TARGET;

                for($i = 0; $i < $length; $i++)
                    {
                        $this->fitness += abs(ord($this->string[$i]) - ord($target[$i]));
                    }
            }

        public function mateWith($creature)
            {
                $length = strlen(Algorithm::TARGET) - 1;
                $place  = rand(0, $length);

                $child1 = new self(false);
                $child1->string = substr($this->string, 0, $place) . substr($creature->string, $place);
                $child1->mutate();
                $child1->calculateFitness();

                $child2 = new self(false);
                $child2->string = substr($creature->string, 0, $place) . substr($this->string, $place);
                $child2->mutate();
                $child2->calculateFitness();

                return array($child1, $child2);
            }

        public function mutate()
            {
                if(rand(1, 100) > Algorithm::MUTATION_RATE)return;

                $char   = $this->randChar();
                $length = strlen(Algorithm::TARGET);
                $place  = rand(0, $length - 1);

                $this->string = substr_replace($this->string, $char, $place, 1);
            }
    }

class HWPopulation extends Population
    {
        protected function populate()
            {
                for($i = 0; $i < Algorithm::POPULATION_SIZE; $i++)
                    {
                        $this->creatures[] = new HWCreature();
                    }
            }
    }

class Algorithm
    {
        const POPULATION_SIZE   = 100; // 1000 in my original test
        const ELITE_RATE        = 50; // %
        const MUTATION_RATE     = 25; // %
        const MAX_GENERATIONS   = 1000;

        const TARGET    = 'Hello World!';

        protected $population;

        public function __construct()
            {
                $this->population = new HWPopulation();
            }

        public function __destruct()
            {
                unset($this->population);
            }

        public function __invoke()
            {
                do
                    {
                        $generation = $this->population->getGeneration();
                        $representer    = $this->population->get(0);

                        echo sprintf(
                                'gen %d > %s',
                                $generation, $representer
                            ),
                            '<br>',
                            PHP_EOL;

                        if($representer == self::TARGET)break;

                        $selector = array('top' => self::ELITE_RATE);

                        $this->population->sort();
                        $this->population->select($selector);
                        $this->population->breed();
                    }
                while($generation < self::MAX_GENERATIONS);
            }
    }

$algorithm = new Algorithm();
$algorithm();
unset($algorithm);
?>

但是,我在带有i7 @ 2.4 GHz CPU的16Gb RAM机器上的结果是:

...
gen 739 > HfkkoWotlc!
gen 740 > HfkkoWotlc!
gen 741 > HfkkoWotlc!
gen 742 > HfkkoWotlc!
gen 743 > HfkkoWotlc!
gen 744 > HfkkoWotlc!
gen 745 > HfkkoWotlc!

Fatal error: Maximum execution time of 30 seconds exceeded in {script} on line 126

所以,看起来效率极低。我相信,这个问题可能出现在选择或育种策略中......而我在那里完全迷失了。

任何人,请解释一下,为什么会发生这种情况?另外,我只是通过交配精英基因/生物组来做错事吗?

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

至少为了调试/测试,您可能需要长时间运行算法,因此您应该在php.ini中增加max_execution_time的值(或使用set_time_limit函数)。

您的代码中的术语似乎有些混乱。从简短的一瞥看,你似乎没有实行精英主义。你似乎有truncation selection。以这种方式选择父母是错误的吗?嗯,它通常是次优的,因为它完全抛弃了较弱的候选者,虽然它们本身不可行,但可能含有可能有助于最终解决方案的遗传物质。在这个简单的例子中,它可能并不重要,但总的来说,你可能会发现fitness-proportionate selection strategy,例如轮盘赌选择,更有效。这种策略有利于更强大的个体,但允许选择较弱的候选人作为父母。

如果你想实施精英主义,你应该将未经修改的精英候选人复制到下一代,然后通过选择当前整个一代(包括精英个体)的父母来培育那一代的其他人。通过精英主义保留的候选人百分比应该是5%左右(你可以尝试找到最佳比例)。

其他一些观察结果:

  1. 如果降低变异概率,您也可能获得更好的结果。太多的突变会破坏好人。
  2. 你的健身功能似乎关心每个字母的接近程度(即如果第一个字母是'G',那么它会得到一个但是'M'会得分为5)。然而,你的变异函数不太复杂,只是随机替换字母,所以它们的接近程度无关紧要('X'被'H'替换为'G'的机会很大)。将每个字母评分为1(错误)或0(正确)可能更简单,更有效,除非您要更改突变以将字母更改为字母表中相邻的字母。