Prime Palindromes( 素數迴文) C++實現

qinleopard發表於2010-04-15

 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
前面的演算法實現執行完總共需要的時間要好幾分鐘,結果還沒執行完。這個時間上的差距確實很大很大。這裡僅僅做了一個小小的優化而已。

其實這裡還有很大的提高空間。

相關文章