Search on the blog

2011年9月29日木曜日

約数が1000個ある最小の数は?

どこかの酔っぱらいに問題を出されたので、解いてみた。
「約数が1000個ある最小の自然数はいくつか?但し答えはintの範囲におさまる。」

答えは、810810000。あってますかね?

ある自然数nの約数の数は、nの素因数p1, p2, ..., pmを用いて
n = p1^(k1+1) * p2^(k2+1) * p3^(k3+1) *....., pm^(km+1)
と書けるので、これを使ってメモ化再帰した。


const double inf = (1e+300);
double dp[1024][1024];
bool isPrime[1<<20];
vector<int>primes;
vector<int>facs;

void init() {
    REP(i, 1<<20) isPrime[i] = true;
    isPrime[0] = isPrime[1] = false;

    for (int i = 2; i * i <= (1<<20); i++) {
        if (!isPrime[i])
            continue;
        for (int j = i*i; j < (1<<20); j += i)
            isPrime[j] = false;
    }
    primes.clear();
    REP(i, 1<<20) if (isPrime[i]) {
        if (primes.size() > 1024)
            break;
        primes.push_back(i);
    }
}

double rec(int pw, int pr) {
    if (pw == 1)
        return 1;

    if (dp[pw][pr] > 0)
        return dp[pw][pr];

    double ret = inf;
    REP(i, facs.size()) {
        if (pw % facs[i] == 0)
            ret = min(ret, pow(primes[pr], facs[i]-1) * rec(pw/facs[i], pr+1));
    }

    return dp[pw][pr] = ret;
}

int main() {
    int n;
    cin >> n;

    init();
    FOR (i, 2, 1001) {
        if (n % i == 0)
            facs.push_back(i);
    }

    REP(i, 1024)REP(j, 1024) dp[i][j] = -1;
    cout << (int)rec(n, 0) << endl;
}

この結果は面白くて、約数の個数が素数の場合は、その数は合成数個の約数を持つ数と比べて大きな数になることを意味している。

例えば、11個の約数を持つ数。
11はこれ以上因数分解できないため、11個の約数を持つ最小の自然数は上の約数の個数の式より、
2^(10) = 1024となる。
これに対して、12 = 3 * 2 * 2と因数分解できるので、12個の約数を持つ最小の自然数は、
2^2 * 3^1 * 5^1 = 60
となる。

約数の数が1000個である最小の自然数と、約数の数が101個である最小の自然数はどちらが大きいでしょうか?
という問いを考えると、約数の個数が多い1000個の方が大きく思える。しかし実際は、

1000個の場合は、810810000。(答えがあってれば。)
101個の場合は、2^100 ≓ 10^30
と101個の方がケタ違いに大きな数となる。

2 件のコメント:

  1. 問題はこちらです…
    http://www.codeforces.com/contest/27/problem/E

    問題出したのにまだ解いてないです…

    返信削除
  2. akimonoiryou>
    おー、ありがとう。intじゃなくて10^18かー。
    それだと上のソースは誤差死かな。
    doubleの仮数部が52ビットだから、dp[][]の型をdoubleからlong long intにして、一旦doubleで計算して結果が10^18以下ならlong long intで計算しなおすみたいな処理がいるね。

    返信削除