If you liked this piece and want to explore further, why not generalizing the TZF functions above to mixed radix representations or make your own variant of them to optimize code, memory or performance.
Done. I substituted recursion for table accumulation. There are three functions,one (stack) specialized for base 10 (prime=5), another (stack) for any prime radix, and the ultimate (tzf) to handle the mixed radii - modeled after your TZF5. I removed the test for min>0 since 0 divided by power is still 0.
stack[0] := 0;
stack[n_] := Quotient[n, 5] + stack[Quotient[n, 5]];
stack[0, r_] := 0;
stack[n_, r_] := Quotient[n, r] + stack[Quotient[n, r], r];
tzf[n_, r_] :=
Min[Quotient[stack[n, #[[1]]], #[[2]]] & /@ FactorInteger[r]];
The new functions are faster. Timing tests follow for TrailingZerosFactorial and the Ntz submission.
TrailingZerosFactorial beats Ntz for very large values. The columns are the N of N!, time for function 1, time for function 2, verification that the values match, and finally the ratio of times.
testGrowth[TrailingZerosFactorial, tzf, 72, time -> 0.1, power -> 4,
size -> 10]
To test Ntz I had to define a two-parameter version.
Ntz[m_] := Total[Table[Floor[m/5^n], {n, 1, Floor[Log[m]/Log[5]]}]]
Ntz[m_, r_] := Ntz[m];
Finally Ntz fails for any power of 5.
Test driver
testGrowth[p1_, p2_, radix_,
OptionsPattern[{time -> 0.2, power -> 8, size -> 10}]] :=
Module[{n, n1, n2},
Block[{$RecursionLimit = Infinity},
Table[{(n = RandomInteger[10^2^i]) //
N, (n1 =
RepeatedTiming[p1[n, radix], OptionValue[time]])[[1]], (n2 =
RepeatedTiming[p2[n, radix], OptionValue[time]])[[1]],
n1[[2]] == n2[[2]], n1[[1]]/n2[[1]]}, {i, OptionValue[power],
OptionValue[power] + OptionValue[size]}]] // TableForm]