Counter Machines
A counter machine may be thought of in one of two ways:
The counter machine has the same structure as the multistack machine (Fig. 8.20), but in place of each stack is a counter. Counters hold any nonnegative integer, but we can only distinguish between zero and nonzero counters. That is, the move of the counter machine depends on its state, input symbol, and which, if any, of the counters are zero. In one move, the counter machine can:
Change state.
Add or subtract 1 from any of its counters, independently. However, a counter is not allowed to become negative, so it cannot subtract 1 from a counter that is currently 0.
A counter machine may also be regarded as a restricted multistack ma¬chine. The restrictions are as follows:
There are only two stack symbols, which we shall refer to as ZQ (the bottom-of-stack marker), and A'.
ZQ is initially on each stack.
We may replace ZQ only by a string of the form X'Z0, for some i > 0.
We may replace X only by X' for some i > 0. That is, ZQ appears only on the bottom of each stack, and all other stack symbols, if any, are X.
Wre shall use definition (1) for counter machines, but the two definitions clearly define machines of equivalent power. The reason is that stack XlZ0 can be identified with the count i. In definition (2), we can tell count 0 from other counts, because for count 0 we see ZQ on top of the stack, and otherwise we see X. However, we cannot distinguish two positive counts, since both have X on top of the stack.
The Power of Counter Machines
There are a few observations about the languages accepted by counter machines that are obvious but worth stating:
Every language accepted by a counter machine is recursively enumerable. The reason is that a counter machine is a special case of a stack machine, and a stack machine is a special case of a multitape Turing machine, which accepts only recursively enumerable languages by Theorem 8.9.
Every language accepted by a one-counter machine is a CFL. Note that a counter, in point-of-view (2), is a stack, so a one-counter machine is a special case of a one-stack machine, i.e., a PDA. In fact, the languages of one-counter machines are accepted by deterministic PDA’s, although the proof is surprisingly complex. The difficulty in the proof stems from the fact that the multistack and counter machines have an endmarker $ at the end of their input. A nondeterministic PDA can guess that it has seen the last input symbol and is about to see the $; thus it is clear that a nondeterministic PDA without the endmarker can simulate a DPDA with the endmarker. However, the hard proof, which we shall not attack, is to show that a DPDA without the endmarker can simulate a DPDA with the endmarker.
The surprising result about counter machines is that two counters are enough to simulate a Turing machine and therefore to accept every recursively enumerable language. It is this result we address now, first showing that three counters are enough, and then simulating three counters by two counters.
Theorem 8.14: Every recursively enumerable language is accepted by a three- counter machine.
PROOF: Begin with Theorem 8.13, which says that every recursively enumer¬able language is accepted by a two-stack machine. We then need to show how to simulate a stack with counters. Suppose there are r — 1 tape symbols used by the stack machine. We may identify the symbols with the digits 1 through r — 1, and think of a stack X X2 ■ • • Xn as an integer in base r. That is, this stack (whose top is at the left end, as usual) is represented by the integer Xnrn~l + Xn-jr"" + • ■ • + Jfjr + Xj.
We use two counters to hold the integers that represent each of the two stacks. The third counter is used to adjust the other two counters. In particular, we need the third counter when we either divide or multiply a count by r.
The operations on a stack can be broken into three kinds: pop the top symbol, change the top symbol, and push a symbol onto the stack. A move of the two-stack machine may involve several of these operations; in particular, replacing the top stack symbol X by a string of symbols must be broken down into replacing X and then pushing additional symbols onto the stack. We perform these operations on a stack that is represented by a count i, as follows.
Note that it is possible to use the finite control of the multistack machine to do each of the operations that requires counting up to r or less.
To pop the stack, we must replace i by i/r, throwing away any remainder, which is X. Starting with the third counter at 0, we repeatedly reduce the count i by r, and increase the third counter by 1. When the counter that originally held i reaches 0, we stop. Then, we repeatedly increase the original counter by 1 and decrease the third counter by 1, until the third counter becomes 0 again. At this time, the counter that used to hold i holds i/r.
To change X to Y on the top of a stack that is represented by count i, we increment or decrement i by a small amount, surely no more than r. If Y > X, as digits, increment i by Y — X; if Y < X then decrement i by X-Y.
To push X onto a stack that initially holds i, we need to replace i by ir + X. We first multiply by r. To do so, repeatedly decrement the count
by 1 and increase the third counter (which starts from 0, as always), by r. When the original counter becomes 0, we have ir on the third counter. Copy the third counter to the original counter and make the third counter
again, as we did in item (1). Finally, we increment the original counter by X.
To complete the construction, we must initialize the counters to simulate the stacks in their initial condition: holding only the start symbol of the two-stack machine. This step is accomplished by incrementing the two counters involved to some small integer, whichever integer from 1 to r - 1 corresponds to the start symbol. □
Theorem 8.15: Every recursively enumerable language is accepted by a two- counter machine.
PROOF: With the previous theorem, we only have to show how to simulate three counters with two counters. The idea is to represent the three counters, say i, j, and k, by a single integer. The integer we choose is m = 2‘3i5fc. One counter will hold this number, while the other is used to help multiply or divide m by one of the first three primes: 2, 3, and 5. To simulate the three-counter machine, we need to perform the following operations:
Increment i, j, and/or k. To increment i by 1, we multiply m by 2. We already saw in the proof of Theorem 8.14 how to multiply a count by any constant r, using a second counter. Likewise, we increment j by multiplying m by 3, and we increment k by multiplying to by 5.
Choice of Constants in the 3-to-2 Counter Construction
Notice how important it is in the proof of Theorem 8.15 2, 3, and 5 are distinct primes. If we had chosen, say m = 2*3J4*, then m = 12 could represent either i = 0, j = 1, and fc — 1, or it could represent i = 2, j = 1, and k = 0. Thus, we could not tell whether i or k was 0, and thus could not simulate the 3-counter machine reliably.
m an even or odd number of times. If we have decremented m an odd number of times when it becomes 0, then i = 0. We then restore m by copying the second counter to the first. Similarly, we test if j = 0 by determining whether m is divisible by 3, and we test if k — 0 by determining whether m is divisible by 5.
Decrement i, j, and/or k. To do so, we divide m by 2, 3, or 5, respec¬tively. The proof of Theorem 8.14 tells us how to perform the division by any constant, using an extra counter. Since the 3-counter machine cannot decrease a count below 0, it is an error, and the simulating 2-counter ma¬chine halts without accepting, if m is not evenly divisible by the constant by which we are dividing
đang được dịch, vui lòng đợi..