Nomor gereja adalah pengkodean bilangan asli sebagai fungsi.
(\ 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
Dengan rapi, Anda dapat mengeksponensial 2 nomor gereja hanya dengan menerapkannya. Artinya, jika Anda menerapkan 4 hingga 2, Anda mendapatkan nomor gereja 16
, atau 2^4
. Jelas sekali, itu sama sekali tidak praktis. Nomor gereja membutuhkan jumlah memori linier dan sangat, sangat lambat. Menghitung sesuatu seperti 10^10
- yang GHCI dengan cepat menjawab dengan benar - akan memakan waktu lama dan toh tidak bisa memuat memori di komputer Anda.
Saya telah bereksperimen dengan evaluator λ yang optimal belakangan ini. Pada pengujian saya, saya tidak sengaja mengetik yang berikut ini pada kalkulator λ optimal saya:
10 ^ 10 % 13
Itu seharusnya perkalian, bukan eksponensiasi. Sebelum saya bisa menggerakkan jari saya untuk membatalkan program yang berjalan selamanya dengan putus asa, itu menjawab permintaan saya:
3
{ iterations: 11523, applications: 5748, used_memory: 27729 }
real 0m0.104s
user 0m0.086s
sys 0m0.019s
Dengan "peringatan bug" saya berkedip, saya membuka Google dan memverifikasi 10^10%13 == 3
. Tapi kalkulator λ tidak seharusnya menemukan hasil itu, ia hampir tidak bisa menyimpan 10 ^ 10. Saya mulai menekankannya, untuk sains. Ini langsung menjawab aku 20^20%13 == 3
, 50^50%13 == 4
, 60^60%3 == 0
. Saya harus menggunakan alat eksternal untuk memverifikasi hasil tersebut, karena Haskell sendiri tidak dapat menghitungnya (karena overflow integer) (jika Anda menggunakan Integer bukan Ints, tentu saja!). Mendorongnya hingga batasnya, ini adalah jawaban untuk 200^200%31
:
5
{ iterations: 10351327, applications: 5175644, used_memory: 23754870 }
real 0m4.025s
user 0m3.686s
sys 0m0.341s
Jika kita memiliki satu salinan alam semesta untuk setiap atom di alam semesta, dan kita memiliki komputer untuk setiap atom yang kita miliki secara total, kita tidak dapat menyimpan nomor gereja 200^200
. Ini mendorong saya untuk mempertanyakan apakah Mac saya benar-benar sekuat itu. Mungkin evaluator optimal mampu melewati cabang yang tidak perlu dan sampai pada jawaban dengan cara yang sama seperti yang dilakukan Haskell dengan evaluasi malas. Untuk mengujinya, saya menyusun program λ ke 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)
Ini dengan benar mengeluarkan 1
( 5 ^ 5 % 4
) - tetapi membuang apa pun di atas 10^10
dan itu akan macet, menghilangkan hipotesis.
The evaluator optimal saya menggunakan adalah 160-garis panjang, program JavaScript unoptimized yang tidak termasuk apapun eksponensial modulus matematika - dan fungsi lambda-kalkulus modulus saya yang digunakan adalah sama sederhana:
(λ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))))))
Saya tidak menggunakan algoritma atau rumus aritmatika modular tertentu. Jadi, bagaimana evaluator yang optimal bisa sampai pada jawaban yang benar?