排序算法之:基于最小平均等待时间调度(Shortest Average Wait Time Scheduling)的贪心策略证明与算法实现
字数 2684 2025-12-22 23:37:53

排序算法之:基于最小平均等待时间调度(Shortest Average Wait Time Scheduling)的贪心策略证明与算法实现

题目描述
假设有一个单处理器系统,需要处理一组任务。每个任务用一个整数数组 tasks 表示,其中 tasks[i] 是第 i 个任务的处理时间(非负)。任务按照到达顺序依次等待处理,但处理器可以自由选择接下来处理哪个任务(即可以重新安排顺序)。目标是最小化所有任务的平均等待时间。等待时间定义为从任务到达(假设所有任务同时到达,时间为0)到开始执行的时间。请设计一个算法,输出任务的最优执行顺序,并证明该顺序确实最小化了平均等待时间。

例如,任务处理时间数组为 [3, 1, 2]
如果按原顺序执行,等待时间分别为:任务0(3)等待0,任务1(1)等待3,任务2(2)等待3+1=4,总等待时间 = 0+3+4=7,平均等待时间 = 7/3 ≈ 2.333。
如果按处理时间升序执行,即顺序为 [1, 2, 3],则等待时间分别为:0, 1, 1+2=3,总等待时间 = 0+1+3=4,平均等待时间 = 4/3 ≈ 1.333。显然更优。


解题过程循序渐进讲解

第一步:问题形式化
设有 n 个任务,处理时间为 p₁, p₂, …, pₙ。假设所有任务在时间 0 同时到达(这是常见简化,若不同到达时间则问题更复杂,需考虑“最短剩余时间优先”等调度策略)。
若执行顺序是任务索引的一个排列 π₁, π₂, …, πₙ,则:

  • 任务 π₁ 开始于时间 0,等待时间 W₁ = 0
  • 任务 π₂ 开始于时间 p₍π₁₎,等待时间 W₂ = p₍π₁₎
  • 任务 π₃ 开始于时间 p₍π₁₎ + p₍π₂₎,等待时间 W₃ = p₍π₁₎ + p₍π₂₎
  • ……
  • 任务 πₖ 的等待时间 Wₖ = Σⱼ=1..k-1 p₍πⱼ₎

总等待时间 T = Σₖ₌₁ⁿ Wₖ
平均等待时间 = T / n
因为 n 固定,最小化平均等待时间等价于最小化总等待时间 T

第二步:将总等待时间写成处理时间的函数
T 展开:

T = W₁ + W₂ + … + Wₙ
  = 0 + p₍π₁₎ + (p₍π₁₎ + p₍π₂₎) + … + (p₍π₁₎ + … + p₍πₙ₋₁₎)

数一下每个处理时间在总和中出现的次数:

  • p₍π₁₎ 出现在 W₂, W₃, …, Wₙ 中,即出现 n-1 次。
  • p₍π₂₎ 出现在 W₃, …, Wₙ 中,即出现 n-2 次。
  • p₍πₖ₎ 出现 n-k 次。
  • 最后 p₍πₙ₎ 出现 0 次。

因此:

T = (n-1)·p₍π₁₎ + (n-2)·p₍π₂₎ + … + 0·p₍πₙ₎

T = Σₖ₌₁ⁿ (n-k)·p₍πₖ₎

第三步:贪心策略的猜想
观察公式:T 是处理时间的加权和,权重从 n-1 递减到 0。
为了最小化 T,显然应该将最小的处理时间赋予最大的权重,即:让最小的 p 对应最大的权重 n-1,次小的 p 对应次大的权重 n-2,依此类推。
这等价于将处理时间按升序排列,先执行最短的任务,再执行次短的,……,最后执行最长的。
这就是最短处理时间优先(Shortest Processing Time first, SPT)策略。

第四步:严格证明贪心策略最优
用交换论证法(exchange argument):
假设存在一个最优顺序,其中相邻两个任务 ABAB 前面执行,但 p_A > p_B(即不是按升序)。
考虑交换 AB 的位置,其他任务顺序不变。
交换前,AB 对总等待时间的贡献(只考虑它们自身及它们之后的任务受它们影响的部分)为:
A 之前的任务总时间为 S,则:

  • A 的等待时间 = S(对总等待时间贡献一次 S,因为 A 自身等待时间计入总和)。
  • B 的等待时间 = S + p_A
  • B 之后的任务,每个都多等了 p_A + p_B 的时间(因为它们在 AB 之后)。

交换后,顺序变为 BA 前:

  • B 的等待时间 = S
  • A 的等待时间 = S + p_B
  • A 之后的任务,每个都多等了 p_B + p_A 的时间(与交换前相同,因为 p_A + p_B = p_B + p_A)。

比较交换前后总等待时间的变化:
交换前,AB 自身等待时间之和 = S + (S + p_A) = 2S + p_A
交换后,BA 自身等待时间之和 = S + (S + p_B) = 2S + p_B
由于 p_B < p_A,交换后减少了 p_A - p_B > 0
而其他任务的等待时间不变(因为 AB 的总处理时间相同,只是顺序不同,不影响后面任务的开始时间)。
因此交换后总等待时间更小,与“最优”矛盾。
所以,最优顺序中任意相邻两个任务必须满足 p_A ≤ p_B,即整个序列按处理时间非递减排序。
这就证明了 SPT 顺序是唯一最优的(若处理时间相同,顺序可任意)。

第五步:算法实现
算法非常简单:

  1. 输入数组 tasks(处理时间列表)。
  2. tasks 进行升序排序。
  3. 输出排序后的顺序,即为最优执行顺序。

同时可计算最小总等待时间:

total_wait = 0
current_time = 0
for t in sorted_tasks:
    total_wait += current_time   # 当前任务开始前的等待时间
    current_time += t
average_wait = total_wait / n

或直接按公式 T = Σₖ₌₁ⁿ (n-k)·tasks_sorted[k-1] 计算。

第六步:示例演算
tasks = [3, 1, 2]
排序后为 [1, 2, 3]
总等待时间 = (3-1)1 + (3-2)2 + (3-3)3 = 21 + 12 + 03 = 4。
平均等待时间 = 4/3 ≈ 1.333。

第七步:扩展讨论

  • 若任务有不同到达时间,则问题变为“最短剩余时间优先(SRTF)”调度,需用优先队列模拟。
  • 若考虑截止时间,则可能变为“最早截止时间优先(EDF)”等。
  • 平均等待时间最小化等价于平均周转时间(等待时间+处理时间)最小化,因为总处理时间固定。
  • 该贪心策略是调度理论中的一个经典结论,是“最小化平均等待时间”的标准解法。

总结
本题展示了如何通过将总等待时间表示为加权和,发现权重递减规律,从而推导出 SPT 贪心策略,并用交换论证法严格证明其最优性。实现上只需一次排序,时间复杂度 O(n log n),空间 O(1) 或 O(n)。

排序算法之:基于最小平均等待时间调度(Shortest Average Wait Time Scheduling)的贪心策略证明与算法实现 题目描述 : 假设有一个单处理器系统,需要处理一组任务。每个任务用一个整数数组 tasks 表示,其中 tasks[i] 是第 i 个任务的处理时间(非负)。任务按照到达顺序依次等待处理,但处理器可以 自由选择 接下来处理哪个任务(即可以重新安排顺序)。目标是 最小化所有任务的平均等待时间 。等待时间定义为从任务到达(假设所有任务同时到达,时间为0)到开始执行的时间。请设计一个算法,输出任务的最优执行顺序,并证明该顺序确实最小化了平均等待时间。 例如,任务处理时间数组为 [3, 1, 2] 。 如果按原顺序执行,等待时间分别为:任务0(3)等待0,任务1(1)等待3,任务2(2)等待3+1=4,总等待时间 = 0+3+4=7,平均等待时间 = 7/3 ≈ 2.333。 如果按处理时间升序执行,即顺序为 [1, 2, 3] ,则等待时间分别为:0, 1, 1+2=3,总等待时间 = 0+1+3=4,平均等待时间 = 4/3 ≈ 1.333。显然更优。 解题过程循序渐进讲解 : 第一步:问题形式化 设有 n 个任务,处理时间为 p₁, p₂, …, pₙ 。假设所有任务在时间 0 同时到达(这是常见简化,若不同到达时间则问题更复杂,需考虑“最短剩余时间优先”等调度策略)。 若执行顺序是任务索引的一个排列 π₁, π₂, …, πₙ ,则: 任务 π₁ 开始于时间 0,等待时间 W₁ = 0 。 任务 π₂ 开始于时间 p₍π₁₎ ,等待时间 W₂ = p₍π₁₎ 。 任务 π₃ 开始于时间 p₍π₁₎ + p₍π₂₎ ,等待时间 W₃ = p₍π₁₎ + p₍π₂₎ 。 …… 任务 πₖ 的等待时间 Wₖ = Σⱼ=1..k-1 p₍πⱼ₎ 。 总等待时间 T = Σₖ₌₁ⁿ Wₖ 。 平均等待时间 = T / n 。 因为 n 固定,最小化平均等待时间等价于最小化总等待时间 T 。 第二步:将总等待时间写成处理时间的函数 将 T 展开: 数一下每个处理时间在总和中出现的次数: p₍π₁₎ 出现在 W₂, W₃, …, Wₙ 中,即出现 n-1 次。 p₍π₂₎ 出现在 W₃, …, Wₙ 中,即出现 n-2 次。 … p₍πₖ₎ 出现 n-k 次。 最后 p₍πₙ₎ 出现 0 次。 因此: 即 T = Σₖ₌₁ⁿ (n-k)·p₍πₖ₎ 。 第三步:贪心策略的猜想 观察公式: T 是处理时间的加权和,权重从 n-1 递减到 0。 为了最小化 T ,显然 应该将最小的处理时间赋予最大的权重 ,即:让最小的 p 对应最大的权重 n-1 ,次小的 p 对应次大的权重 n-2 ,依此类推。 这等价于 将处理时间按升序排列 ,先执行最短的任务,再执行次短的,……,最后执行最长的。 这就是 最短处理时间优先(Shortest Processing Time first, SPT) 策略。 第四步:严格证明贪心策略最优 用交换论证法(exchange argument): 假设存在一个最优顺序,其中相邻两个任务 A 和 B , A 在 B 前面执行,但 p_A > p_B (即不是按升序)。 考虑交换 A 和 B 的位置,其他任务顺序不变。 交换前, A 和 B 对总等待时间的贡献(只考虑它们自身及它们之后的任务受它们影响的部分)为: 设 A 之前的任务总时间为 S ,则: A 的等待时间 = S (对总等待时间贡献一次 S ,因为 A 自身等待时间计入总和)。 B 的等待时间 = S + p_A 。 在 B 之后的任务,每个都多等了 p_A + p_B 的时间(因为它们在 A 和 B 之后)。 交换后,顺序变为 B 在 A 前: B 的等待时间 = S 。 A 的等待时间 = S + p_B 。 在 A 之后的任务,每个都多等了 p_B + p_A 的时间(与交换前相同,因为 p_A + p_B = p_B + p_A )。 比较交换前后总等待时间的变化: 交换前, A 和 B 自身等待时间之和 = S + (S + p_A) = 2S + p_A 。 交换后, B 和 A 自身等待时间之和 = S + (S + p_B) = 2S + p_B 。 由于 p_B < p_A ,交换后减少了 p_A - p_B > 0 。 而其他任务的等待时间不变(因为 A 和 B 的总处理时间相同,只是顺序不同,不影响后面任务的开始时间)。 因此交换后总等待时间更小,与“最优”矛盾。 所以,最优顺序中任意相邻两个任务必须满足 p_A ≤ p_B ,即整个序列按处理时间非递减排序。 这就证明了 SPT 顺序是唯一最优的 (若处理时间相同,顺序可任意)。 第五步:算法实现 算法非常简单: 输入数组 tasks (处理时间列表)。 对 tasks 进行升序排序。 输出排序后的顺序,即为最优执行顺序。 同时可计算最小总等待时间: 或直接按公式 T = Σₖ₌₁ⁿ (n-k)·tasks_sorted[k-1] 计算。 第六步:示例演算 对 tasks = [3, 1, 2] : 排序后为 [1, 2, 3] 。 总等待时间 = (3-1) 1 + (3-2) 2 + (3-3) 3 = 2 1 + 1 2 + 0 3 = 4。 平均等待时间 = 4/3 ≈ 1.333。 第七步:扩展讨论 若任务有不同到达时间,则问题变为“最短剩余时间优先(SRTF)”调度,需用优先队列模拟。 若考虑截止时间,则可能变为“最早截止时间优先(EDF)”等。 平均等待时间最小化等价于平均周转时间(等待时间+处理时间)最小化,因为总处理时间固定。 该贪心策略是调度理论中的一个经典结论,是“最小化平均等待时间”的标准解法。 总结 : 本题展示了如何通过将总等待时间表示为加权和,发现权重递减规律,从而推导出 SPT 贪心策略,并用交换论证法严格证明其最优性。实现上只需一次排序,时间复杂度 O(n log n),空间 O(1) 或 O(n)。