Les nombres d'église sont un encodage de nombres naturels en tant que fonctions.
(\ 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
Soigneusement, vous pouvez exponentiellement 2 numéros d'églises en les appliquant simplement. Autrement dit, si vous appliquez 4 à 2, vous obtenez le numéro d'église 16
, ou 2^4
. De toute évidence, ce n'est absolument pas pratique. Les numéros d'église ont besoin d'une quantité linéaire de mémoire et sont vraiment très lents. 10^10
Calculer quelque chose comme - ce que GHCI répond rapidement correctement - prendrait des années et ne pourrait de toute façon pas tenir la mémoire de votre ordinateur.
J'ai récemment expérimenté des évaluateurs λ optimaux. Lors de mes tests, j'ai accidentellement tapé ce qui suit sur mon λ-calculateur optimal:
10 ^ 10 % 13
C'était censé être une multiplication, pas une exponentiation. Avant que je puisse bouger mes doigts pour abandonner le programme permanent en désespoir de cause, il a répondu à ma demande:
3
{ iterations: 11523, applications: 5748, used_memory: 27729 }
real 0m0.104s
user 0m0.086s
sys 0m0.019s
Avec mon "bug alert" clignotant, je suis allé sur Google et vérifié, en 10^10%13 == 3
effet. Mais le λ-calculateur n'était pas censé trouver ce résultat, il peut à peine stocker 10 ^ 10. J'ai commencé à le souligner, pour la science. Il m'a répondu instantanément 20^20%13 == 3
, 50^50%13 == 4
, 60^60%3 == 0
. J'ai dû utiliser des outils externes pour vérifier ces résultats, car Haskell lui-même n'était pas en mesure de le calculer (en raison d'un débordement d'entier) (c'est si vous utilisez des nombres entiers et non des nombres entiers, bien sûr!). En le poussant à ses limites, c'était la réponse à 200^200%31
:
5
{ iterations: 10351327, applications: 5175644, used_memory: 23754870 }
real 0m4.025s
user 0m3.686s
sys 0m0.341s
Si nous avions une copie de l'univers pour chaque atome de l'univers, et que nous avions un ordinateur pour chaque atome que nous avions au total, nous ne pourrions pas stocker le numéro de l'église 200^200
. Cela m'a incité à me demander si mon mac était vraiment aussi puissant. Peut-être que l'évaluateur optimal a pu sauter les branches inutiles et arriver directement à la réponse de la même manière que Haskell le fait avec une évaluation paresseuse. Pour tester cela, j'ai compilé le programme λ sur 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)
Cela génère correctement 1
( 5 ^ 5 % 4
) - mais lancez tout ce qui est ci 10^10
- dessus et il sera bloqué, éliminant l'hypothèse.
L' évaluateur optimal que j'ai utilisé est un programme JavaScript non optimisé de 160 lignes qui n'incluait aucune sorte de module mathématique exponentiel - et la fonction de module lambda-calcul que j'ai utilisée était tout aussi simple:
(λ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))))))
Je n'ai utilisé aucun algorithme ou formule arithmétique modulaire spécifique. Alors, comment l'évaluateur optimal est-il capable d'arriver aux bonnes réponses?
node test.js
. Faites moi savoir si vous avez des questions.