Sometimes we may wish to generate sets of values that have a natural recursive structure. For example, we may wish to enumerate
all sequences of length N of values from a certain set
all subsets of a given set
all permutations of a given set
all sets of positive integers that add up to a certain value
We can accomplish this using recursion; we call this a combinatorial search.
Note: we discussed this topic in the lecture, but did not have time to cover it fully or work through exercises on this topic. So this topic will not be on the Programming I exam this year. We will return to it and cover it fully in Programming II.
Instead of full lecture notes on this topic, I will only include a few examples that we covered in class.
Here is a procedure that prints all N-letter sequences that can formed from the characters {a, b, c}.
// write all N-letter sequences, preceding each with path procedure abc1(path: string; n: integer); var c: char; begin if n = 0 then writeln(path) else for c := 'a' to 'c' do abc1(path + c, n - 1); end; procedure abc(n: integer); begin abc1('', n); end;
Here are two procedures that print all subsets of the set { 1 .. N }. They show two different ways to perform this task.
// write all subsets of {1 .. N}, preceding each with path procedure subset1(path: string; n: integer); var i: integer; begin for i := n downto 1 do subset1(path + ' ' + intToStr(i), i - 1); writeln(path + ' }'); end; // alternate version procedure subset1(path: string; n: integer); begin if n = 0 then writeln(path, ' }') else begin subset1(path + ' ' + intToStr(n), n - 1); subset1(path, n - 1); end; end; procedure subset(n: integer); begin subset1('{', n,); end;
Here's a procedure to print all permutations of a string:
// delete a character from a string function delete(s: string; i: integer): string; begin exit(leftStr(s, i - 1) + rightStr(s, length(s) - i)); end; procedure permute1(path: string; s: string); var i: integer; begin if s = '' then writeln(path) else for i := 1 to length(s) do permute1(path + s[i], delete(s, i)); end; procedure permute(s: string); begin permute1('', s); end;
Arithmetic expressions are composed from numbers and
operators that act on those numbers. In this section we will
use the operators +
, -
, *
and
/
, the last of which denotes integer division.
In traditional mathematical notation, these operators are infix
operators: they are written
between the values that they
act on (which are called
operands). For
example, we write 2 +
2
, or 8
- 7
. In this last
expression, 8 and 7 are the operands.
Here are some arithmetic expressions written using infix notation:
((4 + 5) * (2 + 1))
((7 / 2)
-
1)
We may choose an alternative syntax that uses prefix
notation, in which we write
each operator before its operands. For example, we write +
2 4
to mean the sum
of 2 and 4, or / 8 2
to mean 8 divided by 2. Here are the above expressions rewritten in
prefix notation:
* + 4 5 + 2 1 - / 7 2 1
Or we may use postfix
notation, in which operators
come after both operands: we might write 4
5 +
to mean the sum
of 4 and 5. Here are the above expressions in postfix notation:
4 5 + 2 1 + * 7 2 / 1 -
In infix notation we must write
parentheses to distinguish expressions that would otherwise be
ambiguous. For example, 4
+
5
*
9
could mean either ((4
+ 5) * 9)
or (4
+ (5 * 9))
. (Another
way to disambiguate expressions is using operator
precedence. For example, *
is generally considered to have higher precedence than +
.
But in this discussion we will assume that no such precedence
exists.)
In prefix or postfix notation there is
no need either for parentheses or operator precedence, because
expressions are inherently unambiguous. For example, the prefix
expression * + 4 5 9
is equivalent to ((4 + 5) *
9)
, and the prefix expression +
4
*
5
9
is equivalent to (4 + (5 * 9))
. There is no
danger of confusing these in prefix form even without parentheses.
In the next lecture we will see how to write programs that read and evaluate expressions in these various notations.
The Tower of Hanoi is a well-known puzzle that looks like this:
The puzzle has 3 pegs and a number of disks of various sizes. The player may move disks from peg to peg, but a larger disk may never rest atop a smaller one. Traditionally all disks begin on the leftmost peg, and the goal is to move them to the rightmost.
Supposedly in a temple in the city of Hanoi there is a real-life version of this puzzle with 3 rods and 64 golden disks. The monks there move one disk each second from one rod to another. When they finally succeed in moving all the disks to their destination, the world will end.
The world has not yet ended, so we can write a program that solves a version of this puzzle with a smaller number of disks. We want our program to print output like this:
move disk 1 from 1 to 2 move disk 2 from 1 to 3 move disk 1 from 2 to 3 move disk 3 from 1 to 2 …
To solve this puzzle, the key insight is that a simple recursive algorithm will do the trick. To move a tower of disks 1 through N from peg A to peg B, we can do the following:
Move the tower of disks 1 through N-1 from A to C.
Move disk N from A to B.
Move the tower of disks 1 through N-1 from C to B.
The program below implements this algorithm:
procedure move(n: integer; fromPeg: integer; toPeg: integer); var other: integer; begin if n = 0 then exit; other := 6 - fromPeg - toPeg; move(n - 1, fromPeg, other); writeln('move disk ', n, ' from ', fromPeg, ' to ', toPeg); move(n - 1, other, toPeg); end; var n: integer; begin readln(n); move(n, 1, 3); end.
We can compute the exact number of moves required to solve the puzzle using the algorithm above. If M(n) is the number of moves to move a tower of height n, then we have the recurrence
M(n) = 2 ⋅ M(n–1) + 1
The solution to this recurrence is, exactly,
M(n) = 2n – 1
Similarly, the running time of our program above follows the recurrence
T(n) = 2 ⋅ T(n–1) + O(1)
And the program runs in time T(n) = O(2n).
It will take 264 - 1 seconds for the monks in Hanoi to move the golden disks to their destination tower. That is far more than the number of seconds that our universe has existed so far.
Here is a program that animates the Tower of Hanoi using text-mode (CRT) graphics.