1. 程式人生 > >NOIP複賽複習(十二)數論演算法鞏固與提高

NOIP複賽複習(十二)數論演算法鞏固與提高

一、數論

  

1.

 

整數、自然數(大於等於0的整數)、正整數(大於0的整數)、負整數、非負整數、非正整數、非零整數、奇數偶數。

 

2.整除性

 

a,bZ,如果存在cZ並且a=bc,則稱b|aba的因子,“|”表示能整除

 

3.質數

 

如果一個數,只有1和自身作為因子的數,叫做質數(素數)。

通論1:存在一個質數p,若p|ab,則p|a或者p|b

通論2:若p|a或者(p,a)=1pa的最大公因子為1),則p|a2

可以推出 p|a

通論3:用π(x)表示不超過x的質數的個數,可以證:limπ(x)lnx÷x=1,換種通俗說法就是:1~x的質數個數大約為x/lnx(證明時間複雜度時可以用)。

 

4.質數的判定

 

1)一個很多人都在用的辦法(判斷一個較小的數是否為質數):

bool prime(int x)//判斷質數時間複雜度:O(sqrt(x)),最多1012~1014

{

   if(x<2) return false;

   for(int a=2;a*a<=x;a++)

   {

        if(x%a==0) return false;//不是質數

   }

   return true;//是質數

}


2)運用費馬小定理: 

p為一個素數且p不是a的倍數,則有:ap-11(mod p)(不能從右邊推到左邊)

做法:多次選取a檢驗p是否滿足費馬小定理(說明p可能是質數,選的滿足條件的a越多,p為質數的可能性越大)。

時間複雜度為:

O(klogp),選取ka,判斷的過程用掉logp,總的加起來為klogp

特別地,這樣的演算法有缺陷,因為有Carmichael數的存在,可導致上述演算法給出一個錯誤的判斷,例如:56111051729,這三個數滿足費馬小定理,但是它們都是合數!

這裡給出1~10000Carmichael數:561110517292465282166018911

 

3Miller-Rabin演算法(判斷一個很大的數是否為質數):

由於Carmichael數的存在,並且Carmichael數是有無窮多個的,那怎麼辦?打表?肯定不行啊!所以就要加強這個演算法!

如果n為素數,取a<n,令n-1=d×2r,則要麼ad1(mod n),要麼存在一個數i滿足:0≤ir,使得:ad×2^i-1(mod n)一個數mod n=-1”可以表示為:一個數mod n=n-1”,同樣地也是不能從右邊推到左邊。

時間複雜度:O(klogn)

做法:多次選取a檢驗p是否滿足,則是質數的概率就大。(有個好訊息:不存在Carmichael數這樣的特殊情況!)

例如:12=3*22


int a[5]={3,7,11,23,37}//這裡選了鍾神喜歡的5個質數來檢驗是否滿足條件,如果不夠保險的話還可以多加幾個

bool Miller_Rabin(int n)//a[]中選出5a

{

   n-1=d*2^r;//n-1來確定dr

   for(int j=1;j<=5;j++)//這裡用了5個小於n的質數a來檢驗,用質數是因為效果更好!

   {

       if(pow(a[j],d)%n!=1)//不滿足第一個條件,pow為快速冪函式,pow(a,b)計算a^b

        {

            for(int i=0;i<r;i++)

            {

                if(pow(a[j],d*2^i)%n==-1)return true;//第二個條件

            }

            return false;

        }

   }

}


4)篩法(處理1~n區間內有哪些質數): 

基本做法:給出要篩數值的範圍sqrt(n),找出 sqrt(n)以內的素數p1,p2,p3,…,pk。先用2去篩,即把2留下,把2的倍數剔除掉;再用下一個素數,也就是3篩,把3留下,把3的倍數剔除掉;接下去用下一個素數5篩,把5留下,把5的倍數剔除掉;這樣不斷重複下去…… 

非線性篩法:

bool not_prime[1000000];//true表示不是質數,false表示是質數

not_prime[1]=true;//1不是質數

for(int a=2;a<=n;a++)

{

   for(int b=a+a;b<=n;b+=a)

   {

        not_prime[b]=true;

   }

}

時間複雜度:(1/1+1/2+1/3+1/4+…+1/n)*n=nlogn

 

下面給出了優化版篩法,時間複雜度為:nlog(logn)

演算法思路:如果當前這個數是合數,之前已經列舉過比它小的因子,在列舉這個小因子的時候,已經把這個合數的倍數覆蓋掉了,所以沒必要。

bool not_prime[1000000];//優化版非線性篩法

not_prime[1]=true;//1不是質數

for(int a=2;a<=n;a++)

{

   if(!not_prime[a])//如果是質數,進入迴圈,是合數就不進入

   {

        for(int b=a+a;b<=n;b+=a)

        {

            not_prime[b]=true;

        }

   }

}


線性篩法:

演算法思路:每個合數都由它最小的質因子篩掉(程式碼第12行)。一個合數會被拆成幾個質因子相乘,利用最小的質因子就可以把這個合數篩掉了,避免了重複篩的過程。

 

int not_prime[1000000];

int prime[1000000];//質數表

int prime_count=0;//質數的個數

memset(not_prime,0,sizeof(not_prime));

not_prime[1]=true;//1不是質數

for(int i=2;i<=n;i++)

{

   if(!not_prime[i]) prime[++prime_count]=i;//i放入質數表prime[]

   for(int j=1;j<=prime_count;++j)//列舉質數表中的每一個數

   {

        if(prime[j]*i>n) break;

        not_prime[prime[j]*i]=true;//翻倍,一個數×另一個數一定為合數

        if(i%prime[j]==0) break;

   }

}

時間複雜度:是線性的,接近於O(n)

 

5.最大公因數

 

1)歐幾里得演算法(輾轉相除法):

原理什麼的我就不說了,看程式碼YY一下就知道啦(詳見人教版高中數學必修三)。

 

int gcd(int a,int b)//歐幾里得演算法時間複雜度:O(loga)

{

   if(!b) return a;

   else return gcd(b,a%b);

}

int gcd(int a,int b)//簡化版歐幾里得演算法時間複雜度:O(loga)

{

   return b?gcd(b,a%b):a;//一行程式碼就是爽

}

 

2)擴充套件歐幾里得演算法: 

用來在已知的ab中求解一組xy,使得ax+by=gcd(a,b)成立(根據數論相關定理,這組解一定存在)

求解過程(引自P2O5dalaobloghttp://p2oileen.xyz/index.php/2017/06/07/exgcd/):

a>b,則有當b=0時,gcd(a,b)=a,此時x=1y=0

ab≠0時,設ax1+by1=gcd(a,b),因為gcd(a,b)=gcd(b,a%b),則一定有:bx2(a%b)y2=gcd(b,a%b)=gcd(a,b)=ax1+by1

所以將bx2+(a%b)y2=ax1+by1移項+整理可得:

ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2;

根據恆等定理:x1=y2;y1=x2-(a/b)*y2;

這樣我們就可以通過x2,y2遞迴求解x1,y1辣!

gcd不斷遞迴求解的過程中,總會有一個時刻b=0,所以遞迴是有終止條件的。

遞迴程式碼如下:

 

int Ex_Gcd(int a,int b,int&x,int &y)

{

   if(b==0)

   {

        x=1;

        y=0;

        return a;

相關推薦

NOIP複賽複習數論演算法鞏固提高

一、數論    1.數   整數、自然數(大於等於0的整數)、正整數(大於0的整數)、負整數、非負整數、非正整數、非零整數、奇數偶數。   2.整除性   設a,b∈Z,如果存在c∈Z並且a=bc,則

NOIP複賽複習字串演算法鞏固提高

一、Trie樹   1.定義: 通過字串建成一棵樹,這棵樹的節點個數一定是最少的。例如:4個字串"ab","abc","bd","dda"對應的trie樹如下: 其中紅色節點表示存在一個字串是以這個點結尾的。  一個性質:在樹上,兩個點u,

NOIP複賽複習基礎演算法鞏固提高

一、倍增演算法:   定義:用f[i][j]表示從i位置出發的2j個位置的資訊綜合(狀態) 一個小小的問題:為什麼是2j而不是3j,5j,…?因為,假設為kj,整個演算法的時間複雜度為(k-1)logk,當k=2時,時間複雜度最小。 這個演算法的三個應用:

NOIP複賽複習動態規劃鞏固提高

經典例題:數字金字塔(Luogu 1216)  寫一個程式來查詢從最高點到底部任意處結束的路徑,使路徑經過數字的和最大。每一步可以走到左下方的點也可以到達右下方的點。 我們現在這裡討論搜尋如何實現:  狀態:目前在第x行第y列 

NOIP複賽複習怎樣才能拿到高分?

摘要 考場策略和程式測試是資訊學競賽中非常重要的環節,很多優秀的選手在很多比賽中總是會在這兩個環節上犯下這樣和那樣的錯誤,導致得到的分數和實力不成正比,最後留下了無盡的遺憾。本文將探討一些這兩個環節上值得注意的地方,提出一些可行的方法,分享一些經驗,以此希望幫助選手們在比賽中發揮水平,減少失

資料結構複習之平衡叉樹及哈夫曼樹

平衡二叉樹需要保證在插入和刪除二叉樹結點時,任意結點的左、右子樹的高度差絕對值不超過1,所以平衡二叉樹或者為一棵空樹,或者為具有左子樹和右子樹都為平衡二叉樹的性質。插入和刪除時出現不滿足條件時可進行一定的調整,分為LL平衡旋轉、RR平衡旋轉、LR平衡旋轉、RL平衡杆旋轉。

Spark筆記整理:日誌記錄監控

提交 說明 默認 conf 分布 core view aps 版本 1 Standalone模式下 按照香飄葉子的文檔部署好完全分布式集群後,提交任務到Spark集群中,查看hadoop01:8080,想點擊查看某個已完成應用的歷史情況,出現下面的提示: Event log

Java開發筆記布林變數論道或非

在程式語言的設計之初,它們除了可以進行數學計算,還常常用於邏輯推理和條件判斷。為了實現邏輯判斷的功能,Java引入了一種布林型別boolean,用來表示“真”和“假”。該型別的變數只允許兩個取值,即true和false,其中true對應真值,而false對應假值。 如同數值變數擁有加減乘

深入理解 Java 虛擬機器Java 記憶體模型執行緒

執行緒安全 Java 語言中的執行緒安全 根據執行緒安全的強度排序,Java 語言中各種操作共享的資料可以分為 5 類:不可變、絕對執行緒安全、相對執行緒安全、執行緒相容、執行緒對立。 不可變 不可變的物件一定是執行緒安全的,如果共享資料是一個基本資料型別,那麼

Python學習筆記:lambda表示式函數語言程式設計

以Mark Lutz著的《Python學習手冊》為教程,每天花1個小時左右時間學習,爭取兩週完成。 --- 寫在前面的話 2013-7-22 21:00 學習筆記 1,lambda的一般形式是關鍵字lambda後面跟一個或多個引數,緊跟一個冒號,以後是一個表示

jdk原始碼解析——執行緒安全鎖優化

上一節我們說了Java記憶體模型與執行緒、那麼我們這節來了解一下執行緒安全與鎖優化 1 概述 在軟體業發展的初期,程式編寫都是以演算法為核心的,程式設計師會把資料和過程分別作為獨立的部分來考慮,資料代表問題空間中的客體,程式程式碼則用於處理這些資料,這種思維方式直接站在計算機的角度去抽象問題

NOIP複賽複習檔案讀寫數論模板

檔案讀入讀出 假設題目名為“add”,那麼資料夾名為“add”,c++程式名為“add.cpp”,讀入檔名為“add.in”,輸出檔名為“add.out”。四個的拼寫均不可有誤,包括大小寫差異。千萬不要除錯後就忘記修改檔案讀入讀出了。  #include<cstdio&

NOIP複賽複習競賽環境注意事項

一、比賽不提供紙質試題,只提供電子版試題檔案。 該檔案壓縮包儲存在計算機桌面上。監考人公佈密碼後,選手自行解密試題。 試題解壓密碼會影響一個人的心情,一定要一次輸對,注意大小寫,不要邊輸入邊檢查,要對自己有自信。 二、江蘇複賽選手上機可自選windows或linux作業系統。

NOIP複賽複習十三圖論演算法鞏固提高

一、圖的儲存   1、鄰接矩陣   假設有n個節點,建立一個n×n的矩陣,第i號節點能到達第j號節點就將[i][j]標記為1(有權值標記為權值),  樣例如下圖:   /*無向圖,無權值*/ i

NOIP複賽複習如何設計測試資料?

有些同學參加一次資訊學比賽之後,自我感覺非常不錯,但是測評結果成績卻並不理想。造成這種情況的原因有多方面,但是我認為其中不可忽視的一大原因就是在寫完程式之後,他們並不知道如何保證程式的正確性。在這裡,我就這個問題提出一點自己的看法。 先從考試的時間分配問題講起。很多同學覺得考試時間很充分,N

NOIP複賽複習STL演算法樹結構模板

STL演算法 STL 演算法是一些模板函式,提供了相當多的有用演算法和操作,從簡單如for_each(遍歷)到複雜如stable_sort(穩定排序),標頭檔案是:#include <algorithm>。常用STL 演算法庫包括:sort快速排序演算法、二分

NOIP複賽複習STL容器字串模板

STL容器 STL 容器是一些模板類,提供了多種組織資料的常用方法。常用的STL容器包括pair(組合)、list(列表,類似於連結串列)、vector(向量,類似於陣列)、priority_queue(優先佇列)、set(集合)、map(對映)、stack(棧)等,通過模板的引數

NOIP複賽複習演算法分析排序模板

演算法分析 演算法分析的目的是預測演算法所需的資源,如計算時間(CPU 消耗)、記憶體空間(RAM 消耗)、通訊時間(頻寬消耗)等,以及預測演算法的執行時間,即在給定輸入規模時,所執行的基本運算元量,或者稱為演算法複雜度。 演算法的執行時間取決於輸入的資料特徵,輸入

NOIP複賽複習程式對拍圖論模板

程式對拍 所謂“對拍”,顧名思義,就是讓兩者相互比對。所謂“兩者”,一是你要測試的程式,二是一個答案在該程式在一定範圍(時間/空間)內結果必定正確的程式(一般是用暴力求解的程式)。對拍一般需要造資料程式(data.exe),保證正確性的暴力對拍程式(test.exe)與測試程式(以moo.e

NOIP複賽複習讀寫外掛高精度模板

讀入輸出掛 讀入輸出掛就是逐個字元地讀入資料,從而讓讀入更加快速。輸出掛的原理也是一樣的,都是通過將輸出數字變成輸出字元以加快速度。當然輸入輸出外掛一般用在大量輸入輸出的情況下,這樣價效比才高一些,否則得不償失。 void Rd(int &res){  &nbs