Each turing machine can be specified by the five elements:
Many interesting properties of Turing Machines have been proven so far. For example, that each Turing Machine can be transformed into a Turing Machine with a tape that is cut-off on one side, and endless on the other side. Or that every Turing machine can be transformed into a Turing machine with set of symbols contains just two symbols.
A language is said to be 'Turing-complete', if for each functions that can be calculated with a Turing Machine, it can be shown that there is a program in this language that performs the same function. There are basically three approaches to proof that a language is Turing-complete. These are:
To achieve this, the set of symbols A is mapped onto the numbers 0 to n-1, where n equals the number of symbols, such that ainit maps onto zero. Likewise, the set of states S is mapped onto the numbers 0 to m-1, where m equals the number of states, such that sinit maps onto zero, and sfinal maps onto 1.
To simulate a Turing machine, we need an array with unlimited length to represent the tape, a index into that array representing the position of the head, and the current state. To program this in BF we will need to assign these to some memory locations. The position of the head will be stored in memory location head, and the current state in the memory location state. Of course, some additional temporary memory locations will be needed. Using the terminology introduced on the page An introduction to programming in BF, we need a program of the following form:
(some code which given the state and the cursymbol
assigns a value to newstate, newsymbol, and newhead)
setarray(base,1,0,newhead,newsymbol)
zero(cursymbol)
zero(newsymbol)
zero(head) move(newhead,head,t1)
zero(state) move(newstate,state,t1)
IsOne(state,t1)
Not(t1,t2,t3)
map(state,head,cursymbol, i,j,k,l,m, newstate,newhead,newsymbol)Where, given the tupple f(si,aj), k is the number representing the new state, l is the number representing the new symbol, and m is equal to 1 if the movement is right and equal to 0 if the movement is left. The procedure map is defined as follows:
map(state,head,cursymbol, astate,asymbol,anewstate,anewsymbol,movement, newstate,newhead,newsymbol) = copy(state,t1,t2) const(t2,astate) Equal(t1,t2,t3,t4,t5) if(t3) copy(cursymbol,t1,t2) const(t2,asymbol) Equal(t1,t2,t3,t4,t5) if(t3) const(newstate,anewstate) const(newsymbol,anewsymbol) copy(head,newhead,t1) ifelse(movement,t1) inc(newhead) else(movement,t1) dec(newhead) endelse(t1) endif(t3) endif(t3)In this code the expression const(v,c) stands for the piece of code that fills the memory location v with the value c. Now, we only need to assign real values to the variable representing the memory locations to arrive at the final BF program executing the given Turing machine. Note that memory location assigned to base should be larger than all other memory locations used.
this fully documented BF program written by Daniel B. Cristofani. |
A Universal Turing Machine (UTM) is a Turing machine that can simulate some Turing-complete computational model. By giving a BF program which simulates a particular UTM, we proof that BF is Turing-complete. The UTM that we implement here is taken from from Yurii Rogozhin's article Small universal Turing machines, in Theoretical Computer Science, November 1996 (volume 168 pgs 215-240). This UTM simulate a Turing-complete class of tag-systems. A tag-system transforms strings over an alphabet A = {a[1],a[2],...,a[n], a[n+1]} as follows: a positive integer m is chosen, and so is a function P that maps each a[i] for 1<=i<=n to a string P(a[i]) over the alphabet A. Now:
The input for this Turing machine is mildly complex, and there is no error checking.
It meets the criteria above; and when applied to the initial string a[2]a[1]a[1] it gives:
a[2]a[1]a[1] a[1]a[3]a[3]a[1] a[3]a[1]a[3]a[3]a[2]a[1]a[4] a[3]a[3]a[2]a[1]a[4]a[3]a[3] a[2]a[1]a[4]a[3]a[3]a[3]a[3] a[4]a[3]a[3]a[3]a[3]a[3]a[3]a[1] a[3]a[3]a[3]a[3]a[3]a[1]and then it's done.
Now, the encoding:
a[1] is 1 a[2] is 11111111111 a[3] is 11111111111111111 a[4] is 111111111111111111111 P(a[1]) is b1 b111111111111111111111b b1b b11111111111b b11111111111111111b b1111111111111111 P(a[2]) is b1 b1b b11111111111111111b b111111 P(a[3]) is b1 b11111111111111111b bthe initial string is 11111111111c1c1
and so the whole input is
b1 b11111111111111111b b b1 b1b b11111111111111111b b111111 b1 b111111111111111111111b b1b b11111111111b b11111111111111111b b1111111111111111 b 11111111111c1c1which when run together for input to the Turing machine becomes
b1b11111111111111111bbb1b1bb11111111111111111bb111111b1b111111111111111111111bb1bb11111111111bb11111111111111111bb1111111111111111b11111111111c1c1
11<L1 | 210R2 | 311R3 | 410R4 |
1b>R1 | 2b>L3 | 3b<R4 | 4bcL2 |
1>bL1 | 2><R2 | 3>bR3 | 4><R4 |
1<0R1 | 2<>L2 | 3< H | 4< |
10<L1 | 201L2 | 30cR1 | 40cL2 |
1c0R4 | 2cbR2 | 3c1R1 | 4cbR4 |
The minimal test case b1b1bbb1c1c11111 represents the tag-system where P(a[1]) = a[1]a[1] and P(a[2]) = STOP, applied to the string a[1]a[1]a[2]. This runs for 518 steps of the Turing machine, exercising all 23 Turing machine instructions, before halting with the output string a[1].
+++>++>>>+[>>,[>+++++<[[->]<<]<[>]>]>-[<<+++++>>-[<<---->>-[->]<]] <[<-<[<]+<+[>]<<+>->>>]<]<[<]>[-[>++++++<-]>[<+>-]+<<<+++>+> [- [<<+>->- [<<[-]>>- [<<++>+>- [<<-->->>+++<- [<<+>+>>--<- [<<->->- [<<++++>+>>+<- [>-<- [<<->->- [<<->>- [<<+++>>>-<- [<<---->>>++<- [<<++>>>+<- [>[-]<- [<<->>>+++<- [<<->>>--<- [<<++++>+>>+<- [<<[-]>->>++<- [<<+++++>+>>--<- [<->>++< [<<->>- ]]]]]]]]]]]]]]]]]]]]]]<[->>[<<+>>-]<<<[>>>+<<<-]<[>>>+<<<-]]>>] >[-[---[-<]]>]>[+++[<+++++>--]>]+<++[[>+++++<-]<]>>[-.>]
The Universal Register Machine is a machine with a fixed number of characters, and it supports the following commands:
(a2;s3)3.
(a4;a5;s2)2; ((a2;s4)4; s3; (a1;a4;s5)5; (a5;s1)1)3.
There is almost a one-to-one mapping from URM to BF. The an expression maps to +, the sn expression maps to -, and the (x)n expression maps to [x], of course preceded with the right amouth of > and < to move to the indicated memory location. The translation of the above two URM programs to BF are:
>>>[<+>-]<<<
>>[>>+>+<<<-]>[>[<<+>>-]<->>[<<<<+>>>+>-]<<<<[>>>>+<<<<-]>>]<<<
It is possible to make context free grammars for both BF and URM, such that matching parse trees indicate equivalent programs. Such a grammar is given below:
URM | BF |
root = S1. | root = S1 |
Sn = an | Sn = + |
Sn = sn | Sn = - |
Sn = Sn ; Sn | Sn = Sn Sn |
Sn = ( Sn )n | Sn = [ Sn ] |
Sn = Sn+1 | Sn = > Sn+1 < |
Sn = Sn-1 | Sn = < Sn-1 > |
Using this mapping, one can create another UTM in BF from the above mentioned UTM in URM.
Still, I feel that BF is far more elegant than URM, because it gives you more with less symbols. BF only needs the symbols "+", "-", "<", ">", "[", and "]", whereas URM needs "a", "s", ";", "(", ")", ".", and "0" to "9". Although, I have to admit, that BF programs will in general be longer than the equivalent URM program, and in most cases also more cryptic.