素数

#一.素数的一些性质:

  1. 素数的个数无限多(不存在最大的素数)

  2. 存在任意长的一段连续数,其中的所有数都是合数(相邻素数之间的间隔任意大)

  3. 所有大于2的素数都可以唯一地表示成两个平方数之差。

(证明:大于2的素数都是奇数。假设这个数是2n+1。由于 (n+1)2 = n2+2n+1,(n+1)2和n2就是我们要找的两个平方数。下面证明这个方案是唯一的。如果素数p能表示成a2-b2,则p=a2-b2=(a+b)(a-b)。由于p是素数,那么只可能a+b=p且a-b=1,这给出了a和b的唯一解。)

  1. 当n为大于2的整数时,2n+1和2n-1两个数中,如果其中一个数是素数,那么另一个数一定是合数。

  2. 如果p是素数,a是小于p的正整数,那么a(p-1) mod p = 1。(费马小定理)

#二.单个判断素数
大于等于5的质数一定和6的倍数相邻

#include <iostream>
#include <math.h>
using namespace std;
int isPrime(int n)
{	//返回1表示判断为质数,0为非质数,在此没有进行输入异常检测
	float n_sqrt;
	if(n==2 || n==3) return 1;
	if(n%6!=1 && n%6!=5) return 0;
	n_sqrt=floor(sqrt((float)n));
	for(int i=5;i<=n_sqrt;i+=6)
	{
	    if(n%(i)==0 | n%(i+2)==0) return 0;
	}
        return 1;
} 
int main()
{
	int flag;
	flag=isPrime(37);
	cout<<flag<<endl;
	return 0;
}

#三.筛选素数
欧拉筛法

int prime[MAXN];
bool vis[MAXN];
int cnt=0;
void Euler_peime(int n)
{
	for(int i=2;i<=n;++i)
	{
		if(!vis[i])
		{prime[cnt++]=i;vis[i]=true;}//vis[i]置为true或不置true都可以
		for(int j=0;j<cnt;++j)
		{
			if(i*prime[j]>n)//判断是否越界
				break;
			vis[i*prime[j]]=true;//筛数
			if(i%prime[j]==0)//时间复杂度为O(n)的关键!
				break;
		}
	}
}

用埃式筛法的同时,同一个数字也许会被筛选多次,比如6先被2筛选一次,再被3筛选一次,这样就浪费了很多不必要的时间,而欧拉筛法通过if(i%prime[j]==0)break;这一步就避免了重复筛选的发生,我们举个例子,比如,2先筛选了4,然后进行下一个循环,3筛选6和9,当我们执行到4的时候,可以发现,当i==4时,第一次运行到if(i%prime[j]==0)这一步的时候就直接break;掉了,这也就是说,当我们的合数进入循环时,其实它已经被之前的数筛选过了,所以当合数进入内层循环时,内层循环只执行了一次,从而减少了时间复杂度。
比如:i=k x prime[j]; 我们如果不跳出循环的话,继续计算prime[j+1]---->i x prime[j+1]=k x prime[j] x prime[j+1];k x prime[j+1]会与i相等,于是式子就变成了i*prime[j],会被第二次筛到,所以加上这个判断可以大大缩小复杂度。

if(i%prime[j]==0)//时间复杂度为O(n)的关键!
                break;

#四.大素数判定(Miller-Rabin算法)
#####1.费马小定理
ap ≡ a (mod p)–>p为质数 必要不充分
两边同除a得:
a(p-1) ≡ 1 (mod p)
#####2.二次探测定理
如果(p为奇素数)的话,则有x2 ≡ 1 (mod p);
该方程有两组解:
(1)x ≡ 1 (mod p)
(2)x ≡ p-1 (mod p)
#####3.算法步骤
(1)先使用费马小定理 if(a(n-1)≡ 1 (mod n) ) 成立的话,就说明p为素数;
(2)步骤1不成立的话,使用二次探测

while(n-1 为偶数){
  u = (n-1)/2;
  套用二次探测
  a^u ≡ 1 (mod n)?
  a^u ≡ n-1 (mod n)?
}

(3)换用a,继续算法,一般当n<264时,a一般选用前7个素数(2, 3, 5, 7, 11, 13和17)进行测试,所有不超过341 550 071 728 320的数都是正确的。
一般我们直接获取随机数就可以。
#####4.代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
#include<map>
#include<set>
#define MOD n
#define LL long long
#define UP 5
using namespace std;
int m;
LL  n;
LL qsc(LL a,LL b,LL mod){
  LL sum = 0;
  while(b){
    if(b&1)sum=(sum+a)%mod;
    a=(a+a)%mod;
    b>>=1;
  }return sum%mod;
}
LL pow(LL a,LL b,LL mod){
  LL res = 1;
  while(b){
    if(b&1)res=qsc(res,a,mod);
    a=qsc(a,a,mod);b>>=1;
  }return res%mod;
}
bool MR(){
  if(n==2)return true;
  if( (!(n&1)) || n<2 )return false;
  LL u=n-1,a,x,y;
  while(!(u&1))u>>=1;
  for(int i=0;i<1;++i){
    a=rand()%(n-2)+2;
    x=pow(a,u,n);int tmp=u;
    while(u<n){
      y=pow(x,2,n);
      if(y==1&&x!=1&&x!=n-1)return false;
      x=y;u*=2;
    }u=tmp;
    if(x!=1)return false;
  }return true;
}
int main(){
  srand(time(NULL));
  scanf("%d",&m);
  for(int i=1;i<=m;++i){
    scanf("%lld",&n);
    if(MR())printf("Yes\n");
    else printf("No\n");
  }return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值