I want to ask a couple questions about the following proof. The proof originally came from a textbook and then a question on stackoverflow below.
How does this proof, that the halting problem is undecidable, work?
Question 1:
Does the proof below essentially make H a simulator for its input machine?
In other words, is there an important difference between saying H = M and the following description from the proof?
H([M,w]) = {accept if M accepts w}
= {reject if M does not accept w.}
Question 2:
How is my following comments correct or incorrect?
I thought the halting problem was the problem of deciding if a given machine will halt regardless of its output(accept/reject). If a solution exists for a halting problem, it has to be something that analyses source code like a compiler/decompiler/disassembler instead of actually running it. If it needed to run it, obviously it would never determine on a "no" answer.
Noticing that apparent problem in the proof, the whole proof seems not to show undecidability of the halting problem.
The proof instead seems to show this:
The following algorithm will not halt:
boolean D()
{
return not D();
}
Following is the proof in question retyped from Intro to the Theory of Computation by Sipser.
THE HALTING PROBLEM IS UNDECIDABLE
Now we are ready to prove Theorem 4.11, the undecidability of the language
ATM = {[M,w] | M is a TM and M accepts w}.
PROOF:
We assume that ATM is decidable and obtain a contradiction. Suppose that H is a decider for ATM. On input , where M is a TM and w is a string, H halts and accepts if M accepts w. Furthermore, H halts and rejects if M fails to accept w. In other words, we assume that H is a TM, where
H([M,w]) = {accept if M accepts w}
= {reject if M does not accept w.}
Now we construct a new Turing machine D with H as a subroutine. This new TM calls H to determine what M does when the input to M is its own description . Once D has determined this information, it does the opposite. That is, it rejects if M accepts and accepts if M does not accept. The following is a description of D.
D = "On input [M], where M is a TM:
1. Run H on input [M, [M]].
2. Output the opposite of what H outputs; that is, if H accepts, reject and if H rejects, accept."
Don't be confused by the idea of running a machine on its own description! That is similar to running a program with itself as input, something that does occasionally occer in practice. For example, a compiler is a program that translates other programs. A compiler for the language Pascal may itself be written in Pascal, so running that program on itself would make sense. In summary,
D([M]) = { accept if M does not accept [M]
= { reject if M accepts [M]
What happens when we run D with its own description as input> In that case we get:
D([D]) = {accept if D does not accept [D]
= {reject if D accepts [D]
No matter what D does, it is forces to do the opposite, which is obviously a contradiction. Thus neither TM D nor TM H can exist.
In other words, is there an important difference between saying H = M and the following description from the proof?
The H machine is called Universal Turing Machine (UTM) and is able to simulate any other Turing Machine, including itself.
If M is an Universal Turing Machine like H, it is ok to say H = M, otherwise this would be weird.
I thought the halting problem was the problem of deciding if a given machine will halt regardless of its output(accept/reject). If a solution exists for a halting problem, it has to be something that analyses source code like a compiler/decompiler/disassembler instead of actually running it. If it needed to run it, obviously it would never determine on a "no" answer.
That is why the proof works based on contradiction and it is kind hard to understand.
Basically it assumes first that exists such a machine that answers "yes" or "no" to any given input. [Hypothesis]
Let's call this machine Q.
Assuming Q is valid and it is an UTM, it can simulate another machine S that works following the steps below:
S reads an input (a program and its input)
S duplicates the input it just read
S calls Q passing the copied input
S waits for Q to answer (and based on our hypothesis it always will)
Let's imagine now the input Q(S, S). Q will receive the program S and the argument of S is S itself. This input will make S call Q indefinitely and will never stop.
Since Q and S were legal programs but there is a kind of input that makes Q never stop, Q is a machine impossible to built and therefore it is impossible to decide if a program S stops or not.
Therefore we have the proof that the halting problem is undecidable.
Sipser explains it well. Read it again now and see if you catch the idea :)
Now, on to your question again. The Turing Machine is our most powerful machine for representing problems. As a recognition machine, it has to go through the input and run the algorithm to determine if it is valid or not. It is impossible to know the output of an algorithm without running it.
The compiler is just a translator of syntax and little semantics. It cannot foresee how one will use the program and what the output will be.
Related
I came across a statement postulating that it's undecidable whether a TM overwrites any of its own input.
What would be intuition as well as an actual proof for that?
PROOF:
Build a machine B that takes as input a machine A, and simulates it without disturbing the input string (the string describing A). This is not difficult.
Now build machine C, a modified version of B. If A ever halts, C will overwrite the input string; until then it will leave the input string untouched.
In order to decide whether C (acting on A) ever overwrites its input string, one must decide whether A ever halts. But "does A halt" is undecidable, therefore so is "does C overwrite its input".
INTUITION:
With Turing machines, almost anything that isn't easy is impossible.
I'm having trouble proving a particular language is non-regular. The language is defined as
La = { wz: w,z ∈ {0,1}* and |w| > |z|}
I don't know how to approach this one. No matter what string I choose, I always run into the issue where w and z are moving targets for me; I haven't been able to create a string which couldn't be pumped or otherwise contradicted. Any thoughts on the right direction for this one?
This problem was part of a homework set, and apparently this question wasn't worded properly and is in fact regular.
I have been coding in MATLAB and I managed to convert my work to a single function, however it has several inputs and outputs. For the sake of simplicity, lets say it receives three inputs: X (vectorial and for reading only), Y (vectorial and for reading and writing) and Z (scalar and for writing only). Thanks to the reply here I was able to understand that I must create variables with special MATLAB types in order to pre-allocate space and then send them as parameters in my function in the C code.
An initial version with a single scalar output (Z) worked as expected, however taking the next step towards having multiple outputs has raised some questions. I'll try to be as concise as possible. Here's the header of my function in MATLAB and C code once I change Z to a vector:
[Y,Z]=foo(X,Y)
void foo(const unsigned int *X, float Y[n_Y], float Z[n_Z])
These are my doubts so far.
1 - I would expect that if Z is only created inside, it should not appear as an input for the C function. What should I do with it in order to obtain it outside the function? My idea would be to provide a fake variable with the same name that would later be overwritten.
2 - If Y is being changed, then the function should receive the pointer to Y. Is it being updated this way, as it should?
3 - Right now the dimensions are set for X as (1x:inf), which causes the pointer to show up. If I change to a smaller and realistic bound, that single input transforms into two, although nothing else changed (the variable creation in C is independent). Now there is const unsigned int X_data[], const int X_size[2] instead of just const unsigned int X. How should I deal with it within the C code?
The call to the function in C is being made as follows:
emxArray_uint32_T *X=emxCreate_uint32_T(1,n_X);
static emxArray_uint32_T *Y=emxCreate_real32_T(1,n_Y), *Z=emxCreate_real32_T(1,n_Z);
foo(X,&Y,&Z);
emxDestroyArray_uint32_T(X);
I should say that I have not tried to compile the lastest steps, since I need a specific environment to do so (laboratory). However, when I have access to it, the code needs to be almost ready to go. Also, without solving these doubts I think I shouldn't anyway. If it works somehow and I don't understand why, then it's the same as not working.
Here the subscripts of the array are out of range.
int a[10], i;
for (i = 1; i <= 10; i++)
a[i] = 0;
printf("India");
The output is an infinte loop and printf statment doesnt get executed. here's what written in K N King:
When i reaches 10. the program stores 0 into a [10] . But a [10] doesn’t exist.
so 0 goes into memory immediately after a [9] . If the variable i happens to follow
a [9] in memory—as might be the case—then i will be reset to O. causing the
loop to start over.
Can anyone explain it ?
The idea that this will form an infinite loop is based on an assumption about how the variables will be laid out in memory. In particular, it assumes that because i is defined immediately after a, that it will also be allocated in memory immediately after a.
That's certainly not guaranteed, but it equally certainly could happen. If it does, then the write to a[10] may actually overwrite i. Since it's writing 0 into the nonexistent a[10], doing so actually writes 0 into i. Then when the condition in the loop checks that i <= 10, that's true, so the loop continues -- and each time i gets to 10, it's immediately overwritten with 0 before the loop condition is evaluated, so the loop re-starts from the beginning.
As far as either the C or C++ standard cares, it's just undefined behavior--when the code writes past the end of the array anything can happen. It might do what somebody expects, or might might do something entirely different and unrelated that doesn't seem to make sense at all. The compiler is free to emit code that does pretty much anything in such a circumstance (or it could, for example, diagnose it as an error, and emit no code at all).
To give some idea of what conforming behavior could be: early versions of gcc had code to detect a specific case of implementation-defined behavior (pretty much like undefined behavior, except the implementation has to document what it does). In this case, the documented behavior was fairly complex. The compiler would attempt to do each of the following in order (and stop at the first one that succeeded):
run nethack (a game)
run rogue (another game)
start emacs, and have it execute a towers of hanoi simulation
print out "You are in a maze of twisty little passages, all alike".
I could be getting the order of those a bit wrong (this was a long time ago), but you get the idea. The result had nothing to do with anything a reasonable person would be likely to expect.
I have one small question about the pumping lemma for regular languages - is it good enough to show that if a specific string belonging to a language L can't be pumped, then the language is irregular? For example - if I choose language L1 being of the form a^nb^n (ab, aabb, aaabbb ...) and I show that the string aabb can't be pumped and still be a part of L1, then is it valid for me to immediately conclude that L1 is irregular?
Cheers.
Yes, that's how the pumping lemma works. It's only useful for proving languages to not be regular. Satisfying the pumping lemma is only a necessary but not a sufficient condition for a language being regular.
(Nota bene: Likewise for context-free languages and the respective pumping lemma there)
It's not quite sufficient to demonstrate that a single, finite-length string does not pump. For a rigorous argument, you'd also have to prove that length of your example string is greater than the pumping length of the language. Usually you assume that some pumping length
p exists, then construct a string longer than p that cannot be pumped.
Yes, this is how it works, but be careful in showing how a string "cannot be pumped"
To do that you have to break a string w in L, into substrings xyz and show that some versions of xy^1z, for int i i>=0 lead to strings not in L, but are still accepted by DFA M (for M built to accept L), arriving at a contradiction.
Note that you cannot pick the location of y and therefore must consider 3 possible positions of it. That's the key, in my opinion.
The pumping lemma says:
If a language A is regular => there is a number p (pumping length) where, if s is any string in L such that |s| >= p, then s may be divided into three pieces s=xyz, satisfying the following condition:
xyiz is in L for each i>=0
|y|>=0
p>=|xy|
The right way to show that a certain language L is not regular is to suppose L regular and try to reach a contradiction.
Lets try to demonstrate that L = {0n1n}|n>=0} is not regular.
We start assuming to the contrary that L is regular.
You can think about this kind of demonstration as a game:
Challenger: He choose the pumping length p. You cannot do any presumption on it.
You: Now it is your turn: choose the "kind" of string that represents the irregularity of the language.
Lets say that the string is in the form 0p1p.
A good tip in this step is to try to limit the adversary next move.
Challenger: He presents to you a string s in the form 0p1p.
You: It's time to pump! If you chose correctly the form of the string in your previous move, you can do some assumption. In our case, for example, we know that the substring y consists only of 0s (at least one 0 because |y|>0), because |xy|<=p and first p-elements are 0s.
Now we show that it exists i>=0 such that xyiz is not in L. For example, for i=2 the string xyyz has more 0s than 1s and so is not a member of L. This case is a contradiction. => L is not regular.
Never forget to demonstrate why the pumped string cannot be a member of L.
Probably it is late to help you, but someone else may need this kind of explanation...maybe ^^
Cheers.