Minimization can be expressed with plain NMinimize
in a bit awkward manner:
NextHighlyCompositeNumber[n_Integer?Positive, primes:{__Integer?PrimeQ}] :=
With[{parms = Unique[] & /@ primes},
With[{eqn = Inner[Power, primes, parms, Times]},
Floor@First@NMinimize[{eqn, eqn >= n &&
parms \[Element] Integers && And @@ (# >= 0 & /@ parms)}, parms]]]
AbsoluteTiming@Table[NextHighlyCompositeNumber[n, {2, 3, 5}], {n, 1, 10}]
(* {3.063287, {1, 2, 3, 4, 5, 6, 8, 8, 9, 10}} *)
Alternatively, a shorter but more explicit exposition is possible using LinearProgramming
:
NextHighlyCompositeNumber[n_Integer?Positive, primes:{__Integer?PrimeQ}] :=
Quiet@Inner[Power, primes,
LinearProgramming[Log[primes], {Log[primes]}, {Log[n]}, Automatic, Integers],
Times]
AbsoluteTiming@Table[NextHighlyCompositeNumber[n, {2, 3, 5}], {n, 1, 50}]
(* {0.140417, {1, 2, 3, 4, 5, 6, 8, 8, 9, 10, 12, 12, 15, 15, 15, 16, 18,
18, 20, 20, 24, 24, 24, 24, 25, 27, 27, 30, 30, 30, 32, 32, 36, 36,
36, 36, 40, 40, 40, 40, 45, 45, 45, 45, 45, 48, 48, 48, 50, 50}} *)
Idea in the latter case is to turn product-of-powers representing all possible values representable by these primes into sum-of-products by applying logarithm on all constraints. This makes the problem a dot product which is, naturally, amenable to linear programming.
Explicit LinearProgramming
implementation (or variant of NMinimize
that would have been written to the same form) is dramatically faster and numerically easier to evaluate.
EDIT:
Not so surprisingly, generating consecutive highly composite numbers is much faster than integer linear programming approach. This tail-recursive code achieves it (sorry, it's bit of a mess, nowhere near elegant):
HighlyCompositesTo[n_Integer?Positive, primes:{__Integer?PrimeQ}] :=
HighlyCompositesTo[n, primes, {1}, 1 & /@ primes, primes, Min[primes]]
HighlyCompositesTo[n_, primes_, list_, pos_, next_, min_] := list /; Last[list] >= n
HighlyCompositesTo[n_, primes_, list_, pos_, next_, min_] :=
With[{nlist = Append[list, min],
npos = MapThread[If[#1 == min, #2 + 1, #2] &, {next, pos}]},
With[{nnext = MapThread[If[#1 == min, #3 nlist[[#2]], #1] &, {next, npos, primes}]},
HighlyCompositesTo[n, primes, nlist, npos, nnext, Min[nnext]]]]
This performs much better than NextHighlyCompositeNumber
in extracting values up to 10000:
HighlyCompositesTo[10000, {2, 3, 5, 7}]; // AbsoluteTiming
(* {0.007496, Null} *)
NestWhileList[NextHighlyCompositeNumber[# + 1, {2, 3, 5, 7}] &,
1, # < 10000 &]; // AbsoluteTiming
(* {5.247371, Null} *)
Both produce the same result:
NestWhileList[NextHighlyCompositeNumber[# + 1, {2, 3, 5, 7}] &, 1, # < 10000 &] ==
HighlyCompositesTo[10000, {2, 3, 5, 7}]
(* True *)
RLink
is not an option, I guess? $\endgroup$