当前位置: 首页 > news >正文

深入理解区间调度问题:从贪心算法到动态规划的加权优化

引言

区间调度问题是一个经典的算法问题,它广泛应用于任务调度、资源分配等领域。本文将从基础的区间调度问题出发,介绍如何通过贪心算法高效解决这个问题。接着,我们会探讨问题的扩展——加权区间调度问题,并展示如何使用动态规划来解决该问题,以实现权重的最大化。通过对这两种算法的实现和对比,您将理解不同算法的应用场景、优劣以及最优性。

一、问题描述

1.1 经典的区间调度问题

经典的区间调度问题定义如下:

  • 我们有 (n) 个请求,每个请求 (i) 具有开始时间 (s(i)) 和结束时间 (f(i)),且 (s(i) < f(i))。
  • 两个请求 (i) 和 (j) 是兼容的,若它们的时间区间不重叠,即 (f(i) \leq s(j)) 或 (f(j) \leq s(i))。
  • 目标是从这些请求中选择一个最大兼容子集,确保所选请求的时间不重叠,并且尽可能多地选择请求。

例如,给定一组请求:

  • 请求1:(s(1) = 1, f(1) = 4)
  • 请求2:(s(2) = 3, f(2) = 5)
  • 请求3:(s(3) = 0, f(3) = 6)
  • 请求4:(s(4) = 5, f(4) = 7)
  • 请求5:(s(5) = 8, f(5) = 9)
  • 请求6:(s(6) = 5, f(6) = 9)

我们需要找到一个不重叠的最大请求子集。

1.2 加权区间调度问题

加权区间调度问题是经典区间调度问题的扩展:

  • 除了每个请求 (i) 具有开始时间 (s(i)) 和结束时间 (f(i)) 之外,每个请求还附带一个权重 (w(i))。
  • 目标从这些请求中选择一个最大权重兼容子集,即所有请求不重叠,并且所选请求的总权重最大。

二、贪心算法解决经典区间调度问题

在经典的区间调度问题中,贪心算法表现良好。它的核心思想是:每次选择结束时间最早的请求,这样可以为后续的请求留出更多的时间空间,保证能够选择尽可能多的不重叠请求。

2.1 贪心算法步骤
  1. 排序:按照请求的结束时间 (f(i)) 对所有请求进行升序排序。
  2. 选择:每次选择当前结束时间最早的请求,并排除与之重叠的其他请求。
  3. 重复:继续选择剩余请求中结束时间最早的请求,直到所有请求都处理完。
2.2 Python实现
class Request:def __init__(self, start, finish):self.start = startself.finish = finishdef greedy_interval_scheduling(requests):# 按结束时间排序sorted_requests = sorted(requests, key=lambda x: x.finish)selected_requests = []last_finish_time = 0# 选择结束时间最早且不重叠的请求for request in sorted_requests:if request.start >= last_finish_time:selected_requests.append(request)last_finish_time = request.finishreturn selected_requests# 测试数据
requests = [Request(1, 4),Request(3, 5),Request(0, 6),Request(5, 7),Request(8, 9),Request(5, 9)
]selected_requests = greedy_interval_scheduling(requests)# 输出结果
print("贪心算法选择的请求:")
for r in selected_requests:print(f"请求({r.start}, {r.finish})")
2.3 运行结果
贪心算法选择的请求:
请求(1, 4)
请求(5, 7)
请求(8, 9)

贪心算法的时间复杂度为 (O(n \log n)),其中 (n) 是请求的数量,排序操作占据了主要的时间复杂度。

三、贪心算法的数学证明

贪心算法的正确性并非仅仅依赖直觉。事实上,我们可以通过数学归纳法严格证明,在经典的区间调度问题中,贪心算法始终能够找到最优解。

3.1 贪心选择策略

贪心算法的选择规则是:每次选择结束时间最早的请求,然后排除与其重叠的其他请求。这种选择保证了剩余的区间有最多的空间选择更多的请求。

3.2 贪心算法输出的区间序列

贪心算法输出的区间序列为:
[
(s(i_1), f(i_1)), (s(i_2), f(i_2)), \dots, (s(i_k), f(i_k))
]
满足:
[
s(i_1) < f(i_1) \leq s(i_2) < f(i_2) \leq \dots \leq s(i_k) < f(i_k)
]
即,所有区间按照结束时间排序,并且这些区间互不重叠。

3.3 数学归纳法证明
基础情况:

当最优解只包含一个区间时(即 (k^* = 1)),任意选择该区间都是最优的。

归纳假设:

假设对于最多选择 (k^*) 个不重叠区间的情况,贪心算法能够找到最优解。

归纳步骤:

假设最优解包含 (k^* + 1) 个不重叠区间,设其为:
[
S^* = {<s(j_1), f(j_1)>, <s(j_2), f(j_2)>, \dots, <s(j_{k^+1}), f(j_{k^+1})>}
]
贪心算法首先选择了结束时间最早的区间 (<s(i_1), f(i_1)>),此时必有 (f(i_1) \leq f(j_1))。我们可以用 (<s(i_1), f(i_1)>) 替换最优解中的第一个区间 (<s(j_1), f(j_1)>),得到新的区间集合 (S^{**}):
[
S^** = {<s(i_1), f(i_1)>, <s(j_2), f(j_2)>, \dots, <s(j_{k^+1}), f(j_{k^+1})>}
]
该集合与 (S^*) 有相同数量的区间,并且仍然是合法的选择。因此,贪心算法在第一步的选择是正确的。

接下来,我们递归在剩余的区间上运行贪心算法。定义集合 (L’) 为所有开始时间不早于 (f(i_1)) 的区间集合。根据归纳假设,贪心算法能够在 (L’) 上找到最优解。将这些不重叠区间与 (<s(i_1), f(i_1)>) 结合,即得到了原问题的最优解。

因此,通过归纳法,我们证明了贪心算法在经典区间调度问题中总能找到最优解。

四、动态规划解决加权区间调度问题

对于加权区间调度问题,贪心算法并不能直接适用,因为它只考虑了时间约束,而忽略了请求的权重。为了解决该问题,我们使用动态规划。

4.1 动态规划思想

我们将定义一个数组 dp[i],表示前 (i) 个请求中可以选择的不重叠请求的最大权重。对每个请求 (i),我们有两个选择:

  1. 不选择请求 (i):此时最大权重等于 (dp[i-1])。
  2. 选择请求 (i):此时我们需要找到最后一个与请求 (i) 不重叠的请求 (j),则最大权重为 (w(i) + dp[j])。
4.2 递归关系

[
dp(i) = max(dp(i-1), w(i) + dp(p(i)))
]
其中 (p(i)) 表示最后一个与 (i) 不重叠的请求。

4.3 Python实现
class Request:def __init__(self, start, finish, weight):self.start = startself.finish = finishself.weight = weightdef find_last_non_conflicting(requests, i):low, high = 0, i - 1while low <= high:mid = (low + high) // 2if requests[mid].finish <= requests[i].start:if requests[mid + 1].finish <= requests[i].start:low = mid + 1else:return midelse:high = mid - 1return -1def weighted_interval_scheduling(requests):# 按结束时间排序requests = sorted(requests, key=lambda x: x.finish)n = len(requests)dp = [0] * ndp[0] = requests[0].weightselected_requests = [[] for _ in range(n)]selected_requests[0] = [requests[0]]for i in range(1, n):last_non_conflicting = find_last_non_conflicting(requests, i)include_weight = requests[i].weightif last_non_conflicting != -1:include_weight += dp[last_non_conflicting]exclude_weight = dp[i - 1]dp[i] = max(include_weight, exclude_weight)if include_weight > exclude_weight:selected_requests[i] = selected_requests[last_non_conflicting] + [requests[i]] if last_non_conflicting != -1 else [requests[i]]else:selected_requests[i] = selected_requests[i - 1]return dp[-1], selected_requests[-1]# 测试数据
requests = [Request(1, 4, 5),Request(3, 5, 1),Request(0, 6, 8),Request(5, 7, 4),Request(8, 9, 6),Request(5, 9, 3)
]max_weight, selected_requests = weighted_interval_scheduling(requests)# 输出结果
print("\n动态规划算法选择的请求及其总权重:")
for r in selected_requests:print(f"请求({r.start}, {r.finish}) - 权重: {r.weight}")
print(f"总权重: {max_weight}")

五、贪心算法与动态规划的对比

特性贪心算法动态规划
适用问题经典区间调度问题加权区间调度问题
考虑权重
时间复杂度(O(n \log n))(O(n \log n))
是否最优解是(不含权重)是(含权重)
算法思路选择结束时间最早的请求通过递归关系最大化总权重

六、结论

在经典的区间调度问题中,贪心算法通过每次选择结束时间最早的请求,可以高效地找到不重叠请求的最大集合。然而,当引入请求的权重后,贪心算法无法确保最优解。这时,动态规划提供了更适合的解决方案,能够在保证请求不重叠的前提下,找到总权重最大的请求子集。

通过对贪心算法的数学归纳法证明,我们进一步验证了其在经典区间调度问题中的最优性。而动态规划则为加权区间调度问题提供了有效的解决方案,确保总权重最大化。


http://www.mrgr.cn/news/19715.html

相关文章:

  • <数据集>安全背心识别数据集<目标检测>
  • 2.门锁_STM32_舵机设备实现
  • el-upload上传文件修改 File 中的name
  • 383. 赎金信
  • 应该怎么从0搭建一个图像识别系统,如果想考计算机的研究生应该如何准备
  • CAS理解和说明
  • 你做的SEO为什么效果不够好?
  • 模型压缩之知识蒸馏
  • 统计学习方法与实战——统计学习方法概论
  • 【技术前沿】智能反向寻车解决方案:提升停车场用户体验与运营效率
  • python如何连接人大金仓数据库
  • 鸿蒙-PC三栏布局
  • 性能测试经典案例解析——网上报税系统
  • 力扣62-不同路径(Java详细题解)
  • 高效易用的仓库进销存管理软件盘点,总有一款适合你!
  • 金仓 KES Plus 不充会员也好用
  • 安装Selenium进行web⾃动化测试
  • 在windows上怎么看动态库dll是64还是32位的
  • 10.6 应用层协议
  • 基于python的Selenium webdriver环境搭建(笔记)