需求:求解函數 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 }