Salida de una expresión a prueba de base

21

Fondo

En algunos futuros posibles, el mundo convertirá sus sistemas numéricos de decimal (base 10 o b10) a alguna otra base (binaria b2, octal b8, hexadecimal b16o incluso unaria b1, ¡en cuyo caso estamos jodidos!). Por lo tanto, en preparación para este posible evento que cambiará el mundo, decide probar todos sus programas a prueba de bases. Esto se puede hacer mediante el uso de sólo singulares 0s y 1s en conjunción con los operadores para reemplazar las constantes número existente.

Sin embargo, solo hay un problema: tiene un montón de programas para cambiar, ¡y convertir manualmente cada número en una expresión tomaría semanas! Por lo tanto, decide escribir un programa (o función) para decidir qué expresión debe reemplazar cada número.

Entrada

La entrada será un entero positivo. Su código debe ser capaz de manejar cualquier número entero hasta 1000.

(Si su código admite decimales y / o entradas negativas, consulte la Puntuación a continuación).

Salida

Su código debe generar una expresión que evalúe la entrada en al menos un idioma. Este puede ser cualquier idioma; no tiene que ser el mismo en el que está escrito su programa o función. Además, esta expresión no necesita ser un programa o función completa.

Para mayor claridad, la salida puede contener cualquiera de estas operaciones:

  • incremento / decremento
  • agregar / suma
  • restar / negar
  • multiplicar / duplicar (¡solo si no involucra directamente el número 2!)
  • dividir / módulo
  • exponentes / logaritmos
  • square / sqrt (de nuevo, ¡solo si estos no involucran directamente el número 2!)
  • operaciones bit a bit (bOR, bAND, bNOT, bXOR, bit-shifts)
  • establecer / obtener variables
  • manipulación de pila

Es posible que no se utilice eval()o cualquier funciones similares en la salida. Tampoco puede utilizar en la salida ninguna función que realice una acción o acciones distintas a las mencionadas anteriormente.

Ah, y una cosa más: dado que queremos que la salida sea válida en tantas bases como sea posible, las únicas constantes numéricas que puede contener son 0y 1. Números como 10(diez) no están permitidos, a menos que el idioma lo interprete como a 1y a 0. El uso de cadenas para contener números tampoco está permitido, al igual que el uso de caracteres como CJam's A- K(que representan 10- 20).

Casos de prueba

(Todas las salidas están en JavaScript, pero pueden funcionar en otros idiomas).

Entrada 1:

2

Salida posible 1:

1+1

Entrada 2:

13

Salida posible 2:

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

Entrada 3:

60

Salida posible 3:

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

Entrada 4:

777

Salida posible 4:

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

Entrada 5:

1000

Salida posible 5:

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

Tanteo

El objetivo de este desafío es acortar la salida de su código tanto como sea posible. Su puntaje se calculará de esta manera:

  • Puntuación base: el recuento de bytes promedio de todas las salidas para enteros 1 a 1000.

  • Puntuación decimal: si su código admite al menos 3 decimales, este es el recuento de bytes promedio de todas las salidas de la secuencia de números que comienzan 0.001y terminan en 1000, aumentando en 1.001cada momento. 0.001, 1.002, 2.003...998.999, 1000.000Luego, obtenga un 50% de descuento en este puntaje.

  • Puntuación negativa: si su código admite números negativos y cero, este es el recuento de bytes promedio de las salidas de todos los enteros desde -1000hasta 0. Luego, obtenga un 10% de descuento en este puntaje.

(La forma más fácil de calcularlos probablemente sería un bucle con su programa / función dentro).

Su puntaje final es el promedio de cualquiera de las fórmulas anteriores que se aplican.

Si la salida no es determinista (es decir, algo aleatorio; varias ejecuciones con la misma entrada producen múltiples salidas únicas), entonces la puntuación para cada entrada está determinada por la salida más grande durante diez ejecuciones en mi CPU.

Además, como no sabe cuán valiosos serán los datos de la computadora en el futuro, el recuento de bytes de su código generador debe ser inferior a 512 bytes.

El puntaje más bajo en dos semanas (el 30 de septiembre) será declarado ganador. ¡Felicitaciones a tu ganador, @ThomasKwa !


Tabla de clasificación

Para asegurarse de que su respuesta se muestre correctamente, comience con este encabezado:

# Language name/Other language name, X points

¿Dónde Xestá el puntaje de tu respuesta? Ejemplo:

# CJam/Pyth, 25.38 points

Si tiene alguna pregunta o sugerencia, hágamelo saber. ¡Buena suerte!

ETHproducciones
fuente
¿Puedo usar variables que contengan 0o 1por defecto?
Dennis
@ Dennis No veo ningún problema con eso, ¡así que adelante!
ETHproductions
Supongo que no puedo hacer una conversión de base entre la base 2 y la base predeterminada.
Azul
@muddyfish No, no se permite conversión de base en la salida.
ETHproductions
Supongo que tampoco se nos permite usar algo como Integer.parseInt("1000", 1+1+1+1+1+1+1+1+1+1)? Estoy bastante seguro de que parseIntsolo usa las operaciones permitidas ;-)
Paŭlo Ebermann

Respuestas:

10

Código de 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]

Bonificaciones: números negativos.

Asume que el hlpar de registros inicialmente contiene 0 y devuelve el resultado hl.

Solo se usan estas tres instrucciones:

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

Utilizamos una pequeña modificación de la representación binaria balanceada de mínimo peso BBR2 . Dado que BBR2 minimiza el peso (número de dígitos distintos de cero), pero queremos minimizar el peso más el número de cambios de bits, cambiamos una constante en el algoritmo de 3/2a 5/3.

Para calcular la puntuación y 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)]))

Salida de ejemplo:

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

O en asamblea:

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

Más programas de ejemplo:

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

Posibles optimizaciones: OP reglas que los inc hy dec hlas instrucciones, que cambian directamente el byte superior hl, son ilegales, pero sla hy los indocumentados sl1 h(desplazamientos de bit a la izquierda 1 sobre hese cambio en una 0y 1se permiten, respectivamente). sla hy sl1 hson dos bytes cada uno, pero a veces pueden acortar la salida.

lirtosiast
fuente
Muy bonito, el más bajo hasta ahora! Supongo que esta es una instancia donde el código de máquina puro es útil. ;)
ETHproductions
2
+1 esto es probablemente inmejorable. También para el genio de usar código de máquina (en una CPU con un conjunto de instrucciones de 8 bits y algunos registros de 16 bits).
Level River St
Es extraño cómo se +traduce dec. Sigo leyendo los ejemplos negativos mal.
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=

No se aplican bonificaciones.

Pruébelo en línea: ejecución de muestra | calculadora de puntaje | verificación

Ejecuciones de ejemplo

   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
fuente
¡Mi palabra, eso fue rápido! Sin embargo, los enlaces no funcionan en Firefox.
ETHproductions
Como esto no es código golf, reemplacé cada uno %con una expresión más larga. Los enlaces deberían funcionar ahora.
Dennis
La entrada 34 da 1. ¿En qué entrada funciona mejor?
Kishan Kumar
2
@KishanKumar La verificación prueba las 1000 entradas posibles. La salida 1 indica que la comparación fue exitosa.
Dennis
¿Podría agregar algunos resultados de ejemplo?
Paŭlo Ebermann
3

ß / BrainFuck, 34.201 puntos

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

Si alguien está interesado, agregaré una explicación. La salida BF ya está bastante optimizada, pero supongo que podría usar los 318B restantes de código ß para implementar

  • una optimización de anidación de bucle,
  • más accesos directos de desbordamiento de 8 bits,
  • operador de colisión de eliminación .

Muestras:

Corriendo en windows:

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

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

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

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

Corriendo en Linux:

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

Validar en el intérprete BF en línea .

Puntuaciones:

  1. Base promedio = 37.495.
  2. Promedio decimal = 60.959 * 0.5 = ~30.48.
  3. Promedio negativo = 38.4765234765235 * 0.9 = ~34.629
  4. Promedio de lo anterior, puntaje final = (37.495 + 30.48 + 34.629)/3 = 34.201.
mınxomaτ
fuente
1
Siempre me gusta ver los nuevos idiomas que la gente ha creado. :) Gracias por el desglose de la puntuación! Me gustaría poner más de un bono en la parte decimal, así que he cambiado la deducción del 40% al 50%.
ETHproductions
@ETHproductions Sí, intentaré configurar un intérprete en línea para esto. Hay alrededor de 435 operadores altamente abstractos, se pueden definir 9,9k adicionales ;-). He corregido el cálculo (con suerte).
mınxomaτ
3

Ruby / Ruby, 29.77885

31.873 * 0.9 (negativo) 30.872 (positivo).

La estrategia básica es la representación simétrica de base 3 ("ternario equilibrado"), es decir, cuando los dígitos son en -1,0,1lugar 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

Aquí está la salida de 0 a 40 antes de la limpieza

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

Y despues de la limpieza

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
fuente
Creo que se llama "ternario equilibrado".
lirtosiast
@ThomasKwa editado en, gracias
Level River St
3

Ceilán / Ceilán, 49.86 40.95 puntos

La tercera versión usa Ceylon 1.2 para el generador y 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;}

Se reduce a 35,22 puntos, pero no lo pondré en la línea del título porque Celyon 1.2 solo se publicó el 29 de octubre. No creo que pueda implementar este algoritmo en Ceylon 1.1 en este tamaño). Más detalles allí abajo, aquí describiré la segunda versión. (La primera versión se puede ver en el historial: solo admitía números positivos, pero cabía en 256 bytes).

Segunda versión

Ahora, la segunda versión, que admite enteros negativos (y 0), y generalmente crea una salida un poco más corta al usar además -. (Esta versión en realidad usa la longitud permitida, la primera intentó permanecer por debajo de 256 bytes en lugar 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 ""));
}

El código tiene una longitud de 487, por lo que aún queda espacio para más optimizaciones más adelante. (También hay muchas reservas en forma de espacios en blanco y nombres largos de variables).

La puntuación:

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

Algunas salidas de muestra:

   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 puede ver, los negativos son siempre un byte (el principal -) más largos que los positivos correspondientes.

La idea base es la misma que el programa anterior: encuentre un cuadrado cerca de nuestro número objetivo y represente su raíz y el resto de forma recursiva. Pero ahora permitimos que nuestro cuadrado sea también más grande que el número objetivo, lo que hace que el resto sea negativo. (Se +0.5puede cambiar a una constante diferente para ajustar el algoritmo, pero parece que ya llegué al óptimo aquí; tanto 0.4 como 0.6 dan peores resultados).

Para hacer que los valores negativos sean negativos (y de lo contrario tener la misma estructura que los positivos, pasamos el operador signa nuestra función recursiva p, es decir, "+"o "-". Podemos usar eso para la combinación en los casos triviales (es decir, n <9) también en cuanto al resto si es positivo, y use el signo opuesto para el resto si es negativo.

La prooffunción maneja el signo inicial (con un caso especial para 0), la pfunción hace el trabajo real, con recursividad.

Tercera versión, para Ceilán 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;
}

La versión de golf (es decir, comentarios y espacios en blanco eliminados) se publica en la parte superior, a exactamente 509 bytes de código.

Esto usa el mismo principio básico que la segunda versión, pero en lugar de solo cuadrados, también trata de usar potencias de números más altas (prueba de exponentes del 2 al 8), y usa el resultado más corto. También almacena en caché los resultados, ya que de lo contrario esto sería inaceptablemente lento para números más grandes con muchas llamadas recursivas.

Tanteo:

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

La gran construcción con sangría en el medio son tres comprensiones iterables anidadas, las dos internas dentro de una expresión let. Estos se desatan usando la función de expansión dos veces, y la reducefunción encuentra la más corta de esas cadenas.

He presentado una solicitud de función para poder hacer esto en una sola comprensión.

Dentro de la comprensión, estamos construyendo una cadena desde la raíz r, el exponente xy el resto ( n-wo w-n).

La letexpresión y la mapfunción son nuevas en Ceylon 1.2. mappodría haber sido reemplazado por HashMap(que habría necesitado más caracteres para la importación, aunque probablemente sería aún más rápido, ya que no construiría el mapa nuevo para cada nueva entrada). Las letexpresiones como let (w = r ^ x)podrían haber sido reemplazadas usando una ifcláusula comoif(exists w = true then r ^ x) (y entonces tampoco hubiera necesitado las dos expandllamadas), pero esto aún sería un poco más largo, no encajando dentro de los 511 bytes permitidos.

Aquí las salidas de muestra correspondientes a las seleccionadas anteriormente, todas ellas excepto los números realmente pequeños son más cortos:

   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 ejemplo, ahora tenemos 1000 = (3 ^ 2 + 1) ^ 3, en lugar de 1000 = (6 ^ 2-4) ^ 2-5 ^ 2 + 1.

Paŭlo Ebermann
fuente
Recordaba erróneamente la restricción del programa como 256 bytes ... en 512 se puede hacer bastante más. Lo intentaré más tarde.
Paŭlo Ebermann
No, dice less than 512. Sou puedes usar un máximo. de 511 bytes;)
mınxomaτ
¿Cómo nunca he oído hablar de este idioma? : O Pero en serio, excelente explicación! Me encanta entender las técnicas que otros usan en sus respuestas. +1
ETHproductions
@ETHproductions También solo lo leí hace unas dos semanas aquí en el sitio, y me empezó a gustar. Entonces, para conocerlo mejor, trato de responder preguntas aquí usando Ceylon.
Paŭlo Ebermann
2

Ruby / dc 20,296 18.414 16.968

¡Programación dinámica! Define una lista de funciones que, dada una instrucción de CC, devuelve una nueva expresión y el valor numérico de esa expresión. Luego, comenzando con 1predefinido, crea una lista de todos los valores accesibles hasta el valor deseado incluido.

editar:

Se agregó una función para n-1 y se hizo ejecutar el algoritmo a través de múltiples pases. Parece necesitar 7 pases para estabilizarse. Tuve que acortar algunos nombres de variables para permanecer dentro de 512 bytes.

editar 2:

Se agregaron funciones para n (n-1) , n (n + 1) y n ^ 3 mientras estaba en eso. Acorté el código un poco más, aterrizando exactamente en 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 generados:

La salida consta completamente de cinco caracteres diferentes: 1empuja el valor 1 en la pila; dduplica la parte superior de la pila; +, -Y * hace estallar los dos valores superiores y empuja su suma, diferencia, y producto, respectivamente. Cada expresión generada agrega solo un valor a la pila después de la ejecución.

   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
fuente
1
Bastante bien, superando todo menos el código de máquina z80 hasta ahora (¡incluso Dennis 'CJam!). ¿Crees que podrías agregar un -operador mientras te mantienes dentro del conteo de bytes?
ETHproductions
@ETHproductions ¿Cómo es eso? ;) Tampoco debería ser difícil agregar números negativos ahora.
daniero
0

Python 2.6, 78.069 - 66.265 puntos

Presentando mi respuesta por lo que vale (no mucho en este caso ... pero demostrando claramente que para este desafío no es suficiente pensar simplemente en componer la salida como una suma de valores desplazados en bits; cuando se tiene en cuenta que no hay dígitos fuera de 0 o 1 puede aparecer en la salida). Podría volver más tarde con una forma diferente de generar resultados.

El código en sí no es demasiado largo (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())))

Genera resultados correctos pero detallados:

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)

Fragmento que calcula la puntuación:

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
fuente