Presentation is loading. Please wait.

Presentation is loading. Please wait.

2.5 Reasoning about Programs: Assertions and Loop Invariants

Similar presentations


Presentation on theme: "2.5 Reasoning about Programs: Assertions and Loop Invariants"— Presentation transcript:

1 2.5 Reasoning about Programs: Assertions and Loop Invariants
2.6 Efficiency of Algorithms 13 – Program Correctness

2 Attendance Quiz #10 Program Correctness

3 Tip #13: '\n' Confusion Program Correctness Internally all applications use '\n' to indicate line termination. But the line termination sequence is platform specific. Windows (DOS) uses CR and LF. Unix (Linux) uses LF. Mac (OSX) CR. 2 additional formats: Unicode Line Separator (LS) and Unicode Paragraph Separator (PS).

4 Tip #13: Newline Confusion
Iterators The problem with platform specific line termination is The '\n' character is transformed into a platform specific sequence of character(s) when you write it to a file. The platform specific sequence is converted back to '\n' when the file is read. The problem becomes a problem you write files on one platform and read them on another. For example, after running "filename >> amount;", the newline is still in the buffer. This normally isn't a problem when using only ">>" as it ignores newlines. But when you mix getline and ">>" you need to ignore the newlines that ">>" leaves behind. Bad getline(cin, line, '\n'); cout << "hello\n"; filename >> amount; getline(filename, line); Good getline(cin, line); cout << "hello" << endl; filename >> amount >> std::ws;

5 Lab 03 Iterator MyArray Iterator Program Correctness
#include <iostream> #include <string> using namespace std; #define MAX_ARRAY_SIZE 1000 template<typename T> class MyArray { private: size_t size_; T* array_; public: MyArray(const size_t array_size) : size_(0) array_ = (T*)malloc(array_size * sizeof(T)); } void push_back(T item) { array_[size_++] = item; } }; int main(int argc, char * argv[]) { MyArray<int> numbers(MAX_ARRAY_SIZE); numbers.push_back(1); numbers.push_back(2); numbers.push_back(3); numbers.push_back(4); return 0; } MyArray<int>::Iterator iter = numbers.begin(); for (int i = 0; i < 4; ++i) cout << iter[i] << ' '; cout << endl << endl; MyArray Iterator class Iterator { private: size_t index_; T* array_; public: Iterator(T* a) : array_(a), index_(0) {} T& operator[](int i) const { return array_[index_ + i]; } }; Iterator begin() { return MyArray<T>::Iterator(array_); }

6 2.5 Reasoning about Programs: Assertions and Loop Invariants
The C++ assert Macro 2.5, pgs

7 Assertions Program Correctness Assertions are logical statements about a program that are "asserted" to be true. A pre-condition is an assertion about the state of the input data (generally the input parameters) before the function is executed. For example, a pre-condition for method double divide(double dividend, double divisor) might be divisor != 0. A post-condition is an assertion about the state of the output data when the function returns. For example, a post-condition for method int search (int x[], int x_length, int target) would be all elements were tested and target not found. An invariant is a particular pre-condition of a procedure is the same after the procedure is completed. For example a valid invariant for a procedure boolean search(int term, int array[]) might say that the state of array before the call is the same as it is after the call.

8 The C++ assert Macro Program Correctness Assertions written as comments are valuable for understanding a program, but are only verified by the programmer. C++ provides an assert macro (defined in <cassert>) that calls an internally defined function to print an error message and terminate the program if the asserted condition is not true. int search (int x[], int x_length, int target) { //assert: x_length is a valid array index assert(x_length >= 0 && "Length can't possibly be negative!"); int i; for (int i = 0; i < x_length; i++) if (x[i] == target) return i; } //assert: all elements were tested and target not found assert(i == x_length && "Didn't check all values!"); return -1;

9 Loop Invariant Program Correctness A loop invariant condition is about the relationship between the variables of a program which are true immediately before and immediately after each iteration of the loop. int max = INT_MIN; int a[] = { 7, 5, 3, 10, 2, 6 }; for (int i = 0; i < 6; i++) { if (a[i] > max) max = a[i]; } For example, in the above procedure, loop invariants include: "The array a doesn't change." "The variable max is always the max value of the first i elements of array a." Ideally in OO, you would isolate what varies as much as possible, so that each object/procedure is mostly invariant. Enforcing invariants on loops reduces side effects that lead to nasty situations where two identical calls to the same procedure (with the same input) might yield different outputs.

10 Describe a loop invariant for the following loop (i. e
Describe a loop invariant for the following loop (i.e. what is true for every iteration): string a = "aibohphobia"; int i, j; for (i = 0, j = 10; i < 11; i++, j++) { // loop invariant before loop begins if (a[i] != a[j]) break; // holds true at end of loop } if (i == 11) cout << "Palidrome"; The first ith elements of array are a palindrome. (i + j) == 10 (i >= 0) and (i <= 11)

11 2.6, pgs. 170-179 2.6 Efficiency of Algorithms Big-O Notation
Comparing Performance Algorithms with Exponential and Factorial Growth Rates 2.6, pgs

12 Efficiency of Algorithms
Program Correctness Getting a precise measure of the performance of an algorithm is difficult. We can try to approximate the effect of a change in the number of data items, n, that an algorithm processes. In this way we can see how an algorithm's execution time increases with respect to n, so we can compare two algorithms by examining their growth rate. For many problems there are algorithms that are relatively obvious but inefficient. Even though computers are getting faster, with larger memories, algorithm growth rates can be so large that no computer can solve the problem above a certain size.

13 1. Array Search Program Correctness int search(int x[], int x_length, int target) { for (int i = 0; i < x_length; i++) if (x[i] == target) return i; } return -1; // target not found x[] ---- 1 2 3 4 If the target is not present in the array, then the for loop will execute x_length times. If the target is present, the for loop will execute (on average) x_length/2 times. Therefore, the total execution time is directly proportional to x_length . If we double the size of the array, we expect the time to double.

14 2. Different Arrays x[] ---- 1 2 3 4 y[] ---- 4 3 2 1
Program Correctness bool are_different(int x[], int x_length, int y[], int y_length) { for (int i = 0; i < x_length; i++) if (search(y, y_length, x[i]) != -1) return false; } return true; x[] ---- 1 2 3 4 y[] ---- 4 3 2 1 The for loop will execute x_length times. But it will call search, which will execute y_length times. The total execution time is proportional to (x_length * y_length).

15 3. Unique Arrays Program Correctness bool are_unique(int x[], int x_length) { for (int i = 0; i < x_length; i++) for (int j = 0; j < x_length; j++) if (i != j && x[i] == x[j]) return false; } return true; x[] ---- 1 2 3 4 If all values are unique, the for loop with i as index will execute x_length times. Inside this loop, the for loop with j as index will execute x_length times. The total number of times the loop body of the innermost loop will execute is ( x.length2 ).

16 4. Better Unique Arrays x[] ---- 1 2 3 4
Program Correctness bool are_unique2(int x[], int x_length) { for (int i = 0; i < x_length; i++) for (int j = i + 1; j < x_length; j++) if (x[i] == x[j]) return false; } return true; x[] ---- 1 2 3 4 This loop will execute x_length - 1 times the first time. The second time it will execute x_length - 2 times, and so on. The last time it will execute once.

17 Big-O Notation Program Correctness Understanding how the execution time (and memory requirements) of an algorithm grows as a function of increasing input size gives programmers a tool for comparing various algorithms and determining how they will perform. If the execution time stays the same regardless of the number of inputs, then the growth rate is of order 1. If the execution time approximately doubles when the number of inputs, n, doubles, then the algorithm grows at a linear rate or a growth rate of order n. If the execution time is approximately quadrupled when the number of inputs is doubled, then the algorithm grows at a quadratic rate or at a growth rate of order of n2.

18 Big-O Notation Program Correctness Consider the following functions with respect to execution: n times for (int i = 0; i < n; i++) { // Statement(s) }

19 Big-O Notation Program Correctness Consider the following functions with respect to execution: n times for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) // Statement(s) } (n  m) times

20 Big-O Notation Program Correctness Consider the following functions with respect to execution: n times (n  m) times for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) // Statement(s) } (n2) times

21 Big-O Notation Program Correctness Consider the following functions with respect to execution: n times (n  m) times (n2) times for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) // Statement(s) } (n log n) times

22 Big-O Notation Program Correctness Consider the following functions with respect to execution: n times (n  m) times (n2) times (n log n) times. Computer scientists use the notation O(n) to represent the first case O(n  m) to represent the second O(n2) to represent the third O(n log n) to represent the fourth The symbol O can be thought of as an abbreviation for “order of magnitude” and is called big-O notation.

23 Big-O Notation Program Correctness A simple way to determine the big-O of an algorithm or program is to look at any loops and to see whether the loops are nested. Assuming that the loop body consists only of simple statements, a single loop is O(n) a nested loop is O(n2) a nested loop in a nested loop is O(n3), and so on The growth rate of f(n) will be determined by the fastest growing term, which is the one with the largest exponent. In general, it is safe to ignore all constants and to drop the lower-order terms when determining the order of magnitude.

24 Big-O Example T(n) = n2 + 3n + 9 void T(int n) {
Program Correctness void T(int n) { for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) // Simple Statement; } // Simple Statement 1; // Simple Statement 2; // Simple Statement 3; // ... // Simple Statement 8; // Simple Statement 9; The outer loop will execute n times. The inner loop will execute n times for each outer loop iteration. We can conclude that the relationship between processing time and n (the number of Simple Statements processed) is: T(n) = n2 + 3n + 9 The growth rate of f(n) will be determined by the fastest growing term, which is the one with the largest exponent. In the example, an algorithm of O(n2 + 3n + 9) is more simply expressed as O(n2). The loop will execute 3 Simple Statements n times Finally, 9 Simple Statements are executed.

25


Download ppt "2.5 Reasoning about Programs: Assertions and Loop Invariants"

Similar presentations


Ads by Google