A loop invariant is a property or condition that holds true before and after each iteration of a loop. These invariants are useful for proving the correctness of algorithms, particularly when trying to show that a loop correctly solves a problem or that an algorithm terminates as expected.
A loop invariant is a statement or condition that:
To effectively use a loop invariant in a proof, you must identify:
Let's look at an example where we use a loop invariant to prove that a loop correctly finds the maximum element in an array.
We are given an array of length , and we want to find the maximum element in this array. We can write a simple loop to do this:
max_element = A[0]
for i in range(1, n):
if A[i] > max_element:
max_element = A[i]
We define the loop invariant for this algorithm:
max_element contains the maximum value of the array elements from A[0] to A[i-1].Now, let's prove that this invariant holds.
Initialization: Before the loop starts, max_element = A[0], so the invariant holds because the maximum value from A[0] to A[-1] (an empty set) is trivially valid.
Maintenance: Assume that the invariant holds at the start of some iteration . That is, max_element contains the maximum value in the subarray A[0] to A[i-1]. In the -th iteration, we compare A[i] with max_element. If A[i] > max_element, we update max_element to be A[i]. After this step, max_element will still hold the maximum value in the subarray A[0] to A[i]. Hence, the invariant is maintained.
Termination: When the loop terminates, the index will be equal to , meaning the loop has considered all elements in the array. By the invariant, max_element will contain the maximum value from A[0] to A[n-1], which is the maximum element of the entire array.
Thus, the loop correctly finds the maximum element in the array.
Let’s look at another example, this time using a loop invariant in the selection sort algorithm. The idea behind selection sort is to repeatedly find the minimum element from the unsorted part of the list and swap it with the first unsorted element.
for i in range(n-1):
min_index = i
for j in range(i+1, n):
if A[j] < A[min_index]:
min_index = j
A[i], A[min_index] = A[min_index], A[i]
We define the loop invariant for this algorithm:
A[0] to A[i-1] is sorted, and each element in this subarray is smaller than every element in the unsorted subarray A[i] to A[n-1].Let’s prove that this invariant holds.
Initialization: Before the first iteration, the subarray A[0] to A[-1] is empty, so the invariant trivially holds because an empty subarray is considered sorted.
Maintenance: Assume that the invariant holds at the start of some iteration , meaning the subarray A[0] to A[i-1] is sorted, and each element in it is smaller than every element in the subarray A[i] to A[n-1]. In the -th iteration, the inner loop finds the minimum element in the unsorted subarray A[i] to A[n-1]. We then swap this minimum element with A[i], ensuring that the element A[i] is now in its correct sorted position. The rest of the subarray remains sorted, and the elements before A[i] are still smaller than those after. Therefore, the invariant is maintained.
Termination: When the outer loop terminates, the index will be equal to , meaning that the entire array is sorted. By the invariant, the subarray A[0] to A[n-1] is sorted, and all the elements in it are smaller than the unsorted part (which is empty). Hence, the entire array is sorted.
Thus, the selection sort algorithm correctly sorts the array.
Loop invariants can be used not only for proving correctness but also for analyzing the performance of algorithms. By proving an invariant, we can often infer properties like:
For example, the invariant in selection sort helps us show that after iterations, the array is sorted, and each iteration involves finding the minimum element from an unsorted subarray, confirming the algorithm’s correctness and efficiency.
Loop invariants are a powerful tool in algorithm design and analysis. They provide a way to reason about the behavior of loops in algorithms, ensuring that the algorithm produces the correct output. By proving that a loop invariant holds at the start, after each iteration, and at the end, we can demonstrate the correctness of algorithms, especially for sorting, searching, and other iterative processes. Understanding and using loop invariants is a crucial skill for analyzing and proving the behavior of algorithms.
Open this section to load past papers