整数分解及素性判断学习笔记
整数分解
整数分解事实上有三个子问题:
1. 合性判断。即判断一个数是否为合数。
2. 素性判断。即确切判断一个数是否为素数。
3. 寻找可能的质因子。
一个大概率的素性判断——Miller-Rabin算法
费马小定理
若p为质数,那么。
当我们要判断整数p是否为素数时,若,则p为合数。
但若找不到满足条件的x,p只有99.999745%的概率为素数。因为存在一种数——Carmichael卡迈克尔数。
卡迈克尔数
若p为合数,且,则称p为卡迈克尔数。
卡迈克尔数有至少三个不同素因子。
1e8以内只有255个卡迈克尔数,前三个为561,1105,1729。
此时考虑一个新的定理:欧几里得引理。
欧几里得引理
若p为素数,且,,则。
证明:
首先将p写成的形式,那么对于已选择的x,首先运算,然后将这个数平方,得,重复此步骤k-1次,得,即。
那么在这k步中,若出现了1,而上一次的结果不是1或-1,则p为合数;若最后结果不为1,则p为合数。
那么当p通过一次x的测试,则p为合数的概率为1/4;那么若p通过5次测试,素性判断正确的概率为99.9%。
如果选择x=2,3,5,7,那么在2.5*10^13内仅有3215031751会判断错误。
一个复杂度并不太好的素性判断
除了2,所有素数都为奇数。对于一个奇数p,若它为合数,则可以写成的形式。令,则。若为完全平方数,则p为合数。
但这个算法十分不优秀..
优化掉这个不太优秀的算法——Pollard Rho
考虑这样一个问题:有没有办法快速且高概率的找到一个小因子呢。
很容易发现一个不高速低概率的方式:随机试除。
这个算法概率极低,基本相当于从[1,N]随机选择一个数,这个数=x的概率——1/N。
但考虑这样一个算法:从[1,N]随机选择两个数a和b,|a-b|=x的概率。显然是2/N。
这个优化非常小,但我们再优化一下这个算法:从[1,N]随机选择k个数x1...xk,存在一对i和j使得|xi-xj|=x的概率。
此时考虑一个经典问题——Birthday paradox生日悖论。
生日悖论
随机选择N个人,设事件P为这N个人中存在两个人生日相同。求P发生的概率。
考虑没有两人生日相同的概率。将人一个一个加入,第二个人和第一个人生日不同的概率为,第三个人和前两个人生日不同的概率为...,第n个人和前面所有人生日不同的概率为。
当时,P发生的概率大于50%。
考虑推广生日悖论。生日悖论等价于N个数中随机选k个数,存在一对数差为0的概率。可以将其推广为:N个数中随机选k个数,存在一对数差为x的概率。即上方的问题。
也就是说,如果我们选择个数,就可以50%的概率找到N的一个小因子。但是可能太过巨大,无法存在内存中,需要特殊处理。
考虑一种流水线的随机方式和floyd判环方法。
Pollard推荐的随机数生成方法
定义。a可以随机生成或直接用a=2。那么随机选取后,,每次和前一个值作差即可。
但当伪随机数出现循环节,后面的伪随机数就没有意义了,我们需要判断掉这种情况。
这里用到的判断方法为floyd算法。
考虑跑道上的两个人,一个人的速度为另一个人的两倍。那么当跑道是环形,第二个人一定可以追上第一个人。这样就可以不消耗空间的判断是否出现了循环节。
1 int find_factorplus(int N) { 2 a = 2; 3 b = a; 4 do { 5 a = f(a);//a runs once 6 b = f(f(b));//b runs twice as fast 7 p = GCD( abs( b - a ) , N); 8 if( p > 1 ) return p;//Found factor: p 9 } while( b != a ); 10 return 0;//Failed. :-( 11 }
这样我们就得到了Pollard Rhd算法。
Pollard Rho与Miller-Rabin的结合——一个优秀的整数分解算法
Pollard Rho算法的优势是可以在的复杂度内得到一个因子p;而当N的因子少而大时,我们可以使用M_R算法来验证下去。这样就得到了一个大概率且高效的整数分解算法。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int ps[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; 7 const int pcnt = sizeof(ps) / sizeof(int); 8 9 inline LL quick_mul(LL a, LL b, LL m) 10 { 11 LL d = ((long double)a / m * b + 1e-8); 12 LL r = a * b - d * m; 13 return r < 0 ? r + m : r; 14 } 15 16 inline LL quick_pow(LL a,LL b,LL m) 17 { 18 LL res = 1; 19 for(; b; b >>= 1, a = quick_mul(a, a, m)) 20 if (b & 1) 21 res = quick_mul(res, a, m); 22 return res; 23 } 24 25 inline LL gcd(LL a, LL b) 26 { 27 if (!a || !b) 28 return a + b; 29 int t = __builtin_ctzll(a | b); 30 a >>= __builtin_ctzll(a); 31 do 32 { 33 b >>= __builtin_ctzll(b); 34 if (a > b) 35 { 36 LL t = b; 37 b = a; 38 a = t; 39 } 40 b -= a; 41 } 42 while (b != 0); 43 return a << t; 44 } 45 46 inline int Miller_Rabin(LL n) 47 { 48 if (n == 1) 49 return 0; 50 if (n == 2 || n == 3 || n == 5) 51 return 1; 52 if (!(n & 1) || (n % 3 == 0) || (n % 5 == 0)) 53 return 0; 54 LL m = n - 1; 55 int k = 0; 56 while (!(m & 1)) 57 m >>= 1, ++k; 58 for (int i = 0; i < pcnt && ps[i] < n; ++i) 59 { 60 LL x = quick_pow(ps[i], m, n), y = x; 61 for (int i = 0; i < k; ++i) 62 { 63 x = quick_mul(x, x, n); 64 if (x == 1 && y != 1 && y != n - 1) 65 return 0; 66 y = x; 67 } 68 if (x != 1) 69 return 0; 70 } 71 return 1; 72 } 73 74 inline LL next_rand(LL x, LL n, LL a) 75 { 76 LL t = quick_mul(x, x, n) + a; 77 return t < n ? t : t - n; 78 } 79 80 const int M = (1 << 7) - 1; 81 inline LL Pollard_Rho(LL n) 82 { 83 if (n % 2 == 0) 84 return 2; 85 if (n % 3 == 0) 86 return 3; 87 LL x = 0, y = 0, t = 1, q = 1, a = (rand() % (n - 1)) + 1; 88 for (int k = 2; true; k <<= 1, y = x, q = 1) 89 { 90 for (int i = 1; i < k; ++i) 91 { 92 x = next_rand(x, n, a); 93 q = quick_mul(q, abs(x - y), n); 94 if (!(i & M)) 95 { 96 t = gcd(q, n); 97 if (t > 1) 98 break; 99 } 100 } 101 if (t > 1 || (t = gcd(q, n)) > 1) 102 break; 103 } 104 if (t == n) 105 for (t = 1; t == 1; t = gcd(abs((x = next_rand(x, n, a)) - y), n)); 106 return t; 107 } 108 109 LL f[105]; 110 int cnt; 111 112 void solve(LL n) 113 { 114 if (n == 1) 115 return; 116 if (Miller_Rabin(n)) 117 { 118 f[cnt++] = n; 119 return; 120 } 121 LL t = n; 122 while (t == n) 123 t = Pollard_Rho(n); 124 solve(t); 125 solve(n / t); 126 } 127 128 int main() 129 { 130 LL n; 131 while (~scanf("%lld", &n)) 132 { 133 cnt = 0; 134 solve(n); 135 sort(f, f + cnt); 136 for (int i = 0; i < cnt; ++i) 137 printf("%lld ", f[i]); 138 putchar('\n'); 139 } 140 return 0; 141 }
原根的存在性及个数证明
https://www.cnblogs.com/zhixingr/p/6822370.html