数论---质数(质数判定,筛质数,找质因数)

1.判定质数 

 暴力做法,判断一个数:O(n)

 优化暴力做法:只判断一个数的1~根号n  O(sqrt(n))

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100005;


bool is_prime(int x)
{
    //1不是质数,也不是合数
    if(x==1) return false;
	//从2开始,1不应该加入
	for (int i = 2;i <= x / i;i++)
	{//i<=n/i 可以防止数据溢出
		if (x % i == 0) return false;
	}
	return true;
}
int main()
{   int q;
	cin >> q;
	while(q--)
	{   
	    int n;
	    cin>>n;
	    if (is_prime(n))
	    {
		    cout << "Yes"<<'\n';
	    }
	    else cout << "No"<<'\n';
    }
    
}

2.分解质因数

基本信息:

质因数素因数质因子)在数论里是指能整除给定正整数质数

概况:算术基本定理:“每一个大于1的整数都能分解成质因数乘积的形式,并且如果把质因数按照由小到大的顺序排列在一起,相同的因数的积写成幂的形式,那么这种分解方法是唯一的。”——又称为“质因数分解定理”

这里有个性质:n中最多只含有一个大于sqrt(n)的因子。证明通过反证法:如果有两个大于sqrt(n)的因子,那么相乘会大于n,矛盾。证毕


于是我们发现最多只有一个大于sqrt(n)的因子,对其进行优化。先考虑比sqrt(n)小的,代码和质数的判定类似
最后如果n还是>1,说明这就是大于sqrt(n)的唯一质因子,输出即可。 

证明

1.i为什么一定是质数

首先i是一个最小因子,假设i不是质数,是一个合数,合数必然存在除了1外的其他最小因子,这样子i就不是一个最小因子了,矛盾,证明结束

2.for循环结束,为什么x是一个最大的质数

设for循环结束的x为t

反证法

当t>1时,for循环如果有一个i是大于t的,那么在遇到这个i之前,x会整除t^s,for循环结束后的x就不会是t了,矛盾。证明结束

#include <iostream>
#include <algorithm>
using namespace std;

void divide(int x)
{
	for (int i = 2;i <= x / i;i++)
	{
		if (x % i == 0)	//此时i一定是一个质数
		{
			int s = 0;	//表示底数的次方(指数)
			while (x % i == 0)
			{//i可以被整除,指数+1
				x /= i;
				s++;
			}
			cout << i << ' ' << s << ' ' << endl;
			//重新循环,x--->x/(i^s)
		}
	}
	//此时还>1就说明他是最大的质因数
	//因为小的质因数已经被上一步给整除掉了
	if (x > 1)		
	{
		cout << x << ' ' << 1 << endl;
	}
	cout << endl;
}

int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int x;
		cin >> x;
		divide(x);
	}

	return 0;
}

3.筛质数

埃氏筛质数法  O(n loglog n)

发现一个质数,就去把  这个质数  作为因子  的所有合数  都标记

void get_primes()
{//找出1~n的所有质数,所以不从1开始,直接从2开始

	for (int i = 2;i <= n;i++)
	{
		if (st[i])	//i是合数 
			continue;
		primes[cnt++] = i;	//i是质数,加入队列
	
		//这里的j=i*i时间会更快,但是会爆int
		for (int j = i + i;j <= n;j += i)
			st[j] = true;
	}

}

线性筛法(欧拉筛法)  O(n)

埃氏筛法的缺陷 :对于一个合数,有可能被筛多次。例如 30 = 2 * 15 = 3 * 10 = 5*6……那么如何确保每个合数只被筛选一次呢?我们只要用它的最小质因子来筛选即可,这便是欧拉筛法。

欧拉筛法:在埃氏筛选法的基础上,只让一个数的最小质因子去染色他,避免重复

两种情况

1.i%primes[j]==0  说明pj是i的最小质因子,同时也是i*pj的最小质因子(染色)
*    由于我们要确保一个合数只会被一个最小质因子染色,所以如果i%pj==0 
    说明下一个比pj大的质数必然不是最小质因子,没有意义应该break

(下一个pj’*i仍然是以i为约数,可知i能被当前pj染色,下一个pj’不是pj‘*i的最小质因子,pj是)
2.i%primes[j]!=0  说明pj不是i的最小质因子,应该继续往后找到i的最小质因子
    但是pj是i*pj的最小质因子,所以也要染色(因为i从2开始,这样子确保了所有以pj为最小质因子的数都会被染色)

问题

1.为什么某个较大质数,例如5,不用让他去染色 5*2,5*3...呢

我们发现,如果一个数t再去染色  t*j  (j=0...t-1)的数,那么t必然不是最小质因子,一个数以t为最小质因子的话,t对应的约数一定≥t ,所以只有一个数被放入质数队列中,当前的i>=t,我们才需要去染色  i*t

例如  25=5*5  49=7*7...

还有2^n次方  3^n   都会被  i   遍历到

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 6;
int n;
int cnt;			//记录当前有几个质数
bool st[N];			//记录是否是质数
int primes[N];		//记录质数

void get_primes()
{//找出1~n的所有质数,所以不从1开始,直接从2开始
	for (int i = 2;i <= n;i++)
	{
		if (!st[i]) //是质数,加入
			primes[cnt++] =i;
		//这里和埃氏筛法不同的地方在于,即使是一个合数,也会被遍历
		//这样子确保了2*2,2*3,2*4...都会被筛选到
		for (int j = 0;primes[j] <= n / i;j++)	//变形得到primes[j]*i<=n  不去筛选大于n的数
		{
			st[i * primes[j]] = true;
			if (i % primes[j] == 0)	//pj是i的最小质因子 
				break;
		}
	}
}

int main()
{
	cin >> n;
	get_primes();
	cout << cnt;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值