Explicación
Befunge es un programa bidimensional que utiliza pilas .
Eso significa que, para hacer 5 + 6, escribes 56+
, lo que significa:
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)
Sin embargo, no podemos insertar el número 56
directamente en la pila.
Para ello, debemos escribir 78*
en su lugar, lo que multiplica 7
y 8
y empuja el producto en la pila.
Detalles
Para cada número de 1
an
, encontrar una cadena formada únicamente por los caracteres: 0123456789+-*/:
(yo no uso %
de módulo).
El objetivo es encontrar la cadena más corta que pueda representar el número, utilizando el formato descrito anteriormente.
Por ejemplo, si la entrada es 123
, entonces la salida sería 67*9:*+
. La salida debe evaluarse de izquierda a derecha.
Si hay más de una salida aceptable (por ejemplo, 99*67*+
también es aceptable), se puede imprimir cualquiera (no hay bonificación por imprimirlas todas).
Explicación adicional
Si aún no comprende cómo se 67*9:*+
evalúa 123
, aquí hay una explicación detallada.
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
El programa necesita encontrar la cadena más corta que pueda representar la entrada (número), utilizando el formato especificado anteriormente.
PUNTUACIÓN
- Ya lo hemos hecho en la menor cantidad de código . Esta vez, el tamaño no importa.
- Su idioma de elección debe tener un compilador / intérprete gratuito para mi sistema operativo (Windows 7 Enterprise).
- Bonificación si incluye el enlace al compilador / intérprete (soy demasiado vago).
- Si es posible, incluya un temporizador para mi conveniencia. La salida del temporizador es válida.
- El puntaje será el más grande
n
en 1 minuto. - Eso significa que el programa necesita imprimir la representación requerida de
1
adelante en adelante. - Sin codificación rígida, excepto
0
para9
.
(más) ESPECIFICOS
- El programa no es válido si genera una cadena más larga que la necesaria para cualquier número.
1/0=ERROR
5/2=2
,(-5)/2=-2
,(-5)/(-2)=2
,5/(-2)=-2
Desambiguación
El -
es second-top minus top
, lo que significa que 92-
vuelve 7
.
Del mismo modo, el /
es second-top divide top
, lo que significa que 92/
vuelve 4
.
Programa de muestra
Lua
Utiliza la búsqueda de profundidad primero.
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
fuente
56
directamente a la pila, ¿cómo podemos empujar78
a la pila?56
cincuenta y seis directamente en la pila, pero podemos empujar7
siete y8
ocho por separado en la pila.56
en Befunge, estás presionando los dígitos , por lo que terminas con una pila de[5, 6]
. Para obtener el número 56, debes empujar7
, luego8
a la pila, y luego multiplicarlos para obtener el número 56 en la pila.:
hace las cosas mucho más complicadas, por lo que recomendaría proporcionar una buena lista de casos de prueba, por ejemplo86387
Respuestas:
C ++, explotando toda la memoria en una computadora cerca de usted
Genera la cadena más corta donde el cálculo en ninguna parte causa un desbordamiento de un entero de 32 bits con signo (por lo que todos los resultados intermedios están en el rango
[-2147483648, 2147483647]
En mi sistema, esto genera una solución para todos los números hasta
483432
30 segundos inclusive, utilizando una memoria de 1.8G. Incluso números más altos explotarán rápidamente el uso de la memoria. El número más alto que puedo manejar en mi sistema es5113906
. El cálculo lleva casi 9 minutos y 24 GB. Cuando termina internamente, tiene una solución para los398499338
valores, aproximadamente el 9% de todos los enteros de 32 bits (positivo y negativo)Necesita un compilador de C ++ 11. En Linux compila con:
Agregue
-DINT64
como una opción para usar un rango entero de 64 bits en lugar de 32 bits para obtener resultados intermedios (esto usará aproximadamente un 50% más de tiempo y memoria). Esto necesita un tipo incorporado de 128 bits. Es posible que deba cambiar el tipo de gcc__int128
. Ningún resultado en al menos el rango[1..483432]
cambia al permitir resultados intermedios más grandes.Añadir
-DOVERFLOW
como una opción para no usar un tipo entero más grande para verificar el desbordamiento. Esto tiene el efecto de permitir el desbordamiento y el ajuste del valor.Si su sistema tiene tcmalloc ( https://github.com/gperftools/gperftools ) puede vincularlo con un programa que generalmente es un poco más rápido y usa un poco menos de memoria. En algunos sistemas UNIX puede usar una precarga, p. Ej.
Uso básico: generar e imprimir todos los números hasta el objetivo:
Opciones:
-a
Imprima también todos los números que se generaron al calcular el objetivo-c
Imprima también todos los números que se generaron comenzando con un "carry" (dup)-f
Encuentre e imprima el primer número más allá del objetivo que no se generó-s
Deténgase si se genera el objetivo, incluso si no se generaron todos los números anteriores-S
Me gusta-s
y-f
en un bucle automático. Tan pronto como se genere el objetivo, encuentre el primer número que aún no se ha generado y haga que el nuevo objetivo-E
No salgas de inmediato cuando alcances la meta. Primero termine todas las cadenas de la longitud actual-O
No envíe las cadenas para todos los números hasta el objetivo. solo la cadena para el objetivo-o
Instrucciones permitidas (el valor predeterminado es+-*/:
-b num
Literal más bajo que se puede empujar (el valor predeterminado es0
)-B num
Literal más alto que se puede empujar (el valor predeterminado es9
)-r num
El resultado intermedio más bajo permitido. Se usa para evitar el desbordamiento. (por defectoINT32_MIN
,-2147483648
-R num
El resultado intermedio más alto permitido. Se usa para evitar el desbordamiento. (por defectoINT32_MAX
,2147483647
-m memory
(solo Linux) sale cuando se ha asignado aproximadamente esta cantidad de memoria adicionalAlgunas combinaciones de opciones interesantes:
Genere todos los números hasta el objetivo y calcule el número más pequeño que necesita un generador más largo que todos estos números:
Generar solo objetivo (-s), imprimir solo objetivo (-O)
Encuentre el número más alto que se puede generar en su sistema dadas las limitaciones de tiempo y / o memoria (esto hará que su sistema se quede sin memoria si lo deja en funcionamiento. Reste 1 de la última salida "en busca" que ve como el último valor seguro ):
Genere soluciones sin usar resultados intermedios negativos (
30932
es el primer valor que necesita resultados intermedios negativos para la cadena más corta):Genere soluciones sin presionar nunca
0
(esto no parece conducir a soluciones subóptimas):Generar soluciones que incluyen
a..f (10..15)
:Genere soluciones sin usar dup
:
(agregue-r0
ya que los valores intermedios negativos nunca son interesantes para este caso)Encuentra el primer valor que no puede ser generada por una longitud de cadena dada usando simplemente
+
,-
,*
y/
:De hecho, esto generará los primeros términos de https://oeis.org/A181898 , pero comenzará a divergir
14771
porque usamos la división truncada para que el número se pueda hacer con una cadena de longitud 13 en lugar de la longitud 15 como la serie OEIS espera:en lugar de
Dado que sin la división de truncamiento parece inútil, la serie OEIS puede generarse mejor utilizando
Suponiendo que la división sigue siendo inútil, esto me dio 3 términos adicionales antes de que me quedara sin memoria:
Otra versión de este programa que almacena parte de los datos en archivos externos agrega 135153107 y 675854293, después de lo cual se han generado todos los enteros de 32 bits.
befour.cpp
Algunos casos de prueba:
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*:*+
fuente
38950002
su programa da89*7+:::**1-*
, que es bastante bueno, pero puede hacerlo299*-::*:*+
por más corto. Creo que esto confirma las dudas que tenía sobre los números negativos ...int main(int argc, char const* const* argv)
No sé C mejor que el Joe promedio, pero ¿qué es esto? un puntero constante a un puntero constante a un char? ¿No debería ser máschar const *argv[]
o menos (ochar const **argv
si eres tan duro)?Nodo JavaScript Fuerza Bruta
Archivo de programa bfCodes.js
Ejecutando bajo Windows
cmd.exe
cmd.exe
usando el acceso directo y verifique que el indicador de DOS comience con el directorio de trabajoMejoramiento
El algoritmo recorre todas las combinaciones de caracteres iniciales comenzando con una cadena de código de longitud 1. Piense que gira un odómetro de base 15 desde el dígito menos significativo. Los dígitos de orden superior hacen clic con mayor lentitud.
bfCodes
no evalúa el código generado que haría que la longitud de la pila fuera cero o negativa o dejara más de un número en la pila en un intento de optimizar la velocidad de ejecución.El problema de la fuerza bruta
Para un conjunto de códigos de 15 caracteres, el tiempo que lleva ejecutar todas las combinaciones de una longitud determinada es dado por
es decir, si su programa se ejecuta quince veces más rápido que el mío, solo podrá verificar una cadena de código de caracteres adicional al mismo tiempo. Para verificar dos caracteres más al mismo tiempo, un programa necesitaría ejecutarse 225 veces más rápido. El tiempo empleado con un enfoque de fuerza bruta aumenta exponencialmente a medida que aumenta la longitud de las cadenas de código. Y la magnitud de un número indica necesariamente el número de bytes de inicio necesarios para generarlo.
Algunas figuras.
Tiempos aproximados para generar una lista de códigos en un bloc de notas de Windows 7 de 32 bits para enteros de hasta
Generar befunge para 3727 (que es 66 al cuadrado más 6) por sí solo tomó 1 hora 47 minutos y generó
578*+:*6+
Generación óptima de código
Generar befunge para números sin verificar longitudes más cortas es relativamente simple. Usando un algoritmo recursivo que usaba raíces cuadradas enteras y restos, las codificaciones para números de hasta 132 tomaron aproximadamente 3 ms en lugar de 28 segundos. No fueron óptimos. Debido a la forma en que funcionó, este algoritmo en particular produjo
638:*-:*+
3727 en aproximadamente 1 ms (en lugar de una hora más o menos) que resultó ser óptimo.El problema de proporcionar un método de fuerza no bruta es demostrar que es óptimo en todos los casos. ¡Buena suerte!
fuente
+-*/
en los nodos interiores y0-9
y:
en las hojas (y:
no se puede estar más a la izquierda). Por lo tanto, genere y evalúe todos los árboles válidos de tamaño 2 * n + 1 en el paso n (n comienza desde 0) yJavaScript
¿Qué se puede hacer con un fragmento de JS? En mi máquina, Firefox 64 bit, 416 en 60 segundos
fuente