인사이트 알고리즘 코딩 이벤트 2주차 문제 풀이입니다.
자연수 n을 입력받고, n!의 계산 결과 중 마지막에 붙은 연속된 0의 개수와 연속된 0 바로 앞에 나오는 숫자를 구하라.
###실행 예
input n: 15
output: 3 8
###설명
15!은 1307674368000이므로, 마지막에 연속된 0은 3개이고, 바로 앞의 숫자는 8이다.
###조건
- n의 범위는 1 이상, 10000 이하입니다.
- 테스트 입력은 다음과 같습니다.
20! = 2432902008176640000
30! = 265252859812191058636308480000000
40! = 815915283247897734345611269596115894272000000000
50! = 30414093201713378043612608166064768844377641568960512000000000000
100! = 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 - 프로그래밍 언어에서 제공하는 자릿수 제한 없는 곱셈을 이용하거나, 이런 형태의 곱셈 함수를 직접 구현해도 답을 얻을 수 있지만, 문제의 의도와는 다릅니다.
- 정답 검토의 편의를 위해 블로그 포스팅에 2012!와 10000!의 결과를 남겨주세요.
- (심화 문제) 연속된 0 앞에 나오는 여러 숫자를 구하는 것도 가능하니, 심심하신 분은 도전해보세요. ^^
이 방법은 팩토리얼 계산을 하면서 뒤의 '0' 숫자를 계속 카운트하고 필요 없는 숫자를 제거해 큰 숫자를 보관하지 않고 계산하도록 하는 방법입니다.
팩토리얼 결과를 다 담을 필요가 없으므로 각 수를 곱할 때마다 뒤의 0 숫자를 따로 기억(zeroTailSize 변수)하고 그 윗자리 숫자들도 0를 제외한 마지막 숫자에 영향을 미지는 자리까지의 수 만 기억(mask 변수 값으로 sum 값의 나머지만 보관)하도록 했습니다.
이 방법은 최초에 시도했다가 포기했는데 정상혁님의 풀이에서 아이디어를 얻고 다시 시도했습니다.
뒤에 0이 생기는 이유는 소수 2와 5가 곱해져서 10이 생겼기 때문이므로 n!에서 2x5의 개수가 몇 개가 나오는 지 알면 됩니다.
이는 소수 2의 개수와 5의 개수를 구해서 2x5를 구성하는 개수, 즉, 2의 개수와 5의 개수 중 적은 수 만큼 10이 만들어지고 이 숫자 만큼 0이 뒤에 붙습니다. 그리고 소인수분해를 하면 당연히 2가 5보다 많이 나오므로 소수 5가 몇번 나오는지 만 알면 0의 개수를 알 수 있습니다.
제가 시도하다 실패한 이유는 0의 앞자리 수를 구하는 아이디어가 떠오르지 않았고 풀이#2가 (더 멋지긴 해도) 풀이#1 보다 효과적이라고 볼 수도 없기에 바로 #1로 넘어갔었습니다.(역시 새벽에 코딩하는 게 아니었...)
아무튼, 0 앞자리의 수는 n!에서 10을 구성하는 만큼의 소인수 2와 5를 제거한 나머지 수를 곱한 결과의 첫자리 수입니다. 이는 다시 말하면 소인수 2와 5를 제외한 너마지 수를 다 곱하고 이에 10을 구성하는 데 사용되지 않은 만큼의 2를 곱해서 구한 수의 첫자리 수라고 보면 됩니다. 그리고 곱하는 과정에서 우리가 관심있는 수는 첫자리 수이므로 그 수만 유지하도록 해서 곱해진 결과가 너무 커지지 않도록 유지하는 게 효과적입니다.
$ java org.fupfin.insight.event2a.SequentialFactorialZeroTailCounter 2012
n = 2012
Result [zeroTailSize=501, lastNoneZeroDigit=8]
$ java org.fupfin.insight.event2a.SequentialFactorialZeroTailCounter 10000
n = 10000
Result [zeroTailSize=2499, lastNoneZeroDigit=8]
$ java org.fupfin.insight.event2a.PrimenumberFactorialZeroTailCounter 2012
n = 2012
Result [zeroTailSize=501, lastNoneZeroDigit=8]
$ java org.fupfin.insight.event2a.PrimenumberFactorialZeroTailCounter 10000
n = 10000
Result [zeroTailSize=2499, lastNoneZeroDigit=8]