這個程序是哈爾濱工業大學的一道作業題,有一位朋友昨晚讓我看了這道題目,我發現這道題目還真不是那麼簡單。學校對剛剛學完數組的大一同學出此題目是否欠妥呢?
現在先看看題目。
Prime Palindromes
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 <= 100,000,000); both a and b are considered to be within the range .
PROGRAM NAME: pprime
INPUT FORMAT
Line 1: Two integers, a and b
SAMPLE INPUT (file pprime.in)
5 500
OUTPUT FORMAT
The list of palindromic primes in numerical order, one per line.
SAMPLE OUTPUT (file pprime.out)
5
7
11
101
131
151
181
191
313
353
373
383
這道題目表面上來看比較簡單,因為是很基礎的算法——求素數和迴文數。我先寫了一個用循環求余法求素數和檢驗是否迴文的函數,循環查找符合條件的數。
結果不出所料,顯然超時。我需要對它進行優化。
所謂優化,無非是三點思路:減少判斷是否素數的時間;減少判斷是否迴文的時間;減少被選數的數量,提高數的精確性。
判斷迴文並沒有什麼好優化的地方。就是把前後對稱的每位對比一下,防止重複對比即可。
那麼判斷素數呢?
首先,我們考慮是否可以用篩法求素數。篩法求素數是先把所有的數保存在一起,然後去掉不符合要求的數。每次他找到一個素數,就把這個素數的所有倍數均刪除,最後留下的就是需要的素數。
但是這種算法在本題是行不通的,因為數據範圍高達 1,000,000,000,如果我們使用篩法,佔用內存太大,然而這道題目要求的內存不超過 32MB。
所以我們繼續優化求余法。首先,所有的偶數都可以排除(除2以外);其次,所有偶數位的數都可以排除。因為這樣的數的奇數位和偶數位之和可以被11整除,這樣的數一定可以被11整除,因此不可能是素數;另外,開頭是0、2、4、5、6、8的可以排除,因為這樣的迴文數一定不是素數。
最後,還可以使用篩法的思路優化素數算法。因為要檢查所有的數,對於大數的運算量過大;考慮下面的想法:如果一個數不能整除比他小的所有的素數,那麼他一定是素數。原因是一個合數總能被分解為好多素數的積;如果每個質數都不能整除,那麼也肯定不會有合數可以整除。
基於上述思路,再結合在求余法中廣為應用的優化:只需要判斷sqrt(該數)之前是否可以被整除,我們就可以確定,對於小於等於1,000,000,000 的數,只需要保存 10000 之前的素數,然後使用這些質數就可以校驗所有的數是否是素數。
那麼如何減少數的範圍呢?根據偶數位的數一定不成立、開頭結尾不能是0、2、4、5、6、8的要求,可以不窮舉所有數,直接使用循環生成迴文數表。而且,只需要循環數的一半,就可以生成對應的迴文數表。
所以最後,我用的算法是這樣的:先直接算出10000內的質數表,生成所有小於取值範圍並且剪枝過的迴文數表,並用質數表去驗證是否為質數。
程序如下(最長用時 0.02 秒)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | /* ID:cxj6661 PROG:pprime LANG:C++ */ /* * Prime Palindromes * By Ceeji * Type: Normal * For: Friend(Liuhaodong) * Date: 2010-10-30 * Under CC 3.0 */ #include <iostream> #include <cmath> #include <string> #include <fstream> #include <cstring> using namespace std; const int maxl = 10, maxp = 10000; const char *inf_name = "pprime.in"; const char *ouf_name = "pprime.out"; int primes[maxp]; bool isp[maxp]; int plen; bool prime(int i) { if (i == 1) return false; if (i == 2 || i == 3) return true; if (i % 2 == 0) return false; int m = (int)sqrt((float)i); if (m > i - 1) m = i - 1; for (int j = 3; j < m + 1; j += 2) { if (i % j == 0) return false; } return true; } bool prime2(int i) { if (i < 10001) { return isp[i]; } if (i == 2 || i == 3) return true; if (i % 2 == 0) return false; int m = (int)sqrt((float)i); for (int j = 0; j < plen; ++j) { if(primes[j] > m + 1) break; if (i % primes[j] == 0) return false; } return true; } bool palindrome(int i) { char ch[10]; sprintf(ch, "%d", i); int l = strlen(ch); for (int p = 0; p < l / 2 + 1; ++p) { if (ch[p] != ch[l - p - 1]) return false; } return true; } int main(void) { ifstream fin(inf_name); ofstream fou(ouf_name); int a, b; fin >> a >> b; /* Find primes which are not bigger than 10000; this will accelerate the prime2 function to check number from 0 to 1,000,000,000. */ for (int i = 1; i <= 10000; ++i) { if (prime(i)) { primes[plen++] = i; isp[i] = true; } else isp[i] = false; } for (int i = a; i < 100; ++i) { if (i >= a && i <= b && palindrome(i) && prime2(i)) { fou << i << endl; } } int now = 0; /* Now check for the palindrome with width of 3 */ for (int i = 1; i < 10; i += 2) for (int j = 0; j < 10; ++j) { if (i == 5) continue; now = i * 100 + j * 10 + i; if (now < a) continue; if (now > b) { fou << flush; return 0; } if (prime2(now)) fou << now << endl; } /* Now check for the palindrome with width of 5 */ for (int i = 1; i < 10; i += 2) { if (i == 5) continue; for (int j = 0; j < 10; ++j) for (int k = 0; k < 10; ++k) { now = i * 10000 + j * 1000 + k * 100 + j * 10 + i; if (now < a) continue; if (now > b) { fou << flush; return 0; } if (prime2(now)) fou << now << endl; } } /* Now check for the palindrome with width of 7 */ for (int i = 1; i < 10; i += 2) { if (i == 5) continue; for (int j = 0; j < 10; ++j) for (int k = 0; k < 10; ++k) for (int l = 0; l < 10; ++l) { now = i * 1000000 + j * 100000 + k * 10000 + l * 1000 + k * 100 + j * 10 + i; if (now < a) continue; if (now > b) { fou << flush; return 0; } if (prime2(now)) fou << now << endl; } } /* Now check for the palindrome with width of 9 */ for (int i = 1; i < 10; i += 2) { if (i == 5) continue; for (int j = 0; j < 10; ++j) for (int k = 0; k < 10; ++k) for (int l = 0; l < 10; ++l) for (int m = 0; m < 10; ++m) { now = i * 100000000 + j * 10000000 + k * 1000000 + l * 100000 + m * 10000 + l * 1000 + k * 100 + j * 10 + i; if (now < a) continue; if (now > b) { fou << flush; return 0; } if (prime2(now)) fou << now << endl; } } fou << flush; return 0; } |
您也許喜歡:

估計是想讓學生打表用數組存吧,哈哈
果斷表示打表er無壓力……
我就是用打表法來做的,用篩選法打表,沒超空間,但是超時了!!!
line93: Find primes which are…
tks