素因数分解に
- > パート1:
算術の基本定理:
一つは有限の質量の積にユニークな番号とすることができるよりも大きい任意の正の整数は、のように書くことができます。
\ [N = \ prod_ {i = 1} ^ m個のP_I ^ {C_I} \]
前記\(C_I \)は正の整数、です\(P_I \)を満たす、素数です\(P_1 <P_2 <... < P_M \を)
- > パート2:
分解:
- トライアル部門
決定された素数"試行除算"とプライムスクリーニングを"結合\(のエラトステネス\)ふるい"、我々は、スキャンすることができ、\(\ SQRT N \ 2-)各数値\(X∈Z \)であれば、\(Xを\)割り切れるの\(N \)、から\(N \)すべての素因数を除去するための\(X \) 、累積除去しながら\(X \)数。
上記のアルゴリズムは、この合成数を取り除くために、走査前補因子の特定の数は、このプロセスで割り切れるようにして得られたことを確実にする\(N \)が素数でなければなりません。時間の複雑さがある\(O(\ sqrtのN)\) 。
注目に値する点場合、すなわち\(N \)は任意ではない\(2- \ SQRT N \)が割り切れる整数であり、\(N \)を分解することなく、素数です。
- ポラード-Rhoのアルゴリズム
試験区分の時間複雑\(O(\ SQRT N) \) と\(ポラードのRho \)アルゴリズムで実用的複雑性理論\(O(\ SQRTの[4] {N})\)が大きくなっています、プログラムの効率を向上させます。
学習\(ポラードのRho \)アルゴリズムを前に、我々はいくつかの知識を持っている必要があります。
1. ミラー・ラビンアルゴリズム
- 学習要件:マスターフェルマーの小定理、二次プロービングの定理
したがって、参照、知識の合同を理解するために合同し、関連する中国の剰余定理を
- フェルマーの小定理:
場合は\(のp \)は素数で、その後、
\ [∀a∈Z、^ Pの\当量の\ PMOD {P} \]
フェルマーの小定理の証明は、「高度なアルゴリズムコンテストへのガイド」を参照してくださいすることができ、読者が自分自身やBaiduのを証明しようとすることができます。
同じ番号の上記定理\(P \) 、塩基番号複数の\(\)は、それが上記式を満たす場合、\(P \)は合成数です。
- 二次プロービング定理:
場合\(P \)は素数であり、
そして\(X ^ 2 \当量。1 \ PMOD {P} \) 、次いで\(X \当量1 \ PMOD {P} \) または\(X \当量のp-1 \ PMOD {P} \)
プログラムの精度を向上させるために使用される二次プロービング定理:
数の我々\(P \)を満足します。$ A ^ {P-1 } \当量1 \ PMOD {P} $ [ フェルマーの小定理]
その後のために\(2 | P - 1 \) 、あなたが得ることができます- \(1 \ PMOD {p型} \ {1 P} {2})^ 2 \当量(X- ^ \ FRAC)は、私たちは少し定理を持っているが、フェルマーのディスカッション$ X ^ \ FRAC {P- 1} {2} MOD {P} \ に基づいて(値(以下\と称する)\ K $)に:
- 場合は\(K≠1 \) 。そしてP≠-K 1 $ \(、その後、\) Pは合成数の$のです\(偽\に戻り\) 。
- もし\(1 K = \。) 、次いで再帰\(X ^ \ P-FRAC 1} {2} {\。) 。
- もし\(K-P 1 = \) 、ない再帰記載の二次プローブ定理によって確認することができない\(P \)を複合であり、\(trueに戻り\ \) 。
(上記引用文献の一部)
私たちは、数よりも多く取る\(のx \)が精度を向上することができるようになります。
上記を達成するためにミラーラビンのアルゴリズム効率的であり、\((O(logN個)) \) 乱数決定プライムアルゴリズム。
昨日2つのがんのデータやカードを私に長い時間、@の感謝雪のダンスの巨大な男の助けを
そして、2つの素数取る\(61 \)と\(24251 \)し、3個の乱数を(\空)\カードが実質的にならないように、。
しかし、私はまだ、次のコードで説明するように、事故のいくつかの種類、ので、3個の乱数をスタックし、着実にACように私は、4個の乱数を書きました。
//Miller_Rabin 算法
inline ll qmul(ll x, ll y, ll mod) { //快速乘
return (x*y-(ll)((long double)x/mod*y)*mod+mod)%mod;
}
inline ll qpow(ll x, ll m, ll mod) { //快速幂
register ll ans = 1;
while(m) {
if(m & 1)
ans = qmul(ans, x, mod) % mod;
x = qmul(x, x, mod) % mod;
m >>= 1;
}
return ans;
}
inline bool FMLT(ll x, ll p) { //费马小定理
return qpow(x, p - 1, p) == 1;
}
inline bool Miller_Rabin(ll x, ll p) {
if(!FMLT(x, p)) return 0;
register ll k = p - 1;
for(;!(k & 1);) {
k >>= 1;
ll K = qpow(x, k, p);
if(K != 1 && K != p - 1) return 0;
if(K == p - 1) return 1;
}
return 1;
}
inline bool check(ll p) { // 判断质数
if(p == 1) return 0;
ll t1 = rand(), t2 = rand(), t3 = rand(), t4 = rand();
if(p == 61 || p == 24251 || p == t1 || p == t2 || p == t3 || p == t4) return 1;
/*这行写成
if(p == 2 || p == 3 || p == t1 || p == t2 || p == t3) return 1;
(原三个随机数)是可以AC的,但是上面那种写法才是理论正确的,所以多加了一个随机数吧tql
*/
bool s1 = Miller_Rabin(61, p), s2 = Miller_Rabin(24251, p);
bool s3 = Miller_Rabin(t1, p), s4 = Miller_Rabin(t2, p);
bool s5 = Miller_Rabin(t3, p), s6 = Miller_Rabin(t4, p);
return s1 && s2 & s3 && s4 && s5 && s6;
}
あなたはこれを超えていると思いますか?あなただけの癌のようなデータの、のように、なぜこの小さな問題を見つけるのだろうか?我々はので\(RANDが\)乱数が現在の数よりも大きくすることができる\(のP- \)の回答に貢献し、あるなし、その後、我々はいくつかの最適化が必要になります。
ll t1 = rand() % (p - 3) + 2
狭くするランダムワード\([2 ,. 1-P)を\) 。そして、少しのデータを渡すことができます
しかし、これは我々は4個の乱数が素数の確率に共同決意の数を入れていることを知って、終わりではありません(\ FRAC {1} {4} \)\ので、我々はこの確率を低減する必要がある、あなたは、実験の数を増やす必要があります我々はできる\(チェック\)と\(ミラー\ラビン\)一緒に書くと、「数」が追加\(REPEAT \) 、一定の時間複雑以内に、我々はいくつかの実験を行いました。
ここでは、最適化の中核である(\(repaetの\)開くように\(23 \)は保険のために、するために開くことができ、もちろん、オーバーしているようだ\(50 \) )
inline bool Miller_Rabin(ll p, int repeat) {
if(p == 2 || p == 3) return true;
if(p % 2 == 0 || p == 1) return false;
//特判
register ll k = p - 1;
register int cnt = 0;
while(!(k & 1)) ++cnt, k >>= 1;
srand((unsigned)time(NULL));//种子
for(register int i = 0; i < repeat; i++) {
register ll test = rand() % (p - 3) + 2;
register ll x = qpow(test, k, p), y = 0;
for(register int j = 0; j < cnt; j++) { //二次探测逆过程
y = qmul(x, x, p);
if(y == 1 && x != 1 && x != (p - 1))
return false;
x = y;
}
if(y != 1) return false; //费马小定理
}
return true;
}
すべての改善のアイデア@巨大な男のおかげで雪のダンス
2. 高速電力|乗算速い(私はこれは言うまでもないと信じて)
typedef long long ll;
ll quick_mul(ll x, ll y, ll mod) {
return (x*y-(ll)((long double)x/mod*y)*mod+mod)%mod;
}
ll quick_pow(ll x, ll m, ll mod) {
ll ans = 1;
while(m) {
if(m & 1)
ans = ans * x % mod;
x = x * x % mod;
m >>= 1;
}
return ans;
}
3. \(GCDの\) (これは私がそれを言うことはありません)
二つの方法:テクニックを下げ|ユークリッド
以下は、ユークリッドアルゴリズムのコードを示しています。
ll gcd(ll x,ll y) {
ll temp;
while(y) {
temp = x % y;
x = y;
y = temp;
}
return x;
}
もちろん、これは最も簡単です\(GCDの\)アルゴリズム、我々はまた、最適化されたバイナリになります
参照してください関連除数を
以下は、ポラード-Rhoのアルゴリズムを達成するために:
ここで私は「をご紹介したいと思います誕生日のパラドックス」、この形而上学的なパラドックスの使用は、我々はすぐに品質係数を分解することができます。その後、我々は使用することができます\(RAND()\)無限ループに非準拠の番号を見つけるためになっている時に使用し、ランダム関数を\(フロイド\)リングを判断する方法、「通常の速度」、「2速」 、二つの「初めての出会い」、無限ループ内であること「円を完成」という。
//参考代码
void Pollard_Rho(int x)
{
if(test(x)) {//素数测试
ans=max(x, ans);
return;
}
ll t1 = rand() % (x-1) + 1;
ll t2 = (qmul(t2, t2, x)+b) % x;
ll b = rand() % (x-1) + 1;
for(;t1 != t2;) {//Flord判断是否进入死循环
ll t = gcd(abs(t1-t2), x);
if(t != 1 && t != x)
{
Pollard_Rho(t),Pollard_Rho(x/t);
}
t1 = (qmul(t1, t1, x)+b) % x;
t2 = (qmul(t2, t2, x)+b) % x;
t2 = (qmul(t2, t2, x)+b) % x;//“两倍速”
}
}
コードは、時間複雑度を超える時間複雑さに達していない\(O(\ SQRT [4] N)\) 、頻繁に要求メインボトルネック\を(GCDを\) 。
だからここに形而上学の定数の最適化です\(127 \) 、各撮影\(127 \)サンプルが行われました\(1 \)回\(GCDの\)は、良い取引のように見えますか?
以下のコードのコア部分を最適化されている(これは本当に私の手を再生することです):
inline void Pollard_Rho(ll x) {
if(check(x)) { //检查质数
ans = max(x, ans);
return;
}
ll s1 = rand() % (x - 1) + 1;
ll m = rand() % (x - 1) + 1, p = 1;
ll s2 = (qmul(s1, s1, x) + m) % x;
for(register ll i = 1; s1 != s2; i++) {
//边界:不进入死循环 即S1 != S2(Floyd判断环)
p = qmul(p, abs(s1 - s2), x);
if(p == 0) {
ll K = gcd(abs(s1 - s2), x);
if(K != 1 && K != x)
Pollard_Rho(K), Pollard_Rho(x / K);
return;
}
if(i % 127 == 0) {
p = gcd(p, x);
if(p != 1 && p != x) {
Pollard_Rho(p), Pollard_Rho(x / p);
return;
}
p = 1;
}
s1 = (qmul(s1, s1, x) + m) % x;
s2 = (qmul(s2, s2, x) + m) % x;
s2 = (qmul(s2, s2, x) + m) % x; //跑两倍
}
//以下部分:最后有可能一个环长不到127
p = gcd(p, x);
if(p != 1 && p != x) {
Pollard_Rho(p), Pollard_Rho(x / p);
return;
}
}
この点では、テンプレートの質問を完了しようとすることができ、読者の一定の理解があります:
P4718 [テンプレート]ポラード-のRhoアルゴリズムは、最後の章素数篩被験者、下記参照コードを実行することが必要です。
\(ポラード-Rhoの\)アルゴリズム\(コード:\)
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
typedef long long ll;
ll ans,n;
inline ll qmul(ll x, ll y, ll mod) {
return (x*y-(ll)((long double)x/mod*y)*mod+mod)%mod;
}
inline ll qpow(ll x, ll m, ll mod) {
register ll ans = 1;
while(m) {
if(m & 1)
ans = qmul(ans, x, mod) % mod;
x = qmul(x, x, mod) % mod;
m >>= 1;
}
return ans;
}
inline bool FMLT(ll x, ll p) {
return qpow(x, p - 1, p) == 1;
}
inline bool Miller_Rabin(ll p, int repeat) {
if(p == 2 || p == 3) return true;
if(p % 2 == 0 || p == 1) return false;
register ll k = p - 1;
register int cnt = 0;
while(!(k & 1)) ++cnt, k >>= 1;
srand((unsigned)time(NULL));
for(register int i = 0; i < repeat; i++) {
register ll test = rand() % (p - 3) + 2;
register ll x = qpow(test, k, p), y = 0;
for(register int j = 0; j < cnt; j++) {
y = qmul(x, x, p);
if(y == 1 && x != 1 && x != (p - 1))
return false;
x = y;
}
if(y != 1) return false;
}
return true;
}
inline ll gcd(ll x, ll y) {
register ll temp;
while(y) {
temp = x % y;
x = y;
y = temp;
}
return x;
}
inline void Pollard_Rho(ll x) {
if(Miller_Rabin(x, 50)) {
ans = max(x, ans);
return;
}
register ll s1 = rand() % (x - 1) + 1;
register ll m = rand() % (x - 1) + 1, p = 1;
register ll s2 = (qmul(s1, s1, x) + m) % x;
for(register ll i = 1; s1 != s2; i++) {
p = qmul(p, abs(s1 - s2), x);
if(p == 0) {
register ll K = gcd(abs(s1 - s2), x);
if(K != 1 && K != x)
Pollard_Rho(K), Pollard_Rho(x / K);
return;
}
if(i % 127 == 0) {
p = gcd(p, x);
if(p != 1 && p != x) {
Pollard_Rho(p), Pollard_Rho(x / p);
return;
}
p = 1;
}
s1 = (qmul(s1, s1, x) + m) % x;
s2 = (qmul(s2, s2, x) + m) % x;
s2 = (qmul(s2, s2, x) + m) % x;
}
p = gcd(p, x);
if(p != 1 && p != x) {
Pollard_Rho(p), Pollard_Rho(x / p);
return;
}
}
namespace IN_OUT {
template <class T>
inline void read(T &x) {
static char c;
static bool op;
while(!isdigit(c = getchar()) && c != '-');
x = (op = c == '-')? 0: c-'0';
while(isdigit(c = getchar()))
x = x * 10 + c - '0';
if(op) x = ~x + 1;
}
template <class T>
inline void output(T x) {
register char buf[30], *tail = buf;
if(x == 0) putchar('0');
else {
if(x < 0) putchar('-'), x = ~x + 1;
for(; x; x /= 10) *++tail = x % 10 + '0';
for(; tail != buf; --tail)
putchar(*tail);
}
putchar('\n');
}
}
using namespace IN_OUT;
int main() {
read(n);
for(register int i = 1; i <= n; i++) {
register ll x;
read(x);
if(Miller_Rabin(x, 50))
printf("Prime\n");
else {
ans=0;
while(ans == 0)
Pollard_Rho(x);
output(ans);
}
}
return 0;
}
以下は、記録モジュール評価質問、追加の高速伝送速度とリードバンチです\(登録\)、$ $ 50のREPEATの値。
章の練習の終わり※
\(終わり\)
\(PS:\)
材料の一部は、李Yudong「アルゴリズムコンテストステップアップガイド」から来ています
\(ポラード・フォー\)アルゴリズムを参照します:
話題の少ない素因数分解、追加してください