[PHP] 遗传算法求函数最大值一般实现
需求:求解函数 f(x) = x + 10*sin(5*x) + 7*cos(4*x) 在区间[0,9]的最大值。
<?php
/*
需求:求解函数 f(x) = x + 10*sin(5*x) + 7*cos(4*x) 在区间[0,9]的最大值。 以我们的目标函数 f(x) = x + 10sin(5x) + 7cos(4x), x∈[0,9] 为例。
假如设定求解的精度为小数点后4位,可以将x的解空间划分为 (9-0)×(1e+4)=90000个等分。
2^16<90000<2^17,需要17位二进制数来表示这些解。换句话说,一个解的编码就是一个17位的二进制串。
一开始,这些二进制串是随机生成的。
一个这样的二进制串代表一条染色体串,这里染色体串的长度为17。
对于任何一条这样的染色体chromosome,如何将它复原(解码)到[0,9]这个区间中的数值呢? 对于本问题,我们可以采用以下公式来解码:x = 0 + decimal(chromosome)×(9-0)/(2^17-1) */
header("Content-Type:text/html;CharSet=UTF-8");
//初始化参数
$elitism = true; //是否精英选择
$population_size = 100; //种群大小
$chromosome_size = 17; //染色体长度
$generation_size = 200; //最大迭代次数
$cross_rate = 0.6; //交叉概率
$mutate_rate = 0.01; //变异概率 //初始化对象,执行算法
$ga_object = new GAEngine($population_size, $chromosome_size, $generation_size, $cross_rate, $mutate_rate, $elitism); $res = $ga_object->run(); //打印结果
echo "迭代{$generation_size}代,最佳个体如下<br>"; echo "染色体:{$res['m']}<br>";
echo "对应的X:{$res['q']}<br>";
echo "结果:{$res['n']}<br>";
echo "最佳个体出现的代:【{$res['p']}】<br>"; //遗传算法主要部分
class GAEngine
{
//初始化参数
public $elitism; //是否精英选择
public $population_size; //种群大小
public $chromosome_size; //染色体长度
public $generation_size; //最大迭代次数
public $cross_rate; //交叉概率
public $mutate_rate; //变异概率 //全局参数
public $G; //当前迭代次数
public $fitness_value; //当前代适应度矩阵
public $best_fitness; //历代最佳适应值
public $fitness_sum; //前i个个体的适应度之和
public $fitness_average; //历代平均适应值矩阵
public $best_individual; //历代最佳个体
public $best_generation; //最佳个体出现代
public $upper_bound = 9; //自变量的区间上限
public $lower_bound = 0; //自变量的区间下限 //对象
public $population_obj; //种群对象 public $populations; //种群数组 //初始化
public function __construct($population_size, $chromosome_size, $generation_size, $cross_rate, $mutate_rate, $elitism)
{
$this->elitism = $elitism;
$this->population_size = $population_size;
$this->chromosome_size = $chromosome_size;
$this->generation_size = $generation_size;
$this->cross_rate = $cross_rate;
$this->mutate_rate = $mutate_rate; //初始化种群
$this->population_obj = new Populations($population_size, $chromosome_size); } //【执行,返回结果】
public function run(){
//初始化种群
$this->populations = $this->population_obj->initPop(); //开始迭代
for($i = 1; $i < $this->generation_size; $i ++){
$this->G = $i;
$this->fitness(); //计算适应度
$this->rank(); //对个体按适应度大小进行排序
$this->selection();//选择操作
$this->crossover(); //交叉操作
$this->mutation(); //变异操作
} //最后,求出二进制基因对应的实数x,以及求出实数对应的结果
$x = $this->upper_bound - bindec($this->best_individual)*($this->upper_bound - $this->lower_bound)/(pow(2, $this->chromosome_size) - 1); return [
'm' => $this->best_individual, //最佳个体
'n' => $this->best_fitness, //最佳适应度
'p' => $this->best_generation, //最佳个体出现的代
'q' => $x //最佳个体基因对应的实数
]; } //【计算适应度】
public function fitness(){
//所有个体适应度初始化为0
//遍历每个个体的基因,求出对应的实数,然后再计算出结果,即适应度
for($i = 0; $i < $this->population_size; $i ++){
// $gens = strrev($this->populations[$i]['gens']);//染色体字符串与实际的自变量x二进制串顺序是相反的
$x = $this->upper_bound - bindec($this->populations[$i]['gens'])*($this->upper_bound - $this->lower_bound)/(pow(2, $this->chromosome_size) - 1); $fx = $x + 10*sin(5*$x) + 7*cos(4*$x);//函数值 $this->fitness_value[$i] = $fx; }
} //【排序】
//对个体按适应度的大小进行排序,并且保存最佳个体
public function rank(){
//$this->fitness_value[] 保存适应度的数组,根据此数组进行排序,还要修改对应个体的位置
//冒泡,从小到大排序
for($i = 0; $i < $this->population_size -1; $i ++){
for($j = $i + 1; $j < $this->population_size; $j ++){
if($this->fitness_value[$i] > $this->fitness_value[$j]){
//交换适应度
$tmp = $this->fitness_value[$i];
$this->fitness_value[$i] = $this->fitness_value[$j];
$this->fitness_value[$j] = $tmp; //交换种群个体位置
$tmp = $this->populations[$i];
$this->populations[$i] = $this->populations[$j];
$this->populations[$j] = $tmp;
}
}
} //计算前i个给个体的适应度之和
$fitness_sum[0] = $this->fitness_value[0];
for($i = 1; $i < $this->population_size; $i ++){
$fitness_sum[$i] = $fitness_sum[$i - 1] +$this->fitness_value[$i];
}
$this->fitness_sum = $fitness_sum; //第G代迭代, 个体的平均适应度
$this->fitness_average[$this->G] = ($fitness_sum[$this->population_size - 1] / $this->population_size); //更新最大适应度和对应的迭代次数,保存最佳个体(最佳个体适应度最大)
if($this->fitness_value[$this->population_size - 1] > $this->best_fitness){
$this->best_fitness = $this->fitness_value[$this->population_size - 1];
$this->best_generation = $this->G;
$this->best_individual = $this->populations[$this->population_size -1]['gens'];
} } //【选择】
public function selection(){
$population_new = [];//保存被选中的个体 //二分查找实现轮盘赌功能
for($i = 0; $i < $this->population_size; $i ++){
$r = (rand(0,10)/10 ) * $this->fitness_sum[$this->population_size - 1];//生成一个随机数,在[0,总适应度] 之间
$first = 1;
$last = $this->population_size;
$mid = round( ($last + $first) / 2 );
$idx = -1; //排中法选择个体
while(($first <= $last) && ($idx == -1)){
if($r > $this->fitness_sum[$mid]){
$first = $mid;
}elseif ( $r < $this->fitness_sum[$mid]){
$last = $mid;
}else{
$idx = $mid;
break;
}
$mid = round( ($last + $first) / 2 );
if(($last - $first) == 1){
$idx = $last;
break;
} } //产生新的个体
//echo $idx.'=';
$population_new[$i]['gens'] = $this->populations[$idx]['gens'];
} //是否精英选择
if($this->elitism){
$p = $this->population_size - 1;
}else{
$p = $this->population_size;
} for($i = 0; $i < $p; $i ++){
if(isset($population_new[$i]))
$this->populations[$i] = $population_new[$i];
} } //【交叉】
public function crossover(){
//步长为2, 遍历种群
for($i = 0; $i < $this->population_size; $i+=2){
//rand < 交叉概率,对2个个体的染色体进行交叉操作
$r = $this->randFloat();//产生0~1的随机数
if($r < $this->cross_rate){
// $r =$this->randFloat();
// $cross_pos = round($r * $this->chromosome_size);
$cross_pos = rand(0, $this->chromosome_size);
if($cross_pos ==0 || $cross_pos == $this->chromosome_size)
continue;
//对 cross_pos及之后的二进制串进行交换
$x = $this->populations[$i]['gens'];
$y = $this->populations[$i+1]['gens'];
$tmp1 = substr($x,0, $cross_pos).substr($y,$cross_pos);
$tmp2 = substr($y,0,$cross_pos).substr($x, $cross_pos); $this->populations[$i]['gens'] = $tmp1;
$this->populations[$i+1]['gens'] = $tmp2;
}
}
} //【变异】
public function mutation(){
for($i =0; $i < $this->population_size; $i ++){
if($this->randFloat() < $this->mutate_rate){
$mutate_pos = rand(0,$this->chromosome_size -1);
$this->populations[$i]['gens'][$mutate_pos] = 1 - $this->populations[$i]['gens'][$mutate_pos];
} }
} public function show($data){
echo '<pre>';
var_dump($data);
echo '<hr>';
} //随机产生0-1的小数
function randFloat($min=0, $max=1){
return $min + mt_rand()/mt_getrandmax() * ($max-$min);
} } //种群
class Populations
{
public $population_size;//种群大小
public $chromosome_size;//染色体长度 //初始化参数
public function __construct($population_size, $chromosome_size)
{
$this->population_size = $population_size;
$this->chromosome_size = $chromosome_size;
} //初始化种群
public function initPop(){
$pop = [];
for($i = 0; $i < $this->population_size; $i ++){
for($j = 0; $j < $this->chromosome_size; $j ++){
$pop[$i]['gens'] .= rand(0, 10) % 2;//产生1-0随机数
}
} return $pop;
} }
参考:
知乎:https://www.zhihu.com/question/23293449
MATLAB的实现GitHub地址:https://github.com/yanshengjia/artificial-intelligence/tree/master/genetic-algorithm-for-functional-maximum-problem
附录:把个体单独抽出来放到一个类中
<?php
/*
需求:求解函数 f(x) = x + 10*sin(5*x) + 7*cos(4*x) 在区间[0,9]的最大值。 以我们的目标函数 f(x) = x + 10sin(5x) + 7cos(4x), x∈[0,9] 为例。
假如设定求解的精度为小数点后4位,可以将x的解空间划分为 (9-0)×(1e+4)=90000个等分。
2^16<90000<2^17,需要17位二进制数来表示这些解。换句话说,一个解的编码就是一个17位的二进制串。
一开始,这些二进制串是随机生成的。
一个这样的二进制串代表一条染色体串,这里染色体串的长度为17。
对于任何一条这样的染色体chromosome,如何将它复原(解码)到[0,9]这个区间中的数值呢? 对于本问题,我们可以采用以下公式来解码:x = 0 + decimal(chromosome)×(9-0)/(2^17-1) */
header("Content-Type:text/html;CharSet=UTF-8");
//初始化参数
$elitism = true; //是否精英选择
$population_size = 100; //种群大小
$chromosome_size = 17; //染色体长度
$generation_size = 200; //最大迭代次数
$cross_rate = 0.6; //交叉概率
$mutate_rate = 0.01; //变异概率 //初始化对象,执行算法
$ga_object = new GAEngine($population_size, $chromosome_size, $generation_size, $cross_rate, $mutate_rate, $elitism); $res = $ga_object->run(); //打印结果
echo "迭代{$generation_size}代,最佳个体如下<br>"; echo "染色体:{$res['m']}<br>";
echo "对应的X:{$res['q']}<br>";
echo "结果:{$res['n']}<br>";
echo "最佳个体出现的代:【{$res['p']}】<br>"; //遗传算法主要部分
class GAEngine
{
//初始化参数
public $elitism; //是否精英选择
public $population_size; //种群大小
public $chromosome_size; //染色体长度
public $generation_size; //最大迭代次数
public $cross_rate; //交叉概率
public $mutate_rate; //变异概率 //全局参数
public $G; //当前迭代次数
public $fitness_value; //当前代适应度矩阵
public $best_fitness; //历代最佳适应值
public $fitness_sum; //前i个个体的适应度之和
public $fitness_average; //历代平均适应值矩阵
public $best_individual; //历代最佳个体
public $best_generation; //最佳个体出现代
public $upper_bound = 9; //自变量的区间上限
public $lower_bound = 0; //自变量的区间下限 //对象
public $population_obj; //种群对象 public $populations; //种群数组 //初始化
public function __construct($population_size, $chromosome_size, $generation_size, $cross_rate, $mutate_rate, $elitism)
{
$this->elitism = $elitism;
$this->population_size = $population_size;
$this->chromosome_size = $chromosome_size;
$this->generation_size = $generation_size;
$this->cross_rate = $cross_rate;
$this->mutate_rate = $mutate_rate; //初始化种群
$this->population_obj = new Populations($population_size, $chromosome_size); } //【执行,返回结果】
public function run(){
//初始化种群
$this->populations = $this->population_obj->initPop(); //开始迭代
for($i = 1; $i <= $this->generation_size; $i ++){
$this->G = $i;
$this->fitness(); //计算适应度
$this->rank(); //对个体按适应度大小进行排序
$this->selection();//选择操作
$this->crossover(); //交叉操作
$this->mutation(); //变异操作
} //最后,求出二进制基因对应的实数x,以及求出实数对应的结果
$x = $this->upper_bound - bindec($this->best_individual)*($this->upper_bound - $this->lower_bound)/(pow(2, $this->chromosome_size) - 1); return [
'm' => $this->best_individual, //最佳个体
'n' => $this->best_fitness, //最佳适应度
'p' => $this->best_generation, //最佳个体出现的代
'q' => $x //最佳个体基因对应的实数
]; } //【计算适应度】
public function fitness(){
//所有个体适应度初始化为0
//遍历每个个体的基因,求出对应的实数,然后再计算出结果,即适应度
for($i = 0; $i < $this->population_size; $i ++){
// $gens = strrev($this->populations[$i]['gens']);//染色体字符串与实际的自变量x二进制串顺序是相反的
$x = $this->upper_bound - bindec($this->populations[$i]->chromosomes)*($this->upper_bound - $this->lower_bound)/(pow(2, $this->chromosome_size) - 1); $fx = $x + 10*sin(5*$x) + 7*cos(4*$x);//函数值 // $this->fitness_value[$i] = $fx;
$this->populations[$i]->fitness = $fx; }
} //【排序】
//对个体按适应度的大小进行排序,并且保存最佳个体
public function rank(){
//冒泡,从小到大排序
for($i = 0; $i < $this->population_size -1; $i ++){
for($j = $i + 1; $j < $this->population_size; $j ++){
if($this->populations[$i]->fitness > $this->populations[$j]->fitness){
//交换个体
$tmp = $this->populations[$i];
$this->populations[$i] = $this->populations[$j];
$this->populations[$j] = $tmp;
}
}
} //计算前i个给个体的适应度之和
$fitness_sum[0] = $this->populations[0]->fitness;
for($i = 1; $i < $this->population_size; $i ++){
$fitness_sum[$i] = $fitness_sum[$i - 1] + $this->populations[$i]->fitness;
}
$this->fitness_sum = $fitness_sum; //第G代迭代, 个体的平均适应度
$this->fitness_average[$this->G] = ($fitness_sum[$this->population_size - 1] / $this->population_size); //更新最大适应度和对应的迭代次数,保存最佳个体(最佳个体适应度最大)
if($this->populations[$this->population_size - 1]->fitness > $this->best_fitness){
$this->best_fitness = $this->populations[$this->population_size - 1]->fitness;
$this->best_generation = $this->G;
$this->best_individual = $this->populations[$this->population_size -1]->chromosomes;
} } //【选择】
public function selection(){
$population_new = [];//保存被选中的个体 //二分查找实现轮盘赌功能
for($i = 0; $i < $this->population_size; $i ++){
$r = (rand(0,10)/10 ) * $this->fitness_sum[$this->population_size - 1];//生成一个随机数,在[0,总适应度] 之间
$first = 1;
$last = $this->population_size;
$mid = round( ($last + $first) / 2 );
$idx = -1; //排中法选择个体
while(($first <= $last) && ($idx == -1)){
if($r > $this->fitness_sum[$mid]){
$first = $mid;
}elseif ( $r < $this->fitness_sum[$mid]){
$last = $mid;
}else{
$idx = $mid;
break;
}
$mid = round( ($last + $first) / 2 );
if(($last - $first) == 1){
$idx = $last;
break;
} } //产生新的个体
//echo $idx.'=';
$population_new[$i] = $this->populations[$idx];
} //是否精英选择
if($this->elitism){
$p = $this->population_size - 1;
}else{
$p = $this->population_size;
} for($i = 0; $i < $p; $i ++){
if(isset($population_new[$i]))
$this->populations[$i] = $population_new[$i];
} } //【交叉】
public function crossover(){
//步长为2, 遍历种群
for($i = 0; $i < $this->population_size; $i+=2){
//rand < 交叉概率,对2个个体的染色体进行交叉操作
$r = $this->randFloat();//产生0~1的随机数
if($r < $this->cross_rate){
// $r =$this->randFloat();
// $cross_pos = round($r * $this->chromosome_size);
$cross_pos = rand(0, $this->chromosome_size);
if($cross_pos ==0 || $cross_pos == $this->chromosome_size)
continue;
//对 cross_pos及之后的二进制串进行交换
$x = $this->populations[$i]->chromosomes;
$y = $this->populations[$i+1]->chromosomes;
$tmp1 = substr($x,0, $cross_pos).substr($y,$cross_pos);
$tmp2 = substr($y,0,$cross_pos).substr($x, $cross_pos); $this->populations[$i]->chromosomes = $tmp1;
$this->populations[$i+1]->chromosomes = $tmp2;
}
}
} //【变异】
public function mutation(){
for($i =0; $i < $this->population_size; $i ++){
if($this->randFloat() < $this->mutate_rate){
$mutate_pos = rand(0,$this->chromosome_size -1);
$this->populations[$i]->chromosomes[$mutate_pos] = 1 - $this->populations[$i]->chromosomes[$mutate_pos];
} }
} public function show($data){
echo '<pre>';
var_dump($data);
echo '<hr>';
} //随机产生0-1的小数
function randFloat($min=0, $max=1){
return $min + mt_rand()/mt_getrandmax() * ($max-$min);
} } //种群
class Populations
{
public $population_size;//种群大小
public $chromosome_size;//染色体长度 //初始化参数
public function __construct($population_size, $chromosome_size)
{
$this->population_size = $population_size;
$this->chromosome_size = $chromosome_size;
} //初始化种群
public function initPop(){
$pop = [];
for($i = 0; $i < $this->population_size; $i ++){
$pop[] = new Individuals($this->chromosome_size);
} return $pop;
}
} //个体
class Individuals
{
public $chromosomes; //染色体
public $fitness; //适应度
public $value; //实数 public $chromosome_size; public function __construct($chromosome_size)
{
$this->chromosome_size = $chromosome_size; for($j = 0; $j < $this->chromosome_size; $j ++){
$this->chromosomes .= rand(0, 10) % 2;//产生1-0随机数
}
} }
[PHP] 遗传算法求函数最大值一般实现的更多相关文章
- hdu 5105 求函数极值 函数求导/三分法
http://acm.hdu.edu.cn/showproblem.php?pid=5105 给定a,b,c,d,l,r,表示有一个函数f(x)=|a∗x3+b∗x2+c∗x+d|(L≤x≤R),求函 ...
- java从键盘输入若干数,求其最大值,最小值,平均值。等等
总结:有一定基础的人,应该发现第一个程序可以运行,其实它有个致命的错误.有谁能一眼看出来呢?第二个程序是对的. 这个题目求最大值,最小值,平均值我不会求,不知道这个if判断放在类的外面还是main函数 ...
- 求int最大值以及int二进制
求int最大值:(((unsigned int)(~0))>>1) 求int的2进制串 string str = ""; int iNum = 100; for(int ...
- //给定N个整数序列{A1,A2,A3...An},求函数f(i,j)=(k=i~j)Ak的求和
//给定N个整数序列{A1,A2,A3...An},求函数f(i,j)=(k=i~j)Ak的求和 # include<stdio.h> void main() { ,sum1; ]={,- ...
- Problem J: 求个最大值
Problem J: 求个最大值 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 871 Solved: 663[Submit][Status][Web ...
- HDU - 1754 I Hate It (线段树单点修改,求区间最大值)
很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感. 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老师有 ...
- 【Java例题】2.4求函数
4.输入x,编程试求函数 y=sin(x^2)/(1-cosx)的值. 这里的"^"表示乘方. package study; import java.util.Scanner; p ...
- noi.openjudge 二分法求函数的零点
二分法求函数的零点 总时间限制: 1000ms 内存限制: 65536kB 描述 有函数:f(x) = x5 - 15 * x4+ 85 * x3- 225 * x2+ 274 * x - 121 已 ...
- HDU1166(线段树 +更新单点,求区间总和)、HDU1754(线段树 + 更新单点,求区间最大值)
线段树简单应用 先附上几张图便与理解,大佬文章传送门1.传送门2 HDU1166:题目描述 线段树 +更新单点,求区间总和 代码如下(递归版) #include<iostream> #in ...
随机推荐
- Java类什么情况下被初始化?
1.创建类的实例(new 的方式).访问某个类或接口的静态变量,或者对该静态变量赋值,调用类的静态方法 2.反射的方式 3.当初始化一个类的时候,如果发现其父类还没有进行初始化,则需先触发其父类的初始 ...
- spark 源码分析之十一--Spark RPC剖析之TransportClient、TransportServer剖析
TransportClient类说明 先来看,官方文档给出的说明: Client for fetching consecutive chunks of a pre-negotiated stream. ...
- ASP.NET Core Web Api之JWT VS Session VS Cookie(二)
前言 本文我们来探讨下JWT VS Session的问题,这个问题本没有过多的去思考,看到评论讨论太激烈,就花了一点时间去研究和总结,顺便说一句,这就是写博客的好处,一篇博客写出有的可能是经验积累,有 ...
- 细说RESTFul API之版本管理
目录 接口实现版本管理的意义 如何实现接口的版本管理 项目实战 接口实现版本管理的意义 API版本管理的重要性不言而喻,对于API的设计者和使用者而言,版本管理都有着非常重要的意义. 首先,对于API ...
- 微服务之springboot 自定义配置(一)Application配置文件
配置的文件的格式 springboot可以识别两种格式的配置文件,分别是yml和properties 文件.我们可以将application.properties文件换成application.yml ...
- 分布式ID系列(2)——UUID适合做分布式ID吗
UUID的生成策略: UUID的方式能生成一串唯一随机32位长度数据,它是无序的一串数据,按照开放软件基金会(OSF)制定的标准计算,UUID的生成用到了以太网卡地址.纳秒级时间.芯片ID码和许多可能 ...
- 【Java例题】2.8 解一元二次方程
8.解一元二次方程. 输入一元二次方程的a,b,c三个系数,解一元二次方程 ax^2+bx+c=0,输出两个根 package study; import java.util.Scanner; pub ...
- 渐进式web应用开发---Service Worker 与页面通信(七)
_ 阅读目录 一:页面窗口向 service worker 通信 二:service worker 向所有打开的窗口页面通信 三:service worker 向特定的窗口通信 四:学习 Messag ...
- 同“窗”的较量:部署在 Windows 上的 .NET Core 版博客站点发布上线
为了验证 docker swarm 在高并发下的性能问题,周一我们发布了使用 docker-compose 部署的 .net core 版博客站点(博文链接),但由于有1行代码请求后端 web api ...
- Missing artifact XXXXX:jar:1.9.1 解决错误问题
昨天导过来一个maven工程的一个项目,由于自己meven库中有许多现成的jar包,但是还是有一些需要去下载的,配置的是阿里云的镜像,把eclise的预编译给关闭,具体做法为:Project----- ...