Ceylan / Ceylan, 49,86 40,95 points
La troisième version utilise Ceylon 1.2 pour le générateur et 509 octets de code:
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;}
Il descend à 35,22 points, mais je ne mettrai pas cela dans la ligne de titre car Celyon 1.2 n'a été publié que le 29 octobre. Je ne pense pas que je serais en mesure d'implémenter cet algorithme dans Ceylon 1.1 dans cette taille.). Plus de détails là-bas, je vais décrire ici la deuxième version. (La première version peut être vue dans l'histoire - elle ne supportait que les nombres positifs, mais elle tenait en 256 octets.)
Deuxième version
Maintenant, la deuxième version, qui prend en charge les entiers négatifs (et 0), et crée généralement une sortie un peu plus courte en utilisant en plus -
. (Cette version utilise en fait la longueur autorisée, la première a essayé de rester sous 256 octets au lieu 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 ""));
}
Le code a une longueur de 487, il y a donc encore de la place pour plus d'optimisations plus tard. (Il existe également de nombreuses réserves sous forme d'espaces et de noms de variables longs.)
Le score:
Total positive: 42652
Average positive:42.652
Total negative: 43653
Average negative: 43.60939060939061
With bonus:39.24845154845155
Overall score: 40.95022577422577
Quelques exemples de sorties:
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
Comme vous pouvez le voir, les négatifs sont toujours d'un octet (le début -
) plus longs que les positifs correspondants.
L'idée de base est la même que le programme précédent: trouver un carré près de notre nombre cible, et représenter sa racine et le reste de manière récursive. Mais maintenant, nous permettons à notre carré d'être également plus grand que le nombre cible, ce qui rend le reste négatif. (La +0.5
peut être changée en une constante différente pour modifier l'algorithme, mais il semble que j'aie déjà atteint l'optimum ici - 0.4 et 0.6 donnent des résultats moins bons.)
Pour rendre les valeurs négatives négatives (et autrement avoir la même structure que les positives, nous passons l'opérateur sign
à notre fonction récursive p
- c'est-à-dire soit "+"
ou "-"
. Nous pouvons également l'utiliser pour le jointeur dans les cas triviaux (c'est-à-dire n <9)) comme pour le reste s'il est positif, et utilisez le signe opposé pour le reste s'il est négatif.
La proof
fonction gère le signe initial (avec un cas spécial pour 0), la p
fonction fait le travail réel, avec récursivité.
Troisième version, pour Ceylon 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 version golfée (c'est-à-dire les commentaires et les espaces supprimés) est affichée en haut, avec exactement 509 octets de code.
Cela utilise le même principe de base que la deuxième version, mais au lieu de simplement des carrés, il essaie également d'utiliser des puissances de nombres plus élevées (en essayant des exposants de 2 à 8), et utilise le résultat le plus court. Il met également en cache les résultats, sinon cela serait trop lent pour de plus grands nombres avec de nombreux appels récursifs.
Notation:
Total positive: 36622
Average positive: 36.622
Total negative: 37623
Average negative: 37.58541458541458
With bonus:33.826873126873124
Overall score: 35.22443656343656
La grande construction en retrait au milieu sont trois compréhensions itérables imbriquées, les deux intérieures à l'intérieur d'une expression let. Celles-ci sont ensuite non imbriquées à l'aide de la fonction d'expansion deux fois, et la reduce
fonction trouve la plus courte de ces chaînes.
J'ai déposé une demande de fonctionnalité pour pouvoir le faire en une seule compréhension.
À l'intérieur de la compréhension, nous construisons une chaîne à partir de la racine r
, de l'exposant x
et du reste ( n-w
ou w-n
).
L' let
expression et la map
fonction sont nouvelles dans Ceylan 1.2. map
aurait pu être remplacé par HashMap
(qui aurait eu besoin de plus de caractères pour l'importation, bien que ce serait probablement encore plus rapide, car je ne construirais pas la carte nouvelle pour chaque nouvelle entrée). Les let
expressions comme let (w = r ^ x)
auraient pu être remplacées en utilisant une if
clause comme if(exists w = true then r ^ x)
(et alors je n'aurais pas eu besoin des deux expand
appels non plus), mais cela serait encore un peu plus long, ne cadrant pas dans les 511 octets autorisés.
Voici les exemples de sorties correspondant à ceux sélectionnés ci-dessus, tous sauf les très petits nombres sont plus courts:
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
Par exemple, nous avons maintenant 1000 = (3 ^ 2 + 1) ^ 3, au lieu de 1000 = (6 ^ 2-4) ^ 2-5 ^ 2 + 1.
0
ou1
par défaut?