사실 살면서 이런걸 궁금해 할 이유가 있을까 싶긴 하지만, 그래도 정말 뇌를 깨워주는 문제 같아서 소개하려고 합니다. 은 몇 개의 연속된 꼬리 0을 가지고 있을까요? 연속된 꼬리 0이라는 정의는, 만약 130500이면 연속된 꼬리 0은 2개이고, 10000은 4개의 연속된 꼬리 0을 가지는 것입니다. 즉, 주어진 이 10의 몇승으로 나눠 떨어지는가의 문제입니다.

가장 간단하게 생각할 수 있는 방법은 실제 factorial을 구해서 나누어 떨어지지 않을때까지 10으로 나눠보는 방법입니다.

def factorial(n):
    if n == 1 or n == 2:
        return n
    else:
        result = 1
        while n > 1:
            result *= n
            n -= 1
        return result

def numTrailingZeros(n):
    num = factorial(n)
    count = 0
    while num % 10 == 0:
        count += 1
        num /= 10
    return count

>>> from time import time
>>> t0 = time()
>>> print numTrailingZeros(10000)
2499
>>> print "걸린시간 : %.2e" % (time()-t0)
걸린시간 : 2.82e-01

이 방법은 n이 커질수록 감당이 안될정도로 느려지게 됩니다. 어떻게 하면 스마트하게 계산해 낼 수가 있을까요? 아마 똑똑하신 분들은 짐작하시겠지만, 10은 2와 5의 곱으로 이루어진다는 점을 생각하는 것입니다. 그리고 조금 더 생각해보면 2가 곱해진 수가 당연히 5가 곱해진 수보다 많을 것입니다. (극단적으로 2의 배수가 5의 배수보다 많잖아요? ^^) 그럼 결국 5가 몇번 곱해졌느냐가 꼬리 0의 개수라는 의미겠죠?

1부터 n까지 중에 5의 배수는 몇개가 있을까요? 답은 간단합니다. 개가 존재합니다. 그럼 5를 다 찾은 걸까요? 아니요~ 25의 배수는 5가 기본적으로 2번씩 곱해진 수겠지요? 그럼 다음 5들은 개가 존재할 것입니다. 이런식으로 쭈욱 가면 결국 몫이 0이 될 때까지 나오는 몫들을 모두 더해주면 꼬리 0의 개수를 쉽게 구할 수 있겠지요? 충격적인 결과는 곧 확인하실 수 있습니다…

def smart_numTrailingZeros(n):
    count = 0
    while n > 0:
        n /= 5
        count += n
    return count

>>> from time import time
>>> t0 = time()
>>> print numTrailingZeros(10000)
2499
>>> print "걸린시간 : %.2e" % (time()-t0)
걸린시간 : 2.10e-04

우와…. 거의 1000배 가까이 빠르죠..? n이 더 커질수록 그 효율성은 훨씬 더 대단해질 것입니다. 정말 이 문제를 보고 똑똑해져야겠다는 생각을 많이 했습니다…