Skip to content

Commit a3b2348

Browse files
committed
Improved Heron's Method
1 parent 9aac149 commit a3b2348

File tree

1 file changed

+23
-14
lines changed

1 file changed

+23
-14
lines changed

text/main/controlFlow/loops/loops.tex

+23-14
Original file line numberDiff line numberDiff line change
@@ -363,15 +363,16 @@
363363
\end{equation}%
364364
%
365365
We can roughly imagine that the algorithm works as follows:
366-
If $x_i$ was too big~($x_i>\sqrt{a}$), then~$\frac{a}{x_i}<\sqrt{a}<x_i$.
367-
If $x_i$ was too small~($x_i<\sqrt{a}$), then $\frac{a}{x_i}>\sqrt{a}>x_i$.
368-
By computing the average of~$x_i$ and~$\frac{a}{x_i}$ as the next guess, we will approach~$\sqrt{a}$.
366+
If $x_i$ was too big, i.e., $x_i>\sqrt{a}$, then~$\frac{a}{x_i}<\sqrt{a}<x_i$.
367+
If $x_i$ was too small, i.e., $x_i<\sqrt{a}$, then $\frac{a}{x_i}>\sqrt{a}>x_i$.
368+
By computing the average of~$x_i$ and~$\frac{a}{x_i}$ as the next guess, we hope to approach~$\sqrt{a}$.
369369
If $x_i=\sqrt{a}$, then $\frac{a}{x_i}=\sqrt{a}$ by definition and~$x_{i+1}=x_i$.
370+
Showing that this actually works and that the error gets smaller over time is more complicated~\cite{S2011NA2}.
370371

371372
If we want to implement this algorithm, we will naturally need a loop of some sort.
372373
Clearly, we perform the same computation again and again.
373374
However, a \pythonil{for}~loop will not do:
374-
We do not know the number of steps we need in advance.
375+
We do not know the number of steps that we will need in advance.
375376
Of course, we could try to pick a very very huge number and then \pythonil{break} the loop when the guesses converg {\dots} but that is just ugly.
376377
The \pythonilIdx{while} comes to rescue:%
377378
%
@@ -395,19 +396,27 @@
395396

396397
We begin the program with an outer \pythonil{for} loop that iterates a variable~\pythonil{number} over the \pythonil{float} values \pythonil{0.5}, \pythonil{2.0}, and~\pythonil{3.0}.
397398
We want to apply the algorithm to each of these values.
398-
We use two variables~\pythonil{guess} be the current guess of what $\sqrt{\pythonil{number}}$ could be and \pythonil{old_guess} be the previous guess.%
399+
We use two variables~\pythonil{guess} be the current guess of what $\sqrt{\pythonil{number}}$ could be and \pythonil{old_guess} be the previous guess.
400+
401+
We initialize \pythonil{guess} with \pythonil{1.0} and \pythonil{old_guess} with a different value, say~\pythonil{0.0}.
402+
Our \pythonilIdx{while} loop should keep iterating as long as~\pythonil{guess != old_guess}.
403+
Of course, if we could represent real numbers at infinite precision, we would never reach \pythonil{guess == old_guess} for any \pythonil{number} with an irrational square root.
404+
However, the \pythonil{float} datatype has limited precision (see \cref{sec:howFloatingPointNumbersWork}).
405+
Therefore, at least in our examples, \pythonil{guess != old_guess} indeed eventually becomes~\pythonilIdx{False}.%
399406
%
400-
\begin{sloppypar}%
401-
We initialize \pythonil{guess} with \pythonil{1.0}.
402-
Our \pythonilIdx{while} loop should keep iterating as long as \pythonil{guess != old_guess}.
403-
This only works because the \pythonil{float} datatype has limited precision which we will eventually exhaust, see \cref{sec:howFloatingPointNumbersWork}.
404-
Anyway, the loop condition necessitates us to store some value different from~\pythonil{1.0} in \pythonil{old_guess} initially (and we pick~\pythonil{0.0}).
405-
In the loop, first the current guess becomes the old guess via \pythonil{old_guess = guess}.
406-
Then we update the guess as in \cref{eq:heronGuessUpdate}, by setting \pythonil{guess = 0.5 * (guess + number / guess)}.%
407-
\end{sloppypar}%
407+
\bestPractice{noFloatEqualAsCondition}{%
408+
Do not use (un)equality comparisons of \pythonils{float} as loop termination criteria, as they may lead to endless loops. %
409+
There can always be inputs that cause endless oscillations between values or the appearance of \pythonilIdx{nan} values (see \cref{sec:float:special}).%
410+
}%
408411
%
412+
The only reason why we did it here is that it looks nice and is easy to read as a functional and yet brief example for a \pythonilIdx{while} loop.
413+
Anyway, the loop condition necessitates us to store some value different from~\pythonil{1.0} in \pythonil{old_guess} initially (and we picked~\pythonil{0.0}).
414+
In the loop, first the current guess becomes the old guess via \pythonil{old_guess = guess}.
415+
Then we update the guess as specified in \cref{eq:heronGuessUpdate}, by setting \pythonil{guess = 0.5 * (guess + number / guess)}.
416+
409417
Finally, we print the result of the computation, and for the sake of comparison, we also print the output of the \pythonilIdx{sqrt} function of the \pythonilIdx{math} module.
410-
As you can see, our algorithm delivers exactly the same result.
418+
As you can see, our algorithm delivers almost the same result.
419+
It works quite well, at very high precision.
411420
Also, notice how we used the Unicode escape method from \cref{sec:unicodeChars} to represent the characters~$\sqrt{\cdot}$ and~$\approx$ as \textil{\\u221A} and \textil{\\u2248} to get them printed on the console.%
412421
%
413422
\FloatBarrier%

0 commit comments

Comments
 (0)