【C++ 遗传算法】最短加法链问题

目录

一、问题描述:

二、挑战题目:

三、遗传算法思路:

整体算法思路:

初始准备:

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 , ki,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代会重复上述步骤,最终将获得当前参数情况下的最短加法链(虽然无法保证一定是最短,但趋于最短)。

初始准备:

1.目标加法链结果

const string standardNumber = "211108170305887"; // 目标数

2.全局变量的参数

const int populationSize = 100;   // 初始个数

const int generations = 200;      // 迭代次数

const int mutationRate = 5;       // 变异概率,以百分比表示

const int createRate = 50;        // 生成加法链加倍概率%

3.随机数产生器以及获取随机数的函数,用于确定交叉点、判断变异以及补全加法链

mt19937 rng(random_device{}());

uniform_int_distribution uni_dist(0, 99);   // 用于生成0到99之间的均匀分布的随机数

int generateRandomNumber() {

    return uni_dist(rng);

}

4.定义个体的结构体,包含加法链以及适应度

struct Individual {

    vector chain;    // 加法链

    int fitness;             // 适应度(加法链长度)

};

5.由于目标值过大,无法使用基本数据类型存储,因此使用字符串存储大数,需要定义相应的大数字符串加法和字符串比较

string add(string a, string b);

int CompareStr(string a, string b);

6.去重函数

string ConcatenateChain(const vector &chain);    // 辅助函数,用于将加法链拼接为一个字符串

void RemoveDuplicateChains(vector &population);

7.在保留父代种群的较好个体以及目前全局的最优个体、去重操作以后,需要有一个按适应度大小升序排序(我们希望适应度越小越好),筛选前populationSize个个体的函数

void Ranking(vector &population, vector &newPopulation);

8.按适应度升序排序

void selection(vector &population);

9.非加倍情况下找最大值的函数

void findAdd(vector &pre);

10.增加一个节点,createRate概率节点加倍,否则调用findAdd

void selectAdd(vector &pre);

11.初始化个体,调用findAdd

void createChain(Individual &p);

12.修补为合法加法链,同时更新个体适应度,调用selectAdd

void RepairChain(Individual &p);

13.寻找parent个体加法链中,加法等于下标为dest处字符串的,字典序最小的一对字符串,他们的的下标组成的pair

pair GetLowPair(Individual &parent, int dest);

14.找最小对,将最小对作为两个加数填补子代加法链,调用GetLowPair

void AddPair(Individual &parent, Individual &child, int start);

遗传算法核心

1.初始化种群 & 评估适应度

void initializePopulation(vector &population)

首先,调用createChain构建加法链,然后把加法链长度作为适应度,形成一个个体,如此初始化种群.在该部分中,包含了全局最优best的初始化——当best的加法链为空时把第一个个体作为最优。

2.选择交叉

void crossover(Individual &parent1, Individual &parent2, Individual &child1, Individual &child2)

首先,用随机数确定一个交叉点crossoverPoint,然后将父代的前crossoverPoint个基因复制到子代中(parent1给child1,parent2给child2),接着调用AddPair函数来进行交叉操作,交叉的本质是沿用父代该节点处的加法对(例如父代该节点是由下标为a和b对应两个字符串相加而来的,那么子代就通过下标为a和b的两个字符串相加作为新节点),同时需要注意交叉的循环结束条件为①父代遍历节点结束②子代所得新节点的数大于目标数。此后需要调用RepairChain函数对子代进行修复,使其是一个合法的加法链个体。

3.变异

void mutation(Individual &individual)

首先,生成随机数确定变异节点。然后生成随机数判断是否大幅变异,若大幅变异,则将变异节点改为前两个节点之和,否则在随机变异的情况下,就会对个体进行随机变异——前一节点与之前的随机节点(随机数给出)。改变节点之后,变异节点后面的不一定合理,于是清空后续节点,然后调用RepairChain函数对变异个体进行修复,使其是一个合法的加法链个体。

4.遗传算法主体

void geneticAlgorithm(vector &population)

预先初始化种群,先升序排序,然后调用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 <

你可能感兴趣的:(算法,c++)