Số nhà thờ là một mã hóa của số tự nhiên như chức năng.
(\ f x → (f x)) -- church number 1
(\ f x → (f (f (f x)))) -- church number 3
(\ f x → (f (f (f (f x))))) -- church number 4
Nhanh chóng, bạn có thể lũy thừa 2 số nhà thờ chỉ bằng cách áp dụng chúng. Đó là, nếu bạn áp dụng 4 đến 2, bạn sẽ có được số nhà thờ 16
, hoặc 2^4
. Rõ ràng, điều đó là hoàn toàn không thực tế. Số nhà thờ cần một lượng bộ nhớ tuyến tính và thực sự, rất chậm. Việc tính toán một cái gì đó như 10^10
- mà GHCI nhanh chóng trả lời chính xác - sẽ mất nhiều thời gian và không thể phù hợp với bộ nhớ trên máy tính của bạn.
Gần đây tôi đã thử nghiệm với người đánh giá tối ưu. Trong các bài kiểm tra của mình, tôi đã vô tình gõ những thứ sau vào-comp tối ưu của mình:
10 ^ 10 % 13
Nó được cho là nhân, không phải lũy thừa. Trước khi tôi có thể di chuyển các ngón tay của mình để hủy bỏ chương trình chạy mãi mãi trong tuyệt vọng, nó đã trả lời yêu cầu của tôi:
3
{ iterations: 11523, applications: 5748, used_memory: 27729 }
real 0m0.104s
user 0m0.086s
sys 0m0.019s
Với đèn flash "thông báo lỗi", tôi đã truy cập Google và xác minh 10^10%13 == 3
. Nhưng calculator máy tính không cần phải tìm thấy kết quả đó, nó chỉ có thể lưu trữ 10 ^ 10. Tôi bắt đầu nhấn mạnh nó, cho khoa học. Nó ngay lập tức trả lời tôi 20^20%13 == 3
, 50^50%13 == 4
, 60^60%3 == 0
. Tôi đã phải sử dụng các công cụ bên ngoài để xác minh các kết quả đó, vì bản thân Haskell không thể tính toán được (do tràn số nguyên) (tất nhiên là nếu bạn sử dụng Số nguyên không phải là Ints!). Đẩy nó đến giới hạn của nó, đây là câu trả lời cho 200^200%31
:
5
{ iterations: 10351327, applications: 5175644, used_memory: 23754870 }
real 0m4.025s
user 0m3.686s
sys 0m0.341s
Nếu chúng ta có một bản sao vũ trụ cho mỗi nguyên tử trên vũ trụ và chúng ta có một máy tính cho mỗi nguyên tử mà chúng ta có, chúng ta không thể lưu trữ số nhà thờ 200^200
. Điều này khiến tôi đặt câu hỏi nếu máy mac của tôi thực sự mạnh đến thế. Có lẽ người đánh giá tối ưu đã có thể bỏ qua các nhánh không cần thiết và đi đến câu trả lời theo cách tương tự mà Haskell làm với đánh giá lười biếng. Để kiểm tra điều này, tôi đã biên dịch chương trình to thành Haskell:
data Term = F !(Term -> Term) | N !Double
instance Show Term where {
show (N x) = "(N "++(if fromIntegral (floor x) == x then show (floor x) else show x)++")";
show (F _) = "(λ...)"}
infixl 0 #
(F f) # x = f x
churchNum = F(\(N n)->F(\f->F(\x->if n<=0 then x else (f#(churchNum#(N(n-1))#f#x)))))
expMod = (F(\v0->(F(\v1->(F(\v2->((((((churchNum # v2) # (F(\v3->(F(\v4->(v3 # (F(\v5->((v4 # (F(\v6->(F(\v7->(v6 # ((v5 # v6) # v7))))))) # v5))))))))) # (F(\v3->(v3 # (F(\v4->(F(\v5->v5)))))))) # (F(\v3->((((churchNum # v1) # (churchNum # v0)) # ((((churchNum # v2) # (F(\v4->(F(\v5->(F(\v6->(v4 # (F(\v7->((v5 # v7) # v6))))))))))) # (F(\v4->v4))) # (F(\v4->(F(\v5->(v5 # v4))))))) # ((((churchNum # v2) # (F(\v4->(F(\v5->v4))))) # (F(\v4->v4))) # (F(\v4->v4))))))) # (F(\v3->(((F(\(N x)->F(\(N y)->N(x+y)))) # v3) # (N 1))))) # (N 0))))))))
main = print $ (expMod # N 5 # N 5 # N 4)
Điều này chính xác đầu ra 1
( 5 ^ 5 % 4
) - nhưng ném bất cứ điều gì ở trên 10^10
và nó sẽ bị mắc kẹt, loại bỏ giả thuyết.
Trình đánh giá tối ưu mà tôi đã sử dụng là một chương trình JavaScript dài, không tối ưu hóa dài 160 dòng, không bao gồm bất kỳ loại toán mô đun hàm mũ nào - và hàm mô đun lambda-tính toán tôi sử dụng cũng đơn giản không kém:
(λab.(b(λcd.(c(λe.(d(λfg.(f(efg)))e))))(λc.(c(λde.e)))(λc.(a(b(λdef.(d(λg.(egf))))(λd.d)(λde.(ed)))(b(λde.d)(λd.d)(λd.d))))))
Tôi không sử dụng thuật toán hoặc công thức số học mô-đun cụ thể. Vì vậy, làm thế nào là người đánh giá tối ưu có thể đi đến câu trả lời đúng?
node test.js
. Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi.