【多机调度问题】“贪心算法”——《算法设计与分析(第五版)》

文章目录

  • 一、算法要求
    • 1. 思路
    • 2. 示例
  • 二、完整代码
    • 1. 主文件
    • 2. 头文件
    • 3. 效果展示
  • 三、补充


一、算法要求

给出一种作业调度方案,使所给的 n 个作业在尽可能短的时间内由 m 台机器加工处理完成。
约定,每个作业均可在任何一台机器上加工处理,但未完工前不允许中断处理。作业不能拆分成更小的子作业。

1. 思路

设有n个独立的作业{1,2,…,n},由m台相同的机器进行加工处理。作业i所需的处理时间为ti。现约定,任何作业可以在任何一台机器上加工处理,但未完工前不允许中断处理,任何作业不能拆分成更小的子作业。
多机调度问题要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。这个问题是一个NP完全问题,到目前为止还没有有效的解法。有时,这类问题用贪心选择策略可以设计出较好的近似算法。
采用最长处理时间作业优先的贪心选择策略,可以设计出解多机调度问题的较好的近似算法。按此策略,当n≤m时,只要将机器i的[0,t]时间区间分配给作业i即可。当n>m时,先将n个作业依其所需的处理时间从大到小排序,再依此顺序将作业分配给空闲的机器。

	1、把作业按加工所用的时间从大到小排序 
	2、如果作业数目比机器的数目少或相等,则直接把作业分配下去 
	3、 如果作业数目比机器的数目多,则每台机器上先分配一个作业,如下的作业分配时,是选那个表头上 s 最小的链表加入新作业。

2. 示例

【多机调度问题】“贪心算法”——《算法设计与分析(第五版)》_第1张图片


二、完整代码

1. 主文件

main.cpp:

// Project1: 矩阵连乘法

#include"Basic1.h"

int main() {
	//原始数据
	int n = 7,	//作业数
		m = 3,	//机器数
		maxtime;//最大作业时间
	int timeJob[] = { 2,14,4,16,6,5,3 };

	Job a[10];
	Header N[10];

	Console(timeJob, n);

	//初始化作业数据
	for (int i = 0; i < n; i++) {
		a[i].ID = i + 1;		//从1开始
		a[i].time = timeJob[i];
	}

	maxtime = Dispatch(a, N, n, m);
	cout << "\nThe shortest time required for all jobs to be completed is: " 
		<< maxtime << endl;
}

2. 头文件

Basic1.h:

#pragma once

#ifndef __BASIC1__
#define __BASIC1__

#include
#include
#include
using namespace std;

// a[] : 作业数组
// M[] : 定义作业数组
// s值 : 记录总时间

//数据结构定义:
typedef struct Job {
	//作业的信息,用于定义作业数组a[]
	int ID;
	int time;
}Job;

typedef struct JobNode {
	//作业节点信息,记录每个机器运行的作业
	int ID;
	int time;
	JobNode* next;
}JobNode, * pJobNode;

typedef struct Header {
	//机器头节点,其中s为该机器需要运行作业的总时间,用于定义作业数组M[]
	int s;
	pJobNode next;
}Header, pHeader;

//功能函数定义
int SelectMin(Header* N, int m) {
	//读取头节点的s值,得出运行时间最小的机器下标并返回
	int k = 0;
	for (int i = 1; i < m; i++)
		if (N[i].s < N[k].s)
			k = i;
	return k;
}

int SelectMax(Header* N, int m) {
	//用于最后得出所有作业完成至少需要多少时间的函数
	//也是对头节点的s进行比较
	int k = 0;
	for (int i = 1; i < m; i++)
		if (N[i].s > N[k].s)
			k = i;
	return k;
}


void Sort(Job* a, int n) {
	//采用冒泡排序策略
	Job temp;
	for (int i = 0; i < n; i++)
		for (int j = i + 1; j < n; j++)
			if (a[i].time < a[j].time) {
				temp = a[i];
				a[i] = a[j];
				a[j] = temp;
			}
}

void Console(int t[], int n) {
	cout << "The default job data is as follows: \n"
		<< setw(20) << "#Job ID :";
	for (int i = 0; i < n; i++) { cout << setw(3) << i + 1; }
	cout << endl << setw(20) << "#Time consuming :";
	for (int i = 0; i < n; i++) { cout << setw(3) << t[i]; }
	cout << endl << endl;
}

//打印对应机器的作业数
void Print(Header* N, int m) {
	JobNode* p;
	for (int i = 0; i < m; i++) {
		cout << "The job ID of the 'No." << i + 1 << "' machine running is: ";
		p = N[i].next;
		while (p) {
			cout << p->ID << " ";
			p = p->next;
		}
		cout << endl;
	}
}

//核心作业调度函数
int Dispatch(Job* a, Header* N, int n, int m) {
	int min, weight;
	JobNode* p, * q;     						//对应的链表指针的中间变量

	for (int i = 0; i < n; i++) {
		N[i].s = 0; N[i].next = NULL;			//将机器的总时间和作业指针初始化
	}

	Sort(a, n);									//将a[]作业数组从小到大进行排序

	if (n <= m)
		return a[0].time;						//作业数小于机器数的情况,直接取排好序的作业数组第一个元素

	for (int j = 0; j < n; j++) {
		//向堆区要块sizeof(int) * N 这么大的空间
		q = (pJobNode)malloc(sizeof(JobNode));  //给中间变量分配空间
		min = SelectMin(N, m);					//得出作业时间总和最小的机器的下标
		N[min].s += a[j].time;					//累计机器的总耗时

		q->ID = a[j].ID;						//将a数组中的元素变成作业节点数据
		q->time = a[j].time;
		q->next = NULL;							//尾指针置空

		if (!N[min].next)						//M未进行节点插入的情况,加入作业节点到头节点之后
			N[min].next = q;
		else {									//M已有作业节点的情况,加入作业节点到尾部
			p = N[min].next;
			while (p->next)
				p = p->next;
			p->next = q;
		}
	}
	Print(N, m);				//输出每台机器的运行作业的ID
	weight = SelectMax(N, m);	//得出最大的s的机器的下标
	return N[weight].s;			//返回至少需要的时间
}


#endif


3. 效果展示

【多机调度问题】“贪心算法”——《算法设计与分析(第五版)》_第2张图片


三、补充

在对问题求解时,总是作出在当前看来是最好的选择。也就是说,不从整体上加以考虑,它所作出的仅仅是在某种意义上的局部最优解。
若要用贪心算法求解某问题的整体最优解,必须首先证明贪心思想在该问题的应用结果就是最优解!
很多贪心类型的题目,不是最朴素的贪心,而是需要做一些变化,关键是找到贪心的本质!

(1)贪心策略
首先要确定贪心策略,选择当前看上去最好的一个方案。例如,挑选苹果,如果你认为个大的是最好的,那你每次都从苹果堆中拿一个最大的,作为局部最优解,贪心策略就是选择当前最大的苹果;如果你认为最红的苹果是最好的,那你每次都从苹果堆中拿一个最红的,贪心策略就是选择当前最红的苹果。因此根据求解目标不同,贪心策略也会不同。
(2)局部最优解
根据贪心策略,一步一步地得到局部最优解。例如,第一次选一个最大的苹果放起来,记为a,第二次再从剩下的苹果堆中选择一个最大的苹果放起来,记为a,以此类推。
(3)全局最优解
把所有的局部最优解合成为原来问题的一个最优解(a1,a2,…)。

文档供本人学习笔记使用,仅供参考。

你可能感兴趣的:(《算法设计与分析(第五版)》,算法,贪心算法,c++)