Processing math: 9%

Nの約数の個数を求めるアルゴリズム

2020年3月26日数学約数,素因数分解,約数の個数

素因数分解を用いることで、約数の個数を簡単に求めることができます。

アルゴリズム

Nの約数の個数:

  1. N を素因数分解する
  2. それぞれの指数に1を足す
  3. 「2.」で得られたものを全てかけ合わせる

※ 素因数分解のアルゴリズムに関してはこちらを参照

※ 例として、120 = 2^3 × 3^1 × 5^1 の約数の個数は、(3+1)×(1+1)×(1+1) = 16 となります。

※ 個数だけではなく、どんな約数があるか全列挙したい場合は、N の約数を全列挙するアルゴリズムを参照してください。

計算量

  • 前処理なしのアルゴリズム
    • 前処理:なし
    • クエリ:O(\sqrt{N})
  • SPF を利用した前処理ありのアルゴリズム
    • 前処理:O(N \log \log N)
    • クエリ:O(\log N)

素因数分解に O(\sqrt{N}) だけ時間がかかります。O(N \log \log N) での前計算を許せば、SPF を利用して O(\log{N}) で素因数分解ができます。(詳細:素因数分解のアルゴリズム

「指数に1を足して、すべてかけ合わせる」のには N の素因数の種類だけ計算することになるので O(\log{N}) 程度で抑えられ、素因数分解の計算量を超えません。

C++での実装例

以下は、標準入力から整数 N を受け取って、Nの約数の個数を出力するプログラムです。前処理なしでのアルゴリズムで実装しています。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include <bits/stdc++.h>
using namespace std;
// 素因数分解
template <typename T>
map<T, T> prime_factor(T n) {
map<T, T> ret;
for (T i = 2; i * i <= n; i++) {
T tmp = 0;
while (n % i == 0) {
tmp++;
n /= i;
}
ret[i] = tmp;
}
if (n != 1) ret[n] = 1;
return ret;
}
/* divisor_num(n)
入力:整数 n
出力:nの約数の個数
計算量:O(√n)
*/
template <typename T>
T divisor_num(T N) {
map<T, T> pf = prime_factor(N);
T ret = 1;
for (auto p : pf) {
ret *= (p.second + 1);
}
return ret;
}
long long N;
int main() {
cin >> N;
cout << divisor_num(N) << endl;
return 0;
}
#include <bits/stdc++.h> using namespace std; // 素因数分解 template <typename T> map<T, T> prime_factor(T n) { map<T, T> ret; for (T i = 2; i * i <= n; i++) { T tmp = 0; while (n % i == 0) { tmp++; n /= i; } ret[i] = tmp; } if (n != 1) ret[n] = 1; return ret; } /* divisor_num(n) 入力:整数 n 出力:nの約数の個数 計算量:O(√n) */ template <typename T> T divisor_num(T N) { map<T, T> pf = prime_factor(N); T ret = 1; for (auto p : pf) { ret *= (p.second + 1); } return ret; } long long N; int main() { cin >> N; cout << divisor_num(N) << endl; return 0; }
#include <bits/stdc++.h>
using namespace std;

// 素因数分解
template <typename T>
map<T, T> prime_factor(T n) {
    map<T, T> ret;
    for (T i = 2; i * i <= n; i++) {
        T tmp = 0;
        while (n % i == 0) {
            tmp++;
            n /= i;
        }
        ret[i] = tmp;
    }
    if (n != 1) ret[n] = 1;
    return ret;
}

/*  divisor_num(n)
    入力:整数 n
    出力:nの約数の個数
    計算量:O(√n)
*/
template <typename T>
T divisor_num(T N) {
    map<T, T> pf = prime_factor(N);
    T ret = 1;
    for (auto p : pf) {
        ret *= (p.second + 1);
    }
    return ret;
}

long long N;

int main() {
    cin >> N;
    cout << divisor_num(N) << endl;
    return 0;
}

練習問題