Chúng ta bắt đầu với một lý lẽ không thân thiện, bằng cách sử dụng một kiến thức giả định của lập trình C, để thấy rằng có những vấn đề cụ thể mà chúng ta không thể giải quyết đựơc bằng
Trang 1Tiểu luận môn Lý thuyết tính toán
Giới thiệu máy Turing
Trong chương này chúng tôi thay đổi khá nhiều về phương pháp truyền đạt Cho đến nay, chúng tôi đã quan tâm đến các lớp ngôn ngữ đơn giản và phương pháp mà chúng có thể được sử dụng cho các vấn đề tương đối hạn chế, chẳng hạn như phân tích các giao thức, tìm kiếm văn bản, hoặc phân tích chương trình Bây giờ, chúng ta sẽ bắt đầu tìm kiếm các câu hỏi cho những ngôn ngữ có thể được định nghĩa bởi bất kỳ thiết
bị tính toán nào Câu hỏi này tương tự như câu hỏi về những gì máy tính có thể làm, kể
từ khi công nhận các chuỗi trong ngôn ngữ là phương pháp chính thức của bất kỳ vấn
đề cụ thể nào, và việc giải quyết một vấn đề là một thay thế hợp lý cho những gì mà máy tính làm
Chúng ta bắt đầu với một lý lẽ không thân thiện, bằng cách sử dụng một kiến thức giả định của lập trình C, để thấy rằng có những vấn đề cụ thể mà chúng ta không thể giải quyết đựơc bằng máy tính Những vấn đề này được gọi là "không giải được." Sau đây, chúng tôi giới thiệu một hình thức đáng nể cho máy tính, được gọi là máy Turing Trong khi một máy Turing trông không giống một máy tính, và không được hiệu quả vô cùng thì một số công ty khởi đầu vẫn quyết định sản xuất và bán chúng; trong một thời gian dài máy Turing được công nhận là mô hình chính xác so với bất kỳ thiết bị tính toán vật lý nào có khả năng làm việc
Trong chương 9, chúng tôi sử dụng máy Turing để phát triển một lý thuyết về bài toán "không giải đuợc", điều này có nghĩa là không có máy tính nào có thể giải đựơc các bài toán này Chúng tôi chỉ ra rằng một số bài toán có thể dễ dàng để biểu diễn là không giải được trên thực tế Một ví dụ đựơc đưa ra ở đây là khi đưa ra một văn phạm được cho là mơ hồ thì chúng ta sẽ thấy nhiều bài toán cần giải quyết
8.1 Những bài toán mà máy tính không thể giải quyết
Mục đích của phần này là cung cấp vô hình thức trong việc giới thiệu ngôn ngữ lập trình C cơ bản để kiểm chứng một bài toán cụ thể mà máy tính không thể giải quyết Các vấn đề cụ thể đựơc đưa ra thảo luận ở đây là tại sao từ đầu tiên mà chương trình C in ra là từ hello và world Mặc dù chúng ta có thể cho rằng việc mô phỏng của chương trình sẽ cho phép chúng ta chỉ ra chương trình đang làm gì, chúng ta phải xác thực nội dung với các chương trình và điều này phải mất một thời gian dài trước khi xuất bất kỳ thông tin nào ra Với bài toán này – không biết khi nào, bao giờ, việc gì đó
sẽ xảy ra – là nguyên nhân căn bản của sự bất lực của chúng tôi khi cho biết chương trình làm gì Tuy nhiên, vấn đề ở đây là không có chương trình nào có thể đảm nhiệm nhiệm vụ khá khó khăn này, và chúng ta cần phải phát triển một số máy hình thức Trong phần này, chúng tôi đưa ra những bằng chứng xác thực liên quan đến vấn dề này
Trang 28.1.1 Chương trình In "Hello, World"
Trong hình 8.1 là chương trình C đầu tiên các sinh viên gặp khi đọc cuốn sách Kernighan và Ritchie Chúng ta dễ dàng để nhận thấy rằng chương trình này in hello, world và kết thúc Chương trình này quá dễ hiểu để trở thành một bài thực hành thông dụng để giới thiệu các ngôn ngữ bằng cách làm thế nào để viết một chương trình in hello, world trong những ngôn ngữ đó
Main ()
{
Printf ("hello, world\ n")
}
Hình 8.1: Chương trình hello-world của Ritchie và Kernighan
Tuy nhiên, có những chương trình khác cũng in hello, world, nhưng thực tế là
những chương trình đó chưa được rõ ràng Hình 8.2 cho thấy chương trình khác có thể
in hello, world Phải nhập đầu vào n, và tìm số nguyên dương để giải phương trình
xn+yn=zn Nếu tìm thấy, in hello, world Nếu nó không bao giờ tìm thấy các số nguyên
x, y, z thỏa mãn phương trình, sau đó nó tiếp tục tìm kiếm mãi mãi, và không bao giờ
in hello, world.
Để hiểu những gì chương trình này làm, trước tiên phải tiến chú ý đến exp là hàm
hỗ trợ tính toán lũy thừa Chương trình chính cần phải tìm kiếm thông qua ba tham số (x, y, z) trong một bậc như vậy cuối cùng chúng ta chắc chắn nhận được mọi bộ ba số
nguyên dương Để tổ chức tìm kiếm, chúng ta sử dụng một biến thứ tư, total, bắt đầu tại 3 và trong vòng lặp while, được tăng lên một đơn vị tại một thời điểm, cuối cùng cũng đạt đến bất kỳ số nguyên hữu hạn nào đó Bên trong vòng lặp while, chúng ta phân chia total cho ba số nguyên dương x, y, z, bằng cách đầu tiên cho phép x nằm trong khoảng từ 1 đến total-2, và trong vòng lặp này có vòng lặp for sẽ cho phép y trong giới hạn từ 1 đến một số nhỏ hơn x mà không được lấy từ total Cái còn lại, phải giữa 1 và total-2, được đưa cho z.
Trong vòng lặp trong cùng, ba tham số (x, y, z) là để kiểm tra phương trình
xn+yn=zn Nếu bằng, chương trình in hello, world, và nếu không bằng, không in gì cả.
int exp(int i, n)
/*tính i mũ n */
{
int ans, j;
Trang 3for (j=1; j<=n; j++) ans *=i;
return(ans) }
main()
{
int n, total, x, y, z;
scanf(“%d”, &n);
total=3;
while (1) {
for (x=1; x<=total-2; x++)
z=total-x-y;
if (exp(x,n)+exp(y,n)==exp(z,n)) }
total++;
} }
Hình 8.2: Định lý Fermat cuối cùng được thể hiện như một chương trình hello-world
Nếu giá trị của n mà chương trình đọc là 2, thì cuối cùng chương trình sẽ tìm
thấy sự kết hợp các số nguyên ví dụ như total = 12, x = 3, y = 4 và z = 5, do xn+yn=zn
Như vậy, cho đầu vào là 2, chương trình in hello, world.
Tuy nhiên, đối với bất kỳ số nguyên n> 2, chương trình sẽ không bao giờ tìm
thấy một bộ ba các số nguyên dương để thoả mãn phương trình xn+yn=zn, và do đó sẽ
không in hello, world Thật thú vị, cho đến một vài năm trước đây, người ta không biết
chương trình này sẽ không in được hello, world đối với một số nguyên lớn n Những
tuyên bố cho rằng điều đó sẽ không xảy ra, tức là không có giải pháp số nguyên của phương trình: xn+yn=zn nếu n> 2, giải pháp này đã được Fermat thực hiện cách đây
300 năm, nhưng không chứng minh được cho đến gần đây Tuyên bố này thường được gọi là "định lý cuối cùng của Fermat" (phương trình toán học của Fermat với nghiệm
có giới hạn)
Trang 4Bài toán hello-world được định nghĩa như sau: hãy xác định có hay không một chương trình C mà khi cho đầu vào, in hello, word như 12 ký tự đầu tiên mà nó in ra
Trong những phần sau, chúng ta thường sử dụng, như một phương pháp tốc ký, việc
trình bày một chương trình in hello, world có nghĩa là in hello, word như 12 ký tự đầu
tiên mà nó in ra
Tại sao tồn tại bài toán không giải được
Trong khi thật khó để chứng minh đó là một bài toán đặc trưng, chẳng hạn như
"bài toán hello-world" đã phân tích ở đây, là không giải được, khá dễ dàng để hiểu tại sao hầu như tất cả các bài toán không giải được bởi bất kỳ hệ điều hành nào thì đều liên quan đến lập trình Cần nhớ rằng một "bài toán" là thành phần của một chuỗi trong một ngôn ngữ Số lượng các ngôn ngữ khác nhau có nhiều hơn môt ký tự trên các bảng chữ cái là không đếm được Điều này có nghĩa là không có cách nào để gán các số nguyên cho các ngôn ngữ vì mọi ngôn ngữ có một số nguyên, và mỗi số nguyên thì được gán cho một ngôn ngữ
Trên những chương trình khác, các chuỗi hữu hạn trong một bảng chữ cái hữu hạn (thường là một tập hợp con của bảng chữ cái ASCCII) là có thể đếm được Điều này có nghĩa, chúng ta có thể sắp xếp chúng theo chiều dài, và đối với các chương trình có chiều dài bằng nhau thì sắp xếp theo thứ tự từ điển Vì vậy, chúng ta có thể
viết đến chương trình đầu tiên, chương trình thứ hai, và đến chương trình thứ i cho bất
kỳ số nguyên i nào.
Kết quả, chúng ta biết số chương trình ít hơn số bài toán Nếu chúng ta chọn một ngôn ngữ một cách ngẫu nhiên, gần như chắc chắn nó sẽ là một bài toán không giải được Lý do duy nhất mà hầu hết các bài toán được xem là không giải được là chúng ta hiếm khi quan tâm đến các bài toán ngẫu nhiên Thay vào đó, chúng ta có xu hướng xem xét những bài toán khá đơn giản, có cấu trúc rõ ràng, và thực sự thường là giải được Tuy nhiên, ngay cả trong những bài toán chúng ta quan tâm và có thể có cách trình bày rõ ràng và ngắn gọn thì chúng ta cũng sẽ tìm thấy nhiều bài toán không giải
được, bài toán hello-world là một trường hợp trong số đó.
Dường như các nhà toán học phải mất 300 năm để giải quyết một câu hỏi duy nhất về một chương trình 22 dòng, sau đó vấn đề được quan tâm là chỉ ra có hay
không một chương trình, cho dữ liệu vào, in ra hello, word quả thật là điều khó khăn
Trong thực tế, bất kỳ những bài toán mà các nhà toán học đã không thể giải được đều được đưa về thành một câu hỏi "chương trình này, với đầu vào này, in được hello, world không?” Vì vậy, người ta sẽ rất chú ý nếu chúng ta có thể viết một chương trình
có thể kiểm tra bất kỳ chương trình P và đầu vào I cho chương trình P, và chỉ ra có hay
không có P, chạy với đầu vào I, sẽ in hello, world Chúng ta sẽ chứng minh rằng
không tồn tại một chương trình như vậy
Trang 58.1.2 Máy thử “Hello, word”
Chứng minh về việc không thể thực hiện các kiểm tra hello-world là cách
chứng minh bằng phương pháp phản chứng Đó là, chúng ta giả sử có một chương trình, gọi đó là H, được xem như đầu vào là một chương trình P và đầu vào I, và cho
dù có hay không P và đầu vào I thì vẫn in ra hello, world Hình 8.3 là một diễn tả của những gì H làm Thực tế, chỉ có đầu ra của H mới in ra ba ký tự là “Yes” hoặc in ra hai ký tự là “No” Đầu ra thường in ra hoặc làm một việc khác
Nếu một bài toán có thuật toán giống như chương trình H, mà thường nói một cách chính xác dù có hay không trường hợp bài toán có câu trả lời là "Yes" hoặc "No", thì sau đó bài toán được xem là giải được Nếu không thì bài toán không giải được
Mục tiêu của chúng ta là để chứng minh rằng chương trình H không tồn tại, tức là, các
vấn đề “hello –world” không thể giải quyết được
Để chứng minh sự trình bày bằng phản chứng, chúng ta sẽ tạo một vài thay đổi cho chương trình H, cuối cùng xây dựng một chương trình liên quan được gọi là chương trình “H2”, đây là một chương trình cho chúng ta thấy nó không tồn tại Vì việc thay đổi H là một biến đổi đơn giản do đó nó có thể được thực hiện bởi bất kỳ chương trình C nào, sự trình bày đáng ngờ duy nhất là sự tồn tại của chương trình H,
vì vậy đó là giả định mà chúng ta phủ nhận
Chúng ta sẽ thảo luận đơn giản hơn, chúng ta sẽ làm giả định về một chương trình C Những giả định làm cho chức năng của H dễ dàng hơn, không khó khăn, vì
vậy nếu chúng ta có thể hiển thị thử nghiệm "hello - world " cho các chương trình hạn
chế này không tồn tại, sau đó chắc chắn không có thử nghiệm nào có thể làm việc cho một lớp rộng lớn hơn của chương trình Giả định của chúng ta là:
1 Tất cả đầu ra đều dựa trên ký tự, chẳng hạn, chúng ta không sử dụng một gói phần mềm đồ họa hay cơ sở nào khác để tạo đầu ra không thuộc mẫu của ký tự
2 Tất cả đầu ra là ký tự cơ sở được thực hiện bằng cách sử dụng “printf”, chứ không phải là put-char () hoặc một hàm đầu ra dựa trên ký tự khác
Hình 8.3 Chương trình giả thiết H là một máy dò hello – word
Trang 6Bây giờ chúng ta giả định rằng chương trình H tồn tại Sửa đổi đầu tiên là thay
đổi đầu ra no, đó là câu trả lời mà H thực hiện khi nó cho đầu vào chương trình P không in được hello, word như đầu ra đầu tiên trong hồi đáp đầu vào I Ngay sau khi H
in "n," chúng ta biết cuối cùng nó sẽ đi kèm theo sau là "o" Vì vậy, chúng ta có thể
sửa đổi bất kỳ sự trình bày printf nào trong H để in ra "n" thay vào đó là in hello, word Một việc trình bày printf nữa đó là in "o" nhưng không phải là "n" được bỏ qua
Kết quả là, các chương trình mới, mà chúng ta gọi là H1, hoạt động như H, ngoại trừ
việc nó in hello, word một cách chính xác khi H in no H1 được đề xuất bởi hình 8.4
Chuyển đổi tiếp theo của chúng ta trên chương trình là một chút phức tạp hơn, đó
là cách nhìn cơ bản sâu sắc cho phép Alan Turing chứng minh kết quả không giải được của mình về máy Turing Từ khi chúng ta thực sự quan tâm đến các chương trình
mà làm cho các chương trình khác như là đầu vào và diễn đạt một số vấn đề về chúng, chúng ta sẽ giới hạn H1 vì vậy H1:
a) Giữ lại đầu vào P, không phải P và I
b) Yêu cầu P làm gì nếu đầu vào là mã riêng của nó, tức là, H1 sẽ làm gì trên dữ liệu vào P như chương trình và P như dữ liệu vào I thì tốt?
Việc thay đổi mà chúng ta phải thực hiện trên H1 để tạo ra chương trình H2 đề xuất
ở hình 8.5 như sau:
1 H2 đầu tiên đọc toàn bộ dữ liệu vào P và lưu trữ trong mảng A, đó là hàm
“malloc’s” cho ra kết quả
2 H2 có mô hình như H1, nhưng bất kỳ lúc nào H1 cũng đọc được dữ liệu vào từ P hoặc I, H2 đọc từ bản copy lưu trữ trong mảng A Để đánh dấu có bao nhiêu P và I thì
H1 đọc, H2 có thể duy trì hai con trỏ đánh dấu vị trí trong A
Hình 8.4 H1 hoạt động như H, nhưng máy nói Hello, Word thay vì No
Figure 8.5: Hi behaves like H\, but uses its input P as both P and I
Hình 8.5 H2 hoạt động như H1, nhưng sử dụng đầu vào P của nó như cả P và I
Yes
Hello, world P
Trang 7Ngay bây giờ chúng ta có thể sẵn sàng đi chứng minh H2 không tồn tại Vì vậy,
H1 không tồn tại, và tương tự, H không tồn tại Trọng tâm của vấn đề đó là hình dung
H2 làm gì khi xem chính bản thân nó như là dữ liệu vào Trường hợp này được đề xuất
ở hình 8.6 Gọi lại H2 đó, cho bất kỳ chương trình P nào như là đầu vào, H2 tạo được
đầu ra Yes nếu P in ra hello, word khi H2 tự cho chính bản thân nó như là đầu vào Ngoài ra, H2 in ra hello, word nếu P tự cho chính bản thân nó như đầu vào, không in ra hello, word như đầu ra đầu tiên của nó.
Giả sử H2 được miêu tả bằng hình 8.6 tạo đầu ra Yes Sau đó H2 ở trong ô cho rằng dữ liệu vào H2 của nó chính là H2, nó cho chính bản thân nó như là đầu vào, in ra hello, word như đầu ra đầu tiên của nó Tuy nhiên chúng ta chỉ giả sử rằng đầu ra H2
đầu tiên tạo ở vị trí này là Yes hơn là hello, word
Vì vậy, xuất hiện ở hình 8.6 đầu ra của ô là hello, word, vì nó là hello, word hoặc
là một cái khác Nhưng nếu H2 tự cho nó như đầu vào, in ra hello, word đầu tiên, thì đầu ra của ô trong hình 8.6 phải là Yes Chúng ta giả sử H 2 tạo ra bất cứ đầu ra nào, chúng ta có thể bàn luận việc nó tạo ra đầu ra khác.
Tình huống này là nghịch lý, và chúng ta quyết định rằng H2 không tồn tại Vì chúng ta đã trái với giả định là Htồn tại Tức là chúng ta đã chứng minh được rằng không có chương trình H nào cho dù có đưa ra hay không chương trình P với đầu vào
I, in ra hello, word như đầu ra đầu tiên của nó
8.1.3 Biến đổi một bài toán thành một bài toán khác
Bây giờ, chúng tôi có bài toán - một chương trình có chạy được với đầu vào được
cho, in hello, word như cái đầu tiên mà nó in ra hay không? - mà chúng ta biết không
có chương trình máy tính nào có thể giải quyết được Một vấn đề mà không thể được giải quyết bằng máy tính được gọi là không giải được Chúng tôi sẽ đưa ra định nghĩa
Figure 8.5: Hi behaves like H\, but uses its input P as both P and I
Hình 8.6 H2 làm gì khi tự cho chính bản thân nó như đầu vào?
Yes
Hello, world
H
2
Trang 8chính thức của "undecide" trong phần 9.3, nhưng cho đến thời điểm này, chúng ta hãy cùng sử dụng thuật ngữ chính thức Giả sử chúng ta muốn xác định có hay không một
số bài toán khác có thể được giải quyết bởi một máy tính Chúng tôi có thể thử viết chương trình để giải quyết nó, nhưng nếu chúng ta không thể tìm ra cách để làm như vậy, thì sau đó chúng ta có thể thử một bằng chứng cho thấy không có chương trình như vậy
Có lẽ chúng ta có thể chứng minh bài toán mới không giải quyết được này bằng một kỹ thuật tương tự như những gì chúng ta đã làm cho các bài toán hello-word: giả
sử có một chương trình để giải quyết nó và phát triển một chương trình nghịch lý mà chương trình này phải làm hai điều trái ngược, giống như chương trình H2 Tuy nhiên, một khi chúng ta có một bài toán mà chúng ta biết là không giải được, chúng ta không còn phải chứng minh sự tồn tại của một tình huống nghịch lý Điều đó quá đủ để cho thấy rằng nếu chúng ta có thể giải quyết bài toán mới, sau đó chúng ta có thể sử dụng giải pháp để giải quyết một bài toán mà chúng ta đã biết là không giải được Chiến lược này được đề xuất trong hình 8.7; kỹ thuật được gọi là biến đổi P1 thành P2
Giả sử chúng ta biết vấn đề P1 không giải được, và P2 là một bài toán mới mà chúng ta muốn chứng minh càng không giải được càng tốt Chúng ta giả sử rằng có một chương trình miêu tả trong hình 8.7 bằng nhãn hình thoi "decide" (quyết định), chương trình này in ra Yes hoặc No, tùy thuộc vào việc có hay không đầu vào của nó,
ví dụ như bài toán P2 có hoặc không có trong ngôn ngữ của bài toán đó
Để tạo ra một bằng chứng rằng bài toán P2 là không giải được, chúng ta phải phát minh ra một cấu trúc, đại diện bởi các ô vuông trong hình 8.7, có thể chuyển đổi trường hợp của P1 thành trường hợp của P2 có câu trả lời tương tự Đó là, bất kỳ chuỗi trong ngôn ngữ P1 được chuyển đổi sang một số chuỗi trong ngôn ngữ P2, và bất kỳ chuỗi nào trên bảng chữ cái của P1 mà không thuộc ngôn ngữ P1 đều được chuyển đổi thành một chuỗi thuộc ngôn ngữ P2 Một khi chúng ta có cấu trúc này, chúng ta có thể giải quyết P1 như sau:
1 Cho một ví dụ như P1, đó là, cho một chuỗi w có thể hoặc không thể thuộc ngôn ngữ P1, áp dụng các thuật toán xây dựng một chuỗi x
No
P
1
instance
Construct P2
instance
Yes decide
Hình 8.7 Nếu chúng ta có thể giải quyết bài toán P2, thì chúng ta có thể sử dụng lời giải của nó để giải quyết bài toán P1
Trang 92 Kiểm tra có x trong P2 không, và đưa ra câu trả lời tương tự cho w và P1
Nếu w thuộc P1, sau đó x thuộc P2, vì vậy thuật toán này nói rằng Yes Nếu w không thuộc P1, sau đó x không thuộc P2, và thuật toán nói No Dù bằng cách nào, nó cũng nói đúng về w Kể từ khi chúng ta giả định rằng không có thuật toán để quyết định các thành viên của một chuỗi trong P1 tồn tại, chúng ta có một bằng chứng bởi sự mâu thuẫn rằng thuật toán quyết định giả thuyết cho P2 không tồn tại, tức là, P2 không giải được
Ví dụ 8.1: cho phép chúng ta sử dụng phương pháp này để hiển thị câu hỏi "tạo chương trình Q, cho đầu vào y, gọi hàm foo" là không giải được Lưu ý rằng Q có thể không có một hàm foo, trong trường hợp bài toán là dễ, nhưng các trường hợp khó khăn là khi Q có một hàm foo nhưng có thể hoặc không thể đạt được một lời gọi đến foo với đầu vào y Từ đó chúng ta chỉ biết một bài toán không giải được, vai trò của P1
trong hình 8.7 sẽ được thể hiện bởi bài toán hello-word P2 sẽ đề cập đến bài toán gọi foo Chúng ta giả sử có một chương trình giải quyết bài toán gọi foo Công việc của
chúng ta là thiết kế một thuật toán chuyển đổi bài toán hello-word thành bài toán gọi
foo
Đó là, cho chương trình Q và đầu y của nó, chúng ta phải xây dựng một chương
trình R và một đầu vào z như R vậy, với đầu vào z, gọi foo nếu và chỉ nếu Q với đầu vào y in ra hello, word Việc xây dựng không phải là khó.
Một máy tính có thể thực sự làm tất cả những điều đó hay không?
Nếu chúng ta kiểm tra một chương trình như hình 8.2, chúng ta có thể đặt câu hỏi liệu nó có thực sự tìm kiếm phản ví dụ cho phương trình toán học của Fermat với nghiệm có giới hạn Cuối cùng, các số nguyên chỉ có độ dài 32 bit trong máy tính cổ điển, và nếu các phản ví dụ nhỏ nhất liên quan đến các số nguyên trong hàng tỉ số, sẽ
có một lỗi tràn bộ nhớ trước khi giải pháp được tìm thấy Trong thực tế, người ta có thể tranh luận rằng một máy tính với bộ nhớ chính128 MB và một đĩa 30 GB, "chỉ" có
25630128000000 trạng thái, và đó là một sự tự động hữu hạn
Tuy nhiên, việc nghiên cứu các máy tính như là những automat hữu hạn (hoặc nghiên cứu bộ não như là những automat hữu hạn, nó chính là nơi bắt nguồn ý tưởng của FA), là không hữu ích Số lượng của các trạng thái liên quan quá lớn, và giới hạn quá khó hiểu đến nỗi bạn không rút ra được bất kỳ kết luận hữu ích nào Trong thực tế,
có mọi lý do để tin rằng, nếu chúng ta muốn, chúng ta có thể mở rộng tập hợp các trạng thái của một máy tính một cách tùy ý
Ví dụ, chúng ta có thể miêu tả các số nguyên như danh sách liên kết của các chữ
số, chiều dài tùy ý Nếu chúng ta gỡ bỏ bộ nhớ ra, chương trình có thể in một yêu cầu cho người tháo dỡ đĩa của nó là lưu nó, và thay thế nó bằng một đĩa trống Thời gian trôi qua, các máy tính có thể in các yêu cầu để trao đổi càng nhiều giữa các đĩa thì
Trang 10càng đáp ứng nhu cầu của máy tính Chương trình này sẽ phức tạp hơn nhiều so với hình 8.2, nhưng không vượt quá khả năng của chúng ta để lập trình Thủ thuật tương
tự sẽ cho phép bất kỳ chương trình khác tránh giới hạn hữu hạn trên kích thước của bộ nhớ hay kích thước của số nguyên hoặc những mục tin dữ liệu khác
1 Nếu Q có một hàm được gọi là foo, đổi tên nó và gọi hàm đó Rõ ràng các
chương trình Q1 mới thực hiện chính xác cái mà Q thực hiện
2 Thêm vào Q1 một hàm foo Hàm này hiện không làm gì cả, và không được gọi
Chương trình kết quả là Q2
3 Chỉnh sửa Q2 để lưu12 ký tự đầu tiên mà nó in ra, lưu trữ chúng trong một mảng
A bao trùm toàn chương trình Hãy để chương trình kết quả là Q3
4 Sửa đổi Q3 để bất cứ khi nào nó cũng thực hiện bản trình bày đầu ra, sau đó nó đi kiểm tra trong mảng A để nhìn thấy bản đó nếu nó được viết 12 ký tự hoặc nhiều hơn,
và nếu vậy, có hay không hello, world là 12 ký tự đầu tiên Trong trường hợp đó, gọi
hàm mới foo là hàm được thêm vào trong mục Chương trình kết quả là R, và đầu vào
z giống như y.
Giả sử rằng Q với đầu vào y in hello, word như đầu ra đầu tiên của mình Sau đó,
R được xây dựng sẽ gọi foo Tuy nhiên, nếu Q với đầu vào y không in hello, word như
đầu ra đầu tiên của mình, thì sau đó R sẽ không bao giờ gọi foo Nếu chúng ta có thể
quyết định có hay không R với đầu vào z gọi foo, sau đó chúng ta còn biết có hay không Q với đầu vào y (lưu y = z) in hello, word Vì chúng ta biết rằng không có thuật
toán quyết định bài toán hello-word tồn tại, và tất cả bốn bước của việc xây dựng R từ
Q có thể được đưa ra bởi một chương trình đã chỉnh sửa mã của các chương trình, giả
định của chúng tôi rằng có một bài kiểm tra calls- foo là sai Không có chương trình như vậy tồn tại, và bài toán calls-foo là không giải được.
Phương diện biến đổi là quan trọng
Là một sai lầm phổ biến để cố gắng chứng minh một bài toán không giải được P2
bằng cách biến đổi P2 thành một số bài toán P1 không giải được đã được biết đến, tức
là, tuyên bố "nếu P1 giải được, thì P2 giải được" Tuyên bố đó, mặc dù chắc chắn đúng, nhưng là vô ích, kể từ khi giả thuyết của nó "P1 giải được" là sai
Cách duy nhất để chứng minh một bài toán P2 mới là không giải được đó là biến đổi một bài toán không giải được P1 đã được biết thành P2 Bằng cách đó, chúng ta chứng minh tuyên bố "nếu P1 giải được, thì P2 giải được" Trái ngược với tuyên bố đó
là "nếu P1 không giải được, thì P2 không giải được" Vì chúng ta biết rằng P1 không giải được, do đó chúng ta có thể suy ra rằng P2 không giải được
8.1.4 Bài tập phần 8.1