数论学习笔记 - 质数那些事儿

定义

若一个正整数无法被除了 1 和它自身之外的任何自然数整除,则称该数为质数(或素数),否则该正整数为合数。

在整个自然数集合中,质数的数量不多,分布比较稀疏,对于一个足够大的整数 N,不超过 N 的质数大约有 N/lnN 个,即每 lnN 个数中大约有 1 个质数。

质数的判定

我们最常用的方法是“试除法”

简单来说就是,扫描 2~\sqrt{N} 之间的所有整数,依次检查它们能否整除 N,若都不能整除,则 N 是质数,否则 N 是合数。试除法的时间复杂度为 O(\sqrt{N})。当然,我们需要特判 0 和1 这两个整数,它们既不是质数,也不是合数。

代码示例

bool is_prime(int n){
    if(n<2) return false;
    for(int i=2;i<=sqrt(n);i++){
        if(n%i==0) return false;
    }
    return true;
}

补充:Miller-Robbin算法 

算法是基于费马小定理(format),二次探测定理(x*x % p == 1 ,若P为素数,则x的解只能是x = 1或者x = p - 1)加上迭代乘法判断的Miller算法。适用于测试单个素数,出错概率比计算机本身出错的概率还要低。

这里借用别人的总结(就是我自己懒)

费马小定理:

如果p是一个素数,且0<a<p,则a^(p-1)%p=1。

利用费马小定理,对于给定的整数n,可以设计素数判定算法,通过 计算d=a^(n-1)%n来判断n的素性,当d!=1时,n肯定不是素数,当d=1时,n   很可能是素数。

二次探测定理:

如果p是一个素数,且0<x<p,则方程x^2%p=1的解为: x=1或x=p-1。
利用二次探测定理,可以再利用费马小定理计算a^(n-1)%n的过程中增加对整数n的二次探测,一旦发现违背二次探测条件,即得出n不是素数的结论。

如果n是素数,则(n-1)必是偶数,因此可令(n-1)=m*(2^q),其中m是正奇数( 若n是偶数,则上面的m*(2^q)一定可以分解成一个正奇数乘以2的k次方的形式 ),q是非负整数,考察下面的测试:

序列:a^m%n; a^(2m)%n; a^(4m)%n; …… ;a^(m*2^q)%n

把上述测试序列叫做Miller测试,关于Miller测试,有下面的定理:

定理:若n是素数,a是小于n的正整数,则n对以a为基的Miller测试,结果为真。

Miller测试进行k次,将合数当成素数处理的错误概率最多不会超过4^(-k)。

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<time.h>
using namespace std;

int n;


int witness(int a, int n){//随机生成的a,来检测n的素性 
	int ans=1;
	int t=n-1;//这里需要注意,你如果没有改变乘方的次数的话,最后的判断就是(ans == a) ? 0 : 1;
	// 并且还要另外开辟空间来存储开始的a,比较麻烦,所以就这样了; 
	int x;
	while(t){
		if(t&1) ans = (long long int)ans * a % n;
		x=a;//从这里开始就是迭代乘法,验证二次验证定理 
		a=(long long)a*a%n;//这里就相当于 x*x % m = 1 
		if(a==1&&x!=1&&x!=(n - 1)) return 1;// 这里需要注意,返回一的话就说明,追踪过程中,出现了不是素数的依据.
		t>>=1;
	}
	return (ans==1)?0:1;
}

int MillerRobin(int n, int s){ // 一般s取50就可以避免所有的偶然性了.
	if(n==2) return 1;
	if(n<2||!(n&1)) return 0;
	int a;
	for(int i=0;i<s;i++){
		a=(long long)rand()*(n-2)/RAND_MAX + 1; //这样生成的随机数就是真正的随机数了 
		if(witness(a, n)) return 0;
	}
	return 1;
}

int main(){
	cin>>n;
	if(MillerRobin(n,50)) cout<<n<<" is a prime!"<<endl;
	else cout<<n<<" is not a prime!"<<endl;
	return 0;
}

质数的筛选

给定一个整数 N,求出 1~N 之间的所有质数,称为质数的筛选问题。

Eratosthenes 筛法

这个筛法基于这样的想法:任意整数 x 的倍数 2x,3x,... 都不是质数。

我们可以从 2 开始,由小到大扫描每个数 x,把它的倍数标记为合数。当扫描到一个数时,若它尚未被标记,则它不能被 2~x-1 之间的任何数整除,该数就是质数。

另外,我们可以发现,小于 x^2 的 x 的倍数在扫描更小的数时就已经被标记过了。因此,我们可以对 Eratosthenes 筛法进行优化,对于每个数 x,我们只需要从 x^2 开始,到 N/x * x 之间所有的数标记为合数即可。

代码示例

void prime(int n){
    memset(vis,0,sizeof(vis));
    for(int i=2;i<=n;i++){
        if(vis[i]) continue;
        cout<<i<<endl;
        for(int j=i;j<=n/i;j++) v[i*j]=1;
    }
}

这个筛法效率已经非常接近于线性O(NloglogN),是比赛中比较常用的质数筛法。

线性筛法

Eratosthenes 筛法即使在优化后(从 x^2 开始),仍然会重复标记合数。

线性筛法通过“从大到小累积质因子”的方式标记每个合数,设数组 v 记录每个数的最小质因子,我们按照以下步骤维护 v。

  1. 依次考虑 2~N 之间的每一个数 i
  2. 若 v[i]=i,说明 i 是质数,把它保存下来
  3. 扫描不大于 v[i] 的每个质数 p,令 v[i*p]=p。也就是在 i 的基础上累积一个质因子 p。因为 p≤ v[i],所以 p 就是合数 i*p 的最小质因子

每个合数 i*p 只会被它的最小质因子 p 筛一次,时间复杂度为 O(N)。

void primes(int n){
    memset(v,0,sizeof(v));
    m=0;
    for(int i=2;i<=n;i++){
        if(v[i]==0){
            v[i]=i;
            prime[++m]=i;
        }
        for(int j=1;j<=m;j++){
            if(prime[j]>v[i]||prime[j]>n/i) break;
            v[i*prime[j]]=prime[j];
        }
    }
    for(int i=1;i<=m;i++) cout<<prime[i]<<endl;
}

质因数分解

算术基本定理

任何一个大于 1 的正整数都能唯一分解为有限个质数的乘积

N=p1^{c1}p2^{c2}p3^{c3}...pm^{cm}

其中 ci 都是正整数,pi 都是质数,且满足 p1<p2<...<pm

试除法

结合质数判定的“试除法”和质数筛选的“Eratosthenes 筛法”,我们可以扫描 2~\sqrt{N} 的每个数 d,若 d 能整除 N,则从 N 中除掉所有的因子 d,同时累计除去的 d 的个数。

因为一个合数的因子一定在扫描到这个合数之前就从 N 中被除掉了,所以在上述过程中能整除 N 的一定是质数。最终就得到了质因数分解的结果,易知时间复杂度为 O(\sqrt{N})。

特别地,若 N 没有被任何 2~\sqrt{N} 的数整除,则 N 是质数,无需分解。

void divide(int n){
    m=0;
    for(int i=2;i<=sqrt(n);i++){
        if(n%i==0){
            p[++m]=i;
            c[m]=0;
            while(n%i==0){
                n/=i;
                c[m]++;
            }
        }
    }
    if(n>1){
        p[++m]=n;
        c[m]=1;
    }
    for(int i=1;i<=m;i++) cout<<p[i]<<"^"<<c[i]<<endl;
}

素数的相关定理

威尔逊定理

若 p 为素数,则(p-1)!\equiv-1(mod p) (其中n! 表示n阶乘)

同时,威尔逊定理的逆定理也成立。

既然如此,则(p-1)! + 1 一定是 p 的倍数,所以再利用 sin 函数的特点,就可以构造出一个素数分布的函数曲线 f(n):f(n)=sin(Π*((n-1)!+1)/n)。这个函数值为 0 的点都是素数所在的点。

费马定理

若 p 为素数,a为正整数,且 a 和 p 互质,则:  a^{p-1}\equiv 1(mod p) 

其实这是一种特殊形式,一般情况下,若 p 为素数,则:a^{p}\equiv a(mod p),这就是费马小定理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值