这个问题非常有趣,回文素数在数论里也占有一席之地,成为Palindromic Prime,或简写为Palprime。
题目的规模比之前的三道一下跳了n个数量级,达到100,000,000。不过因为只是在这个范围内做搜寻,所以并不用担心int的表示范围。但是,一亿还是很大的数字,一个个检测显然不现实,要想办法把范围缩小。首先,除了2,所有的素数都是奇数,而题目范围从5开始……Oh yeah,一下就干掉一半。俗话说好的开始是成功的一半,好兆头好兆头,hoho。接着,我们来看看回文数有什么特质:
1) 所有的个位素数都是Palprime,也就是2,3,5,7。
2) 两位回文素数只有一个,11,其他的如33,55,77等都是11的倍数。
3) 三位回文素数一共15个,分别是101,131,151,181,191,313,353,373,383,727,757,787,797,919,929。
4) 没有4位素数,实事上,偶数位的回文数全部都是11的倍数。因为一个偶数位的回文数必形如abba……E~~~我不知道怎么说了,奇数位的a+b一定等于偶数位的b+a……6位数也一样,abccba,依此类推吧-_-!。结论:11是唯一的偶位回文素数(好拗口)。
5) 五位回文素数有84个,最小的11311,最大的98689。
OK,看出一点端倪来了吧,我们只要把11单独列出来,就可以把所有偶数位数全部干掉。哦也again!再削掉一点,由于2是唯一的偶素数,所以回文素数除了2只能以奇数开头。
另外,即使在5位数10000~99999里,回文数也只有区区84个,可见回文数的数目比素数要少很多,而且,回文数是表面上就可以看出来的,而素数是自然数本身的性质,数字一大就很难直观的判断其素性。那么我们就可以不用一个个去试,而是自己动手把回文数create出来,再判断它是不是素数,如此以来,题目的规模就变得可以接受了。actually,Rob给的两个hints里也就是告诉你这么做。(想当初,Rob可没有那么kind,还给你hint…………鸡蛋!板砖!OKOK,不敢再倚老卖老了,最后一次,事不过三嘛~~~别踩,再踩就烂啦!)
现在来看看实事上我们要检查多少个数字,100,000,000是9位数,回文素数可能出现的区域实际上只有:2-9、11、101-999、10,001-99,999、1,000,001-9,999,999。每个区域内的回文数字都可以组合出来,分别是4,1,5×10,5×10×10,5×10×10×10。只有五千多个!最终的结果还要小,可见回文素数算得上是稀有族群了。
我用一个assemble函数来拼装回文数,它接受三个参数,分别是前缀1,前缀2和中间数。单独把前缀1列出来是因为回文素数开头和结尾都是奇数。重载一个双参数版本for 101-999。得到回文数后检查是不是落在要求的范围内,是不是素数,最后记得排序,因为拼装的数字并不是自然序列。
最后用三个for loop把它们包起来,你可以算一下i×j×k的积,看看和上面的组合数是不是一样的?
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
int assemble(int, int);
int assemble(int, int, int);
int isprime(int);
int main(){
int a,b;
vector<int> array;
ifstream fin("pprime.in");
fin>>a>>b;
if(a <= 5 && b >= 5) array.push_back(5);
if(a <= 7 && b >= 7) array.push_back(7);
if(a <= 11 && b >= 11) array.push_back(11);
for(int i = 1; i < 10; i += 2){ // prefix1 or prefix
for(int j = 0; j < 10; ++j){ // middle
// 1.0.1-9.9.9
int tmp = assemble(i, j);
if(isprime(tmp) && tmp >= a && tmp <= b) array.push_back(tmp);
for(int k = 0; k < 110; ++k){ // prefix2
// 1.0.0.0.1-9.9.9.9.9: 0<=j<=9
// 1.10.0.01.1-9.99.9.99.9: 10<=j<=99
// 1.00.0.00.1-9.09.9.90.9: 100<=j<=109 TRICKY! Be careful!
tmp = assemble(i, k, j);
if(isprime(tmp) && tmp >= a && tmp <= b) array.push_back(tmp);
}
}
}
sort(array.begin(),array.end());
ofstream fout("pprime.out");
vector<int>::const_iterator ci = array.begin();
for(; ci != array.end(); ++ci) fout<<(*ci)<<endl;
}
// generate a palindrome
int assemble(int prefix, int middle){
int result = prefix * 10 + middle;
while(prefix){
result = result * 10 + prefix % 10;
prefix = prefix / 10;
}
return result;
}
int assemble(int prefix1, int prefix2, int middle){
int result, prefix;
if(prefix2 < 10)
prefix = prefix1 * 10 + prefix2;
else if(prefix2 >= 100) //generate sequence: 00-09
prefix = prefix1 * 100 + prefix2 % 100;
else
prefix = prefix1 * 100 + prefix2;
result = prefix * 10 + middle;
while(prefix){
result = result * 10 + prefix % 10;
prefix = prefix / 10;
}
return result;
}
int isprime(int i){
if(i == 2) return 1;
if(!(i%2)) return 0;
for(int divisor = 3; divisor*divisor <= i; divisor += 2){
if(!(i%divisor)) return 0;
}
return 1;
}