폴라드-로 소인수분해 알고리즘
Contents
Motivation
소인수분해는 매우 어려운 과정이다. 실제로 소인수분해는 (입력 비트 수에 대해) 다항 시간에 결정론적으로 풀 수 있는 방법이 알려져 있지 않다.
그러나, 약간의 랜덤성 (확률에 대한 의존) 을 허용한다면 상당히 clever한 알고리즘을 통해 빠르게 소인수분해를 할 수 있는데, 이 방법이 바로 Pollard’s Rho(
생일 문제 (Birthday Problem)
고등학교 확/통 교과서에도 실려있는 유명한 문제인데, 다음 문제에 답해 보자.
- 23명의 사람이 한 방에 모여 있다. 이 중, 적어도 한 쌍 이상이 생일이 겹칠 확률은 얼마인가?
계산을 직접 해 본다면, 별로 직관적이지 못한 결론을 얻는다. 대략 50%가 넘어가는데, 365개의 생일 중 23명이 골랐을 뿐인데 이렇게 높은 확률이라는 것이 비직관적이기 때문에 이 결과를 Birthday Paradox라고도 부른다.
이 문제의 핵심은, 마구 랜덤하게 뽑으면 생각보다 많이 겹친다 라는 정보이다. 이를 이용하여, 어떻게 소인수분해를 할 수 있는지 알아보고자 한다.
구체적으로,
가정
폴라드-로 알고리즘이 잘 작동하기 위해서는, 큰 수
Algorithm
그러나,
이런식으로 결정론적으로 계산하는 것의 장점은,
수열의 주기를
예시
Niven 의 정수론 책에 수록된 예시는 다음과 같다.
구현
위 알고리즘을 그대로 코드로 옮기면 된다.
- 단,
수열을 미리 구해놓고 Tortoise-Hare를 돌리는 것은 어디까지 구해야 할지 모르는 상황에서는 별로 적절하지 않다. 은 구하기 쉬운 다항식이므로, 매 스텝마다 는 두 스텝씩, 는 한 스텝씩 나간다고 생각하면서 진행시키자. 는 랜덤하게 뽑았는데, 별로 상관은 없다.-
is_prime
부분은 일반적인 소수 판정 함수를 쓰면 된다. 보통은 밀러-라빈 판정법을 많이 쓴다 (그냥 판정하면 느리니까).
inline int64_t mulmod(int64_t x, int64_t y, int64_t m)
{
return ((__int128)x * y % m);
}
int32_t PollardRho(int32_t n)
{
if (n==1) return n;
if (n % 2 == 0) return 2;
int32_t x = (rand()%(n-2))+1;
int32_t y = x;
int32_t c = 1;
int32_t d = 1;
while (1)
{
x = (mulmod(x, x, n) + c + n)%n;
y = (mulmod(y, y, n) + c + n)%n;
y = (mulmod(y, y, n) + c + n)%n;
d = __gcd(abs(x-y), n);
if (d == 1) continue;
if (is_prime(d))
return d;
else return PollardRho(d);
}
}
시간 복잡도
앞서 설명한 바와 같이, Birthday Paradox에 의해 실제로 구해야 하는
당연히, 매우 큰 수를 다룰 때는 곱셈이나 모듈러 등이 유의미하게 오래 걸린다. 이 경우 적절하게 시간 복잡도에도 이런 항들을 곱해야 할 것이다. 우선은 64비트 (곱셈이