[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 }

 

posted @ 2018-02-08 17:33  蜗牛噢  阅读(2351)  评论(0编辑  收藏  举报