E022. Эффективные 알고리즘ы факторизации
Источник: e-maxx.ru/algo, страница PDF 67.
Здесь приведены реализации нескольких 알고리즘ов факторизации, каждый из которых по отдельности может работать как быстро, так и очень медленно, но в сумме они дают весьма быстрый метод. Описания этих методов не приводятся, тем более что они достаточно хорошо описаны в Интернете.
Метод Полларда p-1
Вероятностный тест, быстро даёт ответ далеко не для всех чисел. returns либо найденный делитель, либо 1, если делитель не был найден.
template <class T>
T pollard_p_1 (T n)
{ // параметры 알고리즘а, существенно влияют на производительность
и качество поиска
const T b = 13;
const T q[] = { 2, 3, 5, 7, 11, 13 };
// несколько попыток 알고리즘а
T a = 5 % n;
for (int j=0; j<10; j++)
{
// ищем такое a, которое взаимно просто с n
while (gcd (a, n) != 1)
{
mulmod (a, a, n);
a += 3;
a %= n;
}
// вычисляем a^M
for (size_t i = 0; i < sizeof q / sizeof q[0]; i++)
{
T qq = q[i];
T e = (T) floor (log ((double)b) / log ((double)qq));
T aa = powmod (a, powmod (qq, e, n), n);
if (aa == 0)
continue;
// проверяем, не найден ли ответ
T g = gcd (aa-1, n);
if (1 < g && g < n)
return g;
} }
// если ничего не нашли
return 1;
}
Метод Полларда "Ро"
Вероятностный тест, быстро даёт ответ далеко не для всех чисел. returns либо найденный делитель, либо 1, если делитель не был найден.
template <class T>
T pollard_rho (T n, unsigned iterations_count = 100000)
{
T
b0 = rand() % n,
b1 = b0,
g;
mulmod (b1, b1, n);
if (++b1 == n)
b1 = 0;
g = gcd (abs (b1 - b0), n);
for (unsigned count=0; count<iterations_count && (g == 1 || g ==
n); count++)
{
mulmod (b0, b0, n);
if (++b0 == n)
b0 = 0;
mulmod (b1, b1, n);
++b1;
mulmod (b1, b1, n);
if (++b1 == n)
b1 = 0;
g = gcd (abs (b1 - b0), n);
}
return g;
}
Метод Бента (модификация метода Полларда "Ро")
Вероятностный тест, быстро даёт ответ далеко не для всех чисел. returns либо найденный делитель, либо 1, если делитель не был найден.
template <class T>
T pollard_bent (T n, unsigned iterations_count = 19)
{
T
b0 = rand() % n,
b1 = (b0*b0 + 2) % n,
a = b1;
for (unsigned iteration=0, series_len=1;
iteration<iterations_count; iteration++, series_len*=2)
{
T g = gcd (b1-b0, n);
for (unsigned len=0; len<series_len && (g==1 && g==n); len++)
{
b1 = (b1*b1 + 2) % n;
g = gcd (abs(b1-b0), n);
}
b0 = a;
a = b1;
if (g != 1 && g != n)
return g;
}
return 1;
}
Метод Полларда Монте-Карло
Вероятностный тест, быстро даёт ответ далеко не для всех чисел. returns либо найденный делитель, либо 1, если делитель не был найден.
template <class T>
T pollard_monte_carlo (T n, unsigned m = 100)
{
T b = rand() % (m-2) + 2;
static std::vector<T> primes;
static T m_max;
if (primes.empty())
primes.push_back (3);
if (m_max < m)
{
m_max = m;
for (T prime=5; prime<=m; ++++prime)
{
bool is_prime = true;
for (std::vector<T>::const_iterator iter=primes.
begin(), end=primes.end();
iter!=end; ++iter)
{
T div = *iter;
if (div*div > prime)
break;
if (prime % div == 0)
{
is_prime = false;
break;
} }
if (is_prime)
primes.push_back (prime);
} }
T g = 1;
for (size_t i=0; i<primes.size() && g==1; i++)
{
T cur = primes[i];
while (cur <= n)
cur *= primes[i];
cur /= primes[i];
b = powmod (b, cur, n);
g = gcd (abs(b-1), n);
if (g == n)
g = 1;
}
return g;
}
Метод Ферма
Это стопроцентный метод, но он может работать очень медленно, если у числа есть маленькие делители. Поэтому запускать его стоит только после всех остальных методов.
template <class T, class T2>
T ferma (const T & n, T2 unused)
{
T2
x = sq_root (n),
y = 0,
r = x*x - y*y - n;
for (;;)
if (r == 0)
return x!=y ? x-y : x+y;
else
if (r > 0)
{
r -= y+y+1;
++y;
}
else
{
r += x+x+1;
++x;
} }
Тривиальное деление
Этот elementарный метод пригодится, чтобы сразу обрабатывать числа с очень маленькими делителями.
template <class T, class T2>
T2 prime_div_trivial (const T & n, T2 m)
{
// сначала проверяем тривиальные случаи
if (n == 2 || n == 3)
return 1;
if (n < 2)
return 0;
if (even (n))
return 2;
// генерируем простые от 3 до m
T2 pi;
const vector<T2> & primes = get_primes (m, pi);
// делим на все простые
for (std::vector<T2>::const_iterator iter=primes.begin(), end=primes.
end();
iter!=end; ++iter)
{
const T2 & div = *iter;
if (div * div > n)
break;
else
if (n % div == 0)
return div;
}
if (n < m*m)
return 1;
return 0;
}
Собираем всё вместе
Объединяем все методы в одной функции. Также функция использует тест на простоту, иначе 알고리즘ы факторизации могут работать очень долго. На예제, можно выбрать тест BPSW (читать статью по BPSW).
template <class T, class T2>
void factorize (const T & n, std::map<T,unsigned> & result, T2 unused)
{
if (n == 1)
;
else
// проверяем, не простое ли number
if (isprime (n))
++result[n];
else
// если number достаточно маленькое, то его
разлагаем простым перебором
if (n < 1000*1000)
{
T div = prime_div_trivial (n, 1000);
++result[div];
factorize (n / div, result, unused);
}
else
{
// number большое, запускаем на нем
알고리즘ы факторизации
T div;
// сначала идут быстрые 알고리즘ы Полларда
div = pollard_monte_carlo (n);
if (div == 1)
div = pollard_rho (n);
if (div == 1)
div = pollard_p_1 (n);
if (div == 1)
div = pollard_bent (n);
// придётся запускать 100%-ый 알고리즘 Ферма
if (div == 1)
div = ferma (n, unused);
// рекурсивно обрабатываем найденные множители
factorize (div, result, unused);
factorize (n / div, result, unused);
} }
Приложение
Скачать [5 КБ] исходник программы, которая использует все указанные методы факторизации и тест BPSW на простоту.
C# 해법
자동 초안, 제출 전 검토using System;
using System.Collections.Generic;
using System.Linq;
public static class AlgorithmDraft
{
// Auto-generated C# draft from the original e-maxx C/C++ listing. Review before production use.
template <class T>
T pollard_p_1 (T n)
{
// параметры алгоритма, существенно влияют на производительность
и качество поиска
const T b = 13;
const T q[] = { 2, 3, 5, 7, 11, 13 };
// несколько попыток алгоритма
T a = 5 % n;
for (int j=0; j<10; j++)
{
// ищем такое a, которое взаимно просто с n
while (gcd (a, n) != 1)
{
mulmod (a, a, n);
a += 3;
a %= n;
}
// вычисляем a^M
for (size_t i = 0; i < sizeof q / sizeof q[0]; i++)
{
T qq = q[i];
T e = (T) floor (log ((double)b) / log ((double)qq));
T aa = powmod (a, powmod (qq, e, n), n);
if (aa == 0)
continue;
// проверяем, не найден ли ответ
T g = gcd (aa-1, n);
if (1 < g && g < n)
return g;
}
}
// если ничего не нашли
return 1;
}
template <class T>
T pollard_rho (T n, unsigned iterations_count = 100000)
{
T
b0 = rand() % n,
b1 = b0,
g;
mulmod (b1, b1, n);
if (++b1 == n)
b1 = 0;
g = gcd (abs (b1 - b0), n);
for (unsigned count=0; count<iterations_count && (g == 1 || g ==
n); count++)
{
mulmod (b0, b0, n);
if (++b0 == n)
b0 = 0;
mulmod (b1, b1, n);
++b1;
mulmod (b1, b1, n);
if (++b1 == n)
b1 = 0;
g = gcd (abs (b1 - b0), n);
}
return g;
}
template <class T>
T pollard_bent (T n, unsigned iterations_count = 19)
{
T
b0 = rand() % n,
b1 = (b0*b0 + 2) % n,
a = b1;
for (unsigned iteration=0, series_len=1;
iteration<iterations_count; iteration++, series_len*=2)
{
T g = gcd (b1-b0, n);
for (unsigned len=0; len<series_len && (g==1 && g==n); len++)
{
b1 = (b1*b1 + 2) % n;
g = gcd (abs(b1-b0), n);
}
b0 = a;
a = b1;
if (g != 1 && g != n)
return g;
}
return 1;
}
template <class T>
T pollard_monte_carlo (T n, unsigned m = 100)
{
T b = rand() % (m-2) + 2;
static std::vector<T> primes;
static T m_max;
if (primes.empty())
primes.push_back (3);
if (m_max < m)
{
m_max = m;
for (T prime=5; prime<=m; ++++prime)
{
bool is_prime = true;
for (std::vector<T>::const_iterator iter=primes.
begin(), end=primes.end();
iter!=end; ++iter)
{
T div = *iter;
if (div*div > prime)
break;
if (prime % div == 0)
{
is_prime = false;
break;
}
}
if (is_prime)
primes.push_back (prime);
}
}
T g = 1;
for (size_t i=0; i<primes.size() && g==1; i++)
{
T cur = primes[i];
while (cur <= n)
cur *= primes[i];
cur /= primes[i];
b = powmod (b, cur, n);
g = gcd (abs(b-1), n);
if (g == n)
g = 1;
}
return g;
}
template <class T, class T2>
T ferma (const T & n, T2 unused)
{
T2
x = sq_root (n),
y = 0,
r = x*x - y*y - n;
for (;;)
if (r == 0)
return x!=y ? x-y : x+y;
else
if (r > 0)
{
r -= y+y+1;
++y;
}
else
{
r += x+x+1;
++x;
}
}
template <class T, class T2>
T2 prime_div_trivial (const T & n, T2 m)
{
// сначала проверяем тривиальные случаи
if (n == 2 || n == 3)
return 1;
if (n < 2)
return 0;
if (even (n))
return 2;
// генерируем простые от 3 до m
T2 pi;
const vector<T2> & primes = get_primes (m, pi);
// делим на все простые
for (std::vector<T2>::const_iterator iter=primes.begin(), end=primes.
end();
iter!=end; ++iter)
{
const T2 & div = *iter;
if (div * div > n)
break;
else
if (n % div == 0)
return div;
}
if (n < m*m)
return 1;
return 0;
}
template <class T, class T2>
void factorize (const T & n, std::map<T,unsigned> & result, T2 unused)
{
if (n == 1)
;
else
// проверяем, не простое ли число
if (isprime (n))
++result[n];
else
// если число достаточно маленькое, то его
разлагаем простым перебором
if (n < 1000*1000)
{
T div = prime_div_trivial (n, 1000);
++result[div];
factorize (n / div, result, unused);
}
else
{
// число большое, запускаем на нем
алгоритмы факторизации
T div;
// сначала идут быстрые алгоритмы Полларда
div = pollard_monte_carlo (n);
if (div == 1)
div = pollard_rho (n);
if (div == 1)
div = pollard_p_1 (n);
if (div == 1)
div = pollard_bent (n);
// придётся запускать 100%-ый алгоритм Ферма
if (div == 1)
div = ferma (n, unused);
// рекурсивно обрабатываем найденные множители
factorize (div, result, unused);
factorize (n / div, result, unused);
}
}
}
C++ 해법
매칭됨/원본template <class T>
T pollard_p_1 (T n)
{
// параметры алгоритма, существенно влияют на производительность
и качество поиска
const T b = 13;
const T q[] = { 2, 3, 5, 7, 11, 13 };
// несколько попыток алгоритма
T a = 5 % n;
for (int j=0; j<10; j++)
{
// ищем такое a, которое взаимно просто с n
while (gcd (a, n) != 1)
{
mulmod (a, a, n);
a += 3;
a %= n;
}
// вычисляем a^M
for (size_t i = 0; i < sizeof q / sizeof q[0]; i++)
{
T qq = q[i];
T e = (T) floor (log ((double)b) / log ((double)qq));
T aa = powmod (a, powmod (qq, e, n), n);
if (aa == 0)
continue;
// проверяем, не найден ли ответ
T g = gcd (aa-1, n);
if (1 < g && g < n)
return g;
}
}
// если ничего не нашли
return 1;
}
template <class T>
T pollard_rho (T n, unsigned iterations_count = 100000)
{
T
b0 = rand() % n,
b1 = b0,
g;
mulmod (b1, b1, n);
if (++b1 == n)
b1 = 0;
g = gcd (abs (b1 - b0), n);
for (unsigned count=0; count<iterations_count && (g == 1 || g ==
n); count++)
{
mulmod (b0, b0, n);
if (++b0 == n)
b0 = 0;
mulmod (b1, b1, n);
++b1;
mulmod (b1, b1, n);
if (++b1 == n)
b1 = 0;
g = gcd (abs (b1 - b0), n);
}
return g;
}
template <class T>
T pollard_bent (T n, unsigned iterations_count = 19)
{
T
b0 = rand() % n,
b1 = (b0*b0 + 2) % n,
a = b1;
for (unsigned iteration=0, series_len=1;
iteration<iterations_count; iteration++, series_len*=2)
{
T g = gcd (b1-b0, n);
for (unsigned len=0; len<series_len && (g==1 && g==n); len++)
{
b1 = (b1*b1 + 2) % n;
g = gcd (abs(b1-b0), n);
}
b0 = a;
a = b1;
if (g != 1 && g != n)
return g;
}
return 1;
}
template <class T>
T pollard_monte_carlo (T n, unsigned m = 100)
{
T b = rand() % (m-2) + 2;
static std::vector<T> primes;
static T m_max;
if (primes.empty())
primes.push_back (3);
if (m_max < m)
{
m_max = m;
for (T prime=5; prime<=m; ++++prime)
{
bool is_prime = true;
for (std::vector<T>::const_iterator iter=primes.
begin(), end=primes.end();
iter!=end; ++iter)
{
T div = *iter;
if (div*div > prime)
break;
if (prime % div == 0)
{
is_prime = false;
break;
}
}
if (is_prime)
primes.push_back (prime);
}
}
T g = 1;
for (size_t i=0; i<primes.size() && g==1; i++)
{
T cur = primes[i];
while (cur <= n)
cur *= primes[i];
cur /= primes[i];
b = powmod (b, cur, n);
g = gcd (abs(b-1), n);
if (g == n)
g = 1;
}
return g;
}
template <class T, class T2>
T ferma (const T & n, T2 unused)
{
T2
x = sq_root (n),
y = 0,
r = x*x - y*y - n;
for (;;)
if (r == 0)
return x!=y ? x-y : x+y;
else
if (r > 0)
{
r -= y+y+1;
++y;
}
else
{
r += x+x+1;
++x;
}
}
template <class T, class T2>
T2 prime_div_trivial (const T & n, T2 m)
{
// сначала проверяем тривиальные случаи
if (n == 2 || n == 3)
return 1;
if (n < 2)
return 0;
if (even (n))
return 2;
// генерируем простые от 3 до m
T2 pi;
const vector<T2> & primes = get_primes (m, pi);
// делим на все простые
for (std::vector<T2>::const_iterator iter=primes.begin(), end=primes.
end();
iter!=end; ++iter)
{
const T2 & div = *iter;
if (div * div > n)
break;
else
if (n % div == 0)
return div;
}
if (n < m*m)
return 1;
return 0;
}
template <class T, class T2>
void factorize (const T & n, std::map<T,unsigned> & result, T2 unused)
{
if (n == 1)
;
else
// проверяем, не простое ли число
if (isprime (n))
++result[n];
else
// если число достаточно маленькое, то его
разлагаем простым перебором
if (n < 1000*1000)
{
T div = prime_div_trivial (n, 1000);
++result[div];
factorize (n / div, result, unused);
}
else
{
// число большое, запускаем на нем
алгоритмы факторизации
T div;
// сначала идут быстрые алгоритмы Полларда
div = pollard_monte_carlo (n);
if (div == 1)
div = pollard_rho (n);
if (div == 1)
div = pollard_p_1 (n);
if (div == 1)
div = pollard_bent (n);
// придётся запускать 100%-ый алгоритм Ферма
if (div == 1)
div = ferma (n, unused);
// рекурсивно обрабатываем найденные множители
factorize (div, result, unused);
factorize (n / div, result, unused);
}
}
Java 해법
자동 초안, 제출 전 검토import java.util.*;
import java.math.*;
public class AlgorithmDraft {
// Auto-generated Java draft from the original e-maxx C/C++ listing. Review before production use.
template <class T>
T pollard_p_1 (T n)
{
// параметры алгоритма, существенно влияют на производительность
и качество поиска
const T b = 13;
const T q[] = { 2, 3, 5, 7, 11, 13 };
// несколько попыток алгоритма
T a = 5 % n;
for (int j=0; j<10; j++)
{
// ищем такое a, которое взаимно просто с n
while (gcd (a, n) != 1)
{
mulmod (a, a, n);
a += 3;
a %= n;
}
// вычисляем a^M
for (size_t i = 0; i < sizeof q / sizeof q[0]; i++)
{
T qq = q[i];
T e = (T) floor (log ((double)b) / log ((double)qq));
T aa = powmod (a, powmod (qq, e, n), n);
if (aa == 0)
continue;
// проверяем, не найден ли ответ
T g = gcd (aa-1, n);
if (1 < g && g < n)
return g;
}
}
// если ничего не нашли
return 1;
}
template <class T>
T pollard_rho (T n, unsigned iterations_count = 100000)
{
T
b0 = rand() % n,
b1 = b0,
g;
mulmod (b1, b1, n);
if (++b1 == n)
b1 = 0;
g = gcd (abs (b1 - b0), n);
for (unsigned count=0; count<iterations_count && (g == 1 || g ==
n); count++)
{
mulmod (b0, b0, n);
if (++b0 == n)
b0 = 0;
mulmod (b1, b1, n);
++b1;
mulmod (b1, b1, n);
if (++b1 == n)
b1 = 0;
g = gcd (abs (b1 - b0), n);
}
return g;
}
template <class T>
T pollard_bent (T n, unsigned iterations_count = 19)
{
T
b0 = rand() % n,
b1 = (b0*b0 + 2) % n,
a = b1;
for (unsigned iteration=0, series_len=1;
iteration<iterations_count; iteration++, series_len*=2)
{
T g = gcd (b1-b0, n);
for (unsigned len=0; len<series_len && (g==1 && g==n); len++)
{
b1 = (b1*b1 + 2) % n;
g = gcd (abs(b1-b0), n);
}
b0 = a;
a = b1;
if (g != 1 && g != n)
return g;
}
return 1;
}
template <class T>
T pollard_monte_carlo (T n, unsigned m = 100)
{
T b = rand() % (m-2) + 2;
static std::vector<T> primes;
static T m_max;
if (primes.empty())
primes.push_back (3);
if (m_max < m)
{
m_max = m;
for (T prime=5; prime<=m; ++++prime)
{
boolean is_prime = true;
for (std::vector<T>::const_iterator iter=primes.
begin(), end=primes.end();
iter!=end; ++iter)
{
T div = *iter;
if (div*div > prime)
break;
if (prime % div == 0)
{
is_prime = false;
break;
}
}
if (is_prime)
primes.push_back (prime);
}
}
T g = 1;
for (size_t i=0; i<primes.size() && g==1; i++)
{
T cur = primes[i];
while (cur <= n)
cur *= primes[i];
cur /= primes[i];
b = powmod (b, cur, n);
g = gcd (abs(b-1), n);
if (g == n)
g = 1;
}
return g;
}
template <class T, class T2>
T ferma (const T & n, T2 unused)
{
T2
x = sq_root (n),
y = 0,
r = x*x - y*y - n;
for (;;)
if (r == 0)
return x!=y ? x-y : x+y;
else
if (r > 0)
{
r -= y+y+1;
++y;
}
else
{
r += x+x+1;
++x;
}
}
template <class T, class T2>
T2 prime_div_trivial (const T & n, T2 m)
{
// сначала проверяем тривиальные случаи
if (n == 2 || n == 3)
return 1;
if (n < 2)
return 0;
if (even (n))
return 2;
// генерируем простые от 3 до m
T2 pi;
const vector<T2> & primes = get_primes (m, pi);
// делим на все простые
for (std::vector<T2>::const_iterator iter=primes.begin(), end=primes.
end();
iter!=end; ++iter)
{
const T2 & div = *iter;
if (div * div > n)
break;
else
if (n % div == 0)
return div;
}
if (n < m*m)
return 1;
return 0;
}
template <class T, class T2>
void factorize (const T & n, std::map<T,unsigned> & result, T2 unused)
{
if (n == 1)
;
else
// проверяем, не простое ли число
if (isprime (n))
++result[n];
else
// если число достаточно маленькое, то его
разлагаем простым перебором
if (n < 1000*1000)
{
T div = prime_div_trivial (n, 1000);
++result[div];
factorize (n / div, result, unused);
}
else
{
// число большое, запускаем на нем
алгоритмы факторизации
T div;
// сначала идут быстрые алгоритмы Полларда
div = pollard_monte_carlo (n);
if (div == 1)
div = pollard_rho (n);
if (div == 1)
div = pollard_p_1 (n);
if (div == 1)
div = pollard_bent (n);
// придётся запускать 100%-ый алгоритм Ферма
if (div == 1)
div = ferma (n, unused);
// рекурсивно обрабатываем найденные множители
factorize (div, result, unused);
factorize (n / div, result, unused);
}
}
}
Материал разбит как 알고리즘ическая 문제: изучить постановку, понять асимптотику и реализовать 알고리즘 на выбранном языке.
Vacancies for this task
활성 채용 with overlapping task tags are 표시됨.