Prime Palindromes( 素数回文) C++实现

本文介绍了一种C++程序来找到指定范围内的素数回文,通过优化算法提高了程序效率,解决了时间限制问题。程序首先判断数字是否为回文,然后判断是否为素数,特别地,只考虑奇数位的回文,并排除偶数位回文,因为它们不能是素数。最后,程序在给定的时间限制内成功完成任务。

 Prime Palindromes

Time limit:     15sec.     Submitted:     10679
Memory limit:     32M     Accepted:     1958
Source: USACO Gateway

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



开始拿到题目的时候,一看。恩!挺简单的,于是边哗啦啦地开始coding。
很快,代码写好了。
首先我们先判断以下一个数是不是素数,这个就暂时想到朴素素数法了。
bool is_primer(const unsigned int number)
 28 {
 29     int i;
 30     unsigned int sqrt_num;
 31     sqrt_num = sqrt(number + 1);
 32     for(i = 3; i <= sqrt_num; i += 2)
 33     {
 34         if((number % i) == 0)
 35           return false;
 36     }
 37     return true;
 38 }
接下来就是该写palindromele了, 自己也没有多考虑,就是按正常的方法直接把函数写完,如下所示:
bool is_palindrome(const unsigned int number)
 41 {
 42     int button , top, middle;
 43     unsigned int temp = number;
 44     button = top = 0;
 45     int i = 0;
 46     int array[11];
 47     while(1)
 48     {
 49        array[i] = temp % 10;
 50        temp = temp /10;
 51        if(temp == 0)
 52          break;
 53        i++;
 54    }
 55    top = i;
 56    if(top % 2 != 0)
 57    {
 58        if(number == 11)
 59          return true;
 60
 61        return false;
 62    }
 63    middle = top/2;
 64    for(i = 0; (i <= middle)&& (top >= button); i++)
 65    {
 66      if(array[button] == array[top])
 67      {
 68        button++;
 69        top--;
 70      }
 71      else
 72        return false;
 73    }
 74    return true;
 75 }


int main(void)
  8 {
  9    unsigned int min_bounder, max_bounder;
 10    cin >> min_bounder >> max_bounder;
 11
 12    unsigned int i;
 13
 14    for(i = min_bounder; i < max_bounder; i++)
 15    {
 16        if((i % 2) != 0)
 17        {
 18            if(is_primer(i)&& is_palindrome(i))
 19            {
 20               cout << i << endl;
 21            }
 22        }
 23    }
 24
 25 }

哈哈,大功告成! 等等....还没有Submit呢! 提交一看,惨了Time Limit Exceed!

到这一步,终于知道了,程序没有自己想象中的那么简单。
考虑了一下,我们知道,回文的判断应该比素数的判断要快,而且要比素数判断简单。所以应该先判断完了是不是回文,再判断
其是否为素数,这样速度回大有提高。于是改变一下回文判断与素数判断的先后顺序。
if( is_palindrome(i)&&is_primer(i))

提交,结果还是超时!

这回好像的想想办法了。可能是素数的判断算法效率太低了,还有可能是回文的判断效率低。于是我先来看看回文判断有没有可以提高的地方
想像一下1000,000,000。 这么大的数, 如果是按照上面的办法直接找回文的话。
循环for(i = min_bounder; i < max_bounder; i++)一遍就得 1000,000,000趟。可想而知,暂且不讨论素数判断问题,光是这个判断就
足以超时了。


再仔细深入研究了一下, 我们可能会很容易发现1000,000,000的回文最大为 999,999,999
而此处,由于是回文。我们只需要前半部分就可以了,所以我们可以把循环缩小为10000就可以了,比如说
1230321这个数,我们仅仅需要得到前面的半部分123就行了。
想到这里,算法也就已经出来了。同时我们还应该注意到一个问题,这个问题也可已很快地提高程序执行的速度
那就是偶数偶数位回文都不是素数,举个例子吧:
1551 可以被11整除, 153351也可以被11整除, 同理, 偶数位的回文都可以被11整除。
于是程序又可以得到了进一步的简化, 运行速度上也可以得到了很大的提高。
把回文函数修改一下如下所示:
#include <iostream>
#include <cmath>
using namespace std;

 bool is_primer(const unsigned int number);
 bool is_palindrome(const unsigned int min_bound, const unsigned int max_bound);
 int power(const int a, const int n);
 int reverse_num(const int number , unsigned int &total, int &reverse);

int main(void)
{
   unsigned int min_bounder, max_bounder;
   cin >> min_bounder >> max_bounder;
   is_palindrome(min_bounder, max_bounder);
   return 0;
}
/*判断是否为素数*/
bool is_primer(const unsigned int number)
{
    int i;
    int sqrt_num;
    sqrt_num = sqrt(number);
    for(i = 3; i <= sqrt_num; i += 2)
    {
        if((number % i) == 0)
          return false;
    }
    return true;
}
/*获取回文的函数*/
bool is_palindrome(const unsigned int min_bound, const unsigned int max_bound)
{
    int array[] = {5 , 7, 11};
    int log_n_max = log(max_bound)/log(10);
    int log_n_min = log(min_bound)/log(10);
    int reverse = 0;
    log_n_max /= 2;
    log_n_min /= 2;
    int middle_max = power(10, log_n_max);
    int middle_min = power(10, (log_n_min-1));
    unsigned int  result = 0 , temp = 0;

    if(min_bound <= 11 )
    {
        for( int i = 0 ; i < 3 ; ++i )
        {
            if( min_bound <= (unsigned)array[i] )
            {
                cout << array[i] << endl ;
            }
        }
    }

    int i , j , n = 0;
    for(i = middle_min; i <  middle_max; i++)
    {
        n = reverse_num(i, result, reverse);
        temp = result;
        if(reverse % 2 != 0)
        {
           /*这里我们只考虑奇数位数的回文, 这个循环实现如 12 将得到 12021  12121.....12921 */
           for(j = 0; j < 10; j++)
           {
            temp = temp*10 + j*power(10, n);
            temp = temp + reverse;
            if(temp >= min_bound && temp < max_bound)
            {
               if(is_primer(temp))
              {
                cout << temp <<endl;
              }
            }
            else
            continue;
             temp = result;
           }
        }
    }
    return true;
}
/*a 的 n 次幂*/
 int power(const int a, const int n)
{
    unsigned int temp = 1;
    int i = 0;
    for(i =0; i < n; i++)
    {
       temp *= a;
    }
       return temp;
}
/*把 number reverse 一下, 如123 --> 321 */
int reverse_num(const int number , unsigned int &total, int &reverse)
{
    int temp = number ,  result = 0;
    int array;
    total = number;
    int i = 1;
    while(1)
    {
        array = temp % 10;
        temp /= 10;
        result = result * 10 + array;
        total *= 10;
        if(temp == 0)
         break;
         i++;
    }
    reverse = result;
    return i;
}

实现到这一步之后,可以进行提交了,submit一下,OK。成功了。这里没有使用更好的素数判断算法,也没有这个必要了。15secs的时间足够了,
提交时,刚刚好消耗了0.45s的时间。
Proid    Subtime    Judgestatus    Runtime    Memory    Language    Code Length
1004    2010-04-15 18:02:13    Accepted    0.45 s    1248 K    C++    2581 B

后来我有和之前的程序对比了以下运行5  1000 000 000
前面的算法实现运行完总共需要的时间要好几分钟,结果还没运行完。这个时间上的差距确实很大很大。这里仅仅做了一个小小的优化而已。

其实这里还有很大的提高空间。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值