说明:文章写了很久禁止转载,代码完全开源,思想完全可供借鉴
设计
1.1问题简介
设有n个独立的作业{1, 2, …, n}, 有m台独立的机器{1,2,…m}。这n个作业中每个作业里有k道不同的工序{1,2…k},不同的作业k并不一定相同,这k道工序均由m台机器加工。要求:每台机器同时只能执行一个任务的一道工序,对任意一个任务,必须在k-1工序完成之后,才能执行第k道工序。目标,给定这个n个作业的每道工序作用的机器,及其在此机器上的执行时间,设计一个合理的机器调度方案。
1.2算法设计
1.2.1问题抽象
首先要将调度方案抽象为一个序列。这个序列的长度为所有作业工序数的总和。这个序列的元素为作业的序号,并且每个作业的序号在序列中出现的次数等于它所包含的工序数。根据序列元素的排列方式不同,代表不同的调度方案。
举例:假设作业一有三道工序,作业二有两道工序。那么序列12121,所表示的调度方案就为,依次使机器执行作业一的第一道工序,作业二的第一道工序,作业一的第二道工序,作业二的第一道工序,作业一的第三道工序。
1.2.2遗传算法解决方案
1.2.2.1算法思想:
首先它是一类属于猜测一个最优值的方法,因为原序列的解的集合太多了,举例如果一个序列长度35,对这个序列没有特殊要求的话,它的解空间一共是35!这是一个非常大的范围,但对于很多问题,其实最优解并不是唯一的,他可能也是一个解空间,或者找到一个相对好的解是比较容易的,遗传算法就是即根据初始的一个随机序列的集合,模拟遗传的过程,找到一个特定的序列的(即比较好的解)的过程。(实际中发发现,遗传过程应该将初始种群量设置大一点,那样能有更大的范围,迭代次数设置少一点,迭代偏向于找局部最优解,从局部收敛)
1.2.2.2算法步骤
共分为三个步骤。
步骤一:产生种群,根据原问题规模,产生一个指定数量的随机序列。从其中挑选花费时间最少的10%作为第一代。
步骤二:繁殖。从第一代开始,根据基因,逐渐产生下一代(即根据当代序列,产生不同的序列)。
步骤二说明:繁殖步骤对于每一代又可以细分为染色体重新组合和变异及淘汰及灭绝。将以一个特定例子一代中的一对父母(不区分父母,统称父代1和父代2)即两个序列112323,232311来说明。
染色体重新组合:指的是一对父母产生两个子代的过程,分为继承和交叉两个过程,本文所采取的方法为顺序组合。随机的产生两个位置,代表子代要从父母的继承的起点和终点。假设这个位置为3和5,继承:孩子1将从父代1那里继承对应位置的基因,即孩子1的3到5位置的元素序列为232,交叉:对于孩子1得到得232,对父辈2找到对应得元素,这个对应与继承的出现次序是有关系的,232在父代1分别代表第一个2第二个3第二个2,那么将父代2的第一个2第二个3第二个2删除,剩余311,将311依此填充到孩子1染色体的剩余位置,即位置1,1,6得到孩子孩子1的序列312321。孩子2与孩子1的产生方法是对称的。
变异:这里会设置一个变异率,孩子有几率变异。如果上文的孩子1(312321)变异,随机的产生两个位置,孩子对应的位置元素互换,假设位置是3和4的话,那么孩子1将会变为3132321。
淘汰:现实中应该是孩子之间的竞争,但从算法的角度父母序列就利用上。淘汰过程为下一代的父母为在这一代的父母和它们的孩子当中最小的两个。
灭绝:这里会设置一个灭绝参数,当当前代达到上一代的百分之多少,种族就灭绝了。
举例说明:假设父代用时1,父代2用时3,子代1用时2,子代2用时4,那么父代1和子代1将成为新的父母。
步骤三:解码,严格来说这并不属于遗传算法的步骤,但我们得到那个序列在表示调度方案上终究有所欠缺,需要将它映射为真实得调度方案。即每台机器,在哪一段时间,执行哪一道工序。
1.3程序设计思想
为了用C++模板类的方法实现这个算法。需要设计好接口和类函数。同时为了检验程序的正确性和效率性需要设计一个测试函数。
接口:即设计数据的交互方式。我采用的是文件读取数据,屏幕打印结果。
类函数设计:需要哪些功能,即将那些函数封装到对象中。在设计中,一个设计了4个函数。随机序列产生函数(用于产生随机序列),解码函数(用于给特定的序列,找到它在机器上的执行时间),展示函数(用于给特定的序列,展示机器调度的结果),模拟遗传函数(在调用以上3个函数的基础上,执行遗传算法)。
测试函数:设计的测试函数是产生一个符合接口的文件,用它作为数据,测试程序。
实现
将介绍各主要模块的实现主要流程及思路,更多细节请参考附录代码。
2.1类函数的实现
2.1.1随机序列产生函数实现
这个函数对应附录代码中的code_rand()。原问题的随机序列是个特殊的序列。对于产生这个序列有两种思路。
第一种思路:加一些条件,调用rand()函数一个一个随机数产生这个随机序列,这样做效率极低,故不采用。
第二种思路:我改进后的思路,调用random_shuffle()函数,这种思路是用一个符合条件的序列作为种子,类似洗牌的过程,重新排列得到新的随机序列。这样做效率远远高于第一种思路。种子的产生是在构造函数产生的,是基于原文件,我在对原数据做个统计,比如如果任务1有4道工序,2有3道工序,那么种子就是1111222。
2.1.2解码函数的实现
这个函数对应附录代码中的decode()。为了求一个序列的用时。首先要准备两段内存空间,第一段内存空间用来记录每一个任务,开始执行它目前工序前上一个工序的结束时间。第二段内存空间用来记录机器它在执行当前任务当前工序前,上一次执行工序的结束时间。因为刚开始啥也没做,所以都初始化为0。接着要做的事情是遍历序列,查表取值,因为已经在构造函数做了统计,查出比如当前遍历道序列的第三个1,那么查出第一个任务的第三道工序所用的机器和时间。最后要做的事情是更新两段内存空间的值。这里要判断是任务等机器,还是机器等任务。任务等机器的意思是,这个任务上一道工序结束时间早于对应机器结束时间。那么就将工序结束时间和机器结束时间更新为机器结束时间加上当前任务工序执行时间,表示这道工序执行完了。机器等任务类似。
2.1.3展示函数的实现
这个函数对应附录代码中的show()。要将序列转化为一个好的表示,要准备一个4元组二维数组即C++里的tuple<>。第一维下标表示这是第几台机器。第二维是平行的一个四元组数组,它的下标表示这是机器执行的第几道工序,它的四个元素,分别表示当前任务名,当前任务工序号,当前任务起始时间,当前任务结束时间。接着要做的事情参考上一步解码函数的实现,这和解码过程类似,只是这一次我们要把那些中间过程值统统保存下来。最后就将这段空间遍历一遍将所有属性输出出来就好了。
2.1.4模拟遗传函数的实现
对应附录代码中的scheduling()。
2.1.4.1参数的设计
考虑不同的问题,需求不同。我们设计四个接口,每一代人口数,繁殖代数,子代变异率,还有接受率。接受率是种族灭绝条件,如果达到上一代最小的百分之多少,种族就灭绝了。即不再迭代。认为找到了一个最优解。
2.1.4.2性能考量与内存模型建立
这是实现中技术含量最高的部分也是最体现C和C++编程强大特性的部分。
内存模型:假设每一代人口数为num,那么初始随机序列我们将产生10num个随机序列,选择后我们每一代有父代1和父代2也就是,它会产生一个子代1子代2。繁殖1000代的话,会产生2000num个序列。但实际实现时,只需申请存储10num序列的空间,这意味着可以处理更大更多的任务。我们定义一个祖先ancestors指针指向这个空间起始位置。populationg和parent1也指向这个起始位置,往后移动num/2个空间,paretent2指向它,再往后移动num/2个空间,child1指向它,再往后移动num/2个空间,child2指向它。
性能优势:由于指针的特性,我们很多时候可以交换指针的指向,而不用改变内存里的值,会从底层减少一个编码长度系数的复杂度。
2.1.4.3初始挑选的实现
在整理清楚内存模型后。可以申请一个二元组空间(即pair)。二元组第二个参数记录随机序列组的执行时间,第一个参数记录当前内存空间的位置。接着就是对二元组根据第二个参数即产生的随机序列组根据执行时间进行由小到大的排序。使用sort()函数,nlogn的复杂度。然后要做的就是交换,因为二元组记录了排名前10%空间的位置,直接把当前内存空间前10%的指针和排名前10%的指针进行交换即可,即内存内容不变,只是把第一维的指针指向更改。(接下来的淘汰实现和此过程相同,不再赘述,补充一下,由于每一次迭代后第一个指针总是执向时间最小的值,所以你在解码的时候无需记录最小值)。
2.1.4.4基因重组的实现
继承部分相对简单,直接遍历赋值就好。
主要点在交叉的实现。你要产生孩子1,你需要找到从起始点道结束点父代1相对应于父代2的位置。
举例:假设两个序列112323,232311,位置为3和5,你要找到父辈2对应得元素第一个2第二个3第二个2。
策略1:遍历父辈1时候,每一次都遍历父辈2。
采用策略2:查找表的思想,再申请一段空间,先分别遍历一遍父辈1和父辈2,提前记录好位置和出现的次序,如序列112323处理完后就成了121212,(下标代表位置,值代表次序)。空间换时间,减少了一个常数因子的复杂度。
删除操作:找到位置后就要删除了,我不喜欢说删除,我觉得标记更为恰当,申请一个bool型数组,刚开始都初始为false,找到一个就标记为true。
最后是剩余部分赋值操作,根据布尔数组的标记,遍历赋值即可。
2.2接口与测试函数的实现
接口:定义文件数据的一种标准如下示例:
第一行的三个参数分别表示3个任务,3台机器,任务当中最大的工序数为4。
接下来的三行分别表示三个任务的属性。如第二行,表示任务二它有两道工序,,第一道工序作用于机器2它花费时间为1.5,第二道工序在机器3上工作,时长为0.5。注意后面补的0,它是为了使数据格式规范化,每行长度一样,方便实现。可以理解为,这道工序不存在,它不花费时间,不在机器上工作。
测试函数:对应附录代码的rand_file(),它接受一个文件名,以这个文件名产生符合这个标准的文件(注意这个函数没封装到类里。)采用的实现方式是文件流对象,而不是c语言的文件指针。
展示
均运行在配置为的电脑上的win10系统vs2017编译环境下。
3.1算法流程展示
以以上文件数据,每一代人口为4,繁殖两代,变异率为1,接受率为0.1追踪结果。(这里的接受率是我模型改进前的展示结果,接受率为第一代的百分之多少,我将在3.2的展示中用到改进后的模型,它为上一代的百分之多少)
祖先产生:
繁殖:
可观察到父辈1和父辈2是祖先当中最小的4个,并且下一代的父辈1和父辈2是上一代存活下来的4个。
调度方案:
3.2算法收敛性展示
采用数据为:我们初始200个种群,迭代20代,变异率为0.1,接受率为0.8。
可以看到耗时逐渐递减逐渐收敛,到第10代以后基本就不在改变,意味着这很可能就是最小结果。
找到的调度方案为:
3.3速度性能展示
计时开始时间为产生祖先前,计时结束为程序回收内存前(【】delete之前)。计数函数为clock()它的精度时ms;
对于3.2的例子,在本模型下所需时间为
可以看到非常快包括这个时间还包括IO口的调用。
所以我另外选了两组数据一组是中等规模的。
任务长度50,机器数10,最大工序数20.接受系数设为0.001确保不会提前结束。
初始种群1000迭代次数30。
编码长度为648,这样的运算量下算法只需要8到9s。
算法缺陷
不得不痛苦的承认,算法仍然有不可避免的缺陷,在内存管理上的缺陷。这迫使它无法处理更大的数据,测试过2G的数据量,会异常,因为堆上的动态内存空间被破坏,猜测可能有重复冲突。
在内存回收时,[]delete语句会引发中断 ,会提示堆已破坏。如下,仍未解决。C++,C的内存管理确实是一大难点。
附录I参考文献
数学建模算法与应用(第2 版)司守奎,孙兆亮主编。
参考博客:https://blog.csdn.net/mnmlist/article/details/79056522
附录II代码
// machine_scheduling.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include<algorithm>
#include <map>
#include<tuple>
#include<fstream>
#include<ctime>
#include<string>
#include<Windows.h>
#include <memory>
using namespace std;
const int up_limit= 999999;//设定时间上限
template<class T>
class scheduling
{
int job_num;
int machine_num;
int max_working_procedure;
int code_length;
fstream job_file;
string file_name;
int *job_list;//list用在寻找当前序列号值是第几个位置时,降低时间复杂度
int* job_pro_num;
T *machine; //代表空闲时间
T *job_now_time;
int* machine_wpro_statistics;
int* code_seed;
int *plan;
tuple<int*, int*, T*, T*> *machine_plan;//当前任务工序号,当前任务起始时间,当前任务结束时间。
pair<int, T> **job; //第一维下标表示这是第几台机器。第二维是平行的一个四元组数组,它的下标表示这是机器执行的第几道工序,它的四个元素,分别表示当前任务名,
//当前任务工序号,当前任务起始时间,当前任务结束时间。
static bool time_compare(pair<int, T> p1, pair<int, T> p2) { return p1.second <= p2.second; }
T decode(int*p);
void code_rand(int**pro, int num);
void show(int *p);
public:
scheduling(string s1);
void inheritance(int population_num, int times, float balance, float accept_coeff);
~scheduling();
};
template<class T>
scheduling<T>::scheduling(string s1)
{
file_name = s1;
int pos;
job_file.open(s1, ios::binary | ios::in);
if (!job_file)
throw "文件不存在";
job_file >> job_num;
job_file >> machine_num;
job_file >> max_working_procedure;
job_list = new int[job_num + 1]; //用来暂存序列是几道工序
job_pro_num = new int[job_num + 1]{ 0 };
machine = new T[machine_num + 1]{ (int)0 };//强制转换,初始化为整数0
machine_wpro_statistics = new int[machine_num + 1]{ 0 };//统计每台机器执行的工序数
machine_plan = new tuple<int*, int*, T*, T*>[machine_num + 1];
job_now_time = new T[job_num + 1]{ (int)0 };//刚开始一个任务都没做
job = new pair<int, T>*[job_num + 1];//二元组二维数组,第一维下标代表当前是第几个任务,第二维下标代表第几个工序二元组表示它工作的机器和时间
for (int i = 1; i <= job_num; ++i)
job[i] = new pair<int, T>[max_working_procedure + 1];//
code_length = 0;
for (int i = 1; i <= job_num; ++i)
{
for (int j = 1; j <= max_working_procedure; ++j)
{
job_file >> job[i][j].first;
if (job[i][j].first != 0)
{
pos = job[i][j].first;
++machine_wpro_statistics[pos];
++job_pro_num[i];
++code_length;
}
job_file >> job[i][j].second;
}
}
for (int i = 1; i <= machine_num; ++i)
{
pos = machine_wpro_statistics[i];
get<0>(machine_plan[i]) = new int[pos];
get<1>(machine_plan[i]) = new int[pos];
get<2>(machine_plan[i]) = new T[pos];
get<3>(machine_plan[i]) = new T[pos];
}
code_seed = new int[code_length + 1];
plan = new int[code_length + 1];
pos = 1;
for (int i = 1; i <= job_num; ++i)
{
for (int j = 1; j <= job_pro_num[i]; ++j)
{
code_seed[pos] = i;
++pos;
}
}//初始化牌组
job_file.close();
}
template<class T>
void scheduling<T>::code_rand(int **pro, int num)
{
for (int i = 1; i <= num; ++i)
{
random_shuffle(code_seed + 1, code_seed + code_length + 1);//洗牌,注意第0为是不用的,所以要加1
for (int j = 1; j <= code_length; ++j)
{
pro[i][j] = code_seed[j];
if (pro[i][j] == 0)
{
cout << "zero";
throw;
}
}
}
}
template<class T>
T scheduling<T>::decode(int*p)
{
int job_lab1;
int job_lab2; //当前任务号和工序号
int now_machine; //当前机器号
T now_time;
int final_machine = 0;
T final_time = 0;//当前执行最长工序完成时间最后的机器号,和它的完工时间,默认都是0
for (int i = 1; i <= job_num; ++i)
{
job_now_time[i] = (int)0;
job_list[i] = 0;
}
for (int i = 1; i <= machine_num; ++i)
{
machine[i] = (int)0; //强制转换清零
}
for (int i = 1; i <= code_length; ++i)
{
job_lab1 = p[i];
++job_list[p[i]];
job_lab2 = job_list[p[i]]; //取出当前任务和它的工序
if (job_lab1 == 0)
{
cout << "没有第0项任务";
throw;
}
if (job_lab2 == 0)
{
cout << "没有第0项工序";
throw;
}
//cout << "当前任务和工序" << job_lab1 << ' '<<job_lab2 << endl;
//cout << job_lab1 << " " << job_lab2;
now_machine = job[job_lab1][job_lab2].first;
now_time = job[job_lab1][job_lab2].second; //取出机器名和所花费的时间
//cout << now_machine;
if (job_now_time[job_lab1] > machine[now_machine])
{
job_now_time[job_lab1] += now_time;
machine[now_machine] = job_now_time[job_lab1];
} //机器等任务
else
{
machine[now_machine] += now_time;
job_now_time[job_lab1] = machine[now_machine];
} //任务等机器
if (job_now_time[job_lab1] >= final_time)
{
final_time = job_now_time[job_lab1];
}//更新时间
}
return final_time;
}
template<class T>
void scheduling<T>::show(int *p)
{
int job_lab1;
int job_lab2; //当前任务号和工序号
int now_machine; //当前机器号
T now_time;
int final_machine = 0;
T final_time = 0;//当前执行最长工序完成时间最后的机器号,和它的完工时间,默认都是0
int *pos = new int[machine_num + 1];
for (int i = 1; i <= job_num; ++i)
{
job_now_time[i] = 0;
job_list[i] = 0;
}
for (int i = 1; i <= machine_num; ++i)
{
pos[i] = 1;
machine[i] = 0;
}
for (int i = 1; i <= code_length; ++i)
{
job_lab1 = p[i];
++job_list[p[i]];
job_lab2 = job_list[p[i]]; //取出当前任务和它的工序
if (job_lab1 == 0)
{
cout << "没有第0项任务";
throw;
}
if (job_lab2 == 0)
{
cout << "没有第0项工序";
throw;
}
//cout << "当前任务和工序" << job_lab1 << ' '<<job_lab2 << endl;
now_machine = job[job_lab1][job_lab2].first;
now_time = job[job_lab1][job_lab2].second; //取出机器名和所花费的时间
get<0>(machine_plan[now_machine])[pos[now_machine]] = job_lab1;
get<1>(machine_plan[now_machine])[pos[now_machine]] = job_lab2;
if (job_now_time[job_lab1] > machine[now_machine])
{
get<2>(machine_plan[now_machine])[pos[now_machine]] = job_now_time[job_lab1];
//cout << get<2>(machine_plan[now_machine])[j] << ' ';
job_now_time[job_lab1] += now_time;
machine[now_machine] = job_now_time[job_lab1];
get<3>(machine_plan[now_machine])[pos[now_machine]] = job_now_time[job_lab1];
} //机器等任务
else
{
get<2>(machine_plan[now_machine])[pos[now_machine]] = machine[now_machine];
machine[now_machine] += now_time;
job_now_time[job_lab1] = machine[now_machine];
get<3>(machine_plan[now_machine])[pos[now_machine]] = job_now_time[job_lab1];
} //任务等机器
if (job_now_time[job_lab1] >= final_time)
{
final_time = job_now_time[job_lab1];
}//更新时间
++pos[now_machine];//更新位置
}
cout << "机器总占用时间:" << final_time << endl;
cout << "调度方案如下:" << endl;
for (int i = 1; i <= machine_num; ++i)
{
cout << "machine" << i << ':';
for (int j = 1; j <= machine_wpro_statistics[i]; ++j)
{
cout << get<0>(machine_plan[i])[j] << '-';
cout << get<1>(machine_plan[i])[j] << '(';
cout << '(';
cout << get<2>(machine_plan[i])[j] << '-';
cout << get<3>(machine_plan[i])[j] << ')';
cout << " ";
}
cout << endl;
}
delete[]pos;
}
template<class T>
void scheduling<T>::inheritance(int population_num, int times, float balance, float accept_coeff)
{
if (population_num % 2 == 1)
{
cout << "群体个数应该为偶数";
throw;
}
clock_t clock_star, clock_end;
clock_star = clock();
cout << "起始时间" << clock_star << endl;
bool equal_flag = 1;
int balance_coeff = 1000;
int half_population_num = population_num / 2;
balance = (int)balance * 1000;//因为rand()是整数取1到10000,变异率为balance/balance_coeff
int star, end;
int pos; //用于表示位置
T lower_limit = 0;//刚开始灭绝条件是0
T mini_time = up_limit;
bool *code_del = new bool[half_population_num + 1]; //用于交叉时,判断是否删除
int * parent1_vector = new int[job_num + 1];
int * parent2_vector = new int[job_num + 1];
int *parent1_list = new int[code_length + 1];
int *parent2_list = new int[code_length + 1];//(用于辅助遗传中交叉的计算)用来暂存序列是几道工序(以上四个数组是用于找序列中例如1它是第几个1)
pair<int, T> *list_ancestors = new pair<int, T>[20 * half_population_num + 1];//用来暂存祖先个体执行时间的排名
pair<int, T> *list_population = new pair<int, T>[4 * half_population_num + 1];//用来暂存两代个体执行时间的排名
int**ancestors;
int**population;
int**parent1;
int**parent2;
int**child1;
int**child2;
ancestors = new int*[20 * half_population_num + 1];
for (int i = 1; i <= 20 * half_population_num; ++i)
ancestors[i] = new int[code_length + 1];
population = ancestors;
parent1 = population;
parent2 = population + half_population_num;
child1 = population + 2 * half_population_num;
child2 = population + 3 * half_population_num;
srand(unsigned(std::time(0)));
code_rand(ancestors, 20 * half_population_num);
cout << "编码长度" << code_length << endl;
//cout << "初始样本为:" << endl;//
/*for (int i = 1; i <= 20 * half_population_num; ++i)
{
for (int j = 1; j <= code_length; ++j)
//cout << ancestors[i][j];
//temp = decode(ancestors[i]);
//cout << " " << "耗时:" << temp;//
//cout << endl;//
}*/
for (int i = 1; i <= 20 * half_population_num; ++i)
{
list_ancestors[i].first = i;
list_ancestors[i].second = decode(ancestors[i]);
}
sort(list_ancestors + 1, list_ancestors + 1 + 20 * half_population_num, time_compare);
for (int i = 1; i <= 4 * half_population_num; ++i)
{
swap(population[i], ancestors[list_ancestors[i].first]);
}//取出实际要用的4段内存空间
//cout << "祖先最优时间为:" << mini_time << endl;
for (int k = 1; k <= times && mini_time > lower_limit; ++k)
{
//cout << "第" << k << "代"<<' ' ;
for (int i = 1; i <= half_population_num; ++i)
{
/*cout << "父辈" << i << ':' << endl;//
for (int j = 1; j <= code_length; ++j)
{
cout << parent1[i][j];//
}
temp = decode(parent1[i]);
cout << " " << "耗时:" << temp;//
cout << endl;
for (int j = 1; j <= code_length; ++j)
{
cout << parent2[i][j];//
}
temp = decode(parent2[i]);
cout << " " << "耗时:" << temp;//
cout << endl;*/
for (int j = 1; j <= job_num; ++j)
{
parent1_vector[j] = 0;
parent2_vector[j] = 0;
}
for (int j = 1; j <= code_length; ++j)
{
parent1_list[j] = 0;
parent2_list[j] = 0;
}
for (int j = 1; j <= code_length; ++j)
{
++parent1_vector[parent1[i][j]];
parent1_list[j] = parent1_vector[parent1[i][j]];
++parent2_vector[parent2[i][j]];
parent2_list[j] = parent2_vector[parent2[i][j]];
}//记录当前俩序列出现的次序
do
{
star = rand() % code_length + 1;
end = rand() % code_length + 1;
} while (star >= end);
for (int j = star; j <= end; ++j) //继承开始
{
child1[i][j] = parent1[i][j];
child2[i][j] = parent2[i][j];
}
for (int j = 1; j <= code_length; ++j)
{
code_del[j] = 0;
}
for (int j = star; j <= end; ++j)
{
for (int k = 1; k <= code_length; ++k)
{
if ((parent2[i][k] == parent1[i][j]) && (parent2_list[k] == parent1_list[j]))//值相等且排名相等
{
code_del[k] = 1;
//cout << k << ' ';
break;
}
}
}
pos = 1;
for (int j = 1; j < star; ++j)
{
while (code_del[pos])
++pos;
child1[i][j] = parent2[i][pos];
code_del[pos] = 1;
}
for (int j = end + 1; j <= code_length; ++j)
{
while (code_del[pos])
++pos;
child1[i][j] = parent2[i][pos];
code_del[pos] = 1;
} //因为父亲2的染色体给了孩子相当于删除
for (int j = 1; j <= code_length; ++j)
{
code_del[j] = 0;
}
for (int j = star; j <= end; ++j)
{
for (int k = 1; k <= code_length; ++k)
{
if ((parent1[i][k] == parent2[i][j]) && (parent1_list[k] == parent2_list[j]))//值相等且排名相等
{
code_del[k] = 1;
break;
}
}
}
pos = 1;
for (int j = 1; j < star; ++j)
{
while (code_del[pos])
++pos;
child2[i][j] = parent1[i][pos];
code_del[pos] = 1;
}
for (int j = end + 1; j <= code_length; ++j)
{
while (code_del[pos])
++pos;
child2[i][j] = parent1[i][pos];
//cout << pos << " " << parent1[i][pos]<< " ";
code_del[pos] = 1;
} //因为父亲1的染色体给了孩子相当于删除 //继承结束
/*cout << "star:" << star << "end:" << end << endl;
cout << "幼儿:" << endl;
for (int j = 1; j <= code_length; ++j)
{
cout << child1[i][j];
}
cout << endl;
for (int j = 1; j <= code_length; ++j)
{
cout << child2[i][j];
}
cout << endl;*/
if (balance <= balance_coeff) //变异开始
{
do
{
star = rand() % code_length + 1;
end = rand() % code_length + 1;
} while (star >= end);
swap(child1[i][star], child1[i][end]);
swap(child2[i][star], child2[i][end]);
/*cout << "star:" << star << "end:" << end << endl;//
cout << "变异青年:" << endl;//
for (int j = 1; j <= code_length; ++j)
{
cout << child1[i][j];//
}
cout << " " << "耗时:" << temp;
cout << endl;//
for (int j = 1; j <= code_length; ++j)
{
cout << child2[i][j];
}
temp = decode(child2[i]);
cout << " " << "耗时:" << temp2;
cout << endl;*/
}
}
for (int i = 1; i <= 4 * half_population_num; ++i) /采用指针方法留优秀的开始
{
list_population[i].first = i;
list_population[i].second = decode(population[i]);
}
//lower_limit = accept_coeff * list_population[1].second;//更新灭绝条件
sort(list_population + 1, list_population + 1 + 4 * half_population_num, time_compare);
for (int i = 1; i <= 2* half_population_num; ++i)
{
swap(population[i], population[list_population[i].first]);
}///采用指针方法留优秀的结束(为什么不只找前面一半,是防止前面parent指针和后面child指针重复)
/*cout << "存活" << endl;
for (int a = 1; a <= half_population_num; ++a)
{
for (int b = 1; b <= code_length; ++b)
{
cout << parent1[a][b];
}
cout << " " << decode(parent1[a]);
cout << endl;
}
for (int a = 1; a <= half_population_num; ++a)
{
for (int b = 1; b <= code_length; ++b)
{
cout << parent2[a][b];
}
cout << " " << decode(parent2[a]);
cout << endl;
}*/
}
show(population[1]);
clock_end = clock();
cout << "结束时间:" <<clock_end<< endl;
cout << "运行时间:" << clock_end - clock_star<<"ms";
delete[] code_del;
delete[] parent1_vector;
delete[] parent2_vector;
delete[] parent1_list;
delete[] parent2_list;
delete[] list_ancestors;
delete[] list_population;
for (int i = 1; i <= 20 * half_population_num; ++i)
{
//cout << i;
//cout << population[i] << endl;
delete[] ancestors[i];
}
delete[] ancestors;
}
template<class T>
scheduling<T>::~scheduling()
{
delete[]job_list;
delete[]job_pro_num;
//delete[]code_seed;
//delete[]machine;
for (int i = 1; i <= machine_num; ++i)
{
delete[]get<0>(machine_plan[i]);
delete[]get<1>(machine_plan[i]);
delete[]get<2>(machine_plan[i]);
delete[]get<3>(machine_plan[i]);
}
delete[]machine_plan;
delete[]job_now_time;
for (int i = 1; i <= job_num; ++i)
delete[] job[i];
delete[]job;
}
void rand_file(string s1, int job_num_in, int machine_num_in, int max_working_procedure_in)
{
fstream job_file;
int temp;
job_file.open(s1, ios::binary | ios::out);
if (!job_file)
throw "文件不存在";
job_file << job_num_in;
job_file << ' ';
job_file << machine_num_in;
job_file << ' ';
job_file << max_working_procedure_in;
job_file << '\r';
job_file << '\n';
for (int i = 1; i <= job_num_in; ++i)
{
do
{
temp = rand() % max_working_procedure_in + 1;
} while (temp < (max_working_procedure_in / 2));
for (int k = 1; k < temp; ++k)
{
job_file << rand() % machine_num_in + 1;
job_file << ' ';
job_file << rand() % 100 + 1;
job_file << ' ';
}
for (int k = temp; k <= max_working_procedure_in; ++k)
{
job_file << 0;
job_file << ' ';
job_file << 0;
job_file << ' ';
}
job_file << '\r';
job_file << '\n';
}
job_file.close();
}
int main()
{
string s1 = "jobfile1.txt";
//rand_file(s1, 1000, 100, 50);
//scheduling<float> A(s1);
scheduling<int> A(s1);
A.inheritance(100, 20, 0.1, 0.01);
//A.inheritance(300, 100, 0.1, 0.5);
//A.inheritance(300, 1000, 0.1, 0.5);
}