数论——判断素数
數論——判斷素數
小編的話:終于到了數論了
目錄:
文章目錄
- 數論——判斷素數
- 1.素數的定義
- 2. 判斷素數
- 2.1 判斷素數方法1
- 2.2 判斷素數方法2
 
- 3. 總結
 
1.素數的定義
- 在數學里,素數指的是大于1的正整數中,所有只有1和n兩個因子的數,被稱作素數,也叫作 質數
- 合數:大于1的正整數里,不是質數就是合數
- 三質數:一個平方數開根之后是質數的平方數叫做三質數
2. 判斷素數
2.1 判斷素數方法1
我們可以想到利用素數的性質來判定素數
洛谷判斷素數, U82118題
#include <bits/stdc++.h> #define int long long using namespace std;bool isprime(int n) {if(n<2) return false;int cnt=0;for(int i = 1; i <= n; i ++){if(n%i == 0) cnt++; // 求出因子的個數}if(cnt==2) return true; // 質數必定是有且僅有兩個因子的數else return false; }main() // 切記不能寫`int main()`! {int x;while(scanf("%lld", &x)==1){if(isprime(x)) puts("Y");else puts("N");}return 0; }這個大家都能明白吧
時間復雜度O(N2)~O(N^2)~?O(N2)?
這份代碼害的我信用值成了40了
2.2 判斷素數方法2
完全平方數剛剛說過
所有的合數都可以寫成x=y?zx=y*zx=y?z
a?a=ba*a=ba?a=b,那么a2=ba^2=ba2=b
b是完全 平方數
根據反比例函數,可以知道對于所有的合數x,min?(y,z)\min(y, z)min(y,z)都一定小于等于b\sqrt bb?,所以只用枚舉到b\sqrtb?就可以求出答案了。
小計倆:浮點類型不穩定,使用4\sqrt 44?可能等于1.9999991.9999991.999999,然后枚舉到1了。所以浮點數可以加上 一個eps。(eps的取值范圍要注意!一般取值為0.001)
#define int long long #include <cmath>const double eps = 0.001;bool isprime(int x) {if(x<2) return false;double end = 0.001 + sqrt(1. * x);for(int i = 2; (double)i <= end; i ++){if(x % i == 0) return false;}return true; }sqrt函數 我自己手寫一個看看
double sqrt(double x) // 二分 {if(x<0) return -1;double l = 0, r=1E9;int step=100;double best = -1.0;while(step--){double mid=(l+r)/2;if(mid * mid >= x) {best = mid;r = mid;}else{l = mid;}}return best; }大家注意一下,sqrt使用的是二分手段,所以時間復雜度≈O(log?N)~\approx O(\log N)~?≈O(logN)?。乘法只用O(1)O(1)O(1)就能解決。所以 乘法優化:
#define int long long #include <cmath>const double eps = 0.001;bool isprime(int x) {if(x<2) return false;for(int i = 2; i * i <= x; i ++){if(x % i == 0) return false;}return true; }時間復雜度:O(N?T)O(\sqrt N * T)O(N??T),T是數據的組數
還是太慢,過不了
怎么辦呢?
這個時候就需要使用miller_rabin算法求解了
miller_rabin算法依賴的是費馬小定理
an≡aa^n\equiv aan≡a
當然只有n是質數的時候才可以
但是,費馬小定理是有問題的,是一個假命題,QAQ(遺憾)😢
統計表明,在前10億個自然數中共有50847534個素數,而滿足2n-1 ≡ 1 (mod n)的合數n有5597個
如果用費馬小定理的逆命題來判斷一個正整數n是不是素數,在前10億個自然數中出錯的可能性為0.011%
這個出錯的可能性還是很高的,但是仍然可以用這個技巧來排除大量的合數,這種方法就是費馬檢測
我們可以檢測前面的8~12個質數,如果有一個不滿足,就返回false,全部滿足返回true
這樣出錯概率 就只有(111000)g(\frac{11}{1000})^g(100011?)g了。(g是檢測質數的個數)
在 long long 范圍內,經試驗,不會出錯
但是 有可能越界
使用__int128(洛谷的破綻,noip不允許使用)就能通過
快速乘會超時
#include <iostream> #include <cstdio> #include <cstdlib> using namespace std;typedef __int128 il;int list[8] = {2, 3, 5, 7, 19, 37, 53, 89};il ksc(long long x, long long y, long long z) {il xx = 1;il answer = xx * x * y % z;return answer; // if(y == 0) return 0; // il w = ksc(x, y/ 2, z); // w= (w + w) % z; // if(y & 1) // { // w = (w + x) % z; // } // return w; }long long ksm(long long x, long long y, long long z) {if(y == 0) return 1;il w = ksm(x, y /2 , z);w= ksc(w, w, z);if(y & 1){w = ksc(w, x, z);}return w; }bool miller_rabin(long long n,long long a) {long long d = n -1, r = 0;while((d & 1) == 0){d >>= 1;r ++;}il x = ksm(a, d, n);if(x== 1) {return true;}for(int i = 0; i <r ; i ++){if(x == n - 1){return true;}x =ksc(x, x, n);}return false; }bool isprime(long long n ) {if(n < 2) return false;for(int a= 0;a< 8; a ++){if(n == list[a]){return true;}if(n % list[a] == 0){return false;}if(! miller_rabin(n, list[a])){return false;}}return true; }int main() {long long x;while(scanf("%lld", &x) == 1){if(isprime(x)){puts("Y");}else{puts("N");}}return 0; }3. 總結
總結
 
                            
                        - 上一篇: GitHub项目管理维护实用教程
- 下一篇: nodejs: mkdirs 递归创建目
