[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 ...
随机推荐
- C#编程之自动实现的属性
在 C# 3.0 及更高版本,当属性访问器中不需要任何其他逻辑时,自动实现的属性会使属性声明更加简洁.它们还允许客户端代码创建对象.当你声明以下示例中所示的属性时,编译器将创建仅可以通过该属性的 ge ...
- python课堂整理18---文件操作(下)
一.b模式,字节方式(二进制的单位),rb wb ab f = open('test.py', 'rb', encoding = 'utf-8') 报错,因为用了b模式,就不能再指定编码格式了,已经指 ...
- 学习LayUI时自研的表单参数校验框架
开发背景&痛点:每次写前端的表单的时候需要对表单里用户填写的内容进行校验,减少服务器压力,提前对已知错误对用户提示.每次会要写很多的if else等等对输入框中的内容进行判断,并对为空.格式不 ...
- sklearn 第二篇:数据预处理
sklearn.preprocessing包提供了几个常用的转换函数,用于把原始特征向量转换为更适合估计器的表示. 转化器(Transformer)用于对数据的处理,例如标准化.降维以及特征选择等,提 ...
- Spring 源码注解
一.@Retention可以用来修饰注解,是注解的注解,称为元注解. Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolic ...
- Thymeleaf 模板 springboot集成使用
一.Thymeleaf是什么? 简单说, Thymeleaf 是一款用于渲染XML/XHTML/HTML5内容的模板引擎,类似我之前用过的FreeMarker .由于它支持 html 原型,然后在 h ...
- 补充Java面试记录
补充Java面试记录 背景:这两天面试遇到的部分问题都分散在了前面两篇文摘中,这里再做一些其他的记录,以备不时之需! 一.谈谈你对SpringBoot的理解? SpringBoot简介:SpringB ...
- GridLayout and GridData
GridLayout的风格 GridLayout类提供了GridLayout 布局中划分网格的信息,主要通过以下几个参数进行设置. 属性: NumColumns:通过“gridLayout.numCo ...
- 什么是redis的缓存雪崩与缓存穿透
今天来分享一下Redis几道常见的面试题: 如何解决缓存雪崩? 如何解决缓存穿透? 如何保证缓存与数据库双写时一致的问题? 一.缓存雪崩 1.1 什么是缓存雪崩? 首先我们先来回答一下我们为什么要用缓 ...
- SonarQube+Jenkins安装工程中遇到的吭
1. SonarQube是不是有点飘了,居然要java11+才能运行 解决方案: 重新下载老版本 也不知道哪个版本才好用,就下载了7.0 和6.6,这两个版本用jdk1.8就可以用 2. 配置数据库u ...