Re prime 關於質數的演算法

2022-05-31 15:03:09 字數 3376 閱讀 7720

所有**若無說明,均採用快讀模板

關於質數,無非就兩大類:

判斷乙個數字是不是質數

找出[1,n]中所有的質數

先講1:

判斷x是不是質數

根據質數的定義,我們可以列舉所有小於x,大於1的正整數i。如果x%i==0,即i是x的因數,則x不是質數。

很明顯,乙個質數的因數是成對出現,且分別在sqrt(x)的兩邊。所以只要列舉到sqrt(x)即可。

bool judge(ll &x)

return1;

}

時間複雜度o(sqrt(n))

那麼,這裡插一句嘴——只要再從1~n列舉乙個數,不就可以得出1~n中所有的質數了嘛?!

(另外一種判斷質數的方法等會再講)

上面是指極限下1s能過的資料

bool judge(ll & x)//

n=1.5*10^6 這裡用il反而會慢0.01s

return1;

}ll n;

intmain()

return0;

}

時間複雜度o(nsqrt(n))

這裡只要把上面的那個n=1.5*10^6做乙個小小的優化(可以理解為記憶化)

其實,我們在嘗試列舉因數的時候,我們可以只要列舉質因數就好。

比如x=91

如果我列舉到i=2(某個質數)發現不能整除,那麼91當然也不能被2的倍數(質數的(2倍以上的)倍數是不是質數)整除啊

所以,judge()中的列舉i只需要列舉1~sqrt(x)中的質數即可。

使用乙個陣列儲存所有的質數吧。

ll his[1000000

];ll size;

bool judge(ll & x)//

n=4*10^6

return1;

}ll n;

intmain()

return0;

}

時間複雜度o(?)

接下來在看看

這次是埃氏篩(埃拉託斯特尼篩法)。

每找到乙個質數,就將它的倍數打上標記即可。

如果迴圈到某個數的時候,它沒有被打上標記

那麼它就一定是質數了,

就再拿它去更新它的倍數。

bool book[30000000

];ll prime[

30000000

];ll size;

ll n;

int main()//

n=3*10^7}}

return0;

}

時間複雜度o(nloglogn),已經非常接近於線性的了

(注意,雖然它的影象在10000以內的斜率看似比y=x還要小,但是我們比較的是其增長性,即:nloglogn的導數是單調遞增的)

可能一階導看不出來

紫色:nloglogn

紅色:f'(x)

藍色:f''(x)

好了,不管那麼多了,再看這次最強的篩法:尤拉篩 吧

n=3*10^7(***)

請先思考一下

為什麼埃氏篩會超過線性呢?

因為其實,乙個合數可能會被打上多次的合數標記

比如,列舉到2是質數的時候,會把4、6、8、10……打上標記

列舉到3是質數的時候,會把6、9、12……打上標記

然後6就被打了兩次標記。

有乙個更加嚴重的例子,一些合數甚至被打了很多次,這就浪費了很多次的機會,比如60

60這個數字被它的質因數們:2、3、5打了標記

而510510被它的質因數們:2、3、5、7、11、13、17打了標記。。。

浪費的時間就在這裡了。。。

那麼,尤拉篩:

#define maxn 50000000ll prime[maxn];

bool

book[maxn];

ll n,size;

intmain()

}return0;

}

一樣的套路。

首先一定要記憶化(儲存prime陣列)

其次無論當前的數是不是質數都要用這個數字i向後篩

但是僅僅篩到i%prime[j]==0,即i可以被從小到大的某個質數整除為止。

所以,對於乙個i,第二層for僅僅會篩到它的最小質因數倍的i

即比如i=5,就只會篩走2*5,3*5,5*5(然後5%5==0 -> break;)

所以每個數隻會被它的最小質因數篩——只會被篩一遍

所以它的時間複雜度是o(n)的

如果你還是不信:

#define maxn 50000000ll prime[maxn];

bool

book[maxn];

ll n,size;

intmain()

}return0;

}

驗證

被篩的數字是不會相同的。

咦?那這個演算法1s極限怎麼和高貴的埃氏篩相差無幾(明明是根本一樣好吧!)啊~

因為它有一定的常數(用到了取模),還一定要儲存質數(這個影響不大)

但是,在更大的資料下,尤拉篩才能展現出其真正的魅力!

比如說這個資料:

200'000'000(2*10^8,兩億)

看上去就很不錯

用埃氏篩來跑,需要3.682s左右

但是用尤拉篩,只需要2.943s左右!

(以上資料為無輸出資料,且僅用於對比,無實際意義)

所以以後考試的時候還是打埃氏篩吧——又簡單常數又小!

最後——

快速的大質數判斷,基於隨機演算法。

可以在o(klogn)內判斷乙個數字是不是質數,其中k為判斷的次數(稍後你就懂了),n是這個數字的大小。

我不太懂它的證明過程,但是我覺得我這麼菜,只要會用就好了……

以下是步驟:

f是乙個小質數,a是要測試的數字,d=a-1

去除d的因數2(即把d除以二直到d變成奇數)

計算t=f^d%a(快速冪)

迴圈直到d=a-1或t=1或t=a-1

這時如果t==a-1或者d是奇數,那麼可以粗劣地判斷a是質數

多選幾個f判斷,同時滿足的話,就可以說是質數了

關於質數的一些演算法

首先是基礎中的基礎,怎麼判斷乙個數是不是質數,直接試除就行了,只需要嘗試到sqrt n 因此時間複雜度o n 1 2 public static boolean isprime long number 其次是篩法,也就是從乙個範圍內篩選出質數,最直接的辦法是乙個個判斷,這顯然很複雜,因此有了篩法的概...

關於hash質數

最近我在做乙個專案,其中要用到乙個資料結構 hash table 雜湊表 以前只有理論知識,現在實卻發現很不簡單,所以寫下來和大家共分享。我們知道,雜湊表是乙個固定大小的陣列,陣列的每個元素是乙個鍊錶 單向或雙向 的頭指標。如果key一樣,則在一起,如果key不一樣,則不在一起。雜湊表的查詢是飛快的...

關於質數(素數)

素數的演算法 最基礎的演算法 n int input for i in range 2,n if n i 0 print n,可以被 i,整除 break else print n,是乙個質數 改進演算法,通過開方縮小整除範圍 n int input for i in range 2,int n 0...