求5 ~ 1000000000内的回文素数

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 .

Input

Line 1: Two integers, a and b

Output

The list of palindromic primes in numerical order, one per line.

Sample Input
5 500
Sample 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


没有更多推荐了,返回首页