1 / 78

演算法 期末 Project

演算法 期末 Project. 組員 : 99703009 詹伊婷 99703017 孟繁辰 99703036 陳育聖. 題目 ( 判斷質數進階版 ). 題意 : 輸入為多行, 每一行包含兩個大於 1 的整數 a,b , 1 < a ≤ b < 1000000000 (10 9 ) 。 時間限制: 20000 ms 記憶體限制 : 80000 KBytes 輸出 a 與 b 之間 ( 含 ) 有多少個質數 。 來源 : http://judge.nccucs.org/ShowProblem?problemid=d063. 範例輸入、輸出.

inigo
Download Presentation

演算法 期末 Project

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 演算法 期末Project 組員 : 99703009 詹伊婷99703017 孟繁辰99703036陳育聖

  2. 題目(判斷質數進階版) 題意 : 輸入為多行, 每一行包含兩個大於1的整數a,b, 1 < a ≤ b < 1000000000(109)。 時間限制:20000 ms 記憶體限制 : 80000 KBytes 輸出a與b之間(含)有多少個質數。 來源: http://judge.nccucs.org/ShowProblem?problemid=d063

  3. 範例輸入、輸出 範例輸入 : 24 3100 2999999999 範例輸出 : 2 24 50847534

  4. 題目分析(何謂質數?) • 何謂質數? 定義: 指在一個大於1的自然數中, 除了1和此整數自身外, 無法被其他自然數整除的數。

  5. 由定義判斷質數 boolIsPrime (int num) { for (int n = 2; n < num; ++n) { if(num % n == 0) return false; } return true; } /*題目中保證 num > 1 */

  6. 時間複雜度 for (int n = 2; n < num; ++n) { 略 } 可得知,檢查 1 個數的時間複雜度為 (若輸入的數字為 k) 需要 O(k-2) = O(k)

  7. pseudocode Get a, b num =0 For n = a to b// O(b - a) IF( IsPrime(n)) // O( n ) num = num + 1 // O( 1 ) => Answer is num

  8. 整體複雜度 時間複雜度分析 : 假設輸入 1, n,所需要的複雜度為 O(1 + 2 + 3 + … + (n-1) + n) =O((n+1)*(n)/2) = O(n2) ============================================== 假設輸入 a, b,所需要的複雜度為 O(a + (a+1) + (a+2) + … + (b-1) + b) =O((a + b)*(b – a + 1)/2) = O(b2 - a2) 空間複雜度分析 :O(1) (題目要求的是範圍內)

  9. 執行結果(TLE) 檢測所需時間 實際操作 (n最大值為109-1) for(i=0; i<109; ++i);/*耗時2.8秒*/ 所以若用以上的方法 1 ~ 109檢測是否為質數所需時間為 2.8 * 109s => 約 90年 題目時間限制 :20秒

  10. 怎麼辦?

  11. 思考! 思考

  12. 思考!!

  13. 思考!!!

  14. 再思考!!!

  15. 靜下心,思考!

  16. 思考 =>分析 分析1: 對稱 8 = 2*4 = 4*2 9 = 3*3 10 = 2*5 = 5*2 12 = 2*6 = 3*4 = 4*3 = 6*2 n = m0*m1 (m0 <= m1) 發現只需要檢查到 m0最大值 n = m*m 所以 檢查到 sqrt(n) 即可

  17. 思考 =>分析 分析2:歸納 是 2的倍數 => 不是質數 是 3  的倍數 => 不是質數 是 4  的倍數 => 不是質數 (4 = 2*2) (所以 4 不是質數,所以也是質數的倍數) 是 5  的倍數 => 不是質數 是 質數的倍數 => 不是質數 所以只需判斷 n 是否能整除質數

  18. 思考結果 結合分析1、2,只需檢查 小於sqrt(n)的所有質數是否能被整除 sqrt(109) = 31622 所以先產生所有小於31622的質數, 放置在陣列中,判斷是否為質數時, 取用所有小於sqrt(n)的值, 若都不可整除則為 質數

  19. /* p[] 為小於31622的 質數陣列 由小排到大 */ /* p.length = 3401 */ boolIsPrime (int num) { inti = 0; for(; i<p.length && p[i]*p[i]<=num; ++i) if(num % p[i] == 0) return false; return true; }

  20. 時間複雜度 檢查一個數字 n 所需要的時間複雜度 將會是小於等於 sqrt(n) 以下的質數個數 這個問題牽涉到質數的分布 根據質數分布定理(花了一百年才得證) n以下的質數個數 = π(n), 近似值 = n/(log n) ( π(n)=n / (log n), when n = ∞) 所以檢查 1 個數字所需 O(n0.5/log n)

  21. 質數分布定理

  22. 由上圖 可以觀察得到: (1)當x越來越大時,π(x)/x 越來越接近 0。 (2)當x越來越大時,π(x)log(x)/x 越來越接近 1。

  23. 空間複雜度 若需要判斷的數最大值為 n ,就必須產生 小於等於sqrt(n)所有質數個數的陣列存放 O(小於等於 sqrt(n)的所有質數個數) =O(π(n0.5)) = O( n0.5/log(n) )

  24. pseudocode Get a, b (max num = m) Make Prime Table// O(m1/2*m1/4 = m3/4) (建立m1/2以下質數陣列,判斷1個數x需O(x1/2)) num =0 For n = a to b// O(b - a) IF(IsPrime(n))// O(π(n0.5)) num = num + 1 // O( 1 ) => Answer is num

  25. 整體複雜度 時間複雜度分析 : 輸入 a, b O(m3/4 + (b – a) * π(n0.5)) =O(m3/4 + b * b0.5/log(b)) = O(m3/4 + b1.5/log(b)) 空間複雜度分析 :(max num = n) O(小於等於 sqrt(n) 的所有質數個數) =O(π(n0.5)) = O( n0.5/log(n) )

  26. 執行結果(TLE) 實際操作 1 ~ 109檢測 耗費時間 : 13分鐘 90 年 => 13 分鐘 進步惹!!! 題目時間限制 :20秒

  27. 所以…

  28. 接下來怎麼辦?

  29. 思考…

  30. 要不要放棄…

  31. 還是打場LOL^~^

  32. 思考 最初 : for(i=0; i<109; ++i);/*耗時2.8秒*/ 題目時間限制 :20秒 所以時間複雜度必須壓制在 O(n) 最為保險 也就是說,輸入一個數 我必須在 O(1) 判斷出是否為質數

  33. 這個時候…

  34. 發現提示 題目提示 : Sieve of Eratosthenes 建出完全的質數表 太好惹~~~有提示欸~~~

  35. Sieve of Eratosthenes 這是一個製作質數表的方法,通常簡稱為「篩法」。 列出所有正整數 從 2 開始,刪掉 2 的倍數 找下一個未被刪掉的數字,找到3,刪掉 3 的倍數 找下一個未被刪掉的數字,找到5,刪掉 5 的倍數 如此不斷下去,就能刪掉所有合數,找到所有質數。 http://www.csie.ntnu.edu.tw/~u91029/Prime.html

  36. 結合分析 IsPrime[num] = { true }; for(i=2; i<num; ++i) { if(IsPrime[i]) { for(j=2*i; j<num; j+=i) { IsPrime[j] = false; } } } 由前面歸納的重點可修改成更快的方式

  37. for(i=2; i<sqrt_num; ++i) { if(IsPrime[i]) { for(j=i*i; j<num; j+=i) { IsPrime[j] = false; } } } 欲刪掉質數 i 的倍數之時, 早已刪掉 1 倍到 i-1 倍了, 所以直接從 i倍開始。

  38. const int MAX = 109; boolPrimeTable[MAX]; /* 全域陣列初始化為false */ void MakePrimeTable() { for(int A=2; A<SQRT_MAX; ++A) if(PT[ A ] == false) for(int B=A*A; B<MAX; B+=A) PT[ B ] = true; } 最後存放在PT[]中,false為質數 (除0, 1)

  39. 但是…

  40. 無法執行(MLE) MLE : Memory Limit Exceed 表示程序執行超過記憶體限制 sizeof( bool ) 發現佔 1 個byte 所以共使用了 109 bytes = 976,562.5KB (超大) (1024 bytes = 1KB) 題目記憶體限制 :80,000 KB

  41. 解決方法 1.記錄 真與否 只需要 1 bit(1byte = 8bits) (利用unsigned int 4bytes(32 bits)實做) 2.偶數可不用檢測 109 bits = 109 / 2/8 / 1024 = 61035.15625 KB < 80000 KB

  42. bits表示 1 個 unsigned int: 編號:1 2 3 4 5 6 7 8 9 10 11 12 13 14 0 0 0 0 1 0 0 1 0 0 1 0 1 1 數字: 1 3 5 7 9 11 13 15 17 19 21 23 25 27 =============================================== 編號: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 0 0 1 1 0 1 0 0 1 0 1 1 0 1 數字: 29 31 33 35 37 39 41 43 45 47 49 51 53 55 =============================================== 編號: 29 30 31 32 1 0 0 1 數字: 57 59 61 63 ( 0代表 3, 5, 7, 11, 13…… 是質數 )

  43. const int MAX = 1000000000; const int SQRT_MAX = sqrt( MAX ); unsigned int PT[MAX >> 6]; /*全域變數才放得下*/ void MakePrimeTable() { unsigned int A, B; for(A=3; A < SQRT_MAX; A+=2) if((PT[A>>6] & (1<<((A>>1) & 31))) == 0) for(B=A*A; B < MAX; B += (A <<1)) PT[B>>6] |= (1<<((B>>1) & 31)); }

  44. 時間複雜度 只有質數會消掉它本身的倍數 所以為 O((n0.5/log n)*(n/m)) n:範圍n n0.5/log n:質數個數 m:質數的值 => n/m: 每個質數需消除的倍數個數 可寫為 O(n1.5/(log(n)*m))

  45. 執行結果(TLE) 建表需時 :14.2秒 檢測 1 ~ 109需時 :2 秒左右 但是輸入檔不會只有 1 筆輸入 所以需要更快的方法……

  46. 到底想逼死誰…

  47. 振作 我們在一開始就將 2 的倍數剔除 所以說 我們也可以在一開始就將 3 的倍數剔除 6n + 06n + 1 6n + 26n + 3 6n + 46n + 5 所以只需檢查 6n + 1, 6n + 5 的數

  48. const int MAX = (1000000000 >> 1); const int SQRT_MAX = sqrt( (MAX << 1) ) / 2; unsigned int PT[MAX >> 5]; unsigned int N[2] = { 2, 1 }; /* 6n + 1, 6n + 5 跳 2, 1, 2, 1…… 略過偶數 */ boolIsPrime(int n) { return ((PT[n>>5] & (1<<(n & 31))) == 0); }

  49. void MakePrimeTable() { unsigned inti, j, k = 0, n; for(i=2; i<SQRT_MAX; i += N[(++k)&1]) { if(IsPrime( i )){ n = (i << 1) + 1; j = (n * n) >> 1; n = (n << 1) + 1; for(; j < MAX; j = ((j << 1) + n)>>1) PT[j >> 5] |= (1 << (j & 31)); } } }

More Related