루아 5.3, 57522
질문이 게시되었을 때 실제로이 작업을 시작했지만 Brain-Flak 기념일까지 잊어 버렸습니다.
-- 64 gives all results through 10000 (should run in about 1 second)
-- 78 gives all results through 100000 (should run in about 20 seconds)
-- 90 gives all results through 1000000 (should run in about 200 seconds)
-- Note: Timings may not be accurate, as the are not updated every time new cases are added.
local k_max_len = 64
local k_limit = 10000
local pre = os.clock()
local function compute_multiplier_helper(prefix, suffix, m)
if m == 2 then
prefix[#prefix + 1] = "("
suffix[#suffix + 1] = "){}"
elseif m % 2 == 0 then
prefix[#prefix + 1] = "("
compute_multiplier_helper(prefix, suffix, m // 2)
suffix[#suffix + 1] = "){}"
else
suffix[#suffix + 1] = ")"
compute_multiplier_helper(prefix, suffix, m - 1)
prefix[#prefix + 1] = "("
suffix[#suffix + 1] = "{}"
end
end
local function compute_multiplier(m)
local prefix = {}
local suffix = {}
compute_multiplier_helper(prefix, suffix, m)
return table.concat(prefix), table.concat(suffix)
end
local multipliers = {}
for m = 2, k_limit do
-- Including all factors, not just primes.
-- This did improve a few numbers, although none in the ppcg test set.
local prefix, suffix = compute_multiplier(m)
local mult = {prefix = prefix, suffix = suffix, m = m, cost = #prefix + #suffix}
table.insert(multipliers, mult)
end
table.sort(multipliers, function(a, b) return a.cost < b.cost end)
local poly_multipliers = {}
poly_multipliers[1] = {m = 1, s = "({})", l = 4}
for m = 2, k_limit do
local prefix, suffix = compute_multiplier(m)
local s = prefix .. "({})" .. suffix
assert(#s <= 4 * m)
poly_multipliers[m] = {m = m, s = s, l = #s}
end
poly_multipliers[k_limit + 1] = {m = 0, s = "", l = 0}
table.sort(poly_multipliers, function(a, b) return a.l < b.l end)
local pcache = {}
local plen_cache = {}
local function register_push(prefix, suffix, value, pvalue)
if value > 1500000 or value < -1500000 then return end
local old_res = pcache[value]
if old_res == nil then
local res = {prefix = prefix, suffix = suffix, value = value, pvalue = pvalue}
pcache[value] = res
local length = #prefix + #suffix
local lcache = plen_cache[length]
if lcache == nil then
lcache = {}
plen_cache[length] = lcache
end
lcache[#lcache + 1] = res
end
end
local function get_pushes(length)
return ipairs(plen_cache[length] or {})
end
register_push("", "()", 1, 0)
register_push("", "<()>", 0, 0)
local function triangle(n)
return (n * (n + 1)) // 2
end
local function process(length)
-- basic
for _, res in get_pushes(length - 2) do
register_push(res.prefix, res.suffix .. "()", res.value + 1, res.pvalue)
register_push(res.prefix, "[" .. res.suffix .. "]", -res.value, res.pvalue)
end
-- multiplication by constant (precomputed)
for _, mult in ipairs(multipliers) do
local cost = mult.cost
if length - cost >= 4 then
local m, prefix, suffix = mult.m, mult.prefix, mult.suffix
for _, pus in get_pushes(length - cost) do
local name = prefix .. pus.suffix .. suffix
register_push(pus.prefix, name, pus.value * m, pus.pvalue)
end
else
break
end
end
-- residue 2 mod3 trick (Neil)
-- ((n)()){}{}
-- (n) -- push n
-- ( ()) -- push n + 1
-- {}{} -- (n + 1) + (n + 1) + n
if length - 10 >= 2 then
for _, res in get_pushes(length - 10) do
local name = "((" .. res.suffix .. ")()){}{}"
register_push(res.prefix, name, 3 * res.value + 2, res.pvalue)
end
end
-- residue 1 mod3 trick (Wheat Wizard)
-- ((n)()()){}{}
-- (n) -- push n
-- ( ()()) -- push n + 2
-- {}{} -- (n + 2) + (n + 2) + n
-- not useful, but fast...
if length - 12 >= 2 then
for _, res in get_pushes(length - 12) do
local name = "((" .. res.suffix .. ")()()){}{}"
register_push(res.prefix, name, 3 * res.value + 4, res.pvalue)
end
end
-- residue 2 mod5 trick (tehtmi)
-- (((n)){}()){}{}
-- (n) -- push n
-- ( ) -- push n
-- ( {}()) -- push 2n + 1
-- {}{} -- (2n + 1) + (2n + 1) + n
-- [[
if length - 14 >= 2 then
for _, res in get_pushes(length - 14) do
local name = "(((" .. res.suffix .. ")){}()){}{}"
register_push(res.prefix, name, 5 * res.value + 2, res.pvalue)
end
end
-- ]]
-- residue 4 mod5 trick (tehtmi)
-- (((n)()){}){}{}
-- (n) -- push n
-- ( ()) -- push n + 1
-- ( {}) -- push 2n + 2
-- {}{} -- (2n + 2) + (2n + 2) + n
-- [[
if length - 14 >= 2 then
for _, res in get_pushes(length - 14) do
local name = "(((" .. res.suffix .. ")()){}){}{}"
register_push(res.prefix, name, 5 * res.value + 4, res.pvalue)
end
end
-- ]]
-- residue 6 mod7 trick (tehtmi)
-- ((((n)())){}{}){}{}
-- (n) -- push n
-- ( ()) -- push n + 1
-- ( ) -- push n + 1
-- ( {}{}) -- push 3n + 3
-- {}{} -- (3n + 3) + (3n + 3) + n
-- [[
if length - 18 >= 2 then
for _, res in get_pushes(length - 18) do
local name = "((((" .. res.suffix .. ")())){}{}){}{}"
register_push(res.prefix, name, 7 * res.value + 6, res.pvalue)
end
end
--]]
-- residue 4 mod7 trick (tehtmi)
-- ((((n))()){}{}){}{}
-- (n) -- push n
-- ( ) -- push n
-- ( ()) -- push n + 1
-- ( {}{}) -- push 3n + 2
-- {}{} -- (3n + 2) + (3n + 2) + n
-- [[
if length - 18 >= 2 then
for _, res in get_pushes(length - 18) do
local name = "((((" .. res.suffix .. "))()){}{}){}{}"
register_push(res.prefix, name, 7 * res.value + 4, res.pvalue)
end
end
--]]
-- residue 2 mod7 trick (tehtmi)
-- ((((n))){}{}()){}{}
-- (n) -- push n
-- ( ) -- push n
-- ( ) -- push n
-- ( {}{}()) -- push 3n + 1
-- {}{} -- (3n + 1) + (3n + 1) + n
-- [[
if length - 18 >= 2 then
for _, res in get_pushes(length - 18) do
local name = "((((" .. res.suffix .. "))){}{}()){}{}"
register_push(res.prefix, name, 7 * res.value + 2, res.pvalue)
end
end
--]]
-- triangle numbers (?)
--(n){({}[()])}{}
--(n) -- push n
-- { } -- sum and repeat
-- ( ) -- push
-- {}[()] -- top - 1
-- {} -- pop 0
if length - 14 >= 2 then
for _, res in get_pushes(length - 14) do
if res.value > 0 then
local code = "{({}[()])}{}"
register_push(res.prefix .. "(" .. res.suffix .. ")", code, triangle(res.value - 1), res.pvalue + res.value)
register_push(res.prefix, "(" .. res.suffix .. ")" .. code, triangle(res.value), res.pvalue)
register_push("", res.prefix .. "(" .. res.suffix .. ")" .. code, triangle(res.value) + res.pvalue, 0)
end
end
end
-- negative triangle numbers (tehtmi)
--(n){({}())}{}
--(n) -- push n
-- { } -- sum and repeat
-- ( ) -- push
-- {}() -- top + 1
-- {} -- pop 0
if length - 12 >= 2 then
for _, res in get_pushes(length - 12) do
if res.value < 0 then
local code = "{({}())}{}"
register_push(res.prefix .. "(" .. res.suffix .. ")", code, -triangle(-res.value - 1), res.pvalue + res.value)
register_push(res.prefix, "(" .. res.suffix .. ")" .. code, -triangle(-res.value), res.pvalue)
register_push("", res.prefix .. "(" .. res.suffix .. ")" .. code, -triangle(-res.value) + res.pvalue, 0)
end
end
end
-- cubic (tehtmi)
-- (n){(({}[()])){({}[()])}{}}{}
-- (n^3-3*n^2+8*n-6)/6
-- (-6 + n*(8 + n*(-3 + n)))/6
--[[ superceded by negative cubic because
it is the same cost of -ncubic(-n)
if length - 28 >= 2 then
for _, res in get_pushes(length - 28) do
if res.value > 0 then
local code = "{(({}[()])){({}[()])}{}}{}"
local v = res.value + 1
v = (-6 + v*(8 + v*(-3 + v)))//6
register_push(res.prefix .. "(" .. res.suffix .. ")", code, v - res.value, res.pvalue + res.value)
register_push(res.prefix, "(" .. res.suffix .. ")" .. code, v, res.pvalue)
register_push("", res.prefix .. "(" .. res.suffix .. ")" .. code, v + res.pvalue, 0)
end
end
end
--]]
-- negative cubic (tehtmi)
-- (n){(({}())){({}())}{}}{}
-- (n^3-3*n^2+8*n-6)/6
-- (-6 + n*(8 + n*(-3 + n)))/6
-- [[
if length - 24 >= 2 then
for _, res in get_pushes(length - 24) do
if res.value < 0 then
local code = "{(({}())){({}())}{}}{}"
local v = -res.value + 1
v = (-6 + v*(8 + v*(-3 + v)))//6
v = -v
register_push(res.prefix .. "(" .. res.suffix .. ")", code, v - res.value, res.pvalue + res.value)
register_push(res.prefix, "(" .. res.suffix .. ")" .. code, v, res.pvalue)
register_push("", res.prefix .. "(" .. res.suffix .. ")" .. code, v + res.pvalue, 0)
end
end
end
--]]
-- polynomial (Wheat Wizard, modified by tehtmi)
-- <(n)>{A({}[()])B}{} where A, B are ({})({})({})... repeated a, b times
-- <(n)> -- push n (without adding)
-- { } -- repeat until top is zero
-- A -- top * a
-- ({}[()]) -- top = top - 1; += top - 1
-- B -- (top - 1) * b
-- {} -- pop 0
-- triangular numbers are with a = b = 0
-- from B and base:
-- (n - 1) * (B + 1) * (n - 2) * (B + 1) * ...
-- (B + 1) * (1 + ... + n - 1)
-- (B + 1) * n * (n - 1) / 2
-- from A:
-- n * A + (n - 1) * A + ...
-- A * (1 + ... n)
-- A * (n + 1) * n / 2
-- total: (B + 1) * n * (n - 1) / 2 + A * (n + 1) * n / 2
-- [(A + B + 1) * n^2 + (A - B - 1) * n] / 2
-- S := 4 * (A + B)
-- [[
if length - 18 >= 2 then
for S = 4, length - 14, 4 do
for _, res in get_pushes(length - 14 - S) do
if res.value > 0 then
for _, A in ipairs(poly_multipliers) do
if A.l > S then
break
end
for _, B in ipairs(poly_multipliers) do
if A.l + B.l < S then
-- continue
elseif A.l + B.l > S then
break
else
local a = A.m
local b = B.m
local logic = "{" .. A.s .. "({}[()])" .. B.s .. "}{}"
local v = res.value
v = ((a + b + 1) * v * v + (a - b - 1) * v) // 2
register_push(res.prefix .. "(" .. res.suffix .. ")", logic, v, res.pvalue + res.value)
register_push(res.prefix, "(" .. res.suffix .. ")" .. logic, v + res.value, res.pvalue)
register_push("", res.prefix .. "(" .. res.suffix .. ")" .. logic, v + res.value + res.pvalue, 0)
end
end
end
end
end
end
end
--]]
-- negative polynomial (tehtmi)
-- <(n)>{A({}())B}{}
-- [[
if length - 16 >= 2 then
for S = 4, length - 12, 4 do
for _, res in get_pushes(length - 12 - S) do
if res.value < 0 then
for _, A in ipairs(poly_multipliers) do
if A.l > S then
break
end
for _, B in ipairs(poly_multipliers) do
if A.l + B.l < S then
-- continue
elseif A.l + B.l > S then
break
else
local a = A.m
local b = B.m
local logic = "{" .. A.s .. "({}())" .. B.s .. "}{}"
local v = -res.value
v = ((a + b + 1) * v * v + (a - b - 1) * v) // -2
register_push(res.prefix .. "(" .. res.suffix .. ")", logic, v, res.pvalue + res.value)
register_push(res.prefix, "(" .. res.suffix .. ")" .. logic, v + res.value, res.pvalue)
register_push("", res.prefix .. "(" .. res.suffix .. ")" .. logic, v + res.value + res.pvalue, 0)
end
end
end
end
end
end
end
--]]
-- addition
-- [[
if length >= 4 then
for part1 = 4, length // 2, 2 do
for _, res1 in get_pushes(part1) do
for _, res2 in get_pushes(length - part1) do
register_push(res2.prefix .. res1.prefix, res1.suffix .. res2.suffix, res1.value + res2.value, res1.pvalue + res2.pvalue)
end
end
end
end
--]]
-- pseudo-exponentiation (tehtmi)
-- (n)<>(m){({}[()])<>(({}){})<>}{}<>{}
-- (n)<>(m) -- push n and m on opposite stacks
-- { } -- sum and repeat
-- ({}[()]) -- top(m) - 1
-- <>(({}){})<> -- n = 2*n; += n
-- {} -- pop 0
-- <> -- swap to result
-- {} -- pop and add n
-- [[
if length - 34 >= 4 then
local subl = length - 34
for part1 = 2, subl - 2, 2 do
for _, res2 in get_pushes(part1) do
local b = res2.value
if b > 0 and b < 55 then -- overflow could be a problem, so bound...
for _, res1 in get_pushes(subl - part1) do
-- 2n + 4n + 8n + ... + (2^m)*n + 2^m * n
-- n( 2 + 4 + 8 + .. 2^m + 2^m)
-- n( 3 * 2^m - 2 )
local a = res1.value
local body = "(" .. res1.suffix .. ")<>" .. res2.prefix .. "(" .. res2.suffix .. "){({}[()])<>(({}){})<>}{}<>{}"
local v = a * (3 * (1 << b) - 2) + b * (b - 1) // 2 + a + b + res2.pvalue
register_push(res1.prefix, body, v, res1.pvalue)
register_push("", res1.prefix .. body, v + res1.pvalue, 0)
end
end
end
end
end
--]]
end
--print(os.clock(), "seconds (startup)")
local start = os.clock()
for i = 2, k_max_len - 2, 2 do
--print(i)
process(i)
end
plen_cache = nil
local final = {}
for i = 1, k_limit do
if pcache[i] ~= nil then
final[i] = pcache[i].prefix .. "(" .. pcache[i].suffix .. ")"
end
end
pcache = nil
-- hard coded to 10000 for ppcg test
local sieve = {}
for i = 1, 10000 do sieve[i] = true end
for i = 2, 10000 do
for j = i * i, 10000, i do
sieve[j] = false
end
end
--print(os.clock() - start, "seconds (calculation)")
--local bf = require("execute2")
local count = 0
local sum = 0
local sum2 = 0
local maxlen = 0
local pcount = 0
for i = 1, k_limit do
local res = final[i]
final[i] = nil
--print(i, #res, res)
--local ev = res and bf.eval1(bf.compile(res)) or -1; assert( res == nil or ev == i, string.format("Failed %d %s %d", i, res or "", ev))
if sieve[i] and i > 1000 then
sum = #res + sum
pcount = pcount + 1
end
if res then
sum2 = #res + sum2
maxlen = math.max(maxlen, #res)
count = count + 1
end
end
print("sum", sum)
--print("coverage", count / k_limit, "missing", k_limit - count)
--print("sum2", sum2)
--print("maxlen", maxlen)
assert(pcount == 1061)
알려진 유용한 함수를 사용하여 간단한 숫자를 잘 표현하여 더 큰 숫자를 만드는 다른 답변과 비슷한 아이디어입니다.
한 가지 차이점은 작은 숫자로 하위 문제를 해결하는 대신 표현이 짧은 숫자로 하위 문제를 해결한다는 것입니다. 나는 이것이 더 작은 숫자가 더 큰 숫자로 표현되는 경우를 처리하는 것뿐만 아니라 음수를 이용하는 것이 더 우아하다고 생각합니다.
또한 가능한 한 빨리 특정 숫자를 나타내려고 시도하지 않고 특정 크기로 표현할 수있는 모든 숫자를 찾으려고하면 실제로 특정 계산이 간단 해집니다. 수식을 반대로 적용하여 숫자에 적용 할 수 있는지 확인하는 대신 수식을 앞으로 처리하여 모든 숫자에 적용 할 수 있습니다.
또 다른 차이점은 알려진 솔루션이 (선택적) "접두사"와 "접미사"(접두사와 유사)라는 두 부분으로 저장되어 있다는 것입니다. 접두사의 평가는 주어진 숫자를 계산할 때 무시 될 것으로 예상됩니다. 접두사는 단지 접미사가 실행되도록 설정하는 코드를 포함합니다 (일반적으로 하나 이상의 항목을 스택으로 푸시). 따라서 접두사와 접미사가 주어지면 해당 번호를로 스택에 푸시 할 수 있습니다 prefix(suffix)
.
이 분할은 기본적으로 unpack
밀 마법사의 답변에서 와 동일한 문제를 해결합니다 . <...>
나중에이 작업을 취소하기 위해 코드를 래핑하는 대신 이러한 코드가 접두사에 추가됩니다.
경우에 따라 접두사가 실제로 평가 (주로 의사 지수 연산의 경우)되므로 평가도 저장됩니다. 그러나 생성기가 특정 숫자를 생성하려고 시도하지 않기 때문에 이것이 실제로 큰 문제를 일으키지는 않습니다. 이론적으로는 동일한 접두어 평가로 인해 캐시에서 중복되지 않는 동일한 길이와 동일한 수의 코드가 두 개있을 수 있음을 의미합니다. 나는 (적어도이 도메인에서는)별로 중요하지 않은 것처럼 보이기 때문에 이것을 설명하지 않았습니다.
더 많은 사례를 추가하여 바이트 수를 쉽게 줄일 수 있다고 생각하지만 지금은 충분했습니다.
나는 1000000에 달했지만 최대 100000까지 위생 검사 만했습니다.
주어진 소수에 대한 출력의 Pastebin.
2n
가 유효한 프로그램의 수 는4^n catalan(n)
입니다.