La fonction Ackermann est remarquable pour être l’un des exemples les plus simples d’une fonction totale calculable qui n’est pas récursive primitive.
Nous allons utiliser la définition de la A(m,n)
prise en deux entiers non négatifs où
A(0,n) = n+1
A(m,0) = A(m-1,1)
A(m,n) = A(m-1,A(m,n-1))
Vous pouvez implémenter
- une fonction nommée ou anonyme prenant deux entiers en entrée, renvoyant un entier, ou
- un programme prenant deux entiers séparés par une ligne ou un espace sur STDIN, en imprimant un résultat vers STDOUT.
Vous ne pouvez pas utiliser une fonction Ackermann ou une fonction d'hyperexponentiation d'une bibliothèque, s'il en existe une, mais vous pouvez utiliser toute autre fonction d'une autre bibliothèque. Une exponentiation régulière est autorisée.
Votre fonction doit pouvoir trouver la valeur de A(m,n)
pour m ≤ 3 et n ≤ 10 en moins d'une minute. Il doit au moins théoriquement se terminer par toute autre entrée: avec un espace de pile infini, un type Bigint natif et une période de temps arbitrairement longue, la réponse sera renvoyée. Éditer: Si votre langue a une profondeur de récursion par défaut trop restrictive, vous pouvez la reconfigurer sans frais de caractères.
La soumission avec le plus petit nombre de caractères gagne.
Voici quelques valeurs pour vérifier votre réponse:
A | n=0 1 2 3 4 5 6 7 8 9 10
-----+-----------------------------------------------------------------
m=0 | 1 2 3 4 5 6 7 8 9 10 11
1 | 2 3 4 5 6 7 8 9 10 11 12
2 | 3 5 7 9 11 13 15 17 19 21 23
3 | 5 13 29 61 125 253 509 1021 2045 4093 8189
4 | 13 65533 big really big...
A(3,8)
et supérieure aussi naïvement que les autres? Dois-je proposer une solution de non-récursion ou puis-je simplement "supposer un espace infini" dans ces cas? Je suis assez certain, cela se terminerait dans une minute.Réponses:
Pyth , 19
Définit
a
ce qui fonctionne comme la fonction Ackermann. Notez que cela nécessite une profondeur de récursion supérieure à celle calculée par le compilateur pyth officiel jusqu'à ce joura 3 10
, donc j'ai augmenté la profondeur de récursivité. Ce n'est pas un changement de langage, mais uniquement pour le compilateur.Tester:
Explication:
Essentiellement, il conditionne d'abord la valeur de vérité,
G
qu'il s'agisse de récidiver ou de renvoyer H + 1. S'il est récurrent, le premier argument est toujours G-1 et conditionne la valeur de vérité, àH
savoir s'il doit être utiliséa(G,H-1)
comme second argument ou s'il doit être utilisé1
comme second argument.la source
DaGHR
àM
eta
àg
. (L'ordre des arguments en faveur du?
changement est-il correct?)M
place, et oui, l'?
ordre des arguments a changé. C'est maintenant condition, vrai, faux. C'était vrai, condition, faux.M
!Haskell, 35 ans
ceci définit la fonction d'opérateur
%
.cela fonctionne en remarquant que
m%n
(oùa
est la fonction ackerman)m
est(m-1)%
appliqué à desn+1
temps différents de zéro1
. par exemple,3%2
est défini comme2%(3%1)
ce qui est2%(2%(3%0))
, et c'est2%(2%(2%1))
la source
0%n
au lieu de àn+1
cause de la précédenceGolfScript (30)
Démo en ligne
Sans le
1>
(cas particuliersA(1, n)
),A(3, 10)
le calcul sur l'ordinateur sur lequel je l'ai testé prend 9 minutes . Avec ce cas spécial, il est assez rapide que la démonstration en ligne prenne moins de 10 secondes.Notez que ce n'est pas une traduction naïve de la définition. La profondeur de récursivité est délimitée par
m
.Dissection
la source
1>
. Après le retrait (et le passageif
à?
), l'informatique3 10 A
prend 110 secondes avec l'interprète en ligne et six secondes avec l'interpréteur Java.Calcul lambda binaire , 54 bits = 6,75 octets
Hexdump:
Binaire:
C'est λ m . m (λ g . λ n . g ( n g 1)) (λ n . λ f . λ x . f ( n f x )), où tous les nombres sont représentés par des chiffres Eglise .
la source
JavaScript, ES6,
41 à34 octetsExécutez ceci dans une dernière console de Firefox et cela créera une fonction appelée
f
que vous pouvez appeler avec différentes valeurs dem
andn
likeOU
essayez le code ci-dessous dans un dernier Firefox
la source
Python 2.7.8 -
80, 54, 48, 4645(Crédits à xnor!)
Plus lisible, mais avec 1 caractère supplémentaire:
Non pas que je devais régler
sys.setrecursionlimit(10000)
pour obtenir un résultatA(3,10)
. La poursuite de la pratique du golf à l'aide d'une indexation logique n'a pas fonctionné en raison de la profondeur de plus en plus grande de la récursion.la source
1else
. La lettree
initiale pose des problèmes à l'analyseur, car les nombres peuvent être écrits comme suit1e3
.and/or
:A=lambda m,n:m and A(m-1,n<1or A(m,n-1))or-~n
1else
, la plupart des autres versions ne le font pas.1else
; cela me permet de faire sortir un omble ici et probablement ailleurs. Mais c'est vraiment spécifique à la version! Python 2.7.4 ne le permet pas. Utilisez-vous une version en ligne avec 2.7.8 ou devrais-je la télécharger?1else
également.J - 26 caractères
Il existe une autre définition, plus fonctionnelle, d’Ackermann:
Il se trouve qu’il
Iter
est très facile d’écrire en J, parce que J a le moyen de transmettre lem-1
àAck
et de définir la valeur initiale deIter
1. Expliqué par explosion:Cela repose sur ce que J appelle la forme générale de
^:
- un moyen fondamental d'avoir plus de contrôle sur toutes les limites de manière tacite (sans point).Au REPL:
Nous devons définir
ack
par nom pour pouvoir le mettre dans un tableau, car$:
c'est une bête horrible et laide qui frappe furieusement tous ceux qui tentent de le comprendre. C'est une référence à soi, où self est défini comme la phrase du verbe le plus grand qui la contient.table
est un adverbe et aimerait donc faire partie de la phrase du verbe si vous lui en donnez la chance. Vous devez donc vous en tenir$:
à une définition nommée pour l'utiliser.Edit: 24 caractères?
Des années plus tard, j'ai trouvé une solution composée de deux caractères plus courts.
C'est beaucoup plus lent, cependant: cela
3 ack 8
prend plus d'une minute avec ma machine. C'est parce que (1) j'utilise un repli/
au lieu de l'itération, donc J doit probablement se rappeler plus de choses que d'habitude, et (2) tout en0&<~
effectuant le même calcul que(0<[)
, il est en réalité exécuté plusieursn+1
fois avant de passer à l'étape récursive lors de l'appelm ack n
-0&<
cela se produit être idempotent, cela ne gâche donc pas le calcul, maisn
devient rapide etack
très récursif.Je doute qu'une machine plus puissante puisse afficher le nouveau code en moins d'une minute, car il s'agit d'un ordinateur sur lequel l'ancien code peut être trouvé
3 ack 10
en moins de 15 secondes.la source
C - 41 octets
Rien à faire - les petites limites signifient que toutes les valeurs requises peuvent être calculées en moins de 1 seconde en suivant naïvement la définition de la fonction.
la source
Javascript ES6 (34)
La mise en oeuvre:
la source
JavaScript (ES6) - 34
Et un test:
la source
Coq, 40
C'est une fonction de type
nat -> nat -> nat
. Coq n'autorisant que la construction de fonctions globales, il sert également de preuve formelle du bien-fondé de la récurrence d'Ackermann.Démo:
Note: Coq 8.5, publié après ce défi, renommé
nat_iter
pourNat.iter
.la source
Raquette 67
la source
Mathematica, 46 octets
Prend à peu près exactement une minute pour
a[3,10]
. Notez que la limite de récursivité par défaut de Mathematica est trop petite poura[3,8]
et au-delà (du moins sur ma machine), mais cela peut être corrigé en configurantla source
If
être une fonction est encore plus lent.m_~a~n_:=m~a~n=
...Javascript avec lambdas, 34
Une réponse typique, ne peut rien raccourcir.
la source
Haskell,
4844 caractères (36 pour la liste)Bien que n'étant pas aussi courte que l'autre solution Haskell, celle-ci est remarquable car elle exprime la fonction Ackermann sous la forme d'une liste infinie, ce qui, à mon avis, est plutôt génial. Le résultat est une liste infinie (de listes infinies) telle que, à la position [m, n] elle contienne la valeur A (m, n) .
La liste infinie elle-même:
En fonction (pour se conformer à la spécification):
La formulation a été dérivée en observant que le cas général / commun de la fonction Ackermann consiste à utiliser la valeur à gauche comme index dans la ligne ci-dessus. Le cas de base de cette récursivité (c'est-à-dire la colonne la plus à gauche d'une ligne, c'est-à-dire A (m, 0) ) consiste à utiliser la deuxième valeur la plus à gauche de la ligne ci-dessus. Le cas de base pour cela récurrence est le cas A (0, n) = n + 1 , c'est-à-dire que la première ligne est
[1..]
.Ainsi, nous obtenons
Ensuite, nous ajoutons simplement un autre niveau d'itération basé sur ce modèle et faisons des jongles inutiles .
la source
iterate
à un nom de lettre -à- direi=iterate;ack=i ...
Tiny Lisp , 70 ans (hors compétition)
Ceci est hors compétition, car le langage est plus récent que la question et ne réussit pas non plus à exécuter le
(A 3 10)
code requis dans la question, en raison d'un débordement de pile.(d A(q((m n)(i m(i n(A(s m 1)(A m(s n 1)))(A(s m 1)1))(s n(s 0 1))))))
Ceci définit une fonction
A
qui calcule la fonction Ackermann. Formaté:Nous utilisons toutes les macros internes (
d
(définir) etq
(quote) eti
(if)) et une fonction intégrée (s
- soustraire) ici.i
exécute sa vraie partie quand la condition est un nombre> 0 (sinon la fausse partie), nous n'avons donc pas à faire de comparaison explicite ici.s
est la seule opération arithmétique disponible, nous l’utilisons pour len-1
/m-1
, ainsi que(s n (s 0 1))
pourn+1
.Lisp minuscule utilise l'optimisation de la récursion de la queue, mais cela n'aide que pour l'extérieur
A
appel dans le résultat, pas pour l'A(m, n-1)
appel utilisé pour les paramètres.Avec ma toute petite implémentation de lisp à Ceylan sur la machine virtuelle, cela fonctionne
(A 3 5) = 253
, mais il semble s’effondrer lorsque je tente de calculer(A 2 125)
directement (ce qui devrait donner le même résultat). En calculant cela après(A 3 4) = 125
, la JVM semble avoir suffisamment optimisé les fonctions pour intégrer des appels de fonction intermédiaires dans mon interpréteur, ce qui permet une plus grande profondeur de récursivité. Étrange.L' implémentation de référence se lève pour
(A 3 5) = 253
et aussi(A 2 163) = 329
, mais ne réussit pas(A 2 164)
, et donc encore moins(A 3 6) = (A 2 253)
.la source
Go,
260243240122 octetsJe n'ai pas vu que la question permettait anon funcs.
loin d'être compétitif, mais j'apprends cette langue et je voulais le tester.
utilisez-le comme
go run ack.go
et ensuite fournissez deux nombres,m
etn
. si m> 4 ou n> 30, le temps d'exécution peut être supérieur à une demi-minute.pour
m=3 n=11
:edit : total enregistré 17 octets en passant aux importations de
switch
surif/else
et de pointsla source
switch 0 {case m:r=n+1 case n:r=a(m-1,1) default:r=a(m-1,a(m,n-1))}
Laswitch
déclaration de Go est si merveilleusement flexible!Haskell:
8169 octetsa 3 10
prend environ 45 secondes.la source
Pyth (non concurrent), 15 octets
Essayez-le en ligne! (exemple d'utilisation de la fonction
g3T
ajoutée, ce qui signifieg(3,10)
)la source
UGL (non concurrent) ,
31 à30 octetsEntrée séparée par des nouvelles lignes.
Essayez-le en ligne!
(Il a été implémenté comme exemple standard dans l'interpréteur.)
la source
Julia,
343128 octetsC'est une fonction anonyme nommée. C'est une implémentation directe de la définition récursive, abusant de la capacité de Julia à redéfinir les opérateurs .
Essayez-le en ligne!
la source
R -
5452J'ai utilisé cela comme une excuse pour essayer de comprendre R, alors c'est probablement très mal fait :)
Exemple d'exécution
Je reçois un débordement de pile pour quoi que ce soit au-delà
T-SQL- 222
Je pensais que j'essaierais de faire en sorte que T-SQL le fasse également. Utilisé une méthode différente parce que la récursivité n'est pas très agréable en SQL. Tout ce qui dépasse 4,2 le bombe.
la source
{}
bien que la limite de débordement de pile ne soit d'aucun secours, car R n'a pas de coût total de possession ...brainfuck , 90 octets
Essayez-le en ligne!
Suppose une implémentation avec une taille de cellule de taille arbitraire, avec IO en tant que nombres. -6 octets si cela ne vous dérange pas d'utiliser des cellules négatives.
Termine en environ 30 secondes pour 3,8 dans l’interprète lié, à condition de cocher les paramètres appropriés. Tapez les nombres entrés précédés de
\
s, par exemple3,9
est\3\9
.la source
Tcl , 67 octets
Essayez-le en ligne!
Tcl , 77 octets
Essayez-le en ligne!
Dans le compilateur en ligne, son exécution échoue, mais dans un interpréteur Tcl local, il fonctionne bien. Je me suis profilé de chaque appel racine à
A
fonction, pour voir combien de temps le calcul a pris pour chaque paire de{m,n}
sujets à tester:Cela échoue pour la dernière paire
{m,n}={3,10}
, car cela prend un peu plus d'une minute.Pour des valeurs plus élevées de
m
, il sera nécessaire d'augmenter larecursionlimit
valeur.Je ne peux pas l'obtenir plus court à 65 octets, mais cela ne répondra pas à l'exigence de la question "Votre fonction doit pouvoir trouver la valeur de A (m, n) pour m ≤ 3 et n ≤ 10 en moins d'une minute.". Sans
{}
cela, le délai d'attente sur TIO sera dépassé et la démonstration des deux dernières entrées ne sera pas effectuée.Tcl , 65 octets
Essayez-le en ligne!
la source
J: 50
Revient en une fraction de seconde pour 0 ... 3 vs 0 ... 10:
PS: le "0 sert à faire un travail sur chaque élément, au lieu de dévorer les tableaux gauche et droit, et de générer des erreurs de longueur. Mais ce n'est pas nécessaire pour, par exemple, 9 = 2 A 3.
la source
APL, 31
Assez simple. Utilise le caractère une fois pour sauvegarder un octet en inversant les arguments. Prend m comme argument de gauche et n comme argument de droite.
TryAPL.org
la source
Ruby, 65 ans
Explication
Ceci est une traduction assez simple de l'algorithme donné dans la description du problème.
Integer
s sont attendus.Hash
h
. le||=
opérateur est utilisé pour calculer une valeur qui n'avait pas été calculée auparavant.a[3,10]
est calculé en ~ 0.1 secondes sur ma machine.Voici une version non-golfée
la source
a[3,10]
jette un SystemStackError sur ma machine ...m<1?(n+1):(n<1?a[m-1,1]:a[m-1,a[m,n-1]])
pourm<1?n+1:a[m-1,n<1?1:a[m,n-1]]
Mouse-2002 ,
9983 octetsla source
Java, 274 octets
Il calcule
A(3,10)
en quelques secondes, et compte tenu de l'espace mémoire et de la pile infinis, il peut calculer n'importe quelle combinaison deb
etB
aussi longtemps que le résultat est inférieur à 2 2147483647 -1.la source
import java.math.*;BigInteger A(BigInteger b,BigInteger B){return b.equals(B.ZERO)?B.add(B.ONE):B.equals(B.ZERO)?A(b.subtract(B.ONE),B.ONE):A(b.subtract(B.ONE),A(b,B.subtract(B.ONE)));}
Ceylan,
888785Ceci est une implémentation simple. Formaté:
L'alias enregistre un seul octet, sans lui (avec l'écriture
Integer
au lieu deI
), nous aurions 86 octets. Deux autres octets peuvent être sauvegardés en les remplaçant== 0
par< 1
deux fois.Avec les paramètres par défaut de
ceylon run
, cela fonctionnera jusqu’àA(3,12) = 32765
(etA(4,0) = 13
), maisA(3,13)
(et donc aussiA(4,1)
) jettera une erreur de débordement de pile. (A(3,12)
prend environ 5 secondes,A(3,11)
environ 3 sur mon ordinateur.)Utiliser
ceylon run-js
(c.-à-d. Exécuter le résultat de la compilation en JavaScript sur node.js) est beaucoup plus lent (nécessite 1 min 19 sA(3,10)
), et fonctionne déjàA(3, 11)
avec une »Taille maximale de la pile d'appels dépassée« (avec les paramètres par défaut) après une exécution de 1 min 30 s.Ceylan sans récursivité, 228
En bonus, voici une version non-récursive (plus longue, bien sûr, mais à l'abri des débordements de pile - peut provoquer une erreur d'insuffisance de mémoire à un moment donné).
Formaté:
Il est assez lent sur mon ordinateur que la version récursive:
A(3,11)
prend 9,5 secondes,A(3,12)
prend 34 secondes,A(3,13)
prend 2:08 minutes,A(3,14)
prend 8:25 minutes. (J'avais initialement une version utilisant des iterables paresseux au lieu des tuples que j'ai maintenant, qui était même beaucoup plus lente, avec la même taille).Un peu plus rapide (21 secondes pour
A(3,12)
) (mais aussi un octet plus) est une version en utilisant aus.push
lieu des.addAll
, mais qui devait être appelé à plusieurs reprises pour ajouter plusieurs numéros, car il faut un seul entier chacun. L'utilisation d'une LinkedList au lieu d'une ArrayList est beaucoup plus lente.la source