Explication
Befunge est un programme en deux dimensions qui utilise des piles .
Cela signifie que pour faire 5 + 6, vous écrivez 56+
, ce qui signifie:
56+
5 push 5 into stack
6 push 6 into stack
+ pop the first two items in the stack and add them up, and push the result into stack
(to those of you who do not know stacks, "push" just means add and "pop" just means take off)
Cependant, nous ne pouvons pas pousser le nombre 56
directement dans la pile.
Pour ce faire, il faut écrire à la 78*
place, qui se multiplie 7
et 8
et pousse le produit dans la pile.
Détails
Pour chaque numéro de 1
àn
, trouver une chaîne composée de seulement ces caractères: 0123456789+-*/:
(je n'utiliser modulo.)%
Le but est de trouver la chaîne la plus courte pouvant représenter le nombre, en utilisant le format décrit ci-dessus.
Par exemple, si l'entrée est 123
, alors la sortie le serait 67*9:*+
. La sortie doit être évaluée de gauche à droite.
S'il y a plus d'une sortie acceptable (par exemple, elle 99*67*+
est également acceptable), n'importe laquelle peut être imprimée (pas de bonus pour toutes les imprimer).
Plus d'explications
Si vous ne comprenez toujours pas comment est 67*9:*+
évalué 123
, voici une explication détaillée.
stack |operation|explanation
67*9:*+
[6] 6 push 6 to stack
[6,7] 7 push 7 to stack
[42] * pop two from stack and multiply, then put result to stack
[42,9] 9 push 9 to stack
[42,9,9] : duplicate the top of stack
[42,81] * pop two from stack and multiply, then put result to stack
[123] + pop two from stack and add, then put result to stack
TL; DR
Le programme doit trouver la chaîne la plus courte pouvant représenter l'entrée (nombre), en utilisant le format spécifié ci-dessus.
NOTATION
- Nous l'avons déjà fait en un minimum de code . Cette fois, la taille n'a pas d'importance.
- La langue de votre choix doit avoir un compilateur / interprète gratuit pour mon système d'exploitation (Windows 7 Enterprise).
- Bonus si vous incluez le lien vers le compilateur / interprète (je suis trop paresseux).
- Si possible, veuillez inclure une minuterie pour ma commodité. La sortie du temporisateur est valide.
- Le score sera le plus élevé
n
en 1 minute. - Cela signifie que le programme doit imprimer la représentation requise à
1
partir de maintenant. - Pas de codage en dur, sauf
0
pour9
.
(plus) SPÉCIFIQUE
- Le programme n'est pas valide s'il génère une chaîne plus longue que nécessaire pour n'importe quel nombre.
1/0=ERROR
5/2=2
,(-5)/2=-2
,(-5)/(-2)=2
,5/(-2)=-2
Désambiguïsation
Le -
est second-top minus top
, ce qui signifie que 92-
revient 7
.
De même, le /
est second-top divide top
, ce qui signifie que 92/
revient 4
.
Exemple de programme
Lua
Utilise la recherche en profondeur.
local function div(a,b)
if b == 0 then
return "error"
end
local result = a/b
if result > 0 then
return math.floor(result)
else
return math.ceil(result)
end
end
local function eval(expr)
local stack = {}
for i=1,#expr do
local c = expr:sub(i,i)
if c:match('[0-9]') then
table.insert(stack, tonumber(c))
elseif c == ':' then
local a = table.remove(stack)
if a then
table.insert(stack,a)
table.insert(stack,a)
else
return -1
end
else
local a = table.remove(stack)
local b = table.remove(stack)
if a and b then
if c == '+' then
table.insert(stack, a+b)
elseif c == '-' then
table.insert(stack, b-a)
elseif c == '*' then
table.insert(stack, a*b)
elseif c == '/' then
local test = div(b,a)
if test == "error" then
return -1
else
table.insert(stack, test)
end
end
else
return -1
end
end
end
return table.remove(stack) or -1
end
local samples, temp = {""}, {}
while true do
temp = {}
for i=1,#samples do
local s = samples[i]
if eval(s) ~= -1 or s == "" then for n in ("9876543210+-*/:"):gmatch(".") do
table.insert(temp, s..n)
end end
end
for i=1,#temp do
local test = eval(temp[i])
if input == test then
print(temp[i])
return
end
end
samples = temp
end
la source
56
directement dans la pile, comment pouvons-nous pousser78
dans la pile?56
cinquante-six directement dans la pile, mais nous pouvons pousser7
sept et8
huit séparément dans la pile.56
dans Befunge, vous poussez les chiffres , vous vous retrouvez donc avec une pile de[5, 6]
. Pour obtenir le numéro 56, vous devez pousser7
, puis8
sur la pile, puis les multiplier pour obtenir le numéro 56 sur la pile.:
rend les choses beaucoup plus délicates, donc je recommanderais de fournir une bonne liste de cas de test, par exemple86387
Réponses:
C ++, exploser toute la mémoire sur un ordinateur près de chez vous
Génère la chaîne la plus courte où le calcul ne provoque aucun débordement d'un entier signé 32 bits (donc tous les résultats intermédiaires sont dans la plage
[-2147483648, 2147483647]
Sur mon système, cela génère une solution pour tous les numéros jusqu'à et y compris
483432
en 30 secondes lors de l'utilisation de la mémoire 1.8G. Des nombres encore plus élevés exploseront rapidement l'utilisation de la mémoire. Le nombre le plus élevé que je peux gérer sur mon système est5113906
. Le calcul prend près de 9 minutes et 24 Go. Quand il termine, il a en interne une solution pour les398499338
valeurs, environ 9% de tous les entiers 32 bits (positifs et négatifs)Nécessite un compilateur C ++ 11. Sous Linux, compilez avec:
Ajoutez
-DINT64
une option pour utiliser une plage entière de 64 bits au lieu de 32 bits pour les résultats intermédiaires (cela utilisera environ 50% de temps et de mémoire en plus). Cela nécessite un type 128 bits intégré. Vous devrez peut-être modifier le type de gcc__int128
. Aucun résultat dans au moins la plage[1..483432]
change en permettant des résultats intermédiaires plus importants.Ajoutez
-DOVERFLOW
comme option pour ne pas utiliser un type entier plus grand pour vérifier le débordement. Cela a pour effet d'autoriser le débordement et l'encapsulation de valeur.Si votre système dispose de tcmalloc ( https://github.com/gperftools/gperftools ), vous pouvez créer un lien avec celui-ci, ce qui donne un programme qui est généralement un peu plus rapide et utilise un peu moins de mémoire. Sur certains systèmes UNIX, vous pouvez utiliser une précharge, par exemple
Utilisation de base: générer et imprimer tous les nombres jusqu'à la cible:
Options:
-a
Imprimez également tous les nombres générés lors de l'élaboration de la cible-c
Imprimez également tous les nombres générés en commençant par un "carry" (dup)-f
Rechercher et imprimer le premier nombre au-delà de la cible qui n'a pas été généré-s
Arrêter si la cible est générée même si tous les numéros précédents n'ont pas été générés-S
Comme-s
et-f
dans une boucle automatique. Dès que la cible est générée, trouvez le premier numéro non encore généré et faites que la nouvelle cible-E
Ne quittez pas immédiatement lorsque l'objectif est atteint. Terminez d'abord toutes les cordes de la longueur actuelle-O
Ne sortez pas les chaînes pour tous les nombres jusqu'à la cible. juste la chaîne pour la cible-o
Instructions autorisées (par défaut à+-*/:
-b num
Littéral le plus bas pouvant être poussé (par défaut0
)-B num
Littéral le plus élevé pouvant être poussé (par défaut9
)-r num
Le résultat intermédiaire le plus bas autorisé. Utilisé pour éviter les débordements. (par défautINT32_MIN
,-2147483648
-R num
Le résultat intermédiaire le plus élevé autorisé. Utilisé pour éviter les débordements. (par défautINT32_MAX
,2147483647
-m memory
(linux uniquement) quitte quand approximativement autant de mémoire supplémentaire a été allouéeQuelques combinaisons d'options intéressantes:
Générez tous les nombres jusqu'à la cible et calculez le plus petit nombre qui nécessite un générateur plus long que tous ces nombres:
Générer uniquement la cible (-s), imprimer uniquement la cible (-O)
Trouvez le nombre le plus élevé qui peut être généré sur votre système en fonction des contraintes de temps et / ou de mémoire (cela entraînera une insuffisance de mémoire de votre système si vous le laissez en cours d'exécution. Soustrayez 1 de la dernière sortie "recherche" que vous voyez comme la dernière valeur sûre ):
Générez des solutions sans jamais utiliser de résultats intermédiaires négatifs (
30932
est la première valeur qui nécessite des résultats intermédiaires négatifs pour la chaîne la plus courte):Générez des solutions sans jamais pousser
0
(cela ne semble pas conduire à des solutions sous-optimales):Générez des solutions comprenant
a..f (10..15)
:Générer des solutions sans utiliser dup
:
(ajouter-r0
car les valeurs intermédiaires négatives ne sont jamais intéressantes dans ce cas)Trouvez la première valeur qui ne peut être générée pour une longueur de chaîne donnée en utilisant seulement
+
,-
,*
et/
:Cela générera en fait les premiers termes de https://oeis.org/A181898 , mais commencera à diverger
14771
car nous utilisons la division tronquée afin que le nombre puisse être fait avec une chaîne de longueur 13 au lieu de 15 comme la série OEIS attend:au lieu de
Étant donné que sans division de troncature semble inutile, la série OEIS peut être mieux générée en utilisant
En supposant que la division reste inutile, cela m'a donné 3 termes supplémentaires avant de perdre la mémoire:
Une autre version de ce programme stockant une partie des données dans des fichiers externes ajoute 135153107 et 675854293, après quoi tous les entiers 32 bits ont été générés.
befour.cpp
Quelques cas de test:
1: 1: 1
11: 3: 29+
26: 5: 29*8+
27: 3: 39*
100: 5: 19+:*
2431: 9: 56*9*9*1+
3727: 9: 69*7+:*6+
86387: 11: 67*:*1-7*7*
265729: 11: 39*:*:*2/9+
265620: 13: 99*::*6/*7+3*
1921600: 9: 77*:*:*3/
21523360: 9: 99*:*:*2/
57168721: 11: 99*6+:*8-:*
30932: 11: 159*-:4*:*+
la source
38950002
votre programme donne89*7+:::**1-*
, ce qui est assez bon, mais vous pouvez le faire299*-::*:*+
pour plus court. Je pense que cela confirme les doutes que j'avais sur les nombres négatifs ...int main(int argc, char const* const* argv)
Je ne connais pas mieux C que le Joe moyen mais qu'est-ce que c'est? un pointeur const vers un pointeur const vers un caractère? Ne devrait-il pas en êtrechar const *argv[]
ainsi (ouchar const **argv
si vous êtes aussi hardcore)?Node JavaScript Brute Force
Fichier programme bfCodes.js
Sous Windows
cmd.exe
cmd.exe
à l'aide du raccourci et vérifiez que l'invite DOS commence par le répertoire de travailOptimisation
L'algorithme parcourt toutes les combinaisons de caractères befunge en commençant par une chaîne de code de longueur 1. Considérez-le comme faisant tourner un odomètre de base 15 à partir du chiffre le moins significatif. Les chiffres d'ordre supérieur cliquent avec une lenteur croissante.
bfCodes
n'évalue pas le code généré qui rendrait la longueur de pile nulle ou négative ou laisserait plus d'un nombre sur la pile dans le but d'optimiser la vitesse d'exécution.Le problème de la force brute
Pour un jeu de codes de 15 caractères, le temps nécessaire pour parcourir toutes les combinaisons d'une longueur donnée est donné par
c'est-à-dire que si votre programme s'exécute quinze fois plus vite que le mien, vous ne pourrez vérifier qu'une seule chaîne de code de caractères en même temps. Pour vérifier deux autres caractères en même temps, un programme devrait s'exécuter 225 fois plus rapidement. Le temps pris avec une approche par force brute augmente de façon exponentielle à mesure que la longueur des chaînes de code augmente. Et la grandeur d'un nombre indique nécessairement le nombre d'octets befunge nécessaires pour le générer.
Quelques chiffres.
Temps approximatifs pour générer une liste de codes sur un bloc-notes Windows 7 32 bits pour les nombres entiers jusqu'à
Pour générer un befunge pour 3727 (soit 66 carrés plus 6) en soi, il a fallu 1 heure 47 minutes et généré
578*+:*6+
Génération de code optimale
La génération de befunge pour les nombres sans vérifier les longueurs les plus courtes est relativement simple. En utilisant un algorithme récursif qui a utilisé des racines carrées entières et des restes, les codages pour les nombres jusqu'à 132 ont pris environ 3 ms au lieu de 28 secondes. Ils n'étaient pas optimaux. En raison de la façon dont il fonctionnait, cet algorithme particulier a produit
638:*-:*+
pour 3727 en environ 1 ms (au lieu d'une heure environ), ce qui s'est avéré être optimal.Le problème avec la fourniture d'une méthode sans force brute prouve qu'elle est optimale dans tous les cas. Bonne chance!
la source
+-*/
aux nœuds internes0-9
et:
aux feuilles (et:
ne peut pas être le plus à gauche). Générez et évaluez donc tous les arbres valides de taille 2 * n + 1 à l'étape n (n commence à 0) et convertissez-les en chaînes si nécessaireJavascript
Whant peut être fait avec un extrait JS? Dans ma machine, Firefox 64 bits, 416 en 60 secondes
la source