짧고 간결한 프로젝트 오일러 문제 하나.

제목에서도 적었듯이 n제곱 해서 나온 숫자의 자릿수가 n인 모든 경우를 묻는 것이다.

다른 문제도 그렇지만, 이 문제는 일일이 제곱을 해가며 풀 수도 있다.

제곱의 결과가 64비트 범위를 넘어설 수도 있으니 이 부분을 해결할 아이디어만 있으면 된다.

 

하지만 그런 무식한(?) 방법보다는 로그를 활용하는 것이 훨씬 더 좋다.

 

일단 10진수 D의 자릿수가 n이라는 건 n-1 \leq log _{10} D \lt n 이라고 쓸 수 있다.

또한, 문제의 특성상 지수의 밑은 오로지 1~9 까지만 가능하다.

 

다시 말해서 1 \leq d \leq 9 인 자연수 d에 대해서 n-1 \leq log _{10} d ^ {n} \lt n 인 자연수 n의 개수를 세면 되는 것이다.

이 식은 로그의 특성을 고려하면 \frac {n-1}{n} \leq log _{10} d \lt 1 로 정리할 수 있다.

그런데, 애초에 log _{10}{ d } 는 1보다 작을 수밖에 없어 오른쪽 부등호는 없어도 된다.

 

이 내용을 그대로 코드로 표현하면 아래와 같다.

 

#include <iostream>
#include <cmath>

using namespace std;

int main()
{
    int count = 0;
    for (int d = 1; d < 10; ++d) {
        double l10d = log10(d);
        
        for (int n = 1; ; ++n) {
            double ratio = (n - 1) / (double)n;
            if (ratio <= l10d) {
                cout << d << "^" << n << endl;
                ++count;
            }

            if (ratio > l10d) {
                break;
            }
        }
    }

    cout << count << "EA" << endl;
}

 

그런데, 조금만 생각해보면 안쪽 루프 자체를 제거해서 코드를 더 최적화할 수 있다.

 

\frac {n-1}{n} \leq log _{10} d\frac {1}{n} \geq 1 - log _{10} d 로 정리하면 다시 n \leq \frac {1}{ 1 - log _{10} d} 로 정리할 수 있다.

여기서 n은 1 이상이어야 하므로 단순히 \left [ \frac {1}{ 1 - log _{10} d} \right ] 만 누적하면 똑같은 결과를 얻을 수 있다.

 

#include <iostream>
#include <cmath>

using namespace std;

int main()
{
    int count = 0;
    for (int d = 1; d < 10; ++d) {
        double l10d = log10(d);

        int x = (int)(1.0 / (1 - l10d));
        cout << x << endl;
        count += (int)x;
    }

    cout << count << "EA" << endl;
}

 

+ Recent posts