质数和约数

质数

定义

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

相关算法

1.质数的判定——试除法        O(sqrt(n))

概念:

判断每个从2开始到n - 1的数是不是能将x整除,从而来判断x是不是质数。

优化:如果x能够被i整除,那么x也能够被x / i整除。所以我们可以取较小的那部分(x / i)来判断是否为质数。

代码:

#include <iostream>
using namespace std;

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

int main()
{
	int x;cin >> x;
	is_prime(x);
	return 0;
}

2.分解质因数——试除法

概念:

判断每个从2开始到n 的数是不是n的质因子并统计数量。

优化:由于n中最多只包含一个大于sqrt(n)的质因子(如果有两个,两两相乘就会大于n),所以我们可以从2开始遍历sqrt(n),最后剩下的n如果大于1,那么就是大于sqrt(n)的那个质因子了。

代码:

#include <iostream>
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;;
}

int main()
{
	int x;cin >> x;
	divide(x);
	return 0;
}

3.筛质数

1).埃式筛法

概念:

给定一个数n,求2到n的所有质数。

基本步骤:用i从2开始遍历到n,删掉所有i的倍数的数(不包括自己)。

优化:只需删掉质数的所有倍数(把for的步骤放到判断里)。

i这个数是质数,那么前面就没有数能使其删掉。如果i是合数,那么前面就一定有一个数的倍数是他,不然他就是质数了。

这个优化也被称为埃式筛法     O(nloglogn)

代码:

#include <iostream>
using namespace std;

const int N = 1000010;

int primes[N], cnt;
bool st[N];

void get_primes(int n)
{
	for (int i = 2; i <= n; i++) {
		if (!st[i]) {
			primes[cnt++] = i;
			for (int j = i + i; j <= n; j += i) st[j] = true;//优化
		}
		//for (int j = i + i; j <= n; j += i) st[j] = true;//未优化前
	}
}

int main()
{
	int n;
	cin >> n;

	get_primes(n);

	cout << cnt << endl;
	return 0;
}

2). 线性筛法

概念:

核心:n只会被最小质因子筛掉

st[ primes[ j ] * i ] = true;        //n只会被最小质因子筛掉

证明:

1. i % pj == 0

pj一定是i的最小质因子,pj一定是pj * i 的最小质因子

2. i % pj != 0

pj一定小于i的所有质因子,pj也一定是pj*i的最小质因子。

合数一定会被删掉

对于一个合数x,假设pj是x的最小质因子,当i枚举到x/pj时,就会被筛掉。

线性概念:每个数只有一个最小质因子,从而每个数只会被筛一次,所以它是线性的。

线性筛法比埃式筛法要快

代码:

#include <iostream>
using namespace std;

const int N = 1000010;

int primes[N], cnt;
bool st[N];

void get_primes(int n)
{
	for (int i = 2; i <= n; i++) {
		if (!st[i]) primes[cnt++] = i;
		for (int j = 0; primes[j] <= n / i; j++) {
			st[primes[j] * i] = true;
			if (i % primes[j] == 0) break; //primes[j]一定是i的最小质因子
		}
	}
}

int main()
{
	int n;
	cin >> n;

	get_primes(n);

	cout << cnt << endl;
	return 0;
}

约数

定义:

整数a除以整数b(b≠0) 除得的正好是整数而没有余数,我们就说a能被b整除,或b能整除a。a称为b的倍数,b称为a的约数。

例如:4的约数:1,2,4

相关算法:

1.试除法求约数

概念:

从1到n,判断每个数是否为n的约数

优化:d为n的约数,那么d/n也是n的约数。所以我们可以枚举较小的约数,然后再把较大的也添加进来。

代码:

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

vector<int> get_divisors(int n)
{
	vector<int> res;

	for (int i = 1; i <= n / i; i++) {
		if (n % i == 0) {
			res.push_back(i);
			//i为约数,n / i也是约数
			if (i != n / i) res.push_back(n / i);//判断n是否为i的平方
		}
	}
	sort(res.begin(), res.end());
	return res;
}

int main()
{
	int x;cin >> x;
	vector<int> v = get_divisors(x);
	return 0;
}

2.约数个数

概念:

 

 

核心:把n化为质因数相乘。p表示质因数。然后每个xk从0到xk里取值。所以就有总个数

(x1 + 1)(x2 + 1)……(xk + 1)。

代码:

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

typedef long long ll;
const int mod = 1e9 + 7;

int main()
{
	int n;
	cin >> n;

	unordered_map<int, int> primes;
	while (n--)
	{
		int x; cin >> x;

		for (int i = 2; i <= x / i; i++) {
			while (x % i == 0) {
				x /= i;
				primes[i]++;
			}
			if (x > 1) primes[x]++;
		}
	}

	ll res = 1;
	for (auto prime : primes) res = res * (prime.second + 1) % mod;

	cout << res << endl;
	return 0;
}
3.约数之和

概念:

 

 核心:从x1到xk中,只要有一个不同,那么就是不同的两个数。所以我们就可以列出上面的公式。

代码:

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

typedef long long ll;
const int mod = 1e9 + 7;

int main()
{
	int n;
	cin >> n;

	unordered_map<int, int> primes;
	while (n--)
	{
		int x; cin >> x;

		for (int i = 2; i <= x / i; i++) {
			while (x % i == 0) {
				x /= i;
				primes[i]++;
			}
			if (x > 1) primes[x]++;
		}
	}

	ll res = 1;
	for (auto prime : primes) {
		int p = prime.first, a = prime.second;
		ll t = 1;
        //求(p^0 + p^1 + p^2 … + p^xk)
		while (a--) t = (p * t + 1) % mod;//假设a为2,通过计算就可以得到p^2 + p + 1
		res = res * t % mod;
	}

	cout << res << endl;
	return 0;
}

4.欧几里得算法(辗转相除法)

概念:

核心:(a,b)= (b,a % b)——a,b的最大公约数就等于b,a%b的最大公约数

证明:假设d能整除a,也能整除b,那么d就可以整除(ax + by)。利用这个可以正向证明。

如果d能够整除a,b。那么d也能整除b,a % b(a - [a/b] * b)。这里可以把a % b当中a - c * b.

反向证明:假设d能够整除b,a - c * b。那么d也可以整除a - c * b + c * b,d可以整除a。所以(a,b)= (b,a % b)的公约数是一样的。故最大公约数也是一样的。

d

代码return b ? gcd(b, a % b) : a;含义就是不断gcd。直到b等于0。(a,0)的最大公约数就是a(因为0可以整除任何数),此时返回a就可以了。

代码:

#include <iostream>
using namespace std;

//求最大公约数
int gcd(int a, int b)
{
	return b ? gcd(b, a % b) : a;
}

int main()
{
	int n;
	cin >> n;
	while (n--) {
		int a, b;
		cin >> a >> b;
		cout << gcd(a, b) << endl;
	}

	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值