擴展歐幾里得算法求逆元_從輾轉相除法到求逆元,數論算法初體驗

今天是算法和數據結構專題的第22篇文章,我們一起來聊聊輾轉相除法。

輾轉相除法又名歐幾里得算法,是求最大公約數的一種算法,英文縮寫是gcd。所以如果你在大牛的代碼或者是書上看到gcd,要注意,這不是某某黨,而是指的輾轉相除法。

在介紹這個算法之前,我們先來看下最大公約數問題。

暴力解法

這個問題應該很明確了,我們之前數學課上都有講過。給我們紙筆讓我們求都沒有問題,分解因數找下共同的部分,很快就算出來了。但是用代碼實現怎么做呢?

用代碼實現的話,首先排除分解因數的方法。因為分解因數復雜度太高了,也很容易想明白,既然要分解因數,那么首先需要獲得一定量的質數吧。有了質數之后還要遍歷質數,將整數一點一點分解,顯然很麻煩,還不如直接暴力了。暴力解法并不復雜,我們直接從1開始遍歷,記錄下來同時能夠整除這兩個數的最大數即可。我們暴力的范圍也不大,從1到n。

很容易寫出代碼:

def gcd(a, b):    ret = 0    for i in range(min(a, b)):        if a % i == 0 and b % i == 0:            ret = i    return ret

這個很簡單,也許你可能還會想出一些優化,比如說首先判斷一下a和b之間是否有倍數關系,如果有的話直接就可以得到結果了。再比如說我們i的遍歷范圍其實可以不用到min(a, b),如果a和b沒有倍數關系的話min(a, b) / 2就可以了。這些都是沒有問題的,但是即使加上了這些優化依然改變不了這是一個O(n)算法的本質。

比如說a是1e9,b是1e9-1,毫無疑問這樣的做法會超時。

輾轉相除法

接下來就輪到正主——輾轉相除法出場了,這個算法在《九章算術》當中曾經出現過,叫做更相減損術。不管叫什么,原理都是一樣的,它的最核心本質是下面這個式子:

662fe7964dcf4c9fb4ab4514a4ef25dc.png

這個式子就是著名的歐幾里得定理,這里的r可以看成是a對b取余之后的結果,也就是說a和b的最大公約數等于b和r的最大公約數。這樣我們就把a和b的gcd轉移成了b和r,然后我們可以繼續轉移,直到這兩個數之間存在倍數關系的時候就找到了答案。

在我們寫代碼之前,我們先來看一下這個定理的證明。

我們假設u同時整除a和b,顯然這樣的u一定存在,因為u至少可以是1,所以:

27a90241d420348deca533bcbf55bc52.png

所以可以得到u也整除r,同樣我們可以證明能夠整除b和r的整數也可以整除a。我們假設v可以同時整除b和r:

569261f395bd3b173eb515ba52d5376c.png

這樣我們就得到了v也可以整除a。也就是說a和b的每一個因子都是b和r的因子,同樣b和r的每一個因子也是a和b的因子,那么可以得出a和b的最大公約數就是b和r的最大公約數。

以上就是歐幾里得定理的簡單證明,如果看不懂也沒有關系,我們記住這個定理的內容就可以了。

接下來就是用代碼實現了,我們把這個公式套進遞歸當中非常容易:

def gcd(a, b):    if a < b:        a, b = b, a            if a % b == 0:        return b    return gcd(b, a % b)

我們首先判斷了a和b的大小關系,如果a小于b的話,我們就交換它們的值,保證a大于b。如果a和b取模的結果為0,那么說明a已經是b的倍數了,顯然它們之間的最大公約數就是b。

但其實我們沒有必要判斷a和b的大小,我們假設a小于b,那么顯然a % b = a,于是會遞歸調用b和a % b,也就是b和a,也就是說算法會自動調整兩者的順序。這么一來,這個代碼還可以進一步簡化,只需要一行代碼

def gcd(a, b):    return a if b == 0 else gcd(b, a % b)

所以聽到有人說自己用一行代碼實現了一個算法,不要覺得它在裝逼,有可能他真的寫了一個gcd。

拓展歐幾里得

拓展歐幾里得本質上就是gcd,只是在此基礎上做了一定的拓展,從而來解決不定方程。不定方程就是ax + by = c的方程,方程要有解充要條件是(a, b) | c,也就是說a和b的最大公約數可以整除c

也就是說求解ax + by = gcd(a, b)的解。假如說我們找到了這樣一組解x0和y0,那么x0 + (b / gcd) * t和y0 - (a / gcd) * t也是方程的解,這里的t可以取任意整數。

我們代入算一下即可:

40d141955cfc92b5b9efc42b2d286966.png

所以我們求出了這樣的x0和y0之后就相當于求出了無數組解,那么這個x0和y0怎么求呢,這就需要用到gcd算法了。

我們觀察一下gcd算法的遞歸代碼,可以發現算法的終止條件是a=gcd,b=0。對于這樣的a和b來說,我們已經找到了一組解使得ax+by=gcd,比如很明顯,x=1,y=0。實際上y可以為任何值,因為b=0。

我們回到遞歸的上一層的a和b,假設我們已經求出了b和a%b的最大公約數,并且求出了一組解x0和y0。使得b*x0 + (a%b)* y0 = gcd。那么我們能不能倒推得到a和b時候的解呢?

因為a % b = a - (a/b)*b,這里的/是整除計算的意思,我們代入:

d19005e325d3abd4d084921a284d27ff.png

顯然對于a和b來說,它的一組解就是y0和x0 - (a/b)*b*y0,我們把這幾行計算加在代碼當中即可,非常簡單:

def exgcd(a, b, x=1, y=0):    # 當b=0的時候return    if b == 0:        return a, x, y    # 遞歸調用,獲取b, a%b時的gcd與通項解    gcd, x, y = exgcd(b, a%b, x, y)    # 代入,得到新的通項解    x, y = y, x - a//b*y    return gcd, x, y

這里我建議大家不要死記代碼,都去推導一下遞歸的這個推導公式。這個公式搞明白了,即使代碼記不住也沒有關系,后面臨時用到的時候再推導也可以。不然的話,即使背下來了代碼也不記得什么意思,如果碰到的場景稍微變動一下,可能還是做不出來。

逆元與解逆元

拓展歐幾里得算法我們理解了,但是好像看不出來它到底有什么用。一般情況下我們也碰不到讓我們計算通解的情況,但其實是有用的,用的最多的一個功能就是計算逆元

在解釋逆元之前先來看一個問題,我們有兩個數a和b,和一個模底數p。我們可以得到(a + b) % p = (a%p + b%p)%p,也可以得到 (a - b)%p = (a%p - b%p)%p。甚至還可以得到 (a*b)% p =(a%p * b%p) %p,這些都是比較明確的,但是(a / b) % p = (a % p / b % p) % p,這個式子成立嗎?

最后的式子是不成立的,因為模數沒有除法的傳遞性,我們可以很方便舉出反例。比如a是20, b是10,p是4,(a/b)%p=2,而(a %p / b%p) % p = 0。

這就導致了一個問題,假如說我們在一連串計算當中,由于最終的結果特別大,我們無法存儲精確的值,希望存儲它關于一個模底數取模之后的結果。但是我們的計算當中又涉及除法,這個時候應該怎么辦?

這個時候就需要用到逆元了,逆元也叫做數論倒數。它其實起到一個和倒數類似的效果,假設a關于模底數p的逆元是x,那么可以得到:ax = 1 (mod p)

所以我們想要算 (a / b) % p,可以先求出b的逆元假設是inv(b),然后轉化成(a%p * inv(b)%p)%p。

這個逆元顯然不會從天上掉下來,需要我們設計算法去求出來,這個用來求的算法就用到拓展歐幾里得,我們下面來看一下推導過程。

假設a和b互質,那么gcd(a, b) = 1,代入:

8c13056f641e53614470cfc6b049dae8.png

所以x是a關于b的逆元,反之可以證明y是b關于a的逆元。

這么計算是有前提的,就是a和b互質,也就是說a和b的最大公約數為1。否則的話這個計算是不成立的,也就是說a沒有逆元。那么整個求解逆元的過程其實就是調用拓展歐幾里得的過程,把問題說清楚花了很多筆墨,但是寫成代碼只有兩三行:

def cal_inv(a, m):    gcd, x, y = exgcd(a, m)    # 如果gcd不為1,那么說明沒有逆元,返回-1    return (x % m + m) % m if gcd == 1 else -1

在return的時候我們對x的值進行了縮放,這是因為x有可能得到的是負數,我們把它縮放到0到m的范圍當中。

逆元的求解方法除了拓展歐幾里得之外,還有一種算法,就是利用費馬小定理。根據費馬小定理,在m為質數的時候,可以得到

e19045f617dccc53e7282fe6206d241e.png

等式兩邊同時除以a,也就是乘上a的逆元,可以得到:

ab0620130af82059e0108c74958487b1.png

也就是說我們求出a^{m-2}然后再對m取模就得到了a的逆元,我們使用快速冪可以很方便地求出來。但是這個只有m為質數的時候才可以使用。

總結

今天我們聊了歐幾里得定理聊了輾轉相除法還聊了拓展歐幾里得和求解逆元,雖然這些內容單獨來看并不難,合在一篇文章當中量還是不小的。這些算法底層的基礎知識是數論,對于沒有參加過競賽的同學來說可能有些陌生,但是它也是算法領域一個很重要的分支。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/258740.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/258740.shtml
英文地址,請注明出處:http://en.pswp.cn/news/258740.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[翻譯] Fast Image Cache

https://github.com/path/FastImageCache Fast Image Cache is an efficient, persistent, and—above all—fast way to store and retrieve images in your iOS application. Part of any good iOS applications user experience is fast, smooth scrolling, and Fast Image …

centos 安裝 MatConvNet (gpu)

1. 安裝準備 matlab2017a &#xff0c;參考&#xff1a;《centos 安裝matlab2017a(無root權限)》 GCC 4.8(支持c11) 鍵入&#xff1a;sudo yum install gcc gcc-c &#xff08;建議sudo裝&#xff09; 至少CUDA 7.5&#xff0c;&#xff08;本人選擇cuda8.0&#xff…

php練習 租房子

題目要求 1.封裝類 <?php class DBDA {public $fuwuqi"localhost"; //服務器地址public $yonghuming"root";//用戶名public $mima"";//密碼 public $dbconnect;//連接對象//操作數據庫的方法//$sql代表需要執行的SQL語句//$type代表SQL語…

【SHARE】WEB前端學習資料

參考資料&#xff1a;https://github.com/karlhorky/learn-to-program學習網站&#xff1a;http://www.codecademy.com/learn https://www.codeschool.com/ 制作網站&#xff1a;https://webmaker.org/zh-CN/explore JavaScript2015&#xff1a;https://esdiscuss.org/topic/ja…

python軟件安裝和使用方法_aws cli的安裝及使用(內含python的安裝方法)

liunx環境(使用bundled installer)&#xff1a;1.wget https://s3.amazonaws.com/aws-cli/awscli-bundle.zip //下載bundled installer2.unzip awscli-bundle.zip3.sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws如果你沒有sudo權限或者是你想在當…

centos 安裝boost(caffe需要)

安裝 由于安裝caffe&#xff0c;要求boost的版本在1.55以上&#xff0c;而服務器上的剛好是1.54,所以進行了重裝。 參考&#xff1a;《CentOS 7下編譯安裝Boost_1_57_0 》 不過由于pycaffe需要boost.python,因此需要在./b2時修改為./b2 –stage debug 才可以。而不能去掉py…

JAVA正則表達式介紹和使用

本文引用自 http://www.cnblogs.com/android-html5/archive/2012/06/02/2533924.html 技術博客 1.Java中在某個字符串中查詢某個字符或者某個子字串 Java代碼 String s "Shang Hai Hong Qiao Fei Ji Chang";    String regEx "a|F"; //表示a或F Pat…

集合框架中的接口及其實現類

Collection&#xff1a;集合層次中的根接口&#xff0c;JDK沒有提供這個接口直接地實現類。Set&#xff1a;不能包含重復的元素。SortedSet是一個按照升序排列元素的Set。List&#xff1a;是一個有序的集合&#xff0c;可以包含重復的元素。提供了按索引訪問的方式。Map&#x…

C# 多線程 Parallel.For 和 For 誰的效率高?那么 Parallel.ForEach 和 ForEach 呢?

還是那句話&#xff1a;十年河東&#xff0c;十年河西&#xff0c;莫欺少年窮。 今天和大家探討一個問題&#xff1a;Parallel.For 和 For 誰的效率高呢&#xff1f; 從CPU使用方面而言&#xff0c;Parallel.For 屬于多線程范疇&#xff0c;可以開辟多個線程使用CPU內核&#x…

cuda、cudnn相關問題鏈接

1. cuda&#xff0c;cudnn安裝 <caffe安裝系列——安裝cuda和cudnn> 2. 查看已有的cuda等版本 cuda 版本 cat /usr/local/cuda/version.txtcudnn 版本 cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 23. cudnn的安裝&#xff0c;路徑和版本問題 http://…

bigdecimal 小于等于0_圖解小于 K 的兩數之和

點擊藍色“五分鐘學算法”關注我喲加個“星標”&#xff0c;天天中午 12:15&#xff0c;一起學算法作者 | P.yh來源 | 五分鐘學算法題目描述 題目來源于 LeetCode 上第 1099 號問題&#xff1a;小于 K 的兩數之和。給你一個整數數組 A 和一個整數 K&#xff0c;請在該數組中找出…

用STS創建Maven的Web項目轉

右鍵New——>other——》Maven——》Maven Project 彈出框中點擊Next&#xff0c;在Filter中寫上&#xff1a;webapp. 然后在下面的框中選擇org.apache.maven.archetypes&#xff0c;點擊Next 在新彈出的窗口中寫上Group Id和Artifact Id&#xff0c;Finish即可成功。 創建完…

img超出div width時, jQuery動態改變圖片顯示大小

參考&#xff1a; 1. http://blog.csdn.net/roman_yu/article/details/6641911 2. http://www.cnblogs.com/zyzlywq/archive/2012/02/23/2364292.html轉載于:https://www.cnblogs.com/carlo/p/4584008.html

《TOGAF 9.1IT企業架構》什么是企業IT架構

2. 什么是企業IT架構 現在有越來越多的企業IT架構定義。在這一章&#xff0c;你會學習到一些企業IT架構的方法&#xff0c;我們會給你深入解釋一種實用的方法&#xff0c;這種方法視企業架構師為CIO(譯注&#xff1a;CIO首席信息官&#xff0c;是負責一個公司信息技術和系統所有…

pdf 深入理解kotlin協程_Kotlin協程實現原理:掛起與恢復

今天我們來聊聊Kotlin的協程Coroutine。如果你還沒有接觸過協程&#xff0c;推薦你先閱讀這篇入門級文章What? 你還不知道Kotlin Coroutine?如果你已經接觸過協程&#xff0c;但對協程的原理存在疑惑&#xff0c;那么在閱讀本篇文章之前推薦你先閱讀下面的文章&#xff0c;這…

編譯py-faster-rcnn的問題匯總及解決方法

按照官網 的提示&#xff0c;我開始安裝faster rcnn&#xff0c;但是出現了很多問題&#xff0c;我將其匯總了起來&#xff0c;并提出了解決辦法。 先說明一下我的配置&#xff1a; python : anaconda2linux: centos 6.9 安裝faster rcnn請先參考&#xff1a;《cuda8cudnn4 F…

openWRT自學---針對backfire版本的主要目錄和文件的作用的分析整理

特別說明&#xff1a;要編譯backfire版本&#xff0c;一定要通過svn下載:svn co svn://svn.openwrt.org/openwrt/branches/backfire&#xff0c;而不能使用http://downloads.openwrt.org/backfire/10.03/中的源碼包&#xff1a;backfire_10.03_source.tar.bz2 結合文檔《OpenWr…

自然語言交流系統 phxnet團隊 創新實訓 項目博客 (五)

3DMax方面所涉及的專業知識&#xff1a; &#xff08;1&#xff09;一下的關于3DMax中對于人物的設計和操作均需要在對3DMax基礎知識熟練掌握的情況下進行的。 &#xff08;2&#xff09;骨骼架設&#xff1a;首先對導入到3DMax中的人物模型進行架設骨骼…

linux 安裝python-opencv

三種方法&#xff1a; 1. pip 安裝 &#xff1a; pip install opencv-python &#xff0c;最新版為opencv3安裝后>>> import cv2 >>> print cv2.__version__參考&#xff1a;http://www.cnblogs.com/lclblack/p/6377710.html 2. anaconda的conda安裝 ,可以指…

《你的燈亮著嗎》讀書筆記Ⅲ

轉載于:https://www.cnblogs.com/yue3475975/p/4586220.html