(流水车间调度 FSSP) NEH算法(优化后)c++源代码

    Taillard等将对NEH启发式算法进行了改进,将时间复杂度从 O(mn^3) 降至了O(mn^2) ,详细原理见我的上一篇文章:

    (22条消息) (流水车间调度 FSSP) NEH启发式算法改进 (时间复杂度从 O(mn^3) 降至 O(mn^2) )_JC.223的博客-CSDN博客

    对Taillard提出的改进NEH算法进行了复现,c++源代码如下:

    NEH.h:

#pragma once
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

class NEH
{
private:
	int n; //作业数量
	int m; //机器数量
	vector<pair<int, double>> sumPcsTime; //作业根据总加工时间降序排列,int为作业,double为总加工时间
	vector<int> res; //NEH算法求解的调度结果
	double makespan = 0; //NEH算法调度结果对应的最大完工时间

	//vector<vector<double>> e; //NEH中的e矩阵,e[i][j]为第i个作业在第j台机器上的完成时间
	//vector<vector<double>> q; //NEH中的q矩阵,q[i][j]为第i个作业在第j台机器上开始的时间到所有操作结束的时间
	//vector<vector<double>> f; //NEH中的f矩阵,f[i][j]为插入第i个位置的作业k在第j台机器上的最早相对完成时间

public:
	NEH(const vector<vector<double>>& matrixT);  //输入:加工时间矩阵T (从0开始,T[0][0]即作业1在机器1上加工时间)
	vector<int> getRes(); //输出NEH算法求解结果
	double getMakespan(); //输出NEH算法调度结果对应的最大完工时间
	vector<vector<double>> calculateMakespan(vector<int> res, const vector<vector<double>>& matrixT); //直接计算最大完工时间的函数
	
    //测试用
	void printPcsTime(); //测试用,打印sumPcsTime
};

    NEH.cpp:

#include "NEH.h"
#include <numeric>
using namespace std;

NEH::NEH(const vector<vector<double>>& matrixT) {
	//初始化 作业数量n,机器数量m
	n = matrixT.size();
	m = matrixT[0].size();
	sumPcsTime.resize(n);
	//测试用,打印作业数n、机器数m、加工时间矩阵matrixT
	cout << "--NEH算法输入--" << endl;
	cout << "作业数:" << n << "  机器数:" << m << endl << "加工时间:" << endl;
	for (size_t i = 0; i < matrixT.size(); i++)
	{
		for (size_t j = 0; j < matrixT[0].size(); j++)
		{
			cout << matrixT[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;

	//NEH Step1:作业根据总加工时间降序排列
	//1.1 保存作业在各机器上的加工时间之和
	for (int i = 0; i < sumPcsTime.size(); i++) {
		sumPcsTime[i].first = i + 1;
		sumPcsTime[i].second = accumulate(matrixT[i].begin(), matrixT[i].end(), 0.0); //0 -- int;0.0 -- double
	}
	//测试用,打印排序前作业顺序以及对应的总加工时间
	cout << "NEH Step1 -- 作业根据总加工时间降序排列" << endl;
	cout << "排序前:" << endl;
	printPcsTime();

	//1.2 快速排序 O(nlogn)
	sort(sumPcsTime.begin(), sumPcsTime.end(), [](auto a, auto b) {
		return a.second > b.second;
		});
	//测试用,打印排序前作业顺序以及对应的总加工时间
	cout << "排序后:" << endl;
	printPcsTime();
	cout << endl;

	//NEH Step2:对排序后前两个作业进行调度
	//2.1 分别计算两种排列{sumPcsTime[0].first, sumPcsTime[1].first}、{sumPcsTime[1].first, sumPcsTime[0].first}的完工时间
	double makespan_1 = calculateMakespan(vector<int>{sumPcsTime[0].first, sumPcsTime[1].first}, matrixT)[1][m - 1];
	double makespan_2 = calculateMakespan(vector<int>{sumPcsTime[1].first, sumPcsTime[0].first}, matrixT)[1][m - 1];

	//2.2 返回完工时间更小的调度结果
	if (makespan_1 < makespan_2) {
		res.push_back(sumPcsTime[0].first);
		res.push_back(sumPcsTime[1].first);
	}
	else {
		res.push_back(sumPcsTime[1].first);
		res.push_back(sumPcsTime[0].first);
	}

	//测试用,打印两个调度结果
	cout << "NEH Step2 -- 对排序后前两个作业进行调度" << endl;
	vector<vector<double>> schedule1 = calculateMakespan(vector<int>{sumPcsTime[0].first, sumPcsTime[1].first}, matrixT);
	vector<vector<double>> schedule2 = calculateMakespan(vector<int>{sumPcsTime[1].first, sumPcsTime[0].first}, matrixT);
	cout << "第一种排列:" << sumPcsTime[0].first << ", " << sumPcsTime[1].first << endl << "对应调度矩阵:";
	for (size_t i = 0; i < schedule1.size(); i++)
	{
		for (size_t j = 0; j < schedule1[0].size(); j++)
		{
			cout << schedule1[i][j] << " ";
		}
		cout << "; ";
	}
	cout << "对应完工时间:" << schedule1[1][m - 1] << endl;
	cout << "第二种排列:" << sumPcsTime[1].first << ", " << sumPcsTime[0].first << endl << "对应调度矩阵:";
	for (size_t i = 0; i < schedule2.size(); i++)
	{
		for (size_t j = 0; j < schedule2[0].size(); j++)
		{
			cout << schedule2[i][j] << " ";
		}
		cout << "; ";
	}
	cout << "对应完工时间:" << schedule2[1][m - 1] << endl;
	cout << "NEH Step2 调度结果:" << res[0] << " " << res[1] << endl << endl;

	//测试用
	cout << "NEH Step3 -- k从3到n,计算使完工时间最小的第k个作业的最优插入位置" << endl;

	//NEH Step3:k从3到n,计算使完工时间最小的第k个作业的最优插入位置
	for (int k = 3; k <= n; k++) {
		//初始化e, q, k, Mmin, Imin;为了编程方便(对齐下标),扩充了q和f的大小
		vector<vector<double>> e(k, vector<double>(m + 1, 0)); //NEH中的e矩阵
		vector<vector<double>> q(k + 1, vector<double>(m + 2, 0)); //NEH中的q矩阵
		vector<vector<double>> f(k + 1, vector<double>(m + 1, 0)); //NEH中的f矩阵

		//3.1 计算第i个作业在第j台机器上的最早完成时间e[i][j]
		for (int i = 1; i <= k - 1; i++) {
			for (int j = 1; j <= m; j++) {
				e[i][j] = max(e[i][j - 1], e[i - 1][j]) + matrixT[res[i - 1] - 1][j - 1];
			}
		}

		//3.2 计算第i个作业在第j台机器上开始的时间到操作结束的时间q[i][j]
		for (int i = k - 1; i >= 1; i--) {
			for (int j = m; j >= 1; j--) {
				q[i][j] = max(q[i][j + 1], q[i + 1][j]) + matrixT[res[i - 1] - 1][j - 1];
			}
		}

		//3.3 计算插入第i个位置的作业k在第j台机器上的最早相对完成时间f[i][j]
		for (int i = 1; i <= k; i++) {
			for (int j = 1; j <= m; j++) {
				f[i][j] = max(f[i][j - 1], e[i - 1][j]) + matrixT[sumPcsTime[k - 1].first - 1][j - 1];
			}
		}

		//3.4 计算在第i个位置插入作业k时,部分完成时间Mi的值,并更新Mmin、Imin
		double Mmin = DBL_MAX;  //用于记录第k个作业插入不同位置时的最小完工时间
		int Imin = 0;  //用于记录第k个作业插入取得最小完工时间时对应的插入位置
		vector<double> recordMi;  //测试用,记录Mi
		for (int i = 1; i <= k; i++) {
			double Mi = 0;
			
			//计算Mi
			for (int j = 1; j <= m; j++) {
				if (f[i][j] + q[i][j] > Mi) Mi = f[i][j] + q[i][j];				
			}

			//比较,得出最优插入位置Imin,以及对应的Mmin
			if (Mi < Mmin) {
				Mmin = Mi;
				Imin = i;
			}

			//测试用,记录Mi
			recordMi.push_back(Mi); 
		}

		//在最优位置插入作业k,更新调度结果
		res.insert(res.begin() + (Imin - 1), sumPcsTime[k - 1].first);
		if (k == n) makespan = Mmin;

		//测试用
		cout << "****插入第" << k << "个作业 ( 作业 " << sumPcsTime[k - 1].first << " )****";
		cout << endl << "作业k插入位置i的完工时间Mi:";
		for (size_t i = 0; i < recordMi.size(); i++) {
			cout << recordMi[i] << " ";
		}
		cout << endl << "最小完工时间:" << Mmin << ", 对应插入位置:" << Imin << endl << "本次调度结果:";
		for (size_t i = 0; i < res.size(); i++) {
			cout << res[i] << " ";
		}
		cout << endl << endl;
	}

	//测试用,打印最终调度结果
	cout << "NEH算法生成的调度结果:" << endl;
	for (size_t i = 0; i < res.size(); i++)
	{
		cout << res[i] << " ";
	}
	cout << endl << "对应最大完工时间:" << makespan << endl;
}

vector<int> NEH::getRes() {
	return res;
}

double NEH::getMakespan() {
	return makespan;
}

vector<vector<double>> NEH::calculateMakespan(vector<int> res, const vector<vector<double>>& matrixT) {
	int n = res.size(), m = matrixT[0].size();
	vector<vector<double>> schedule(n, vector<double>(m, 0));  //调度矩阵,存放各机器上的调度结果
	// 计算第1个工件:sche(J1,1) = matrixT(J1,1); sche(J1,k) = sche(J1,k-1) + matrixT(J1,k)
	schedule[0][0] = matrixT[res[0] - 1][0];
	for (int j = 1; j < m; j++)
	{
		schedule[0][j] = schedule[0][j - 1] + matrixT[res[0] - 1][j];
	}
	/* 计算第2至n个工件:sche(Ji,1) = sche(Ji-1,1) + matrixT(Ji,1); 
                         sche(Ji,k) = max{sche(Ji-1,k), sche(Ji,k-1)} + matrixT(J1,k) */
	for (int i = 1; i < n; i++)
	{
		schedule[i][0] = schedule[i - 1][0] + matrixT[res[i] - 1][0];
		for (int j = 1; j < m; j++)
		{
			schedule[i][j] = max(schedule[i - 1][j], schedule[i][j - 1]) + matrixT[res[i] - 1][j];
		}
	}
	// 最大完工时间:schedule[n - 1][m - 1]
	return schedule;
}

//测试用
void NEH::printPcsTime() {
	cout << "作业顺序:";
	for (size_t i = 0; i < sumPcsTime.size(); i++)
	{
		cout << sumPcsTime[i].first << " ";
	}
	cout << endl << "总加工时间:";
	for (size_t i = 0; i < sumPcsTime.size(); i++)
	{
		cout << sumPcsTime[i].second << " ";
	}
	cout << endl;
}

    main.cpp:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "NEH.h"
using namespace std;

//测试NEH
void testNEH() {
    
    //vector<vector<double>> MatrixT(20, vector<double>(5, 0));
    //Taillard-PFSP测试案例:ta001  已知最优解:1278
    //对应排序: { 3,17,15,8,9,6,5,14,16,7,11,13,18,19,1,4,2,10,20,12 }
    //MatrixT[0] = { 54, 79, 16, 66, 58 };
    //MatrixT[1] = { 83, 3, 89, 58, 56 };
    //MatrixT[2] = { 15, 11, 49, 31, 20 };
    //MatrixT[3] = { 71, 99, 15, 68, 85 };
    //MatrixT[4] = { 77, 56, 89, 78, 53 };
    //MatrixT[5] = { 36, 70, 45, 91, 35 };
    //MatrixT[6] = { 53, 99, 60, 13, 53 };
    //MatrixT[7] = { 38, 60, 23, 59, 41 };
    //MatrixT[8] = { 27, 5, 57, 49, 69 };
    //MatrixT[9] = { 87, 56, 64, 85, 13 };
    //MatrixT[10] = { 76, 3, 7, 85, 86 };
    //MatrixT[11] = { 91, 61, 1, 9, 72 };
    //MatrixT[12] = { 14, 73, 63, 39, 8 };
    //MatrixT[13] = { 29, 75, 41, 41, 49 };
    //MatrixT[14] = { 12 ,47 ,63 ,56 ,47 };
    //MatrixT[15] = { 77 ,14 ,47 ,40 ,87 };
    //MatrixT[16] = { 32 ,21 ,26 ,54 ,58 };
    //MatrixT[17] = { 87 ,86 ,75 ,77 ,18 };
    //MatrixT[18] = { 68 , 5 ,77 ,51 ,68 };
    //MatrixT[19] = { 94 ,77 ,40 ,31 ,28 };
    
    vector<vector<double>> MatrixT = { {19,46,65},{44,63,12},{85,56,98},{59,68,25},{87,66,53},{51,4,63} };

    NEH neh(MatrixT);
    cout << "直接计算得出的最大完工时间:" 
        << neh.calculateMakespan(neh.getRes(), MatrixT)[MatrixT.size() - 1][MatrixT[0].size() - 1] << endl;
}

int main()
{
    srand((unsigned)time(NULL));
    testNEH();

    system("pause");
}

    使用的测试案例来自:

    (19条消息) 基础NEH算法 在 流水车间调度问题(FlowShop)问题 的应用(C++)_Liablbility的博客-CSDN博客_neh算法

    测试结果:

    可以看到调度结果为{ 6, 2, 1, 3, 5, 4 },最大完工时间为445,与上述链接一致。

    main.cpp中还包含了Taillard的一个测试案例(ta001),可自行测试。

参考文献:

[1]    TAILLARD E. Some efficient heuristic methods for the flow shop sequencing problem[J]. European Journal of Operational Research, 1990,47(1): 65-74.

[2]    Muhammad, Nawaz, and, et al. A heuristic algorithm for the m-machine, n-job flow-shop sequencing problem[J]. Omega, 1983.

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值