PROGRAMMING FUNDAMENTALS Dr. Sheikh Faisal Rashid shfaisal@gmail.com,uet.edu.pk Department of Computer Science and Engineering UET Lahore REPETITIOUS OPERATIONS Outline • In this presentation, we will: • Review conditional statements • Introduce repetitious operations • Review the game of PictionaryTM • Describe how these solve specific real-wold problems • Understand the need for true/false conditions to continue looping • Look at the flow chart Conditional statements • In real-world situations, you take actions based on criteria or conditions • Programming languages restrict decisions to only those that are absolutely necessary to accomplish goals • This boils down to Boolean-valued conditional statements if ( condition ) { // Do something… } else { // Do something else… } Repetitious operations • Another possibility is to repeat an operation until some goal is accomplished • In Pictionary, you are given a word and you must get the audience to guess the word based on a drawing Repetitious operations • A more realistic version followed by many players • Each decision is not really objective • What is “close”? • What is “sort-of”? Repetitious operations • In mathematics, you learned a number of algorithms that depended on repetition: • To determine if n is prime: • Try dividing n by every integer k = 2, …, n – 1 • If any remainder is zero, then n is not prime • Even long division is a repetitive algorithm: • In calculating m ÷ n, perform the following: • Take the first digit of m and call it r • Find the largest integer multiple d of n such that r – dn ≥ 0, record d and let r – dn be the new r • Add the next digit of m to the new r Repetitious operations • In mathematics, you learned a number of algorithms that depended on repetition: • To calculate n! • You multiplied together all of the integers k = 2, …, n • To calculate factorial n k , you find n! n k !k ! which requires three n n 1 n 2 calculations followed by one long division • You can reduce your work by calculating and then dividing by k! n k 1 Repetitious operations • In programming languages, it is also necessary to repetitively perform a sequence of actions • In Pictionary, the choice of action was someone fuzzy • In mathematics, in each case the repetitive operation is well defined • In some cases, we know how often we will have to repeat an operation, in others, we may not know until we get a solution • That is, if a solution even exists • Like decision making, in programming languages we will have to reduce the decision as to how often to repeat down to a simple yes-no question Flow charts • A flow chart for simple repetition is to continue repeating a code block as long as some condition is TRUE • As soon as the condition is FALSE, we proceed to other statements Flow charts • The repetitive behavior of this repetitive flow chart component leads to the name looping statement • We continue looping until a condition fails Looping statements • We implemented conditional statements based on a condition being evaluated to TRUE if ( condition ) { // Do something... } else { // Do something else... } Looping statements • A looping statement is implemented based on repeatedly executing a block of statements so long as a condition is TRUE while ( Boolean-valued condition ) { The looped block of statements - to be executed as long as the condition is 'true' } // Continue executing here as soon as the // condition evaluates to 'false' Looping statements • Because this state block is repeatedly executed (i.e., looped) while a condition is TRUE, we refer to such a statement as a “while loop” while ( Boolean-valued condition ) { The looped block of statements - to be executed as long as the condition is 'true' } // Continue executing here as soon as the // condition evaluates to 'false' Looping statements • The easiest while loop is one that does so forever: #include <iostream> int main(); int main() { // @non-terminating@ while ( true ) { std::cout << "Hello world!" << std::endl; } return 0; } Looping statements • Such non-terminating while loops are used in real-time systems that respond to external events: int main(); int main() { // @non-terminating@ while ( true ) { // Wait for an event // Respond to that event } // Technically, we never get here return 0; } Collatz conjecture • Normally, however, the condition is affected by the action of the looped block of statements • We’ll look at one example based on an interesting mathematical quandary: • The Collatz conjecture says that if you start with a number a, do the following: • If it is odd, multiply it by three and add one • If it is even, divide it by two • The Collatz conjecture says that this sequence will ultimately reduce to the cycle 1, 4, 2, 1, 4, 2, 1, … Collatz conjecture • We can try this with any number of initial values 1 2, 1 3, 10, 5, 16, 8, 4, 2, 1 4, 2, 1 5, 16, 8, 4, 2, 1 6, 3, 10, 5, 16, 8, 4, 2, 1 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1 8, 4, 2, 1 9, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1 10, 5, 16, 8, 4, 2, 1 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1 12, 6, 3, 10, 5, 16, 8, 4, 2, 1 Collatz conjecture • Write a function collatz_print(…) that takes a positive integer a and prints out the sequence of integers until it reaches one, in which case, terminate with "..." 1. Print “a, ” 2. If a ≠ 1, a. If a is even, i. Divide a by two, ii. Otherwise, set a ← 3a + 1 b. Print “a, ” c. Go to Step 2. 3. Print “…” Collatz conjecture • An implementation of this algorithm is: void collatz_print( unsigned int a ); void collatz_print( unsigned int a ) { std::cout << a << ", "; while ( a != 1 ) { if ( (a % 2) == 0 ) { a /= 2; } else { a = 3*a + 1; } std::cout << a << ", "; } std::cout << "..." << std::endl; } Question: What happens if the argument passed is 0? Collatz conjecture • Try it yourself: #include <iostream> #include <cassert> // Function declarations void collatz_print( unsigned int a ); // Function definitions void collatz_print( unsigned int a ) { assert( a != 0 ); std::cout << a << ", "; while ( a != 1 ) { if ( (a % 2) == 0 ) { a /= 2; } else { a = 3*a + 1; } std::cout << a << ", "; } std::cout << "..." << std::endl; } int main() { collatz_print( 1970 ); return 0; } Collatz conjecture • Mathematicians aren’t so interested in the actual sequences, but rather the number of terms in the sequence until you get to 1 • For example, 27, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1 171, 514, 257, 772, 386, 193, 580, 290, 145, 436, 218, 109, 328, 164, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1 Collatz conjecture • Write a function collatz(…) that takes a positive integer n and returns the number of steps required until we get to one: unsigned int collatz( unsigned int a ); unsigned int collatz( unsigned int a ) { unsigned int num_iterations{0}; while ( a != 1 ) { ++num_iterations; if ( (a % 2) == 0 ) { a /= 2; } else { a = 3*a + 1; } } return num_iterations; } Collatz conjecture • Notice the function declarations: void collatz_print( unsigned int n ); unsigned int collatz( unsigned int n ); • Could we not just give the same name? • After all, we had other functions with the same name • Problem: The C++ compiler cannot choose based on return type only • The compiler only chooses based on the types of any arguments • If two functions have identical parameter types, they must have different names Collatz conjecture • We could create a second loop that queries the user for an argument: #include <iostream> int main(): void collatz_print( unsigned int n ); int main() { bool keep_going{true}; while ( keep_going ) { unsigned int n{}; std::cout << "Enter a positive integer ('0' to quit): "; std::cin >> n; if ( n == 0 ) { keep_going = false; } else { collatz_print( n ); } } return 0; } Collatz conjecture • Problem: what happens if we pass our function the argument 0? • While the specification may require the argument to be greater than zero, you must explicitly check: unsigned int collatz( unsigned int n ) { assert( n >= 1 ); unsigned int num_iterations{0}; // Other code... return num_iterations; } unsigned int collatz( unsigned int n ) { if ( n == 0 ) { return 0; } else { unsigned int num_iterations{0}; // Other code... return num_iterations; } } Counting • Suppose we want a loop to run exactly n times • Track of how often the loop has executed with a local variable niterations 1. Initialize a local variable niterations ← 0 2. As long as niterations < n, a. execute the block of statements associated with the loop, b. increment niterations, and c. return to Step 2. Counting • Here is a while loop that executes a fixed number of times • This assumes the value of maxiterations is never changed… unsigned int num_iterations{0}; while ( num_iterations < max_iterations ) { // Do something... ++num_iterations; } • Once num_iterations == max_iterations, we have executed the block of statements the required number of times Factorial function • Suppose we want to calculate n! • Because 0! = 1! = 1, we could start with this value, and then keep multiplying this by 2, then 3, and so on up until n: 1. Initialize a result r ← 1 and a variable k ← 2 2. If k ≤ n, a. multiply r by k: r ← kr, b. increment k ← k + 1, and c. return to Step 2. 3. Return r Factorial function • Here is an implementation of the factorial function: unsigned int factorial( unsigned int n ); unsigned int factorial( unsigned int n ) { unsigned int result{1}; unsigned int k{2}; while ( k <= n ) { result *= k; ++k; } return result; } How to design a while loop • Suppose you are attempting to implement an algorithm where you repeated apply a number of steps • How do you make the transition from manual to programmatic? • Recommendation: • Do the algorithm on paper—in full • Examine the steps you took, and determine: • What steps were repeated? • What condition caused you to stop repeating the steps? The greatest-common divisor • From secondary school, you saw that the algorithm for calculating the greatest common denominator (gcd) • You are asked to find the gcd of 8008 and 8085 • You first note that 8085 > 8008 • Next, you find that 8085 ÷ 8008 equals 1 with a remainder of 77 • Next, you find that 8008 ÷ 77 equals 104 with a remainder of 0 • From this, you are told that the gcd is 77 The greatest-common divisor • Let’s try again: • You are asked to find the gcd of 1583890 and 85800 • You first note that 1583890 > 85800 • Next, you find that 1583890 ÷ 85800 equals 18 with a remainder of 39490 • Next, you find that 85800 ÷ 39490 equals 2 with a remainder of 6820 • Next, you find that 39490 ÷ 6820 equals 5 with a remainder of 5390 • Next, you find that 6820 ÷ 5390 equals 1 with a remainder of 1430 • Next, you find that 5390 ÷ 1430 equals 3 with a remainder of 1100 • Next, you find that 1430 ÷ 1100 equals 1 with a remainder of 330 • Next, you find that 1100 ÷ 330 equals 3 with a remainder of 110 • Next, you find that 330 ÷ 110 equals 3 with a remainder of 0 • From this, you are told that the gcd is 110 The greatest-common divisor • At each step, we had a pair of numbers • Call the m and n • For the initial step, we set • the larger number to be m, and • the smaller number to be n • After that, we repeatedly • calculated the remainder r when dividing m ÷ n, and • if r = 0, we are done, and n is the gcd, • otherwise, we repeat with the pair n and r • That is, we set m ← n and then we set n ← r Remember: when you calculate m ÷ n, the remainder r must always satisfy n > r ≥ 0 The greatest-common divisor • Let’s look at the first steps: • At each step, we had a pair of numbers • Call them m and n • For the initial step, we set • the larger number to be m, and • the smaller number to be n • Problem: We are doing something if the condition is false: • We execute a block of statements when m ≥ n is FALSE • Let’s use the complementary condition: • This is equivalent to executing the statements when m < n is TRUE The greatest-common divisor • Thus, we swap the parameters m and n if m < n The greatest-common divisor • The next steps we perform are that we • calculate the remainder r, and • if r = 0, we are done, and n is the gcd, • otherwise, we repeat with the pair n and r • That is, we set m ← n and then we set n ← r • Problem: this is not in the form a while loop The greatest-common divisor • First, a while loop must return to the condition • We can repeat the first statement at the end The greatest-common divisor • Second, the condition for looping must evaluate to TRUE • Continuing while r = 0 is FALSE is equivalent to continuing while r ≠ 0 is TRUE The greatest-common divisor • Thus, our final flow chart is: The greatest-common divisor • Programming this: unsigned int gcd( unsigned int m, unsigned int n ) { The greatest-common divisor • Programming this: unsigned int gcd( unsigned int m, unsigned int n ) { if ( m < n ) { unsigned int tmp{m}; m = n; n = tmp; } The greatest-common divisor • Programming this: unsigned int gcd( unsigned int m, unsigned int n ) { if ( m < n ) { unsigned int tmp{m}; m = n; n = tmp; } unsigned int r{m % n}; The greatest-common divisor • Programming this: unsigned int gcd( unsigned int m, unsigned int n ) { if ( m < n ) { unsigned int tmp{m}; m = n; n = tmp; } unsigned int r{m % n}; while ( r != 0 ) { } The greatest-common divisor • Programming this: unsigned int gcd( unsigned int m, unsigned int n ) { if ( m < n ) { unsigned int tmp{m}; m = n; n = tmp; } unsigned int r{m % n}; while m n r } ( = = = r != 0 ) { n; r; m % n; The greatest-common divisor • Programming this: unsigned int gcd( unsigned int m, unsigned int n ) { if ( m < n ) { unsigned int tmp{m}; m = n; n = tmp; } unsigned int r{m % n}; while m n r } ( = = = r != 0 ) { n; r; m % n; return n; } Infinite loop? • Question: • What do you do if you accidentally execute a program that has an infinite loop? • Solution: • In Eclipse, there is a stop button that becomes active when a program is executing • Other ides will have similar features • At the console, press Ctrl-C Summary • Following this lesson, you now • Understand how to implement while loops in C++ • Understand how to limit the number of times the corresponding statement block is executed • Seen how to implement various functions requiring looping statements: • The Collatz conjecture • The factorial function • Understand how to convert a description of an algorithm to one that you can program • The example we used was the greatest-common divisor • Know how to terminate a program in an infinite loop References [1] [2] [3] Wikipedia https://en.wikipedia.org/wiki/While_loop cplusplus.com http://www.cplusplus.com/doc/tutorial/control/ tutorialspoint https://www.tutorialspoint.com/cplusplus/cpp_while_loop.htm Outline • In this lesson, we will: • Describe for loops and their implementation in C++ • Describe their purpose • Specifically count-controlled loops Count-controlled loops • We previous looked at executing a block of code a fixed number of times: unsigned int num_iterations{0}; while ( num_iterations < max_iterations ) { // Do something... ++num_iterations; } Count-controlled loops • This is so common, it is given a short form: unsigned int k{0}; while ( k < n ) { // Do something... ++k; } for ( unsigned int k{0}; k < n; ++k ) { // Do something... } Count-controlled loops • This form is called a for loop: for ( unsigned int k{0}; k < n; ++k ) { // Do something... } Initialization statement Incremental statement Conditional expression • The format gives a clean presentation of a count- controlled loop • All you need to know about the loop is on one line Count-controlled loops • Behavior: for ( unsigned int k{0}; k < n; ++k ) { // Do something... } • First, the initialization statement is executed • Before each execution of the block of statements, the condition is checked • If the condition is false, the for loop exits • After all statements in the block are executed, the incremental statement is executed as a separate statement Count-controlled loops • If the variable declaration is in the for loop for ( unsigned int k{0}; k < n; ++k ) { // The scope of 'k' is this block of statements only // Do something... } • If the declaration is before, the variable must simply be assigned an initial value for the for loop unsigned int k{}; for ( k = 0; k < n; ++k ) { // Do something... } // 'k' continues to be in scope Warning • Some programming languages have true count-controlled loops: • For example, Maple: for k from 1 to 10 do % 'k' is assigned a value from 1 to 10 % Do something... end do; This loop will iterate exactly ten times, and with each subsequent execution, the variable k will be assigned the next value Warning • In C++, the values of k and n can be changed inside the body unsigned int n{10}; for ( unsigned int k{0}; k < n; ++k ) { if ( (k % 3) == 2 ) { k += 2; } if ( (k % 4) == 1 ) { ++n; } std::cout << k << ", " << n << std::endl; } Warning • The output may appear confusing: unsigned int n{10}; for ( unsigned int k{0}; k < n; ++k ) { if ( (k % 3) == 2 ) { k += 2; } Output: 0, 1, 4, 7, 10, 10 11 11 11 11 if ( (k % 4) == 1 ) { ++n; Note that k == n in the last iteration… } std::cout << k << ",\t" << n << std::endl; } Warning • In general, don’t do this—use a while loop instead! unsigned int n{10}; unsigned int k{0}; while ( k < n ) { if ( (k % 3) == 2 ) { k += 2; } if ( (k % 4) == 1 ) { ++n; } std::cout << k << ", " << n << std::endl; ++k } Variations on a theme • The following are identical: for ( unsigned int k{0}; k < 10; ++k ) { // 'k' takes on the values, 0, 1, 2, 3, ..., 8, 9 } for ( unsigned int k{0}; k != n; ++k ) { // 'k' takes on the values, 0, 1, 2, 3, ..., 8, 9 } • The first is more common Variations on a theme • Jumping by different values • For example, jumping by two: for ( unsigned int k{1}; k < 16; k += 2 ) { // 'k' takes on the values 1, 3, 5, 7, ..., 15 } • Going down • For example, going down by one: for ( unsigned int k{9}; k > 0; --k ) { // 'k' takes on the values 9, 8, 7, 6, 5, ..., 1 } Variations on a theme • You can jump geometrically: • For example, multiplying by two for ( unsigned int k{1}; k < 100; k *= 2 ) { // 'k' takes on 1, 2, 4, 8, 16, 32, 64 } • You can shrink geometrically: for ( unsigned int k{100}; k > 0; k /= 2 ) { // 'k' takes on 100, 50, 25, 12, 6, 3, 1 } Variations on a theme • You can even use floating-point numbers: for ( double x{0.0}; x <= 1.0; k += 0.1 ) { // 'x' takes on 0.0, 0.1, 0.2, ..., 0.9, 1.0 } • Problem: floating-point numbers are not exact: for ( double x{0.0}; x <= 1.0; k += 1.0/9.0 ) { // 'x' takes on 0, 0.111111, 0.222222, 0.333333, // ..., 0.666667, 0.777778, 0.888889 } Factorial function • Here is an implementation of the factorial function: unsigned int factorial( unsigned int n ); unsigned int factorial( unsigned int n ) { unsigned int result{1}; for ( unsigned int k{2}; k <= n; ++k ) { result *= k; } } return result; If n < 2, the body of the loop is never executed Factorial function • Try this yourself: #include <iostream> // Function declarations int main(); unsigned int factorial( unsigned int n ); // Function definitions int main() { for ( int k{0}; k < 20; ++k ) { std::cout << k << "! = " << factorial( k ) << std::endl; } return 0; } unsigned int factorial( unsigned int n ) { unsigned int result{1}; for ( unsigned int k{2}; k <= n; ++k ) { result *= k; } return result; } Perfect numbers • A number is perfect—whatever that means—if it is the sum of its divisors bool is_perfect( unsigned int n ); bool is_perfect( unsigned int n ) { unsigned int sum{0}; for ( unsigned int k{1}; k < n; ++k ) { if ( (n % k) == 0 ) { sum += k; } } return (sum == n); } Prime numbers • A number n is prime if it is not divisible by any number between 2 and n – 1: bool is_prime( unsigned int n ); bool is_prime( unsigned int n ) { for ( unsigned int k{2}; k < n; ++k ) { if ( (n % k) == 0 ) { return false; } } return true; } Prime numbers • You really only need to search up to the integer square root of n: bool is_prime( unsigned int n ); bool is_prime( unsigned int n ) { unsigned int upper_bound{isqrt(n)}; for ( unsigned int k{2}; k <= upper_bound; ++k ) { if ( (n % k) == 0 ) { return false; } } return true; } Summary • Following this lesson, you now • Understand how to implement for loops in C++ • Know this is a special case of the while loop • Understand it should be restricted to count-controlled loops • Seen various applications References [1] Wikipedia https://en.wikipedia.org/wiki/For_loop