【题目】求n以内的素数个数
最近在leetCode上刷提,还是满锻炼人的,为以后面试打基础吧。不多说下面开始。
问题:求[2,n]之间的素数的个数。
来源:leetCode OJ
提示:
Let's start with a isPrime function. To determine if a number is prime, we need to check if it is not divisible by any number less than n. The runtime complexity of isPrime function would be O(n) and hence counting the total prime numbers up to n would be O(n2). Could we do better?
先让我们写一个函数isPrime用于判断给定的数是否是素数,为了判别总一个数是否是素数,我们需要依次检查比这个数小的数能否整除n。那么函数isPrime的时间复杂度就将是O(n),因此总体的时间复杂度就会达到O(n2),我们能做的更好吗?
第一次尝试
预备知识
① 什么是素数:素数(又叫质数),与之相反的是合数。如果只考虑非负数范围内,一个大于1的数,它只能被 1 和 他本身整除。这样的数就是素数。
② 0 和 1既不是质数也不是合数。
③ 素数定理:如果一个数x是素数,那么在整数范围[2,√x ]之间,找不到任何能整除x 的整数。因此,我们不必对 [2, n) 的所有整数去尝试,而只需要对 [2,√x]之间的数尝试整除就OK了,节约了时间。
#include<iostream>
#include<cassert>
#include<time.h>
#include<cmath>
using namespace std; /**
作用:判断一个数是否是素数
参数x:待判断的素
返回:是素数返回true,否则返回false
*/
bool isPrime(int x)
{
for(int i=;i<=int(sqrt(x));++i)
{
if(x%i==) return false;
}
return true;
} /**
作用:统计[2,n]之间的素数的个数
参数:n
返回:素数的个数
*/
int countPrimes(int n) {
int count=; if(n<) return ; for(int i=;i<=n;++i)
{
if(isPrime(i)) //如果 i为素数
{
++count;
}
}
return count; } int main()
{ clock_t start = clock();
int total = countPrimes();
clock_t end = clock(); cout<<"耗时:"<<(double(end-start))/CLOCKS_PER_SEC*<<"毫秒"<<endl; return ;
} /**********测试数据**************************
n=200000 81毫秒
n=700000 385毫秒
*/
这是很多人第一感觉写出来的solution,它最大问题在于使用了sqrt函数。第一:sqrt是用来处理浮点数的,而浮点数的计算速度远远慢于integer。第二,函数调用也会造成时间的浪费。第三:浮点数的存储误差可能引出致命错误,如 sqrt(9) 可能等于 2.9999999 ,那么 int(sqrt(9)) 就等于2 而不是3。
但是我从来都不是这样写的,这个让我印象很深,在我的编程启蒙书 《C prime Plus》书中,它教我使用 C/C++内置于语言的运算符乘 * ,而不是sqrt()函数。
第二次尝试
#include<iostream>
#include<cassert>
#include<time.h>
#include<cmath>
using namespace std; /**
作用:判断一个数是否是素数
参数x:待判断的素
返回:是素数返回true,否则返回false
*/
bool isPrime(int x)
{
for(int i=;i*i<=x;++i) //改进:使用 i*i<=x 而不是 i<= sqrt(x)
{
if(x%i==) return false;
}
return true;
} /**
作用:统计[2,n]之间的素数的个数
参数:n
返回:素数的个数
*/
int countPrimes(int n) {
int count=; if(n<) return ; for(int i=;i<=n;++i)
{
if(isPrime(i)) //如果 i为素数
{
++count;
}
}
return count; } int main()
{ clock_t start = clock();
int total = countPrimes();
clock_t end = clock(); cout<<"耗时:"<<(double(end-start))/CLOCKS_PER_SEC*<<"毫秒"<<endl; return ;
} /*********************测试数据*****************************
n=200000 46毫秒
n=700000 194毫秒
*/
可以发现,小小的改变,时间复杂度减少了大半!!!让我觉得很遗憾的是,很少有人知道这个技巧,leetCode上,也没有发现使用这个技巧的。
第三次尝试
前面的2个solution是不能Accept的,时间复杂度不达标。下面我又开始了google,找到了如下的优化方案。
预备知识:
① 合数一定能分解为 若干个 质素相乘 。如 27 = 3x3x3 ,155 = 5x31
② 原命题的真假性 和他的逆否命题相同。因此我们可以推导出第①命题的逆否命题--->: 不能分解为 素数相乘的数一定是质素(素数)。
因此根据这个推导出的定理,我们不再需要将待判断的所有数x去对 [2,sqrt(x)]之间的数试除,而只需要去对 [2,sqrt(x)]之间的素数试除就OK了。所以:我们必须在迭代判断素数的过程中,将已经判定为素数的数用数组存储起来,因为后面更大的数判断时,需要用比他小的素数(在[2,sqrt(x)]之间的)去试除做判断。那么这个数组需要多大呢?
[0,n]之间有 n +1 个整数,然而0 和 1不是素数也不是质素,因此剩下 n +1 -2 = n-1 个数。素数偶数各一半,除了2,偶数一定不是素数,因此我们大致将这个数组大小定义为 (n-1)/2 +1
好了,时间 和 空间上都过了优化,下面试试吧。
#include<iostream>
#include<cassert>
#include<time.h>
#include<cmath>
using namespace std; /**
作用:判断一个数是否是素数
参数x:待判断的素
参会primes:存储比x小的所有素数 的数组
返回:是素数返回true,否则返回false
*/
bool isPrime(int x,int *primes)
{
for(int i=;primes[i]*primes[i]<=x;++i) //用primes中存储的素数做为试除因子。
{
if(x%primes[i]==) return false;
}
return true;
} /**
作用:统计[2,n]之间的素数的个数
参数:n
返回:素数的个数
*/
int countPrimes(int n) { if(n<) return ; int count=;
int* primes = new int[(n-)/+] ; if(n >=) primes[count++] = ; //2是第一个素数,直接将他放入数组。 for(int i=;i<=n;++i)
{
if(isPrime(i,primes)) //如果 i为素数
{
primes[count++] = i; //则将 i存储到数组中
}
}
delete[] primes;
return count; } int main()
{ clock_t start = clock();
int total = countPrimes();
clock_t end = clock(); cout<<"耗时:"<<(double(end-start))/CLOCKS_PER_SEC*<<"毫秒"<<endl; return ;
} /****************测试数据******************
n=200000 13毫秒
n=700000 50毫秒
*/
时间复杂度又大大减少了!!!可见,我们的程序优化的空间往往很大。
最后,我的程序终于被AC了 ,不过,还可以继续优化,下面是我的程序的所处的时间复杂度的位置,前面有70% 的solution比我更优 。优化尚未成功,码农仍需努力。
:)

更佳的solution 请看这篇
欢迎转载,请注明出处:www.cnblogs.com/lulipro
为了获得更好的阅读体验,请访问原博客地址。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
代码钢琴家
【题目】求n以内的素数个数的更多相关文章
- 记一次【求n以内的素数个数】的优化记录
最近在leetCode上刷提,还是满锻炼人的,为以后面试打基础吧.不多说下面开始. 问题:求[2,n]之间的素数的个数. 来源:leetCode OJ 提示: Let's start with a i ...
- Python3求m以内的素数、求m个数中最小的n个数
[本文出自天外归云的博客园] 题1:求m以内的素数(m>2) def find_all_primes_in(m): def prime(num): for i in range(2, num): ...
- Python练习题 026:求100以内的素数
[Python练习题 026] 求100以内的素数. ------------------------------------------------- 奇怪,求解素数的题,之前不是做过了吗?难道是想 ...
- 斐波那契数列(递归)&求100以内的素数
Java 5 添加了 java.util.Scanner 类,这是一个用于扫描输入文本的新的实用程序.它是以 前的 StringTokenizer 和 Matcher 类之间的某种结合.由于任何数据都 ...
- Algorithm --> 求N以内的真分数个数
求N以内的真分数个数 For example, if N = 5, the number of possible irreducible fractions are 11 as below. 0 1/ ...
- 【C语言】输入一个整数N,求N以内的素数之和
[C语言]输入一个整数N,求N以内的素数之和 /* ========================================================================== ...
- 求1e11以内的素数
有两种做法,一种是打表,另一种是直接求. 打表 将1e11每隔len(len=2000w)个数字统计一下该区间内素数的个数,比如cnt[1] 表示[1,len]以内有多少个素数,cnt[2]表示[le ...
- 求小于n的素数个数
本文是对 LeetCode Count Primes 解法的探讨. 题目: Count the number of prime numbers less than a non-negative num ...
- Educational Codeforces Round 12 F. Four Divisors 求小于x的素数个数(待解决)
F. Four Divisors 题目连接: http://www.codeforces.com/contest/665/problem/F Description If an integer a i ...
随机推荐
- 调整iRedmail之Roundcube webmail服务不可不知的几件事
iRedMail集成了roundcube webmail,极大的方便了安装维护工作,但是需求是千遍万化的,总有需要深入调整的地方.下面就是我调整roundcube时遇到的几点问题: 一.修改网页标题p ...
- LR进行接口测试
其实无论用那种测试方法,接口测试的原理是通过测试程序模拟客户端向服务器发送请求报文,服务器接收请求报文后对相应的报文做出处理然后再把应答报文发送给客户端,客户端接收应答报文这一个过程. 方法一.用Lo ...
- MongoDB 之 幽灵操作避免
进行静态加载数据到集合的过程中可能会出现. 假设建立一个任务(Job):在MongoDB中进行千条更新操作,开始后迅速终止任务,终止所有更新操作,但依然发现新的更新任务在不断出现,即使任务已经停止. ...
- WebXml.com.cn 中国股票行情数据 WEB 服务(支持深圳和上海股市的全部基金、债券和股票),数据即时更新
http://www.webxml.com.cn/WebServices/ChinaStockWebService.asmx
- [Unity3D]关于NaN(Not a Number)的问题
在游戏运行时,代码若写得不安全很容易出现NAN的异常.一旦NAN出现整个游戏不崩溃也坏死掉了,游戏上了则是要被直接打回来的节奏,更是一个开发及测试人员每人都要扣3000块的大BUG. 一般表现为: ...
- QT OpenGL绘制三维图形(立方体、圆柱体、圆锥、球体、圆环等等)
本文使用QGLWidget来绘制各种三维基本图形,包括立方体.圆柱体.圆锥.球体.圆环等等,涉及包括基本绘制以及上色.纹理.旋转等操作. 使用的软件版本:QT5.12 + QT Creater4.8. ...
- 消息中间件的意义和应用场景 (activeMq)
消息中间件一般两个功能,解耦和异步处理,分别举个例子吧 解耦合:比如我们做一个微博产品中的好友系统,就很需要使用消息中间件当我们添加一个关注的时候, 涉及以下几个子系统 推荐系统,需要根据你关注的人给 ...
- LINE 不被封锁的技巧
什么是封锁? 谈LINE 被封锁之前,我们先来了解一下什么是封锁.LINE 的封锁分为「好友封锁你」与「官方封锁你」二种,有些人将官方封锁讲成「停权」,其实LINE 的停权并不是你的帐号全被封锁,被封 ...
- android 网络检测
这个过程我觉得有必要记录一下事情的起因是这样的, 写的程序在虚拟机下面无法连接到服务器,首先想到的是,虚拟机能不能访问外网,打开某搜索网站,正常,想用ping命令来ping服务器,于是就有了下面的过程 ...
- Windows最全快捷键
单独按Windows:显示或隐藏“开始”功能表 Windows+BREAK:显示“系统属性” 对话框 Windows+D:显示桌面 Windows+M:最小化所有窗口 Windows+Shift+M: ...