10
$\begingroup$

Is there a way to generate a counting function for prime powers - i.e. to create a similar function to PrimePi, but including prime powers. The following will, of course, generate a list of these numbers:

Select[ Range[1000000], PrimePowerQ]
$\endgroup$
1
  • $\begingroup$ Something like this maybe? Module[{count = 0}, Do[If[PrimePowerQ[k], count++], {k, 1, 100000}]; count] Do I understand that you want to avoid keeping any long lists in memory? In that case you need to use Do. Though in practice your limiting factor may be processing power and not memory, so Count[Range[100000], _?PrimePowerQ] will do. $\endgroup$
    – Szabolcs
    Commented Nov 20, 2013 at 2:28

4 Answers 4

12
$\begingroup$

A naive approach would be this:

primePower[n_] := Count[ Range @ n, _?PrimePowerQ]

This function works well however it might be very inefficient for large n. It takes a bit to evaluate e.g.

primePower[10^6]
78734

which is only a little bigger than

PrimePi[10^6]
78498

The latter is much more efficient since it uses advanced algorithms for counting primes which exploit sparse caching and sieving techniques ( in documentation pages see Some Notes on Internal Implementation, then J. C. Lagarias, V. S. Miller and A. M. Odlyzko "Computing π(x): the Meissel-Lehmer method," Math. Comp., 44 (1985) 537-560, another interesting resources The Prime Pages: Prime Number Research, Records and Results).

Therefore we proceed along a different way making use of PrimePi to count prime powers, namely we need to sum Log[ Prime[k], n] (it measure how many times appears given prime with different powers up to n, e.g. for k == 3 there are 5, 25, 125, 625, ..., 5^Floor[ Log[ Prime[3], n]]) over every prime up to PrimePi[ Sqrt[n] // Floor] :

primePowerPi[n_] := PrimePi[n] + Sum[   Floor @ Log[ Prime[k], n] - 1, 
                                      { k, PrimePi[ Sqrt[n] // Floor]}]

This might be done as well with:

Floor @ Log[ Prime[ Range[ PrimePi[ Sqrt[n] // Floor]]], n]

e.g.

Total[ Floor @ Log[ Prime[ Range[ PrimePi[ Sqrt[10^5] // Floor]]], 10^5] - 1] == 
primePowerPi[10^5] - PrimePi[10^5]

primePowerPi[10^5] - PrimePi[10^5]
True

108

Now we can find immediately:

primePowerPi /@ { 10^6, 10^7}
{78734, 665134}

while

PrimePi[10^7]
664579

For some limitations of Prime and PrimePi domains this reference would be helpful What is so special about Prime?.

$\endgroup$
2
  • 1
    $\begingroup$ an ingenious approach! Thanks for your input on this - it has greatly helped my understanding of generating functions :) $\endgroup$
    – martin
    Commented Nov 20, 2013 at 8:04
  • 1
    $\begingroup$ this was a really in-depth answer that helped me to understand how Mathematica works - again, many thanks :) $\endgroup$
    – martin
    Commented Nov 20, 2013 at 8:12
6
$\begingroup$

Here's a fancy memoized solution:

Clear[primePowerCount, primePowerCount`cache, iPrimePowerCount]

iPrimePowerCount[n1_, n2_] := Count[Range[n1, n2], _?PrimePowerQ]

primePowerCount`cache = {1};
primePowerCount[1] = 0;
primePowerCount[n_?Positive] := 
 Module[{n0, res},
  n0 = First@Nearest[primePowerCount`cache, n];
  If[n0 < n,
   res = primePowerCount[n0] + iPrimePowerCount[n0 + 1, n],
   res = primePowerCount[n0] - iPrimePowerCount[n + 1, n0]
  ];
  primePowerCount`cache = Union[primePowerCount`cache, {n}];
  primePowerCount[n] = res
 ]

This is going to be conveniently fast for interactive use with "random" inputs. If you need to evaluate it for a complete Range though, it will not be the best way.

The idea is to store all previously calculated counts, and to compute any newly requested count relative to an already known (cached) one.

You can even pre-initialize this for a range of exponentially increasing values, e.g. Scan[primePowerCount, Union@Round[1.1^Range[150]]], to speed things up for subsequent computations. These results could be saved and later re-loaded.


EDIT: I just saw Artes's answer, which is the best solution, as it exploits the specific problem. For general slow-to-evaluate brute force counting functions, I believe that my memoized approach is still valuable.

$\endgroup$
1
  • 1
    $\begingroup$ thank you for your reply - not sure I understand it yet - but I will persevere with it! :) $\endgroup$
    – martin
    Commented Nov 20, 2013 at 8:06
5
$\begingroup$

I can't complete with Artes's mathematical knowledge and approach, but simply as a point of reference, for formulating a brute-force approach it will be more memory efficient to use Sum, though it will still be very slow for large input.

Sum[Boole @ PrimePowerQ @ i, {i, 5*^6}] // Timing

MaxMemoryUsed[]
{46.535, 348940}

15083688
$\endgroup$
1
  • 1
    $\begingroup$ @MrWizard - thank you for your solution - it is great from a learning point of view :) $\endgroup$
    – martin
    Commented Nov 20, 2013 at 8:10
5
$\begingroup$

Late again, but ...

In addition toPrimePowerQ, the built-inMangoldtLambda[n]also works as in

n - Count[MangoldtLambda[Range[n]], 0]

where n is the upper limit. However, these functions are both too slow. The answer by @Artes usesPrimePieffectively for much faster results.

A cool method given by Riemann himself is to usePrimePion fractional powers of the upper limit n.

Sum[PrimePi[n^(1/k)], {k, Floor[Log[2, n]]}]

On my machine with n=10^12, theAbsoluteTimingwas just 0.015s, whereasprimePowerPirequired several seconds.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.