目录
一、问题描述:
二、挑战题目:
三、遗传算法思路:
整体算法思路:
初始准备:
1.目标加法链结果
2.全局变量的参数
3.随机数产生器以及获取随机数的函数,用于确定交叉点、判断变异以及补全加法链
4.定义个体的结构体,包含加法链以及适应度
5.由于目标值过大,无法使用基本数据类型存储,因此使用字符串存储大数,需要定义相应的大数字符串加法和字符串比较
6.去重函数
7.在保留父代种群的较好个体以及目前全局的最优个体、去重操作以后,需要有一个按适应度大小升序排序(我们希望适应度越小越好),筛选前populationSize个个体的函数
8.按适应度升序排序
9.非加倍情况下找最大值的函数
10.增加一个节点,createRate概率节点加倍,否则调用findAdd
11.初始化个体,调用findAdd
12.修补为合法加法链,同时更新个体适应度,调用selectAdd
13.寻找parent个体加法链中,加法等于下标为dest处字符串的,字典序最小的一对字符串,他们的的下标组成的pair
14.找最小对,将最小对作为两个加数填补子代加法链,调用GetLowPair
遗传算法核心
1.初始化种群 & 评估适应度
2.选择交叉
3.变异
4.遗传算法主体
四、代码实现
加法链问题(ACP): 给定正整数n,设n − 5 ≤ m ≤ n + 5 ,指出哪一个m 值具有最短的加法链表示,并给出其加法链表示。加法链打印规则如下:设U = (u0 ,u1 , u2 … , u r )是可计算n的加法链,则按照如下规则进行打印:n = n, l = r, v0 = 1, v1 = 2, …,v k = ( u k , k,i,j ),…其中 u k = u i + u j , k ≥ 2,例如n = 23, l = 6, v 0 =1, v 1 = 2, v 2 = (4, 2,1,1), v3 = (5,3,0,1), v4 = (9,4, 2,3), v 5 = (18,5, 4, 4), v 6 = (23,6,3,5)
(1)第一类挑战问题参数:n = 211108170305887
(2)第二类挑战问题参数:n = 55211775190703851000955237173238443091;
首先,初始化种群,生成包含populationSize个个体的种群。然后,对这个种群根据适应度进行升序排序,确保加法链长度越短的个体排在前面。接下来,依次对这些个体进行两两交叉(1与2, 3与4,以此类推),得到一批子代。为了保留父代种群中较好的个体以及当前全局的最优个体,我们将子代、父代和最优个体合一、去重、排序,保留前populationSize个个体。此外,将本轮最佳的个体与当前全局最优个体进行比较,选择更优的设置为全局最优个体。通过这一轮的迭代,完成第一轮的遗传算法优化。接下来的generations-1代会重复上述步骤,最终将获得当前参数情况下的最短加法链(虽然无法保证一定是最短,但趋于最短)。
const string standardNumber = "211108170305887"; // 目标数
const int populationSize = 100; // 初始个数
const int generations = 200; // 迭代次数
const int mutationRate = 5; // 变异概率,以百分比表示
const int createRate = 50; // 生成加法链加倍概率%
mt19937 rng(random_device{}());
uniform_int_distribution
int generateRandomNumber() {
return uni_dist(rng);
}
struct Individual {
vector
int fitness; // 适应度(加法链长度)
};
string add(string a, string b);
int CompareStr(string a, string b);
string ConcatenateChain(const vector
void RemoveDuplicateChains(vector
void Ranking(vector
void selection(vector
void findAdd(vector
void selectAdd(vector
void createChain(Individual &p);
void RepairChain(Individual &p);
pair
void AddPair(Individual &parent, Individual &child, int start);
void initializePopulation(vector
首先,调用createChain构建加法链,然后把加法链长度作为适应度,形成一个个体,如此初始化种群.在该部分中,包含了全局最优best的初始化——当best的加法链为空时把第一个个体作为最优。
void crossover(Individual &parent1, Individual &parent2, Individual &child1, Individual &child2)
首先,用随机数确定一个交叉点crossoverPoint,然后将父代的前crossoverPoint个基因复制到子代中(parent1给child1,parent2给child2),接着调用AddPair函数来进行交叉操作,交叉的本质是沿用父代该节点处的加法对(例如父代该节点是由下标为a和b对应两个字符串相加而来的,那么子代就通过下标为a和b的两个字符串相加作为新节点),同时需要注意交叉的循环结束条件为①父代遍历节点结束②子代所得新节点的数大于目标数。此后需要调用RepairChain函数对子代进行修复,使其是一个合法的加法链个体。
void mutation(Individual &individual)
首先,生成随机数确定变异节点。然后生成随机数判断是否大幅变异,若大幅变异,则将变异节点改为前两个节点之和,否则在随机变异的情况下,就会对个体进行随机变异——前一节点与之前的随机节点(随机数给出)。改变节点之后,变异节点后面的不一定合理,于是清空后续节点,然后调用RepairChain函数对变异个体进行修复,使其是一个合法的加法链个体。
void geneticAlgorithm(vector
预先初始化种群,先升序排序,然后调用crossover两两交叉,再调用mutation对子代进行变异操作,形成新一个种群,接着将父代种群和子代种群合一,插入全局最优个体后去重,排序,筛选,完成一轮操作,如此共循环generations次
#include
using namespace std;
// 定义全局随机数生成器
mt19937 rng(random_device{}());
uniform_int_distribution uni_dist(0, 99); // 用于生成0到99之间的均匀分布的随机数
int generateRandomNumber() {
return uni_dist(rng);
}
// 设置参数
const int populationSize = 100; // 初始个数
const int generations = 200; // 迭代次数
const int mutationRate = 5; // 变异概率,以百分比表示
const int createRate = 50; //生成加法链加倍概率%
// 定义问题
const string targetNumber = "211108170305887"; // 目标数
// 定义个体结构
struct Individual{
vector chain; // 加法链
int fitness; // 适应度(加法链长度)
};
//最终结果
Individual best;
Individual GloBest;
// 大数加法 加法结果在out里面
string add(string a, string b){
int l1 = a.length();
int l2 = b.length();
int maxLength = max(l1, l2);
while (a.length() < maxLength){
a = "0" + a;
}
while (b.length() < maxLength){
b = "0" + b;
}
int carry = 0;
string out = "";
for (int i = maxLength - 1; i >= 0; i--){
int digitA = a[i] - '0';
int digitB = b[i] - '0';
int sum = digitA + digitB + carry;
carry = sum / 10;
out = to_string(sum % 10) + out;
}
if (carry){
out = "1" + out;
}
return out;
}
//大数比较 a &chain) {
string concatenatedChain;
for (const auto &item : chain) {
concatenatedChain += item;
}
return concatenatedChain;
}
void RemoveDuplicateChains(vector &population) {
// 使用集合来存储已经出现过的加法链
unordered_set chainSet;
// 用于存储没有重复的加法链
vector uniqueChains;
for (const auto &individual : population) {
// 检查当前加法链是否已经存在
if (chainSet.find(ConcatenateChain(individual.chain)) == chainSet.end()) {
// 如果加法链不重复,则将其加入uniqueChains和chainSet中
uniqueChains.push_back(individual);
chainSet.insert(ConcatenateChain(individual.chain));
}
}
// 更新种群为没有重复加法链的集合
population = uniqueChains;
}
// 更新种群 前者筛选前,后者为筛选后
// 选取前populationSize个作为新种群
void Ranking(vector &population,vector &newPopulation){
//插入目前最优解并去重
if(!best.chain.empty()) population.push_back(best);
RemoveDuplicateChains(population);
// 按照适应度(加法链长度)升序排序
sort(population.begin(), population.end(),
[](const Individual &a, const Individual &b)
{
return a.fitness < b.fitness;
});
newPopulation = population;
//更新best
if(newPopulation[0].fitness &pre){
vector::iterator iter2=pre.end()-1, iter1=iter2-1;
while(iter1>=pre.begin()){
string sum=add(*iter1 , *iter2);
if (CompareStr(targetNumber,sum)){
--iter1;
}
else{
pre.push_back(sum);
return;
}
}
}
//使用createRate作为翻倍概率
void selectAdd(vector &pre){
string x=pre.back();
string dou=add(x , x);
//同时可变异
if (generateRandomNumber() < createRate && (CompareStr(dou,targetNumber) || dou==targetNumber))
pre.push_back(dou);
else{
findAdd(pre);
}
}
//初始个体
void createChain(Individual &p){
vector chainset=p.chain;
string x = chainset.back();
while (x != targetNumber){
string dou=add(x,x);
//同时可变异
if (generateRandomNumber() < createRate && (CompareStr(dou,targetNumber) || dou==targetNumber))
chainset.push_back(dou);
else{
findAdd(chainset);
}
x = chainset.back();
}
p.chain=chainset;
p.fitness = chainset.size();
}
// 修补一个加法链,返回合法加法链个体 #防止大于出现
void RepairChain(Individual &p){
vector chainset=p.chain;
string x = chainset.back();
while (x != targetNumber){
selectAdd(chainset);
x = chainset.back();
}
p.chain=chainset;
p.fitness = chainset.size();
}
// 初始化种群
void initializePopulation(vector &population){
for (int i = 0; i < populationSize; ++i)
{
// 生成一个个体
Individual indiv;
indiv.chain = {"1", "2"};
createChain(indiv);
population.push_back(indiv);
if(best.chain.empty()) best=indiv;
}
}
//寻找最小对 必定存在,除非先前数据有误
pair GetLowPair(Individual &parent,int dest){
int head=0,end=dest-1;
vector chainset=parent.chain;
string sum;
while(head<=end){
sum=add(chainset[head],chainset[end]);
if (CompareStr(chainset[dest],sum)){
--end;
}
else if(sum==chainset[dest]){
return make_pair(head,end);
}
else{
++head;
}
}
exit(100);//不存在
}
//找最小对并且完成部分对象的填补
void AddPair(Individual &parent,Individual &child,int start){
pair pr=GetLowPair(parent,start);
string sum=add(child.chain[pr.first],child.chain[pr.second]);
while(CompareStr(sum,targetNumber) && start &population){
sort(population.begin(), population.end(),
[](const Individual &a, const Individual &b)
{
return a.fitness < b.fitness;
});
}
// 遗传算法主体
void geneticAlgorithm(vector &population){
for (int generation = 0; generation < generations; ++generation){
selection(population);
// 创建新一代个体 采用一前一后两两交叉的形式
vector newPopulation;
for (int i = 0; i < population.size(); i += 2){
Individual parent1 = population[i];
Individual parent2 = population[i + 1];
Individual child1, child2;
crossover(parent1, parent2, child1, child2);
mutation(child1);
mutation(child2);
newPopulation.push_back(child1);
newPopulation.push_back(child2);
}
// 更新种群 new的为筛选前,后者为筛选后
newPopulation.insert(newPopulation.end(),population.begin(),population.end());
Ranking(newPopulation, population);
// 输出当前代的最优个体
cout << "Generation " << generation + 1 << ": Best Fitness = " << population[0].fitness << endl;
}
}
int main()
{
vector population;
// 初始化种群
initializePopulation(population);
// 运行遗传算法
geneticAlgorithm(population);
// 输出最终结果
cout <