Kirchenzahlen sind eine Kodierung natürlicher Zahlen als Funktionen.
(\ 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
Ordentlich können Sie 2 Kirchennummern potenzieren, indem Sie sie einfach anwenden. Das heißt, wenn Sie 4 bis 2 anwenden, erhalten Sie die Kirchennummer 16
oder 2^4
. Das ist natürlich völlig unpraktisch. Kirchenzahlen benötigen eine lineare Menge an Gedächtnis und sind sehr, sehr langsam. Das Berechnen von so etwas 10^10
- was GHCI schnell richtig beantwortet - würde ewig dauern und könnte sowieso nicht in den Speicher Ihres Computers passen.
Ich habe in letzter Zeit mit optimalen λ-Bewertern experimentiert. Bei meinen Tests habe ich versehentlich Folgendes auf meinem optimalen λ-Rechner eingegeben:
10 ^ 10 % 13
Es sollte Multiplikation sein, keine Potenzierung. Bevor ich meine Finger bewegen konnte, um das ewig laufende Programm verzweifelt abzubrechen, beantwortete es meine Anfrage:
3
{ iterations: 11523, applications: 5748, used_memory: 27729 }
real 0m0.104s
user 0m0.086s
sys 0m0.019s
Mit blinkendem "Bug Alert" ging ich zu Google und überprüfte 10^10%13 == 3
tatsächlich. Aber der λ-Rechner sollte dieses Ergebnis nicht finden, er kann kaum 10 ^ 10 speichern. Ich begann es für die Wissenschaft zu betonen. Es hat mich sofort beantwortet 20^20%13 == 3
, 50^50%13 == 4
, 60^60%3 == 0
. Ich musste externe Tools verwenden , um diese Ergebnisse zu überprüfen, da Haskell selbst nicht in der Lage war, sie zu berechnen (aufgrund eines Überlaufs von Ganzzahlen) (wenn Sie natürlich Ganzzahlen und nicht Ints verwenden!). Dies war die Antwort auf 200^200%31
:
5
{ iterations: 10351327, applications: 5175644, used_memory: 23754870 }
real 0m4.025s
user 0m3.686s
sys 0m0.341s
Wenn wir eine Kopie des Universums für jedes Atom im Universum hätten und einen Computer für jedes Atom, das wir insgesamt hatten, könnten wir die Kirchennummer nicht speichern 200^200
. Dies veranlasste mich zu der Frage, ob mein Mac wirklich so leistungsfähig war. Vielleicht konnte der optimale Bewerter die unnötigen Zweige überspringen und auf die gleiche Weise zur Antwort gelangen, wie es Haskell mit der faulen Bewertung tut. Um dies zu testen, habe ich das λ-Programm für Haskell kompiliert:
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)
Dies gibt 1
( 5 ^ 5 % 4
) korrekt aus - aber wirf irgendetwas darüber 10^10
und es bleibt hängen, wodurch die Hypothese beseitigt wird.
Der optimale Evaluator, den ich verwendet habe, ist ein 160 Zeilen langes, nicht optimiertes JavaScript-Programm, das keinerlei Exponentialmodul-Mathematik enthielt - und die von mir verwendete Lambda-Kalkül-Modul-Funktion war ebenso einfach:
(λ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))))))
Ich habe keinen bestimmten modularen arithmetischen Algorithmus oder eine Formel verwendet. Wie kann der optimale Bewerter zu den richtigen Antworten gelangen?
node test.js
. Lassen Sie mich wissen, wenn Sie Fragen haben.