我在读CLRS的《算法导论》。在第二章中,作者提到了“循环不变量”。什么是循环不变量?
当前回答
It is hard to keep track of what is happening with loops. Loops which don't terminate or terminate without achieving their goal behavior is a common problem in computer programming. Loop invariants help. A loop invariant is a formal statement about the relationship between variables in your program which holds true just before the loop is ever run (establishing the invariant) and is true again at the bottom of the loop, each time through the loop (maintaining the invariant). Here is the general pattern of the use of Loop Invariants in your code:
... // the Loop Invariant must be true here while ( TEST CONDITION ) { // top of the loop ... // bottom of the loop // the Loop Invariant must be true here } // Termination + Loop Invariant = Goal ... Between the top and bottom of the loop, headway is presumably being made towards reaching the loop's goal. This might disturb (make false) the invariant. The point of Loop Invariants is the promise that the invariant will be restored before repeating the loop body each time. There are two advantages to this:
Work is not carried forward to the next pass in complicated, data dependent ways. Each pass through the loop in independent of all others, with the invariant serving to bind the passes together into a working whole. Reasoning that your loop works is reduced to reasoning that the loop invariant is restored with each pass through the loop. This breaks the complicated overall behavior of the loop into small simple steps, each which can be considered separately. The test condition of the loop is not part of the invariant. It is what makes the loop terminate. You consider separately two things: why the loop should ever terminate, and why the loop achieves its goal when it terminates. The loop will terminate if each time through the loop you move closer to satisfying the termination condition. It is often easy to assure this: e.g. stepping a counter variable by one until it reaches a fixed upper limit. Sometimes the reasoning behind termination is more difficult.
应该创建循环不变量,以便当达到终止条件时,且不变量为真,则达到目标:
不变+终止=>目标 它需要实践来创建简单而相关的不变式,这些不变式捕获了除了终止之外的所有目标实现。最好使用数学符号来表示循环不变量,但当这导致过于复杂的情况时,我们依赖于清晰的散文和常识。
其他回答
我喜欢这个非常简单的定义:
循环不变量是(程序变量之间的)一种条件,它必须在每次循环迭代之前和之后立即为真。(请注意,在迭代过程中,这并没有说明它的真伪。)
By itself, a loop invariant doesn't do much. However, given an appropriate invariant, it can be used to help prove the correctness of an algorithm. The simple example in CLRS probably has to do with sorting. For example, let your loop invariant be something like, at the start of the loop, the first i entries of this array are sorted. If you can prove that this is indeed a loop invariant (i.e. that it holds before and after every loop iteration), you can use this to prove the correctness of a sorting algorithm: at the termination of the loop, the loop invariant is still satisfied, and the counter i is the length of the array. Therefore, the first i entries are sorted means the entire array is sorted.
一个更简单的例子:循环不变量、正确性和程序推导。
我理解循环不变量的方式是作为一个系统的,正式的工具来推理程序。我们做了一个陈述,我们专注于证明它是正确的,我们称之为循环不变量。这组织了我们的逻辑。虽然我们也可以非正式地讨论一些算法的正确性,但使用循环不变量迫使我们非常仔细地思考,并确保我们的推理无懈可击。
简单地说,循环不变量是对循环的每次迭代都成立的某个谓词(条件)。例如,让我们看一个简单的For循环,它是这样的:
int j = 9;
for(int i=0; i<10; i++)
j--;
在这个例子中,i + j == 9(对于每个迭代)是正确的。一个较弱的不变式也是成立的 I >= 0 && I <= 10。
《如何思考算法》的定义,Jeff Edmonds著
循环不变式是放置在循环和循环顶部的断言 每次计算返回到循环的顶部时,这必须成立。
在线性搜索(根据书中给出的练习)中,我们需要在给定的数组中找到值V。
它很简单,从0 <= k < length开始扫描数组并比较每个元素。如果找到V,或者扫描到数组的长度,就终止循环。
根据我对上述问题的理解-
循环不变量(初始化): 在k - 1迭代中找不到V。第一次迭代,这是-1因此我们可以说V不在-1位置
保养: 在下一次迭代中,V不在k-1中成立
Terminatation: 如果V位于k个位置,或者k达到数组的长度,则终止循环。
在处理循环和不变量时,有一件事很多人没有马上意识到。他们混淆了循环不变量和循环条件(控制循环终止的条件)。
正如人们指出的那样,循环不变量必须为真
在循环开始之前 在每次循环迭代之前 在循环结束之后
(尽管在循环体期间它可以暂时为假)。另一方面,循环条件在循环结束后必须为false,否则循环将永远不会终止。
因此循环不变量和循环条件必须是不同的条件。
复杂循环不变量的一个很好的例子是用于二分搜索。
bsearch(type A[], type a) {
start = 1, end = length(A)
while ( start <= end ) {
mid = floor(start + end / 2)
if ( A[mid] == a ) return mid
if ( A[mid] > a ) end = mid - 1
if ( A[mid] < a ) start = mid + 1
}
return -1
}
因此循环条件看起来非常简单——当开始>结束时,循环终止。但是为什么循环是正确的呢?什么是循环不变量来证明它的正确性?
不变量是逻辑语句:
if ( A[mid] == a ) then ( start <= mid <= end )
这句话是逻辑重言——在我们试图证明的特定循环/算法的上下文中,它总是正确的。并且在循环结束后,它提供了关于循环正确性的有用信息。
If we return because we found the element in the array then the statement is clearly true, since if A[mid] == a then a is in the array and mid must be between start and end. And if the loop terminates because start > end then there can be no number such that start <= mid and mid <= end and therefore we know that the statement A[mid] == a must be false. However, as a result the overall logical statement is still true in the null sense. ( In logic the statement if ( false ) then ( something ) is always true. )
那么我说的循环条件在循环结束时必然为假呢?当在数组中找到元素时,循环条件在循环结束时为true !?实际上不是,因为隐含的循环条件实际上是while (A[mid] != A && start <= end),但我们缩短了实际的测试,因为第一部分是隐含的。这个条件在循环结束后明显为false,而不管循环如何结束。
推荐文章
- 有效的方法应用多个过滤器的熊猫数据框架或系列
- BASE术语解释
- 哪些是遗传算法/遗传规划解决方案的好例子?
- JavaBean和POJO之间的区别是什么?
- 如何在O(n)中找到长度为n的无序数组中的第k大元素?
- 一个用于膨胀/收缩(抵消,缓冲)多边形的算法
- 我如何使用Node.js Crypto创建HMAC-SHA1哈希?
- 如何用动态规划确定最长递增子序列?
- 在c++程序中以编程方式检测字节序
- 理解递归
- 段树、区间树、二叉索引树和范围树之间有什么区别?
- 给定一个数字,找出下一个与原始数字具有完全相同的数字集的更高的数字
- HSL到RGB的颜色转换
- 使用Java在原语数组中查找最大/最小值
- 好的Java图算法库?