Appearance
約数列挙(Divisor Enumeration)
整数Nの正の約数をすべて列挙する。
約数はペアで現れる性質を利用して、O(√N)で求められる。
特徴
| 項目 | 内容 |
|---|---|
| 用途 | 正の約数の列挙 |
| 計算量 | O(√N) |
| 空間計算量 | O(d(N)) |
| 前処理 | 不要 |
d(N)はNの正の約数の個数。
考え方
Nの約数dが存在するとき、N/dも約数となる。
例えばN=36なら、
text
1 × 36
2 × 18
3 × 12
4 × 9
6 × 6したがって、√N以下を調べるだけで、すべての約数を取得できる。
- 小さい約数は昇順に格納する。
- 対応する大きい約数は降順に格納する。
- 大きい約数の配列を反転して結合すれば昇順になる。
実装
cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
vector<ll> divisors(ll n) {
assert(n >= 1);
vector<ll> small, large;
for (ll d = 1; d <= n / d; d++) {
if (n % d != 0) continue;
small.push_back(d);
if (d != n / d) large.push_back(n / d);
}
reverse(large.begin(), large.end());
small.insert(small.end(), large.begin(), large.end());
return small;
}
int main() {
ll n;
cin >> n;
for (ll d : divisors(n)) cout << d << ' ';
cout << '\n';
}使用例
入力
text
36出力
text
1 2 3 4 6 9 12 18 36返り値は vector<ll> で昇順に格納される。
cpp
auto ds = divisors(36);
// {1, 2, 3, 4, 6, 9, 12, 18, 36}典型的な応用
約数の個数
cpp
int cnt = divisors(n).size();約数の総和
cpp
ll sum = 0;
for (ll d : divisors(n)) sum += d;約数を利用した全探索
約数となり得る値だけを列挙することで、探索候補を減らせる。
cpp
for (ll d : divisors(n)) {
// dを候補として処理
}注意点
- Nは1以上とする。
- N=1の場合、約数は
{1}。 - 完全平方数の場合、√Nを重複して追加しない。
d*d <= nはオーバーフローする可能性があるため、d <= n/dが安全。- 約数の列挙と素因数分解は異なる。
- 非常に大きなNでは、O(√N)でも時間がかかる場合がある。