【C++】 【判定质数】 【分解质因数】【筛质数(编制质数表)】

数学知识之质数

质数的定义

质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。”
比1大但不是素数的数称为合数。1和0既非素数也非合数。素数在数论中有着很重要的作用。
质数的分布规律是以36N(N+1)为单位,随着N的增大,素数的个数以波浪形式渐渐增多。

质数的性质

质数具有许多独特的性质:
(1)质数p的约数只有两个:1和p。
(2)初等数学基本定理:任一大于1的自然数,要么本身是质数,要么可以分解为几个质数之积,且这种分解是唯一的。
(3)质数的个数是无限的。
(4)质数的个数公式Π(n)是不减函数。
(5)若n为正整数,n2在(n+1)2到 之间至少有一个质数。
(6)若n为大于或等于2的正整数,在n到n!之间至少有一个质数。
(7)所有大于10的质数中,个位数只有1,3,7,9。


题目来源ACWing866题

一、判定质数
  1. 查表法
    直接查看目标数在不在质数表内。质数表的编制在后面筛质数中有介绍。
  2. 试除法
    如果将小于num的数意义枚举,则时间复杂度为O(n),显然这是可以优化的。
    性质:如果d能整除n的话,n%d = 0,那么n/d也能整除n,即n%(n/d)=0。
    由以上性质,所以我们可以只枚举更小的因子来判定n是否为质数。
    此时算法的时间复杂度为O(sqar(n))
    代码如下:
bool is_prime(int num)
{
    if(num < 2 ) return false;
    int i=2;
    while(i <= num/i){
        if(num%i == 0) return false; //如果n整除i,则n不为质数
        else i++;
    }
    return true;
}

分析上面代码,在循环枚举中i是逐1增加的,显然这里面由很多的冗余,下面是经优化过后的算的,循环中i每次加6,显然时间复杂度要更小。

bool isPrime( int num )
{
	if(num < 2 ) return false;
	//两个较小数另外处理
	else if(num ==2|| num==3 ) return true ;
	//不在6的倍数两侧的一定不是质数
	if(num %6!= 1&&num %6!= 5)  return false;
	//在6的倍数两侧的也可能不是质数
	for(int i= 5;i <= num/i; i+=6 )
		if(num %i== 0||num %(i+ 2)==0 )
		    return false;
	//排除所有,剩余的是质数
	return true ;
}

题目来源ACWing867题

二、分解质因数

每个合数都可以写成几个质数相乘的形式,其中每个质数都是这个合数的因数,把一个合数用质因数相乘的形式表示出来,叫做分解质因数。如30=2×3×5 。分解质因数只针对合数。
性质:质数除2以外全是奇数。合数最多只有1个因子大于sqrt(n),

void divideToPrime(int n){
	int s = 0; //记录某个质因子的次数
	if(n%2 == 0){
        //计算因子2的次数s
        while(n%2 == 0){
            n /= 2;
            s++;
        }
        cout << "2 " << s << endl; //输出质因子2及其次数
    }
    for(int i = 3; i <= n/i; i +=2){
        s = 0;
        if(n%i == 0){
            //计算质因数次数s
            while(n%i == 0){
                n /= i;
                s++;
            }
            cout << i << " " << s << endl; //输出质因子及其次数
        }
    }
    //没有除尽,剩下的为一个大于sqrt(n)的质因子
    if(n > 1) cout << n << " 1" << endl;
    cout << endl;
}

题目来源ACWing868题

二、筛质数(编制质数表)

埃氏筛法求素数个数及构造素数表。
首先,列出从2开始的所有自然数,构造一个序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取序列的第一个数2,判断它是否为素数,如果是素数,然后用2把序列为2的倍数筛掉:
3, 4 , 5, 6 , 7, 8 , 9, 10 , 11, 12 , 13, 14 , 15, 16 , 17, 18 , 19, 20 , …
取新下一个数3,判断它是否为素数,它一定是素数,然后用3把序列的3的倍数筛掉:
5, 7, 9 , 11, 13, 15 , 17, 19,…
另外,每次取到的下一个筛数一定是素数,因为合数肯定为前面素数的倍数。
不断筛下去,就可以得到所有的素数。
线性筛法(欧拉筛法)
性质:每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)
优越性:
1、合数一定被干掉了。
2、每个数都没有被重复地删掉。

const int N = 1000010;
int prime[N],num; //prime[]用来存放素数,num存放素数数量
bool p[N]; // p[i]表示i是否不是素数,初始值为false
void get_primes1(int n){ //朴素筛法
    for(int i = 2;i <= n; i++)
    {
        if(!p[i]) {
            prime[num++] = i;
        }
        //后面的数都筛了一遍
        for(int j = i+i;j <= n; j += i )
            p[j] = true;
    }
    cout << num << endl ;
}
void get_primes2(int n){ //埃式筛法
    for(int i = 2;i <= n; i++)
    {
        if(!p[i]) {
            prime[num++] = i;
            //把素数的倍数都筛了,所有的合数均为素数的倍数
            for(int j = i+i;j <= n; j += i )
                p[j] = true;
        }
    }
    cout << num << endl;
}
void get_primes3(int n){ //线性筛法
    //算法核心:x仅会被其最小质因子筛去
    for(int i = 2;i <= n; i++)
    {
        if(!p[i])
            prime[num++] = i;
            
        for(int j = 0;prime[j] <= n/i; j ++ )
        {
            //对于任意一个合数x,假设pj为x最小质因子,当i<x/pj时,一定会被筛掉
            p[prime[j] * i] = true;
            if(i % prime[j] == 0) break;
            /*
            1.i%pj == 0, pj定为i最小质因子,pj也定为pj*i最小质因子
            2.i%pj != 0, pj定小于i的所有质因子,所以pj也为pj*i最小质因子
            */
        }    
    }
    cout << num << endl;
}

总结

质数相关的算法都很简洁也不难,但是都可以优化,优化的思路思想很值得学习参考!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江山酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值