Jouez au golf sur un Brain-Flak Integer

28

Les entiers sont fastidieux à représenter dans Brain-Flak . Il y a 8 opérateurs:

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

foopeut être composé de plusieurs opérateurs, auquel cas ils sont évalués et additionnés. Par exemple, (()())pousse 2vers la pile (et évalue 2aussi).

De toute évidence, le (()...())mécanisme n'est pas utile dans Code Golf car un grand nombre prendrait des n*2+2octets à représenter. Votre défi est donc d'écrire un programme ou une fonction qui produira en aussi peu d'octets que possible un programme Brain-Flak qui poussera un entier positif donné nvers la pile active. Ce programme ne doit pas faire d'hypothèses sur le contenu existant des piles, il ne doit donc pas laisser les piles échangées ni ajouter ou supprimer des valeurs supplémentaires des piles.

Bien que votre programme ou fonction doit être capable de renvoyer un programme Brain-Flak fonctionnel pour toutes les entrées de 1 à 1 000 000, le gagnant sera le programme ou la fonction qui génère le plus petit ensemble de programmes Brain-Flak appropriés pour tous les 1061 nombres premiers compris entre 1 000 et 1 000. 10 000 . Vous devez noter la taille totale de vos sorties pour ces 1061 entrées dans le cadre de votre soumission. Votre programme ou fonction peut accepter l'entier et renvoyer le programme (chaîne) Brain-Flak dans l'un des formats d'E / S acceptables habituels. Les liens seront rompus en fonction de la taille de votre programme ou fonction.

Neil
la source
4
Juste une note: le nombre de programmes valides de longueur 2nest 4^n catalan(n).
Leaky Nun
2
Hmm, j'aime le défi, mais je pense qu'il devrait être marqué sur des entiers inconnus. Sinon, les programmes entiers qui sont notés pourraient être forcés brutalement et d'autres entiers simplement laissés comme (()()()...()). De plus, si vous utilisez simplement des nombres premiers, cela pourrait manquer certaines optimisations possibles pour les composites.
DJMcMayhem
Aussi, pourquoi n'est []pas défini pour ce défi? Je trouve étrange de mettre en œuvre 7 des 8 opérateurs. Quoi qu'il en soit, défi cool, je suis honoré que quelqu'un écrive un défi inspiré de ma propre langue!
DJMcMayhem
2
@DJMcMayhem Je veux que les gens puissent calculer leur propre score. Tous les nombres premiers pertinents sont un de plus qu'un nombre composite, il devrait donc y avoir beaucoup d'optimisations potentielles. De plus, je ne veux pas que les gens se fient à une valeur particulière de []dans leur réponse.
Neil
1
@YetiCGN La taille du script ne compte que pour un bris d'égalité.
Neil

Réponses:

16

Python 2, 59394 59244 58534 58416 58394 58250

Ok voici ma solution.

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 fonction pertinente est push(n). Pour l'appeler, appuyez simplement sur l'entier que vous souhaitez représenter.

Explication

La principale optimisation effectuée par le programme est le codage en dur de la multiplication. L'idée du codage en dur de la multiplication est assez simple. Vous poussez le nombre a, puis sautez et poussez-le pour créer une nouvelle valeur. Par exemple, pour multiplier par deux, vous pouvez utiliser le code suivant ((n){})où n code produisant un nombre spécifique. Cela fonctionne parce que les deux (n)et {}ont une valeur de n.

Cette idée simple peut être rendue plus complexe pour de plus grands nombres. Prenez par exemple 5, il a été découvert il y a quelque temps que la meilleure façon de multiplier par cinq était (((n)){}){}{}. Ce code fait deux copies du n multiplie une par 4 et ajoute les deux. En utilisant la même stratégie, je fais chaque multiplication basée sur la représentation binaire d'un nombre. Je n'entrerai pas dans les détails de la façon dont cela fonctionne en ce moment, mais je le fais en coupant la première de la représentation binaire et en remplaçant 0 par ){}et 1 par){}{}. Il s'assure ensuite que n est poussé le nombre approprié de fois et équilibre toutes les parenthèses. (Si vous voulez savoir comment cela se fait, vous pouvez consulter mon code). Si vous voulez savoir pourquoi cela fonctionne, demandez-moi simplement un commentaire. Je ne pense pas que quiconque lise réellement toutes les mises à jour de mon message, j'ai donc laissé les explications.

Lorsque l'algorithme tente de trouver un code dur de multiplication, il tente tous les facteurs premiers d'un nombre. Il ignore les facteurs composites car, à un moment donné, les facteurs composites pourraient toujours être exprimés de manière plus concise comme ses propres facteurs premiers, on ne sait pas si cela est toujours vrai.

L'autre mécanisme d'économie d'octets est un chercheur de solution polynomiale. Il existe certaines formes de polynômes faciles à représenter avec des boucles de décrémentation. Ces polynômes comprennent, mais sans s'y limiter, des nombres polygonaux. Cette optimisation trouve des polynômes qui correspondent au formulaire et crée le code qui les crée.

Sortie

bac à pâte

Assistant de blé
la source
"si n est plus grand ou plus petit que n + 1" ??
Sparr
@Sparr si l'interprétation de nest plus grande ou plus petite quen+1
Wheat Wizard
Vous devez annuler le retrait des lignes de if n % 3 == 2: la fin de cette fonction d'un niveau.
user202729
13

Brain-Flak, 64664

Essayez-le en ligne!

Voici mon code annoté

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

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

Explication

Cela ne met en œuvre que deux règles à partir de maintenant:

  • Si n est divisible par deux, retournez (n/2){}

  • Si n n'est pas divisible par deux, retournez n-1()

Il code également en dur tous les nombres inférieurs à 6.

Assistant de blé
la source
Il semble qu'un contrôle de la divisibilité par trois devrait réduire considérablement le score
ASCII uniquement
@ ASCII uniquement, je l'ai implémenté et cela a augmenté le nombre d'octets. Je travaille sur un moyen de mettre en œuvre une version plus intelligente de la divisibilité par trois.
Wheat Wizard
Ok, en utilisant Brain-Flak pour créer un programme qui génère des nombres Brain-Frak. Agréable.
Draco18s
10

Perl, 59222 59156 58460 caractères

  • n() (11322660 caractères)
  • (n){}() (64664 caractères)
  • ((n)){}{} (63610 caractères)
  • ((n)()){}{} (63484 caractères) - ceci est un nouveau calcul
  • (n){({}[()])}{} (60748 caractères)
  • n[m] (62800 caractères)
  • (n){m({}[l])}{} (58460 caractères) - ceci est un nouveau calcul

La formule pour ce dernier calcul est n(n/l+1)/2+mn/l. J'ai essayé d'autres calculs, mais ils ne sont plus utiles pour la sortie donnée. Le programme génère en fait toutes les valeurs jusqu'à 9999 mais répertorie ensuite les nombres premiers donnés et leur longueur 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);
}
Neil
la source
Pourriez-vous fournir un lien vers la sortie?
DJMcMayhem
@DJMcMayhem Oups, j'avais accidentellement corrompu ma liste de nombres premiers, invalidant mon nombre de caractères.
Neil
@Linus ((X) ()) {} {} pousse X, puis ajoute 1, pousse le résultat, puis fait apparaître X + 1 et X. Total 3X + 2. Je pense que j'ai essayé l'autre formule sur Try It Online, mais je peux vérifier si vous le souhaitez.
Neil
@Neil Mon erreur ... Celles-ci semblent bonnes mais qu'est-ce qui corrompt exactement vos nombres premiers alors?
Linus
1
@Neil J'obtiens 58158 lorsque j'ajoute &try($i * $i, "$numbers[$i]{({})({}[()])}{}");, ce qui descend à 58032 quand j'ajoute également &try((3 * $i * $i - $i) / 2, "$numbers[$i]{({})({}[()])({})}{}");(nombres carrés / pentagonaux) - c'est d' ici
ASCII uniquement
5

Python, 59136 58676 caractères

Fonction golf Brainflak Number:

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

Itération du nombre premier:

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)

Sortie:

Pastebin

Explication:

Nous préremplissons une liste R de représentation de Brain-flak évaluant en entiers individuels sur une plage plus grande que nécessaire [1, m -1] pour définir notre fonction f . Les représentations sont formées en prenant la représentation inutilisée la plus basse (indexée par l ) et en en formant de nombreuses représentations nouvelles, en ne gardant que la plus courte. La représentation inutilisée la plus basse suppose que tous les numéros 1 à 1 ont reçu une représentation et que ces représentations ont déjà été utilisées pour produire de nouveaux nombres. Si une valeur inférieure à l obtient une représentation plus courte, nous devons revenir en arrière et reproduire les nombres à partir de ce point. La fonction f produit un programme enregistrant le nombre dans la pile en ajoutant des parenthèses.

Je ne connaissais aucun Brainflak lorsque j'ai commencé cela, et j'apprécie grandement la réponse d'Eamon Olive pour avoir souligné la formule des nombres triangulaires. La plupart du temps, j'ai généralisé la sommation et je me suis efforcé de vérifier les sommes et les différences. Ajouter beaucoup de multiples de sommes a eu un grand effet.

Pour ceux qui s'en soucient, voici le code à gratter que j'ai utilisé pour voir quelles formules en valaient la peine.

Formules de représentation:

  1. Multiplication par de petits nombres premiers:
    (X){}
    ((X)){}{}
    ((((X)))){}{}{}{}
    ((((((X)))))){}{}{}{}{}{}
  2. Addition X + Y :
    XY
  3. Soustraction X - Y :
    X[Y]
  4. Addition à X inclus de l'incrément Y :
    (X){({}[Y])}{}
  5. Multiples de sommations à X de l'incrément Y , plus X :
    (X)({({}[Y])}{}){}
    (X)(({({}[Y])}{})){}{}
    (X)(({({}[Y])}{}){}){}
    etc ...
Linus
la source
Je pensais que 5 * n'était pas utile, mais je vois maintenant qu'il enregistre 10 caractères sur ma réponse. Je pensais avoir essayé ces sommations, mais je vais revérifier!
Neil
La somme des incréments et des multiples me fait gagner encore 46 octets, et même alors, je dois rincer et répéter trois fois pour les attraper tous.
Neil
Il s'avère que si j'utilise la soustraction, je n'utilise plus 5 *.
Neil
4

Lua 5.3, 57522

En fait, j'ai commencé à travailler là-dessus lorsque la question a été publiée, mais je l'ai oublié jusqu'à l'anniversaire de 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)

Idée similaire aux autres réponses où des fonctions utiles connues sont utilisées pour construire de plus grands nombres à partir de bonnes représentations de nombres plus simples.

Une différence est qu'au lieu de résoudre des sous-problèmes en termes de nombres plus petits, je résous des sous-problèmes en termes de nombres avec des représentations plus courtes. Je pense que cela rend plus élégant de tirer parti des nombres négatifs ainsi que de gérer le cas où les petits nombres sont représentés en termes de plus grands nombres.

De plus, essayer de trouver tous les nombres qui peuvent être représentés dans une certaine taille plutôt que de représenter un nombre particulier le plus rapidement possible simplifie en fait certains calculs. Au lieu de travailler une formule à l'envers pour voir si elle peut être appliquée à un nombre, la formule peut être travaillée vers l'avant et appliquée à chaque nombre.

Une autre différence est que les solutions connues sont stockées en deux parties - un "préfixe" (facultatif) et un "suffixe" (plus comme un infixe). L'évaluation du préfixe devrait être ignorée lors du calcul du nombre donné - le préfixe ne contient que du code qui configure le suffixe à exécuter (généralement en poussant une ou plusieurs choses dans la pile). Donc, étant donné un préfixe et un suffixe, le nombre correspondant peut être poussé sur la pile avec prefix(suffix).

Cette division résout essentiellement le même problème que la unpackfonction dans la réponse de Wheat Wizard. Au lieu d'encapsuler le code avec <...>uniquement pour l'annuler ultérieurement, ce code est simplement ajouté au préfixe.

Dans quelques cas, le préfixe est en fait évalué (principalement pour l'opération de pseudo-exponentiation), donc son évaluation est également stockée. Cependant, cela ne pose pas vraiment de gros problème, car le générateur n'essaie pas de construire des nombres spécifiques. Cela semble impliquer théoriquement qu'il pourrait y avoir deux morceaux de code de la même longueur et générant le même nombre qui ne seraient pas redondants dans le cache en raison de différentes évaluations de préfixe. Je n'ai pas pris la peine de rendre compte de cela, car cela ne semble pas avoir beaucoup d'importance (au moins dans ce domaine).

J'imagine qu'il serait facile de réduire le nombre d'octets simplement en ajoutant plus de cas, mais j'en ai assez pour le moment.

J'ai couru à 1000000, mais j'ai seulement vérifié la santé mentale jusqu'à 100000.

Pastebin de sortie sur des nombres premiers donnés.

tehtmi
la source
Que faire k_limitet k_max_lenfaire? Je ne suis pas sûr de comprendre l'en-tête.
Wheat Wizard
1
Plutôt que d'essayer de calculer des nombres particuliers, je calcule tous les programmes utiles (c'est-à-dire donnant des nombres pas trop grands plus courts que tout autre programme trouvé) jusqu'à une certaine longueur - k_max_len. Il pourrait aussi facilement vérifier qu'il a trouvé tous les nombres que vous avez demandés après avoir traité chaque longueur, mais il m'a été utile de pouvoir limiter la longueur maximale pendant les tests afin que le programme s'exécute plus rapidement. (Le traitement de plus grandes longueurs peut être très lent.) k_limitEst fondamentalement le paramètre d'entrée - il produira des programmes pour des nombres allant jusqu'à cela - en supposant qu'il k_max_lenétait suffisamment grand pour les trouver.
tehtmi
4

rubis, 60246 octets

$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

J'utilise un hachage. Je trouve le meilleur golf pour un nombre donné et utilise les plus petits pour trouver les plus grands.

Les hachages récursifs sont tellement amusants!

MegaTom
la source
2

Python, 64014 caractères

Je ne connaissais rien à brainflak avant ce défi et je ne l'ai joué qu'un peu sur tryitonline, donc il pourrait y avoir des raccourcis évidents que j'ai ratés. C'est une solution assez ennuyeuse, divise simplement l'entrée en x=x/2+x%2ou x=x/3+x%3, selon la plus courte des deux.

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

Appelez-le ainsi: b(42)

sortie sur pastebin

KarlKastor
la source
1

Lua, 64664 octets

Le programme imprime la longueur totale des programmes et le programme pour le 203e premier (il y a une ligne que vous pouvez changer pour changer celle qui est imprimée, ou décommenter une ligne pour imprimer tous les programmes)

À l'heure actuelle, la seule optimisation est x = 2 * n + 1

J'espère que j'aurai le temps d'ajouter quelques optimisations supplémentaires pour réduire le score.

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
la source