
Discrete math with computers_3
.pdf12.6. BALANCED TREES |
347 |
balanced.
When we reach the top of the spine, we deliver a new tree with key, data item, left subtree, and right subtree specified as follows: (1) use the key and data item cached key from the sacrum, (2) for the left subtree, use the tree we constructed as we rotated our way back up the spine, and (3) for the right subtree, use the right subtree from the original tree.
Finally, we rotate the tree left, if necessary to get it back in balance. This may be necessary because the height of the left subtree may now be one less than it was originally, and this may have caused the tree to get out of balance by one. Placing the cached key (the one that was in the sacrum) at the root preserves order because it was the largest key in the left subtree, so all keys in the left subtree will be smaller than it is, as they should be in an ordered search tree. Also, it is smaller than any key in the original right subtree because it was smaller than the key at the root, and the key at the root was larger than any key in the original left subtree.
To make these informal ideas amenable to mathematical reasoning, we need formal equations to specify the relationships among the trees and subtrees involved. It will help to consider parts of the transformation separately.
12.6.7Shrinking the Spine
One major component in the transformation is the shrinking of the spine when the left subtree is not a Nub. In Figure 12.8, this is the part of the transformation diagrammed in the top box of the figure. At the same time, we cache the key and data item at the sacrum. So, this part of the transformation needs to deliver a new left subtree, with a shrunken spine, together with the key and data item from the sacrum. We will implement this delivery with a three-tuple in which the first component is the key from the sacrum, the second component is the data item from the sacrum, and the third component is the new, shrunken, left subtree. With these conventions, the following equations specify the relationships among the components that the diagram in Figure 12.8 displays.
shrink(Cel y b lf rt) = |
{shrink} |
if rt == Nub then (y, b, lf) |
|
else if (height lf) > (height shrunken) + 1 |
|
then (x, a, rotR (Cel y b lf shrunken)) |
|
else (x, a, Cel y b lf shrunken) |
|
where |
|
(x, a, shrunken) = shrink rt |
|
The shrink operation preserves order and balance, and it delivers a key that exceeds all the keys remaining in the shrunken tree. The following theorems, all of which can be proved using tree induction, make these claims precise. We will prove only one of them and, as usual, assume it is possible to construct the other proofs.
348 |
CHAPTER 12. THE AVL TREE MIRACLE |
Theorem 96. (shrink preserves balance)
y. b. lf. rt.
(balanced (Cel y b lf rt) ((x, a, sh) = shrink (Cel y b lf rt))) → balanced (sh)
Theorem 97. (shrink loses no keys)
y. b. lf. rt.
(ordered (Cel y b lf rt) ((x, a, sh) = shrink (Cel y b lf rt)) (k (Cel y b lf rt)) (k = x)) → (k sh)
Theorem 98. (shrink adds no keys)
y. b. lf. rt.
ordered (Cel y b lf rt) ((x, a, sh) = shrink (Cel y b lf rt)) (k (Cel y b lf rt)) → (k sh)
Theorem 99. (shrink preserves order)
y. b. lf. rt.
ordered (Cel y b lf rt) ((x, a, sh) = shrink (Cel y b lf rt)) → (ordered (sh) ( k sh. x > k))
Proof. of Theorem 99 (by tree induction).
Base Case.
ordered (Cel y b lf Nub) → (ordered (lf) ( k lf.y > k))
by the definition of ordered , and
((y, b, lf) = shrink (Cel y b lf Nub))
by the definition of shrink.
Therefore, as sh = lf and x = y,
(ordered (Cel y b lf Nub) ((x, a, sh) = shrink (Cel y b lf Nub)))
→ (ordered (sh) ( k sh. x > k))
Inductive Case.
We are assuming, in the inductive case that (Cel y b lf (Cel k d kL kR)) is ordered and that (x, a, sh) = shrink (Cel y b lf (Cel k d kL kR)). In this case the else-branch will be the applicable formula in the {shrink} equation. Therefore, sh will have one of the following two values: (Cel y b lf shrunken) or rotR (Cel y b lf shrunken), where (x, a, shrunken) = shrink (Cel k d kL kR). Also, we know from the conservation-of-keys theorems for rotR (Theorem 92) and for shrink (Theorems 97 and 98) that in either case sh contains all the keys of lf plus all the keys of (Cel k d kL kR) except x.
12.6. BALANCED TREES |
349 |
From the induction hypothesis, we know that x exceeds all the keys of shrunken. This means that x exceeds all the keys of (Cel k d kL kR) except x, itself, and that x (Cel k d kL kR). Therefore, since (Cel y b lf (Cel k d kL kR)) is ordered, it must be true that x > y and that x exceeds all the keys in lf.
This accounts for all the keys in sh, so it must also be true that x exceeds all the keys in sh. Furthermore, if sh = (Cel y b lf shrunken), then it must be ordered because lf is ordered (a consequence of the fact that (Cel y b lf (Cel k d kL kR)) is ordered), shrunken is ordered (a consequence of the induction hypothesis), and y exceeds all the elements in lf and precedes all the elements in (Cel k d kL kR) (a consequence of the fact that (Cel y b lf (Cel k d kL kR)) is ordered). Finally, if it turns out that sh = rotR (Cel y b lf shrunken), then sh must be ordered because rotations preserve order (Theorem 90).
12.6.8Equations for Deleting Root
Figure 12.8 describes the deletion process, for the case when the left subtree is not Nub, in two steps: (1) cache the sacrum and shrink the spine in the left subtree and (2) form a new tree (with the cached key/data at the root, the shrunken left subtree, and the right subtree that came with the original). Finally, rotate left if the constructed tree is out of balance. The following equation expresses these relationships in a formal way.
delRoot (Cel z d Nub zR) = zR |
{delRoot N} |
delRoot (Cel z d (Cel y b lf rt) zR) = |
{delRoot C} |
if (height zR) > (height shrunken) + 1 |
|
then rotL (Cel x a shrunken rt) |
|
else (Cel x a shrunken rt) |
|
where |
|
(x, a, shrunken) = shrink (Cel y b lf rt) |
|
These equations imply that delRoot preserves order and balance and conserves keys (except for the one at the root). The following theorems, which can be proved using tree induction, state these facts.
Theorem 100. (delRoot preserves order)
y. b. lf. rt.
order (Cel y b lf rt) → order (delRoot (Cel y b lf rt))
Theorem 101. (delRoot preserves balance)
y. b. lf. rt.
balanced (Cel y b lf rt) → balanced (delRoot (Cel y b lf rt))
Theorem 102. (delRoot conserves keys)
y. b. lf. rt.
(ordered (Cel y b lf rt) (k (Cel y b lf rt)) (k = y)) →
350 |
CHAPTER 12. THE AVL TREE MIRACLE |
(k delRoot (Cel y b lf rt))
and
(ordered (Cel y b lf rt) (k delRoot (Cel y b lf rt))) → (k (Cel y b lf rt))
12.6.9Equations for Deletion
With equations for shrinking the spine and deleting the root in place, we are now in a position to discuss the equations for deleting a key from a search tree. The key to be deleted will be located in the root of a subtree of the tree from which the deletion is to occur. Part of the process is to locate that subtree. Since we are assuming the tree is ordered, these parts of the equations will be similar to the equations for retrieval from search trees. Once we have located the key at the root of a subtree, we apply delRoot to delete it and use the tree it delivers in place of the subtree where the key was located. Of course, the height of this tree may now be one less than it was before, and that might unbalance the tree. If so, we apply a rotation to rebalance it.
We have been assuming that the key to be deleted occurs somewhere in the tree. If it doesn’t, of course it can’t be deleted. In that case, we simply deliver the tree we started with. Other alternatives for this case might be appropriate for certain applications, but we have chosen this one because it makes the theorems straightforward.
The following equations specify the relationships among search trees, keys, and the results of deleting a key from a tree, following the outline we have been discussing informally.
Nub ˆ- |
k = Nub |
{ˆ- N} |
(Cel z |
d lf rt) ˆ- k = |
{ˆ- C} |
if |
k = z then delRoot (Cel x a lf rt) |
|
else if k < z && (height rt) > (height newL) + 1 then rotL (Cel z d newL rt)
else (Cel z d newL rt)
else if (height lf) > (height newR) + 1 then rotR (Cel z d lf newR) else (Cel z d lf newR)
where
newL = lf ˆ- k newR =rt ˆ- k
These equations imply that deletion satisfies order and balance preservation laws and key conservation laws similar to those of insertion. The following theorems, which are provable using tree induction, express these laws.
Theorem 103. (deletion preserves order).
s. k. ordered (s) → ordered (s ˆ- k)
12.6. BALANCED TREES |
351 |
Theorem 104. (deletion preserves balance).
s. k. balanced (s) → balanced (s ˆ- k)
Theorem 105. (insertion conserves keys).
s. k. ordered (s) → (k (s ˆ- k))
s. k. x. (ordered (s) |
|
(x = k) |
(x s)) → (x (s ˆ- k)) |
s. k. x. (ordered (s) |
|
(x s)) |
→ (x (s ˆ- k)) |
12.6.10Deletion in Logarithmic Time
The theorems about retrieval (Theorem 83), insertion (Theorems 93-95), and deletion (Theorems 103-105) confirm the properties that are important for confirming the correctness of retrieval, insertion, and deletion operations. The theorems are consequences of the assumption that these operations satisfy the equations labeled {getItem N }, {getItem C}, {ˆ: N }, {ˆ: C}, {ˆ- N }, and {ˆ- C}. No other assumptions are needed.
The number of computational steps required to delete a key and its associated data item from a search tree depends on how the equations are interpreted. We assume that the interpretation follows the equational model of computation.
In this case, at each level in a tree from which a key is being deleted, from the root to the point where the deletion occurs, the computation will require at least the construction of a tree by a Cel constructor, and possibly also a rotation. Each of these operations requires a fixed number of steps, independent of the size of the tree. Because the height is the maximum number of levels in the tree, the total number of steps required for this part of the deletion operation is proportional, at worst, to the height of the tree.
In the subtree where the deletion actually takes place, the key to be deleted occurs in the root, and the delRoot operation is applied. How many computational steps will this require? It will require a Cel construction at the root level, and possibly a rotation. It also calls for shrinking the spine.
The rotation and Cel construction, together, are a fixed amount of computation, regardless of the size of the tree. The shrinking operation, on the other hand, requires examining each level of the tree down the spine and performing a Cel construction and possibly a rotation at each of those levels. Nevertheless, the amount of computation at each level is independent of the size of the tree, so the total number of steps will be proportional to the height of the tree to which the delRoot operation is being applied. Since this height is no more than the height of the tree, the total number of computational steps in a deletion operation is proportional to the height of the tree.
This argument about the number of computational steps in deletion has been an informal one, but it can be made precise through tree induction, along the same lines as our proof that the number of computational steps in retrieval and in insertion is proportional to tree height.
352 |
CHAPTER 12. THE AVL TREE MIRACLE |
As with insertion, the height operation will slow down the deletion process significantly unless the height is imbedded in the tree structure and updated incrementally. This means that everywhere in the equations for deletion that a Cel construction occurs, the value of the height must be recorded. Rewriting the equations to take care of this detail is straightforward. The following version of the equation {ˆ- C} show how it can be done in one case. To complete the process, the equations for deletion at the root and for shrinking the spine would have to be similarly rewritten.
(Cel z d h lf rt) ˆ- k =
if k = z then delRoot (Cel h x a lf rt)
else if k < z && (height rt) > (height newL) + 1
then rotL (Cel ((max (height newL) (height rt)) + 1) z d newL rt)
else (Cel ((max (height newL) (height rt)) + 1) z d newL rt)
else if (height lf) > (height newR) + 1
then rotR (Cel ((max (height lf) (height newR)) + 1) z d lf newR)
else (Cel z ((max (height lf) (height newR)) + 1) d lf newR)
where
newL = lf ˆ- k newR = rt ˆ- k
12.7Things We Didn’t Tell You
It is not di cult to prove, using tree induction, that the height of a search tree s is the next integer larger than the base-2 logarithm of the number of keys that occur in the search tree if you assume that all of the subtrees of s have about the same number of nodes in their left subtree as in their right. This is a strict kind of balancing, but it is not the kind of balancing we have used in writing equations for insertion and deletion.
The kind of balance we have used has to do with the heights of subtrees, not the number of keys they contain. It is not so easy to verify that the height of a search tree that is balanced in the sense of the equations {bal N } and {bal C} is proportional to the logarithm of the number of keys in the tree. It turns out that it is (in fact, the height of such a tree is at most 44% more than the base-2 logarithm of the number of keys that occur in the search tree), but we haven’t proved it. It wouldn’t be hard for you to track down a proof of this fact if you wanted to, but this is one of the details we are going to skip in our discussion.
A more troubling point is that we have left out the proofs of many theorems, and we haven’t been entirely formal in most of the proofs we have taken the
12.7. THINGS WE DIDN’T TELL YOU |
353 |
trouble to write down. Our proofs are probably right in broad outline, but it is certain that if we tried to carry them down to the last detail, we would make mistakes. People simply are not attentive enough and patient enough to work out, correctly, all the details in the thousands of logic formulas that would be required to complete these proofs in an entirely formal manner.
For such an undertaking, a mechanized logic is required. That is, running a computer program that verifies that our proofs are correct in every detail is the only practical way to be sure. We don’t know of such a program for Haskell programs, but practical tools of this kind do exist for other notations based on the same computation model. For example, ACL2 [1] is a mechanized logic based in a programming language called Common Lisp.
ACL2 is able not only to verify that a proof is correct, but is actually able to construct many inductive proofs on its own. The strategy one would use, if the goal were to make sure a set equations implies a certain property of the operators involved, would be to use a tool like ACL2 to derive a proof of the property by stating a sequence of simple theorems that lead to the proof. The proof of each of the simple theorems would cite, somewhere along the line, one or more of the previous theorems in the sequence.
The tool would prove the simple theorems automatically, and the result would be a fully verified proof. The hard part is stating just the right sequence of theorems simple enough for the tool to prove automatically. This requires the same kinds of skills as constructing proofs at the level of detail we have been using in our discussion of AVL trees.
Proof assistants such as ACL2 have become, over the past few years, after decades of e ort by their developers, practical to use as a regular part of the software development process. By attempting to do proofs of properties of software without the use of such tools, students learn how to state relevant theorems and how to build a proof of a theorem through a sequence of simpler theorems. In other words, students learn what they need to know to make use of proof assistants in the practice of software development.
It is our hope that by now you have gained enough experience with this sort of proof to enable you to make use of well-established proof assistants. We also hope that you will be motivated to do so by a certain level of confidence, born of your experience in working through problems, that the phrase “defect-free software” is not an oxymoron.
Chapter 13
Discrete Mathematics in
Circuit Design
The techniques of discrete mathematics that you have been studying in this book are used throughout computer science. We have seen many small examples of the application of mathematics to computing, and we have also used programming to help with the mathematics. The previous chapter has examined a sophisticated application, the design of AVL trees.
This chapter looks at another application of formal reasoning and a mathematical approach to computer science: the use of discrete mathematics to help with the process of designing digital circuits. Hardware design is not the real subject here, so we will explain just enough about hardware to explain the particular circuits involved in the discussion.
In addition to applying discrete mathematics, we will use Haskell to specify and simulate circuits. The combination of discrete mathematics and Haskell makes it possible to carry out several useful tasks: precise specification of circuits, simulation, correctness proofs, and circuit derivations.
Digital circuit design is a vast subject area, and there is not space here to cover all of it. Therefore we will consider only one class of digital circuits (combinational circuits, which don’t contain flip flops). However, that restriction is made only to keep the chapter short; discrete mathematics is used heavily throughout the entire subjects of digital circuit design and computer architecture.
You do not need to have any prior knowledge about hardware in order to read this chapter; everything you need to know is covered here. We will begin by defining the basic hardware components, Boolean logic gates, and then will look at how to specify and simulate simple circuits using Haskell. Then we apply the methods of Propositional Logic to circuit design, including reasoning with truth tables and algebraic reasoning about circuits.
One of the principal problems in designing circuits is ensuring that they
355

356 CHAPTER 13. DISCRETE MATHEMATICS IN CIRCUIT DESIGN
a |
x |
Figure 13.1: Symbol for the Inverter
are correct. It is extremely expensive to debug circuit designs by building and testing them, so getting the design right in the first place is crucial. The last sections in this chapter address this issue: we use mathematics to state precisely what it means for an addition circuit to be correct. We use recursion and higher order functions to design an adder, and we use induction to prove that it works correctly.
13.1Boolean Logic Gates
Digital circuits are constructed with primitive circuits called logic gates. There are logic gates that implement the basic operations of propositional logic, ,, ¬, as well as a few other similar operations.
The simplest logic gate is the inverter , which implements the logical not (¬) operation. It takes one input and produces one output; Figure 13.1 shows the standard symbol for the inverter with input a and output x. Instead of using the ¬ symbol to specify an inverter, we will use the name inv ; thus inv a means the output of an inverter whose input is a. The inverter’s truth table is identical to the truth table for the logical not (¬) operator. Throughout this chapter, we will frequently use 0 and 1 as shorthand notations for False and True. This convention is frequently used in hardware design.
a |
inv a |
0 |
1 |
1 |
0 |
Some of the most commonly used logic gates take two inputs. The logical
operation is performed by the and2 gate, whose symbol is shown in Figure
13.2.This gate is named and2 because it takes two inputs; there are similar gates and3, and4 that take 3 and 4 inputs, respectively. The inclusive logical or operation is produced by the or2 gate (Figure 13.3), and the exclusive or operation, which produces 1 if either argument is 1 but not both, is provided by the xor gate (Figure 13.4). The following table defines the outputs of these logic gates.

13.2. FUNCTIONAL CIRCUIT SPECIFICATION |
357 |
a |
x |
|
b |
||
|
Figure 13.2: And Gate
a
x
b
Figure 13.3: Or Gate
a |
b |
and2 a b |
or2 a b |
xor a b |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
|
|
|
|
|
13.2Functional Circuit Specification
Since a digital circuit produces outputs that depend on its inputs, a circuit can be modelled by a mathematical function. Furthermore, we can implement such functions directly with Haskell functions. This provides several valuable benefits, including error checking, powerful specification techniques, and tools for circuit simulation.
A circuit can be specified in two ways: using a Haskell function definition, or using a schematic diagram. Most of the time we will use both forms, and later in the chapter you will see some of the advantages of each kind of specification. Meanwhile, we will look at how to write functional circuit specifications and what the corresponding schematic diagrams look like.
A circuit’s function is applied to its inputs, and the result is the output. For example, to specify that a and b should be connected to the inputs of an
a
x
b
Figure 13.4: Exclusive Or Gate