首页 > 编程语言 >有关素数的算法

有关素数的算法

时间:2024-02-12 13:13:09浏览次数:21  
标签:prime int 有关 算法 sqrt 因数 素数 ans

一、素性判断

素数,又叫质数,是指一个整数,除了1和本身之外,还有其它的因数(注意:1不是素数)。因此,对于一个整数 \(n\),我们只要检测 \([2, n - 1]\) 能否整除 \(n\)。
整除的定义:\(\exist\) \(a, b, k \in \mathbb{Z}\),使得 \(a = kb\),则称 \(b\) 整除 \(a\),记 \(b\) \(|\) \(a\)。
若 \(d\) 是 \(n\) 的因数,则 \(\frac{n}{d}\) 也是 \(n\) 的因数。由 \(n = d \ast \frac{n}{d}\) 可知,\(min(d, \frac{n}{d}) \leq \sqrt{n}\)。同时,我们知道了一个因数,就能求出另一个因数。所以,我们只要检测 \([2, \sqrt{n}]\) 即可。

  • 判断一个正整数的素性
// 检测n是否是素数
bool if_prime(int n)
{
    // 遍历 [2, sqrt{n}],素数要忽略1和本身
    for (int i = 2; i * i <= n; i++)
    {
        if (n % i == 0)
            return false;
    }
    return n != 1;        // 1 不是素数
}

该算法的核心思想是判断因数。以下两个算法也用到了该思想:

  • 枚举一个正整数的所有因数
// 枚举n的因数
vector<int> divisors(int n)
{
    vector<int> ans;
    // 因数要包含1和本身
    for (int i = 1; i * i <= n; i++)
    {
        if (n % i == 0)
            ans.push_back(i);
        if (i != n / i)             // i*i==n时,要忽略一个i
            ans.push_back(n / i);
    }
    return ans;
}
  • 分解一个正整数
// 将整数n分解成若干个素数,除1和本身
map<int, int> factors(int n)
{
    map<int, int> ans;      // 分解n后,有ans[i]个i
    // n==1,特殊考虑
    if (n == 1)
    {
        ans[n] = 1;
        return ans;
    }
    // 1和本身总是因数,这里忽略
    for (int i = 2; i * i <= n; i++)
    {
        // 可能有若干个i
        while (n % i == 0)
        {
            ans[i]++;       // 分解出一个i
            n /= i;
        }
    }
    if (n != 1)             // n==1,已经分解完了
        ans[n] = 1;
    return ans;
}

二、埃氏筛法

2.1 问题描述

给定正整数 \(n\),求出 \(n\) 以内有多少个素数。

2.2 问题简析

解决该问题需要用到埃氏筛法:先将 \([2, n]\) 内的整数写下来。接下来,开始筛数。每次筛数,保留最小的数 \(m\),即素数,然后筛去 \(m\) 的倍数。经过多轮的筛数,留下的就都是素数了。

2.3 代码

#include <bits/stdc++.h>

using namespace std;

#define MAX 10000000

int n;
vector<int> ans;
bool is_prime[MAX];

int main()
{
	scanf("%d", &n);
	fill(begin(is_prime), end(is_prime), true);
	for (int i = 2; i <= n; i++)
	{
		if (is_prime[i])
		{
			ans.push_back(i);

			// 筛去 i 的倍数
			for (int j = 2 * i; j <= n; j += i)
				is_prime[j] = false;
		}
	}

	printf("%d\n", ans.size());

	return 0;
}

三、区间筛法

3.1 问题描述

给定正整数 \(a\) 和 \(b\),求除 \([a, b)\) 内的素数个数。

3.2 问题简析

由上文可知,\(b\) 的最小质因数不大于 \(\sqrt{b}\)。所以,用 \([2, \sqrt{b})\) 就能筛去 \([a, b)\) 里的数。解释:

\[\begin{split} &\exist~m \in [2, \sqrt{b}),使得~m~|~b。假设~m~是素数,\\ &则遍历到~\sqrt{b}~之前,就能筛去~b。 \end{split} \]

3.3 代码

#include <bits/stdc++.h>

using namespace std;

#define MAX 1000003

typedef long long ll;

ll a, b;
bool is_prime_mini[MAX];      // [2, sqrt(b))
bool is_prime[MAX];           // [a, b)

int main()
{
	scanf("%lld%lld", &a, &b);

	fill(begin(is_prime_mini), end(is_prime_mini), true);
	fill(begin(is_prime), end(is_prime), true);

	// 筛[2, sqrt(b))
	for (int i = 2; (ll)i * i < b; i++)
	{
		if (is_prime_mini[i])
		{	// 筛[2, sqrt(b))
			for (int j = 2 * i; (ll)j * j < b; j += i)
				is_prime_mini[j] = false;

			// 筛[a, b)
			ll j = a / i;                 // 向下取整,可能差一个i
			if (j * i < a)    j += 1;     // 差一个i
			for (j *= i; j < b; j += i)
				is_prime[j - a] = false;  // [a, b) 压缩到 [0, b - a)
		}
	}

	int ans = 0;
	for (ll i = a; i < b; i++)
		if (is_prime[i - a])
			ans++;
	printf("%d\n", ans);

	return 0;
}

该算法有几个注意点:

  • 1、一般区间筛法中 \(a\) 和 \(b\) 都是较大的,所以要用 long long 来存储。
  • 2、区间一般默认左闭右开,所以两个循环判断条件都没有等号。
  • 3、筛完 \([2, \sqrt{b})\) 后,再筛 \([a, b)\),需要注意 j 的取值。 j 应该为 i 的倍数,且 j 应该不小于 \(a\),所以要先比较 j 与 \(a\) 的大小。

标签:prime,int,有关,算法,sqrt,因数,素数,ans
From: https://www.cnblogs.com/hoyd/p/18013782

相关文章

  • 我在代码随想录|写代码| 贪心算法 | 理论基础, 455.分发饼干, 376. 摆动序列,53. 最大
    学习目标:博主介绍:27dCnc专题:数据结构帮助小白快速入门......
  • 在k8S中,Scheduler使用哪两种算法将Pod绑定到worker节点?
    在Kubernetes(k8S)中,Scheduler使用两种主要的算法阶段来决定将Pod绑定到哪个worker节点上:预选算法(Predicates)预选阶段的主要目标是过滤掉不满足调度条件的节点。Scheduler会根据一系列预定义的预选策略对所有可用节点进行筛选。这些策略可能包括但不限于:检查节点上的资源是否......
  • 2024牛客寒假算法基础集训营2 补题
    2024牛客寒假算法基础集训营2补题A.TokitsukazeandBracelet签到模拟参考代码:#include<bits/stdc++.h>usingnamespacestd;#definefffirst#definesssecond#defineebemplace_back#defineall(u)u.begin(),u.end()#defineendl'\n'#definedebug(x)cout......
  • UUID算法:独一无二的标识符解决方案
    引言在分布式系统和大数据环境下,唯一标识符的生成和管理是一项关键任务。UUID(UniversallyUniqueIdentifier)算法应运而生,成为了解决重复数据和标识符冲突的有效工具。本文将探讨UUID算法的优势和劣势,分析其在分布式系统、大数据环境以及其他领域中的应用,同时给出Python完整示例......
  • 文心一言 VS 讯飞星火 VS chatgpt (198)-- 算法导论14.3 6题
    六、用go语言,说明如何来维护一个支持操作MIN-GAP的一些数的动态集Q,使得该操作能给出Q中两个最接近的数之间的差值。例如,Q=(1,5,9,15,18,22),则MIN-GAP返回18-15=3,因为15和18是Q中两个最接近的数。要使得操作INSERT、DELETE、SEARCH和MIN-GAP尽可能高效,并分析它们的运行时间。文心一言,代......
  • 文心一言 VS 讯飞星火 VS chatgpt (197)-- 算法导论14.3 5题
    五、用go语言,对区间树T和一个区间i,请修改有关区间树的过程来支持新的操作INTERVALSEARCH-EXACTLY(T,i),它返回一个指向T中结点x的指针,使得x.int.low==i.low且x.int.high==i.high;或者,如果不包含这样的区间时返回T.nil。所有的操作(包括INTERVAL-SEARCH-EXACTLY)......
  • 代码随想录算法训练营第十六天| 104.二叉树的最大深度 559.n叉树的最大深度 111.二
    104.二叉树的最大深度  题目链接:104.二叉树的最大深度-力扣(LeetCode)n叉树也一样思路:我的普通递归方法classSolution{public:intdepth(TreeNode*node,intd){intl=0,r=0;if(node->left==NULL&&node->right==NULL)returnd;if(node-......
  • A* 算法
    先来想想这么一个题目:在方格图中,有\(M\)个障碍,不能通过这些障碍。给定起点坐标和终点坐标,求出最短路。1.贪心最优搜索和Dijkstra算法贪心最优搜索贪心最优搜索的流程是:从起点出发,在它的邻居结点上选择离终点最近的点。但是这样往往不能取得最优解。其思想为只看终点......
  • 分块与莫队算法
    1.分块分块的思想分块是把一个长度为\(N\)的数列分为\(\sqrt{N}\)个块,每个块的长度为\(\sqrt{N}\)。这样在区间操作的时候,对于每个涉及到的块,如果涉及到半个块,就直接操作;如果涉及到整个块,就整体操作。下面通过例题来了解一下分块。例1洛谷-P3372这道题可以用分块来做......
  • 2024牛客寒假算法基础集训营1
    D.数组成鸡题解观察到\(abs(M)\leq1e9\),容易知道如果绝对值不为\(1\)的数的个数大于\(30\)个的话,显然溢出,不会在答案的范围内再仔细分析性质,如果整个数组中数的种类超过了\(20\)种,那么除了\(0\)之外,最坏的结果就是\(-10,-9...-1,1,2,...10\)这样的情况,他们......