7

I'm seeking an efficient implementation of the number-theoretic function giving the smallest integer $n$ that has exactly $k$ factors (not necessarily prime):

f[k_Integer]:= ...
  • f[1] = 1 because $1$ is the smallest integer that has just a single factor, i.e., $\{ 1 \}$
  • f[2] = 2 because $2$ is the smallest integer that has just the two factors, i.e., $\{ 1, 2 \}$
  • f[3] = 4 because $4$ is the smallest integer that has exactly three factors, i.e., $\{ 1, 2, 4 \}$
  • f[4] = 6 because $6$ is the smallest integer that has exactly four factors, i.e., $\{ 1, 2, 3, 6 \}$
  • f[5] = 16 because $16$ is the smallest integer that has exactly five factors, i.e., $\{ 1, 2, 4, 8, 16 \}$
  • f[6] = 12 because $12$ is the smallest integer that has exactly six factors, i.e., $\{ 1, 2, 3, 4, 6, 12 \}$
  • f[7] = 64 because $64$ is the smallest integer that has exactly seven factors, i.e., $\{ 1, 2, 4, 8, 16, 32, 64 \}$
  • f[8] = 24 because $24$ is the smallest integer that has exactly eight factors, i.e., $\{ 1, 2, 3, 4, 6, 8, 12, 24 \}$
  • f[9] = 36 because $36$ is the smallest integer that has exactly nine factors, i.e., $\{ 1, 2, 3, 4, 6, 9, 12, 18, 36 \}$

A few moments of thought will show that for $k$ odd, $n$ is a perfect square. Moreover, note that f[k] is not monotonic.

Very inefficient code would advance through increasing $n$ until an integer is found with the criterion of exactly $k$ factors, but this is extremely inefficient for large $k$.

This generates the pairs $n,k$ up to $n=100$:

myList = Table[{n, Times @@ (# + 1 & /@ FactorInteger[n][[All, 2]])}, 
  {n, 2, 100}]

And it is a simple matter to select cases with a given $k$:

Select[myList, #[[2]] == 60]

When $k \sim 10^6$, this is somewhat slow and definitely memory intensive.

As background/edification, here is a log plot of $n$ versus $k$.

enter image description here

In 1644, the great mathematician Mersenne asked for f[60] = 5040.

David G. Stork
  • 41,180
  • 3
  • 34
  • 96
  • f[60] = 5040 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 28, 30, 35, 36, 40, 42, 45, 48, 56, 60, 63, 70, 72, 80, 84, 90, 105, 112, 120, 126, 140, 144, 168, 180, 210, 240, 252, 280, 315, 336, 360, 420, 504, 560, 630, 720, 840, 1008, 1260, 1680, 2520, 5040} is surprisingly fast. – bbgodfrey Nov 21 '18 at 03:12
  • Yes... but I would like to calculate f[10^6] and even higher, without having to calculate billions of "lower" cases. – David G. Stork Nov 21 '18 at 03:15
  • Understand, By the way, Table[f[k], {k, 1, 20, 1}] is {1, 2, 4, 6, 16, 12, 64, 24, 36, 48, 1024, 60, 4096, 192, 144, 120, 65536, 180, 262144, 240}. Check your f[4]. – bbgodfrey Nov 21 '18 at 03:20
  • @bbgodfrey. Oooh... thanks. Fixed. I did the table by hand before I wrote my code. (Bad idea!) – David G. Stork Nov 21 '18 at 03:23
  • The accepted answer fails for ALL extraordinary numbers {8, 16, 24, 32, 48, 64, 72, 80, 96, 108...} and there are infinitely many of them https://oeis.org/A072066. An answer should be accepted only if it returns the correct results. – ZaMoC Nov 21 '18 at 12:44
  • https://math.stackexchange.com/a/2733075/416565 – ZaMoC Nov 21 '18 at 14:12

2 Answers2

7

Note that the divisor count function of a number with prime factorization $$n=p_1^{a_1} p_2^{a_2} \cdots p_i^{a_i}$$ satisfies: $$\tau (n)=\prod _k^i \left(a_k+1\right)$$

So, to find an inverse of the divisor count function, we need to find a number whose prime factorization is equal to the right hand side, from which we can determine what the values of $a_k$ must be. Here is a function that does this:

InverseDivisor[n_] := With[
    {f = Reverse[Join @@ ConstantArray @@@ FactorInteger[n] -1]},
    Times @@ ((Prime @ Range @ Length @ f)^f)
]

Some more work is needed to make sure the inverse returned is the minimum. For example, my simple minded algorithm gives 30 instead of 24 for the inverse of 8. Let's check your examples:

i = InverseDivisor /@ Range[9]

{1, 2, 4, 6, 16, 12, 64, 30, 36}

And let's check your harder version:

InverseDivisor[60]

5040

Next, let's see how long it takes to do $10^6$:

big = InverseDivisor[10^6]; //AbsoluteTiming
big

{0.000059, Null}

200961610708938459249870000

Finally, we can check the above results by using DivisorSigma:

DivisorSigma[0, i]
DivisorSigma[0, 5040]
DivisorSigma[0, big]

{1, 2, 3, 4, 5, 6, 7, 8, 9}

60

1000000

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
  • Thanks so much (+1). I'm a bit surprised how fast this is for large $k$. Now all we need do is ensure that the minimum $n$ be returned. – David G. Stork Nov 21 '18 at 05:03
  • @DavidG.Stork Consider why the 8 case fails. 8 can be represented by {{2, 3}}, which yields f = {1,1,1} and InverseDivisor of 30. However, 8 also can be represented by {{2, 1}, {4, 1}}, which yields f = {3,1} and InverseDivisor of 24. Generalizing, it appears that not only must the IntegerFactors of 'nbe considered but also products of those factors: Analyze all sets of products of factors and chose the one that yields the smallestInverseDivisor`. Though slower than the approach in the answer, it is vastly faster than the brute force approach in the question. – bbgodfrey Nov 21 '18 at 06:23
  • @DavidG.Stork More generally, it appears that most cases that are multiples of 8 require the approach outlined in my comment immediately above. I have not found any other cases requiring that approach. – bbgodfrey Nov 21 '18 at 06:40
  • @CarlWoll: Your code is fast enough (accept). (I guess I was too pessimistic in my predictions of how slow "basic" approaches would be.) – David G. Stork Nov 21 '18 at 06:50
  • 1
    @DavidG.Stork I cannot understand how an aswer returning false results gets accepted... This code (which is very similar with OEIS code https://oeis.org/A037019) returns the right answer "most of the times". In number theory this makes a big difference and it is totally wrong. Here is the OEIS with correct results https://oeis.org/A005179. – ZaMoC Nov 21 '18 at 11:06
  • @J42161217 OEIS is a great resource, but the code provided in A005179 also does not answer the question, because it is so slow. By the way, f[k_?IntegerQ] := (n = 1; While[Length@Divisors[n] != k, n++]; n) is compact and rigorously correct, but it also is very slow. – bbgodfrey Nov 21 '18 at 13:45
  • @bbgodfrey Finding an efficient formula for ALL numbers may be an open problem. Search for "Grost, M. (1968). The Smallest Number with a Given Number of Divisors" .Carl Wall's answer works only on ordinary numbers and not on extraordinary. An answer should first return the RIGHT results and then be efficient. This questions is more about maths and not coding – ZaMoC Nov 21 '18 at 14:09
  • Is 1,000,000 an extraordinary number? I get the correct answer for 10^6 in 0.0170 sec. with my hacked-together Python code. d(173804636288811640432320000) = 1000000. I see someone confirms that result below. However, the code above gets the INCORRECT answer about 200 times as quickly! There may be good efficiency clues there, or maybe not. I can't see defending it without fixing it first, though. – SteveWithamDuplicate Jun 18 '22 at 17:00
7

If a number $n$ has $k$ divisors, then $k=(a_1+1)(a_2+1)\ldots (a_m+1)$, where the prime factorisation of $n=p_1^{a_1} p_2^{a_2}\ldots p_m^{a_m}$, as @CarlWoll points out. Therefore, $k$ must be the product of $m$ factors, each $\ge2$.

The general problem of multiplicative partitions is discussed on Stack Exchange here and here. Adapting code from the article by Knopfmacher and Mays gives the following function MultiplicativePartitions[n].

MultiplicativePartitions[1, m_] := {{}}
MultiplicativePartitions[n_, 1] := {{}}
MultiplicativePartitions[n_?PrimeQ, m_] := If[m < n, {}, {{n}}]

MultiplicativePartitions[n_, m_] :=     
   Join @@ Table[
              Map[Prepend[#, d] &, MultiplicativePartitions[n/d, d]],
              {d, Select[Rest[Divisors[n]], # <= m &]}]

MultiplicativePartitions[n_] := MultiplicativePartitions[n, n]

For example,

 MultiplicativePartitions[24]

{{3, 2, 2, 2}, {4, 3, 2}, {6, 2, 2}, {6, 4}, {8, 3}, {12, 2}, {24}}

Thus,

MinWithDivisors[k_] :=    
   Min[Map[Times @@ (Prime[Range[Length[#]]]^(# - 1)) &, 
           MultiplicativePartitions[k]]]

SetAttributes[MinWithDivisors, Listable]

A quick test:

MinWithDivisors[Range[20]]

{1, 2, 4, 6, 16, 12, 64, 24, 36, 48, 1024, 60, 4096, 192, 144, 120, 65536, 180, 262144, 240}

The function MinWithDivisors[k] agrees with a brute-force search.

Block[{t = DivisorSigma[0, Range[300000]]},
   Table[FirstPosition[t, k, {0}][[1]], {k, 1, 20}]
]

The solution for one million divisors, $k=10^6$, is the following.

AbsoluteTiming[MinWithDivisors[10^6]]

{0.077611, 173804636288811640432320000}

Note that this result is less than that given by InverseDivisor defined by Carl, who correctly warned that "more work" was required.

KennyColnago
  • 15,209
  • 26
  • 62
  • 1
    Excellent! +1... – ZaMoC Nov 21 '18 at 19:05
  • @KennyColnago: Very nice (+1)... and reasonable efficient! – David G. Stork Nov 21 '18 at 19:06
  • MinWithDivisors[k] is not a panacea for larger k. Specifically, one may run into Mathematica kernel RAM issues. For example, attempting MinWithDivisors[293318625600] (I wanted to verify the final term in OEIS A009287) on a machine with 128 GB real RAM had the kernel accessing more than 512 GB RAM (thanks to how modern operating systems handle these things) before finally crashing. – Hans Havermann Jun 13 '22 at 17:54