Question:
The number 151 is a prime palindrome because it is both a prime number and a palindrome (it is the same number when read forward as backward). Write a program that finds all prime palindromes in the range of two supplied numbers a and b (5 <= a < b <= 1000,000,000); both a and b are considered to be within the range .
InputLine 1: Two integers, a and b
OutputThe list of palindromic primes in numerical order, one per line.
Sample Input5 500Sample Output
5 7 11 101 131 151 181 191 313 353 373 383
解题思路:
可能符合条件的回文数:以下称为回文数。
设a为左界,b为右界,范围内的回文数数量很多,如果通过判断a~b之间的数是否为回文数,则会浪费大量的时间在不符合条件的数上。
通过分析:
1)可以证明除了11以外,不存在偶数位的回文数是素数,因为该回文数能被11整除,也就说明大于11的满足条件的回文数是奇数位,以中间数为对称轴。
2)因大于2的素数都是奇数,故在奇数位回文数中,首位为2、4、6、8的数均不是素数。
3)因5的任何倍数末尾为5,故在奇数位回文数中,首位为5的数均不是素数。
4)回文数满足上述条件,故通过构造回文数比根据整数判断是否为回文数节约大多数时间。
通过以上分析,可以得到,除<=11的回文数外,其它为奇数位,且呈现对称性。即最多为5位(99999个数),减去一半的偶数,还剩不到5W个,剩下的数中首位为0、2、4、5、6、8的数均不满足条件,回文数最多为2W个。可见通过构造回文数确实可节约大量时间。
通过以上构造得到的回文数是满足回文素数的必要条件,但并不充分,故还需要进一步判断是否为素数,如确实为素数,则该数为回文素数。
以下为参考代码:
// PrimePalindrome.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <cmath> #include <cstdlib> #define MIN 5 #define MAX 1000000000 static unsigned g_iSeq = 5; // 从5开始算回文数 unsigned CreatePossiblePrimePalindrome(unsigned); bool IsPrime(int iPriorPrime); int _tmain(int argc, _TCHAR* argv[]) { unsigned left,right; std::cin >> left >> right; while(left < MIN || right > MAX) { std::cin >> left >> right; } // 调整为奇数 if (0 == left % 2) { ++left; } for(unsigned i = left; i < right;) { i = CreatePossiblePrimePalindrome(g_iSeq); if(IsPrime(i) && i >= left && i <= right) { std::cout << i << std::endl; } } system("pause"); return 0; } /* 构造可能的回文素数,如给定123,则返回12321 * 原则1- 偶数位的回文不可能是素数,因为能被11整除 * 原则2- 首位为2,4,6,8不可能是素数(素数除2外都是奇数) * 原则3- 首位为5的回文不可能是素数,因为能被5整除 */ unsigned CreatePossiblePrimePalindrome(unsigned n) { if(n < 10) { switch(n) { case 5: ++g_iSeq; return 5; case 6: g_iSeq = 7; return 7; case 7: case 8: case 9: g_iSeq = 10; return 11; } } // n十进制最高次项,如25876,则iCount = 4,表示10000数量级 int iCount = (int)(log(n * 1.0) / log(10 * 1.0)); // n的最高位数字,如25876,则iHigh = 2 int iHigh = n / (int)(pow(10 * 1.0, iCount)); unsigned palindrome = 0; //最高位为2,4,5,6,8则不可能是回文素数,排除,并重新构造,如给定200,首位为2,调整为3 switch(iHigh) { case 2: case 4: case 6: case 8: // 如24876,本应构造为248767842,但依据原则2,所以构造为300000003 g_iSeq = (iHigh + 1) * (int)pow(10 * 1.0, iCount); palindrome = g_iSeq * (int)pow(10* 1.0, iCount) + (iHigh + 1); ++g_iSeq; return palindrome; case 5: // 首位为5,根据原则3和原则2,则应构造为7开头的回文数字 g_iSeq = 7 * (int)pow(10 * 1.0, iCount); palindrome = g_iSeq * (int)pow(10* 1.0, iCount) + 7; ++g_iSeq; return palindrome; } // 最高位满足条件,则构造回文,如12345,构造为123454321 unsigned iReverse = 0; // 前n-1位的逆数和,如12345前4位的逆数和最终结果为4321 palindrome = n * (int)pow(10 * 1.0, iCount); // n后面添0 n /= 10; // 12345变为1234 // 求逆数和,如12345,求得为4321 while(n) { iReverse = iReverse * 10 + n % 10; n /= 10; } ++g_iSeq; return palindrome + iReverse; } bool IsPrime(int x) { for(int i = 3; i * i <= x; i += 2) { if (0 == x % i) { return false; } } return true; }
该算法找5到10亿内的回文素数耗时0.47s,内存消耗为1274K。耗时多花在判断回文数是否为素数。网上有现在较快的素数测试算法:Rabbin Miller测试法,可参考前文:http://blog.csdn.net/arvonzhang/article/details/8564836