《数据结构、算法与应用 —— C++语言描述》学习笔记 — 优先级队列 — 应用

news/2024/5/19 4:57:33 标签: 算法, c++, 数据结构, 堆排序, 调度

数据结构算法与应用 —— C++语言描述》学习笔记 — 优先级队列 — 应用

  • 一、堆排序
  • 二、机器调度
    • 1、使用堆建立 LPT
    • 2、实现
      • (1)修改heap
      • (2)job
      • (3)调度类头文件
      • (4)机器类
      • (5)调度功能
    • 3、测试

一、堆排序

堆可以用来实现 n 个元素的排序,所需时间为 O ( n l o g n ) O(nlog n) O(nlogn)。先用 n 个待排序的元素来初始化一个大根堆。然后从堆中逐个提取元素。结果,这些元素按非递增顺序排序。初始化的时间为 O ( n ) O(n) O(n),每次删除的时间为 O ( l o g n ) O(log n) O(logn),因此总时间为 O ( n l o g n ) O(nlog n) O(nlogn)

#pragma once
#include "heap.h"

template<typename T, typename Pred = std::greater<T>>
void heapSort(T arr[], int arrayLength)
{
	heap<T, Pred> h;
	h.initialize(arr, arrayLength);

	for (int i = 0; i < arrayLength; ++i)
	{
		arr[i] = h.top();
		h.pop();
	}
}

二、机器调度

这里的机器调度问题和前面的工厂仿真类似。一个工厂具有 m 台一样的机器,我们有 n 个任务需要处理。假设作业 i 的处理时间为 t i t_i ti,这个时间包括把作业放入机器和从机器上取下的时间。所谓调度是指按作业在机器上的运行时间分配作业,使得:
① 一台机器在同一时间内只能处理一个作业。
② 一个作业不能同时在两台机器上处理。
③ 一个作业 i 的处理时间是 t i t_i ti个时间单位。

假如每台机器在0时刻都是可用的,完成时间或调度长度是指完成所有作业的时间。在一个非抢先调度中,一项作业 i 在一台机器上处理,从时刻 s i s_i si开始,到时刻 s i + t i s_i+t_i si+ti结束。

假设在3台机器处理7个作业,对应的处理时间分别为(2, 14, 4, 16, 6, 5, 3),处理时序如图:
在这里插入图片描述
图中的调度是一个在给定处理时间前提下最小完成时间的调度。为了认识到这一点,注意,处理时间的总和为50。因此,三台机器调度都最少需要 ⌈ 50 3 ⌉ = 17 \lceil\frac{50}{3}\rceil=17 350=17

调度问题是一个NP-复杂问题,相关的介绍可以参考P问题、NP问题、NPC问题(NP完全问题)、NPH问题和多项式时间复杂度。在我们的调度问题中,采用了一个简单调度策略,称为最长处理时间(LPT),它的调度长度是最优调度长度的 4 3 − 1 3 m \frac{4}{3}-\frac{1}{3m} 343m1。在 LPT 算法中,作业按处理时间的递减顺序排列。当一个作业需要分配时,总是分配给最先变为空闲的机器。没有固定的分配方案。

1、使用堆建立 LPT

使用堆来建立 LPT调度方案的时间性能为 O ( n l o g n ) O(nlog n) O(nlogn)。当 n ≤ m n\le m nm时,只需要将作业 i 在0~ t i t_i ti 时间内分配给机器 i 来处理。当 n > m n \gt m n>m时,可以首先利用堆排序将作业按处理时间递增排序排列。为了建立 LPT 调度方案,作业按相反次序进行分配。

为了决定将一个作业分配给哪一台机器,必须知道哪台机器最先空闲。为此,维持一个 m 台机器的小根堆,元素类型为 machine,它有数据成员可用时间和机器编号。pop函数用于获取下一个最先可用的机器。当机器的可用时间增加后,需要再次插入小根堆。

类似地,我们使用类 job 表示作业,包含编号和时长两个数据成员。作业同样需要保存在堆中,但是是大根堆。

2、实现

(1)修改heap

前面实现的堆有些问题,其实并没有真正的支持比较函数的设置。我们需要修改后默认构造函数及其调用:

inline heap<T, Predicate>::heap(int capacity,const Predicate& pred):
	comp(pred)
{
	elements = new T[capacity];
	this->capacity = capacity;
}

template<typename T, typename Predicate>
inline void heap<T, Predicate>::initialize(T* origin, int arrayLength)
{
	auto tempHeap = new heap(arrayLength * 2, comp);
	...
}

(2)job

job 类为作业类,需要所有文件都可以访问:

// job.h
#pragma once
struct Job
{
	int id;
	int totalTime;
};

(3)调度类头文件

// JobSchedule.h
#pragma once

class Job;
class JobSchedule
{
public:
	void schedule(Job* jobs, int jobNum, int machineNum);
};

(4)机器类

机器类为调度功能使用,定义在调度类的实现文件中:

// JobSchedule.cpp
struct machine
{
	int id;
	int availableTime = 0;
};

(5)调度功能

// JobSchedule.cpp
#include "JobSchedule.h"
#include "Job.h"
#include "../heap.h"
#include <iostream>
#include <functional>
using namespace std;

void JobSchedule::schedule(Job* jobs, int jobNum, int machineNum)
{
	if (jobNum < machineNum)
	{
		cout << "schedule each job on a different machine." << endl;
		return;
	}

	auto machineComp = [](machine& left, machine& right) {return left.availableTime < right.availableTime; };
	heap<machine, decltype(machineComp)> machineHeap(10, machineComp);

	for (int i = 1; i <= machineNum; ++i)
	{
		machineHeap.push({ i, 0 });
	}

	auto jobComp = [](Job& left, Job& right) {return left.totalTime >= right.totalTime; };
	heap<Job, decltype(jobComp)> jobHeap(10, jobComp);
	jobHeap.initialize(jobs, jobNum);

	while (!jobHeap.empty())
	{
		auto curJob = jobHeap.top();
		jobHeap.pop();

		auto curMachine = machineHeap.top();
		machineHeap.pop();

		cout << "schedule Job " << curJob.id << " on machine " << curMachine.id << " from " << curMachine.availableTime
			<< " to " << curMachine.availableTime + curJob.totalTime << endl;

		curMachine.availableTime += curJob.totalTime;
		machineHeap.push(curMachine);
	}
}

3、测试

#pragma once
#include "JobSchedule.h"
#include "Job.h"
void test()
{
	JobSchedule schedule;
	
	Job jobs[] = { {1, 2}, {2, 14}, {3, 4}, {4, 16}, {5, 6}, {6, 5}, {7, 3} };
	schedule.schedule(jobs, sizeof(jobs) / sizeof(Job), 3);
}

在这里插入图片描述
和我们前面图表中的结果是一致的。


http://www.niftyadmin.cn/n/1340350.html

相关文章

《数据结构、算法与应用 —— C++语言描述》学习笔记 — 优先级队列 — 应用 — 霍夫曼编码

《数据结构、算法与应用 —— C语言描述》学习笔记 — 优先级队列 — 应用 — 霍夫曼编码一、原理及构造过程1、原理2、扩展二叉树与霍夫曼编码3、构造步骤二、实现1、类图2、公共常量和结构体3、基类定义4、霍夫曼树的构建5、生成映射关系6、压缩类定义7、压缩类实现8、解压缩…

《C++标准库》学习笔记 — 通用工具 — Clock 和 Timer

《C标准库》学习笔记 — 通用工具 — Clock 和 Timer一、Chrono 程序库概观二、duration1、duration的算术运算2、Duration 的其他操作3、duration_cast4、rep 和 period三、clock 和 timepoint1、Clock2、Timepoint一、Chrono 程序库概观 Chrono 程序库的设计&#xff0c;是希…

《数据结构、算法与应用 —— C++语言描述》学习笔记 — 竞赛树

《数据结构、算法与应用 —— C语言描述》学习笔记 — 竞赛树一、赢者树二、二叉树的数组描述&#xff08;补充&#xff09;1、声明2、实现三、赢者树1、抽象数据类型2、赢者树的表示3、声明4、初始化5、重新组织比赛6、获取胜者一、赢者树 假定有 n 个选手参加一次网球比赛。…

《C++标准库》学习笔记 — STL — 容器与算法

《C标准库》学习笔记 — STL — 容器与算法一、reverse 迭代器二、Funtion Object1、pass by value 的函数对象2、获得结果的办法&#xff08;1&#xff09;通过引用传值方式传递函数对象&#xff08;2&#xff09;利用 for_each 算法的返回值三、Predicate 和 remove_if四、la…

《数据结构、算法与应用 —— C++语言描述》学习笔记 — 竞赛树 — 应用 — 箱子装载

《数据结构、算法与应用 —— C语言描述》学习笔记 — 竞赛树 — 应用 — 箱子装载一、问题描述二、近似算法三、赢者树和最先适配法四、最先适配法的实现1、问题修复2、find3、FF实现4、测试代码一、问题描述 在箱子装载问题中&#xff0c;箱子的数量不限&#xff0c;每个箱子…

集装箱学习(两):动手模拟AOP

简单的说&#xff0c;Spring是一个轻量级的控制反转(IOC&#xff09;和面向切面&#xff08;AOP)的容器框架。上文已经介绍模拟IoC实现&#xff0c;这篇文章来动手模拟AOP。 AOP简述 面向对象强调"一切皆是对象"&#xff0c;是对真实世界的模拟。然而面向对象也并不是…

《数据结构、算法与应用 —— C++语言描述》学习笔记 — 二叉搜索树

《数据结构、算法与应用 —— C语言描述》学习笔记 — 二叉搜索树一、二叉搜索树1、二叉搜索树特征2、索引二叉搜索树二、抽象数据类型三、二叉搜索树实现1、字典类接口修改2、接口3、查找接口4、删除&#xff08;1&#xff09;父节点扩展&#xff08;2&#xff09;后继节点&am…

《数据结构、算法与应用 —— C++语言描述》学习笔记 — 平衡搜索树 — AVL 树

《数据结构、算法与应用 —— C语言描述》学习笔记 — 平衡搜索树 — AVL 树一、AVL 树1、定义2、高度3、描述4、搜索5、插入&#xff08;1&#xff09;特点&#xff08;2&#xff09;旋转&#xff08;3&#xff09;算法6、删除二、实现1、节点类修改2、二叉树修改3、BST 修改&…