CS1010 Notes
  • Welcome
  • Lec/Tut/Lab/Exes
    • Lecture
      • Lec 01 - Computational Problem Solving
      • Lec 02 - Functions and Types
      • Lec 03 - Basic C Programming
      • Lec 04 - Conditionals
      • Lec 05 - Loops
      • Lec 06 - Call Stacks, Arrays
        • Diagnostic Quiz
      • Lec 07 - Pointers, Memory management
        • Diagnostic Quiz
      • Lec 08 - Multi-d Array, Efficiency
        • Diagnostic Quiz
      • Lec 09 - Searching and Sorting
        • Diagnostic Quiz
      • Lec 10 - More Recursion
        • Diagnostic Quiz
      • Lec 11 - Strcut & Standard I/O
        • Diagnostic Quiz
      • Lec 12 - Recap
    • Tutorial
      • Tut 01 - Computational Problem-Solving
      • Tut 02 - Functions and Conditionals
      • Tut 03 - More on Conditionals
      • Tut 04 - Loops
      • Tut 08 - Searching and Sorting
    • Lab
      • Lab 01 - Unix/Vim Setup
      • Lab 02 - Debugging
      • Lab 03 - Assert
      • Lab 04 - Test Cases
      • Lab 05 - Arrays
      • Lab 06 - Memory Errors
      • Lab 07 - Compiling with Clang
      • Lab 08 - C Preprocessor
      • Lab 09 - Backtracking
      • Lab 10 - Struct and Wrap up
    • Exercises
      • Exercise 3 - Fixed-Length Arrays
      • Exercise 4 - Dynamic Arrays and Strings
      • Exercise 6 - Searching and Sorting
      • Exercise 7 - More Recursion
      • Exercise 8 - Struct
  • Past Year Exam
    • Midterm PE
      • PE1 (AY18/19)
      • PE1 (AY20/21)
      • PE1 (AY21/22)
      • PE0 (AY22/23)
      • PE0 (AY23/24)
    • Midterm Paper
      • Midterm (AY18/19)
      • Midterm (AY20/21)
      • Midterm (AY21/22)
      • Midterm (AY22/23)
    • PE1 Review
      • PE1 (AY23/24)
    • PE2 Review
      • PE2 (AY18/19)
      • PE2 (AY20/21)
      • PE2 (AY21/22)
      • PE2 (AY22/23)
      • PE2 (AY23/24)
    • Final Paper
      • Final (AY18/19)
      • Final (AY20/21)
      • Final (AY21/22)
      • Final (AY22/23)
      • Final (AY23/24)
  • Current Year Exam
    • PE0 (AY24/25)
    • PE1 (AY24/25)
    • PE2 (AY24/25)
    • Final (AY24/25)
  • Toolbox
    • Vim & Unix
    • GDB
  • After CS1010
Powered by GitBook
On this page
  • Problem Set 11
  • Problem 11.1
  • Problem 11.2
  • Problem Set 12
  • Problem 12.1
Edit on GitHub
  1. Lec/Tut/Lab/Exes
  2. Tutorial

Tut 04 - Loops

Big thanks for my tutor Eric Han!

PreviousTut 03 - More on ConditionalsNextTut 08 - Searching and Sorting

Last updated 8 months ago

Problem Set 11

Problem 11.1

To get a better understanding of the code, we can draw the flowchart first.

Before we analyze this problem, let's talk about how we can judge whether an algorithm is correct or incorrect.

  1. This answer is binary right, an algorithm can either be correct or incorrect.

  2. Now, if an algorithm is incorrect, we just need to find at least one counterexample.

Usually we start from proving an algorithm to be incorrect by using the counterexample method since finding counterexamples is easier than proving it directly. And if after some tries, we cannot find the counterexample, we may try using proving techniques, like proof by induction and so on, to prove that the alrogitm is correct.

Go back to our problem now. Let's try with some examples. Let's say our input n is 4. Let's step through the code line by line;

Input: n = 4
Line 3: i = 3
Line 4: product (variable declaration)
Line 5: product = 4 (loop initialization)
Line 5: i >= 2 -> True (loop check condition)
Line 7: i = 2 (loop body)
Line 5: product = 4 * 2 = 8 (loop update)
Line 5: i >= 2 -> True (loop check condition)
Line 7: i = 1 (loop body)
Line 5: product = 8 * 1 = 8 (loop update)
Line 5: i >= 2 -> False (loop check condition)
Line 9: return 8

After this stepping through, you may notice that 4!=244! = 244!=24 but is not 888. This means that we've already found a counterexample and this will be enough to show that our algorithm is incorrect.

Now, we may wonder what shall we do to make our program correct?

In either our flowchart or our stepping through. We may find that i is decremented by 1 before we multiply the product with i. To solve it, we have two solutions:

  1. Initialize i to be n instead of n-1 at Line 3.

  2. Swap the product *= i statement with the i -= 1, which is equivalent to swapping our loop body and our loop update.

Problem 11.2

Part (a)

This is quite simple so I won't step through it one by one. Answers are below:

  1. 3 is returned

  2. 4 is returned

  3. 2 is returned

Pay attention to the third case where n is 100 and k is 5. In this case, the reason why the value returned is not 3 but 2 is that your loop check condition is something >= 1.

Part (b)

If your math is good, you may quickly find that this algorithm looks similar to logarithm, and you might intuitively think that the value returned is merely the logarithm of n to the base k. However, take a look at the case 3 in Part (a), or if you are familiar with the property of logarithm functions. You may find that our mystery function will only return integer values, but a logarithm function can return any real number. (Let's just forget about complex number here)

So, how can we optimzie the logarithm based on our probelm and make its output an integer?

If you are a man good at observing, you may find that our output is always smaller than or equal to the logarithm of n to the base k. Using this idea, we may quickly think of the floor function. So, our final answer should be

valuereturned=⌊log⁡kn⌋value_{returned}=\lfloor\log_kn\rfloorvaluereturned​=⌊logk​n⌋

Part (c)

Logarithm, like log⁡ab\log_abloga​b has some limitations on its base aaa and argument bbb, where a>0a>0a>0 && a≠1a\neq1a=1 and b>0b>0b>0. But in our mystery function (now we know that it's a floor logarithm function), there is no such restriction. So, we may start from the "crazy" numbers that violate the requirements the definition of Logarithm. For example, we can use "crazy" numbers like k=0,n=0k=0,n=0k=0,n=0. And of course, you can comp up with many many crazy numbersr like these.

Part (d)

To make an infinite loop, its loop check condition must always be TRUE.

Using this idea, our loop check condition is something >= 1 and our loop body contains something /= k. So, how to make our loop check condition always TRUE? We can set k=1k=1k=1 right? And if at the moment or before we enter the loop, something is already greater than or equal to 1. We are pretty sure we will enter a infinite loop right?

Problem Set 12

Problem 12.1

Loop Invariant

A loop invariant is an assertion that is true before the loop, after each iteration of the loop, and after the loop.

An invariant is useful thinking and reasoning tool to help us convince ourselves that our loop behaves correctly. { product == n! }

Loop invariant, however, is not unique. We can write down infinitely many loop invariants. A good invariant, however, will lead us to an assertion that we want to see (e.g., relating product to n).

Part (a)

In this problem, our base case is when m=l0,i=1m=l_0,i=1m=l0​,i=1. And since we want to relate our loop invariant to our input k and our purpose find the maximum number among the list so that our loop invariant is useful. So, we may redefine our base case statment as:

m=l0,m=max[l0,li−1]\large m=l_0,m=max[l_0,l_{i-1}]m=l0​,m=max[l0​,li−1​]

Using this base case statement, let's move on to the loop body. Our induction statement should be

m=lk−1,m=max[l0,lk−1]\large m=l_{k-1}, m=max[l_0,l_{k-1}]m=lk−1​,m=max[l0​,lk−1​]

Is this loop invariant correct?

Obviously no. In our statement above, mmm (which is the maximum) must be the kthk^{th}kth element in the list. That doesn't hold! But this may help us revise our loop invariant to

m=max[l0,li−1]\large m=max[l_0,l_{i-1}]m=max[l0​,li−1​]

Now, we go back to our base case and check our loop invariant's correctness.

m=max[l0,l0]\large m=max[l_0,l_0]m=max[l0​,l0​]

There is only one element in our list, so m=l0m=l_0m=l0​ should obviously be the maximum in the list.

Now, let's check whether our loop invariant is correct after the loop

m=max[l0,lk−1]\large m=max[l_0,l_{k-1}]m=max[l0​,lk−1​]

Since m is already the maximum of the list, we can safely say that we have found a good and useful loop invariant, which is

m=max[l0,li−1]\large m=max[l_0,l_{i-1}]m=max[l0​,li−1​]

Part (b)

Using the similar loop invariant we have found in Part (a), we can see that at the base case stage,

m=0,i=0\large m=0,i=0m=0,i=0

But our loop invariant now should be

m=max[l0,l0]\large m=max[l_0,l_0]m=max[l0​,l0​]

But, this is not true if l0l_0l0​ is smaller than 0. Even inside the loop body, if all the elements is smaller than 0, then our m will still be 0 at last. So, we cannot find a loop invariant that is similar to Part (a).

Note that it is we cannot find a loop invariant that is similar to Part (a). But it is still possible to find other loop invariants, though they may not be useful and none of them can help us assert that our algorithm is correct.

So, if an algorithm is correct, we need to prove it. How can we prove? Use the assertion. See more at

To find a good and useful loop invariant, we'd better to know what mathematical induction (or proof by induction) is. For more detail, please see .

What's the use of assertion
Mathematical Induction
Problem 11.1
Flowchart for part (a)