From bdc11bcba7510edd838f70aa07cf2dd74148f4f2 Mon Sep 17 00:00:00 2001 From: az13js <1654602334@qq.com> Date: Fri, 13 Nov 2020 22:03:12 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=8F=A6=E4=B8=80=E7=A7=8D=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E2=80=94=E2=80=94=E5=AE=9E=E6=95=B0=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 +++--- src/GeneticAlgorithm/Chromosome.cpp | 90 ++++++--------------- src/GeneticAlgorithm/Chromosome.h | 91 ++++++++++++++++------ src/GeneticAlgorithm/ChromosomeFactory.cpp | 13 ++-- src/GeneticAlgorithm/ChromosomeFactory.h | 35 +++++++-- src/GeneticAlgorithm/MainProcess.cpp | 6 +- src/GeneticAlgorithm/MainProcess.h | 15 ++-- src/GeneticAlgorithm/PopulationFactory.cpp | 6 +- src/GeneticAlgorithm/PopulationFactory.h | 14 +++- src/main.cpp | 8 +- 10 files changed, 170 insertions(+), 132 deletions(-) diff --git a/README.md b/README.md index 85d8a4a..dbfd541 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ - 通过锦标赛算法选择个体 - 通过从上一代中剔除低适应度个体的方式去除劣质解 - 个体由一条染色体组成,所以程序不区分个体和染色体两个概念 -- 通过128位的10编码组成染色体,求解 De jong 函数 f2 在 -2.048 到 2.048 范围内的最小值 +- 通过实数编码组成染色体,求解 De jong 函数 f2 在 -2.048 到 2.048 范围内的最小值 +- 通过两条染色体相同位置基因求和再平均进行染色体交叉 +- 通过高斯分布的随机数对基因进行变异操作 - 适应度取 1 / (0.01 + f2) - 算法的参数可以通过修改`main.cpp`中`run`方法的输入参数来改变 @@ -32,21 +34,13 @@ 然后在`build`目录下会产生一堆文件,包括可执行程序`GeneticAlgorithm.out`。执行程序: $ ./GeneticAlgorithm.out - 代数=0, 最大适应度=3.35249, 个体信息=Fitness=3.35249,v1=1.18915,v2=1.36384 - 代数=1, 最大适应度=6.58385, 个体信息=Fitness=6.58385,v1=0.829805,v2=0.72218 - 代数=2, 最大适应度=8.04685, 个体信息=Fitness=8.04685,v1=1.32987,v2=1.77594 - 代数=3, 最大适应度=8.04685, 个体信息=Fitness=8.04685,v1=1.32987,v2=1.77594 - 代数=4, 最大适应度=8.04685, 个体信息=Fitness=8.04685,v1=1.32987,v2=1.77594 - 代数=5, 最大适应度=13.5202, 个体信息=Fitness=13.5202,v1=0.843112,v2=0.730674 - 代数=6, 最大适应度=22.4512, 个体信息=Fitness=22.4512,v1=0.98739,v2=0.993482 - 代数=7, 最大适应度=87.8533, 个体信息=Fitness=87.8533,v1=0.964518,v2=0.929183 - 代数=8, 最大适应度=87.8533, 个体信息=Fitness=87.8533,v1=0.964518,v2=0.929183 - 代数=9, 最大适应度=87.8533, 个体信息=Fitness=87.8533,v1=0.964518,v2=0.929183 - 代数=10, 最大适应度=97.2695, 个体信息=Fitness=97.2695,v1=0.983901,v2=0.967597 + 代数=0, 最大适应度=21.2265, 个体信息=Fitness=21.2265,v1=1.16928,v2=1.35803 + 代数=1, 最大适应度=21.2265, 个体信息=Fitness=21.2265,v1=1.16928,v2=1.35803 + 代数=2, 最大适应度=34.1127, 个体信息=Fitness=34.1127,v1=0.881568,v2=0.784435 + 代数=3, 最大适应度=75.6878, 个体信息=Fitness=75.6878,v1=0.943324,v2=0.88987 + 代数=4, 最大适应度=98.7995, 个体信息=Fitness=98.7995,v1=1.01102,v2=1.02211 结束。 -由于算法使用了随机数,所以可能在别的系统上得到的结果有点不一样,比如结束时的代数不一样、最大适应度不一样或者个体信息不一样,这是正常的。 - ## 修改源码 如果需要针对别的问题调整算法,那么需要修改源码。这里假设你知道遗传算法的原理且能使用 C++ ,那么根据需要你可能会修改下面列举的文件: @@ -58,4 +52,4 @@ - `ChromosomeFactory.cpp`和`ChromosomeFactory.h` 染色体创建的工厂类,包含创建染色体的不同方式 - `Chromosome.cpp`和`Chromosome.h` 染色体类(一个染色体就是种群里的一个个体),实现方式不区分染色体和个体,如果需要区分染色体和个体那么得单独拆分出个体类,如果不需要区分的话,染色体和个体的对外互动、通信的方法都在这里面 -另外,`GlobalCppRandomEngine.cpp`和`GlobalCppRandomEngine.h`目前只是在全局提供随机数引擎。 \ No newline at end of file +另外,`GlobalCppRandomEngine.cpp`和`GlobalCppRandomEngine.h`目前只是在全局提供随机数引擎。 diff --git a/src/GeneticAlgorithm/Chromosome.cpp b/src/GeneticAlgorithm/Chromosome.cpp index 4e7e350..eb036a9 100644 --- a/src/GeneticAlgorithm/Chromosome.cpp +++ b/src/GeneticAlgorithm/Chromosome.cpp @@ -6,7 +6,7 @@ namespace GeneticAlgorithm { Chromosome::Chromosome(unsigned long lengthOfChromosome) { - this->dataArray = new int[lengthOfChromosome]; + this->dataArray = new long double[lengthOfChromosome]; this->lengthOfData = lengthOfChromosome; } @@ -14,7 +14,7 @@ namespace GeneticAlgorithm { delete[] this->dataArray; } - bool Chromosome::setGene(unsigned long offset, int value) { + bool Chromosome::setGene(unsigned long offset, long double value) { if (offset > this->lengthOfData - 1) { return false; } @@ -25,7 +25,7 @@ namespace GeneticAlgorithm { return true; } - int Chromosome::getGene(unsigned long offset) { + long double Chromosome::getGene(unsigned long offset) { if (offset > this->lengthOfData - 1) { throw "Error, out of range."; } @@ -33,30 +33,37 @@ namespace GeneticAlgorithm { } void Chromosome::dump() { - std::cout << "Fitness=" << this->getFitness() << ",v1=" << this->v1 << ",v2=" << this->v2 << std::endl; + std::cout << "Fitness=" << this->getFitness() << ",v1=" << this->dataArray[0] << ",v2=" << this->dataArray[1] << std::endl; } unsigned long Chromosome::getLength() { return this->lengthOfData; } + long double Chromosome::getFitness() { + if (this->isFitnessCached) { + return this->fitnessCached; + } + if (this->lengthOfData < 2) { + throw "Can not less then 2."; + } + long double v1, v2, y; + v1 = this->dataArray[0]; + v2 = this->dataArray[1]; + y = 100.0 * (v1 * v1 - v2) * (v1 * v1 - v2) + (1.0 - v1) * (1.0 - v1); + // y 最小等于0,我们求最大适应度需要反过来 + this->fitnessCached = 1.0 / (y + 0.01); + return this->fitnessCached; + } + Chromosome* Chromosome::crossover(Chromosome* another) { if (another->getLength() != this->lengthOfData) { throw "Length not equals!"; } - using std::uniform_int_distribution; - using GeneticAlgorithm::Utils::GlobalCppRandomEngine; - uniform_int_distribution range(0, 1); - - int* newData = new int[this->lengthOfData]; + long double* newData = new long double[this->lengthOfData]; for (unsigned long i = 0; i < this->lengthOfData; i++) { - if (range(GlobalCppRandomEngine::engine) == 0) { - newData[i] = this->dataArray[i]; - } else { - newData[i] = another->getGene(i); - } + newData[i] = (this->dataArray[i] + another->getGene(i)) / 2.0; } - Chromosome* newChromosome = ChromosomeFactory().buildFromArray(newData, this->lengthOfData); delete[] newData; return newChromosome; @@ -66,59 +73,10 @@ namespace GeneticAlgorithm { if (r <= 0.0) { return; } - using std::uniform_real_distribution; using GeneticAlgorithm::Utils::GlobalCppRandomEngine; - uniform_real_distribution range(0.0, 1.0); + std::normal_distribution distribution(0, r); for (unsigned long i = 0; i < this->lengthOfData; i++) { - if (range(GlobalCppRandomEngine::engine) <= r) { - this->dataArray[i] = 1 - this->dataArray[i]; - this->isFitnessCached = false; - } - } - } - - long double Chromosome::getFitness() { - if (this->isFitnessCached) { - return this->fitnessCached; - } - if (this->lengthOfData < 16) { - throw "Can not less then 16 bit."; - } - unsigned int len1 = 64, len2 = 64; - if (this->lengthOfData < 128) { - len1 = this->lengthOfData / 2; - len2 = this->lengthOfData - len1; - } - int *arr1 = new int[len1]; - int *arr2 = new int[len2]; - unsigned long long max1 = 0, max2 = 0; - long double v1, v2; - for (unsigned int i = 0; i < len1; i++) { - arr1[i] = this->dataArray[i]; - max1 = (max1 << 1) | 0x01; - } - v1 = (long double)this->getSimpleInteger(arr1, len1) / (long double)max1 * 4.096 - 2.048; - for (unsigned int i = 0; i < len2; i++) { - arr2[i] = this->dataArray[len1 + i]; - max2 = (max2 << 1) | 0x01; - } - v2 = (long double)this->getSimpleInteger(arr2, len2) / (long double)max2 * 4.096 - 2.048; - long double y = 100.0 * (v1 * v1 - v2) * (v1 * v1 - v2) + (1.0 - v1) * (1.0 - v1); - // y 最小等于0,我们求最大适应度需要反过来 - this->fitnessCached = 1.0 / (y + 0.01); - this->v1 = v1; - this->v2 = v2; - delete[] arr1; - delete[] arr2; - return this->fitnessCached; - } - - unsigned long long Chromosome::getSimpleInteger(int *intArray, unsigned long lengthOfIntArray) { - unsigned long long x = 0; - for (unsigned long i = 0; i < lengthOfIntArray; i++) { - x <<= 1; - x |= (0x01 & intArray[i]); + this->dataArray[i] += distribution(GlobalCppRandomEngine::engine); } - return x; } } diff --git a/src/GeneticAlgorithm/Chromosome.h b/src/GeneticAlgorithm/Chromosome.h index 67867c1..670e5da 100644 --- a/src/GeneticAlgorithm/Chromosome.h +++ b/src/GeneticAlgorithm/Chromosome.h @@ -8,44 +8,91 @@ namespace GeneticAlgorithm { class Chromosome { public: - // 创建空染色体 - Chromosome(unsigned long lengthOfChromosome); - // 删除染色体,释放内存 + + /** + * 创建染色体,其中实数初始化为不确定的值 + * + * 入参是染色体中的实数个数,一个染色体保存多个实数 + * + * @param unsigned long numberOfReal + */ + Chromosome(unsigned long numberOfReal); + + /** + * 删除染色体,释放内存 + */ ~Chromosome(); - // 设置给定位置的基因 - bool setGene(unsigned long offset, int value); - // 获取给定位置基因 - int getGene(unsigned long offset); - // 打印调试信息 + /** + * 设置给定位置的实数大小 + * + * @param unsigned long offset 位置,大于等于0小于染色体的长度numberOfReal + * @param long double value 设置的实数的值 + * @return bool 成功返回 true + */ + bool setGene(unsigned long offset, long double value); + + /** + * 获取给定位置的实数大小 + * + * @param unsigned long offset 位置,大于等于0小于染色体的长度numberOfReal + * @return long double value 实数的值 + */ + long double getGene(unsigned long offset); + + /** + * 打印调试信息 + * + * @return void + */ void dump(); - // 获取长度,也就是基因的位数 + + /** + * 获取长度,也就是染色体内保存的实数个数 + * + * @return unsigned long + */ unsigned long getLength(); - // 获取适应度 + /** + * 获取适应度 + * + * @return long double + */ long double getFitness(); - // 与另一个染色体交叉,返回新的染色体 + + /** + * 与另一个染色体交叉,返回新的染色体 + * + * @param Chromosome* another 另一个染色体对象 + * @return Chromosome* 新的染色体对象,需要手动释放内存 + */ Chromosome* crossover(Chromosome* another); - // 以一定的概率r变异 + + /** + * 以一定的概率r变异 + * + * @param long double r + * @return void + */ void mutation(long double r); private: - // 保存了此染色体的长度 + + /** @var unsigned long 保存了此染色体的长度 */ unsigned long lengthOfData; - // 保存了此染色体中基因的信息 - int *dataArray; - // 为true时表示计算Fitness后缓存了计算结果,可以不用重复算 + /** @var long double* 保存了此染色体中基因的信息 */ + long double* dataArray; + + /** @var bool 为true时表示计算Fitness后缓存了计算结果,可以不用重复算 */ bool isFitnessCached = false; - // 缓存的上一次的适应度计算结果。需要判断isFitnessCached以确定确实缓存下来了。 + + /** @var long double 缓存的上一次的适应度计算结果。需要判断isFitnessCached以确定确实缓存下来了。 */ long double fitnessCached; - // 在getFitness计算的时候,解码出来的临时变量,因为在dump()时用于打印调试所以也存储了下来。 - long double v1, v2; - // 辅助私有方法,用于将一个0-1整数数组按位转换成一个unsigned long long的整数。 - unsigned long long getSimpleInteger(int *intArray, unsigned long lengthOfIntArray); }; } -#endif \ No newline at end of file +#endif diff --git a/src/GeneticAlgorithm/ChromosomeFactory.cpp b/src/GeneticAlgorithm/ChromosomeFactory.cpp index d72fa43..8a14fa7 100644 --- a/src/GeneticAlgorithm/ChromosomeFactory.cpp +++ b/src/GeneticAlgorithm/ChromosomeFactory.cpp @@ -5,7 +5,7 @@ namespace GeneticAlgorithm { - Chromosome* ChromosomeFactory::buildFromArray(int data[], unsigned long lengthOfData) { + Chromosome* ChromosomeFactory::buildFromArray(long double data[], unsigned long lengthOfData) { Chromosome* buildChromosome = this->buildEmpty(lengthOfData); for (unsigned long i = 0; i < lengthOfData; i++) { if (!buildChromosome->setGene(i, data[i])) { @@ -15,16 +15,13 @@ namespace GeneticAlgorithm { return buildChromosome; } - Chromosome* ChromosomeFactory::buildRandomBinrary(unsigned long lengthOfData) { - using std::uniform_int_distribution; + Chromosome* ChromosomeFactory::buildRandomChromosome(unsigned long lengthOfData, long double min, long double max) { using namespace GeneticAlgorithm::Utils; - - int *data = new int[lengthOfData]; - uniform_int_distribution formateRandomNumberRange(0, 1); + long double *data = new long double[lengthOfData]; + std::uniform_real_distribution formateRandomNumberRange(min, max); for (unsigned long i = 0; i < lengthOfData; i++) { data[i] = formateRandomNumberRange(GlobalCppRandomEngine::engine); } - Chromosome* buildChromosome = this->buildFromArray(data, lengthOfData); delete[] data; return buildChromosome; @@ -34,4 +31,4 @@ namespace GeneticAlgorithm { return new Chromosome(lengthOfData); } -} \ No newline at end of file +} diff --git a/src/GeneticAlgorithm/ChromosomeFactory.h b/src/GeneticAlgorithm/ChromosomeFactory.h index b04da5a..ab27e75 100644 --- a/src/GeneticAlgorithm/ChromosomeFactory.h +++ b/src/GeneticAlgorithm/ChromosomeFactory.h @@ -12,15 +12,38 @@ namespace GeneticAlgorithm { class ChromosomeFactory { public: - // 从一个数组中创建染色体 - Chromosome* buildFromArray(int data[], unsigned long lengthOfData); - // 给定长度创建随机的0-1染色体 - Chromosome* buildRandomBinrary(unsigned long lengthOfData); - // 创建空的染色体 + + /** + * 从一个数组中创建染色体 + * + * 实数数组,会原样拷贝给染色体用 + * + * @param long double data[] 数组 + * @param unsigned long lengthOfData 数组的长度 + * @return Chromosome* + */ + Chromosome* buildFromArray(long double data[], unsigned long lengthOfData); + + /** + * 随机地创建染色体 + * + * @param unsigned long lengthOfData 染色体中存储随机实数的个数 + * @param unsigned double min 随机实数的最小值 + * @param unsigned double max 随机实数的最大值 + * @return Chromosome* + */ + Chromosome* buildRandomChromosome(unsigned long lengthOfData, long double min, long double max); + + /** + * 创建空的染色体,其中实数都初始化为 0 + * + * @param unsigned long lengthOfData 染色体中存储随机实数的个数 + * @return Chromosome* + */ Chromosome* buildEmpty(unsigned long lengthOfData); }; } -#endif \ No newline at end of file +#endif diff --git a/src/GeneticAlgorithm/MainProcess.cpp b/src/GeneticAlgorithm/MainProcess.cpp index f2e8dae..b729bf8 100644 --- a/src/GeneticAlgorithm/MainProcess.cpp +++ b/src/GeneticAlgorithm/MainProcess.cpp @@ -17,6 +17,8 @@ namespace GeneticAlgorithm { void MainProcess::run( unsigned long numberOfChromosome, unsigned long lengthOfChromosome, + long double min, + long double max, unsigned long maxLoop, long double stopFitness, unsigned long keep, @@ -26,6 +28,8 @@ namespace GeneticAlgorithm { this->freeMemory(); // 防止重复调用run()没有释放上一次的内存 this->numberOfChromosome = numberOfChromosome; this->lengthOfChromosome = lengthOfChromosome; + this->min = min; + this->max = max; this->keep = keep; this->kill = numberOfChromosome - keep; this->r = r; @@ -69,7 +73,7 @@ namespace GeneticAlgorithm { } void MainProcess::init() { - this->population = PopulationFactory().buildRandomPopulation(this->numberOfChromosome, this->lengthOfChromosome); + this->population = PopulationFactory().buildRandomPopulation(this->numberOfChromosome, this->lengthOfChromosome, this->min, this->max); this->loopNow = 0; this->maxFitness = 0.0; this->selectedChromosome = new Chromosome*[2 * this->kill]; diff --git a/src/GeneticAlgorithm/MainProcess.h b/src/GeneticAlgorithm/MainProcess.h index 6434b3c..d8d886f 100644 --- a/src/GeneticAlgorithm/MainProcess.h +++ b/src/GeneticAlgorithm/MainProcess.h @@ -6,10 +6,9 @@ namespace GeneticAlgorithm { - // 内存占用估计方法:y=Kx+b - // K=0.9504 b=1764 - // x 是种群中个体的数量 - // y 是程序占用内存,单位kb + /** + * 算法主流程 + */ class MainProcess { public: @@ -22,6 +21,8 @@ namespace GeneticAlgorithm { void run( unsigned long numberOfChromosome, // 种群中个体数量 unsigned long lengthOfChromosome, // 每个个体的基因长度 + long double min, // 一开始初始种群时,随机数范围最小值 + long double max, // 一开始初始种群时,随机数范围最大值 unsigned long maxLoop, // 最大迭代次数 long double stopFitness, // 达到多大的适应度就立刻停止迭代 unsigned long keep, // 每次迭代保留多少个上一代的个体 @@ -40,6 +41,10 @@ namespace GeneticAlgorithm { unsigned long numberOfChromosome; // 染色体的长度 unsigned long lengthOfChromosome; + // 随机初始范围 + long double min; + // 随机初始范围 + long double max; // 每次迭代从上一代保留多少个个体 unsigned long keep; // 每次迭代上上一道销毁多少个个体,这个根据numberOfChromosome和lengthOfChromosome算出来的 @@ -77,4 +82,4 @@ namespace GeneticAlgorithm { } -#endif \ No newline at end of file +#endif diff --git a/src/GeneticAlgorithm/PopulationFactory.cpp b/src/GeneticAlgorithm/PopulationFactory.cpp index d198935..73197a9 100644 --- a/src/GeneticAlgorithm/PopulationFactory.cpp +++ b/src/GeneticAlgorithm/PopulationFactory.cpp @@ -4,13 +4,13 @@ namespace GeneticAlgorithm { - Population* PopulationFactory::buildRandomPopulation(unsigned long numberOfChromosome, unsigned long lengthOfChromosome) { + Population* PopulationFactory::buildRandomPopulation(unsigned long numberOfChromosome, unsigned long lengthOfChromosome, long double min, long double max) { ChromosomeFactory chromosomeFactory = ChromosomeFactory(); Population* population = new Population(numberOfChromosome); for (unsigned long i = 0; i < numberOfChromosome; i++) { - population->setChromosome(i, chromosomeFactory.buildRandomBinrary(lengthOfChromosome)); + population->setChromosome(i, chromosomeFactory.buildRandomChromosome(lengthOfChromosome, min, max)); } return population; } -} \ No newline at end of file +} diff --git a/src/GeneticAlgorithm/PopulationFactory.h b/src/GeneticAlgorithm/PopulationFactory.h index f5797ed..3937f3d 100644 --- a/src/GeneticAlgorithm/PopulationFactory.h +++ b/src/GeneticAlgorithm/PopulationFactory.h @@ -10,11 +10,19 @@ namespace GeneticAlgorithm { class PopulationFactory { public: - // 返回种群实体,numberOfChromosome是种群的大小,lengthOfChromosome是个体染色体的长度 - Population* buildRandomPopulation(unsigned long numberOfChromosome, unsigned long lengthOfChromosome); + + /** + * 返回种群实体 + * @param unsigned long numberOfChromosome 种群的大小, + * @param unsigned long lengthOfChromosome 个体染色体的长度 + * @param long double min 随机范围最小值 + * @param long double max 随机范围最大值 + * @return Population* + */ + Population* buildRandomPopulation(unsigned long numberOfChromosome, unsigned long lengthOfChromosome, long double min, long double max); }; } -#endif \ No newline at end of file +#endif diff --git a/src/main.cpp b/src/main.cpp index 2149581..2862091 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,11 +17,13 @@ int main() mainProcess.setDebug(true); mainProcess.run( 240, // 种群大小 - 128, // 染色体长度 + 2, // 染色体长度 + -2.048, // 初始范围 + 2.048, // 初始范围 100, // 最大迭代次数 90.0, // 停止迭代适应度 - 22, // 每次迭代保留多少个上一代的高适应度个体 - 0.05 // 变异概率 + 20, // 每次迭代保留多少个上一代的高适应度个体 + 0.15 // 变异概率,随便变动范围系数 ); return 0; } -- Gitee From c17762f15afd30a0169bf0275bb9d90cf01dafea Mon Sep 17 00:00:00 2001 From: az13js <1654602334@qq.com> Date: Thu, 10 Dec 2020 13:02:20 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 2862091..9b6fde4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,7 +23,7 @@ int main() 100, // 最大迭代次数 90.0, // 停止迭代适应度 20, // 每次迭代保留多少个上一代的高适应度个体 - 0.15 // 变异概率,随便变动范围系数 + 0.015 // 变异概率,随便变动范围系数 ); return 0; } -- Gitee