Explicação
O Befunge é um programa bidimensional que utiliza pilhas .
Isso significa que, para fazer 5 + 6, você escreve 56+
, significando:
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)
No entanto, não podemos enviar o número 56
diretamente para a pilha.
Para isso, devemos escrever 78*
em vez disso, que se multiplica 7
e 8
e empurra o produto para a pilha.
Detalhes
Para cada número de 1
atén
, encontre uma sequência composta apenas por esses caracteres: 0123456789+-*/:
(eu não usaria o %
módulo.)
O objetivo é encontrar a string mais curta que possa representar o número, usando o formato descrito acima.
Por exemplo, se a entrada for 123
, a saída seria 67*9:*+
. A saída deve ser avaliada da esquerda para a direita.
Se houver mais de uma saída aceitável (por exemplo, 99*67*+
também é aceitável), qualquer uma poderá ser impressa (sem bônus por imprimir todas elas).
Explicação adicional
Se você ainda não entende como 67*9:*+
avalia 123
, aqui está uma explicação detalhada.
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
O programa precisa encontrar a string mais curta que possa representar a entrada (número), usando o formato especificado acima.
PONTUAÇÃO
- Já fizemos isso na menor quantidade de código . Desta vez, o tamanho não importa.
- Seu idioma de escolha deve ter um compilador / intérprete gratuito para o meu sistema operacional (Windows 7 Enterprise).
- Bônus se você incluir o link para o compilador / intérprete (estou com preguiça).
- Se possível, inclua um cronômetro para minha conveniência. A saída do temporizador é válida.
- A pontuação será a maior
n
em 1 minuto. - Isso significa que o programa precisa imprimir a representação necessária a partir de então
1
. - Sem codificação, exceto
0
para9
.
(mais) ESPECÍFICOS
- O programa é inválido se gerar uma sequência maior que o necessário para qualquer número.
1/0=ERROR
5/2=2
,(-5)/2=-2
,(-5)/(-2)=2
,5/(-2)=-2
Desambiguação
O -
é second-top minus top
, o que significa que 92-
retorna 7
.
Da mesma forma, o /
é second-top divide top
, o que significa que 92/
retorna 4
.
Programa de exemplo
Lua
Usa a pesquisa em profundidade.
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
fonte
56
diretamente para a pilha, como podemos empurrar78
para dentro da pilha?56
inserir 56 diretamente na pilha, mas podemos inserir7
sete e8
oito separadamente na pilha.56
com o Befunge, está pressionando os dígitos e acaba com uma pilha de[5, 6]
. Para obter o número 56, você tem que empurrar7
, em seguida,8
para a pilha e , em seguida, multiplicá-los para obter o número 56 na pilha.:
torna as coisas mais complicadas muito, então eu recomendo fornecer uma boa lista de casos de teste, por exemplo86387
Respostas:
C ++, explodindo toda a memória em um computador perto de você
Gera a string mais curta em que o cálculo em nenhum lugar causa um estouro de um número inteiro de 32 bits assinado (portanto, todos os resultados intermediários estão no intervalo
[-2147483648, 2147483647]
No meu sistema, isso gera uma solução para todos os números até
483432
30 segundos, inclusive, usando a memória 1.8G. Números ainda maiores explodirão rapidamente o uso da memória. O número mais alto que eu posso manipular no meu sistema é5113906
. O cálculo leva quase 9 minutos e 24 GB. Quando termina internamente, possui uma solução para398499338
valores, cerca de 9% de todos os números inteiros de 32 bits (positivos e negativos)Precisa de um compilador C ++ 11. No Linux, compile com:
Adicione
-DINT64
como opção usar um intervalo inteiro de 64 bits em vez de 32 bits para resultados intermediários (isso usará cerca de 50% mais tempo e memória). Isso precisa de um tipo interno de 128 bits. Pode ser necessário alterar o tipo de gcc__int128
. Nenhum resultado em pelo menos o intervalo[1..483432]
muda, permitindo resultados intermediários maiores.Adicione
-DOVERFLOW
como opção não usar um tipo inteiro maior para verificar se há excesso. Isso tem o efeito de permitir estouro e quebra de valor.Se o seu sistema tiver tcmalloc ( https://github.com/gperftools/gperftools ), você poderá vincular ao resultado, resultando em um programa geralmente mais rápido e que usa um pouco menos de memória. Em alguns sistemas UNIX, você pode usar uma pré-carga, por exemplo
Uso básico: gere e imprima todos os números até o destino:
Opções:
-a
Também imprima todos os números que foram gerados durante o trabalho de destino-c
Imprima também todos os números que foram gerados começando com um "carry" (dup)-f
Encontre e imprima o primeiro número além do destino que não foi gerado-s
Pare se o destino for gerado, mesmo que nem todos os números anteriores tenham sido gerados-S
Como-s
e-f
em um loop automático. Assim que o alvo for gerado, encontre o primeiro número ainda não gerado e faça com que o novo alvo-E
Não saia imediatamente quando o objetivo for alcançado. Primeiro termine todas as cordas do comprimento atual-O
Não produza as strings para todos os números até o destino. apenas a string para o alvo-o
Instruções permitidas (o padrão é+-*/:
-b num
Literal mais baixo que pode ser enviado (o padrão é0
)-B num
Literal mais alto que pode ser enviado (o padrão é9
)-r num
O menor resultado intermediário permitido. Usado para evitar o fluxo insuficiente. (o padrão éINT32_MIN
,-2147483648
-R num
O resultado intermediário mais alto permitido. Usado para evitar transbordamentos. (o padrão éINT32_MAX
,2147483647
-m memory
(somente linux) sai quando aproximadamente essa quantidade de memória extra é alocadaAlgumas combinações de opções interessantes:
Gere todos os números até o destino e calcule o menor número que precisa de um gerador mais longo do que todos esses números:
Gere apenas destino (-s), imprima apenas destino (-O)
Encontre o número mais alto que pode ser gerado no seu sistema, devido a restrições de tempo e / ou memória (isso deixará o sistema sem memória se você deixá-lo em execução. Subtraia 1 da última saída "procurando" que você vê como o último valor seguro ):
Gere soluções sem nunca usar resultados intermediários negativos (
30932
é o primeiro valor que precisa de resultados intermediários negativos para a string mais curta):Gere soluções sem pressionar
0
(isso não parece levar a soluções subótimas):Gere soluções, incluindo
a..f (10..15)
:Gere soluções sem usar dup
:
(adicione-r0
como valores intermediários negativos nunca são interessantes para este caso)Encontrar o primeiro valor que não pode ser gerado para um determinado comprimento da corda usando apenas
+
,-
,*
e/
:De fato, isso gerará os primeiros termos de https://oeis.org/A181898 , mas começará a divergir em
14771
porque usamos a divisão de truncamento para que o número possa ser feito com uma corda 13 de comprimento em vez de 15 como a série OEIS espera:ao invés de
Como sem divisão de truncamento parece inútil, a série OEIS pode ser melhor gerada usando
Supondo que a divisão permaneça inútil, isso me deu três termos extras antes de eu ficar sem memória:
Outra versão deste programa que armazena parte dos dados em arquivos externos adiciona 135153107 e 675854293, após o qual todos os números inteiros de 32 bits foram gerados.
befour.cpp
Alguns casos de teste:
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*:*+
fonte
38950002
o programa dá89*7+:::**1-*
, o que é muito bom, mas você pode fazer299*-::*:*+
por um tempo menor. Eu acho que isso confirma as dúvidas que eu tinha sobre os números negativos ...int main(int argc, char const* const* argv)
Não conheço C melhor que o Joe médio, mas o que é isso? um ponteiro const para um ponteiro const para um char? Não deveria serchar const *argv[]
assim (ouchar const **argv
se você é tão hardcore)?Força Bruta do Nó JavaScript
Arquivo de programa bfCodes.js
Executando no Windows
cmd.exe
cmd.exe
usando o atalho e verifique se o prompt do DOS começa com o diretório de trabalhoOtimização
O algoritmo percorre todas as combinações de caracteres anteriores, começando com uma sequência de códigos de comprimento 1. Pense nisso como girar um odômetro de base 15 a partir do dígito menos significativo. Dígitos de ordem superior clicam com o aumento da lentidão.
bfCodes
não avalia o código gerado que tornaria o comprimento da pilha zero ou negativo ou deixaria mais de um número na pilha na tentativa de otimizar a velocidade de execução.O Problema da Força Bruta
Para um conjunto de códigos de 15 caracteres, o tempo necessário para executar todas as combinações de um determinado comprimento é dado por
ou seja, se seu programa for executado quinze vezes mais rápido que o meu, você poderá verificar apenas uma sequência de códigos de caracteres adicionais ao mesmo tempo. Para verificar mais dois caracteres ao mesmo tempo, um programa precisaria executar 225 vezes mais rápido. O tempo gasto com uma abordagem de força bruta aumenta exponencialmente à medida que o comprimento das cadeias de código aumenta. E a magnitude de um número indica necessariamente o número de bytes necessários para gerá-lo.
Algumas figuras.
Tempos aproximados para gerar uma lista de códigos em um bloco de notas do Windows 7 de 32 bits para números inteiros até
Gerar befunge para 3727 (que é 66 ao quadrado mais 6) por si só levou 1 hora e 47 minutos e gerou
578*+:*6+
Geração de código ideal
Gerar befunge para números sem verificar os menores comprimentos é relativamente simples. Usando um algoritmo recursivo que usava raízes e números quadrados inteiros, as codificações para números de até 132 demoravam cerca de 3 ms em vez de 28 segundos. Eles não eram ótimos. Devido à maneira como ele funcionou, esse algoritmo específico foi produzido
638:*-:*+
para 3727 em cerca de 1 ms (em vez de uma hora), o que foi ótimo.A questão de fornecer um método de força não bruta está provando que é ideal em todos os casos. Boa sorte!
fonte
+-*/
nos nós internos0-9
e:
nas folhas (e:
não pode ser mais à esquerda). Então, gerar e avaliar todas árvore válida do tamanho 2 * n + 1 no passo n (n começa em 0) e convertê-los para um cordas quando necessárioJavascript
O Whant pode ser feito com um snippet JS? Na minha máquina, Firefox de 64 bits, 416 em 60 segundos
fonte