Efficient way to count the number of zeros at the (right) end of a very large number

For general large integers n, I don't know if there's a better method than Min[IntegerExponent[n, 5], IntegerExponent[n, 2]]. Or more compactly, IntegerExponent[n, 10] or IntegerExponent[n].


If you are strictly interested in the number of trailing zeros in factorials $n!$, as the example in your question suggests, then consider the number of pairs of 2 and 5 in all the factors of numbers 1 through $n$. There is always a 2 to match a 5, so the number of fives gives the number of zeros. Integers divisible by 5 contribute one 5 to the total. Integers divisible by 25 contribute one additional 5, and so on. The maximum power to consider is Floor[Log[5,n]].

This method avoids time- and memory-consuming calculation of $n!$, and is about 50 times faster than IntegerExponent on my machine.

NumberOfFives[n_Integer] := Total[Floor[n/5^Range[Floor[Log[5,n]]]]]

However, the fastest method I've found to calculate the exponent of prime $p$ in $n!$ is the following:

PrimeExponent[n_Integer, p_Integer] := (n - Total[IntegerDigits[n, p]])/(p - 1)

which, on my machine, is about three times as fast as Mr. Wizard's answer:

Tr@Floor@NestWhileList[#/5` &, #/5`, # > 1 &] & @ 12345

Here is a recursive divide-and-conquer. There are probably nicer ways to code it.

trailingZeros[n_, b_] := Module[
  {scale=Log[b,N[n]], sqrt, ndigits},
  If [scale<1, Return[0]];
  sqrt = Ceiling[scale/2];
  ndigits = IntegerDigits[n, b^sqrt, 2];
  If [Last[ndigits]==0,
    sqrt + trailingZeros[First[ndigits],b],
    trailingZeros[Last[ndigits], b]]
  ]

In[39]:= Timing[trailingZeros[4234567!, 33]]
Out[39]= {6.740000, 423454}

In terms of speed, it is essentially identical to J.M.'s approach. It just shows how one might do this were there no IntegerExponent function available.

--- edit 2017-06-25 ---

Since this showed up again I decided to time the various responses, all special-cased to digits base 10. The second and fifth are also specialized for factorials and are, as noted in respective responses and comments, hugely faster than the rest. The input for them is the non-factorialed value while for the rest it is the factorial.

zero1[n_] := 
 StringCases[
  ToString[n], {Longest[x : "0" ..] ~~ EndOfString} :> StringLength@x]
zero2[n_] := Tr@Floor@NestWhileList[#/5` &, #/5`, # > 1 &] &@n
zero3[n_] := LengthWhile[Reverse@IntegerDigits[n], # == 0 &]
zero4[n_] := 
 Module[{scale = Log[10, N[n]], sqrt, ndigits}, 
  If[scale < 1, Return[0]];
  sqrt = Ceiling[scale/2];
  ndigits = IntegerDigits[n, 10^sqrt, 2];
  If[Last[ndigits] == 0, sqrt + zero4[First[ndigits]], 
   zero4[Last[ndigits]]]]
zero5[n_Integer] := (n - Total[IntegerDigits[n, 5]])/4
zero6[n_] := IntegerExponent[n, 10]

Here is a big test. It shows that the "slow" methods are all comparable though there are modest factors separating them: best to worst is around a factor of 4 with this test.

nn = 12345678;
mm = nn!;

AbsoluteTiming[zero1[mm]]
AbsoluteTiming[zero2[nn]]
AbsoluteTiming[zero3[mm]]
AbsoluteTiming[zero4[mm]]
AbsoluteTiming[zero5[nn]]
AbsoluteTiming[zero6[mm]]

(* Out[114]= {78.1305, {3086416}}

Out[115]= {0.0000988615, 3086416}

Out[116]= {95.3831, 3086416}

Out[117]= {22.2577, 3086416}

Out[118]= {0.0000522287, 3086416}

Out[119]= {45.3224, 3086416} *)

I was a bit surprised that IntegerExponent was not fastest and maybe more surprised that it is almost exactly a factor of 2 off in terms of speed. Something to look into on a rainy day.

--- end edit ---