Files
CSC110/08-runtime/05-more-runtime.html
T
Hykilpikonna 6fffdf686a deploy
2021-12-07 22:28:01 -05:00

323 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>8.5 Analyzing Comprehensions and While Loops</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="../tufte.css" />
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" type="text/javascript"></script>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<div style="display:none">
\(
\newcommand{\NOT}{\neg}
\newcommand{\AND}{\wedge}
\newcommand{\OR}{\vee}
\newcommand{\XOR}{\oplus}
\newcommand{\IMP}{\Rightarrow}
\newcommand{\IFF}{\Leftrightarrow}
\newcommand{\TRUE}{\text{True}\xspace}
\newcommand{\FALSE}{\text{False}\xspace}
\newcommand{\IN}{\,{\in}\,}
\newcommand{\NOTIN}{\,{\notin}\,}
\newcommand{\TO}{\rightarrow}
\newcommand{\DIV}{\mid}
\newcommand{\NDIV}{\nmid}
\newcommand{\MOD}[1]{\pmod{#1}}
\newcommand{\MODS}[1]{\ (\text{mod}\ #1)}
\newcommand{\N}{\mathbb N}
\newcommand{\Z}{\mathbb Z}
\newcommand{\Q}{\mathbb Q}
\newcommand{\R}{\mathbb R}
\newcommand{\C}{\mathbb C}
\newcommand{\cA}{\mathcal A}
\newcommand{\cB}{\mathcal B}
\newcommand{\cC}{\mathcal C}
\newcommand{\cD}{\mathcal D}
\newcommand{\cE}{\mathcal E}
\newcommand{\cF}{\mathcal F}
\newcommand{\cG}{\mathcal G}
\newcommand{\cH}{\mathcal H}
\newcommand{\cI}{\mathcal I}
\newcommand{\cJ}{\mathcal J}
\newcommand{\cL}{\mathcal L}
\newcommand{\cK}{\mathcal K}
\newcommand{\cN}{\mathcal N}
\newcommand{\cO}{\mathcal O}
\newcommand{\cP}{\mathcal P}
\newcommand{\cQ}{\mathcal Q}
\newcommand{\cS}{\mathcal S}
\newcommand{\cT}{\mathcal T}
\newcommand{\cV}{\mathcal V}
\newcommand{\cW}{\mathcal W}
\newcommand{\cZ}{\mathcal Z}
\newcommand{\emp}{\emptyset}
\newcommand{\bs}{\backslash}
\newcommand{\floor}[1]{\left \lfloor #1 \right \rfloor}
\newcommand{\ceil}[1]{\left \lceil #1 \right \rceil}
\newcommand{\abs}[1]{\left | #1 \right |}
\newcommand{\xspace}{}
\newcommand{\proofheader}[1]{\underline{\textbf{#1}}}
\)
</div>
<header id="title-block-header">
<h1 class="title">8.5 Analyzing Comprehensions and While Loops</h1>
</header>
<section>
<p>In the previous section, we began our study of algorithm running time analysis by looking at functions that are implemented using for loops. We chose for loops as a starting point because they make explicit the <em>repeated statements</em> that occur when we execute a function body, while also being relatively straightforward to analyze because of their predicable iteration patterns.</p>
<p>In this section, well extend what we learned about for loops to two different kinds of Python code: comprehension expressions and while loop. Well see how all three obey similar patterns when it comes to repeating code, but while loops offer both more flexibility and more complexity in what they can do.</p>
<h2 id="comprehensions">Comprehensions</h2>
<p>Consider the following function:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">def</span> square_all(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">int</span>]:</span>
<span id="cb1-2"><a href="#cb1-2"></a> <span class="co">&quot;&quot;&quot;Return a new list containing the squares of the given numbers.&quot;&quot;&quot;</span></span>
<span id="cb1-3"><a href="#cb1-3"></a> <span class="cf">return</span> [x <span class="op">**</span> <span class="dv">2</span> <span class="cf">for</span> x <span class="kw">in</span> numbers]</span></code></pre></div>
<div class="analysis">
<p>How do we analyze the running time of this code? It turns out that we do so in the same way as a for loop:</p>
<ol type="1">
<li>We determine the number of steps required to evaluate the leftmost expression in the comprehension. In this case, evaluating <code>x ** 2</code> takes 1 step (i.e., is constant time).</li>
<li>The collection that acts as the source of the comprehension (in our example, <code>numbers</code>), determines how many times the leftmost expression is evaluated.</li>
</ol>
<p>So let <span class="math inline">\(n\)</span> be the length of the input list <code>numbers</code>. The comprehension expression takes <span class="math inline">\(n\)</span> steps (1 step per element of <code>numbers</code>). So the running time of <code>square_all</code> is <span class="math inline">\(n\)</span> steps, which is <span class="math inline">\(\Theta(n)\)</span>.</p>
</div>
<p>Importantly, the fact that a comprehension is creating a new collection (in our above example, a list) does <em>not</em> count as additional time when analysing the cost of a comprehension. This is true for all three of list, set, and dictionary comprehensions, and so the same analysis would hold in the above function if we had used a set or dictionary comprehension instead.</p>
<h2 id="while-loops">While loops</h2>
<p>Analysing the running time of code involving while loops follows the same principle using for loops: we calculate the sum of the different loop iterations, either using multiplication (when the iteration running time is constant) or a summation (when the iterations have different running times). There is one subtle twist, though: a while loop requires that we write statements to initialize the loop variable(s) before the loop, and update the loop variable(s) inside the loop body. We must be careful to count the cost of these statements as well, just like we did for statements involving loop accumulators in the previous section.</p>
<p>To keep things simple, our first example is a simple rewriting of an earlier example using a while loop instead of a for loop.</p>
<div class="example">
<p>Analyse the running time of the following function.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">def</span> my_sum_v2(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb2-2"><a href="#cb2-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers.&quot;&quot;&quot;</span></span>
<span id="cb2-3"><a href="#cb2-3"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb2-4"><a href="#cb2-4"></a> i <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb2-5"><a href="#cb2-5"></a></span>
<span id="cb2-6"><a href="#cb2-6"></a> <span class="cf">while</span> i <span class="op">&lt;</span> <span class="bu">len</span>(numbers):</span>
<span id="cb2-7"><a href="#cb2-7"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[i]</span>
<span id="cb2-8"><a href="#cb2-8"></a> i <span class="op">=</span> i <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb2-9"><a href="#cb2-9"></a></span>
<span id="cb2-10"><a href="#cb2-10"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<div class="analysis">
<p>Let <span class="math inline">\(n\)</span> be the length of the input <code>numbers</code>.</p>
<p>In this function, we now have both an accumulator and the loop variable to worry about. We can still divide up the function into three parts, and compute the cost of each part separately.</p>
<ol type="1">
<li><p>The cost of the assignment statements <code>sum_so_far = 0</code> and <code>i = 0</code> is constant time. Well count this as a constant-time block of code, which is just <em>1</em> step.<label for="sn-0" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-0" class="margin-toggle"/><span class="sidenote"> This might be a bit surprising, because they are two lines of code and look like two separate “actions”. The power of our asymptotic notation is that whether we count this block of code as 1 step or 2, we get the same Theta bound in the end! And so we just go with the simpler one here, but youre welcome to count this as “two steps” in your own analyses if you find that more intuitive.</span></p></li>
<li><p>To analyse the while loop, we need to determine the cost of each iteration and the total number of iterations, just like a for loop.</p>
<ul>
<li>Each iteration is constant time, so well count that as one step.</li>
<li>There are <span class="math inline">\(n\)</span> iterations, since <code>i</code> starts at 0 and increases by 1 until it reaches <span class="math inline">\(n\)</span>. Note that this is less obvious than the for loop version! Here we need to look at three different places in the code: how <code>i</code> is initialized, how <code>i</code> is updated inside the loop body, and how <code>i</code> is used in the loop condition.</li>
</ul></li>
<li><p>The return statement again takes constant time, and so counts as 1 step.</p></li>
</ol>
<p>So the total running time is <span class="math inline">\(1 + n + 1 = n + 2\)</span>, which is <span class="math inline">\(\Theta(n)\)</span>.</p>
</div>
</div>
<p>Now, the previous example was a little contrived because we could have implemented the same function more simply using a for loop. Here is another example, which uses a while loop to compute powers of two to act as indexes into a list.</p>
<div class="example">
<p>Analyse the running time of the following function.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1"></a><span class="kw">def</span> my_sum_powers_of_two(numbers: <span class="bu">list</span>[<span class="bu">int</span>]) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb3-2"><a href="#cb3-2"></a> <span class="co">&quot;&quot;&quot;Return the sum of the given numbers whose indexes are powers of 2.</span></span>
<span id="cb3-3"><a href="#cb3-3"></a></span>
<span id="cb3-4"><a href="#cb3-4"></a><span class="co"> That is, return numbers[1] + numbers[2] + numbers[4] + numbers[8] + ...</span></span>
<span id="cb3-5"><a href="#cb3-5"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-6"><a href="#cb3-6"></a> sum_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb3-7"><a href="#cb3-7"></a> i <span class="op">=</span> <span class="dv">1</span></span>
<span id="cb3-8"><a href="#cb3-8"></a></span>
<span id="cb3-9"><a href="#cb3-9"></a> <span class="cf">while</span> i <span class="op">&lt;</span> <span class="bu">len</span>(numbers):</span>
<span id="cb3-10"><a href="#cb3-10"></a> sum_so_far <span class="op">=</span> sum_so_far <span class="op">+</span> numbers[i]</span>
<span id="cb3-11"><a href="#cb3-11"></a> i <span class="op">=</span> i <span class="op">*</span> <span class="dv">2</span></span>
<span id="cb3-12"><a href="#cb3-12"></a></span>
<span id="cb3-13"><a href="#cb3-13"></a> <span class="cf">return</span> sum_so_far</span></code></pre></div>
<div class="analysis">
<p>Let <span class="math inline">\(n\)</span> be the length of the input list <code>numbers</code>.</p>
<p>This code has much of the same structure as <code>my_sum_v2</code>, and we can reuse most of the same analysis here. In particular, well still count the initial assignment statements as 1 step, and the return statement as 1 step. To analyse the loop, we still need the number of steps per iteration and the total number of iterations. Each iteration still takes constant time (1 step), same as <code>my_sum_v2</code>. It is the number of loop iterations that is most challenging.</p>
<p>To determine the number of loop iterations, we need to take into account the initial value of <code>i</code>, how <code>i</code> is updated, and how <code>i</code> is used in the while loop condition. More formally, we follow these steps:</p>
<ol type="1">
<li><p>Find a pattern for how <code>i</code> changes at each loop iteration, and a general formula formula for <span class="math inline">\(i_k\)</span>, the value of <code>i</code> after <span class="math inline">\(k\)</span> iterations. For relatively simple updates, we can find a pattern by writing a small loop tracing table, showing the value of the loop variable at the <em>end</em> of the iteration.</p>
<div class="reference-table">
<table>
<thead>
<tr class="header">
<th style="text-align: left;">Iteration</th>
<th>Value of <code>i</code></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">0</td>
<td>1</td>
</tr>
<tr class="even">
<td style="text-align: left;">1</td>
<td>2</td>
</tr>
<tr class="odd">
<td style="text-align: left;">2</td>
<td>4</td>
</tr>
<tr class="even">
<td style="text-align: left;">3</td>
<td>8</td>
</tr>
<tr class="odd">
<td style="text-align: left;">4</td>
<td>16</td>
</tr>
</tbody>
</table>
</div>
<p>So we find that after <span class="math inline">\(k\)</span> iterations, <span class="math inline">\(i_k = 2^k\)</span>.<label for="sn-1" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote"> Note that we havent <em>proved</em> that this formula is true; a formal proof would require a proof by induction, which you may have already seen in your math classes.</span></p></li>
<li><p>We know the while loop continues while <code>i &lt; len(numbers)</code>. Another way to phrase this is that the while loop continues <em>until</em> <code>i &gt;= len(numbers)</code>.</p>
<p>So to find the number of iterations, we need to find the smallest value of <span class="math inline">\(k\)</span> such that <span class="math inline">\(i_k \geq n\)</span> (making the loop condition False). This is where our formula for <span class="math inline">\(i_k\)</span> comes in:</p>
<p><span class="math display">\[\begin{align*}
i_k &amp;\geq n \\
2^k &amp;\geq n \\
k &amp;\geq \log_2 n
\end{align*}\]</span> So we need to find the smallest value of <span class="math inline">\(k\)</span> such that <span class="math inline">\(k \geq \log_2 n\)</span>. This is exactly the definition of the ceiling function, and so the smallest value of <span class="math inline">\(k\)</span> is <span class="math inline">\(\ceil{\log_2 n}\)</span>.</p></li>
</ol>
<p>So the while loop iterates <span class="math inline">\(\ceil{\log_2 n}\)</span> times, with 1 step per iteration, for a total of <span class="math inline">\(\ceil{\log_2 n}\)</span> steps.</p>
<p>Putting it all together, the function <code>my_sum_powers_of_two</code> has a running time of <span class="math inline">\(1 + \ceil{\log_2 n} + 1 = \ceil{\log_2 n} + 2\)</span>, which is <span class="math inline">\(\Theta(\log n)\)</span>.<label for="sn-2" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote"> Note that our convention is to drop the base of the log when writing a Theta expression, since all bases <span class="math inline">\(&gt; 1\)</span> are equivalent to each other in Theta bounds.</span></p>
</div>
</div>
<h2 id="a-trickier-example">A trickier example</h2>
<p>It turns out that the extreme flexibility of while loops can make analysing their running time much more subtle than it might appear. Our next example considers a standard loop, with a twist in how the loop variable changes at each iteration.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">def</span> twisty(n: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb4-2"><a href="#cb4-2"></a> <span class="co">&quot;&quot;&quot;Return the number of iterations it takes for this special loop to stop</span></span>
<span id="cb4-3"><a href="#cb4-3"></a><span class="co"> for the given n.</span></span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a> iterations_so_far <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb4-6"><a href="#cb4-6"></a> x <span class="op">=</span> n</span>
<span id="cb4-7"><a href="#cb4-7"></a> <span class="cf">while</span> x <span class="op">&gt;</span> <span class="dv">1</span>:</span>
<span id="cb4-8"><a href="#cb4-8"></a> <span class="cf">if</span> x <span class="op">%</span> <span class="dv">2</span> <span class="op">==</span> <span class="dv">0</span>:</span>
<span id="cb4-9"><a href="#cb4-9"></a> x <span class="op">=</span> x <span class="op">/</span> <span class="dv">2</span></span>
<span id="cb4-10"><a href="#cb4-10"></a> <span class="cf">else</span>:</span>
<span id="cb4-11"><a href="#cb4-11"></a> x <span class="op">=</span> <span class="dv">2</span> <span class="op">*</span> x <span class="op">-</span> <span class="dv">2</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> iterations_so_far <span class="op">=</span> iterations_so_far <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb4-13"><a href="#cb4-13"></a></span>
<span id="cb4-14"><a href="#cb4-14"></a> <span class="cf">return</span> iterations_so_far</span></code></pre></div>
<p>Even though the individual lines of code in this example are simple, they combine to form a pretty complex situation. The challenge with analyzing the runtime of this function is that, unlike previous examples, here the loop variable <code>x</code> does not always get closer to the loop stopping condition; sometimes it does (when divided by two), and sometimes it increases!</p>
<p>The key insight into analyzing the runtime of this function is that we dont just need to look at what happens after a single loop iteration, but instead perform a more sophisticated analysis based on <em>multiple</em> iterations.<label for="sn-3" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote"> As preparation, try tracing <code>twisty</code> on inputs <span class="math inline">\(7\)</span>, <span class="math inline">\(9\)</span>, and <span class="math inline">\(11\)</span>.</span> More concretely, well prove the following claim.</p>
<div class="claim">
<p>For any integer value of <code>x</code> greater than <span class="math inline">\(2\)</span>, after <em>two</em> iterations of the loop in <code>twisty</code> the value of <code>x</code> decreases by at least one.</p>
<div class="proof">
<p>Let <span class="math inline">\(x_0\)</span> be the value of variable <code>x</code> at some iteration of the loop, and assume <span class="math inline">\(x_0 &gt; 2\)</span>. Let <span class="math inline">\(x_1\)</span> be the value of <span class="math inline">\(x\)</span> after one loop iteration, and <span class="math inline">\(x_2\)</span> the value of <span class="math inline">\(x\)</span> after two loop iterations. We want to prove that <span class="math inline">\(x_2 \leq x_0 - 1\)</span>.</p>
<p>We divide up this proof into four cases, based on the remainder of <span class="math inline">\(x_0\)</span> when dividing by four.<label for="sn-4" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span class="sidenote">The intuition for these cases is that this determines whether <span class="math inline">\(x_0\)</span> is even/odd, and whether <span class="math inline">\(x_1\)</span> is even/odd.</span> Well only do two cases here to illustrate the main idea, and leave the last two cases as an exercise.</p>
<p><strong>Case 1</strong>: Assume <span class="math inline">\(4 \DIV x_0\)</span>, i.e., <span class="math inline">\(\exists k \in \Z,~ x_0 = 4k\)</span>.</p>
<p>In this case, <span class="math inline">\(x_0\)</span> is even, so the <code>if</code> branch executes in the first loop iteration, and so <span class="math inline">\(x_1 = \frac{x_0}{2} = 2k\)</span>. And so then <span class="math inline">\(x_1\)</span> is also even, and so the <code>if</code> branch executes again: <span class="math inline">\(x_2 = \frac{x_1}{2} = k\)</span>.</p>
<p>So then <span class="math inline">\(x_2 = \frac{1}{4}x_0 \leq x_0 - 1\)</span> (since <span class="math inline">\(x_0 \geq 4\)</span>), as required.</p>
<p><strong>Case 2</strong>: Assume <span class="math inline">\(4 \DIV x_0 - 1\)</span>, i.e., <span class="math inline">\(\exists k \in \Z,~ x_0 = 4k + 1\)</span>.</p>
<p>In this case, <span class="math inline">\(x_0\)</span> is odd, so the <code>else</code> branch executes in the first loop iteration, and so <span class="math inline">\(x_1 = 2x_0 - 2 = 8k\)</span>. Then <span class="math inline">\(x_1\)</span> is even, and so <span class="math inline">\(x_2 = \frac{x_1}{2} = 4k\)</span>.</p>
<p>So then <span class="math inline">\(x_2 = 4k = x_0 - 1\)</span>, as required.</p>
<p><strong>Cases 3 and 4</strong>: left as exercises.</p>
</div>
</div>
<p>Now lets see how take this claim and use it to formally analyse the running time of <code>twisty</code>.</p>
<div class="analysis">
<p>(<em>Analysis of <code>twisty</code></em>)</p>
<p>As before, we count the variable initializations before the while loop as 1 step, and the return statement as 1 step.</p>
<p>For the while loop:</p>
<ul>
<li><p>The loop body also takes 1 step, since all of the code consists of operations that do not depend on the size of the input <span class="math inline">\(n\)</span>.</p></li>
<li><p>To count the number of loop iterations, we first observe that <span class="math inline">\(x\)</span> starts at <span class="math inline">\(n\)</span> and the loop terminates when <span class="math inline">\(x\)</span> reaches 1 or less. The <em>Claim</em> tells us that after every two iterations, the value of <span class="math inline">\(x\)</span> decreases by at least one.</p>
<p>So then after 2 iterations, <span class="math inline">\(x \leq n - 1\)</span>, after 4 iterations, <span class="math inline">\(x \leq n - 2\)</span>, and in general, after <span class="math inline">\(2k\)</span> iterations, <span class="math inline">\(x \leq n - k\)</span>. This tells us that after <span class="math inline">\(2(n - 1)\)</span> loop iterations, <span class="math inline">\(x \leq n - (n - 1) = 1\)</span>, and so the loop must stop.</p></li>
</ul>
<p>This analysis tells us that the loop iterations <em>at most</em> <span class="math inline">\(2(n - 1)\)</span> times, and so takes <em>at most</em> <span class="math inline">\(2(n - 1)\)</span> steps (remember that each iteration takes 1 step).</p>
<p>So the total running time of <code>twisty</code> is <em>at most</em> <span class="math inline">\(1 + 2(n - 1) + 1 = 2n\)</span> steps, which is <span class="math inline">\(\cO(n)\)</span>.</p>
</div>
<p>Something funny happened at the end of the above analysis: we did not actually compute the exact number of steps the function <code>twisty</code> takes, only an <em>upper bound</em> on the number of steps (signalled by our use of the phrase “at most”). This means that we were only able to conclude a Big-O bound, and not a Theta bound, on the running time of this function: its running time is <em>at most</em> <span class="math inline">\(\cO(n)\)</span>, but we dont know whether this bound is tight.</p>
<p>In fact, it isnt! It is possible to prove something pretty remarkable about what happens to the variable <code>x</code> after <em>three</em> iterations of the twisty loop.</p>
<div class="claim">
<p>(<em>Improved claim</em>)</p>
<p>For any integer value of <code>x</code> greater than <span class="math inline">\(2\)</span>, let <span class="math inline">\(x_0\)</span> be the initial value of <code>x</code> and let <span class="math inline">\(x_3\)</span> be the value of <code>x</code> after <em>three</em> loop iterations. Then <span class="math inline">\(\frac{1}{8} x_0 \leq x_3 \leq \frac{1}{2} x_0\)</span>.</p>
</div>
<p>It is a good exercise to prove this claim<label for="sn-5" class="margin-toggle sidenote-number"></label><input type="checkbox" id="sn-5" class="margin-toggle"/><span class="sidenote"> Hint: you can use the same approach as the previous claim, but consider remainders when you divide by 8 instead of 4.</span> and then use this claim to conduct a more detailed running time analysis of <code>twisty</code>. When you do so, you should be able to show that the running time of <code>twisty</code> is both <span class="math inline">\(\cO(\log n)\)</span> and <span class="math inline">\(\Omega(\log n)\)</span>, and hence conclude that its running time is actually <span class="math inline">\(\Theta(\log n)\)</span>, not just <span class="math inline">\(\cO(n)\)</span>!</p>
<!-- <div exercise>
<div questions data-series="chapter5">
\item
The analysis we performed in the previous example is incomplete for a few reasons; our goal with this set of exercises is to complete it here.
(a) Complete the last two cases in the proof of the claim.
(b) State and prove an analogous statement for how much $x$ must decrease by after *three* loop iterations.
(c) Find an exact upper bound on the number of loop iterations taken by this algorithm. Your upper bound should be smaller (and therefore more accurate) than the one given in the example.
(d) Finally, find, with proof, a good *lower bound* on the number of loop iterations taken by this algorithm.
</div>
</div> -->
</section>
<footer>
<a href="https://www.teach.cs.toronto.edu/~csc110y/fall/notes/">CSC110 Course Notes Home</a>
</footer>
</body>
</html>