数论合集
来源:互联网 发布:php系统开发教程 编辑:程序博客网 时间:2024/04/29 13:42
注:本文关于素数部分属于转载,剩余部分根据网上资料总结。
快速幂
a^b % k
long long PowerMod(long long a, long long b, long long k) { long long tmp = a, ret = 1; while (b) { if (b & 1) ret = ret * tmp % k; tmp = tmp * tmp % k; b >>= 1; } return ret;}
素数:只有两个正因数(1和自己)的自然数。素数,作为数论中最基础的理论之一,又是许多著名定理的根源。
几个可以无视的性质:1. 1不是素数
2. 除了2以为所有偶数都是合数。
最大公约数和最小公倍数:
欧几里得(辗转相除法)
int gcd(int a, int b) { //最大公约数 if (b == 0) return a; return gcd(b, a%b);}int lcm(int a, int b) { //最小公倍数 if (a*b == 0) return 0; return a*b / gcd(a, b);}
扩展欧几里得算法:
求解方程:ax+by = gcd(a,b) 的整数解(其中a,b都为非零整数)
int ex_gcd(int a, int b, int &x, int &y) { if (b == 0) { x = 1, y = 0; return a; } int r = ex_gcd(b, a%b, x, y); int t = x; x = y; y = t - a/b *y; return r; //返回gcd(a,b)}
互质:若 gcd(n, m) = 1; 则 n 和 m 互质。
关于互质的一个结论:
比 n 小的且与 n 互质的数,必成对存在。即:n 与 m 互质(m < n),则 n 与 n-m 互质。
欧拉函数:对于一个正整数n,小于n且和n互质的正整数(包括 1)的个数,记作 φ(n)。
欧拉定理:对于互质的正整数 a 和 n ,有 a^φ(n) ≡ 1 mod n
推论:所有比 n 小且与 n 互质的数之和为 φ(n) * n / 2.
代码:
int Euler(int n) { //欧拉函数 int e = n; for (int i=2; i*i<=n; i++) if (n % i == 0) { e = e*(i-1)/i; while (n % i == 0) n /= i; } if (n > 1) e = e*(n-1) / n; return e;}
提到素数,第一反应都会是如果判断素数,找到我们想要的素数。这里列举几种最常用的判断素数的方法。
1. 朴素判别素数
简述:即判断一个数N是不是素数,只需要判断从2到N^0.5的数是否能整除N,如果能则不是素数,否则就是素数。
代码:(无视之...)
评价:从定义出发,简单易懂,适合初初初学者,但要理解上限为啥是N^0.5。
2. 朴素筛法
简述:筛法的原理这里就不赘述了,思想就是去掉当前的素数的倍数(因为他们一定是合数)。
代码:
const int maxn = 10000000;bool IsNotPrime[maxn]; // 判断是否为素数.void PrimeNormal(void){ // 朴素的筛法. 10000000内0.7s出解. int i, j; memset(IsNotPrime, 0, sizeof(IsNotPrime)); // 初始赋值所有数都是素数. IsNotPrime[1] = 1; IsNotPrime[0] = 1; // 0和1不是素数. for (i = 2; i < maxn; i++) { if (!IsNotPrime[i]) { // 注意:这里只用素数来筛,因为合数筛时肯定被素数筛过了,所以没必要. if (1LL * i * i > 1LL * maxn) continue; for(j = i * i; j < maxn; j += i) IsNotPrime[j] = 1; } }}
评价:相对朴素的判断素数,速度上有了质的飞跃,但是仍有些素数的性质没有用上,导致速度不够理想。
优化:我们可以在朴素筛法上做些显而易见的优化:用到前面提到的可以无视的性质,我们可以把除了2以外的偶数提前判断,这样只要循环奇数即可。
3. 线性筛法
简述:朴素筛法虽然是筛素数的倍数,但是所有倍数都要筛过去,这是完全没必要的。
我们可以利用,每个合数必有一个最小质因数,每个合数仅被它的最小质因数筛去正好一次这个性质来优化筛法.
代码:
const int maxn = 10000000;bool IsNotPrime[maxn]; // 判断是否为素数.int PrimeList[maxn]; // 素数列表.int PrimeNum;void Prime_Linear(void){ // 速度比朴素筛法快2倍以上,该筛法进行稍微修改即可用于求欧拉函数Phi[]. int i, j; memset(IsNotPrime, 0, sizeof(IsNotPrime)); // 初始赋值所有数都是素数. IsNotPrime[1] = 1; IsNotPrime[0] = 1; // 0和1不是素数. for (i = 4; i < maxn; i += 2) IsNotPrime[i] = 1; // 除2以外的所有偶数都不是素数. PrimeNum = 0; for (i = 3; i < maxn; i += 2) { if (!IsNotPrime[i]) { // 如果是素数则加入素数列表. PrimeList[PrimeNum++] = i; } // 注意:这里与朴素筛法不同,即使合数也要进行筛. // 因为这里素数只筛它的素数倍,那么有些合数就可能没被筛掉. // 而这些合数就需要合数来晒,而且只要筛到它的最小质因子倍即可(想想为什么?). for (j = 0; j < PrimeNum && i * PrimeList[j] < maxn; j++) { IsNotPrime[i * PrimeList[j]] = 1; if (i % PrimeList[j] == 0) { // 说明PrimeList[j]是i的最小质因子,即i * PrimeList[j]的最小质因子,则跳出. break; } } }}评价:该筛法利用了每个合数必有一个最小质因数,素数只晒素数倍,合数筛到最小质因子倍,复杂度是线性的.
4. 区间筛素数
简述:有的时候,我们需要知道某个特定区间的素数(区间大小较小,但数可能很大)。
那么数组就开不下,这时候我们仍然可以使用筛法,只是所有的下标都进行了偏移。
大家理解下面这段代码可以先用普通筛法写,然后数组下标集体移动即可。
const int maxn = 100000;int PrimeList[maxn];int PrimeNum;bool IsNotPrime[maxn]; // IsNotPrime[i] = 1表示i + L这个数是素数.void SegmentPrime(int L, int U){ // 求区间[L, U]中的素数. int i, j; int SU = sqrt(1.0 * U); int d = U - L + 1; for (i = 0; i < d; i++) IsNotPrime[i] = 0; // 一开始全是素数. for (i = (L % 2 != 0); i < d; i += 2) IsNotPrime[i] = 1; // 把偶数的直接去掉. for (i = 3; i <= SU; i += 2) { if (i > L && IsNotPrime[i - L]) continue; // IsNotPrime[i - L] == 1说明i不是素数. j = (L / i) * i; // j为i的倍数,且最接近L的数. if (j < L) j += i; if (j == i) j += i; // i为素数,j = i说明j也是素数,所以直接 + i. j = j - L; for (; j < d; j += i) IsNotPrime[j] = 1; // 说明j不是素数(IsNotPrime[j - L] = 1). } if (L <= 1) IsNotPrime[1 - L] = 1; if (L <= 2) IsNotPrime[2 - L] = 0; PrimeNum = 0; for (i = 0; i < d; i++) if (!IsNotPrime[i]) PrimeList[PrimeNum++] = i + L;}
5. Miller_Rabin
简述:很数论的一种方法,需要费马小定理,还要a ^ b的快速幂,还要注意Carmicheal number. 有兴趣可以专门找相关文章看,没兴趣的直接用就哦啦~~
int Modular_Exponent(int a, int b, int MOD){ // a ^ b mod MOD. int temp(1); int aa(a); while (b) { if (b & 1) temp = 1LL * temp * aa % MOD; aa = 1LL * aa * aa % MOD; b >>= 1; } return temp;}// Carmicheal number: 561,41041,825265,321197185bool Miller_Rabin(int n, int time = 20){ // 如果是素数,则返回1,否则返回0. if (n == 1 || (n != 2 && !(n % 2)) || (n != 3 && !(n % 3)) || (n != 5 && !(n % 5)) || (n != 7 && !(n % 7))) return 0; while (time--) { if (Modular_Exponent(((rand() & 0x7fff << 16) + rand() & 0x7fff + rand() & 0x7fff) % (n-1) + 1, n - 1, n) != 1) return 0; } return 1;}
评价:解决了筛法需要连续性判素数的确定,可以在很高概率(多次判断,实际效果很好)判断出素数.
6. 筛法的额外用途:求每个数的最小质因数
简述:原理与筛法相通,只是factor存的是最小质因数,为0当然就是素数啦.
void PrimeFactor(void){ // 求每个数最小的质因数. int i, j; mem(factor, 0); factor[1] = 1; factor[0] = 1; // 0和1不是素数. PrimeNum = 0; for (i = 2; i < maxn; i++) { if (!factor[i]) PrimeList[PrimeNum++] = i; for (j = 0; j < PrimeNum && i * PrimeList[j] < maxn && (PrimeList[j] <= factor[i] || factor[i] == 0); j++) { // 当PrimeList[j] > factor[i] && factor[i] != 0时,那么最小质因数为factor[i]. // PrimeList数组中的素数是递增的, 当i % PrimeList[j] == 0时,就break,理由同上面的线性筛法. factor[i * PrimeList[j]] = PrimeList[j]; if (i % PrimeList[j] == 0) break; } }// cout << PrimeNum << endl;}
评价:该方法使用十分巧妙,可以快速地一个数的质因数个数,强烈推荐~~
7. java大数素数判断
简述:此方法极度猥琐...话说导致09年合肥站regional的一堆杯具...
函数:IsProbablePrime(int certainty)
评价:可以用来判断大数是否为素数。
以上总结了素数的一些常用的判断方法及用途,素数的判断,特别是大素数的判断一直是数论未能突破的地方。
本文仅是抛砖引玉,供大家讨论素数的相关理论~~
- 数论合集
- 数论合集 I
- 【数论基础算法合集】
- ACM 数论知识 合集
- 数据类型转换合集
- Eclipse插件合集
- 冷笑话合集
- Windows命令合集
- 智力题合集
- js问题合集
- session问题合集
- ABAP函数合集
- 经典网站 合集
- Mysql优化合集
- 遗传算法合集
- c# 算法合集
- lucene入门合集
- windows命令合集
- GNUstep简介
- POJ Biorhythms 中国剩余定理概念理解入门
- componentsSeparatedByString
- eclipse @override错误
- 连接mysql出现的问题
- 数论合集
- Linux通过SMTP Server发送邮件
- Ajax/JavaScript脚本大全,JS脚本大全
- 互联网时代的社会语言学:基于SNS的文本数据挖掘
- 今天天气特别好
- UIView详解
- 隐马尔可夫模型
- Node.js 究竟是什么?
- MySQL DBA必备命令(实践中记录下来的)