使用循环不变量证明归并排序的正确性(初始化、维护、终止)
Using Loop invariant to prove correctness of merge sort (Initialization , Maintenance , Termination)
你将如何通过对循环不变量状态的推理来证明合并排序的正确性?我唯一可以想象的是在合并步骤中,子数组(不变量)在组合时保持它们的状态,即它们在每次合并时再次排序 step.But 我不知道我是否进行正确或 not.I 对循环不变量没有太多了解 stuff.Can 有人启发我这个?。解释每个阶段会发生什么
a) 初始化
b) 维护
c) 终止
感激不尽!
合并排序的伪代码
合并排序(A, p, r)
1 if p < r
2 then q <-- [(p + r) / 2]
3 MERGE-SORT(A, p, q)
4 MERGE-SORT(A, q + 1, r)
5 MERGE-SORT(A, p, q, r)
合并排序(A、p、q、r)
1 n1 <-- q - p + 1
2 n2 <-- r - q
3 create arrays L[1 ... n1 + 1] and R[1 ... n2 + 1]
4 for i <-- 1 to n1
5 do L[i] <-- A[p + i - 1]
6 for j <-- 1 to n2
7 do R[j] <-- A[q + j]
8 L[n1 + 1] <-- infinite
9 R[n2 + 1] <-- infinite
10 i <-- 1
11 j <-- 1
12 for k <-- p to r
13 do if L[i] <= R[j]
14 then A[k] <-- L[i]
15 i <-- i + 1
16 else A[k] <-- R[j]
17 j <-- j + 1
我们尝试对两堆卡片进行排序,但我们避免在每个基本步骤中检查任一堆是否为空,并且我们使用无限作为哨兵卡片来简化我们的代码。所以,无论什么时候哨兵卡 infinie 被暴露出来,它都不可能是较小的牌,除非两堆的哨兵牌都暴露了。但是一旦发生这种情况,所有非哨兵卡都已经放在输出堆上了。因为我们事先知道 r - p + 1
张卡片将被放入输出堆,所以我们可以在执行完那么多基本步骤后停止。
循环不变量:
初始化:在循环的第一次迭代之前,我们有 k = p
,因此子数组 A[p ... k - 1]
为空。此空子数组包含 L 和 R 的 k - p = 0
个最小元素,并且由于 i = j = 1
,L[i] 和 R[j] 都是其数组中尚未复制回 A 的最小元素.
维护:要看到每次迭代都保持循环不变,让我们首先假设 l[i] <= R[j]。那么 L[i] 是尚未复制回 A 的最小元素。因为 A[p ... k - 1]
包含 k - p
个最小元素,在第 14 行将 L[i] 复制到 A[k] 后,子数组 A[p ... k]
将包含 k - p + 1
个最小的元素。递增 k(在 for 循环更新中)和 i(在第 15 行)为下一次迭代重新建立循环不变量。相反,如果 L[i] > R[j],则第 16-17 行执行适当的操作以保持循环不变。
终止:终止时,k = r + 1
。根据循环不变式,子数组 A[p ... k - 1]
即 A[p ... r]
包含 L[1 ... n1 + 1]
和 R[1 ... n2 + 1]
的最小元素 k - p = r - p + 1
,按排序顺序排列。数组 L 和 R 一起包含 n1 + n2 + 2 = r - p + 3
个元素。除了两个最大的元素之外的所有元素都被复制回 A,这两个最大的元素是哨兵。
你将如何通过对循环不变量状态的推理来证明合并排序的正确性?我唯一可以想象的是在合并步骤中,子数组(不变量)在组合时保持它们的状态,即它们在每次合并时再次排序 step.But 我不知道我是否进行正确或 not.I 对循环不变量没有太多了解 stuff.Can 有人启发我这个?。解释每个阶段会发生什么
a) 初始化 b) 维护 c) 终止
感激不尽!
合并排序的伪代码
合并排序(A, p, r)
1 if p < r
2 then q <-- [(p + r) / 2]
3 MERGE-SORT(A, p, q)
4 MERGE-SORT(A, q + 1, r)
5 MERGE-SORT(A, p, q, r)
合并排序(A、p、q、r)
1 n1 <-- q - p + 1
2 n2 <-- r - q
3 create arrays L[1 ... n1 + 1] and R[1 ... n2 + 1]
4 for i <-- 1 to n1
5 do L[i] <-- A[p + i - 1]
6 for j <-- 1 to n2
7 do R[j] <-- A[q + j]
8 L[n1 + 1] <-- infinite
9 R[n2 + 1] <-- infinite
10 i <-- 1
11 j <-- 1
12 for k <-- p to r
13 do if L[i] <= R[j]
14 then A[k] <-- L[i]
15 i <-- i + 1
16 else A[k] <-- R[j]
17 j <-- j + 1
我们尝试对两堆卡片进行排序,但我们避免在每个基本步骤中检查任一堆是否为空,并且我们使用无限作为哨兵卡片来简化我们的代码。所以,无论什么时候哨兵卡 infinie 被暴露出来,它都不可能是较小的牌,除非两堆的哨兵牌都暴露了。但是一旦发生这种情况,所有非哨兵卡都已经放在输出堆上了。因为我们事先知道 r - p + 1
张卡片将被放入输出堆,所以我们可以在执行完那么多基本步骤后停止。
循环不变量:
初始化:在循环的第一次迭代之前,我们有
k = p
,因此子数组A[p ... k - 1]
为空。此空子数组包含 L 和 R 的k - p = 0
个最小元素,并且由于i = j = 1
,L[i] 和 R[j] 都是其数组中尚未复制回 A 的最小元素.维护:要看到每次迭代都保持循环不变,让我们首先假设 l[i] <= R[j]。那么 L[i] 是尚未复制回 A 的最小元素。因为
A[p ... k - 1]
包含k - p
个最小元素,在第 14 行将 L[i] 复制到 A[k] 后,子数组A[p ... k]
将包含k - p + 1
个最小的元素。递增 k(在 for 循环更新中)和 i(在第 15 行)为下一次迭代重新建立循环不变量。相反,如果 L[i] > R[j],则第 16-17 行执行适当的操作以保持循环不变。终止:终止时,
k = r + 1
。根据循环不变式,子数组A[p ... k - 1]
即A[p ... r]
包含L[1 ... n1 + 1]
和R[1 ... n2 + 1]
的最小元素k - p = r - p + 1
,按排序顺序排列。数组 L 和 R 一起包含n1 + n2 + 2 = r - p + 3
个元素。除了两个最大的元素之外的所有元素都被复制回 A,这两个最大的元素是哨兵。