数论 质数

在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数,也叫做素数。

质数的判定

试除法

原理

  1. 判断是否大于1
  2. 判断是否只包含1和本身两个约数

时间复杂度

O(sqrt(n)),时间复杂度一定是O(sqrt(n))

代码

#include<iostream>
#include<algorithm>
using namespace std;
bool is_prime(int n){
    if(n<2) 
        return false;
    for(int i=2;i<=n/i;i++)//i<=sqrt(n),每次都会执行sqrt函数,sqrt比较慢;
                           //i*i<=n,当n接近int的最大值时,i^2<n,(i+1)^2可能会溢出,
                           //存在溢出风险,溢出后变为一个负数,影响最终的判断
        if(n%i==0)
            return false;
    return true;
}

分解质因数

试除法

原理

       求x的质因子,从2挨个枚举所有数i,当x整除i,则将x一直除到无法整除i为止,此时i为x的一个质因子,然后令i自增,一直重复操作,直到x变为1。

       因为所有合数都是由比合数小的质数构成,而在操作过程中都是将小的数一直除,直到无法整除,这样就可以保证,在除的一定是一个质数。

       而x中最多只包含一个大于根号x的质因子。所以只用枚举完所有小于等于根号x的质因子,若是枚举完发现x不为1,则证明此时的x本身为原来x的一个质因子。

时间复杂度

O(sqrt(n)),时间复杂度最好是 O(logn),最坏是 O(sqrt(n))

代码

#include<iostream>
#include<algorithm>
using namespace std;
void divide(int n){
    for(int i=2;i<=n/i;i++)
        if(n%i==0){//i一定是质数
            int s=0;//质因数的指数
            while(n%i==0){//拆掉n,查看其中有多少个i
                n/=i;
                s++;
            }
            printf("%d %d\n",i,s);
        }
    if(n>1)//如果n没有拆解完,直接输出n
        printf("%d %d",n,1);
    puts("");
    return;
}

例题——分解质因数

#include<iostream>
#include<algorithm>
using namespace std;
void divide(int n) {
	for (int i = 2;i <= n / i;i++) {
		if (n % i == 0) {
			int s = 0;
			while (n % i == 0) {
				n /= i;
				s++;
			}
			cout << i << " " << s << endl;
		}
	}
	if (n > 1)
		cout << n << " " << "1" << endl;
	cout << endl;
	return;
}
int main() {
	int n;
	cin >> n;
	while (n--) {
		int x;
		cin >> x;
		divide(x);
	}
	return 0;
}

筛选质数的方法

定义法

原理

质数:指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数

输出0——100中所有的质数

代码

bool is_prime(long long x){
    if(x<=1)
        return false;
    if(x==2)
        return true;
    if(x%2==0)
        return false;
    for(long long i=3;i<=x/i;i+=2)
        if(x%i==0)
            return false;
    return true;
}
#include<stdio.h>
#include<math.h>
int main()
{
	int n = 0,i, j;
	printf("%d\n", 2);
	for (i = 3;i<=100;i+=2)
	{
		for (j = 2;j <=sqrt(i);j++)
		{
			if (i % j == 0)
				n++;
		}
		if (n == 0)
			printf("%d\n", i);
		n = 0;
	}
	return 0;
}

欧拉筛

原理

一个合数是由一堆质数相乘得出的

创建两个数组,一个用来存储质数,另一个用来判断状态(bool类型的数组),合数状态为1,质数状态为0,利用下标来表示数值。若输出[a,b]之间的质数,也需要从2开始筛,不然就会漏掉其中某个数,只是在输出的时候加条件限定。

算法讲解

是埃氏筛法的优化,与埃氏筛一样用一个数i去乘当前所有已知的质数,将所得的结果标记为一个合数。但是欧拉筛会在筛完i的最小质因数后停止数i的筛选合数过程。这个操作是防止重复筛,原理如下。

现在假设要根据i来筛合数,a为i的最小质因子,i=a*m;

若在筛去i*a后仍然不停止,又要用a的下一个质数b来筛合数。那此时筛去的是i*b,即(a*m)*b

那i之后不断进行自增筛合数,由于自增量为1,所以必有一次i的值会达到b*m

那此时用b*m筛合数时,则必会筛到(b*m)*a。

此时重筛。所以在筛去最小质因子后应该停止当前i的筛合数操作,换下一个i进行。

n只会被最小质因子筛掉

  1. i%zhishu[k]==0

说明 zhishu[k] 一定是 i 的最小质因子,zhishu[k] 一定是 zhishu[k]*i 的最小质因子

  1. i%zhishu[k]!=0

因为是从小到大枚举的所有质数,并且未枚举到 i 的任何一个质因子,说明 zhishu[k] 一定小于 i 的所有质因子,zhishu[k] 也一定是 zhishu[k]*i 的最小质因子

所有,无论什么情况,zhishu[k] 一定是 zhishu[k]*i 的最小质因子,所以任何一个合数都是被其最小质因子筛去。对于一个合数 x,假设 zhishu[k] 是 x 的最小质因子,当 i 枚举到 x/zhishu[k] 时,会被删去。每个数只有一个最小质因子,所以每个数只会被筛一次。

代码

#include<stdio.h>
int main()
{
	int zhishu[100] = { 0 };
	_Bool zhuangtai[105] = { 0 };
	int i, j = 0,k,l=0;
	for (i = 2;i <= 100;i++)
	{
		if (zhuangtai[i] == 0)
		{
			zhishu[j] = i;
			j++;
			
		}
        for (k = 0;k < j && i * zhishu[k] <105;k++)//k<j是没有必要的
			{
				l = i * zhishu[k];
				zhuangtai[l] = 1;
				if (i % zhishu[k] == 0)   //条件——避免重复操作,提高效率,只需要*到i的最小质因子即可
					break;
			}
       }
	for (i = 2;i <= 100;i++)
	{
		if (zhuangtai[i] == 0)
			printf("%d\n", i);
	}
	return 0;
}

为什么k<j是没有必要的?

因为如果 i 是合数的话,zhishu[k] 枚举到 i 的最小质因子时,一定会停下来;当 i 是质数时,zhishu[k]=i 时会停下来。无论如何会在 k>=j 之前停下来,所以不需要加上 k<j 。

埃氏筛

原理

素数的倍数肯定不是素数,使用质数筛合数,将质数依次乘以常数,并标记。

要得到n以内的全部素数,必须把<=根号n的所有素数的倍数剔除,剩下的是素数。

1~n 中有 n/logn 个质数。

时间复杂度

O(nloglogn)

代码

#include<stdio.h>
int main()
{
	int zhuangtai[105] = { 0 };
	int i,j;
	for (i = 2;i <=100;i++)
	{
		if (zhuangtai[i] == 0)
		{
			for (j = 2;i * j <= 100;j++)
				zhuangtai[i * j] = 1;
		}
	}
	for (i = 2;i <= 100;i++) //遍历次数太多,遍历n次,将筛出来的素数放入数组中,遍历的次数会减少
	{
		if (zhuangtai[i] == 0)
			printf("%d\n", i);
	}
	return 0;
}

优化

#include<stdio.h>
int main()
{
	int zhuangtai[105] = { 0 };
	int sushu[100];
	int i,j,k=0;
	for (i = 2;i <= 100;i++)
	{
		if (zhuangtai[i] == 0)
		{
			sushu[k] = i;
			k++;
			for (j = 2;i * j <= 100;j++)
				zhuangtai[i * j] = 1;
		}
	}
	for (i = 0;i <k;i++)
	   printf("%d\n", sushu[i]);
	return 0;
}

区间筛

原理

因为b以内合数的最小质因数一定不超过sqrt(b),如果有sqrt(b)以内的素数表的话,就可以把筛选法用在[a,b)上了,先分别做好[2,sqrt(b))的表和[a,b)的表,然后从[2,sqrt(b))的表中筛得素数的同时,也将其倍数从[a,b)的表中划去,最后剩下的就是区间[a,b)内的素数了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

何hyy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值