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.5
puede 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 sign
a 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 proof
función maneja el signo inicial (con un caso especial para 0), la p
funció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 reduce
funció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 x
y el resto ( n-w
o w-n
).
La let
expresión y la map
función son nuevas en Ceylon 1.2. map
podrí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 let
expresiones como let (w = r ^ x)
podrían haber sido reemplazadas usando una if
cláusula comoif(exists w = true then r ^ x)
(y entonces tampoco hubiera necesitado las dos expand
llamadas), 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.
0
o1
por defecto?