[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 }
生命不息,学习不止