Golf a Brain-Flak Integer


28

I numeri interi sono noiosi da rappresentare in Brain-Flak . Ci sono 8 operatori:

()      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

foopuò essere composto da più operatori, nel qual caso vengono valutati e sommati. Ad esempio, (()())spinge 2nello stack (e lo valuta 2anche).

Ovviamente, il (()...())meccanismo non è utile in Code Golf poiché un numero elevato richiederebbe n*2+2byte per rappresentare. La tua sfida è quindi quella di scrivere un programma o una funzione che produrrà nel minor numero di byte possibile un programma Brain-Flak che invierà un dato intero positivo nallo stack attivo. Questo programma non deve fare alcuna ipotesi sul contenuto esistente degli stack, quindi non deve lasciare gli stack scambiati o aggiungere o rimuovere valori aggiuntivi dagli stack.

Sebbene il tuo programma o funzione debba essere in grado di restituire un programma Brain-Flak funzionante per tutti gli input da 1 a 1.000.000, il vincitore sarà il programma o la funzione che genererà il più piccolo set di programmi Brain-Flak appropriati per tutti i 1061 numeri primi tra 1.000 e 10.000 . Dovresti notare la dimensione totale delle tue uscite per quegli ingressi 1061 come parte del tuo invio. Il programma o la funzione può accettare il numero intero e restituire il programma (stringa) Brain-Flak in uno dei soliti formati I / O accettabili. I legami verranno interrotti utilizzando le dimensioni del programma o della funzione.


4
Proprio come una nota: il numero di programmi validi di lunghezza 2nè 4^n catalan(n).
Leaky Nun,

2
Hmm, mi piace la sfida, ma penso che dovrebbe essere segnato su numeri interi sconosciuti. Altrimenti, i programmi con numeri interi su cui viene assegnato il punteggio potrebbero essere forzati e altri numeri interi lasciati come (()()()...()). Inoltre, se usi solo numeri primi, potresti perdere alcune ottimizzazioni possibili per i compositi.
DJMcMayhem

Inoltre, perché []non è definito per questa sfida? Trovo strano implementare 7 degli 8 operatori. Ad ogni modo, bella sfida, sono onorato che qualcuno scriva una sfida ispirata alla mia lingua!
DJMcMayhem

2
@DJMcMayhem Voglio che le persone siano in grado di calcolare il proprio punteggio. Tutti i numeri primi rilevanti sono uno in più di un numero composto, quindi dovrebbero esserci molte potenziali ottimizzazioni. Inoltre, non voglio che le persone facciano affidamento su un valore particolare []nella loro risposta.
Neil,

1
@YetiCGN La dimensione dello script conta solo come un pareggio.
Neil,

Risposte:


16

Python 2, 59394 59244 58534 58416 58394 58250

Ok ecco la mia soluzione.

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

La funzione rilevante è push(n). Per chiamarlo è sufficiente chiamare push sull'intero che si desidera rappresentare.

Spiegazione

L'ottimizzazione principale effettuata dal programma è la codifica hardware della moltiplicazione. L'idea del codice hard moltiplicativo è piuttosto semplice. Spingi un numero e poi fai clic e lo spingi per creare un nuovo valore. Ad esempio, per moltiplicare per due è possibile utilizzare il seguente codice in ((n){})cui n codice produce un numero specifico. Questo funziona perché entrambi (n)e {}hanno un valore di n.

Questa semplice idea può essere resa più complessa per numeri più grandi. Prendiamo ad esempio 5 è stato scoperto qualche tempo fa che il modo migliore per moltiplicare per cinque era (((n)){}){}{}. Questo codice fa due copie di n moltiplica una per 4 e aggiunge le due. Usando la stessa strategia faccio ogni moltiplicazione in base alla rappresentazione binaria di un numero. Non entrerò nei dettagli di come funziona ora, ma lo faccio tagliando il primo della rappresentazione binaria e sostituendo 0 con ){}e 1 con){}{}. Si assicura quindi che n venga premuto il numero appropriato di volte e bilancia tutte le parentesi. (Se vuoi sapere come è possibile, puoi guardare il mio codice). Se vuoi sapere perché funziona, chiedimi in un commento. Non penso che nessuno abbia effettivamente letto tutti gli aggiornamenti del mio post, quindi ho lasciato fuori la spiegazione.

Quando l'algoritmo tenta di trovare un hardcode di moltiplicazione, tenta tutti i fattori primi di un numero. Ignora i fattori compositi perché a un certo punto i fattori compositi potrebbero sempre essere espressi in modo più conciso in quanto i suoi primi fattori non è noto se ciò sia ancora vero.

L'altro meccanismo di salvataggio dei byte è un cercatore di soluzioni polinomiali. Esistono alcune forme di polinomi che sono facili da rappresentare con anelli decrescenti. Questi polinomi includono, ma non sono limitati a, numeri poligonali. Questa ottimizzazione trova i polinomi che si adattano al modulo e crea il codice che li rende.

Produzione

paste-bin


"se n è maggiore o minore di n + 1" ??
Sparr,

@Sparr se l'interpretazione di nè maggiore o minore din+1
Wheat Wizard

Dovresti separare le linee if n % 3 == 2: fino alla fine di quella funzione di un livello.
user202729,

13

Brain-Flak, 64664

Provalo online!

Ecco il mio codice annotato

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

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

Spiegazione

Questo implementa solo due regole al momento:

  • Se n è divisibile per due ritorna (n/2){}

  • Se n non è divisibile per due ritorna n-1()

Inoltre codifica tutti i numeri inferiori a 6.


Sembra un controllo della divisibilità per tre dovrebbe ridurre il punteggio di un bel po '
solo ASCII il

@ ASCII-solo l'ho effettivamente implementato e ha aumentato il conteggio dei byte. Sto lavorando a un modo per implementare una versione più intelligente della divisibilità per tre.
Wheat Wizard

Ok, usando Brain-Flak per creare un programma che genera numeri Brain-Frak. Bello.
Draco18s,

10

Perl, 59222 59156 58460 caratteri

  • n() (11322660 caratteri)
  • (n){}() (64664 caratteri)
  • ((n)){}{} (63610 caratteri)
  • ((n)()){}{} (63484 caratteri) - questo è un nuovo calcolo
  • (n){({}[()])}{} (60748 caratteri)
  • n[m] (62800 caratteri)
  • (n){m({}[l])}{} (58460 caratteri) - questo è un nuovo calcolo

La formula per l'ultimo calcolo è n(n/l+1)/2+mn/l. Ho provato altri calcoli ma non sono più utili per l'output specificato. Il programma in realtà genera tutti i valori fino a 9999 ma quindi elenca i numeri primi indicati e la loro lunghezza totale.

@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);
}

Potresti fornire un link all'output?
DJMcMayhem

@DJMcMayhem Oops, ho accidentalmente danneggiato il mio elenco di numeri primi, invalidando il conteggio dei personaggi.
Neil,

@Linus ((X) ()) {} {} spinge X, quindi aggiunge 1, spinge il risultato, quindi apre X + 1 e X. Totale 3X + 2. Penso di aver provato l'altra formula su Provalo online, ma posso ricontrollare se vuoi.
Neil,

@Neil Il mio errore ... Questi sembrano buoni ma cosa corrompe esattamente i tuoi numeri primi allora?
Linus,

1
@Neil ottengo 58158 quando aggiungo &try($i * $i, "$numbers[$i]{({})({}[()])}{}");, che scende a 58032 quando aggiungo anche &try((3 * $i * $i - $i) / 2, "$numbers[$i]{({})({}[()])({})}{}");(numeri quadrati / pentagonali) - è da qui
solo ASCII il

5

Python, 59136 58676 caratteri

Funzione di golf numero 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])

Iterazione dei numeri primi:

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)

Produzione:

pastebin

Spiegazione:

Pre-popoliamo un elenco R di rappresentazione Brak-flak che valuta singoli numeri su un intervallo più ampio del necessario [1, m -1] per definire la nostra funzione f . Le rappresentazioni si formano prendendo la rappresentazione inutilizzata più bassa (indicizzata da l ) e formando molte nuove rappresentazioni da essa, mantenendo solo la più breve. La rappresentazione inutilizzata più bassa presuppone che a tutti i numeri da 1 a 1 sia stata assegnata una rappresentazione e che queste rappresentazioni siano già state utilizzate per produrre nuovi numeri. Se un valore inferiore a l ottiene una rappresentazione più breve, dobbiamo tornare indietro e riprodurre i numeri che iniziano da quel punto. La funzione f produce un programma salvando il numero nello stack aggiungendo parentesi.

All'inizio non conoscevo Brainflak e apprezzo molto la risposta di Eamon Olive per aver indicato la formula per i numeri dei triangoli. Principalmente ho generalizzato la somma e sono stato implacabile nel controllare somme e differenze. L'aggiunta di molti multipli di somme ha avuto un grande effetto.

Per chi se ne frega, ecco il codice scratch che ho usato per vedere quali formule valessero la pena.

Formule di rappresentazione:

  1. Moltiplicazione per piccoli numeri primi:
    (X){}
    ((X)){}{}
    ((((X)))){}{}{}{}
    ((((((X)))))){}{}{}{}{}{}
  2. Aggiunta X + Y :
    XY
  3. Sottrazione X - Y :
    X[Y]
  4. Sommatoria a X inclusa l' incremento Y :
    (X){({}[Y])}{}
  5. Multipli di sommazioni a X di incremento Y , più X :
    (X)({({}[Y])}{}){}
    (X)(({({}[Y])}{})){}{}
    (X)(({({}[Y])}{}){}){}
    ecc ...

Pensavo che 5 * non fosse utile, ma ora vedo che salva 10 caratteri nella mia risposta. Pensavo di aver provato quelle somme, ma ricontrollerò!
Neil,

Le somme di incrementi più multipli mi fanno risparmiare altri 46 byte, e anche allora devo risciacquare e ripetere tre volte per catturarli tutti.
Neil,

Si scopre che se uso la sottrazione, non uso di nuovo 5 *.
Neil,

4

Lua 5.3, 57522

In realtà ho iniziato a lavorare su questo argomento quando la domanda è stata pubblicata, ma me ne sono dimenticata fino all'anniversario di 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)

Un'idea simile alle altre risposte in cui vengono utilizzate funzioni note-utili per costruire numeri più grandi da buone rappresentazioni di numeri più semplici.

Una differenza è che invece di risolvere i sottoproblemi in termini di numeri più piccoli, sto risolvendo i sottoproblemi in termini di numeri con rappresentazioni più brevi. Penso che ciò renda più elegante sfruttare i numeri negativi e gestire il caso in cui i numeri più piccoli sono rappresentati in termini di numeri più grandi.

Inoltre, cercare di trovare tutti i numeri che possono essere rappresentati in una determinata dimensione piuttosto che cercare di rappresentare un determinato numero il più presto possibile semplifica effettivamente alcuni calcoli. Invece di lavorare una formula al contrario per vedere se può essere applicata a un numero, la formula può essere lavorata in avanti e applicata a ogni numero.

Un'altra differenza è che le soluzioni note sono memorizzate in due parti: un "prefisso" (facoltativo) e un "suffisso" (più simile a un infisso). La valutazione del prefisso dovrebbe essere ignorata quando si calcola il numero dato - il prefisso contiene solo il codice che imposta l'esecuzione del suffisso (generalmente spingendo una o più cose nello stack). Quindi, dato un prefisso e un suffisso, il numero corrispondente può essere inserito nello stack con prefix(suffix).

Questa suddivisione risolve sostanzialmente lo stesso problema della unpackfunzione nella risposta di Wheat Wizard. Invece di racchiudere il codice <...>solo per annullarlo in seguito, tale codice viene semplicemente aggiunto al prefisso.

In alcuni casi, il prefisso viene effettivamente valutato (principalmente per l'operazione di pseudo-esponenziazione), quindi viene memorizzata anche la sua valutazione. Tuttavia, ciò non causa realmente un grosso problema, poiché il generatore non sta cercando di costruire numeri specifici. Sembra teoricamente implicare che potrebbero esserci due pezzi di codice della stessa lunghezza e generare lo stesso numero che non sarebbe ridondante nella cache a causa di valutazioni di prefisso diverse. Tuttavia, non mi sono preoccupato di contabilizzarlo, poiché non sembra importare molto (almeno in questo settore).

Immagino che sarebbe facile ridurre il numero di byte semplicemente aggiungendo altri casi, ma per il momento ne ho abbastanza.

Ho corso a 1000000, ma ho fatto solo il controllo della sanità mentale fino a 100000.

Pastebin di output su determinati numeri primi.


Cosa fare k_limite k_max_lenfare? Non sono sicuro di aver capito l'intestazione.
Mago del grano,

1
Invece di provare a calcolare numeri particolari, sto calcolando tutti i programmi utili (ovvero dando numeri non troppo grandi più brevi di qualsiasi altro programma trovato) fino a una certa lunghezza - k_max_len. Potrebbe facilmente verificare di aver trovato tutti i numeri richiesti dopo l'elaborazione di ciascuna lunghezza, ma è stato utile per me essere in grado di limitare la lunghezza massima durante il test in modo che il programma funzionasse più velocemente. (L'elaborazione di lunghezze maggiori può essere molto lenta.) In k_limitsostanza è il parametro di input - produrrà programmi per numeri fino a questo - supponendo che k_max_lenfosse abbastanza grande da trovarli.
tehtmi,

4

rubino, 60246 byte

$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

Uso un hash. Trovo il golf migliore per un dato numero e uso quelli più piccoli per trovare quelli più grandi.

Gli hash ricorsivi sono così divertenti!


2

Python, 64014 caratteri

Non sapevo nulla di Brainflak prima di questa sfida e mi sono solo dato un po 'di scherzo su Tryitonline, quindi ci potevano essere scorciatoie ovvie che mi mancavano. Questa è una soluzione abbastanza noiosa, divide semplicemente l'input in x=x/2+x%2o x=x/3+x%3, qualunque sia il più breve.

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)+")"

Chiamalo così: b(42)

uscita su pastebin


1

Lua, 64664 byte

Il programma stampa la lunghezza totale dei programmi e il programma per il 203esimo primo (c'è una linea che puoi cambiare per cambiare quella che è stampata, oppure decommentare una linea per stampare tutti i programmi)

Al momento l'unica ottimizzazione è x = 2 * n + 1

Spero di avere il tempo di aggiungere alcune ottimizzazioni per abbassare il punteggio.

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.