成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Algorithms, Princeton, Coursera課程整理與回顧

Luosunce / 1168人閱讀

摘要:除特別標(biāo)注外,文章非原創(chuàng)插圖全部來自課程相關(guān)資源。劇透預(yù)警內(nèi)容包含大作業(yè)的關(guān)鍵問題解法分析。為的返回值此方案下,判斷只需要對應(yīng),判斷使用結(jié)果準(zhǔn)確,判斷檢測的對應(yīng)是否為。更新此方法已確定違反的。

Princeton的算法課是目前為止我上過的最酣暢淋漓的一門課,得師如此夫復(fù)何求,在自己的記憶徹底模糊前,愿對這其中一些印象深刻的點(diǎn)做一次完整的整理和回顧,以表敬意。

注:
這是一篇更關(guān)注個(gè)人努力與完成任務(wù)項(xiàng)目過程相關(guān)的文章,內(nèi)容集中于課程背后值得提到的部分,不會(huì)介紹課程基本信息及學(xué)習(xí)時(shí)必讀的設(shè)定要求等部分,敬請諒解。
在學(xué)習(xí)一門課程的時(shí)候考慮為什么這么教是個(gè)人習(xí)慣,我會(huì)嘗試給出一些解讀,為什么這門課這么屌(awesome)。
優(yōu)化無止境,越學(xué)習(xí)才能越深刻地感受自己的無知,即使是作業(yè)內(nèi)已提到的額外內(nèi)容我也并沒有一一探究完整,這里只是謙卑地盡力記錄自己的努力,并無意與誰比較,如有新的進(jìn)展還會(huì)回來更新。
除特別標(biāo)注外,文章非原創(chuàng)插圖全部來自課程相關(guān)資源。
每一小節(jié)的作業(yè)題目鏈接到specification,“√”鏈接到checklist,“〇”鏈接到code下載。
這里提供一個(gè)全文完整的資源樹。
劇透預(yù)警:
內(nèi)容包含大作業(yè)的關(guān)鍵問題解法分析。

算法,第一部分

I. Union Find與Percolation √ 〇


作為第一部分開學(xué)第一課,作業(yè)Percolation可謂精妙:

既沒有復(fù)雜的語法使用(僅數(shù)組操作),又著實(shí)比在基礎(chǔ)語言層面上升了一個(gè)檔次;

漂亮的visualizer動(dòng)畫效果激勵(lì)著初學(xué)者完成任務(wù);

強(qiáng)大的autograder功能初次展現(xiàn),評價(jià)算法的主要管道一目了然,每一微秒每一字節(jié)都很重要;

針對學(xué)習(xí)迅速的同學(xué)還隱含了一個(gè)很大的挑戰(zhàn):在僅使用一個(gè)WeightedQuickUnionUF對象的前提下,解決backwash問題。

下面主要聊聊backwash:

問題的核心源自virtual top/bottom,一個(gè)強(qiáng)行被導(dǎo)師在課程視頻中提到的優(yōu)化方法,于是天真的同學(xué)們(我)就去開心地實(shí)現(xiàn)這個(gè)看似無辜而又性能楚楚動(dòng)人的方法,卻毫不了解導(dǎo)師在下一節(jié)中馬上提出的backwash問題是何物,還覺得這種低級錯(cuò)誤怎么可能會(huì)發(fā)生:

java-algs4 PercolationVisualizer percolation/input10.txt

……

(我發(fā)誓這就是我看到的東西……)
把痛碎的心小心拼回,認(rèn)真思索一番后確定問題根源出在virtual bottom,一個(gè)顯而易見的解決方案便浮現(xiàn)在眼前:使用兩個(gè)UF,一個(gè)使用vb一個(gè)不用,判斷percolate用前者,判斷full用后者,解決!

Raw Score 100.00 / 100.00

然而checklist的一句話又引起了我的注意:

However, many students consider this to be the most challenging and creative part of the assignment (especially if you limit yourself to one union-find object).

加上autograder特別溫馨的提醒“bonus failed”,我不得不重新開始審視這個(gè)問題。
后來的事實(shí)證明,直到課程結(jié)束沒有一個(gè)問題再讓我如此頭疼。經(jīng)過了一段可謂長征式的思考,加之在forum的討論中得到了一點(diǎn)靈感(多加利用Find()),最終形成并實(shí)現(xiàn)了解決方案,方案核心如下(corner case另行處理):

將原方案中表示open狀態(tài)的boolean數(shù)組改為byte數(shù)組,設(shè)定規(guī)則如下:初始化的默認(rèn)值0代表blocked site,賦1代表open site,賦2代表與尾行相連的open site;

每open一個(gè)site,如果位于尾行則賦2,否則賦1;

分別對每個(gè)鄰接site檢測:如任何一方的root site對應(yīng)byte值為2,將雙方Union后的root site設(shè)為2。(root為Find()的返回值)

此方案下,判斷open只需要對應(yīng)byte>0,判斷full使用UF結(jié)果準(zhǔn)確,判斷percolates檢測virtual top的root site對應(yīng)byte是否為2。

Raw Score 101.25 / 100.00 
Test 2 (bonus): Check that total memory <= 11 N^2 + 128 N + 1024 bytes
==> passed

對這個(gè)作業(yè)來說,上面這三行的成績凝聚了太多。

II. Resizing Array & Linked Lists與Randomized Queues and Deques √ 〇

第二周課程正式展開,但在作業(yè)深度上相對稍有下降,根據(jù)指定的性能和API要求,選擇適當(dāng)?shù)膶?shí)現(xiàn)方式(動(dòng)態(tài)數(shù)組、單、雙向鏈表),實(shí)現(xiàn)Randomized Queue和Deque,主要訓(xùn)練基本的算法分析和編程嚴(yán)謹(jǐn)性(完善處理corner-case),但并無特別的難點(diǎn)。有兩個(gè)小技巧可以提出:鏈表可使用sentinel node(s)使代碼更簡潔(checklist已提),寫client時(shí)可先shuffle再插入(bonus)。

2018更新:最近同學(xué)在做這道題時(shí)發(fā)現(xiàn)autograder已明確指出不可以使用數(shù)組,這就使得這個(gè)bonus變得有趣多了:
仔細(xì)想一下,既然API已經(jīng)把我們非常嚴(yán)格地限制在只能遍歷一遍輸入的情況下,而我們?nèi)匀幌M鸕Q不超過k個(gè)元素,那么在正常讀入k個(gè)元素后面對下一個(gè)元素我們只有兩個(gè)選擇,dequeue一個(gè)舊元素然后enqueue新元素,或直接忽略這個(gè)新元素;所以問題的關(guān)鍵就變成了一個(gè)概率問題,兩個(gè)選擇應(yīng)當(dāng)如何權(quán)衡,才能保證每個(gè)元素最終留在RQ中的概率相等呢?
相對應(yīng)地,autograder也有完整的假設(shè)檢驗(yàn)(t-test)來實(shí)際檢測,小伙子你真的做對了嗎?
不必多說,擼起袖子上吧,撿起高中的排列組合知識(shí),仔細(xì)分析一下,你算出的忽略當(dāng)前新元素的概率應(yīng)該是這樣:
$$mathrm{P}_{discard}=frac{mathrm{C}_{n-1}^k}{mathrm{C}_n^k}=frac{n-k}{n}$$
plug it in and feel the magic! 不得不說這個(gè)作業(yè)的設(shè)計(jì)之精巧,看似非常不起眼的一個(gè)小bonus,竟然也幾乎是蘊(yùn)含著Monte Carlo方法的一些基礎(chǔ)啟發(fā),真是妙啊。

過程中一并學(xué)習(xí)了Java的Generics,Iterable,Iterator概念,在大學(xué)課程部分已學(xué)習(xí)掌握得比較熟練,可不談,the result speaks:

Raw Score 100.19 / 100.00 
Test 3 (bonus): Check that maximum size of any or Deque or RandomizedQueue object created is <= k
==> passed

結(jié)合兩次作業(yè)特性,可看出一些值得一提的點(diǎn):

作業(yè)任務(wù)要求與說明往往面面俱到細(xì)致入微,在給定公有API框架及其對應(yīng)時(shí)間空間復(fù)雜度的基礎(chǔ)上,結(jié)合課程視頻知識(shí)內(nèi)容,這種“受指導(dǎo)”的編程過程變得十分清晰,尤其是其中API的設(shè)計(jì),風(fēng)格簡潔高效到自成一家,我認(rèn)可自己的代碼風(fēng)格已很簡潔,而algs4.jar讓我第一次看到了更高;

給定充足的測試材料,包括各類corner-case或相對有趣的輸入數(shù)據(jù)(如sedgewick60.txt),及合適情況下生動(dòng)的可視化工具(如PercolationVisualizer.java),都使得學(xué)生可以全力,高效,有動(dòng)力地,將精力集中在核心算法本身上。

III. Sorting與Pattern Recognition - Collinear Points √ 〇

本周的課程介紹了兩大經(jīng)典排序:Mergesort和Quicksort,自然作業(yè)也與sorting緊密相關(guān)。Collinear Points(找出所有4個(gè)或以上共線的點(diǎn)構(gòu)成的點(diǎn)集)是第一個(gè)在運(yùn)行時(shí)見證“好的算法”與“暴力算法”直觀差別的作業(yè),這樣的對比能給學(xué)生帶來深刻的影響:忙了好久,為了什么?

上圖為暴力算法(~N^4)求解100個(gè)數(shù)據(jù)點(diǎn)(input100.txt),下圖為基于排序的算法(~N^2logN)求解1423個(gè)數(shù)據(jù)點(diǎn)(rs1423.txt)
測試數(shù)據(jù)還有很多。圖中示例對快速算法給定數(shù)據(jù)量龐大了不止10倍,運(yùn)行時(shí)間卻與不到1/10數(shù)據(jù)量下的暴力算法接近;對上圖數(shù)據(jù),快速算法基本看不到找線的動(dòng)畫過程就完成了;對下圖數(shù)據(jù),暴力算法在可以忍受的時(shí)間里基本找不到幾條線。
這樣的運(yùn)行結(jié)果可以給學(xué)生一種非常好的對自己努力的掌控感,正是這樣一個(gè)個(gè)美妙的瞬間使學(xué)生能以最好的狀態(tài)與飽滿的好奇心在算法之路上繼續(xù)走下去。
作業(yè)說明中對核心算法的描述非常清晰,應(yīng)當(dāng)特別小心的技術(shù)點(diǎn)(浮點(diǎn)誤差、正負(fù)零等等)也都在checklist中予以強(qiáng)調(diào),因而實(shí)現(xiàn)難度不算很大,其中使用了Java的Comparable與Comparator,排序過程調(diào)用Arrays.sort(),詳細(xì)思考問題理清關(guān)系后,實(shí)現(xiàn)非常自然,前提是編程基本功必須扎實(shí)。
值得提到的點(diǎn):

checklist鼓勵(lì)初學(xué)者開始編寫快速算法時(shí)先不要擔(dān)心5個(gè)或以上的點(diǎn)共線的情況,而實(shí)際上對基本功扎實(shí)的同學(xué),從開始便考慮這個(gè)問題更為合適;

checklist提到compare()和FastCollinearPoints類可以完全避免浮點(diǎn)數(shù)用整形運(yùn)算實(shí)現(xiàn),我想到的唯一符合要求(Point.java注釋中規(guī)定不得依賴toString())的方法是,對Point類的compareTo()方法返回值增加意義(不單純返回正負(fù)1),以獲取兩點(diǎn)的坐標(biāo)之差(因題目給定坐標(biāo)取值范圍0-32767,可在返回值低兩位字節(jié)存儲(chǔ)x坐標(biāo)差, 高兩位字節(jié)存儲(chǔ)y坐標(biāo)差),再利用坐標(biāo)差判斷斜率是否相等。雖依然完全符合題目要求,但已有一點(diǎn)奇技淫巧之嫌,并且不一定能夠通過autograder(評分時(shí)會(huì)替換Point類去測試FastCollinearPoints),不多討論。(更新:此方法已確定違反FastCollinearPoints的API。)

最終實(shí)現(xiàn)版本拿到了100分,未發(fā)現(xiàn)關(guān)于bonus的討論。

IV. Priority Queue與8 Puzzle √ 〇

剛剛講解完使用Binary Heap實(shí)現(xiàn)的Priority Queue,便迎來了這周的8 Puzzle——使用PQ實(shí)現(xiàn)A*解決NP難問題,所以——重點(diǎn)在優(yōu)化咯。
(Image Source Here)
左邊是目標(biāo)狀態(tài),右邊是起始狀態(tài);對,就是那個(gè)——小時(shí)候功能手機(jī)上的蒙娜麗莎。
隨著學(xué)習(xí)的深入,這次作業(yè)的復(fù)雜度有了一個(gè)明顯的上升,但這門課最美妙的地方之一也就在這里:面臨的問題越來越復(fù)雜,而導(dǎo)師給出的指導(dǎo)思路依然保持最大程度的簡潔優(yōu)雅,每一步驟的設(shè)定都直指問題核心,并在實(shí)現(xiàn)功能基礎(chǔ)上提供非常豐富的優(yōu)化選擇,以及每個(gè)優(yōu)化會(huì)帶來的影響分析。

"It"s a funny feeling being took under the wings of a dragon. It"s warmer than you think." —— Gangs of New York

整個(gè)問題相對比較復(fù)雜,但經(jīng)過給定API的劃分和給定的限制后,問題變成了實(shí)現(xiàn)一個(gè)個(gè)目標(biāo)明確的小方法而已,其中最復(fù)雜的不過是A*的實(shí)現(xiàn),而周到合理的封裝和PQ的使用也使得這個(gè)過程無比自然,在此之前我從未意識(shí)到我可以將A*在如此短小清晰的代碼中實(shí)現(xiàn):(源碼如此,僅省略了聲明過程)

while (!pq.min().board.isGoal()) { 
    cur = pq.delMin();
    for (Board nb : cur.board.neighbors()) {
        if (cur.prev != null && nb.equals(cur.prev.board)) continue;
        pq.insert(new Node(nb, cur.moves+1, cur));
    }
}

PQ使用了Manhattan Priority作為Comparator,Node為封裝Board的單鏈表節(jié)點(diǎn),結(jié)束后獲取最短路徑只需取PQ中最小Node,利用prev指針遍歷。
解決8 Puzzle這一NP難問題的核心代碼便完成了。
當(dāng)然,在其余部分還是有不少值得提到的點(diǎn):

用char[]存儲(chǔ)一個(gè)Board的狀態(tài)比用int[][]好太多,但使用前需要詳細(xì)測試將char做數(shù)字使用與直接用int的區(qū)別;

Board的equals()方法只需比較char[]構(gòu)成的String即可,早期因圖省事直接比較了toString()的返回值(one-liner),造成了很大的性能損失,最后尋找問題來源也費(fèi)了不少功夫(教訓(xùn):看似再簡單的部分也要在腦袋里多轉(zhuǎn)一轉(zhuǎn)再下手,devil in the details,…,you name it);

Solvability: 這篇文章描述的解決方案應(yīng)為checklist所述方案,但需要脆弱地依賴toString()反推Board內(nèi)容,在本期課程的API設(shè)定中已被明確禁止,要求使用兩個(gè)同步A*的方案解決(最好使用一個(gè)PQ),但未來session可能會(huì)在兩方案對應(yīng)API設(shè)定間切換,所以我都實(shí)現(xiàn)了一遍,上面出現(xiàn)的A*代碼來自文章所述方案;

實(shí)現(xiàn)Priority的cache時(shí)需要稍多做思考,直覺想到的第一方案是儲(chǔ)存在Node中與A*過程配合,而這將使A*代碼迅速腫脹,且沒有很好地利用更多規(guī)律:如checklist所指出,對相鄰Board每次重新計(jì)算Priority其實(shí)也有很多重復(fù)工作,我的做法是將cache過程作為Board類一個(gè)私有構(gòu)造函數(shù),構(gòu)造相鄰Priority只需做對應(yīng)增量操作即可,以最簡潔的手段達(dá)到了目的。

我的最終測試文件及結(jié)果在這里,運(yùn)行于i5-2450 (8G),結(jié)尾幾個(gè)復(fù)雜的4x4開始因內(nèi)存不足報(bào)錯(cuò),論壇查到大概需要5-6G超出了我的空閑內(nèi)存,即使要測也會(huì)用到虛擬內(nèi)存,因而結(jié)果參考意義不大。
對于這個(gè)作業(yè),努力的最終匯報(bào)便是,看著終端中puzzle一個(gè)個(gè)被正確解決沾沾自喜,然后到相關(guān)的拓展材料中去體驗(yàn)無知,發(fā)現(xiàn)更大的世界。

V. Search Trees與Kd-Trees √ 〇

這一周的Balanced Search Trees課程令我印象深刻。在進(jìn)入作業(yè)討論前,我希望先聊聊這次的課程內(nèi)容:
在前一周的課程中就已引入了二叉樹的概念,一個(gè)無比經(jīng)典的數(shù)據(jù)結(jié)構(gòu),這我在大學(xué)課程中便已了解到,只是認(rèn)識(shí)非常淺薄,并從未,也從未認(rèn)為我能夠,動(dòng)手實(shí)現(xiàn)過。
可如果說在上一周的學(xué)習(xí)中彌補(bǔ)了我的這個(gè)問題,對二叉樹有了更深刻的理解,那么這一周的課程徹底顛覆了曾經(jīng)心中幼稚的見解,所謂質(zhì)變,我應(yīng)該是在這里體會(huì)到的:

從小學(xué)四年級開始在少年宮學(xué)習(xí)BASIC到初中的PASCAL,以致雖然后來停止學(xué)習(xí),但因?yàn)榻佑|時(shí)間遠(yuǎn)早于同齡人而在此方面經(jīng)歷了各種優(yōu)勢,但從小在我心中“程序”,以及它背后所代表的機(jī)械邏輯始終被認(rèn)為是“就那么回事兒”的。我天真地認(rèn)為自己早已掌握“程序”界的終極真理,無非就是嚴(yán)絲合縫一步不差地執(zhí)行、再執(zhí)行,只要“機(jī)械”性地認(rèn)真下去,便沒有什么解決不了的,以至于從小就在性格中造就了一面“徹頭徹尾的唯物+實(shí)用主義者”,不相信任何世間美好。

有這句天真的話在前,接著慢慢成長,讀書,行路,閱人,歷事,自然會(huì)學(xué)到原來這個(gè)世界真的不是那樣,開始理解愛與人性,開始理解這個(gè)世界的美妙。加上比較幸運(yùn),因而“唯心+浪漫主義者”的一面在我的性格里也發(fā)展得很好。但大學(xué)開始繼續(xù)學(xué)習(xí)計(jì)算機(jī),多少,對“程序”這一徹頭徹尾“唯物+實(shí)用”精神的產(chǎn)物,還是抱著一些看法的。

結(jié)束這一周的課程學(xué)習(xí)后,我改變了這個(gè)看法。
課程開始先講解了2-3 search trees,一種引入了新規(guī)則樹型結(jié)構(gòu)。這是一個(gè)“理解容易,實(shí)現(xiàn)復(fù)雜”的Symbol table方案,它可以對任何輸入數(shù)據(jù)保證樹形結(jié)構(gòu)的平衡以達(dá)到保證各項(xiàng)操作logN的復(fù)雜度,而規(guī)則卻足夠簡單到可以在課堂中很快描述清楚,可以算是課程中第一個(gè)令我驚艷的算法。
這時(shí)我想:果然在一步步深入后算法變得高深莫測了起來呢,這個(gè)2-3 tree,理解起來容易,要實(shí)現(xiàn)起來可真是困難吶。已經(jīng)不是二叉樹那么簡單的東西了,以后是不是只會(huì)更難啊,看來我遇到了一個(gè)屏障,這是不是我能走到的最遠(yuǎn)了啊。(開始有一點(diǎn)點(diǎn)擔(dān)心)
直到視頻結(jié)尾出現(xiàn)關(guān)于implementation的討論:

Bottom Line: Could do it, but there"s a better way.

緊接著下一節(jié)就是我們的主角:左傾紅黑二叉樹(LLRB)。
它只做到了一件事:將2-3 tree帶回了二叉樹世界,卻僅對樸素的二叉樹做極其微小的改動(dòng)——每個(gè)節(jié)點(diǎn)增加1 bit,用以表示“顏色”,加之無比簡潔的引導(dǎo),便在現(xiàn)實(shí)世界中實(shí)現(xiàn)了原先只是構(gòu)想的2-3 tree幾乎全部的優(yōu)點(diǎn)。
紅黑樹本身就是70年代Sedgewick教授參與提出的,而LLRB是由他一手提出的極其簡潔的紅黑樹實(shí)現(xiàn)版本,尤其是它的insertion,在課堂上作為重點(diǎn)講解,僅在原樸素二叉樹實(shí)現(xiàn)代碼基礎(chǔ)上,增加了3個(gè)小工具函數(shù)(左旋、右旋、翻轉(zhuǎn))和遞歸插入過程中的4行代碼(如圖),便完成了所有工作。

在徹底理解LLRB后,我想我被征服了。程序不再僅僅是徹頭徹尾“唯物+實(shí)用”精神的產(chǎn)物,而是我們理解世界的工具,也見證著我們改變世界過程,它與其他一切事物一樣,可以充滿真正的美。
我想可以這樣評價(jià)紅黑樹背后的思想:

它就像人類從蒙昧?xí)r代開啟智慧的一道光,將原來松散無序的組織變得優(yōu)雅而高效,并且還尊重了所有舊時(shí)代的傳統(tǒng);

也正是因?yàn)樽鹬貍鹘y(tǒng),才使得新的秩序得以穩(wěn)定存在;

新的秩序完整了傳統(tǒng),成為了傳統(tǒng)當(dāng)中,最為亮麗的部分。

這樣的成果也不是憑空出現(xiàn)的:

勇于跳出傳統(tǒng)的框框,用自由的視角審度;(2-3 tree已不是二叉樹)

回到現(xiàn)實(shí)在一點(diǎn)一滴中細(xì)膩體驗(yàn);(發(fā)現(xiàn)除繁瑣的直接實(shí)現(xiàn)外其他實(shí)現(xiàn)方式的可能性)

不妄自菲薄出身,用尊重的態(tài)度行動(dòng)。(選擇回到經(jīng)典二叉樹模式,試圖在舊傳統(tǒng)上建立新秩序)

知行合一,至此可謂正途。

同為插入255個(gè)隨機(jī)節(jié)點(diǎn),左圖為樸素二叉樹,右圖為左傾紅黑二叉樹。

作為第一部分的最后一個(gè)作業(yè),kd-tree其實(shí)難度沒有它看起來那么大。課程部分已很完整地討論過很多種經(jīng)典的樹形結(jié)構(gòu)(分析+實(shí)現(xiàn)),到這個(gè)階段學(xué)生應(yīng)已對樹形結(jié)構(gòu)非常熟悉了,實(shí)現(xiàn)kd-tree已是水到渠成。

同樣的,編程嚴(yán)謹(jǐn)性在這個(gè)作業(yè)中變得更加重要——情況相比前幾周又要復(fù)雜一些,從零編寫一個(gè)特定(且不簡單的)規(guī)則下運(yùn)行的樹形結(jié)構(gòu),主要功能是高效地插入與查找;
這次的作業(yè)同樣包含了一個(gè)暴力算法的版本(使用默認(rèn)的紅黑樹結(jié)構(gòu)),除了在執(zhí)行效率上可以做比較外,還提供了一個(gè)重要的軟件工程思路:面臨一個(gè)較為復(fù)雜,而優(yōu)化空間很大的問題時(shí),先實(shí)現(xiàn)一個(gè)最簡單的暴力算法版本檢測結(jié)果(往往可以非常簡便地實(shí)現(xiàn)),再考慮進(jìn)階的算法與數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)性能優(yōu)化,并在實(shí)現(xiàn)期間,暴力算法可以作為檢驗(yàn)算法正確性最直接的參考。
實(shí)現(xiàn)過程中值得提到的點(diǎn):

節(jié)點(diǎn)的奇偶性是一個(gè)不小的麻煩,編寫需要十分謹(jǐn)慎,我的做法是在Node類中添加了一個(gè)boolean用以表示奇偶,相信一定有更好的方法(不存儲(chǔ)RectHV),還會(huì)回來探索;

Nearest Neighbor的查找過程需要思考清楚,剪枝的界限十分清晰,尤其是剪裁其中一個(gè)子樹的條件:如果本子樹中找到的最近點(diǎn)與給定點(diǎn)距離 已小于 給定點(diǎn)到另一子樹矩形區(qū)域的距離(點(diǎn)到點(diǎn)距離比點(diǎn)到矩形距離還近時(shí)),才能跳過另一子樹的遍歷過程。


左圖為Nearest Neighbor,右圖為Range Search。(根據(jù)鼠標(biāo)狀態(tài)信息)
圖中紅色部分為暴力算法運(yùn)行結(jié)果,藍(lán)色部分為kdtree運(yùn)行結(jié)果;在增加數(shù)據(jù)量后多帶帶測試,暴力解法效率明顯下降。

算法,第二部分


在一個(gè)學(xué)期的辛苦努力后,帶著滿滿的收獲與日益成熟的頭腦,第二部分正式開始了。

“相信經(jīng)過了第一部分的學(xué)習(xí)同學(xué)們都會(huì)有很大的提高,短暫休息后也恢復(fù)了飽滿的學(xué)習(xí)熱情,那么我有理由相信你們準(zhǔn)備好了接受點(diǎn)真正的考驗(yàn)?!?
—— 視頻中永遠(yuǎn)不會(huì)提到的導(dǎo)師內(nèi)心獨(dú)白(歷經(jīng)折磨后的腦補(bǔ))

第二部分在課程與作業(yè)兩個(gè)方向的難度都比第一部分提升了一個(gè)檔次。好在不巧被導(dǎo)師言中,我的確有非常好的提升感和新一輪的熱情。
所以,放馬過來吧。

VI. Directed Graphs與WordNet √ 〇

第一周便直奔主題,開啟了數(shù)據(jù)結(jié)構(gòu)領(lǐng)域的又一大領(lǐng)域:圖。
課程中出現(xiàn)了一個(gè)很有趣的分類值得提到,是形容問題難度的:

任何程序員都能夠解決。

典型的勤奮的算法課學(xué)生能夠解決。

雇傭一個(gè)領(lǐng)域?qū)<摇?/p>

非常難。

沒有人知道難度。

已證明不可能解決。

并舉出了一些符合條件例子來說明這些分類,當(dāng)然可以料到的,有很多難度2的例子。
這針雞血打得,嘖嘖嘖,不留痕跡。
很好。諸位紅著眼的同學(xué),準(zhǔn)備變身吧。

第一周的作業(yè)是WordNet,少了第一部分作業(yè)多圖多動(dòng)畫的喧鬧,不再為搏人眼球而吵吵嚷嚷,卻在安靜的terminal和腦中的抽象之海中展開了一場貨真價(jià)實(shí)的戰(zhàn)斗。
因文字方面中西文化背景截然不同,導(dǎo)致WordNet的概念不太方便簡單地描述清楚,但作業(yè)說明中已解釋得比較詳細(xì),這里只放一張示意圖:

整個(gè)作業(yè)可以分為三大部分:選擇合適的結(jié)構(gòu)存儲(chǔ)結(jié)構(gòu)復(fù)雜的詞典及其網(wǎng)絡(luò)關(guān)系(基本功),實(shí)現(xiàn)正確高效的廣度優(yōu)先遍歷計(jì)算SAP(難點(diǎn)),和在此基礎(chǔ)上一層一層更細(xì)致的優(yōu)化(進(jìn)階);
本次也是checklist優(yōu)化部分唯一標(biāo)注optional的作業(yè),因?yàn)榈拇_實(shí)現(xiàn)起來有難度;分布如上所注。
詞典(synsets)為ID與單詞的多對多關(guān)系(一詞多號(hào),一號(hào)多詞,釋義僅供參考無需讀?。?,上下位關(guān)系(hypernyms)是以ID為單位的樹形結(jié)構(gòu)(圖中的箭頭關(guān)系),可以明顯看出hypernyms使用有向圖表示,synsets存儲(chǔ)方式取決于訪問需求,因程序中正反都會(huì)使用(以詞求號(hào),以號(hào)求詞,求詞存在),我選擇了空間換時(shí)間:

    private ArrayList> synset; // 以號(hào)求詞(獲取SAP中的ancestor對應(yīng)的單詞)
    private HashMap> ids; // 以詞求號(hào)(獲取索引單詞的ID以計(jì)算SAP)
    private HashSet nouns; // 求詞存在(API需求功能)
    private SAP sap; // 有向圖

文件讀取過程稍繁瑣,編程習(xí)慣要良好,保持謹(jǐn)慎不會(huì)錯(cuò)。
接著是SAP的實(shí)現(xiàn),這里周旋的余地很大,一定要認(rèn)真思考問題本質(zhì),并多考慮一些特殊情況檢測自己的想法,再開始下手去做,并如checklist所強(qiáng)調(diào),不要輕易嘗試直接實(shí)現(xiàn)優(yōu)化版本,因?yàn)榧?xì)節(jié)繁瑣不易考慮周全,而可能帶來的潛在問題非常多,每實(shí)現(xiàn)一部分都要做周全的測試確保無誤才行。(盡管最終成功的優(yōu)化,也可能僅僅是幾行的改變而已)
這里要提到的點(diǎn)(細(xì)節(jié)):

在checklist提到的三個(gè)優(yōu)化選擇中,我選擇了相對保守,但工作良好的優(yōu)化方式:每次的搜索初始化使用HashMap構(gòu)造函數(shù);正確實(shí)現(xiàn)了兩端同步運(yùn)行的BFS,并提供了提前結(jié)束的出口;只緩存了單個(gè)ID(單個(gè)節(jié)點(diǎn)與單個(gè)節(jié)點(diǎn))的查詢結(jié)果;

對單ID緩存的存儲(chǔ)方式,我的symbol table使用了一種很直接的無重合又簡單的hash計(jì)策:給定查詢ID為v與w,總ID數(shù)為V,則查詢結(jié)果的編號(hào)記為:

(V-1)+(V-2)+(V-3)+...+v+(w-v),化簡后為(V-1)v-(vv+v)/2+w。

提前結(jié)束BFS的出口開始時(shí)沒有考慮清楚,就暫時(shí)沒有添加,提交的版本拿到了103.12;后來仔細(xì)思考,發(fā)現(xiàn)了這部分余地,僅增加了一行代碼,分?jǐn)?shù)便提高了不少;

SAP同時(shí)支持DAG與DCG(有環(huán)圖),仔細(xì)分析如下這個(gè)特殊情況可能有助思考:(給定節(jié)點(diǎn)1與8,求其SAP)

(Made with Graphviz)

BFS如由節(jié)點(diǎn)1先開始,則輪到第4次迭代時(shí),節(jié)點(diǎn)1遍歷,發(fā)現(xiàn)共同祖先節(jié)點(diǎn)5,路徑距離為4+3=7,但此時(shí)不能停止搜索;實(shí)際SAP的共同祖先為1,路徑距離為0+4=4,是在第4次迭代的節(jié)點(diǎn)8遍歷部分被發(fā)現(xiàn)的。

在這門課程的通用說明中便已提示過,不要把a(bǔ)utograder當(dāng)作debug的工具,以省去自己發(fā)現(xiàn)問題的過程;我必須保持誠實(shí):至少,對于我這樣編程時(shí)基本“實(shí)用”精神至上的性格來講,因?yàn)閍utograder在各個(gè)方面都真的很完善,很多時(shí)候這是一條很難遵守的規(guī)定,所以我也體會(huì)到了這樣做的壞處:對于溫室中的作業(yè)來說有便利的autograder幫助簡直美好,但real world problem永遠(yuǎn)是殘酷的;在這個(gè)作業(yè)中,我想我失去了一些很寶貴的機(jī)會(huì),利用充足的測試材料親自去到那些數(shù)據(jù)的最深處游蕩,去體會(huì)探索算法的優(yōu)劣,這個(gè)過程簡直是最迷人的部分(如上示例);意識(shí)到這些后,有一種作弊的愧疚感;每個(gè)人都應(yīng)當(dāng)正視它們,然后作出自己的選擇。
最終成績?yōu)?06.25,具體Bonus點(diǎn)可以看這個(gè)文件。
守得云開見月明。

VII. Shortest Paths與Seam Carving √ 〇

本周作業(yè)算是一個(gè)“輕松有趣”的短暫休息;輕松到checklist都不知道該寫點(diǎn)什么好了……
在第一部分結(jié)束后休息的階段,因?yàn)槭值胗浲瓿蛇@門課作業(yè)的快感,于是到booksite轉(zhuǎn)悠了好久,又完成了一些額外的項(xiàng)目;
(完整項(xiàng)目下載:N-Body,Barnes-Hut (很美),Atomic Nature of Matter)
而緊接著在booksite推薦的Nifty Assignment中便見到了這個(gè)作業(yè),感覺很是驚艷,但還沒有動(dòng)手做,沒想到這么快就又見到它了。

(取自源SIGGRAPH2007介紹視頻,2008推出改進(jìn)版)
作業(yè)中值得提到的點(diǎn)其實(shí)不多,大部分功能的實(shí)現(xiàn)都非常直接,中間加入了一點(diǎn)圖像處理的入門知識(shí),同樣也非常好理解,重點(diǎn)是最短路徑的BFS:

對于固定值邊界的處理比較繁瑣,需要仔細(xì)處理;

對很多圖像處理問題,雖處理對象大體上還符合“圖”的定義,但因?yàn)楹芏鄨D像固有的因素,會(huì)將問題簡化得多,不必再使用重量級的Digraph類,就事論事地解決問題即可;

要找到能量最小的seam需要對整個(gè)圖像計(jì)算路徑距離,我的做法是維護(hù)distTo(最近距離)和edgeTo(最近距離對應(yīng)“父節(jié)點(diǎn)”)兩個(gè)數(shù)組,遍歷全圖后在最后一行(列)找到distTo的最小值即為所求seam的尾元素,通過追蹤edgeTo即可得到所求seam。

energy的復(fù)用是一個(gè)小難點(diǎn),需要認(rèn)真考慮每移除一列或一行seam后,哪些情況下的點(diǎn)應(yīng)該被重新計(jì)算,參考作業(yè)說明的vertical-seam.png、horizontal-seam.png或下圖去分析,可能會(huì)有幫助:

我想到的轉(zhuǎn)置的目的是為了在縱橫兩個(gè)方向都能利用System.arraycopy()的高效,開始時(shí)我沒有做這項(xiàng)優(yōu)化,是手寫了removeHorizontalSeam()的這個(gè)過程,因已達(dá)到了滿分要求便沒有再優(yōu)化。

VIII. MaxFlow與Baseball Elimination √ 〇

MaxFlow將是本課程接觸的最復(fù)雜的圖論問題了。別這樣,我才剛剛興奮起來……
同樣地,作業(yè)Baseball Elimination是一個(gè)非常巧妙的Maxflow應(yīng)用,甚至都與“流量”沒有任何關(guān)系:
根據(jù)實(shí)時(shí)賽季數(shù)據(jù),通過建立流量模型,判斷當(dāng)前局勢是否已出現(xiàn)被“數(shù)學(xué)上淘汰”的隊(duì)伍(已無法再獲得冠軍);

雖然算法上十分有趣,但個(gè)人并不喜歡這道題背后某個(gè)方向的思維:對辛苦奮戰(zhàn)的運(yùn)動(dòng)員來說,每場比賽都是戰(zhàn)斗,每個(gè)細(xì)節(jié)的表現(xiàn)才定義了自己,不是由一群snobbish的“科學(xué)家”說你被“數(shù)學(xué)上淘汰”了,你就真的毫無希望了;
當(dāng)然同時(shí)要明白這只是“黑暗版”的理解,對于專業(yè)選手來講任何信息都有其價(jià)值,在賽前以最清楚的數(shù)據(jù)明確自己的處境,以更好地調(diào)整戰(zhàn)略、激勵(lì)人心,再參與比賽,才算是真正在現(xiàn)實(shí)世界用出了好算法的最大價(jià)值。
Again, all in all,要nerdy地?zé)釔?,但不要做nerd。
同如修行的和尚,功德要回向眾生,而禪喜,留給自己就好。

本次作業(yè)在細(xì)節(jié)上有一些非常簡單的小技巧可以幫助簡化實(shí)現(xiàn)(簡潔性也是checklist提示的,參考代碼不到200行,我的版本為150行左右),但可能不是第一次思考時(shí)就能想到:

在讀取文件數(shù)據(jù)時(shí)就可額外計(jì)算(保存)一些后期需要的數(shù)據(jù):目前榜首隊(duì)伍與獲勝場數(shù)(用于簡單淘汰),和以雙方隊(duì)伍為單位的比賽場次(用于建立FlowNetwork的頂點(diǎn)數(shù));

"Do not worry about over-optimizing your program because the data sets that arise in real applications are tiny." 對我的思路,這意味著無需考慮FlowNetwork的復(fù)用問題——一個(gè)早期造成很多痛苦的優(yōu)化嘗試;

FordFulkerson類的使用實(shí)際上使問題僅剩下了建模部分,且在說明中已解釋得非常詳細(xì),故實(shí)現(xiàn)十分直接。

IX. Tries與Boggle √ 〇

然而Boggle卻是第二部分課程作業(yè)中,最有挑戰(zhàn)性的一個(gè)。(因?yàn)槲覜]有拿到Bonus T_T)

“The goal on this assignment is raw speed—for example, it"s fine to use 10× more memory if the program is 10× faster. ”

(“明確地”暗示使用Trie)
從動(dòng)手開始做本次作業(yè),到基本感到?jīng)]有優(yōu)化余地,我的代碼經(jīng)歷了大概4-5個(gè)大版本的改變,小的修正與優(yōu)化更是不計(jì)其數(shù),即使是這樣一個(gè)固定情形下的問題,也令我好好體會(huì)了一次,程序迭代更新、不斷修正與優(yōu)化的過程。

本次任務(wù)相當(dāng)于編寫圖示游戲的AI部分——BoggleSolver,而要拿到滿分以至Bonus,需要一秒內(nèi)解決成千上萬個(gè)Boggle Board。
下面是我經(jīng)歷的迭代過程的簡短介紹:(只選出了幾個(gè)典型版本,括號(hào)內(nèi)為autograder針對getAllValidWords()的時(shí)間測試,值為5秒內(nèi)調(diào)用次數(shù)的reference / student ratio,越小越快;滿分要求小于2,Bonus要求小于0.5;源材料沒有保留完整,數(shù)據(jù)可能稍有出入)

一版(~2.5):簡單更改庫中現(xiàn)成的TST(三叉搜索樹)類(增加一個(gè)單行的PrefixQuery小函數(shù)),將Board預(yù)處理為char[W*H],使用非遞歸方式的DFS(主要維護(hù)兩個(gè)stack)遍歷Board實(shí)現(xiàn);

二版(~1.6):在前版基礎(chǔ)上,改TST為手寫實(shí)現(xiàn)的26-way Trie類,Board預(yù)處理為Bag[W*H],所有函數(shù)都為非遞歸方式實(shí)現(xiàn)(如checklist所要求);

三版(~0.8):在前版基礎(chǔ)上,改非遞歸方式DFS為遞歸,將Trie類內(nèi)容直接寫入BoggleSolver,在DFS過程中直接傳遞Node指針而非調(diào)用PrefixQuery函數(shù);

終版(0.55):在前版基礎(chǔ)上,全面整理了各步驟細(xì)節(jié),cache中間變量,使用Bag而非HashSet存儲(chǔ)查詢結(jié)果,及(參考論壇討論后)其他細(xì)節(jié)上的技巧性優(yōu)化。

很遺憾最終依然沒有拿到Bonus,但每一份性能都已是得來不易。

Test 2: timing getAllValidWords() for 5.0 seconds using dictionary-yawl.txt
(must be <= 2x reference solution)

reference solution calls per second: 6175.83

student solution calls per second: 11200.52

reference / student ratio: 0.55

學(xué)習(xí)經(jīng)驗(yàn):

開始時(shí)其實(shí)比較抗拒自己實(shí)現(xiàn)一些工具類,如TST或Trie,除了必要的功能外很不愿意改動(dòng)它們;但當(dāng)完成前期版本,開始尋求性能優(yōu)化,實(shí)實(shí)在在地了解了這些工具類運(yùn)作機(jī)理后,才發(fā)現(xiàn)最大的障礙其實(shí)來自這些工具類“為通用性而做出的性能上的犧牲”,于是義無反顧地自己手動(dòng)實(shí)現(xiàn)了滿足“最低需求”的版本,但因細(xì)節(jié)盡在自己的掌握,使得在DFS中傳遞Node指針這樣最直接有效的實(shí)現(xiàn)方式變得可能,自然,也得到了對應(yīng)的回報(bào);

checklist也不是圣經(jīng),有獨(dú)立思考問題的意識(shí)才可能發(fā)現(xiàn)更大的世界:在實(shí)現(xiàn)非遞歸版DFS時(shí)明顯感到比較吃力,同時(shí)需要追蹤維護(hù)很多變量,而DFS的邏輯本身也更適合遞歸;非遞歸版DFS全當(dāng)練手,而能夠轉(zhuǎn)回遞歸實(shí)現(xiàn)版本則是大膽?yīng)毩⑺伎嫉慕Y(jié)果;

有同學(xué)使用多線程達(dá)到了非??斓某煽儯?.24),但在算法課程作業(yè)這個(gè)意義上,多線程其實(shí)尚處于“灰色區(qū)域”,有作弊的嫌疑——況且也沒什么技術(shù)含量,無須在意。

X. Data Compression與Burrows-Wheeler √ 〇

如果要用一個(gè)詞來形容這次的作業(yè),我想這個(gè)新學(xué)的fancy word會(huì)再合適不過,“Nifty”。
課程講到這周基本接近尾聲,正則表達(dá)式?jīng)]有作為重點(diǎn)分析太多,而數(shù)據(jù)壓縮,則成為了這最后一次作業(yè)的主題;它看似無趣,卻在作業(yè)細(xì)致入微的步驟上隱藏著許多非常精致的點(diǎn),其間如抽絲撥繭般細(xì)膩的過程,堪稱快感連連;而其中一個(gè)Circular Suffix Array(CSA)排序的實(shí)現(xiàn),可以涵蓋從第一部分起討論過的所有排序算法:題目本身并沒有任何限制;學(xué)生需要認(rèn)真比較每一種排序方式的優(yōu)劣,并選擇出其中一種或幾種方法的組合,重新制造出一個(gè)最適合當(dāng)前情況下的高效解決方案;在這個(gè)意義上,它相比前面的作業(yè),與real world problem更接近了一步。
數(shù)據(jù)壓縮的過程本身就是比較抽象的,而這次的作業(yè)說明與checklist更是文字翻倍而全無附圖,有同學(xué)開始時(shí)看不懂題目也是正常,慢慢來啦?!鞠挛乃兄欣ㄌ?hào)為題目簡短說明,推薦閱讀詳細(xì)說明】

i Original Suffixes Sorted Suffixes index[i]
0 A B R A C A D A B R A ! ! A B R A C A D A B R A 11
1 B R A C A D A B R A ! A A ! A B R A C A D A B R 10
2 R A C A D A B R A ! A B A B R A ! A B R A C A D 7
*3 A C A D A B R A ! A B R A B R A C A D A B R A ! *0
4 C A D A B R A ! A B R A A C A D A B R A ! A B R 3
5 A D A B R A ! A B R A C A D A B R A ! A B R A C 5
6 D A B R A ! A B R A C A B R A ! A B R A C A D A 8
7 A B R A ! A B R A C A D B R A C A D A B R A ! A 1
8 B R A ! A B R A C A D A C A D A B R A ! A B R A 4
9 R A ! A B R A C A D A B D A B R A ! A B R A C A 6
10 A ! A B R A C A D A B R R A ! A B R A C A D A B 9
11 ! A B R A C A D A B R A R A C A D A B R A ! A B 2

【encode部分:對源字符串的CSA(左)排序,返回排好序的CSA(右)的最后一列(ARD!RCAAAABB),及源字符串所在位置(3)。】
認(rèn)真閱讀作業(yè)材料即可大致了解,Burrows-Wheeler壓縮算法的三部分:
Huffman壓縮已實(shí)現(xiàn)不必關(guān)心;
Move-to-front編碼最為簡單可直接實(shí)現(xiàn);
Burrows-Wheeler decoder被稱為“the trickest part”,但緊接著便已提示到key-indexed counting與10行的核心代碼長度,其實(shí)已算是提示了很多;
而其encoding需要使用Circular Suffix Array,所以最大的課題其實(shí)是,如何以最高效率實(shí)現(xiàn)Circular Suffix Array排序。

下面就先來看CSA的排序問題:
因由源字符串逐個(gè)循環(huán)偏移構(gòu)成,CSA是一種具備很多特殊性質(zhì)的數(shù)組,而對這樣的數(shù)組排序照搬通用的排序算法一定有很大的優(yōu)化空間沒有利用,因此手工打造一個(gè)高效實(shí)用“特別化”的排序算法,就變成了眼前最實(shí)際的問題。
有上一周迭代更新的經(jīng)驗(yàn)在前,這次幾乎是立即開始了手工打造的過程,而可考慮的范圍又幾乎沒有限制,以下是我的幾個(gè)正確版本:(分?jǐn)?shù)為CSA構(gòu)造函數(shù)運(yùn)行時(shí)間的student / reference ratio(越小越快),均取數(shù)據(jù)長度為409600的測試成績作為樣本;兩個(gè)分?jǐn)?shù)僅輸入數(shù)據(jù)不同,第一個(gè)為隨機(jī)ASCII,第二個(gè)為典型英文內(nèi)容(dickens.txt);滿分要求均為3以下)

一版(2.85,5.16):統(tǒng)一使用Mergesort;

二版(4.07,6.69):統(tǒng)一使用Comparator暴力比較(或封裝為Comparable類,效率接近);

三版(0.45,1.34):長度15以下切換到insertion sort,以上使用MSD radix sort;

四版(1.29,1.87):長度15以下切換到insertion sort,以上使用3 way radix quick sort;

五版(3.04,3.83):改編自booksite所附源碼,較為低效的ManberMyers實(shí)現(xiàn)(使用MSD排好首位字符,接著使用簡單quicksort);

這幾個(gè)是在權(quán)衡利弊后選擇實(shí)現(xiàn)(如沒有選擇LSD),并已保證正確性的版本(其中不少的實(shí)現(xiàn)使用了algs4.jar源碼),可以看出目前相對最高效的方案是MSD+insertion;
因?qū)φn堂中提到的ManberMyers算法很感興趣,所以后期做了不少嘗試,但還是沒有完全原創(chuàng)地完成一個(gè)正確版本,加上還有很多好的選擇,這里還會(huì)繼續(xù)努力;
課程API對Java的static塊沒有要求,所以可以在合適處(如MoveToFront類)利用。

最后是本作業(yè)最“Nifty”的部分,Burrows-Wheeler decoder:

i Sorted Suffixes next
0 ! ? ? ? ? ? ? ? ? ? ? A 3
1 A ? ? ? ? ? ? ? ? ? ? R 0
2 A ? ? ? ? ? ? ? ? ? ? D 6
*3 A ? ? ? ? ? ? ? ? ? ? ! 7
4 A ? ? ? ? ? ? ? ? ? ? R 8
5 A ? ? ? ? ? ? ? ? ? ? C 9
6 B ? ? ? ? ? ? ? ? ? ? A 10
7 B ? ? ? ? ? ? ? ? ? ? A 11
8 C ? ? ? ? ? ? ? ? ? ? A 5
9 D ? ? ? ? ? ? ? ? ? ? A 2
10 R ? ? ? ? ? ? ? ? ? ? B 1
11 R ? ? ? ? ? ? ? ? ? ? B 4

【decode部分(層層相扣,建議讀原文):已知CSA最后一列(ARD!...),與源字符串所在行(3);對最后一列排序可得第一列(!AAA...);而next數(shù)組使用方式如下:因已知源字符串(排序前第0行)在排序后位置為3,而next[3] = 7,即排序后第7行(B...A)為排序前的第1行(第0行的下一行),可知源字符串第1位為"B"(根據(jù)第3行,第0位為‘A’),依此類推即可得到源字符串;next數(shù)組的構(gòu)造為通過比較首尾字符出現(xiàn)位置得到:如第i行末位為A,而首位第一個(gè)未標(biāo)記過的A出現(xiàn)在第j行,則next[i] = j,標(biāo)記第j行?!?br>開始時(shí)我只看了作業(yè)說明,所以冒失地寫好了一個(gè)“暴力decode”還渾然不自知,直到參考checklist后:“WTF”
事實(shí)證明那10行核心代碼基本與下圖所示相同:

問題的關(guān)鍵在于next數(shù)組的構(gòu)造,可總結(jié)為如下幾點(diǎn):

key-indexed counting核心機(jī)理是index的累加,可以保證排序結(jié)果的穩(wěn)定(stable);

根據(jù)作業(yè)說明的分析,next數(shù)組構(gòu)建的過程,確定重復(fù)字符位置的核心機(jī)理也是“穩(wěn)定性”(針對多次出現(xiàn)的同一字符,首尾列的相對先后順序一致);

對CSA可以不顯式創(chuàng)建字符串?dāng)?shù)組而僅保留源字符串,只添加一個(gè)簡單的根據(jù)offset和index循環(huán)獲取對應(yīng)字符的工具函數(shù)即可,這為直接使用key-indexed counting創(chuàng)造了非常好的環(huán)境。

于是在對源碼極簡單的改變后,完成了截然不同的任務(wù):(spoiler alert)

for (int i = 0; i < N; i++)
    count[t[i]+1]++;
for (int i = 0; i < 256; i++)
    count[i+1] += count[i];
// The trickiest part
for (int i = 0; i < N; i++)
    next[count[t[i]]++] = i;

其中N為字符串長度,count為計(jì)數(shù)數(shù)組(默認(rèn)ASCII編碼包含256個(gè)字符),next為目標(biāo)數(shù)組,t為已知尾列字符串的對應(yīng)char[];(無需顯式排序找出首列,第二步count的累加已達(dá)到目的)
6行完成了next數(shù)組的構(gòu)建,加上接下來還原源字符串的過程,達(dá)到10行。
仔細(xì)品讀其中內(nèi)容至理解透徹,相信這妙不可言的感覺一定已深深印在閣下腦海。
歡迎來到算法世界。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65984.html

相關(guān)文章

  • Algorithms(第四版)1.1課后練習(xí)答案(個(gè)人整理

    摘要:最近著手學(xué)習(xí)的這本書,開始做習(xí)題時(shí)發(fā)現(xiàn)配套網(wǎng)站上對應(yīng)的習(xí)題答案并不完全,后發(fā)現(xiàn)以及有些人的博客上有部分答案,不過一般只做了第一章節(jié)的題目,大概是題目太多了的原因,在此自己整理自己所做的一份答案,希望有同行的人一起交流,分享。 最近著手學(xué)習(xí)Robert Sedgewick的Algorithms這本書,開始做習(xí)題時(shí)發(fā)現(xiàn)配套網(wǎng)站上對應(yīng)的習(xí)題答案并不完全,google后發(fā)現(xiàn)github以及有些...

    android_c 評論0 收藏0
  • 算法分析 - Algorithms, Part I, week 1 ANALYSIS OF ALGO

    摘要:實(shí)際上這個(gè)情形中存在冪定律實(shí)際上絕大多數(shù)的計(jì)算機(jī)算法的運(yùn)行時(shí)間滿足冪定律。基于研究得知,原則上我們能夠獲得算法,程序或者操作的性能的精確數(shù)學(xué)模型。 前言 上一篇:并查集下一篇:棧和隊(duì)列 在算法性能上我們常常面臨的挑戰(zhàn)是我們的程序能否求解實(shí)際中的大型輸入:--為什么程序運(yùn)行的慢?--為什么程序耗盡了內(nèi)存? 沒有理解算法的性能特征會(huì)導(dǎo)致客戶端的性能很差,為了避免這種情況的出線,需要具備算法...

    Leo_chen 評論0 收藏0
  • 職業(yè)轉(zhuǎn)型的終極指南:從新手到專業(yè)的機(jī)器學(xué)習(xí)工程師

    摘要:作者微信號(hào)微信公眾號(hào)簡書地址最近機(jī)器學(xué)習(xí)工程師已經(jīng)成為了一個(gè)非常熱門的崗位,很多的工程師都想轉(zhuǎn)行到這個(gè)崗位。本文根據(jù)上面的課程,列了一個(gè)從新手到專業(yè)工程師的學(xué)習(xí)計(jì)劃,提供給大家學(xué)習(xí)。 作者:chen_h微信號(hào) & QQ:862251340微信公眾號(hào):coderpai簡書地址:http://www.jianshu.com/p/32b2... 最近機(jī)器學(xué)習(xí)工程師已經(jīng)成為了一個(gè)非常熱門的崗...

    XanaHopper 評論0 收藏0
  • 重磅 | 完備的 AI 學(xué)習(xí)路線,最詳細(xì)的資源整理!

    摘要:是你學(xué)習(xí)從入門到專家必備的學(xué)習(xí)路線和優(yōu)質(zhì)學(xué)習(xí)資源。的數(shù)學(xué)基礎(chǔ)最主要是高等數(shù)學(xué)線性代數(shù)概率論與數(shù)理統(tǒng)計(jì)三門課程,這三門課程是本科必修的。其作為機(jī)器學(xué)習(xí)的入門和進(jìn)階資料非常適合。書籍介紹深度學(xué)習(xí)通常又被稱為花書,深度學(xué)習(xí)領(lǐng)域最經(jīng)典的暢銷書。 showImg(https://segmentfault.com/img/remote/1460000019011569); 【導(dǎo)讀】本文由知名開源平...

    荊兆峰 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<