Code: Mathematica, Output: Julia, ~98.9457% (20177/20392 bytes)
optimise[n_] :=
Module[{bits, trimmedBits, shift, unshifted, nString, versions,
inverted, factorised, digits, trimmedDigits, exponent, base,
xored, ored, anded},
nString = ToString@n;
versions = {nString};
(* Try bitshifting *)
bits = IntegerDigits[n, 2];
trimmedBits = bits /. {x___, 1, 0 ..} :> {x, 1};
shift = ToString[Length[bits] - Length[trimmedBits]];
unshifted = ToString@FromDigits[trimmedBits, 2];
AppendTo[versions, unshifted <> "<<" <> shift];
(* Try inverting *)
inverted = ToString@FromDigits[1 - PadLeft[bits, 32], 2];
AppendTo[versions, "~" <> inverted];
(* Try invert/shift/invert *)
trimmedBits = bits /. {x___, 0, 1 ..} :> {x, 1};
shift = ToString[Length[bits] - Length[trimmedBits]];
unshifted = ToString@FromDigits[trimmedBits, 2];
AppendTo[versions, "~(~" <> unshifted <> "<<" <> shift <> ")"];
(* Try factoring *)
factorised = Riffle[
FactorInteger[n]
/. {a_, 1} :> ToString@a
/. {a_Integer, b_Integer} :> ToString[a] <> "^" <> ToString[b]
, "+"] <> "";
AppendTo[versions, factorised];
(* Try scientific notation *)
digits = IntegerDigits[n, 10];
trimmedDigits = digits /. {x___, d_ /; d > 0, 0 ..} :> {x, d};
exponent = ToString[Length[digits] - Length[trimmedDigits]];
base = ToString@FromDigits[trimmedDigits, 10];
AppendTo[versions, base <> "e" <> exponent];
(* Don't try hexadecimal notation. It's never shorter for 32-bit uints. *)
(* Don't try base-36 or base-62, because parsing those requires 12 characters for
parseint("...") *)
SortBy[versions, StringLength][[1]]
];
mathpack[n_] :=
Module[{versions, increments},
increments = Range@9;
versions = Join[
optimise[#2] <> "+" <> ToString@# & @@@ ({#, n - #} &) /@
Reverse@increments,
{optimise@n},
optimise[#2] <> "-" <> ToString@# & @@@ ({#, n + #} &) /@
increments,
optimise[#2] <> "*" <> ToString@# & @@@
Cases[({#, n / #} &) /@ increments, {_, _Integer}],
optimise[#2] <> "/" <> ToString@# & @@@ ({#, n * #} &) /@
increments
];
SortBy[versions, StringLength][[1]]
];
The function takes a number and returns the shortest string it finds. Currently it applies four simple optimisations (I might add more tomorrow).
You can apply it to the entire file (to measure its score) as follows:
input = StringSplit[Import["path/to/benchmark.txt"]];
numbers = ToExpression /@ input;
output = mathpack /@ numbers;
N[StringLength[output <> ""]/StringLength[input <> ""]]
Note that some of these optimisations assume that you're on a 64-bit Julia, such that integer literals give you an int64
by default. Otherwise, you'll be overflowing anyway for integers greater than 231. Using that assumption we can apply a few optimisations whose intermediate steps are actually even larger than 232.
EDIT: I added the optimisation suggested in the OP's examples to bitwise xor two large numbers in scientific notation (actually, for all of xor, or and and). Note that extending the xormap
, ormap
and andmap
to include operands beyond 232 might help finding additional optimisations, but it doesn't work for the given test cases and only increases run time by something like a factor of 10.
EDIT: I shaved off another 16 bytes, by checking all n-9, n-8, ..., n+8, n+9
for whether any of those can be shortened, in which case I represented the number based on that, adding or subtracting the difference. There are a few cases, where one of those 18 numbers can be represented with 3 or more characters less than n
itself, in which case I can make some extra savings. It takes about 30 seconds now to run it on all test cases, but of course, if someone actually "used" this function, he'd only run it on a single number, so it's still well under a second.
EDIT: Another incredible 4 bytes by doing the same for multiplication and division. 50 seconds now (the divided ones don't take as long, because I'm only checking these if the number is actually divisible by the factor of interest).
EDIT: Another optimisation that doesn't actually help with the given test set. This one could save a byte for things like 230 or 231. If we had uint64s instead, there'd be a lot of numbers where this could be a huge saving (basically, whenever the bit representation ends in a lot of 1s).
EDIT: Removed the xor, or, and optimisations altogether. I just noticed they don't even work in Julia, because (quite obviously) scientific notation gives you a float where bit-wise operators are not even defined. Interestingly, one or more of the newer optimisations seem to catch all of the cases that were shortened by these optimisations, because the score didn't change at all.
write in any language - output in any language
- the two languages can be different, right? \$\endgroup\$