Вместо выполнения полного умножения необходимо отслеживать только коэффициенты 2 и 5. Если число можно записать как N = 2^a * 5^b * (factors other than 2 or 5)
, то число конечных нулей в N
равно min(a, b)
.(Это потому, что конечный ноль - это просто множитель 10, для которого требуется одно 2 и одно 5).
Обратите внимание, что умножение складывает вместе экспоненты факторов.Итак, если вы можете написать:
s(n-2) = 2^a * 5^b * (factors other than 2 or 5)
s(n-1) = 2^c * 5^d * (factors other than 2 or 5)
Тогда у нас будет:
s(n) = s(n-1) * s(n-2)
= 2^(a+c) * 5^(b+d) * (factors other than 2 or 5)
Следовательно, мы можем рассматривать эту проблему как две последовательности Фибоначчи .Вы начинаете с числа 2 и 5 в s(0)
и s(1)
и вычисляете число 2 и 5 в s(2), s(3), ..., s(n)
в последовательности Фибоначчи:
#2s in s(n) = (#2s in s(n-1)) + (#2s in s(n-2))
#5s in s(n) = (#5s in s(n-1)) + (#5s in s(n-2))
Наконец, числоконечных нулей составляет min(#2s in s(n), #5s in s(n))
.
Вышеприведенный алгоритм (если реализован с помощью цикла или запомненной рекурсии) равен O(n)
.Ваша попытка была экспоненциальной в n
, поэтому даже для n = 30
требуется много времени.Я не хочу разбивать вашу попытку, но хорошо понимать эти ошибки - ваш код работает медленно по двум основным причинам:
Во-первых, умножение очень больших целых чисел с полной точностью (как вы делаете сBigInteger
) очень медленно, так как количество цифр может удваиваться с каждым умножением.Если вас волнует только число конечных нулей, полная точность не требуется.
Во-вторых, игнорируя стоимость умножения, ваша рекурсивная реализация s
по-прежнему экспоненциально, но это не такдолжен быть.Обратите внимание, что вы вычисляете одни и те же значения много раз - s(n-2)
вычисляется отдельно для s(n)
и s(n-1)
, но значение s(n-2)
явно одно и то же.Хитрость заключается в том, чтобы запомнить рекурсию , запоминая ранее вычисленные результаты, чтобы избежать повторного вычисления.Кроме того, вы можете вычислять последовательности, подобные Фибоначчи, с помощью цикла:
// Computes the n-th Fibonacci number in O(n) time
int[] fib = new int[n + 1];
fib[0] = 0;
fib[1] = 1;
for (int i = 2; i <= n; i++)
fib[i] = fib[i-1] + fib[i-2];
return fib[n];
Это гораздо более простой подход, чем запомненная рекурсия, по крайней мере для этой проблемы.