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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM