• [PHP] 遗传算法求函数最大值一般实现


    需求:求解函数 f(x) = x + 10*sin(5*x) + 7*cos(4*x) 在区间[0,9]的最大值。

      1 <?php
      2 /*
      3     需求:求解函数 f(x) = x + 10*sin(5*x) + 7*cos(4*x) 在区间[0,9]的最大值。
      4 
      5 
      6     以我们的目标函数 f(x) = x + 10sin(5x) + 7cos(4x), x∈[0,9] 为例。
      7     假如设定求解的精度为小数点后4位,可以将x的解空间划分为 (9-0)×(1e+4)=90000个等分。
      8     2^16<90000<2^17,需要17位二进制数来表示这些解。换句话说,一个解的编码就是一个17位的二进制串。
      9     一开始,这些二进制串是随机生成的。
     10     一个这样的二进制串代表一条染色体串,这里染色体串的长度为17。
     11     对于任何一条这样的染色体chromosome,如何将它复原(解码)到[0,9]这个区间中的数值呢?
     12 
     13     对于本问题,我们可以采用以下公式来解码:x = 0 + decimal(chromosome)×(9-0)/(2^17-1)
     14 
     15 */
     16 header("Content-Type:text/html;CharSet=UTF-8");
     17 //初始化参数
     18 $elitism = true;    //是否精英选择
     19 $population_size = 100; //种群大小
     20 $chromosome_size = 17;  //染色体长度
     21 $generation_size = 200; //最大迭代次数
     22 $cross_rate = 0.6;      //交叉概率
     23 $mutate_rate = 0.01;    //变异概率
     24 
     25 
     26 //初始化对象,执行算法
     27 $ga_object = new GAEngine($population_size, $chromosome_size, $generation_size, $cross_rate, $mutate_rate, $elitism);
     28 
     29 $res = $ga_object->run();
     30 
     31 
     32 //打印结果
     33 echo "迭代{$generation_size}代,最佳个体如下<br>";
     34 
     35 echo "染色体:{$res['m']}<br>";
     36 echo "对应的X:{$res['q']}<br>";
     37 echo "结果:{$res['n']}<br>";
     38 echo "最佳个体出现的代:【{$res['p']}】<br>";
     39 
     40 
     41 
     42 
     43 
     44 //遗传算法主要部分
     45 class GAEngine
     46 {
     47     //初始化参数
     48     public $elitism;            //是否精英选择
     49     public $population_size;    //种群大小
     50     public $chromosome_size;    //染色体长度
     51     public $generation_size;    //最大迭代次数
     52     public $cross_rate;         //交叉概率
     53     public $mutate_rate;        //变异概率
     54 
     55     //全局参数
     56     public $G;  //当前迭代次数
     57     public $fitness_value;  //当前代适应度矩阵
     58     public $best_fitness;   //历代最佳适应值
     59     public $fitness_sum;    //前i个个体的适应度之和
     60     public $fitness_average;    //历代平均适应值矩阵
     61     public $best_individual;    //历代最佳个体
     62     public $best_generation;    //最佳个体出现代
     63     public $upper_bound = 9;    //自变量的区间上限
     64     public $lower_bound = 0;    //自变量的区间下限
     65 
     66     //对象
     67     public $population_obj;     //种群对象
     68 
     69 
     70     public $populations;    //种群数组
     71 
     72 
     73     //初始化
     74     public function __construct($population_size, $chromosome_size, $generation_size, $cross_rate, $mutate_rate, $elitism)
     75     {
     76         $this->elitism = $elitism;
     77         $this->population_size = $population_size;
     78         $this->chromosome_size = $chromosome_size;
     79         $this->generation_size = $generation_size;
     80         $this->cross_rate = $cross_rate;
     81         $this->mutate_rate = $mutate_rate;
     82 
     83         //初始化种群
     84         $this->population_obj = new Populations($population_size, $chromosome_size);
     85 
     86     }
     87 
     88     //【执行,返回结果】
     89     public function run(){
     90         //初始化种群
     91         $this->populations = $this->population_obj->initPop();
     92 
     93         //开始迭代
     94         for($i = 1; $i < $this->generation_size; $i ++){
     95             $this->G = $i;
     96             $this->fitness();  //计算适应度
     97             $this->rank();     //对个体按适应度大小进行排序
     98             $this->selection();//选择操作
     99             $this->crossover();    //交叉操作
    100             $this->mutation();     //变异操作
    101         }
    102 
    103 
    104         //最后,求出二进制基因对应的实数x,以及求出实数对应的结果
    105         $x = $this->upper_bound - bindec($this->best_individual)*($this->upper_bound - $this->lower_bound)/(pow(2, $this->chromosome_size) - 1);
    106 
    107         return [
    108             'm' => $this->best_individual,  //最佳个体
    109             'n' => $this->best_fitness,     //最佳适应度
    110             'p' => $this->best_generation,  //最佳个体出现的代
    111             'q' => $x                       //最佳个体基因对应的实数
    112         ];
    113 
    114     }
    115 
    116     //【计算适应度】
    117     public function fitness(){
    118         //所有个体适应度初始化为0
    119         //遍历每个个体的基因,求出对应的实数,然后再计算出结果,即适应度
    120         for($i = 0; $i < $this->population_size; $i ++){
    121 //            $gens = strrev($this->populations[$i]['gens']);//染色体字符串与实际的自变量x二进制串顺序是相反的
    122             $x = $this->upper_bound - bindec($this->populations[$i]['gens'])*($this->upper_bound - $this->lower_bound)/(pow(2, $this->chromosome_size) - 1);
    123 
    124             $fx = $x + 10*sin(5*$x) + 7*cos(4*$x);//函数值
    125 
    126             $this->fitness_value[$i] = $fx;
    127 
    128         }
    129     }
    130 
    131     //【排序】
    132     //对个体按适应度的大小进行排序,并且保存最佳个体
    133     public function rank(){
    134         //$this->fitness_value[] 保存适应度的数组,根据此数组进行排序,还要修改对应个体的位置
    135         //冒泡,从小到大排序
    136         for($i = 0; $i < $this->population_size -1; $i ++){
    137             for($j = $i + 1; $j < $this->population_size; $j ++){
    138                 if($this->fitness_value[$i] > $this->fitness_value[$j]){
    139                     //交换适应度
    140                     $tmp = $this->fitness_value[$i];
    141                     $this->fitness_value[$i] = $this->fitness_value[$j];
    142                     $this->fitness_value[$j] = $tmp;
    143 
    144                     //交换种群个体位置
    145                     $tmp = $this->populations[$i];
    146                     $this->populations[$i] = $this->populations[$j];
    147                     $this->populations[$j] = $tmp;
    148                 }
    149             }
    150         }
    151 
    152         //计算前i个给个体的适应度之和
    153         $fitness_sum[0] = $this->fitness_value[0];
    154         for($i = 1; $i < $this->population_size; $i ++){
    155             $fitness_sum[$i] = $fitness_sum[$i - 1] +$this->fitness_value[$i];
    156         }
    157         $this->fitness_sum = $fitness_sum;
    158 
    159         //第G代迭代, 个体的平均适应度
    160         $this->fitness_average[$this->G] = ($fitness_sum[$this->population_size - 1] / $this->population_size);
    161 
    162         //更新最大适应度和对应的迭代次数,保存最佳个体(最佳个体适应度最大)
    163         if($this->fitness_value[$this->population_size - 1] > $this->best_fitness){
    164             $this->best_fitness = $this->fitness_value[$this->population_size - 1];
    165             $this->best_generation = $this->G;
    166             $this->best_individual = $this->populations[$this->population_size -1]['gens'];
    167         }
    168 
    169     }
    170 
    171     //【选择】
    172     public function selection(){
    173         $population_new = [];//保存被选中的个体
    174 
    175         //二分查找实现轮盘赌功能
    176         for($i = 0; $i < $this->population_size; $i ++){
    177             $r = (rand(0,10)/10 ) * $this->fitness_sum[$this->population_size - 1];//生成一个随机数,在[0,总适应度] 之间
    178             $first = 1;
    179             $last = $this->population_size;
    180             $mid = round( ($last + $first) / 2 );
    181             $idx = -1;
    182 
    183 
    184             //排中法选择个体
    185             while(($first <= $last) && ($idx == -1)){
    186                 if($r > $this->fitness_sum[$mid]){
    187                     $first = $mid;
    188                 }elseif ( $r < $this->fitness_sum[$mid]){
    189                     $last = $mid;
    190                 }else{
    191                     $idx = $mid;
    192                     break;
    193                 }
    194                 $mid = round( ($last + $first) / 2 );
    195                 if(($last - $first) == 1){
    196                     $idx = $last;
    197                     break;
    198                 }
    199 
    200             }
    201 
    202             //产生新的个体
    203             //echo $idx.'=';
    204             $population_new[$i]['gens'] = $this->populations[$idx]['gens'];
    205         }
    206 
    207         //是否精英选择
    208         if($this->elitism){
    209             $p = $this->population_size - 1;
    210         }else{
    211             $p = $this->population_size;
    212         }
    213 
    214         for($i = 0; $i < $p; $i ++){
    215             if(isset($population_new[$i]))
    216                 $this->populations[$i] = $population_new[$i];
    217         }
    218 
    219     }
    220 
    221     //【交叉】
    222     public function crossover(){
    223         //步长为2, 遍历种群
    224         for($i = 0; $i < $this->population_size; $i+=2){
    225             //rand < 交叉概率,对2个个体的染色体进行交叉操作
    226             $r = $this->randFloat();//产生0~1的随机数
    227             if($r < $this->cross_rate){
    228 //                $r =$this->randFloat();
    229 //                $cross_pos = round($r * $this->chromosome_size);
    230                 $cross_pos = rand(0, $this->chromosome_size);
    231                 if($cross_pos ==0 || $cross_pos == $this->chromosome_size)
    232                     continue;
    233                 //对 cross_pos及之后的二进制串进行交换
    234                 $x = $this->populations[$i]['gens'];
    235                 $y =  $this->populations[$i+1]['gens'];
    236                 $tmp1 = substr($x,0, $cross_pos).substr($y,$cross_pos);
    237                 $tmp2 = substr($y,0,$cross_pos).substr($x, $cross_pos);
    238 
    239                 $this->populations[$i]['gens'] = $tmp1;
    240                 $this->populations[$i+1]['gens'] = $tmp2;
    241             }
    242         }
    243     }
    244 
    245     //【变异】
    246     public function mutation(){
    247         for($i =0; $i < $this->population_size; $i ++){
    248            if($this->randFloat() < $this->mutate_rate){
    249                $mutate_pos = rand(0,$this->chromosome_size -1);
    250                $this->populations[$i]['gens'][$mutate_pos] = 1 - $this->populations[$i]['gens'][$mutate_pos];
    251            }
    252 
    253         }
    254     }
    255 
    256 
    257 
    258     public function show($data){
    259         echo '<pre>';
    260         var_dump($data);
    261         echo '<hr>';
    262     }
    263 
    264     //随机产生0-1的小数
    265     function randFloat($min=0, $max=1){
    266         return $min + mt_rand()/mt_getrandmax() * ($max-$min);
    267     }
    268 
    269 }
    270 
    271 //种群
    272 class Populations
    273 {
    274     public $population_size;//种群大小
    275     public $chromosome_size;//染色体长度
    276 
    277     //初始化参数
    278     public function __construct($population_size, $chromosome_size)
    279     {
    280         $this->population_size = $population_size;
    281         $this->chromosome_size = $chromosome_size;
    282     }
    283 
    284     //初始化种群
    285     public function initPop(){
    286         $pop = [];
    287         for($i = 0; $i < $this->population_size; $i ++){
    288             for($j = 0; $j < $this->chromosome_size; $j ++){
    289                 $pop[$i]['gens'] .=  rand(0, 10) % 2;//产生1-0随机数
    290             }
    291         }
    292 
    293         return $pop;
    294     }
    295 
    296 }

    参考:

      知乎:https://www.zhihu.com/question/23293449

      MATLAB的实现GitHub地址:https://github.com/yanshengjia/artificial-intelligence/tree/master/genetic-algorithm-for-functional-maximum-problem

    附录:把个体单独抽出来放到一个类中

      1 <?php
      2 /*
      3     需求:求解函数 f(x) = x + 10*sin(5*x) + 7*cos(4*x) 在区间[0,9]的最大值。
      4 
      5 
      6     以我们的目标函数 f(x) = x + 10sin(5x) + 7cos(4x), x∈[0,9] 为例。
      7     假如设定求解的精度为小数点后4位,可以将x的解空间划分为 (9-0)×(1e+4)=90000个等分。
      8     2^16<90000<2^17,需要17位二进制数来表示这些解。换句话说,一个解的编码就是一个17位的二进制串。
      9     一开始,这些二进制串是随机生成的。
     10     一个这样的二进制串代表一条染色体串,这里染色体串的长度为17。
     11     对于任何一条这样的染色体chromosome,如何将它复原(解码)到[0,9]这个区间中的数值呢?
     12 
     13     对于本问题,我们可以采用以下公式来解码:x = 0 + decimal(chromosome)×(9-0)/(2^17-1)
     14 
     15 */
     16 header("Content-Type:text/html;CharSet=UTF-8");
     17 //初始化参数
     18 $elitism = true;    //是否精英选择
     19 $population_size = 100; //种群大小
     20 $chromosome_size = 17;  //染色体长度
     21 $generation_size = 200; //最大迭代次数
     22 $cross_rate = 0.6;      //交叉概率
     23 $mutate_rate = 0.01;    //变异概率
     24 
     25 
     26 //初始化对象,执行算法
     27 $ga_object = new GAEngine($population_size, $chromosome_size, $generation_size, $cross_rate, $mutate_rate, $elitism);
     28 
     29 $res = $ga_object->run();
     30 
     31 
     32 //打印结果
     33 echo "迭代{$generation_size}代,最佳个体如下<br>";
     34 
     35 echo "染色体:{$res['m']}<br>";
     36 echo "对应的X:{$res['q']}<br>";
     37 echo "结果:{$res['n']}<br>";
     38 echo "最佳个体出现的代:【{$res['p']}】<br>";
     39 
     40 
     41 
     42 
     43 
     44 //遗传算法主要部分
     45 class GAEngine
     46 {
     47     //初始化参数
     48     public $elitism;            //是否精英选择
     49     public $population_size;    //种群大小
     50     public $chromosome_size;    //染色体长度
     51     public $generation_size;    //最大迭代次数
     52     public $cross_rate;         //交叉概率
     53     public $mutate_rate;        //变异概率
     54 
     55     //全局参数
     56     public $G;  //当前迭代次数
     57     public $fitness_value;  //当前代适应度矩阵
     58     public $best_fitness;   //历代最佳适应值
     59     public $fitness_sum;    //前i个个体的适应度之和
     60     public $fitness_average;    //历代平均适应值矩阵
     61     public $best_individual;    //历代最佳个体
     62     public $best_generation;    //最佳个体出现代
     63     public $upper_bound = 9;    //自变量的区间上限
     64     public $lower_bound = 0;    //自变量的区间下限
     65 
     66     //对象
     67     public $population_obj;     //种群对象
     68 
     69 
     70     public $populations;    //种群数组
     71 
     72 
     73     //初始化
     74     public function __construct($population_size, $chromosome_size, $generation_size, $cross_rate, $mutate_rate, $elitism)
     75     {
     76         $this->elitism = $elitism;
     77         $this->population_size = $population_size;
     78         $this->chromosome_size = $chromosome_size;
     79         $this->generation_size = $generation_size;
     80         $this->cross_rate = $cross_rate;
     81         $this->mutate_rate = $mutate_rate;
     82 
     83         //初始化种群
     84         $this->population_obj = new Populations($population_size, $chromosome_size);
     85 
     86     }
     87 
     88     //【执行,返回结果】
     89     public function run(){
     90         //初始化种群
     91         $this->populations = $this->population_obj->initPop();
     92 
     93         //开始迭代
     94         for($i = 1; $i <= $this->generation_size; $i ++){
     95             $this->G = $i;
     96             $this->fitness();  //计算适应度
     97             $this->rank();     //对个体按适应度大小进行排序
     98             $this->selection();//选择操作
     99             $this->crossover();    //交叉操作
    100             $this->mutation();     //变异操作
    101         }
    102 
    103 
    104         //最后,求出二进制基因对应的实数x,以及求出实数对应的结果
    105         $x = $this->upper_bound - bindec($this->best_individual)*($this->upper_bound - $this->lower_bound)/(pow(2, $this->chromosome_size) - 1);
    106 
    107         return [
    108             'm' => $this->best_individual,  //最佳个体
    109             'n' => $this->best_fitness,     //最佳适应度
    110             'p' => $this->best_generation,  //最佳个体出现的代
    111             'q' => $x                       //最佳个体基因对应的实数
    112         ];
    113 
    114     }
    115 
    116     //【计算适应度】
    117     public function fitness(){
    118         //所有个体适应度初始化为0
    119         //遍历每个个体的基因,求出对应的实数,然后再计算出结果,即适应度
    120         for($i = 0; $i < $this->population_size; $i ++){
    121 //            $gens = strrev($this->populations[$i]['gens']);//染色体字符串与实际的自变量x二进制串顺序是相反的
    122             $x = $this->upper_bound - bindec($this->populations[$i]->chromosomes)*($this->upper_bound - $this->lower_bound)/(pow(2, $this->chromosome_size) - 1);
    123 
    124             $fx = $x + 10*sin(5*$x) + 7*cos(4*$x);//函数值
    125 
    126 //            $this->fitness_value[$i] = $fx;
    127             $this->populations[$i]->fitness = $fx;
    128 
    129         }
    130     }
    131 
    132     //【排序】
    133     //对个体按适应度的大小进行排序,并且保存最佳个体
    134     public function rank(){
    135         //冒泡,从小到大排序
    136         for($i = 0; $i < $this->population_size -1; $i ++){
    137             for($j = $i + 1; $j < $this->population_size; $j ++){
    138                 if($this->populations[$i]->fitness > $this->populations[$j]->fitness){
    139                     //交换个体
    140                     $tmp = $this->populations[$i];
    141                     $this->populations[$i] = $this->populations[$j];
    142                     $this->populations[$j] = $tmp;
    143                 }
    144             }
    145         }
    146 
    147         //计算前i个给个体的适应度之和
    148         $fitness_sum[0] = $this->populations[0]->fitness;
    149         for($i = 1; $i < $this->population_size; $i ++){
    150             $fitness_sum[$i] = $fitness_sum[$i - 1] + $this->populations[$i]->fitness;
    151         }
    152         $this->fitness_sum = $fitness_sum;
    153 
    154         //第G代迭代, 个体的平均适应度
    155         $this->fitness_average[$this->G] = ($fitness_sum[$this->population_size - 1] / $this->population_size);
    156 
    157         //更新最大适应度和对应的迭代次数,保存最佳个体(最佳个体适应度最大)
    158         if($this->populations[$this->population_size - 1]->fitness > $this->best_fitness){
    159             $this->best_fitness = $this->populations[$this->population_size - 1]->fitness;
    160             $this->best_generation = $this->G;
    161             $this->best_individual = $this->populations[$this->population_size -1]->chromosomes;
    162         }
    163 
    164     }
    165 
    166     //【选择】
    167     public function selection(){
    168         $population_new = [];//保存被选中的个体
    169 
    170         //二分查找实现轮盘赌功能
    171         for($i = 0; $i < $this->population_size; $i ++){
    172             $r = (rand(0,10)/10 ) * $this->fitness_sum[$this->population_size - 1];//生成一个随机数,在[0,总适应度] 之间
    173             $first = 1;
    174             $last = $this->population_size;
    175             $mid = round( ($last + $first) / 2 );
    176             $idx = -1;
    177 
    178 
    179             //排中法选择个体
    180             while(($first <= $last) && ($idx == -1)){
    181                 if($r > $this->fitness_sum[$mid]){
    182                     $first = $mid;
    183                 }elseif ( $r < $this->fitness_sum[$mid]){
    184                     $last = $mid;
    185                 }else{
    186                     $idx = $mid;
    187                     break;
    188                 }
    189                 $mid = round( ($last + $first) / 2 );
    190                 if(($last - $first) == 1){
    191                     $idx = $last;
    192                     break;
    193                 }
    194 
    195             }
    196 
    197             //产生新的个体
    198             //echo $idx.'=';
    199             $population_new[$i] = $this->populations[$idx];
    200         }
    201 
    202         //是否精英选择
    203         if($this->elitism){
    204             $p = $this->population_size - 1;
    205         }else{
    206             $p = $this->population_size;
    207         }
    208 
    209         for($i = 0; $i < $p; $i ++){
    210             if(isset($population_new[$i]))
    211                 $this->populations[$i] = $population_new[$i];
    212         }
    213 
    214     }
    215 
    216     //【交叉】
    217     public function crossover(){
    218         //步长为2, 遍历种群
    219         for($i = 0; $i < $this->population_size; $i+=2){
    220             //rand < 交叉概率,对2个个体的染色体进行交叉操作
    221             $r = $this->randFloat();//产生0~1的随机数
    222             if($r < $this->cross_rate){
    223 //                $r =$this->randFloat();
    224 //                $cross_pos = round($r * $this->chromosome_size);
    225                 $cross_pos = rand(0, $this->chromosome_size);
    226                 if($cross_pos ==0 || $cross_pos == $this->chromosome_size)
    227                     continue;
    228                 //对 cross_pos及之后的二进制串进行交换
    229                 $x = $this->populations[$i]->chromosomes;
    230                 $y =  $this->populations[$i+1]->chromosomes;
    231                 $tmp1 = substr($x,0, $cross_pos).substr($y,$cross_pos);
    232                 $tmp2 = substr($y,0,$cross_pos).substr($x, $cross_pos);
    233 
    234                 $this->populations[$i]->chromosomes = $tmp1;
    235                 $this->populations[$i+1]->chromosomes = $tmp2;
    236             }
    237         }
    238     }
    239 
    240     //【变异】
    241     public function mutation(){
    242         for($i =0; $i < $this->population_size; $i ++){
    243            if($this->randFloat() < $this->mutate_rate){
    244                $mutate_pos = rand(0,$this->chromosome_size -1);
    245                $this->populations[$i]->chromosomes[$mutate_pos] = 1 - $this->populations[$i]->chromosomes[$mutate_pos];
    246            }
    247 
    248         }
    249     }
    250 
    251 
    252 
    253     public function show($data){
    254         echo '<pre>';
    255         var_dump($data);
    256         echo '<hr>';
    257     }
    258 
    259     //随机产生0-1的小数
    260     function randFloat($min=0, $max=1){
    261         return $min + mt_rand()/mt_getrandmax() * ($max-$min);
    262     }
    263 
    264 }
    265 
    266 //种群
    267 class Populations
    268 {
    269     public $population_size;//种群大小
    270     public $chromosome_size;//染色体长度
    271 
    272     //初始化参数
    273     public function __construct($population_size, $chromosome_size)
    274     {
    275         $this->population_size = $population_size;
    276         $this->chromosome_size = $chromosome_size;
    277     }
    278 
    279     //初始化种群
    280     public function initPop(){
    281         $pop = [];
    282         for($i = 0; $i < $this->population_size; $i ++){
    283             $pop[] = new Individuals($this->chromosome_size);
    284         }
    285 
    286         return $pop;
    287     }
    288 }
    289 
    290 //个体
    291 class Individuals
    292 {
    293     public $chromosomes;    //染色体
    294     public $fitness;        //适应度
    295     public $value;          //实数
    296 
    297     public $chromosome_size;
    298 
    299     public function __construct($chromosome_size)
    300     {
    301         $this->chromosome_size = $chromosome_size;
    302 
    303         for($j = 0; $j < $this->chromosome_size; $j ++){
    304             $this->chromosomes .=  rand(0, 10) % 2;//产生1-0随机数
    305         }
    306     }
    307 
    308 
    309 }
  • 相关阅读:
    JVMTIAgent
    Java 虚拟机编程接口JVMIT
    Java调试平台体系JPDA
    什么是缓存
    2019第49周日
    什么是你拥有的资本
    java里的static/final含义
    Java语言和JVM的使用说明书
    画图前端:mermaid。时序图/类图/甘特图/流程图/状态图/饼图。类似工具:Typora
    java通过下划线数字字面量增加可读性:10_00_00表示100000
  • 原文地址:https://www.cnblogs.com/reader/p/8432499.html
Copyright © 2020-2023  润新知