Lua 5.3, 57522
Aslında soru sorulduğunda bu konuda çalışmaya başladım, ancak Brain-Flak yıldönümüne kadar bunu unuttum.
-- 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)
Bilinen yararlı işlevlerin daha basit sayıları iyi temsil etmekten daha büyük sayılar oluşturmak için kullanıldığı diğer cevaplara benzer bir fikir.
Bir fark, alt sayıları daha küçük sayılar olarak çözmek yerine, kısa sayıları temsil eden sayılar açısından alt sorunları çözüyorum. Bunun, negatif sayılardan faydalanmanın yanı sıra daha küçük sayılarla daha büyük sayılarla temsil edildiği durumu ele almanın daha zarif olduğunu düşünüyorum.
Ayrıca, belirli bir boyutta temsil edilebilecek tüm sayıları bulmaya çalışmak, belirli bir sayıyı mümkün olan en kısa sürede göstermeye çalışmak, aslında belirli hesaplamaları basitleştirir. Bir sayıya uygulanıp uygulanamayacağını görmek için bir formülü tersine çalışmak yerine, formül ileriye doğru çalışabilir ve her numaraya uygulanabilir.
Diğer bir fark, bilinen çözümlerin iki parça halinde depolanmasıdır - bir (isteğe bağlı) "önek" ve "sonek" (daha fazlası gibi). Verilen sayıyı hesaplarken önek değerinin göz ardı edilmesi beklenir - önek sadece çalıştırılacak son eki ayarlayan kodu içerir (genellikle yığına bir veya daha fazla şey iterek). Böylece, bir önek ve bir sonek verildiğinde, karşılık gelen sayı istifin üzerine itilebilir prefix(suffix)
.
Bu bölünme temel olarak, unpack
Buğday Sihirbazı'nın cevabındaki fonksiyonla aynı problemi çözer . Kodu <...>
daha sonra geri almak için yalnızca kodla değiştirmek yerine , bu kod basitçe ön eke eklenir.
Bazı durumlarda, önek aslında değerlendirilir (esas olarak sahte üstelleştirme işlemi için), bu nedenle de değeri kaydedilir. Ancak, jeneratör belirli sayılar oluşturmaya çalışmadığından, bu gerçekten büyük bir soruna neden olmaz. Teorik olarak, farklı önek değerlerine sahip olması nedeniyle önbellekte gereksiz olmayacak aynı uzunlukta iki kod parçası olabileceği ve aynı sayıyı üretebileceği anlaşılmaktadır. Bunun için muhasebe zahmetine girmedim, çünkü pek de önemli değil (en azından bu alanda).
Sadece daha fazla vaka ekleyerek bayt sayımını azaltmanın kolay olacağını hayal ediyorum, ama şu an için yeterli zamanım oldu.
1000000’e koştum, ancak yalnızca 100000’e kadar kontrol sağladı.
Verilen primerlerde pastebin çıktısı.
2n
ise4^n catalan(n)
.