Golf Integer Brain-Flak

28

Liczby całkowite są uciążliwe do reprezentowania w Brain-Flak . Istnieje 8 operatorów:

()      Evaluates to 1, but does not push anything on any stack
[]      Evaluates to an indeterminate value for the purposes of this question
{}      Removes the top of the stack and evaluates to it
<>      Switches to or back from the alternate stack and evaluates to zero
(foo)   Pushes the value of the expression foo to the stack and evaluates to it
[foo]   Evaluates to the negation of foo
{foo}   Evaluates the expression foo until the top of the stack is zero
<foo>   Evaluates to zero but executes foo anyway

foomoże składać się z wielu operatorów, w którym to przypadku są one oceniane i sumowane. Na przykład (()())wypycha 2na stos (i ocenia 2też).

Oczywiście ten (()...())mechanizm nie jest użyteczny w Code Golf, ponieważ duże liczby zajmowałyby n*2+2bajty. Wyzwaniem jest więc napisanie programu lub funkcji, która wyświetli jak najmniej bajtów programu Brain-Flak, który wypchnie daną liczbę całkowitą dodatnią ndo aktywnego stosu. Ten program nie może przyjmować żadnych założeń dotyczących istniejącej zawartości stosów, więc nie może pozostawiać stosów wymienianych ani dodawać ani usuwać dodatkowych wartości ze stosów.

Chociaż Twój program lub funkcja musi mieć możliwość zwrócenia działającego programu Brain-Flak dla wszystkich danych wejściowych od 1 do 1 000 000, zwycięzcą zostanie program lub funkcja, która generuje najmniejszy zestaw odpowiednich programów Brain-Flak dla wszystkich 1061 liczb pierwszych od 1000 do 10 000 . Powinieneś zanotować całkowity rozmiar twoich wyników dla tych 1061 danych wejściowych jako część twojego przesłania. Twój program lub funkcja może zaakceptować liczbę całkowitą i zwrócić (ciąg) program Brain-Flak w dowolnym akceptowalnym formacie We / Wy. Więzy zostaną zerwane przy użyciu rozmiaru programu lub funkcji.

Neil
źródło
4
Tak jak uwaga: liczba prawidłowych programów długości 2nwynosi 4^n catalan(n).
Leaky Nun
2
Hmm, podoba mi się wyzwanie, ale myślę, że powinno być punktowane na nieznanych liczbach całkowitych. W przeciwnym razie programy liczb całkowitych są punktowane, a inne liczby całkowite mogą pozostać jako (()()()...()). Dodatkowo, jeśli użyjesz liczb pierwszych, może to spowodować pominięcie niektórych możliwych optymalizacji kompozytów.
DJMcMayhem
Ponadto, dlaczego nie jest []zdefiniowane dla tego wyzwania? Dziwne jest dla mnie wdrożenie 7 z 8 operatorów. Tak czy inaczej, fajne wyzwanie, jestem zaszczycony, że ktoś napisze wyzwanie inspirowane moim własnym językiem!
DJMcMayhem
2
@DJMcMayhem Chcę, aby ludzie mogli obliczać własne wyniki. Wszystkie istotne liczby pierwsze są o jeden więcej niż liczbą złożoną, więc powinno być wiele potencjalnych optymalizacji. Ponadto nie chcę, aby ludzie polegali na określonej wartości []w swojej odpowiedzi.
Neil,
1
@YetiCGN Rozmiar skryptu liczy się tylko jako rozstrzygający.
Neil,

Odpowiedzi:

16

Python 2, 59394 59244 58534 58416 58394 58250

Ok, oto moje rozwiązanie.

import re
import math

cache = {0:"<()>"}

def find(x,i,j):
    return i*((x**2+x)/2)+(j+1)*((x**2-x)/2)

def solve(x, i, j):
    a = (i + j + 1)/2.
    b = (i - j - 1)/2.
    c = -x
    return (-b + math.sqrt(b**2 - 4*a*c))/(2*a)

def size(i,j=0):
    return 4*(i+j)+14

def polynomials(n):
    upperBound = int(4*math.log(n,2))
    i = 0
    answers = []
    while size(i) < upperBound:
        for j in range(i):
            sol = int(solve(n, i-j, j)+.5)
            if find(sol, i-j, j) == n:
                answers.append((sol, i-j, j))
        i += 1
    return answers

def complement(character):
        dict = {"(":")","{":"}","<":">","[":"]",")":"(","}":"{",">":"<","]":"["}
        return dict[character]

def findMatch(snippet, index):
        increment = 1 if snippet[index] in "({<[" else -1
        stack = []
        if snippet[index] in "(){}<>[]":
                stack.append(snippet[index])
        while len(stack) > 0 and index + increment < len(snippet):
                index += increment
                if snippet[index] in "(){}<>[]":
                        if complement(snippet[index]) == stack[-1]:
                                stack = stack[:-1]
                        else:
                                stack.append(snippet[index])
        return index

def isPrime(n):
    return not [0 for x in range(2,int(n**.5)+1) if n%x==0] and n>1

def getPrimeFactors(n):
    return [x for x in range(2,n/2) if n%x==0 and isPrime(x)]

def divHardcode(n,m):
    assert n%m == 0
    assert m != 1
    assert n != 1
    binary = bin(m)[3:]
    return (binary.count("1")+len(binary))*"("+getBF(n/m)+")"*binary.count("1")+binary.replace("1","){}{}").replace("0","){}")

def isTriangular(n):
    #Triangles must be between sqrt(2n) and cbrt(2n)
    if n < 0: return isTriangular(-n)
    for x in range(int((2*n)**(1/3.)),int((2*n)**.5)+1):
        if (x**2+x) == 2*n:
            return True
    return False

def getTriangle(n):
    if n < 0: return -getTriangle(-n)
    #Triangles must be between sqrt(2n) and cbrt(2n)
    for x in range(int((2*n)**(1/3.)),int((2*n)**.5)+1):
        if (x**2+x) == 2*n:
            return x
    #If we don't find one we made a mistake
    assert False

def getSimpleBF(n):
    if n in cache:return cache[n]
    if n < 0:
        # There is room for better solutions here
        return "["+getSimpleBF(-n)+"]"
    elif n == 0:
        return ""
    elif n < 6:
        return "()"*n
    #Non-edge cases
    solutions = []
    factors = getPrimeFactors(n)
    if n >= 78 and isTriangular(n):
        solutions.append(
           min([push(getTriangle(n))+"{({}[()])}{}","<"+push(getTriangle(n)+1)+">{({}[()])}{}"],key=len)
        )
    polynomialSolutions = polynomials(n)
    for polynomial in polynomialSolutions:
        solutions.append("<%s>{%s({}[()])%s}{}"%(push(polynomial[0]),"({})"*polynomial[1],"({})"*polynomial[2]))
        #Mod 3 tricks
    if n % 3 == 2:
       solutions.append(("((%s)()){}{}")%getBF(n/3))
    elif n % 3 == 1:
       solutions.append(("((%s)()()){}{}")%getBF(n/3-1))
    #Basic solutions
    if isPrime(n):
        solutions.append(getSimpleBF(n-1) + "()")
    else:
        #TODO multithread
        solutions += map(lambda m:divHardcode(n,m),factors)
    return min(solutions,key=lambda x:len(unpack(x)))

def getBF(n):
    if n in cache: return cache[n]
    result = getSimpleBF(n)
    index = n - 1
    while index > n-(len(result)/2):
        score = getSimpleBF(index)+getSimpleBF(n-index)
        if len(score) < len(result):result = score
        index -= 1
    index = n + 1
    while index < n+(len(result)/2):
        score = getSimpleBF(index)+getSimpleBF(n-index)
        if len(score) < len(result):result = score
        index += 1
    cache[n] = result
    return result

def unpack(string):
    reMatch = re.match("\(*<",string)
    if reMatch:
        location =reMatch.span()
        return string[location[1]:findMatch(string,location[1]-1)] +string[:location[1]-1] + string[findMatch(string,location[1]-1)+1:]
    return string

def push(n):
    return unpack("("+getBF(n)+")")

def kolmo(string):
    code = push(ord(string[-1]))
    stringVector = map(ord,string)
    for x,y in zip(stringVector[-1:0:-1],stringVector[-2::-1]):
        code = "("+code+getBF(y-x)+")"
    code = code.replace("<()>)",")")
    return code

def kolmo(stringVector):
    code = push(stringVector[-1])
    for x,y in zip(stringVector[-1:0:-1],stringVector[-2::-1]):
        code = "("+code+getBF(y-x)+")"
    code = code.replace("<()>)",")")
    return code


if __name__ == "__main__":
    import primes
    sum = 0
    for prime in primes.nums:
        print push(prime)
        sum += len(push(prime))
    print sum

Odpowiednią funkcją jest push(n). Aby to nazwać, po prostu zadzwoń naciśnij liczbę całkowitą, którą chcesz reprezentować.

Wyjaśnienie

Główną optymalizacją wykonaną przez program jest mnożenie na stałe. Pomysł zwielokrotnienia na stałe jest dość prosty. Naciskasz cyfrę, a następnie pop i pchasz ją, aby utworzyć nową wartość. Na przykład, aby pomnożyć przez dwa, możesz użyć następującego kodu, ((n){})gdzie n kod tworzy określoną liczbę. To działa, ponieważ oba (n)i {}mają wartość n.

Ten prosty pomysł można uczynić bardziej złożonym dla większych liczb. Weźmy na przykład 5, jakiś czas temu odkryto, że najlepszym sposobem pomnożenia przez pięć jest (((n)){}){}{}. Ten kod tworzy dwie kopie n mnożą jeden przez 4 i dodaje dwa. Stosując tę ​​samą strategię, dokonuję każdego mnożenia na podstawie reprezentacji binarnej liczby. Nie będę wchodził w szczegóły, jak to działa w tej chwili, ale robię to, odcinając pierwszą reprezentację binarną i zastępując 0 przez ){}i 1 przez){}{}. Następnie upewnia się, że n jest popychane odpowiednią liczbę razy i równoważy wszystkie nawiasy. (Jeśli chcesz wiedzieć, jak to zrobić, możesz spojrzeć na mój kod). Jeśli chcesz wiedzieć, dlaczego to działa, po prostu zapytaj mnie w komentarzu. Nie sądzę, aby ktokolwiek faktycznie czytał wszystkie aktualizacje mojego postu, więc pominąłem wyjaśnienie.

Gdy algorytm próbuje znaleźć stały kod mnożenia, próbuje wszystkich liczb pierwszych czynników. Ignoruje czynniki złożone, ponieważ w pewnym momencie czynniki złożone można zawsze wyrazić bardziej zwięźle jako własne czynniki pierwsze, nie wiadomo, czy jest to nadal prawdą.

Innym mechanizmem zapisywania bajtów jest wielomianowa wyszukiwarka rozwiązań. Istnieją pewne formy wielomianów, które można łatwo przedstawić za pomocą pętli zmniejszających. Te wielomiany obejmują między innymi liczby wielokątne. Ta optymalizacja wyszukuje wielomiany pasujące do formy i tworzy kod, który je tworzy.

Wydajność

pojemnik na pastę

Kreator pszenicy
źródło
„czy n jest większe czy mniejsze niż n + 1”?
Sparr
@Sparr, czy interpretacja njest większa czy mniejsza niżn+1
Wheat Wizard
Powinieneś cofnąć linie if n % 3 == 2: do końca tej funkcji o jeden poziom.
user202729
13

Brain-Flak, 64664

Wypróbuj online!

Oto mój kod z adnotacjami

({}<
 ((((()()()()()){}){}){}()) #41
>)
{
 (({})[()()()()()()])
 ([({}<(())>)](<>)){({}())<>}{}<>{}{}<>(({})){(<{}({}<>)>)}{}({}<>)
 {((< #IF
  {} 
  {({}[()]< #FOR
   ((((()()()()()){}){}){}()) #41
   (({})[()])                 #40
  >)}{}
 >))}{}
 (({}))
 #MOD2
 {(<
  ({}<(())>)({<({}[()]<>)><>(()[{}])<><({}<>)>}{}<({}<>)><>)<>({}<>)
  {((<{}({}< #IF
   {}
   (((()()()()())({})({})({}){})({})({})({}){})  #125
   (({})[()()])                                  #123
   ((((()()()()()){}){}){}())                    #41
   <>
   ((((()()()()()){}){}){})                      #40
   <>
   >)

  >))}{}{}
 >)}{}
 #MOD2 (number 2)
 (({}))
 ({}(())){({}[()]<>)<>(()[{}])<>({}<>)}{}
 (({})<([{}]{})>)
 {
  ({}[()]<<>
    ((((()()()()()){}){}){}) #40
    (({})())                 #41
   <>>)
 }{}
}{}
<>{({}<>)<>}<>((((()()()()()){}){}){})

Wyjaśnienie

To implementuje tylko dwie reguły od teraz:

  • Jeśli n jest podzielne przez dwa znaki powrotu (n/2){}

  • Jeśli n nie jest podzielne przez dwa znaki powrotu n-1()

Koduje także wszystkie liczby mniejsze niż 6.

Kreator pszenicy
źródło
Wydaje się, że próba podzielności przez trzy powinna znacznie zmniejszyć wynik
tylko ASCII
@ Tylko ASCII I rzeczywiście go zaimplementowałem i to zwiększyło liczbę bajtów. Pracuję nad sposobem wprowadzenia inteligentniejszej wersji podzielności przez trzy.
Wheat Wizard
Ok, używając Brain-Flak do stworzenia programu, który generuje liczby Brain-Frak. Miły.
Draco18s
10

Perl, 59222 59156 58460 znaków

  • n() (11322660 znaków)
  • (n){}() (64664 znaków)
  • ((n)){}{} (63610 znaków)
  • ((n)()){}{} (63484 znaków) - to nowatorskie obliczenie
  • (n){({}[()])}{} (60748 znaków)
  • n[m] (62800 znaków)
  • (n){m({}[l])}{} (58460 znaków) - to nowatorskie obliczenie

Wzór na ostatnie obliczenia to n(n/l+1)/2+mn/l. Próbowałem innych obliczeń, ale nie są one już pomocne dla danych wyjściowych. Program faktycznie generuje wszystkie wartości do 9999, ale następnie podaje podane liczby pierwsze i ich całkowitą długość.

@primes = (<list of the 4-digit prime numbers here>);
@numbers = ();
for ($i = 1; $i < 10000; $i++) {
  $numbers[$i] = "()" x $i; # default calculation
}
for ($i = 2; $i < 10000; $i++) {
  for ($j = 1; $j < 8; $j++) {
    &try($i, "$numbers[$i+$j]\[$numbers[$j]]");
  }
  &try($i + 1, "$numbers[$i]()");
  &try($i * 2, "($numbers[$i]){}");
  &try($i * 3, "(($numbers[$i])){}{}");
  &try($i * 3 + 2, "(($numbers[$i])()){}{}");
  for ($l = 1; $l * $l < $i; $l++) { 
    unless ($i % $l) { 
      for ($j = 0; ($k = (($i + $j + $j) * $i / $l + $i) / 2) < 10000; $j++) { 
        &try($k, "($numbers[$i]){$numbers[$j]({}[$numbers[$l]])}{}");
      } 
    } 
  } 
}
$len = 0;
foreach (@primes) {
  print "($numbers[$_])\n";
  $len += 2 + length $numbers[$_];
}
print "$len\n";
sub try {
  ($n, $s) = @_;
  $numbers[$n] = $s if (length($numbers[$n]) > length $s);
}
Neil
źródło
Czy możesz podać link do wyniku?
DJMcMayhem
@DJMcMayhem Ups, przypadkowo zepsułem listę liczb pierwszych, unieważniając liczbę moich postaci.
Neil,
@Linus ((X) ()) {} {} popycha X, następnie dodaje 1, popycha wynik, a następnie wysuwa X + 1 i X. Łącznie 3X + 2. Myślę, że wypróbowałem inną formułę w Try It Online, ale mogę sprawdzić dwukrotnie, jeśli chcesz.
Neil
@ Nee Mój błąd ... Wyglądają dobrze, ale co dokładnie psuje twoje liczby pierwsze?
Linus
1
@Neil Dostaję 58158, gdy dodam &try($i * $i, "$numbers[$i]{({})({}[()])}{}");, co spada do 58032, gdy również dodam &try((3 * $i * $i - $i) / 2, "$numbers[$i]{({})({}[()])({})}{}");(liczby kwadratowe / pięciokątne) - to stąd
tylko ASCII
5

Python, 59136 58676 znaków

Funkcja gry w numer Brainflak:

m=11111
R=range(0,m)
R[1]="()"
R[2]="()()"
l=2
def a(v,r):
 if v>0 and v<m:
  if isinstance(R[v],int) or len(r)<len(R[v]):
   R[v]=r
   if v<R[0]:
    R[0]=v
def s(v,k):
 S=0
 while v>0:
  S+=v
  v-=k
 return S
p=lambda r:"("+r+")"
w=lambda r:"{({}["+r+"])}{}"
def q(r,v):
 for i in range(1,v):
  r="("+r+")"
 for i in range(1,v):
  r+="{}"
 return r
def e(r,v,k):
 for i in range(0,k):
  r=q(r,v)
 return r
while l<m:
 R[0]=l+1
 a(l*2,q(R[l],2)) 
 a(l*3,q(R[l],3))
 a(l*5,q(R[l],5))
 a(l*7,q(R[l],7))
 for i in range(1,l):
  a(l+i,R[l]+R[i])
  a(l-i,R[l]+"["+R[i]+"]")
  if l%i==0:
   t=s(l-i,i)
   a(s(l,i),p(R[l])+w(R[i]))
   a(l+2*t,p(R[l])+q(w(R[i]),2))
   a(l+4*t,p(R[l])+e(w(R[i]),2,2))
   a(l+8*t,p(R[l])+e(w(R[i]),2,3))
   a(l+16*t,p(R[l])+e(w(R[i]),2,4))
   a(l+32*t,p(R[l])+e(w(R[i]),2,5))
   a(l+64*t,p(R[l])+e(w(R[i]),2,6))
   a(l+128*t,p(R[l])+e(w(R[i]),2,7))
   a(l+3*t,p(R[l])+q(w(R[i]),3))
   a(l+9*t,p(R[l])+e(w(R[i]),3,2))
   a(l+27*t,p(R[l])+e(w(R[i]),3,3))
   a(l+5*t,p(R[l])+q(w(R[i]),5))
   a(l+6*t,p(R[l])+q(q(w(R[i]),3),2))
   a(l+10*t,p(R[l])+q(q(w(R[i]),5),2))
   a(l+15*t,p(R[l])+q(q(w(R[i]),5),3))
   a(l+12*t,p(R[l])+q(q(q(w(R[i]),3),2),2))
   a(l+18*t,p(R[l])+q(q(q(w(R[i]),3),3),2))
   a(l+20*t,p(R[l])+q(q(q(w(R[i]),5),2),2))
   a(l+24*t,p(R[l])+q(q(q(q(w(R[i]),3),2),2),2))
   a(l+36*t,p(R[l])+q(q(q(q(w(R[i]),3),3),2),2))
   a(l+40*t,p(R[l])+q(q(q(q(w(R[i]),5),2),2),2))
 l=R[0]
f=lambda v:p(R[v])

Iteracja liczb pierwszych:

def isPrime(v):
 i=2
 while i*i<=v:
  if v%i==0:
   return False
  i+=1
 return True

for i in range(1000,10000):
 if isPrime(i):
  print f(i)

Wydajność:

Pastebin

Wyjaśnienie:

Wstępnie wypełniamy listę R reprezentacji płata mózgowego, oceniając poszczególne liczby całkowite w zakresie większym niż konieczny [1, m -1], aby zdefiniować naszą funkcję f . Reprezentacje są tworzone przez przyjęcie najniższej nieużywanej reprezentacji (indeksowanej przez l ) i utworzenie z niej wielu nowych reprezentacji, zachowując tylko najkrótszą. Najniższa nieużywana reprezentacja zakłada, że ​​wszystkim numerom od 1 do 1 przypisano reprezentację i że te reprezentacje zostały już wykorzystane do wytworzenia nowych liczb. Jeśli wartość mniejsza niż l ma krótszą reprezentację, musimy cofnąć się i odtworzyć liczby zaczynające się od tego punktu. Funkcja f tworzy program zapisujący numer na stosie przez dodanie nawiasu.

Na początku nie znałem żadnego Brainflaka i bardzo doceniam odpowiedź Eamona Olive'a za wskazanie wzoru na liczby trójkątów. Głównie uogólniłem podsumowanie i byłem nieustępliwy w sprawdzaniu sum i różnic. Dodanie wielu wielokrotności sum ma świetny efekt.

Dla tych, którzy są zainteresowani, oto kod zdrapki , w którym sprawdziłem, które formuły były tego warte.

Formuły reprezentacji:

  1. Mnożenie przez małe liczby pierwsze:
    (X){}
    ((X)){}{}
    ((((X)))){}{}{}{}
    ((((((X)))))){}{}{}{}{}{}
  2. Dodatek X + Y :
    XY
  3. Odejmowanie X - Y :
    X[Y]
  4. Sumowanie do X przyrostu Y włącznie :
    (X){({}[Y])}{}
  5. Wielokrotności sumy do X przyrostu Y plus X :
    (X)({({}[Y])}{}){}
    (X)(({({}[Y])}{})){}{}
    (X)(({({}[Y])}{}){}){}
    itd ...
Linus
źródło
Myślałem, że 5 * nie było pomocne, ale teraz widzę, że zapisuje 10 znaków w mojej odpowiedzi. Myślałem, że spróbowałem tych podsumowań, ale sprawdzę jeszcze raz!
Neil
Zsumowanie przyrostów i wielokrotności pozwala mi zaoszczędzić kolejne 46 bajtów, a nawet wtedy muszę trzykrotnie przepłukać i powtórzyć, aby złapać je wszystkie.
Neil
Okazuje się, że jeśli użyję odejmowania, nie użyję ponownie 5 *.
Neil,
4

Lua 5.3, 57522

Właściwie zacząłem nad tym pracować, kiedy pytanie zostało opublikowane, ale zapomniałem o tym aż do rocznicy 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)

Podobny pomysł do innych odpowiedzi, w których znane-przydatne funkcje są używane do tworzenia większych liczb z dobrych reprezentacji liczb prostszych.

Jedną różnicą jest to, że zamiast rozwiązywać podproblemy w kategoriach mniejszych liczb, rozwiązuję podproblemy w kategoriach liczb o krótszych reprezentacjach. Myślę, że to sprawia, że ​​bardziej elegancko jest korzystać z liczb ujemnych, a także zajmować się przypadkiem, w którym mniejsze liczby są reprezentowane przez większe liczby.

Ponadto próba znalezienia wszystkich liczb, które mogą być reprezentowane w określonym rozmiarze, a raczej próba przedstawienia konkretnej liczby tak krótko, jak to możliwe, w rzeczywistości upraszcza pewne obliczenia. Zamiast pracować z formułą w odwrotnej kolejności, aby sprawdzić, czy można ją zastosować do liczby, można ją przerobić do przodu i zastosować do każdej liczby.

Inna różnica polega na tym, że znane rozwiązania są przechowywane w dwóch częściach - (opcjonalnym) „przedrostku” i „sufiksie” (bardziej przypominającym przedrostek). Oczekuje się, że wycena prefiksu zostanie zignorowana podczas obliczania podanej liczby - prefiks zawiera tylko kod, który ustawia sufiks do uruchomienia (zazwyczaj przez wypchnięcie jednej lub więcej rzeczy na stos). Tak więc, biorąc pod uwagę prefiks i sufiks, odpowiednią liczbę można wypchnąć na stos za pomocą prefix(suffix).

Ten podział zasadniczo rozwiązuje ten sam problem, co unpackfunkcja w odpowiedzi Kreatora pszenicy. Zamiast owijania kodu <...>tylko w celu cofnięcia tego później, taki kod jest po prostu dodawany do prefiksu.

W kilku przypadkach prefiks faktycznie jest oceniany (głównie dla operacji pseudoeksponentiacyjnej), więc jego wycena jest również przechowywana. Nie stanowi to jednak dużego problemu, ponieważ generator nie próbuje konstruować określonych liczb. Wydaje się teoretycznie sugerować, że mogą istnieć dwa fragmenty kodu o tej samej długości i generujące ten sam numer, który nie byłby zbędny w pamięci podręcznej z powodu różnych wycen prefiksów. Nie zawracałem sobie głowy rozliczaniem tego, ponieważ wydaje się, że nie ma to większego znaczenia (przynajmniej w tej dziedzinie).

Wyobrażam sobie, że łatwo byłoby zmniejszyć liczbę bajtów, dodając więcej przypadków, ale na razie mam dość.

Dobiegłem do 1000000, ale sprawdziłem tylko zdrowie psychiczne do 100000.

Pastebin produkcji na danych liczbach pierwszych.

tehtmi
źródło
Co robisz k_limiti co k_max_lenrobisz? Nie jestem pewien, czy rozumiem nagłówek.
Kreator pszenicy
1
Zamiast próbować obliczyć określone liczby, obliczam wszystkie przydatne (tj. Podając niezbyt duże liczby krótsze niż jakikolwiek inny znaleziony program) programy o określonej długości - k_max_len. Równie łatwo mógł sprawdzić, czy znalazł wszystkie liczby, o które prosiłeś po przetworzeniu każdej długości, ale przydało mi się ograniczenie maksymalnej długości podczas testowania, aby program działał szybciej. (Przetwarzanie większych długości może być bardzo powolne.) k_limitJest zasadniczo parametrem wejściowym - wyświetli programy dla liczb do tego - zakładając, że k_max_lenbyło wystarczająco duże, aby je znaleźć.
tehtmi
4

rubin, 60246 bajtów

$brain_flak = Hash.new{|h, k|
    a = []
    a.push "()"*k
    if k > 1
        if k > 10
            # Triangle Numbers:
            n = (Math.sqrt(1+8*k).to_i-1)/2
            if (n*n+n)/2 == k
                a.push "("+h[n]+"){({}[()])}{}" 
                a.push  h[n+n]+")({({}[()])}{}"
            end
        end
        (k**0.51).to_i.downto(2){|i|
            # multiplication:
            if k%i==0
                a.push "("*(i-1) + h[k/i] + ")"*(i-1)+"{}"*(i-1)

            end
        }
        (k/2).downto(1){|i|
            # Addition
            a.push h[k-i] + h[i]
        }
    end

    h[k] = a.min_by{|x|x.length}
}
$brain_flak[0] = "<><>"

def get_code_for (i)
  "(#{$brain_flak[i]})"
end

Używam skrótu. Znajduję najlepszy golf dla danej liczby i używam mniejszych, aby znaleźć większe.

Hasła rekurencyjne to świetna zabawa!

MegaTom
źródło
2

Python, 64014 znaków

Przed tym wyzwaniem nie wiedziałem nic na temat ataku mózgowego i tylko trochę się nim bawiłem na tryitonline, więc mogłem zauważyć oczywiste skróty, których mi brakowało. Jest to dość nudne rozwiązanie, po prostu dzieli dane wejściowe na x=x/2+x%2lub x=x/3+x%3, w zależności od tego, co jest krótsze.

k=lambda x:"(("+o(x/3)+")){}{}"+(x%3)*"()"if x>3else"()"*x
m=lambda x:"("+o(x/2)+"){}"+(x%2)*"()"if x>6else"()"*x
o=lambda x:min(k(x),m(x),key=len)
b=lambda x:"("+o(x)+")"

Nazwij to tak: b(42)

wyjście na pastebin

KarlKastor
źródło
1

Lua, 64664 bajtów

Program wypisuje całkowitą długość programów i program dla 203. liczby pierwszej (tam jest linia, którą możesz zmienić, aby zmienić, który jest drukowany, lub odkomentować linię, aby wydrukować wszystkie programy)

W tej chwili jedyną optymalizacją jest x = 2 * n + 1

Mam nadzieję, że będę miał czas, aby dodać więcej optymalizacji w celu obniżenia wyniku.

local primeS = [[<INSERT PRIMES HERE>]]

local primes = {}

for num in primeS:gmatch("%d+") do
    table.insert(primes, num+0)
end

local progs = {}
progs[0] = ""
progs[1] = "()"
progs[2] = "()()"

local function half(n)
    if progs[n] then return progs[n] end
    local p = ""
    local div = math.floor(n/2)
    local rem = n%2 == 1 and "()" or ""
    return "("..progs[div].."){}"..rem
end

for i = 3, 10000 do

    local bin = half(i)

    progs[i] = progs[i-1] .. "()"

    if #bin < #progs[i] then
        progs[i] = bin
    end

    if i % 1000 == 0 then
        print(i)
    end

end

local n = 203 -- This is the program it outputs
print(n..", ("..progs[203]..")")

local len = 0
for i,v in ipairs(primes) do
    len = len + #progs[v] + 2
    --print(v.." ("..progs[v]..")\n")
end
print("Total len: "..len)
PiGuy
źródło