欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【代码超详解】HDU 2138 How many prime numbers? 有多少个质数?(质数埃筛打表 + 判定,31 ms)

程序员文章站 2022-05-20 19:34:41
...

一、题目描述

How many prime numbers

Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 27558 Accepted Submission(s): 9246(2019/11/11)

Problem Description

Give you a lot of positive integers, just to find out how many prime numbers there are.
给一堆正整数,找出其中有多少个质数。

Input

There are a lot of cases. In each case, there is an integer N representing the number of integers to find. Each integer won’t exceed 32-bit signed integer, and each of them won’t be less than 2.
有很多样例。每个样例中,有N个整数要判定。每个整数不超过32位有符号整数的范围,且不小于2。

Output

For each case, print the number of prime numbers you have found out.
对每个样例,输出所含质数的个数。

Sample Input

3
2 3 4

Sample Output

2

Author

wangye

Source

HDU 2007-11 Programming Contest_WarmUp

Recommend

威士忌 | We have carefully selected several similar problems for you: 2136 1431 2132 2139 2134

二、算法分析说明与代码编写指导

质数的埃氏筛法

1、用途:快速选出一定数量的质数。
2、算法内容:
埃拉托斯特尼(Eratosthenes)筛法(埃氏筛法) 要筛选出不大于n的质数,排除sqrt(n)以内全部质数的倍数即可。
(Eratosthenes(前276—前194),古希腊数学家)
设需要生成前若干个质数,prime数组用于存储已经验证为质数的数。
设a为当前正在验证是否为质数的正整数,t = sqrt(a)为需要试除的范围。
用已知的质数去除a,如果能整除,a就有除了1和a本身以外的因数,终止当前的判断,准备判断a+1。
为什么只用试除到t = sqrt(a)呢,因为因数是一对一对的,例如:
100 的因数有:1 2 4 5 10 20 25 50 100。我们发现,1 * 100 = 2 * 50 = 4 * 25 = 5 * 20 = 10 * 10。
如果验证到sqrt(a)后,还没有发现第3个a的因数,自然也不会再有了。
换句话说,还是以100为例子,如果找出了因数2,就可以计算出因数50。如果找到了因数4,就可以计算出因数25。
如果验证了某个数d不是a的因子,那么d的倍数也不是a的因子。
比如2不是 a 的因子,那么4 6 8 …… 都不用验证了。比如3不是 a 的因子,那么6 9 12 …… 都不用验证了。
如果要验证是否为a的因子的数d是质数,那么在之前验证过的数及其倍数都不是这个质数d的因数,也就是说d还没有用于验证过。
可见,用质数试除是不能避免的。对于合数来说,如果已知它的一个质因数不是a的因子,那么这个合数也不用再验证是否为a的因子。意即,要用这个合数去除a,就相当于用a的每个质因数去除a。如果某个质因数不能整除a,那么这个合数也不能整除a。
从几个角度都啰嗦完了,相信总有一种角度适合你的理解。

质数的判定

1、用途:迅速判定一个数是否为质数。
2、算法内容:
用埃氏筛法打质数表,打表结果保存在prime数组中。然后判断要验证是否为质数的整数x是否在表的范围内。如果是,在prime数组内二分搜索x,搜到就是质数,搜不到就不是。如果x超出了表的范围,就验证x是不是已经打表的质数的倍数。如果都不是,x就是质数。

3、代码实现:(含埃氏筛法打表,有时候int / unsigned的范围不够用,此时需要自己换类型)
要求:采用本参考代码时,需要先包含头文件algorithm和cmath(有的编译器的algorithm不含sqrt),且声明一个prime数组用于存储已经验证的质数,以及using namespace std; 传入的参数是需要生成的质数的数量。前2个质数2、3必须要先写入prime数组。额外建立一个变量_PTy,其类型与prime数组的类型相同,以便decltype关键字自动侦测类型。此外,x的类型不能大于prime数组的类型。
4、注意事项:
x不能超过最大已打表质数的平方,否则会返回错误的结果,从而WA。
5、时间复杂度:
设要判定的数为n。当n在质数打表范围时,二分搜索并返回搜索结果,时间复杂度为O(log n)(也可以写成O(ln n))。
当n不在质数打表范围sqrt(n)时,最坏情况是需要用π(sqrt(n))个质数去验证(π(x)是不大于整数x的质数的数量)。所以判定一个数n是否为质数的时间复杂度不超过 【代码超详解】HDU 2138 How many prime numbers? 有多少个质数?(质数埃筛打表 + 判定,31 ms)

三、AC 代码(31 ms)

【代码超详解】HDU 2138 How many prime numbers? 有多少个质数?(质数埃筛打表 + 判定,31 ms)

#include<cstdio>
#include<algorithm>
#pragma warning(disable:4996)
using namespace std;
unsigned prime[4792] = { 2,3 }, _PTy, MaxPrime, * prime_end = prime + sizeof(prime) / sizeof(prime[0]);
inline void genprime() {
	decltype(_PTy) a = 4, t; bool flag;
	for (auto i = prime + 2; i < prime_end;) {
		t = sqrt(a); flag = true;
		for (auto j = prime; *j <= t; ++j)
			if (a % *j == 0) { flag = false; break; }
		if (flag) { *i = a, ++i; }
		++a;
	}
	MaxPrime = *(prime_end - 1);
}
inline bool isprime(const decltype(_PTy)& x) {
	if (x <= MaxPrime)return binary_search(prime, prime_end, x);
	else {
		decltype(_PTy) t = min((decltype(_PTy))sqrt(x), MaxPrime);
		for (auto j = prime; *j <= t; ++j)
			if (x % *j == 0)return false;
		return true;
	}
}
int main() {
	genprime(); unsigned N, X, C;
	for (;;) {
		if (scanf("%u", &N) == EOF)return 0;
		++N, C = 0;
		while (--N) {
			scanf("%u", &X);
			if (isprime(X))++C;
		}
		printf("%u\n", C);
	}
}
相关标签: 详解