問題
$Q$ 個の整数 $N_i$ ($N_i \le 10^{18}$) それぞれに対して、最大の素因数を出力せよ。$N_i = 1$ のときは 1 を出力する。
制約
$1 \le Q \le 100$
$1 \le N_i \le 10^{18}$
時間制限: 5sec / メモリ: 256MB
入出力例
入力例 1
3
12
998244353
1000000007000000049
出力例 1
3
998244353
1000000007
概念図: Pollard's ρ のサイクル検出
ヒント(段階的開示)
ヒント1: 方向性
$N \le 10^{18}$ では試し割り法は不可($10^9$ までしか試せない)。Miller-Rabin 確率的素数判定(決定的版)と Pollard's $\rho$ 法で素因数分解を $O(N^{1/4})$ で行う。
ヒント2: アプローチ
- Miller-Rabin: 基底
{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}を使えば $N < 3.3 \times 10^{24}$ まで決定的に判定。 - Pollard's $\rho$: $f(x) = x^2 + c \pmod N$ のサイクル検出で非自明な因数を発見。
- 再帰的に素因数分解し最大素因数を返す。
ヒント3: 実装骨格
from math import gcd
def miller_rabin(n):
if n < 2: return False
if n == 2: return True
if n % 2 == 0: return False
witnesses = [2,3,5,7,11,13,17,19,23,29,31,37]
d, r = n - 1, 0
while d % 2 == 0: d //= 2; r += 1
for a in witnesses:
if a >= n: continue
x = pow(a, d, n)
if x == 1 or x == n - 1: continue
for _ in range(r - 1):
x = x * x % n
if x == n - 1: break
else: return False
return True
def pollard_rho(n):
if n % 2 == 0: return 2
import random
while True:
x = random.randint(2, n-1)
c = random.randint(1, n-1)
y, d = x, 1
while d == 1:
x = (x*x + c) % n
y = (y*y + c) % n
y = (y*y + c) % n
d = gcd(abs(x-y), n)
if d != n: return d
模範解答 (Python)
import sys
from math import gcd
import random
input = sys.stdin.readline
def miller_rabin(n):
if n < 2: return False
if n == 2: return True
if n % 2 == 0: return False
witnesses = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
d, r = n - 1, 0
while d % 2 == 0:
d //= 2
r += 1
for a in witnesses:
if a >= n: continue
x = pow(a, d, n)
if x == 1 or x == n - 1: continue
for _ in range(r - 1):
x = x * x % n
if x == n - 1: break
else:
return False
return True
def pollard_rho(n):
if n % 2 == 0: return 2
while True:
x = random.randint(2, n - 1)
c = random.randint(1, n - 1)
y, d = x, 1
while d == 1:
x = (x * x + c) % n
y = (y * y + c) % n
y = (y * y + c) % n
d = gcd(abs(x - y), n)
if d != n:
return d
def factorize(n):
if n == 1: return []
if miller_rabin(n): return [n]
for p in [2, 3, 5, 7, 11, 13]:
if n % p == 0:
res = []
while n % p == 0:
res.append(p)
n //= p
return res + factorize(n)
d = pollard_rho(n)
return factorize(d) + factorize(n // d)
def largest_prime_factor(n):
if n == 1: return 1
return max(factorize(n))
def solve():
Q = int(input())
for _ in range(Q):
n = int(input())
print(largest_prime_factor(n))
solve()
Step-by-Step 解説
1Miller-Rabin 素数判定
Fermat の小定理の拡張。$n-1 = 2^r \cdot d$ と書いて、$a^d \not\equiv 1$ かつ $a^{2^j d} \not\equiv -1$ ($j=0,...,r-1$) なら合成数の証人。特定の基底集合で $N < 3.3 \times 10^{24}$ まで確定的に判定可能。
Fermat の小定理の拡張。$n-1 = 2^r \cdot d$ と書いて、$a^d \not\equiv 1$ かつ $a^{2^j d} \not\equiv -1$ ($j=0,...,r-1$) なら合成数の証人。特定の基底集合で $N < 3.3 \times 10^{24}$ まで確定的に判定可能。
2Pollard's ρ 法の原理
$f(x) = x^2 + c \pmod n$ の軌道はいずれサイクルを形成する(誕生日パラドックス的な衝突)。Floyd の2ポインタ法でサイクル中の2点 $x \neq y$ を見つけ、$\gcd(|x-y|, n) > 1$ となれば非自明な因数。
$f(x) = x^2 + c \pmod n$ の軌道はいずれサイクルを形成する(誕生日パラドックス的な衝突)。Floyd の2ポインタ法でサイクル中の2点 $x \neq y$ を見つけ、$\gcd(|x-y|, n) > 1$ となれば非自明な因数。
3再帰的素因数分解
factorize(n): 素数なら [n]、そうでなければ $\rho$ で因数 $d$ を見つけ factorize(d) + factorize(n/d)。
4小さい因数の前処理
2, 3, 5, ... などの小さな素数は試し割りで先に除いておくと $\rho$ の効率が上がる。
2, 3, 5, ... などの小さな素数は試し割りで先に除いておくと $\rho$ の効率が上がる。
5最大素因数の抽出
全素因数を列挙して
全素因数を列挙して
max を取る。$N \le 10^{18}$ なら素因数の個数は最大60程度($2^{60} > 10^{18}$)。
計算量
Miller-Rabin(1回): $O(\log^2 N)$(pow の高速計算含む)
Pollard's $\rho$(1回): 期待 $O(N^{1/4} \log N)$
完全素因数分解: $O(N^{1/4} \log^2 N)$ 期待
$Q$ 個の入力: $O(Q \cdot N^{1/4} \log^2 N)$
$Q=100, N=10^{18}$: 約 $100 \times 10^{4.5} \approx 3 \times 10^6$ 演算(十分高速)
Pollard's $\rho$(1回): 期待 $O(N^{1/4} \log N)$
完全素因数分解: $O(N^{1/4} \log^2 N)$ 期待
$Q$ 個の入力: $O(Q \cdot N^{1/4} \log^2 N)$
$Q=100, N=10^{18}$: 約 $100 \times 10^{4.5} \approx 3 \times 10^6$ 演算(十分高速)
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| Pollard's rho が無限ループ | c の選択が悪い場合 (d==n) | if d != n: return d、さもなくば再試行 |
| miller_rabin で a ≥ n を処理しない | 小さな n での境界 | if a ≥ n: continue |
| gcd の引数に負の値 | abs(x - y) を忘れる | gcd(abs(x-y), n) |
| factorize(1) の基底ケース | 空リストを返す必要 | if n == 1: return [] |
次のステップ
- 発展問題: $N \le 10^{18}$ の全素因数と指数を列挙(約数列挙に応用)
- 応用: ECM(楕円曲線法)による大因数の分解($10^{30}$ 超)
- 類題: AtCoder ABC 176 E、競プロ典型90問 No.51