Problem 95 Prime Palindromes

这个问题非常有趣,回文素数在数论里也占有一席之地,成为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;
}

参考:http://hk.geocities.com/goodprimes/OBack.htm

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值