[problem10]欧拉
The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
Find the sum of all the primes below two million.
解题思路:看上去简单的透明。下面是我的瞬间实现:
View Code
1 #include <iostream>
2 using namespace std;
3
4 bool isPrime(int val)
5 {
6 for(int i = 2; i < val; i++)
7 {
8 if(val % i == 0)
9 return false;
10 }
11
12 return true;
13 }
14
15 int _tmain(int argc, _TCHAR* argv[])
16 {
17 unsigned long sum = 0;
18 for(unsigned int i = 2; i <= 2000000; i++)
19 {
20 if(isPrime(i))
21 sum += i;
22 }
23 cout << sum << endl;
24
25 return 0;
26 }
2 using namespace std;
3
4 bool isPrime(int val)
5 {
6 for(int i = 2; i < val; i++)
7 {
8 if(val % i == 0)
9 return false;
10 }
11
12 return true;
13 }
14
15 int _tmain(int argc, _TCHAR* argv[])
16 {
17 unsigned long sum = 0;
18 for(unsigned int i = 2; i <= 2000000; i++)
19 {
20 if(isPrime(i))
21 sum += i;
22 }
23 cout << sum << endl;
24
25 return 0;
26 }
结果是:根本算不出来!最终花了>15min才算完。为什么呢?起初就老想着是需要多线程来提高速度的。看了欧拉problem10的thread,并对质数的性质及判断技巧做了学习,才弄明白。下面是我的总结:
1. 在判断是否质数时,最佳实践是limit为被判断数字的平方根。这个认识应该说是相当基础的(我承认我弱了在这个知识点上)。算法速度的差别主要就是这里造成的:判断到 n / 2, 算完需要> 14min;而判断到sqrt(n),却仅仅需要 < 1second!!! 这是我第一次见识到算法造成的血淋淋的速度巨大差异的事实!这下子记住了!
2. 在考虑算法时,一定要把数学上相关的知识点,定理考虑进去。在问题规模小的时候可能感觉不到差别,但是规模一变大,那区别就出来啦。
3. 考虑到各数据类型的取值范围,能否包含下你面对的问题的limit。关于数据类型的溢出(overflow)。比如int的取值范围是(-2exp31,2exp31-1),赋给它超出取值范围的整数就会导致 overflow。因此,要根据问题选择合适的整数数据类型。非常清楚int, unsigned int,long(on 32bit, same as int), unsigned long, long long, unsigned long long的取值范围。
4. IO确实相当占用时间。cout的使用直接导致<1 second的算法花了N倍的时间。
5. 关于素数的数学特性:
--2是唯一偶素数
--任何大于1的正整数n可以唯一表示成有限个素数的乘积: n=p_1p_2...p_s, 这里p_1≦p_2 ≦...≦p_s是素数。 这个定理也经常被称为唯一因子分解定理。 因为,任何非素数都可以被分解,分解到不能分解,必然是一组素数的乘积。
--判断质数的技巧
根据质数的定义,在判断一个数n是否是质数时,我们只要用1至n-1去除n,看看能否整除即可。但我们有更好的办法。先找一个数m,使m的平方大于n, 再用<=m的质数去除n,如果都不能整除,则n必然是质数。如我们要判断1993是不是质数,50*50>1993,那么我们只要用1993 去除<50的质数就可以了。100以内的质数有25个,还是比较好记的,我们只要记熟100以内质数,就可以快速判断10000以内的数是不是质数 了。
--任何大于1的正整数n可以唯一表示成有限个素数的乘积: n=p_1p_2...p_s, 这里p_1≦p_2 ≦...≦p_s是素数。 这个定理也经常被称为唯一因子分解定理。 因为,任何非素数都可以被分解,分解到不能分解,必然是一组素数的乘积。
--判断质数的技巧
根据质数的定义,在判断一个数n是否是质数时,我们只要用1至n-1去除n,看看能否整除即可。但我们有更好的办法。先找一个数m,使m的平方大于n, 再用<=m的质数去除n,如果都不能整除,则n必然是质数。如我们要判断1993是不是质数,50*50>1993,那么我们只要用1993 去除<50的质数就可以了。100以内的质数有25个,还是比较好记的,我们只要记熟100以内质数,就可以快速判断10000以内的数是不是质数 了。
View Code
#include <cmath>
#include <ctime>
#include <iostream>
using namespace std;
bool isPrime(unsigned long val)
{
// best practice: sqrt(val) is the desired limit. Otherwise, for example using val/2 as the limit, 14mins VS 500milliseconds would be the result.
unsigned long limit = (unsigned long)sqrt((float)val);
for(unsigned long i = 3; i <= limit; i += 2 )
{
if(val % i == 0)
return false;
}
return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
clock_t startTime = clock();
long long sum = 2;
for(unsigned long i = 3; i < 2000000; i += 2) // except 2, all primes are odd.
{
if(isPrime(i))
sum += i;
//cout << "i: " << i << endl; IO is very very time-consuming.
}
cout << "sum: "<< sum << endl;
clock_t endTime = clock();
double spentTime = difftime(endTime, startTime); // milliseconds
cout << "spentTime: " << spentTime << endl;
return 0;
}
#include <ctime>
#include <iostream>
using namespace std;
bool isPrime(unsigned long val)
{
// best practice: sqrt(val) is the desired limit. Otherwise, for example using val/2 as the limit, 14mins VS 500milliseconds would be the result.
unsigned long limit = (unsigned long)sqrt((float)val);
for(unsigned long i = 3; i <= limit; i += 2 )
{
if(val % i == 0)
return false;
}
return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
clock_t startTime = clock();
long long sum = 2;
for(unsigned long i = 3; i < 2000000; i += 2) // except 2, all primes are odd.
{
if(isPrime(i))
sum += i;
//cout << "i: " << i << endl; IO is very very time-consuming.
}
cout << "sum: "<< sum << endl;
clock_t endTime = clock();
double spentTime = difftime(endTime, startTime); // milliseconds
cout << "spentTime: " << spentTime << endl;
return 0;
}


浙公网安备 33010602011771号