MathLabs

Nền tảng toán học

Hàm đệ quy, máy Turing

Các mô hình hình thức xác định chính xác những hàm nào có thể tính được bằng thuật toán.

Trực giácMáy tính được những gì?

Máy tính bỏ túi cộng và nhân được; trình biên dịch kiểm tra kiểu được; AI (đôi khi) trả lời câu hỏi được. Nhưng có hàm nào mà không thuật toán nào, dù thông minh đến đâu, có thể tính được không? Câu trả lời của Alan Turing — có — đến từ một mô hình toán học chính xác cho "thuật toán": máy Turing, gồm một băng, một đầu đọc/ghi, và một bảng quy tắc hữu hạn. Hai cách hình thức hóa tưởng chừng khác nhau, hàm đệ quy (xây từ các mảnh đơn giản bằng hợp thành và đệ quy) và máy Turing (một quá trình cơ học từng bước), hóa ra tính được chính xác cùng một lớp hàm — bằng chứng mạnh cho luận đề Church–Turing rằng đây thực sự là "mọi thứ tính được".

Đồ thị có hướng các trạng thái máy Turing với các cạnh chuyển tiếp có nhãn.
Đồ thị chuyển trạng thái của một máy Turing nhỏ: đỉnh là trạng thái, cạnh là chuyển tiếp gắn nhãn (đọc → ghi, di chuyển).

Đại họcHàm đệ quy nguyên thủy và hàm μ\mu-đệ quy

Định nghĩa: Hàm đệ quy nguyên thủy

Hàm đệ quy nguyên thủy là lớp hàm nhỏ nhất Nk→N\mathbb N^k \to \mathbb N chứa hàm không, hàm kế tiếp S(n)=n+1S(n)=n+1, mọi phép chiếu, và đóng dưới hợp thành và đệ quy nguyên thủy: cho g:Nk→Ng:\mathbb N^k\to\mathbb N và h:Nk+2→Nh:\mathbb N^{k+2}\to\mathbb N, phép đệ quy f(x⃗,0)=g(x⃗)f(\vec x,0)=g(\vec x), f(x⃗,n+1)=h(x⃗,n,f(x⃗,n))f(\vec x,n+1)=h(\vec x,n,f(\vec x,n)) định nghĩa một hàm đệ quy nguyên thủy mới ff. Cộng, nhân, lũy thừa, và mọi chương trình "vòng lặp for" có chặn cố định đều là đệ quy nguyên thủy — và mọi hàm đệ quy nguyên thủy đều toàn phần (xác định trên mọi đầu vào) và dừng sau số bước bị chặn với mỗi đầu vào.

f(x⃗,0)=g(x⃗),f(x⃗,n+1)=h(x⃗,n,f(x⃗,n))f(\vec x,0)=g(\vec x), \qquad f(\vec x,n+1)=h(\vec x,n,f(\vec x,n))

Để bắt trọn mọi hàm tính được — kể cả hàm có thể không dừng — ta thêm một phép toán nữa. Hàm **μ\mu-đệ quy (đệ quy tổng quát) thêm phép cực tiểu hóa không chặn**: μy. [P(x⃗,y)=0]\mu y.\,[P(\vec x,y)=0] trả về yy nhỏ nhất sao cho P(x⃗,y)=0P(\vec x,y)=0, tìm kiếm y=0,1,2,…y=0,1,2,\dots — và đơn giản không bao giờ trả về nếu không tồn tại yy như vậy. Chính điều này làm hàm μ\mu-đệ quy có khả năng không dừng, và định lý Kleene chỉ ra chúng tính đúng cùng lớp hàm (bộ phận) như máy Turing.

μy. [P(x⃗,y)=0]=min⁡{y:P(x⃗,y)=0}\mu y.\,[P(\vec x,y)=0] = \min\{y : P(\vec x,y)=0\}
Đệ quy nguyên thủy so với đệ quy tổng quát (μ\mu) so với khả tính Turing
LớpXây từLuôn dừng?Ví dụ
Đệ quy nguyên thủyHợp thành + đệ quy có chặnCó, luôn toàn phần+,×,+,\times, lũy thừa
Đệ quy tổng quát (μ\mu)Đệ quy nguyên thủy + μ\mu không chặnKhông, có thể chạy mãiHàm Ackermann AA
Khả tính Turing (bộ phận)Trạng thái + băng + quy tắc chuyểnKhông, đúng bằng μ\mu-đệ quyBất kỳ thuật toán nào

Không tồn tại thuật toán H(e,x)H(e,x) mà, với mọi chỉ số chương trình ee và đầu vào xx, luôn dừng và cho ra chính xác việc φe(x)\varphi_e(x) (chạy chương trình ee trên đầu vào xx) có dừng hay không.

Vì sao đúng?

Đây là lý do toán học vì sao không phần mềm diệt vi-rút, trình biên dịch, hay IDE nào có thể phát hiện hoàn hảo vòng lặp vô hạn, mã chết, hay "hàm này luôn lỗi" một cách tổng quát — không phải giới hạn kỹ thuật hiện tại, mà là một bức tường toán học cứng.

Chứng minh

Giả sử phản chứng tồn tại bộ quyết định HH: H(e,x)=1H(e,x)=1 nếu φe(x) ⁣↓\varphi_e(x)\!\downarrow (dừng) và H(e,x)=0H(e,x)=0 nếu φe(x) ⁣↑\varphi_e(x)\!\uparrow (chạy mãi), và bản thân HH luôn dừng với đáp án đúng.

Dùng HH, xây một chương trình mới DD mà, với đầu vào ee: tính H(e,e)H(e,e); nếu H(e,e)=1H(e,e)=1, thì DD vào vòng lặp vô hạn; nếu H(e,e)=0H(e,e)=0, thì DD dừng ngay. DD được xây hiệu quả từ HH (chỉ là HH cộng một câu lệnh if và một vòng lặp), nên nó có chỉ số chương trình nào đó dd, tức D=φdD=\varphi_d.

Giờ đặt câu hỏi tự quy chiếu: φd(d)\varphi_d(d) có dừng không?

Trường hợp 1: nếu φd(d)\varphi_d(d) dừng, thì theo tính đúng của HH, H(d,d)=1H(d,d)=1. Nhưng theo định nghĩa của DD, H(d,d)=1H(d,d)=1 làm DD chạy mãi ở đầu vào dd — tức φd(d)\varphi_d(d) không dừng. Mâu thuẫn.

Trường hợp 2: nếu φd(d)\varphi_d(d) không dừng, thì theo tính đúng của HH, H(d,d)=0H(d,d)=0. Nhưng theo định nghĩa của DD, H(d,d)=0H(d,d)=0 làm DD dừng ở đầu vào dd — tức φd(d)\varphi_d(d) có dừng. Mâu thuẫn.

Cả hai trường hợp đều mâu thuẫn, nên giả thiết HH tồn tại là sai. Bài toán dừng không quyết định được. ■\blacksquare

Nâng caoĐịnh lý Rice

Bài toán dừng chỉ là một ví dụ của hiện tượng rộng lớn hơn nhiều. Gọi tính chất PP của các hàm khả tính bộ phận là ngữ nghĩa nếu nó chỉ phụ thuộc vào hàm φe\varphi_e mà chương trình ee tính, không phụ thuộc mã nguồn, và không tầm thường nếu một hàm khả tính có nó và một hàm khác thì không.

Định lý: Định lý Rice

Với mọi tính chất ngữ nghĩa không tầm thường PP của các hàm khả tính bộ phận, tập {e:φe has property P}\{e : \varphi_e \text{ has property } P\} không quyết định được.

Vì sao đúng?

Định lý duy nhất này ngay lập tức loại trừ thuật toán cho "chương trình này có tính hàm không hay không", "chương trình này có tính toàn phần không", "hai chương trình này có tương đương không", và vô số câu hỏi tự nhiên khác về hành vi chương trình — tất cả cùng một lúc, không cần lập luận đường chéo riêng cho từng câu.

Chứng minh

Không mất tổng quát giả sử hàm không xác định khắp nơi ∅\emptyset (tính bởi chương trình không bao giờ dừng ở đầu vào nào) không có tính chất PP — nếu không thì lập luận với tính chất bù ¬P\lnot P, tính chất này quyết định được đúng khi PP quyết định được. Vì PP không tầm thường, cố định một chương trình e0e_0 mà hàm φe0\varphi_{e_0} của nó có tính chất PP.

Giả sử phản chứng PP quyết định được bởi thuật toán DD nào đó (cho một chỉ số chương trình, DD dừng và báo cáo đúng liệu hàm của chương trình đó có tính chất PP hay không). Ta quy về bài toán dừng bởi PP, mâu thuẫn với Định lý 1.

Cho một cặp (e,x)(e,x) bất kỳ, xây dựng hiệu quả (bằng thao tác văn bản đơn giản — định lý s-m-n của Kleene) một chương trình mới e′e' mà, với đầu vào yy bất kỳ: trước hết mô phỏng chương trình ee chạy trên đầu vào xx; nếu mô phỏng đó dừng, thì e′e' tiếp tục mô phỏng chương trình e0e_0 trên đầu vào yy và cho ra bất cứ gì nó cho ra.

Xét hai khả năng. Nếu ee dừng ở xx: mô phỏng ee trên xx kết thúc, nên e′e' sau đó hành xử y hệt e0e_0 ở mọi đầu vào, tức φe′=φe0\varphi_{e'}=\varphi_{e_0} — hàm này có tính chất PP (vì PP ngữ nghĩa, chỉ phụ thuộc hàm được tính, và φe0\varphi_{e_0} có PP). Nếu ee không dừng ở xx: mô phỏng ee trên xx không bao giờ kết thúc, nên e′e' không bao giờ tới bước mô phỏng e0e_0 ở bất kỳ đầu vào yy nào; do đó φe′\varphi_{e'} là hàm không xác định khắp nơi ∅\emptyset, theo giả định không có tính chất PP.

Vậy: ee dừng ở xx   ⟺  \iff φe′\varphi_{e'} có tính chất PP   ⟺  \iff D(e′)D(e') trả lời "có". Vì (e,x)↦e′(e,x)\mapsto e' tính được, thuật toán "tính e′e' từ (e,x)(e,x), rồi chạy D(e′)D(e')" sẽ quyết định được bài toán dừng — mâu thuẫn Định lý 1. Vậy không tồn tại DD như thế: PP không quyết định được. ■\blacksquare

Đại họcỨng dụng thực tiễn và Ví dụ minh họa

Trình tối ưu hóa biên dịch phải quyết định những điều như "đoạn mã này có tới được không?" hay "giá trị biến này có quan trọng không?" — theo định lý Rice, những điều này không quyết định được một cách tổng quát, đó chính xác là lý do trình biên dịch thật dùng xấp xỉ bảo thủ (có thể giữ lại mã chết thật sự thay vì mạo hiểm xóa mã sống). Hàm Busy Beaver BB(n)BB(n) — số bước lớn nhất một máy Turing nn-trạng thái dừng có thể thực hiện trước khi dừng — là một hàm cụ thể, không tính được: các giá trị đã biết là BB(1)=1BB(1)=1, BB(2)=6BB(2)=6, BB(3)=21BB(3)=21, BB(4)=107BB(4)=107, và năm 2024 dự án hợp tác Busy Beaver Challenge (dẫn dắt bởi Tristan Stérin và cộng sự, với chứng minh được xác minh bằng Coq từ một người đóng góp dùng bút danh "mxdys") xác lập BB(5)=47,176,870BB(5)=47{,}176{,}870 — cho thấy ngay cả câu hỏi tổ hợp "đơn giản" này cũng chỉ tính được từng trường hợp, không bao giờ bằng một thuật toán tổng quát.

Ví dụ: Khai triển hàm Ackermann A(2,2)A(2,2)

Dùng các quy tắc A(0,n)=n+1A(0,n)=n+1, A(m,0)=A(m−1,1)A(m,0)=A(m-1,1) với m>0m>0, và A(m,n)=A(m−1,A(m,n−1))A(m,n)=A(m-1,A(m,n-1)) với m,n>0m,n>0, tính A(2,2)A(2,2) từng bước, và giải thích vì sao hàm Ackermann toàn phần nhưng không đệ quy nguyên thủy.

Lời giải

A(2,2)=A(1,A(2,1))A(2,2)=A(1,A(2,1)) theo quy tắc thứ ba. Trước hết cần A(2,1)=A(1,A(2,0))A(2,1)=A(1,A(2,0)), và A(2,0)=A(1,1)A(2,0)=A(1,1) theo quy tắc thứ hai.

A(1,1)=A(0,A(1,0))=A(0,A(0,1))=A(0,2)=3A(1,1)=A(0,A(1,0))=A(0,A(0,1))=A(0,2)=3 (khai triển hai lần bằng quy tắc thứ hai và thứ nhất). Vậy A(2,0)=A(1,1)=3A(2,0)=A(1,1)=3, nên A(2,1)=A(1,3)A(2,1)=A(1,3).

A(1,3)=A(0,A(1,2))A(1,3)=A(0,A(1,2)), và A(1,2)=A(0,A(1,1))=A(0,3)=4A(1,2)=A(0,A(1,1))=A(0,3)=4. Vậy A(1,3)=A(0,4)=5A(1,3)=A(0,4)=5. Nên A(2,1)=5A(2,1)=5.

Quay lại đỉnh: A(2,2)=A(1,A(2,1))=A(1,5)=A(0,A(1,4))A(2,2)=A(1,A(2,1))=A(1,5)=A(0,A(1,4)); khai triển A(1,4)=A(0,A(1,3))=A(0,5)=6A(1,4)=A(0,A(1,3))=A(0,5)=6; nên A(1,5)=A(0,6)=7A(1,5)=A(0,6)=7. Vậy A(2,2)=7A(2,2)=7.

Hàm Ackermann được chứng minh toàn phần (nó luôn cuối cùng quy về trường hợp A(0,n)A(0,n)), nên nó thuộc lớp hàm đệ quy tổng quát — nhưng nó tăng nhanh hơn mọi hàm đệ quy nguyên thủy (ví dụ A(3,n)=2n+3−3A(3,n)=2^{n+3}-3, A(4,n)A(4,n) đã là tháp lũy thừa). Vì có thể chỉ ra mọi hàm đệ quy nguyên thủy cuối cùng bị chặn bởi một A(k,⋅)A(k,\cdot) cố định nào đó, không hàm đệ quy nguyên thủy nào có thể bằng chính AA — một lập luận thống trị kiểu đường chéo, không phải toán tử μ\mu, mới là điều đặt Ackermann ra ngoài đệ quy nguyên thủy dù nó toàn phần.

Ví dụ: Quy bài toán dừng về "chương trình này có in hello không?"

Chứng minh bài toán "cho chương trình ee, chạy ee (không đầu vào) có bao giờ in chuỗi `hello` không?" không quyết định được, bằng quy về trực tiếp từ bài toán dừng — không dùng định lý Rice.

Lời giải

Giả sử phản chứng thuật toán Q(e)Q(e) quyết định được việc chạy ee có bao giờ in `hello` không. Ta dùng QQ để quyết định bài toán dừng, mâu thuẫn Định lý 1.

Cho chương trình ee và đầu vào xx bất kỳ, xây hiệu quả chương trình mới e′e' (không cần đầu vào) mà: mô phỏng ee chạy trên xx; nếu mô phỏng đó dừng, e′e' tiếp đó in `hello` rồi dừng.

Nếu ee dừng ở xx: mô phỏng kết thúc, nên e′e' tới bước in và in `hello`. Nếu ee không dừng ở xx: mô phỏng không bao giờ kết thúc, nên e′e' không bao giờ tới bước in và không bao giờ in `hello`.

Vậy ee dừng ở xx   ⟺  \iff Q(e′)Q(e') trả lời "có". Vì (e,x)↦e′(e,x)\mapsto e' tính được, "xây e′e' rồi chạy Q(e′)Q(e')" quyết định được bài toán dừng — mâu thuẫn Định lý 1. Vậy QQ không thể tồn tại: bài toán "in hello" không quyết định được. (Đây chính là khuôn mẫu phân tích trình biên dịch: "dòng mã này có bao giờ được tới không" có cùng hình dạng.)

Dùng A(1,n)=n+2A(1,n)=n+2, A(1,3)A(1,3) bằng bao nhiêu?

Điều nào sau đây là hệ quả của tính không quyết định của bài toán dừng đối với thiết kế trình biên dịch?

Định lý Rice KHÔNG áp dụng cho tính chất nào?

Trong chứng minh đường chéo của tính không quyết định của bài toán dừng, điều gì dẫn tới mâu thuẫn?

Tài liệu tham khảo

  1. Wikipedia contributors (2024). Halting problem
  2. Wikipedia contributors (2024). Rice's theorem
  3. Wikipedia contributors (2024). Ackermann function