日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MIT算法导论(一)——算法分析和引论

發(fā)布時間:2023/12/9 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MIT算法导论(一)——算法分析和引论 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 1 算法分析及引論
    • 1.1 算法
    • 1.2 排序
      • 1.2.1 插入排序
        • 1.2.1.1 插入排序原理
        • 1.2.1.2 時間復雜度
        • 1.2.1.3 漸進時間復雜度
        • 1.2.1.4 回到算法
      • 1.2.2 歸并排序
        • 1.2.2.1 歸并排序原理
        • 1.2.2.2 歸并排序時間復雜度

1 算法分析及引論

1.1 算法

算法是一門關(guān)注性能的學科,也就是說,我們致力于讓我們要做的事變得更快。讓我們提出一個問題,在程序設(shè)計中,什么比性能更重要?

成本(程序員的時間成本),可維護性,穩(wěn)定性(健壯性,是否老崩潰),功能性,安全,可擴展性。

既然這么多因素都比性能更重要,那我們?yōu)楹芜€要研究算法,到底是性能重要還是用戶的體驗更重要呢?

可以肯定的是,很多時候性能和用戶體驗是緊密聯(lián)系在一起的,很多時候沒有什么東西比干等著更難受了。

我們研究算法的緣由是:很多時候性能的好壞決定其是否可行,如我們對實時的要求,如果算法性能不快,那么就會導致無法追求實時的效果,實時當然就不可行了。或者說,如果它占用過多的內(nèi)存,它也是不可行的,因為硬件需求跟不上。總的來說:算法走在所有因素的最前沿

第二個關(guān)鍵是,算法是一種解釋性語言。其通用讓幾乎所有程序員都接收,它是一種讓程序最為簡潔的思考方式。我們有一個很好說明的例子:我們把算法比作貨幣,那么貨幣作為社會最底層的東西,如果你想要買吃的,買住的,都離不開貨幣。

第三個關(guān)鍵是,算法決定了一個程序的安全和用戶體驗的好壞。雖然C比Java快很多,java編程性能會損失三倍左右,但是人們還是喜歡用java來編寫程序,因為java的面向?qū)ο髾C制以及異常機制值得人們?yōu)橹冻觥_@也是為什么我們要提高性能,因為我們總是把性能分攤給其他的要素。

第四個關(guān)鍵是,我們對此樂此不疲。人們總是喜歡快的東西,比如火箭、滑雪,我們總喜歡這些快速的東西。

在某種意義上,上述這些簡單的問題是我們研究下列問題的指路明燈。

1.2 排序

讓我們來看看一個非常古老的問題,排序。排序包含了許多基本的算法。讓我們舉一個排序的例子:我們輸入一組序列a1,a2...ana_1,a_2...a_na1?,a2?...an?,按照需求排列后作為輸出。排列一般是使得如a1<=a2...<=ana_1<=a_2...<=a_na1?<=a2?...<=an?一般。按一定順序排序。

1.2.1 插入排序

1.2.1.1 插入排序原理

這里我們介紹第一個算法,即插入排序。通常我們描述算法用的是偽代碼而不是編程語言。偽代碼中包含有一些解釋性語言如英語或者中文,其能夠讓我們更清楚該算法包含的思想。

值得一提的是,用偽代碼描述算法時,我們通常會使用縮進,其相當于大多數(shù)語言中標注開始和結(jié)束的分隔符。好比Java和C中的大括號。事實上,歷史上還真有一些語言(例如python)是用縮進代表分隔,但是不是一種好的主義,因為在換頁的時候,你很難知道自己在哪一個嵌套的層級,而是用大括號就能夠很好的表示。

讓我們締造一個數(shù)組,如下所示:

這個數(shù)組通常是無序的,然而我們通過從從左到右指定一個變量,這個變量我們稱為鍵,他會從數(shù)組中提取數(shù)據(jù),然后和前面已經(jīng)排好的序列做比較,然后插入前面的序列,這也是為這么這個算法叫做插入排序的原因。

比較經(jīng)典的例子我認為是斗地主,在發(fā)牌時,我們時常會事先排好手中的牌,我們是一張一張拿起來的,然后將亂序的牌提取出來,然后插入前面已經(jīng)排序好的位置中。無論什么時候,左手的牌都是排好序的,而右手拿的牌都是即將要插入到排好序的牌中,并且插入位置符合其自身順序。

我們來看一下插入排序的偽代碼實現(xiàn):

INSERTION-SORT(A)for j←2 to length[A]do key←A[j]Insert A[j] into the sorted sequence A[1..j-1]i←j-1while i>0 and A[i]>keydo A[i+1]←A[i]i←i-1A[i+1]←key

看上面的偽代碼似乎有些費勁,可事實上,它與我們上面講述的別無二致。讓我們引入一個具體的例子來體會這個算法。

我們有一個序列:8 2 4 9 3 6

當我們執(zhí)行插入排序時,我們的首要任務(wù)是從第二個元素開始,依次提取鍵key,然后和前面排序好的序列做對比再插入。題意來看首先提取的是2,然后插入8之前。原序列則變?yōu)? 8 4 9 3 6。接著我們找到4,4和2 8進行比對,然后插入2和8之間,原序列變?yōu)? 4 8 9 3 6,以此類推按照排序規(guī)則循環(huán)移動。

讓我們借助數(shù)學工具來解剖該算法。如果輸入序列之前就有序,那么已排序序列后的排序工作量就大大減少。因為每次遍歷,它都是做上面循環(huán)往復的事。最壞的情況是該序列剛好是逆序,那么每個元素都將進行插入排序。

對應(yīng)在這道題上,這個例子總共有6個數(shù)據(jù),我們將其輸入的規(guī)模擴大到100000,那么一旦發(fā)生逆序,其時間開銷很大。為此在進行算法分析時,我們通常使用漸進時間復雜度,即假設(shè)輸入的規(guī)模為n,趨于無限,則運行時間可以看做是以n為映射的函數(shù)。

1.2.1.2 時間復雜度

上述引入的時間復雜度中,顯然有最好最壞的情況,最壞對應(yīng)算法時間的上限,其代表著對用戶的一種承諾,(是的尊貴的用戶我的程序是不會超過這個時間的)。而最好的情況對應(yīng)著算法時間的下限。我們通常關(guān)注算法的最壞時間復雜度。即我們要給出用戶保證,我們的程序總能做到這樣,而不是有時能這樣,我們通常會把輸入考慮成最壞的情況,對應(yīng)上面插入排序的例子,我們最壞的情況是逆序。

當然有時我們也會討論平均時間復雜度,這個時候T(n)就成了我們算法的時間期望值。那不禁有人會問,期望值是啥?

我們希望聽到一些比較數(shù)學味道的回答,所謂的期望實際上就是每種輸入的運行時間,乘以那種輸入出現(xiàn)的概率。這是一種連續(xù)型的思考方式,我們可以理解為加權(quán)平均。

那我們?nèi)绾沃捞囟ㄝ斎朐诮o定情況下的概率是多少?有時候我們并不知道,所以我們需要給出一個假設(shè)了,一個有關(guān)輸入的統(tǒng)計分布的假設(shè),否則期望時間無從談起。

最常見的假設(shè)是均勻分布,即所有規(guī)模為n的輸入情況都是等可能地出現(xiàn)。

最后我們想講講最好時間復雜度,我們稱之為假象!~bogus(我在大聲地笑哈哈),沒啥用!因為最好的情況不可能出現(xiàn)。如果一個序列已經(jīng)排好序,我們還有必要去排序嗎,哈哈哈。除非你想要cheat!欺騙!在你得到一個特定輸入下,然后你得到一個比較滿意的結(jié)果后欺騙自己,對!這就是我想要的效果,那你就可以考慮它。

1.2.1.3 漸進時間復雜度

回到這個算法,我們不禁發(fā)問,插入排序的最壞情況時間是多少?

最簡單的回答是,這取決于你的計算機。你用的是超級計算機還是腕表那么大的計算機,算力會大相徑庭。但是通常來說,我們都是比對兩個算法在同一臺機器上的表現(xiàn),即相對速度。當然也有人關(guān)注絕對速度,我們猜想真有某種算法能無論在什么機器上運行都表現(xiàn)得更好嗎?

以上的回答實際上會對我們最開始的問題造成混淆,這不得不提到我們的大局觀(big idea)了,這也是為什么算法涉獵如此廣泛的原因。我們應(yīng)該使用抽象的眼光來看待事物,從復雜的情況中提取關(guān)鍵的因素對其分析。這就是所謂的漸進分析。漸進分析的基本思路是:忽略掉依賴于機器的常量,不去檢查其因素,而是關(guān)注算法本身時間的增長。

使用漸進分析,自然要引入相應(yīng)的數(shù)學符號。這里我們使用的是thetaΘ\ThetaΘ來表示漸進時間復雜度。實際上theta寫起來很簡單,你需要做的是,棄去它的低階項,并忽略前面的常數(shù)因子。

比如一個算法花費時間是:3n3+90n2?5n+60463n^3+90n^2-5n+60463n3+90n2?5n+6046,那我們就要使用高數(shù)中采取的抓大頭準則,使的3n33n^33n3后面的項全部拋棄,然后再把3拋棄掉即可。所得為Θ(n3)\Theta(n^3)Θ(n3)

上述情況需要知道的是,假設(shè)有一個算法的時間復雜度是Θ(n2)\Theta(n^2)Θ(n2),那么它遲早比Θ(n3)\Theta(n^3)Θ(n3)速度要快。因為當n逐漸增大時,指數(shù)會造成指數(shù)爆炸,使得比最大項還小的常數(shù)項無法動搖這個最終的結(jié)果。這對應(yīng)到計算機的優(yōu)劣上,即使你的Θ(n2)\Theta(n^2)Θ(n2)算法是在慢速計算機上運行,總有一天它也會超越在快速計算機上運行的Θ(n3)\Theta(n^3)Θ(n3)算法。

當然,站在工程角度上看,n有時候太大沒有節(jié)制是不行的,這會導致我們的計算機無法運行該算法,這也是為什么有時候我們對一些慢速算法比較感興趣,因為它們在n較少的時候能夠保持較高的速度。所以僅僅會做算法分析并不能使你成為高手,你需要保持每天編程,并且在實際編程中運用,知道其什么時候相關(guān)什么時候不相關(guān)。

1.2.1.4 回到算法

這時候我們可以來分析一些剛才的插入排序算法了。就像我們說的我們關(guān)注其最壞時間復雜度。即輸入為逆序。

INSERTION-SORT(A)for j←2 to length[A]do key←A[j]Insert A[j] into the sorted sequence A[1..j-1]i←j-1while i>0 and A[i]>keydo A[i+1]←A[i]i←i-1A[i+1]←key

在這其中明顯有嵌套循環(huán),我們實際上關(guān)注循環(huán),因為其他常數(shù)操作無關(guān)緊要,我們是對其漸進分析。第一個循環(huán)中是2到j(luò),而內(nèi)部循環(huán)是從無需的抽出元素對前面的有序序列做插入操作,實際上是由2到j(luò)次比對。也就是說,時間復雜度是θ(n2n^2n2)。

那這個時間復雜度到底快不快呢?對于小的n它確實挺快,但是對于巨大的n它就不是那么回事了,所以在下面我會給你一個更快的算法。

1.2.2 歸并排序

1.2.2.1 歸并排序原理

讓我們還是用抽象逐步講到具體。我們給出一個對于A[1…n]的歸并排序。

  • 如果給定序列是1,自然不用排序,因為序列中只有一個元素。
  • 否則進行遞歸,遞歸的每一層都會將序列一分為二,直到分出一個元素為止,然后對其一對一排序。
  • 最后將排序后的序列全部重組。

這里我們出現(xiàn)一個新名詞——遞歸,這個知識點實際上有點小難,我推薦你去看一下我寫的博客數(shù)據(jù)結(jié)構(gòu)雜談番外篇——搞懂遞歸的小文章_弄鵲-CSDN博客。

上述歸并算法關(guān)鍵的部分在于歸并。歸并實際上是使用了分治法的思想,先將大問題分解成小問題,然后再將小問題求解后合并結(jié)果。

假設(shè)我現(xiàn)在有這么一個數(shù)組 8 4 5 7 1 3 6 2,如果采用歸并算法,我們是這么做的:

在我們使用遞歸進入最深層次(即不可再分的第三層)時,我們開始進行治,我們治的方式是用兩根數(shù)組指針分別指向分開的兩個序列,如下圖所示:

這個操作的時間復雜度是θ(n),因為我們所花時間都是在合并n次上。實際上分解并不耗費時間,因為每次遞歸分解都是分解一次。而每次合并要移動指針。

1.2.2.2 歸并排序時間復雜度

讓我們來看看整個歸并排序的時間復雜度是多少。實際上歸并排序總時間=子序列排好序時間+合并時間。

現(xiàn)在我們提前使用遞歸樹方法來解決這里的問題,關(guān)于詳細我們會在下一小節(jié)敘述:

假設(shè)我們有n個數(shù)的序列,那么第一次分就可以分為兩個n/2的序列。

T(n)=2?T(n/2)+合并時間T(n) = 2*T(n/2)+合并時間T(n)=2?T(n/2)+。又由于合并的時候按照我們上面所說是循環(huán)比較兩個指針指向值的大小,所以復雜度為n。則我們可以改寫T(n)=2?T(n/2)+θ(n)T(n) = 2 *T(n/2)+θ(n)T(n)=2?T(n/2)+θ(n)。當然了,對于n = 1的情況,T(n) = 1,這個情況我們一般忽略,因為他對遞歸解沒有影響。我們用顯式的c?nc*nc?n來替換隱式的θ(n),然后把其寫出樹狀結(jié)構(gòu)如下所示:

c* n指的是多個常數(shù)階步驟所消耗的時間復雜度。常數(shù)階的時間復雜度在進行漸進表示時通常省略,所以我們說他是隱式的,而我們用顯式的c*n來表示這些步驟。因為我們在這個時候是要計算T(n)而不是Θ(n)\Theta(n)Θ(n)

也就是說,這個遞歸樹的每一層實際上都是cn。如第二層cn = T(n/2)+T(n/2)。那我們要計算遞歸樹中所有結(jié)點所消耗的時間復雜度,即全部相加,用cn乘上樹的高度即可。樹的高度為lgn,所以在遞歸樹中歸并算法花費的時間復雜度為cn?lgncn*lgncn?lgn。這時候我們?nèi)绻鬂u進時間復雜度,只需忽略常數(shù)項,所以由此可得歸并排序時間復雜度為Θ(nlgn)\Theta(nlgn)Θ(nlgn)

在計算機這一塊領(lǐng)域中,lgn就是log2nlog_2nlog2?n。我們設(shè)n除2分了一次,除2除了x次,那么最終n/2xn/2^xn/2x = 1,通過指對互換進行反解,即x=log2nx = log_2nx=log2?n。也就是說樹的高度是log2nlog_2nlog2?n。而lgn只不過是log2nlog_2nlog2?n的常數(shù)倍,由漸進表示可知這個不影響。所以你要拿lgn表示log3、log4nlog_3、log_4nlog3?log4?n也是可以的。

總結(jié)

以上是生活随笔為你收集整理的MIT算法导论(一)——算法分析和引论的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。