Saída de uma expressão à prova de base

21

fundo

Em alguns futuros possíveis, o mundo converterá seus sistemas numéricos de decimal (base 10 ou b10) para outra base (binária b2, octal b8, hexadecimal b16ou até unária b1, caso em que estamos ferrados!). Assim, em preparação para esse possível evento de mudança mundial, você decide fazer a prova de base de todos os seus programas. Isto pode ser feito usando apenas singulares 0s e 1s em conjunto com os operadores para substituir as constantes numéricas existentes.

No entanto, há apenas um problema: você tem uma tonelada de programas para alterar e a conversão manual de cada número em uma expressão levaria semanas! Assim, você decide escrever um programa (ou função) para decidir qual expressão deve substituir cada número.

Entrada

A entrada será um número inteiro positivo. Seu código deve ser capaz de lidar com qualquer número inteiro de até 1000.

(Se o seu código suportar entradas decimais e / ou negativas, consulte Pontuação abaixo.)

Saída

Seu código deve gerar uma expressão que seja avaliada para a entrada em pelo menos um idioma. Este pode ser qualquer idioma; não precisa ser o mesmo em que seu programa ou função está escrito. Além disso, essa expressão não precisa ser um programa ou função completo.

Para maior clareza, a saída pode conter qualquer uma destas operações:

  • incremento / decremento
  • adicionar / somar
  • subtrair / negar
  • multiplicar / dobrar (apenas se não envolver diretamente o número 2!)
  • dividir / módulo
  • expoentes / logaritmos
  • square / sqrt (novamente, apenas se estes não envolverem diretamente o número 2!)
  • operações bit a bit (bOR, BAND, bNOT, bXOR, troca de bits)
  • configurando / obtendo variáveis
  • manipulação de pilha

Você não pode usar eval()ou quaisquer funções semelhantes na saída. Você também não pode usar na saída quaisquer funções que executem uma (s) ação (ões) além das mencionadas acima.

Ah, e mais uma coisa: como queremos que a saída seja válida no maior número de bases possível, as únicas constantes de número que ela pode conter são 0e 1. Números como 10(dez) não são permitidos, a menos que o idioma o interprete como a 1e a 0. O uso de strings para conter números também não é permitido, assim como o uso de caracteres como CJam's A- K(que representam 10- 20).

Casos de teste

(Todas as saídas estão em JavaScript, mas podem funcionar em outros idiomas.)

Entrada 1:

2

Possível saída 1:

1+1

Entrada 2:

13

Saída possível 2:

(a=1+1+1)*a+a+1

Entrada 3:

60

Possível saída 3:

(b=(a=1+1+1+1)*a)*a-a

Entrada 4:

777

Saída possível 4:

(c=(b=((a=1+1+1+1)*a-a+1)*a)*a+b)+c+c-a+1

Entrada 5:

1000

Saída possível 5:

Math.pow((a=1+1+1)*a+1,a)

Pontuação

O objetivo desse desafio é reduzir o máximo possível a saída do seu código. Sua pontuação será calculada desta maneira:

  • Pontuação básica: a contagem média de bytes de todas as saídas para números inteiros de 1 a 1000.

  • Pontuação decimal: se o seu código suportar pelo menos três casas decimais, essa é a contagem média de bytes de todas as saídas da sequência de números começando 0.001e terminando em 1000, aumentando a 1.001cada vez. 0.001, 1.002, 2.003...998.999, 1000.000Então tire 50% dessa pontuação.

  • Pontuação negativa: se o seu código suportar números negativos e zero, esta é a contagem média de bytes das saídas de todos os números inteiros de -1000até 0. Em seguida, tire 10% dessa pontuação.

(A maneira mais fácil de calcular isso provavelmente seria um loop com seu programa / função dentro.)

Sua pontuação final é a média de qualquer uma das fórmulas acima.

Se a saída for não determinística (ou seja, um pouco aleatória; várias execuções com a mesma entrada produzem várias saídas exclusivas), a pontuação de cada entrada é determinada pela maior saída em dez execuções na minha CPU.

Além disso, como você não sabe como serão preciosos os dados do computador no futuro, a contagem de bytes do código do gerador deve ser menor que 512 bytes.

A pontuação mais baixa em duas semanas (em 30 de setembro) será declarada vencedora. Parabéns ao seu vencedor, @ThomasKwa !


Entre os melhores

Para garantir que sua resposta seja exibida corretamente, inicie-a com este cabeçalho:

# Language name/Other language name, X points

Onde Xestá a pontuação da sua resposta. Exemplo:

# CJam/Pyth, 25.38 points

Se você tiver alguma dúvida ou sugestão, entre em contato. Boa sorte!

ETHproductions
fonte
Posso usar variáveis ​​que retêm 0ou 1por padrão?
Dennis
@ Dennis Não vejo nenhum problema com isso, então vá em frente!
ETHproductions
Estou assumindo que não posso fazer a conversão de base entre a base 2 e a base padrão.
Blue
@muddyfish Não, nenhuma conversão básica é permitida na saída.
ETHproductions
Eu acho que nós também não temos permissão para usar algo como Integer.parseInt("1000", 1+1+1+1+1+1+1+1+1+1)? Tenho certeza de que parseIntusa apenas a operações permitidas ;-)
Paulo Ebermann

Respostas:

10

Código da máquina Python / Zilog Z80, 11.653 11.488

import math,numpy as np
def R(n):
    if n==0:return []
    if n<0:return -R(-n)
    e=int(math.log(n,2))
    if n >= 5/3 * 2**e:
        return np.append(2**(e+1),-R(2**(e+1)-n))
    return np.append(2**e,R(n-2**e))

def strR(n):
    b = R(n)
    s = ""
    if n==0:return s
    e=max(abs(b))
    while e:
        if e in b:s+="#"
        elif -e in b:s+="+"
        s+=")"
        e//=2
    return s[:-1]

Bônus: números negativos.

Supõe que o hlpar de registradores inicialmente contenha 0 e retorne o resultado em hl.

Somente estas três instruções são usadas:

ASCII   Hex    Instruction
--------------------------
#       23     inc hl
)       29     add hl,hl
+       2B     dec hl

Utilizamos uma pequena modificação da representação binária equilibrada de peso mínimo BBR2 . Como o BBR2 minimiza o peso (número de dígitos diferentes de zero), mas queremos minimizar o peso mais o número de trocas de bits, alteramos uma constante no algoritmo de 3/2para 5/3.

Para calcular a pontuação e verificar, use este código:

def verify(n):
v = 0
for c in strR(n):
    if c=="#":v += 1
    elif c=="+":v -= 1
    else: v *= 2
return v==n

print(0.5*(sum([len(strR(n)) for n in range(1,1001)])/1000 + \
           sum([len(strR(n)) for n in range(-1000,1)])/1001 * 0.9))

print(all([verify(n) for n in range(-1000,1001)]))

Exemplo de saída:

strR(486)
         '#)))))+)+))+)'

Ou na montagem:

inc hl \ add hl,hl \ add hl,hl \ add hl,hl \ add hl,hl \ add hl,hl \ dec hl \ add hl,hl \ dec hl \ add hl,hl \ add hl,hl \ dec hl \ add hl,hl

Mais exemplos de programas:

-256  +))))))))
-255  +))))))))#
-254  +)))))))#)
-253  +)))))))#)#
-252  +))))))#))
-251  +))))))#))#
-250  +))))))#)#)
-249  +)))))#)))+
-248  +)))))#)))
-247  +)))))#)))#
-246  +)))))#))#)
-245  +)))))#))#)#
-244  +)))))#)#))
-243  +)))))#)#))#
-242  +))))#)))+)
-241  +))))#))))+

  -5  +))+
  -4  +))
  -3  +)+
  -2  +)
  -1  +
   0  
   1  #
   2  #)
   3  #)#
   4  #))
   5  #))#

Otimizações possíveis: regras OP que os inc he dec hinstruções, que mudam diretamente o byte superior hl, são ilegais, mas sla he os indocumentados sl1 h(turnos esquerda pouco a 1 sobre hessa mudança em um 0e 1são permitidas, respectivamente). sla he sl1 htêm dois bytes cada, mas às vezes podem reduzir a saída.

lirtosiast
fonte
Muito bom, o mais baixo até agora! Eu acho que esse é um caso em que o código de máquina puro é útil. ;)
ETHproductions
2
+1 é provavelmente imbatível. Também para o gênio de usar o código de máquina (em uma CPU com um conjunto de instruções em grande parte, de 8 bits e alguns registradores de 16 bits.)
Nível River St
É estranho como isso se +traduz dec. Eu continuo lendo os exemplos negativos erradamente.
ETHproductions
9

CJam / CJam, 143.263 42.713 28.899 23.901 21.903 20.468

ri
[
    ['X\2b1>e`{~{"1)*)"*}{_({(')*1\"m<"}{"1)*"*}?}?}/]s
    "X1)*"/"1)"*
    "1)1)*"/"1)))"*
    "X1)m<"/"1)))"*
    _"1)"/("1):Y"+\'Y*+
]
{,}$0=

Nenhum bônus se aplica.

Experimente online: exemplo de execução | calculadora de pontuação | verificação

Execuções de exemplo

   1 X
   2 1)
   3 1))
   4 1)))
   5 1))))
   6 1))1)*
   7 1))1)*)
   8 X1))m<
   9 1)))1)*)
  10 1))))1)*
  11 1))))1)*)
  12 1))1)m<
  13 1))1)*1)*)
  14 1))1)*)1)*
  15 1))1)*)1)*)
  16 X1)))m<
  17 X1))m<1)*)
  18 1)))1)*)1)*
  19 1)))1)*)1)*)
  20 1))))1)m<
 981 1):Y)Y*)Y*)Y*Y*)Y*Y*)Y*Y*)
 982 1):Y)Y*)Y*)Y*Y*)Y*Y*)Y*)Y*
 983 1):Y)Y*)Y*)Y*Y*)Y*Y*)Y*)Y*)
 984 1):Y)Y*)Y*)Y*Y*)Y*)Y)m<
 985 1):Y)Y*)Y*)Y*Y*)Y*)Ym<Y*)
 986 1):Y)Y*)Y*)Y*Y*)Y*)Y*Y*)Y*
 987 1):Y)Y*)Y*)Y*Y*)Y*)Y*Y*)Y*)
 988 1):Y)Y*)Y*)Y*Y*)Y*)Y*)Ym<
 989 1):Y)Y*)Y*)Y*Y*)Y*)Y*)Y*Y*)
 990 1):Y)Y*)Y*)Y*Y*)Y*)Y*)Y*)Y*
 991 1):Y)Y*)Y*)Y*Y*)Y*)Y*)Y*)Y*)
 992 1):Y)Y*)Y*)Y*)Y)))m<
 993 1):Y)Y*)Y*)Y*)Y))m<Y*)
 994 1):Y)Y*)Y*)Y*)Y)m<Y*)Y*
 995 1):Y)Y*)Y*)Y*)Y)m<Y*)Y*)
 996 1):Y)Y*)Y*)Y*)Ym<Y*)Ym<
 997 1):Y)Y*)Y*)Y*)Ym<Y*)Y*Y*)
 998 1):Y)Y*)Y*)Y*)Ym<Y*)Y*)Y*
 999 1):Y)Y*)Y*)Y*)Ym<Y*)Y*)Y*)
1000 1):Y)Y*)Y*)Y*)Y*Y*)Y)m<
Dennis
fonte
Minha palavra, isso foi rápido! Os links não funcionam no Firefox, no entanto.
ETHproductions
Como esse não é um código de golfe, substituí cada %um por uma expressão mais longa. Os links devem funcionar agora.
Dennis
Entrada 34 dá 1. No qual a entrada é que funciona melhor
Kishan Kumar
2
@KishanKumar A verificação testa todas as 1000 entradas possíveis. A saída 1 indica que a comparação foi bem-sucedida.
Dennis
Você poderia adicionar alguns exemplos de saídas?
Paŭlo Ebermann 16/09
3

ß / BrainFuck, 34.201 pontos

fonte ß (194B):

E='++[------>+<]>++'°\c[1]<0°{E&='.'µA=ß"-ß°°c[1]),'')µE&='+++'°/B=1°(A[0]°\A[B]='.'°{µE&='--.++'°]E&=ß~A'+',A[B])&'.'&ß~A'-',A[B])°}°)°'ß"&E,'+-')+ß"&E,'-+')>0µE=ß"'ß"'E,'-+',''),'+-','')°!€E)

Se alguém estiver interessado, adicionarei uma explicação. A saída BF já está bastante otimizada, mas acho que eu poderia usar o restante 318B de código ß para implementar

  • uma otimização de aninhamento de loop,
  • mais atalhos de estouro de 8 bits,
  • remoção de colisão do operador .

Amostras:

Correndo no Windows:

$ sharps encode.ss 42
++[------>+<]>+++++++++.--.--

$ sharps encode.ss -42
++[------>+<]>++.+++++++.--.--

$ sharps encode.ss 1.427
++[------>+<]>++++++.---.++++++.--.+++++.-------

$ sharps encode.ss -946.427
++[------>+<]>++.++++++++++++.-----.++.--------.++++++.--.+++++.-------

Rodando em Linux:

$ WINEDEBUG=-all wine sharps source.ss -4.72
++[------>+<]>++.+++++++.------.+++++++++.-----.--

Valide no intérprete online de BF .

Pontuações:

  1. Média base = 37.495.
  2. Média decimal = 60.959 * 0.5 = ~30.48.
  3. Média negativa = 38.4765234765235 * 0.9 = ~34.629
  4. Média do resultado anterior, pontuação final = (37.495 + 30.48 + 34.629)/3 = 34.201.
mınxomaτ
fonte
11
Eu sempre gosto de ver os novos idiomas que as pessoas criaram. :) Obrigado pela classificação! Gostaria de colocar mais bônus na parte decimal, então alterei a dedução de 40% para 50%.
ETHproductions
@ETHproductions Sim, tentarei configurar um intérprete online para isso. Existem cerca de 435 operadores altamente abstratos, 9,9k adicionais podem ser definidos ;-). Corrigi o cálculo (espero).
mınxomaτ 17/09/2015
3

Ruby / Ruby, 29.77885

31,873 * 0,9 (negativo) 30,872 (positivo).

A estratégia básica é a representação simétrica da base 3 ("ternário balanceado"), ou seja, quando os dígitos estão em -1,0,1vez de0,1,2

#function
f=->n{m=n  
  a='0' 
  7.times{|i|
    r=m%3;r-=r/2*3
    m=(m-r)/3
    #produce expression: replace 0 with (0*x+-1)
    #only add 0*x if there are higher base 3 digits to follow.
    #only add (..+-1) if the current base 3 digit is nonzero. 
    a.sub!('0',['','(','('][r]+(m.abs>0?'0*x':'')+['','+1)','-1)'][r])
  }
  #tidy up expression
  a.sub!('(-1)*','-')          #remove internal (-1)*
  a.sub!('(+1)*','')           #remove internal (+1)*
  a[-1]==')' && a=a[1..-2]     #remove unnecessary global brackets
  a.sub!('x','(x=1+1+1)')      #find the first x and define it as 1+1+1=3
  #special cases for small numbers 
  n.abs<8 && a=n==0?'0':['','1'+'+1'*(n-1).abs,'-1'*n.abs][n<=>0] 
  a 
}

#call like this
(1..1000).each{|p|
b=f.call(p)
puts b

Aqui está a saída de 0 a 40 antes da limpeza

(+1)
((+1)*x-1)
(+1)*x
((+1)*x+1)
(((+1)*x-1)*x-1)
((+1)*x-1)*x
(((+1)*x-1)*x+1)
((+1)*x*x-1)
(+1)*x*x
((+1)*x*x+1)
(((+1)*x+1)*x-1)
((+1)*x+1)*x
(((+1)*x+1)*x+1)
((((+1)*x-1)*x-1)*x-1)
(((+1)*x-1)*x-1)*x
((((+1)*x-1)*x-1)*x+1)
(((+1)*x-1)*x*x-1)
((+1)*x-1)*x*x
(((+1)*x-1)*x*x+1)
((((+1)*x-1)*x+1)*x-1)
(((+1)*x-1)*x+1)*x
((((+1)*x-1)*x+1)*x+1)
(((+1)*x*x-1)*x-1)
((+1)*x*x-1)*x
(((+1)*x*x-1)*x+1)
((+1)*x*x*x-1)
(+1)*x*x*x
((+1)*x*x*x+1)
(((+1)*x*x+1)*x-1)
((+1)*x*x+1)*x
(((+1)*x*x+1)*x+1)
((((+1)*x+1)*x-1)*x-1)
(((+1)*x+1)*x-1)*x
((((+1)*x+1)*x-1)*x+1)
(((+1)*x+1)*x*x-1)
((+1)*x+1)*x*x
(((+1)*x+1)*x*x+1)
((((+1)*x+1)*x+1)*x-1)
(((+1)*x+1)*x+1)*x
((((+1)*x+1)*x+1)*x+1)

E após a limpeza

0
1
1+1
1+1+1
1+1+1+1
1+1+1+1+1
1+1+1+1+1+1
1+1+1+1+1+1+1
(x=1+1+1)*x-1
(x=1+1+1)*x
(x=1+1+1)*x+1
((x=1+1+1)+1)*x-1
((x=1+1+1)+1)*x
((x=1+1+1)+1)*x+1
(((x=1+1+1)-1)*x-1)*x-1
(((x=1+1+1)-1)*x-1)*x
(((x=1+1+1)-1)*x-1)*x+1
((x=1+1+1)-1)*x*x-1
((x=1+1+1)-1)*x*x
((x=1+1+1)-1)*x*x+1
(((x=1+1+1)-1)*x+1)*x-1
(((x=1+1+1)-1)*x+1)*x
(((x=1+1+1)-1)*x+1)*x+1
((x=1+1+1)*x-1)*x-1
((x=1+1+1)*x-1)*x
((x=1+1+1)*x-1)*x+1
(x=1+1+1)*x*x-1
(x=1+1+1)*x*x
(x=1+1+1)*x*x+1
((x=1+1+1)*x+1)*x-1
((x=1+1+1)*x+1)*x
((x=1+1+1)*x+1)*x+1
(((x=1+1+1)+1)*x-1)*x-1
(((x=1+1+1)+1)*x-1)*x
(((x=1+1+1)+1)*x-1)*x+1
((x=1+1+1)+1)*x*x-1
((x=1+1+1)+1)*x*x
((x=1+1+1)+1)*x*x+1
(((x=1+1+1)+1)*x+1)*x-1
(((x=1+1+1)+1)*x+1)*x
(((x=1+1+1)+1)*x+1)*x+1
Level River St
fonte
Eu acredito que é chamado de "ternário equilibrado".
lirtosiast 17/09/2015
@ThomasKwa editado em, obrigado
Level River St
3

Ceilão / Ceilão, 49,86 40,95 pontos

A terceira versão usa o Ceilão 1.2 para o gerador e 509 bytes de código:

import ceylon.language{S=String,I=Integer,e=expand}S q(I n)=>n==0then"0"else(n<0then"-"+p(-n,"-")else p(n,"+"));variable Map<[I,S],S>c=map{};S p(I n,S s){S v=c[[n,s]]else(n<8then s.join([1].repeat(n)))else(let(a="+-".replace(s,""))e(e{for(x in 2..8)let(l=(n^(1.0/x)).integer){for(r in l:2)if(r>1)let(w=r^x){if(w-n<n)"("+p(r,"+")+")^("+p(x,"+")+")"+(w<n then s+p(n-w,s)else(n<w then a+p(w-n,a)else""))}}}).reduce<S>((x,y)=>x.size<y.size then x else y))else"";c=[n,s]in c then c else map{[n,s]->v,*c};return v;}

Ele chega a 35,22 pontos, mas não vou colocar isso na linha de título porque o Celyon 1.2 foi publicado apenas em 29 de outubro. Acho que não seria capaz de implementar esse algoritmo no Ceilão 1.1 nesse tamanho.). Mais detalhes lá embaixo, aqui vou descrever a segunda versão. (A primeira versão pode ser vista na história - suportava apenas números positivos, mas cabia em 256 bytes.)

Segunda versão

Agora, a segunda versão, que suporta números inteiros negativos (e 0), e geralmente cria uma saída um pouco mais curta usando além disso -. (Esta versão realmente usa o comprimento permitido, o primeiro tentou permanecer com menos de 256 bytes em vez de 512.)

String proof(Integer n) {
    if (n == 0) { return "0"; }
    if (n < 0) { return "-" + p(-n, "-"); }
    return p(n, "+");
}
String p(Integer n, String sign) {
    if (n < 9) {
        return sign.join([1].repeat(n));
    }
    value anti = (sign == "+") then "-" else "+";
    value root = ((n^0.5) + 0.5).integer;
    return "(" + p(root, "+") + ")^(1+1)" +
       ( (root^2 < n) then sign + p(n - root^2, sign) else
         ((n < root^2) then anti + p(root^2 - n, anti) else ""));
}

O código tem comprimento 487, então ainda há espaço para mais otimizações posteriormente. (Também existem muitas reservas em forma de espaço em branco e nomes longos de variáveis.)

A pontuação:

Total positive: 42652
Average positive:42.652
Total negative: 43653
Average negative: 43.60939060939061
With bonus:39.24845154845155
Overall score: 40.95022577422577

Algumas saídas de amostra:

   27:  21: (1+1+1+1+1)^(1+1)+1+1
   28:  23: (1+1+1+1+1)^(1+1)+1+1+1
   29:  25: (1+1+1+1+1)^(1+1)+1+1+1+1
   30:  27: (1+1+1+1+1)^(1+1)+1+1+1+1+1
   31:  29: (1+1+1+1+1+1)^(1+1)-1-1-1-1-1
   32:  27: (1+1+1+1+1+1)^(1+1)-1-1-1-1
   33:  25: (1+1+1+1+1+1)^(1+1)-1-1-1
   34:  23: (1+1+1+1+1+1)^(1+1)-1-1

  -27:  22: -(1+1+1+1+1)^(1+1)-1-1
  -28:  24: -(1+1+1+1+1)^(1+1)-1-1-1
  -29:  26: -(1+1+1+1+1)^(1+1)-1-1-1-1
  -30:  28: -(1+1+1+1+1)^(1+1)-1-1-1-1-1
  -31:  30: -(1+1+1+1+1+1)^(1+1)+1+1+1+1+1
  -32:  28: -(1+1+1+1+1+1)^(1+1)+1+1+1+1
  -33:  26: -(1+1+1+1+1+1)^(1+1)+1+1+1
  -34:  24: -(1+1+1+1+1+1)^(1+1)+1+1


  993:  65: ((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)-(1+1+1+1+1+1)^(1+1)+1+1+1+1+1
  994:  63: ((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)-(1+1+1+1+1)^(1+1)-1-1-1-1-1
  995:  61: ((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)-(1+1+1+1+1)^(1+1)-1-1-1-1
  996:  59: ((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)-(1+1+1+1+1)^(1+1)-1-1-1
  997:  57: ((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)-(1+1+1+1+1)^(1+1)-1-1
  998:  55: ((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)-(1+1+1+1+1)^(1+1)-1
  999:  53: ((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)-(1+1+1+1+1)^(1+1)
 1000:  55: ((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)-(1+1+1+1+1)^(1+1)+1

 -993:  66: -((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)+(1+1+1+1+1+1)^(1+1)-1-1-1-1-1
 -994:  64: -((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)+(1+1+1+1+1)^(1+1)+1+1+1+1+1
 -995:  62: -((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)+(1+1+1+1+1)^(1+1)+1+1+1+1
 -996:  60: -((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)+(1+1+1+1+1)^(1+1)+1+1+1
 -997:  58: -((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)+(1+1+1+1+1)^(1+1)+1+1
 -998:  56: -((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)+(1+1+1+1+1)^(1+1)+1
 -999:  54: -((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)+(1+1+1+1+1)^(1+1)
-1000:  56: -((1+1+1+1+1+1)^(1+1)-1-1-1-1)^(1+1)+(1+1+1+1+1)^(1+1)-1

    1:   1: 1
    2:   3: 1+1
    3:   5: 1+1+1
    4:   7: 1+1+1+1
    5:   9: 1+1+1+1+1
    6:  11: 1+1+1+1+1+1
    7:  13: 1+1+1+1+1+1+1
    8:  15: 1+1+1+1+1+1+1+1
    9:  13: (1+1+1)^(1+1)
   10:  15: (1+1+1)^(1+1)+1

    0:   1: 0
   -1:   2: -1
   -2:   4: -1-1
   -3:   6: -1-1-1
   -4:   8: -1-1-1-1
   -5:  10: -1-1-1-1-1
   -6:  12: -1-1-1-1-1-1
   -7:  14: -1-1-1-1-1-1-1
   -8:  16: -1-1-1-1-1-1-1-1
   -9:  14: -(1+1+1)^(1+1)
  -10:  16: -(1+1+1)^(1+1)-1

Como você pode ver, os negativos são sempre um byte (o principal -) mais longo que os positivos correspondentes.

A idéia base é a mesma do programa anterior: encontre um quadrado próximo ao número de destino e represente sua raiz e o restante recursivamente. Mas agora permitimos que nosso quadrado também seja um pouco maior que o número alvo, o que torna o restante negativo. (O valor +0.5pode ser alterado para uma constante diferente para ajustar o algoritmo, mas parece que eu já atingi o ideal aqui - 0,4 e 0,6 dão resultados piores.)

Para tornar negativos os valores negativos (e, de outra forma, ter a mesma estrutura que os positivos, passamos o operador signpara nossa função recursiva p- "+"ou seja, ou é) "-". Podemos usar isso para o marceneiro nos casos triviais (ou seja, n <9) também quanto ao restante, se for positivo, e use o sinal oposto para o restante, se for negativo.

A prooffunção lida com o sinal inicial (com um caso especial para 0), a pfunção faz o trabalho real, com recursão.

Terceira versão, para o Ceilão 1.2

import ceylon.language { S=String, I=Integer,e=expand }

// output a base-proof Ceylon expression for an integer
// (i.e. using only 0 and 1 as digits).
//
// Question: http://codegolf.stackexchange.com/q/58084/2338
// My Answer:  http://codegolf.stackexchange.com/a/58122/2338
//
// The goal is to produce an expression as short as possible, with
// the code staying under 512 bytes in length.
//
// This approach is to represent a positive integer as a square
// of a positive integer plus some remainder (where the remainder
// can be negative), and for negative integers replace the + on the
// outer level by -.

S q(I n) =>
        n == 0 then "0"
        else (n < 0 then "-" + p(-n, "-")
            else p(n, "+"));

// cache for values of p
variable Map<[I, S],S> c = map { };

// Transforms a positive number into a base-proof term, using
// the given sign for the summation on the outer level.
S p(I n, S s) {
    S v =
    // look into the cache
            c[[n, s]] else (
        // hard-code small numbers
        n < 8 then s.join([1].repeat(n)))
            else
    // do the complicated stuff
    (let (a = "+-".replace(s,""))
            e(e {
                    for (x in 2..8) // try these exponents
                        let (l = (n ^ (1.0 / x)).integer) // \[ sqrt[exp]{n} \] in LaTeX
                            { for (r in l:2) // lowerRoot, lowerRoot + 1
                                    if (r > 1)
                                        let (w = r ^ x)
                                            { if (w-n < n) // avoid recursion to larger or same number
                                                    // format the string as  r^x + (n-w)
                                                    "(" + p(r, "+") + ")^(" + p(x, "+") + ")" +
                                                            (w < n then s + p(n - w, s)
                                                                else (n < w then a + p(w - n, a)
                                                                    else ""))
                                            } } })
            // and now find the shortest formatted string
                .reduce<S>((x, y) => x.size < y.size then x else y))
    // this should never happen, but we can't tell the compiler
    // that at least some of the iterables are non-empty due to the if clause.
            else "";

    // this builds a new cache in each step – quite wasteful,
    // as this also happens when the value was found in the cache,
    // but we don't have more characters remaining.
    //// c = map { [n, s] -> v, *c };
    ///better way:
     c = [n,s] in c then c else map{[n,s]->v, *c}; 
    return v;
}

A versão em golf (por exemplo, comentários e espaços em branco removidos) é postada na parte superior, com exatamente 509 bytes de código.

Isso usa o mesmo princípio básico da segunda versão, mas, em vez de apenas quadrados, também tenta usar potências de números mais altas (tentando expoentes de 2 a 8) e usa o resultado mais curto. Ele também armazena em cache os resultados, caso contrário isso seria inaceitavelmente lento para números maiores com muitas chamadas recursivas.

Pontuação:

Total positive: 36622
Average positive: 36.622
Total negative: 37623
Average negative: 37.58541458541458
With bonus:33.826873126873124
Overall score: 35.22443656343656

A grande construção recuada no meio são três compreensões iteráveis ​​aninhadas, as duas internas dentro de uma expressão let. Eles são aninhados usando a função de expansão duas vezes e a reducefunção encontra o menor desses caracteres.

Arquivei uma solicitação de recurso para poder fazer isso em uma única compreensão.

Dentro da compreensão, estamos construindo uma cadeia a partir da raiz r, do expoente xe do restante ( n-wou w-n).

A letexpressão e a mapfunção são novas no Ceilão 1.2. mappoderia ter sido substituído por HashMap(que precisaria de mais caracteres para a importação, embora provavelmente fosse ainda mais rápido, pois eu não criaria o mapa novo para cada nova entrada). As letexpressões like let (w = r ^ x)poderiam ter sido substituídas usando uma ifcláusula like if(exists w = true then r ^ x)(e então eu também não precisaria das duas expandchamadas), mas isso ainda seria um pouco mais longo, não cabendo nos 511 bytes permitidos.

Aqui, as saídas de amostra correspondentes às selecionadas acima, todas elas, exceto os números realmente pequenos, são mais curtas:

   27:  15: (1+1+1)^(1+1+1)
   28:  17: (1+1+1)^(1+1+1)+1
   29:  19: (1+1+1)^(1+1+1)+1+1
   30:  21: (1+1)^(1+1+1+1+1)-1-1
   31:  19: (1+1)^(1+1+1+1+1)-1
   32:  17: (1+1)^(1+1+1+1+1)
   33:  19: (1+1)^(1+1+1+1+1)+1
   34:  21: (1+1)^(1+1+1+1+1)+1+1

  -27:  16: -(1+1+1)^(1+1+1)
  -28:  18: -(1+1+1)^(1+1+1)-1
  -29:  20: -(1+1+1)^(1+1+1)-1-1
  -30:  22: -(1+1)^(1+1+1+1+1)+1+1
  -31:  20: -(1+1)^(1+1+1+1+1)+1
  -32:  18: -(1+1)^(1+1+1+1+1)
  -33:  20: -(1+1)^(1+1+1+1+1)-1
  -34:  22: -(1+1)^(1+1+1+1+1)-1-1

  993:  39: ((1+1+1)^(1+1)+1)^(1+1+1)-1-1-1-1-1-1-1
  994:  37: ((1+1+1)^(1+1)+1)^(1+1+1)-1-1-1-1-1-1
  995:  35: ((1+1+1)^(1+1)+1)^(1+1+1)-1-1-1-1-1
  996:  33: ((1+1+1)^(1+1)+1)^(1+1+1)-1-1-1-1
  997:  31: ((1+1+1)^(1+1)+1)^(1+1+1)-1-1-1
  998:  29: ((1+1+1)^(1+1)+1)^(1+1+1)-1-1
  999:  27: ((1+1+1)^(1+1)+1)^(1+1+1)-1
 1000:  25: ((1+1+1)^(1+1)+1)^(1+1+1)

 -993:  40: -((1+1+1)^(1+1)+1)^(1+1+1)+1+1+1+1+1+1+1
 -994:  38: -((1+1+1)^(1+1)+1)^(1+1+1)+1+1+1+1+1+1
 -995:  36: -((1+1+1)^(1+1)+1)^(1+1+1)+1+1+1+1+1
 -996:  34: -((1+1+1)^(1+1)+1)^(1+1+1)+1+1+1+1
 -997:  32: -((1+1+1)^(1+1)+1)^(1+1+1)+1+1+1
 -998:  30: -((1+1+1)^(1+1)+1)^(1+1+1)+1+1
 -999:  28: -((1+1+1)^(1+1)+1)^(1+1+1)+1
-1000:  26: -((1+1+1)^(1+1)+1)^(1+1+1)

    1:   1: 1
    2:   3: 1+1
    3:   5: 1+1+1
    4:   7: 1+1+1+1
    5:   9: 1+1+1+1+1
    6:  11: 1+1+1+1+1+1
    7:  13: 1+1+1+1+1+1+1
    8:  13: (1+1)^(1+1+1)
    9:  13: (1+1+1)^(1+1)
   10:  15: (1+1+1)^(1+1)+1

    0:   1: 0
   -1:   2: -1
   -2:   4: -1-1
   -3:   6: -1-1-1
   -4:   8: -1-1-1-1
   -5:  10: -1-1-1-1-1
   -6:  12: -1-1-1-1-1-1
   -7:  14: -1-1-1-1-1-1-1
   -8:  14: -(1+1)^(1+1+1)
   -9:  14: -(1+1+1)^(1+1)
  -10:  16: -(1+1+1)^(1+1)-1

Por exemplo, agora temos 1000 = (3 ^ 2 + 1) ^ 3, em vez de 1000 = (6 ^ 2-4) ^ 2-5 ^ 2 + 1.

Paŭlo Ebermann
fonte
Lembrei-me erroneamente da restrição do programa como 256 bytes ... em 512 é possível fazer muito mais. Vai tentar isso mais tarde.
Pa Elo Ebermann
Não, diz less than 512. Sou você pode usar um max. de 511 bytes;)
mınxomaτ 17/09/2015
Como nunca ouvi falar desse idioma?!? : O Mas sério, excelente explicação! Adoro entender as técnicas que outras pessoas usam em suas respostas. +1
ETHproductions
@ETHproductions Eu também li sobre isso há cerca de duas semanas aqui no site e comecei a gostar. Então, para conhecê-lo melhor, tento responder a perguntas aqui usando o Ceilão.
Paŭlo Ebermann
2

Ruby / dc, 20.296 18,414 16,968

Programaçao dinamica! Define uma lista de funções que, dadas uma instrução dc, retornam uma nova expressão e o valor numérico dessa expressão. Em seguida, começando com 1prefinado, ele cria uma lista de todos os valores alcançáveis, incluindo o valor desejado.

editar:

Adicionou uma função para n-1 e fez executar o algoritmo através de várias passagens. Parece precisar de 7 passes para estabilizar. Teve que diminuir alguns nomes de variáveis ​​para permanecer dentro de 512 bytes.

editar 2:

Adicionadas funções para n (n-1) , n (n + 1) e n ^ 3 enquanto eu estava nisso. Encurtou o código um pouco mais, chegando a exatamente 512 bytes.

N = gets.to_i

fns = [
  ->(n,s){[n-1,   s+'1-']},
  ->(n,s){[n+1,   s+'1+']},
  ->(n,s){[n*2,   s+'d+']},
  ->(n,s){[n*3,   s+'dd++']},
  ->(n,s){[n*~-n, s+'d1-*']},
  ->(n,s){[n*n,   s+'d*']},
  ->(n,s){[n*-~n, s+'d1+*']},
  ->(n,s){[n*n*n, s+'dd**']},
]

lst = []*(N+1)
lst[0..2] = %w[0 1 1d+]

loop do
  prev = lst.dup

  (1..N).each do |n|
    fns.each do |f|
      m,s = f[n, lst[n]]
      lst[m] = s if m <= N && (lst[m].nil? || lst[m].size > s.size)
    end
  end

  break if lst == prev
end

puts lst[N]

Números gerados:

A saída consiste inteiramente em cinco caracteres diferentes: 1empurra o valor 1 na pilha; dduplica a parte superior da pilha; +, -, E * aparece os dois valores superior e empurra a sua soma, diferença, e do produto, respectivamente. Cada expressão gerada adiciona apenas um valor à pilha após a execução.

   1: 1
   2: 1d+
   3: 1dd++
   4: 1d+d+
   5: 1d+d+1+
   6: 1d+dd++
   7: 1d+dd++1+
   8: 1d+dd**
   9: 1dd++d*
  10: 1d+d+1+d+
  11: 1d+d+1+d+1+
  12: 1dd++d1+*
  13: 1dd++d1+*1+
  14: 1d+dd++1+d+
  15: 1d+d+d*1-
  16: 1d+d+d*
  17: 1d+d+d*1+
  18: 1dd++d*d+
  19: 1dd++d*d+1+
  20: 1d+d+d1+*
  21: 1d+d+d1+*1+
  22: 1d+d+1+d+1+d+
  23: 1d+dd**dd++1-
  24: 1d+dd**dd++
  25: 1d+d+1+d*

...

 989: 1d+d+d*d+d1-*1-1-1-
 990: 1d+d+d*d+d1-*1-1-
 991: 1d+d+d*d+d1-*1-
 992: 1d+d+d*d+d1-*
 993: 1d+d+d*d+d1-*1+
 994: 1d+d+d*d+d1-*1+1+
 995: 1d+d+d*d+d1-*1+1+1+
 996: 1d+d+1+dd**d+1-d+d+
 997: 1d+d+1+d+dd**1-1-1-
 998: 1d+d+1+d+dd**1-1-
 999: 1d+d+1+d+dd**1-
1000: 1d+d+1+d+dd**
daniero
fonte
11
Muito bom, superando tudo, menos o código de máquina z80 até o momento (até o Dennis 'CJam!). Você acha que poderá adicionar um -operador enquanto permanecer na contagem de bytes?
ETHproductions
@ETHproductions Como é isso? ;) Também não deve ser difícil adicionar números negativos agora.
Daniero 19/09/2015
0

Python 2.6, 78.069 - 66.265 pontos

Enviando minha resposta para o que vale a pena (não muito nesse caso ... mas demonstrando claramente que, para esse desafio, não basta pensar em compor a saída como uma soma de valores com deslocamento de bit; quando levados em conta que nenhum dígito fora de 0 ou 1 pode aparecer na saída). Posso voltar mais tarde com uma maneira diferente de gerar saída.

O código em si não é muito longo (176 caracteres):

def f(a):return'+'.join(('(1<<%s)'%['0','+'.join('1'*x)][x>0]).replace('(1<<0)','1')for x in[i for i,e in enumerate(bin(a)[::-1][:-2])if int(e)])
print"".join(f(int(input())))

Ele gera uma saída correta, mas detalhada:

17
1+(1<<1+1+1+1)

800
(1<<1+1+1+1+1)+(1<<1+1+1+1+1+1+1+1)+(1<<1+1+1+1+1+1+1+1+1)

Snippet que calcula a pontuação:

def f(a):return'+'.join(('(1<<%s)'%['0','+'.join('1'*x)][x>0]).replace('(1<<0)','1')for x in[i for i,e in enumerate(bin(a)[::-1][:-2])if int(e)])
print sum(len("".join(f(i)))for i in range(1000))
ChristopheD
fonte