目录
一、概述
二、知识回顾
2.1适应度函数的确定
2.2基因编码
2.3遗传算法复制(选择)
2.4遗传算法交叉操作 通过交叉操作可以增加种群个体的多样性,既可以产生更多的优秀解。下面通过顺序编码方法进行改进(网上有很多方法)。注意这里直接用顺序编码方式会产生非法子代,不是结果不好,本文采取整数编码。
2.5遗传算法变异操作 从遗传算法的角度来看,解的进化主要是靠选择机制和交叉策略来完成,变异只是产生新个体的辅助方法,目的在于提高算法的局部搜索能力,避免陷入早熟。通过交叉算子和变异算子的相互配合,共同完成对搜索空间的全局搜索和局部搜索,从而使遗传算法能够良性的搜索以完成最优化问题的寻优过程,变异概率一般不能太大,本次取为0.05左右。
2.5.1位置交换变异
2.5.2旋转变异(基因正负号变化)
三、所用函数代码与运行结果
3.1所用函数代码
3.2运行结果
3.3结果分析
一、概述
本文前三篇对矩形排料的理论知识已经做出了讲解,第四篇基于最低水平线搜索策略已经实现了NX基于最低水平线排料,这篇主要详细的记录一下基于最低水平线+遗传算法排料策略实现。
二、知识回顾
2.1适应度函数的确定
上一篇矩形排料3已经对适应度函数进行了认识,这里我进行简单的叙述。参考“下台阶算法”提出了以板材利用率的大小表示适应度函数值的大小,板材利用率越高,适应度函数值越大,定义如下:
/******************************************************************************************
double ratio 板材的利用率(适应度函数值)
double usedArea 需要排样矩形件的面积总和(*100单位转化)
double width 板材宽度
double maxHigh 板材排料后最大使用高度,不包括使用完整后的板材
int symbol 板料个数标识
******************************************************************************************/
ratio = (usedArea * 100.0) / (width * (maxHigh + (symbol - 1)));
2.2基因编码
用遗传算法解决实际问题时,必需先确定染色体的编码方法。编码就是将问题的解用一种代码来表示,从而将问题的状态空间与遭传算法的码空间相对应,这在很大程度上依赖于所要求解问题的性质。编码是应用遭传算法求解问题时的一个关键步骤 ,编码方法决定了染色体的排列形式,编码的好坏将直接影响到遭传算法的性能和效率。
由于板材和零件都是矩形,为了使矩形排放时板材的利用率尽可能高,每个零件在具体排放时只能有横放和竖放两种方式。由于每个基因的编码可以为正或负,在本文中统一规定:矩形的编号为正表示矩形横排,即矩形的长边平行于X轴;矩形的编号为负表示矩形竖排,即矩形的长边平行于Y轴 。
本文采用整数顺序编码方式:先将要排放的每个矩形都统一进行编号,矩形的编号可以为正或负,矩形编号的正负是根据矩形是横放还是竖放来决定的。一个矩形的编号对应一个基因的编码。设要排放的矩形总数为n,n个矩形的编号构成了一个染色体。种群中每条染色体的长度与待排矩形零件总数相同,染色体中每个基因的编码对应相应矩形的编号,所有基因编码的一个排列顺序构成了一个染色体。
图1基因编码
2.3遗传算法复制(选择)
选择操作是为了保证个体的优良特性得以保存并能有效的遗传到下一代中,目的在于保留有效、优良基因,以保证解的质量,提高收敛速度。选择算子,就是依适应度大小,按照某种规则,从当前的种群中选择适应度高的个体遗传到下一代种群中同时淘汰那些适应度值低的个体。本次利用轮盘赌进行选择,其主要实现的思路如下:将种群中所有染色体编号,并根据各自适应值计算按比例分配的概率,依次计算染色体累加概率,产生(0,1)之间随机数,若其最多能大于序列中第m个值,则第m个染色体被随机选择。
个体放入适应度越大,其被选中的概率越高,个体被选择的概率值可以看做在轮盘区间范围,每次轮盘赌算法都会在(0,1)之间产生一个随机数,通过随机数落在轮盘哪个区间来确定被选中个体。注意累计所占比例最后一定是1,如果不为1的话一定计算有误。
2.4遗传算法交叉操作
通过交叉操作可以增加种群个体的多样性,既可以产生更多的优秀解。下面通过顺序编码方法进行改进(网上有很多方法)。注意这里直接用顺序编码方式会产生非法子代,不是结果不好,本文采取整数编码。
根据交叉概率,选择 2个个体 和进行交叉操作。随机选择一个交叉点,在交叉点处,交换 2个个体后半部分得到 2个新的个体 和, 如下图所示:注意这里数字前的负号只表示矩形是横放还是竖放(在考虑交叉时,看做标记即可)
6 | 1 | 3 | 5 | 4 | 2 | 7 | 9 | 8 | 10 |
个体
9 | 7 | 5 | 6 | 8 | 3 | 2 | 1 | 4 | 10 |
个体
6 | 1 | 3 | 5 | 4 | 3 | 2 | 1 | 4 | 10 |
个体
9 | 7 | 5 | 6 | 8 | 2 | 7 | 9 | 8 | 10 |
个体
很显然,交叉操作可能产生非法个体,即个体中有重复的基因。所以必须对2个新个体的基因进行调整。
**1**找出个体的重复基因1,3,4;个体的重复基因9,7,8;
**2**将1,3,4与9,7,8以及其关联的负号对应交换,得到两个合法的新个体和,如下所示:
6 | 9 | 7 | 5 | 8 | 3 | 2 | 1 | 4 | 10 |
个体
1 | 3 | 5 | 6 | 4 | 2 | 7 | 9 | 8 | 10 |
个体
2.5遗传算法变异操作
从遗传算法的角度来看,解的进化主要是靠选择机制和交叉策略来完成,变异只是产生新个体的辅助方法,目的在于提高算法的局部搜索能力,避免陷入早熟。通过交叉算子和变异算子的相互配合,共同完成对搜索空间的全局搜索和局部搜索,从而使遗传算法能够良性的搜索以完成最优化问题的寻优过程,变异概率一般不能太大,本次取为0.05左右。
2.5.1位置交换变异
位置变异的思想是产生[1,n]之间的两个正整数随机数,num1、num2,将两个位置交换。例如:产生的随机数num1=2、num2=6
原染色体:
6 | 1 | 3 | 5 | 4 | 2 | 7 | 9 | 8 | 10 |
变异后染色体:
6 | 2 | 3 | 5 | 4 | 1 | 7 | 9 | 8 | 10 |
具体步骤:
**1**在要进行变异的个体中,随机选择2个基因位;
**2**将所选择 2个基因位上的基因值交换,得到 1个新个体。
2.5.2旋转变异(基因正负号变化)
排列方式变异的思想是:第一步在[1,n]之间产生一个随机数num;第二步在[0,1]之间产生一个随机数 rand,若rand>0.5,刚将其排列方式进行改变,反之,不予理睬,这里所说的变是指排列时矩形是横放还是竖放。
例如:产生的随机数分别为num=5,rand=0.7。
原染色体:
6 | 1 | 3 | 5 | 4 | 2 | 7 | 9 | 8 | 10 |
变异后染色体:
6 | 1 | 3 | 5 | -4 | 2 | 7 | 9 | 8 | 10 |
三、所用函数代码与运行结果
3.1所用函数代码
//用户定义头文件
#include <atlbase.h>
#include <Windows.h>
#include <vector>
#include <algorithm>
#include "uf.h"
#include "uf_modl.h"
#include "uf_obj.h"
#include "uf_ui.h"
#include "uf_eval.h"
#include "uf_csys.h"
#include <numeric>
#include <algorithm>
#include <random>
#include <stdio.h>
#include <random>
#include <cmath>
#include <numeric>
#include <iostream>
#include <unordered_map>/*****************************************************************************************************
struct: OutLine1 水平线类结构体
double Origin 水平线起始x位置
double End 水平线终止x位置
double Height 水平线高度
******************************************************************************************************/
struct OutLine1
{double Origin;double End;double Height;// 有参数构造函数OutLine1(const double& origin, const double& end, const double& height): Origin(origin), End(end), Height(height){}
};//用户定义
/*****************************************************************************************************
Function: vector<vector<int>> NXOpen_GA_RectangularLayout 初始化种群
input: int ProductsNum 每个染色体的尺寸大小
input: int Population_size 种群大小
return: vector<vector<int>> Population 初始化种群后的数据
******************************************************************************************************/
vector<vector<int>> NXOpen_GA_RectangularLayout::Initial_population(int ProductsNum, int Population_size)
{。。。。。。
}/*****************************************************************************************************
Function: void initLineList1 初始化水平线集
input: double origin 水平线起始x位置
input: double end 水平线终止x位置
input: double height 水平线高度
input,output: vector<OutLine1> lineList 水平线集合
input,output: OutLine1 lowestLine 最低水平线
input,output: int lowestLineIdx 水平线集合中的最低水平线所在位置的ID
******************************************************************************************************/
void NXOpen_GA_RectangularLayout::initLineList1(double origin, double end, double height, vector<OutLine1> &lineList, OutLine1 &lowestLine, int &lowestLineIdx)
{。。。。。。
}/*******************************************************************************************************************
Function: void findLowestLine1 找出最低水平线(如果最低水平线不止一条则选取最左边的那条)
input,output: vector<OutLine1> lineList 水平线集合
input,output: OutLine1 lowestLine 最低水平线
input,output: int lowestLineIdx 水平线集合中的最低水平线所在位置的ID
*********************************************************************************************************************/
void NXOpen_GA_RectangularLayout::findLowestLine1(vector<OutLine1> lineList, int &lowestLineIdx, OutLine1 &lowestLine)
{。。。。。。
}/*******************************************************************************************************************
Function: double lineWidth1 获得放置矩形件的可用长度
input: int index 水平线集合中的最低水平线所在位置的ID
input: OutLine1 lineList 水平线集合
return: double availableWidth 返回放置矩形件的可用长度
*********************************************************************************************************************/
double NXOpen_GA_RectangularLayout::lineWidth1(int index, vector<OutLine1> lineList)
{。。。。。。
}/*******************************************************************************************************************
Function: double lineHigh1 获得放置矩形件的可用高度
input: int index 水平线集合中的最低水平线所在位置的ID
input: OutLine1 lineList 水平线集合
input,output: double containerHigh 板材的高度
return: double availableHigh 返回放置矩形件的可用高度
*********************************************************************************************************************/
double NXOpen_GA_RectangularLayout::lineHigh1(int index, vector<OutLine1> lineList, double containerHigh)
{。。。。。。
}/*******************************************************************************************************************
Function: double maxlineHigh1 水平线集合中最大的高度
input: OutLine1 lineList 水平线集合
return: double maxlineHigh1 返回放置矩形件的可用高度
*********************************************************************************************************************/
double NXOpen_GA_RectangularLayout::maxlineHigh1(vector<OutLine1> lineList)
{。。。。。。
}/*******************************************************************************************************************************************
Function: int searchBySize1 获得候选物品的索引;根据长度搜索矩形件(找出宽度或高度小于目标长度的首个矩形件)
input: double targetWidth 放置矩形件的可用长度
input: vector<vector<double>> data 要放置的矩形物品列表
return: int candidateIdx 返回候选物品的索引
********************************************************************************************************************************************/
int NXOpen_GA_RectangularLayout::searchBySize1(double targetWidth, vector<vector<double>> data)
{。。。。。。
}/*******************************************************************************************************************************************
Function: int searchBySize2 获得候选物品的索引;根据长度搜索矩形件(找出宽度或高度小于目标长度的首个矩形件)
input: double targetWidth 放置矩形件的可用长度
input: double targetHigh 放置矩形件的可用高度
input: vector<vector<double>> data 要放置的矩形物品列表
return: int candidateIdx 返回候选物品的索引
********************************************************************************************************************************************/
int NXOpen_GA_RectangularLayout::searchBySize2(double targetWidth, double targetHigh, vector<vector<double>> data)
{。。。。。。
}/***********************************************************************************************************************************
Function: vector<double> rotateNew1 对于满足长度小于可用长度的矩形件进行旋转放置(宽度放不下时,对矩形件进行旋转)
input: vector<double> pro 要放置的矩形件
return: vector<double> temp 返回放置矩形件
*************************************************************************************************************************************/
vector<double> NXOpen_GA_RectangularLayout::rotateNew1(vector<double> pro)
{。。。。。。
}/***********************************************************************************************************************************
Function: void updateLineList 在水平线集合中更新最低水平线
input: int index 水平线集合中的最低水平线所在位置的ID
intput,output: OutLine1 newLine 新的最低水平线
intput,output: vector<OutLine1> lineList 替换新的最低水平线后的水平线集合
*************************************************************************************************************************************/
void NXOpen_GA_RectangularLayout::updateLineList(int index, OutLine1& newLine, vector<OutLine1> &lineList)
{。。。。。。
}/***********************************************************************************************************************************
Function: void insertLineList 在水平线集合的最低水平线后插入新的最低水平线
input: int index 水平线集合中的最低水平线所在位置的ID
intput,output: OutLine1 newLine 新的最低水平线
intput,output: vector<OutLine1> lineList 替换新的最低水平线后的水平线集合
*************************************************************************************************************************************/
void NXOpen_GA_RectangularLayout::insertLineList(int index, OutLine1& newLine, vector<OutLine1> &lineList)
{。。。。。。
}/**************************************************************************************************
Function: void packing 将候选物品排样
input,output: vector<double> pro 要放置的矩形件
intput: int lowestLineIdx 水平线集合中的最低水平线所在位置的ID
input,output: OutLine1 lineList 水平线集合
input,output: vector<vector<double>> resultPos 排序后的矩形物品列表
intput: OutLine1 lowestLine 最低水平线
***************************************************************************************************/
void NXOpen_GA_RectangularLayout::packing(vector<double> pro, int &lowestLineIdx, vector<OutLine1> &lineList, vector<vector<double>> &resultPos, OutLine1 lowestLine)
{。。。。。。
}/*****************************************************************************************************************
Function: void enhanceLine1 最低水平线宽度小于要排样矩形宽度,提升水平线
intput,output: int index 水平线集合中的最低水平线所在位置的ID
input,output: vector<OutLine1> lineList 水平线集合
input,output: OutLine1 lowestLine 最低水平线
input: double containerHigh 板料高度
input: double containerWidth 板料宽度
input: double lingjianjuli 零件距离
input,output: int symbol 板料个数标识
******************************************************************************************************************/
void NXOpen_GA_RectangularLayout::enhanceLine1(int &index, vector<OutLine1> &lineList, double containerHigh, double containerWidth, double lingjianjuli, OutLine1 &lowestLine)
{。。。。。。
}/*****************************************************************************************************************
Function: double calHighLine1 计算板材的最大使用高度
intput: vector<OutLine1> lineList 水平线集合
return: double maxHigh 最大高度
******************************************************************************************************************/
double NXOpen_GA_RectangularLayout::calHighLine1(vector<OutLine1> lineList)
{。。。。。。
}/***********************************************************************************************************************************
Function: double calUsedRatio1 计算排料矩形件的总面积、板材的最低水平线高度,板材利用率1,板材利用率2
intput,output: double usedArea 排样矩形件的面积总和
input: vector<vector<double>> resultPos 排样矩形件信息集合
input: double width 板材宽度
input: double High 板料使用的最小高度,不包括使用完整后的板材
input: vector<OutLine1> lineList 水平线集合
input: int symbol 板料个数标识
intput,output: double ratio 板材利用率
************************************************************************************************************************************/
void NXOpen_GA_RectangularLayout::calUsedRatio1(double &usedArea, vector<vector<double>> resultPos, double width, double High, vector<OutLine1> lineList, int symbol, double &ratio)
{。。。。。。
}/**************************************************************************************************
Function: void Packing_main 将候选物品排样主函数(放置个体)
intput,output: double usedArea 排样矩形件的面积总和
intput,output: double ratio 板材利用率
intput,output: double MaxHeight 板料排放物品后达到的最大高度,不包括使用完整后的板材
input: double container_height板材高度
input: double container_width 板材宽度
input: vector<vector<double>> products 要排放的矩形物件集合
return: vector<vector<vector<double>>> resultPos 返回将多个板料候选物品排样数据集合
***************************************************************************************************/
vector<vector<vector<double>>> NXOpen_GA_RectangularLayout::Packing_main(double &usedArea,double &ratio, double&MaxHeight, double container_height, double container_width, vector<vector<double>> products)
{。。。。。。
}/*****************************************************************************************************************
Function: vector<double> Get_Fitness 获得适应度函数
input: vector<vector<double>> products 要排放的矩形物件集合
input: double container_height 板材高度
input: double container_width 板材宽度
intput: vector<vector<int>> Population 种群集合
return: vector<double> fitness 当代种群中的适应度函数值大小集合
******************************************************************************************************************/
vector<double> NXOpen_GA_RectangularLayout::Get_Fitness(vector<vector<double>> products, double container_height, double container_width, vector<vector<int>> Population)
{。。。。。。
}/*****************************************************************************************************************
Function: void Get_bestAndworst 获得当代最佳和最差个体索引
intput,output: int best_idx 当代最佳个体索引
intput,output: double worst_idx 当代最差个体索引
input: vector<double> fitness 当代适应度函数值集合
******************************************************************************************************************/
void NXOpen_GA_RectangularLayout::Get_bestAndworst(int &best_idx, int &worst_idx, vector<double> fitness)
{。。。。。。
}/*****************************************************************************************************************
Function: void select 遗传算法选择(复制)操作——轮盘赌思想
intput: vector<vector<int>> Population 种群集合
input: vector<double> fitness 当代适应度函数值集合
return: vector<vector<int>> Population 新的种群集合
******************************************************************************************************************/
vector<vector<int>> NXOpen_GA_RectangularLayout::select(vector<vector<int>> Population, vector<double> fitness)
{。。。。。。
}/*****************************************************************************************************************
Function: vector<int> randNum 随机产生1到n的数,一共n个,每个只出现一次
intput: int number 需要产生的个数
return: vector<vector<int>> random_combination 产生随机数集合
******************************************************************************************************************/
vector<int> NXOpen_GA_RectangularLayout::randNum(int number)
{。。。。。。
}/*****************************************************************************************************************
Function: int getRandomNumber 随机产生一个在[main,max]区间内的正整数
intput: int min 产生随机数的下限
intput: int max 产生随机数的上限
return: int RandomNumber 返回产生的随机数
******************************************************************************************************************/
int NXOpen_GA_RectangularLayout::getRandomNumber(int min, int max)
{。。。。。。
}/*****************************************************************************************************************
Function: double getRandomNumber 随机产生一个在[main,max]区间内的double类型
intput: double min 产生随机数的下限
intput: double max 产生随机数的上限
return: double RandomNumber 返回产生的随机数
******************************************************************************************************************/
double NXOpen_GA_RectangularLayout::getRandomNumber(double min, double max)
{。。。。。。
}/*****************************************************************************************************************
Function: void findDuplicatePositions 查询两个vector中重复的元素并记录下其位置
intput: vector<int> nums1 第一个vector
intput: vector<int> nums2 第二个vector
intput,output: vector<int> Element 第一个与第二个重复数字集合
intput,output: vector<int> positions 第一个与第二个重复数字位置集合
******************************************************************************************************************/
void NXOpen_GA_RectangularLayout::findDuplicatePositions(vector<int> nums1, vector<int> nums2, vector<int> &Element, vector<int> &positions)
{。。。。。。
}/*****************************************************************************************************************
Function: void crossover_inner 两个个体进行交叉操作
intput,output: vector<int> individual1 第一个个体
intput,output: vector<int> individual2 第二个个体
intput: int position 要交叉的位置
******************************************************************************************************************/
void NXOpen_GA_RectangularLayout::crossover_inner(vector<int> &individual1, vector<int> &individual2, int position)
{。。。。。。
}/*****************************************************************************************************************
Function: void crossover 交叉操作
intput,output: vector<vector<int>> Population 种群集合
intput: double PC 交叉概率
******************************************************************************************************************/
void NXOpen_GA_RectangularLayout::crossover(vector<vector<int>> &Population, double PC)
{。。。。。。
}/*****************************************************************************************************************
Function: void mutation 变异操作
intput,output: vector<vector<int>> Population 种群集合
intput: double PM 变异概率
******************************************************************************************************************/
void NXOpen_GA_RectangularLayout::mutation(vector<vector<int>> &Population, double PM)
{
。。。。。。
}/*****************************************************************************
Function: void print 打印输出信息
intput,output: double d 排样矩形件的面积总和
input: bool ISEnter 是否换行
注意:下边重载函数类似
******************************************************************************/
void NXOpen_GA_RectangularLayout::print(const double &d, bool ISEnter)
{char msg[50];sprintf(msg, "%f", d);logical response = 0;UF_UI_is_listing_window_open(&response);if (!response) UF_UI_open_listing_window();UF_UI_write_listing_window(msg);if (ISEnter) UF_UI_write_listing_window("\n");
}void NXOpen_GA_RectangularLayout::print(const char * msg, bool ISEnter)
{logical response = 0;UF_UI_is_listing_window_open(&response);if (!response) UF_UI_open_listing_window();UF_UI_write_listing_window(msg);if (ISEnter) UF_UI_write_listing_window("\n");
}void NXOpen_GA_RectangularLayout::print(const int &i, bool ISEnter)
{char msg[50];sprintf(msg, "%d", i);logical response = 0;UF_UI_is_listing_window_open(&response);if (!response) UF_UI_open_listing_window();UF_UI_write_listing_window(msg);if (ISEnter) UF_UI_write_listing_window("\n");
}
3.2运行结果
基于最低水平线算法+遗传算法的NX矩形排料-CSDN直播具体运行结果看下图所示:
动图太大上传不了,这里有视频链接,大家可以查看。
3.3结果分析
有3.2运行结果效果图可以看出,遗传算法计算的并不是最好的,只是在有限的时间内进行相对最优的求解,与第四章中的基于最低水平线搜索策略相比总是给人的很差的感觉,这里值得说明的是,本次采用的是最低水平线法,由于相较于基于最低水平线搜索策略其存在弊端,所以避免不了,下一篇我想利用基于最低水平线搜索策略+遗传算法看一看效果。当然这里所说的效果不好只是相对的,因为我主要是思路是做一款支持设置零件间距离修改,和零件与板料间距离,以及超过最大板材高度后可以自动在另一个完整的板材上进行排料,为了效果明显,我故意设置其宽度和高度,实际的应用中并不是这样差,应该可以达到80%左右。