創(chuàng)作時(shí)間:2019-03-18
作者:周林
版權(quán)所有矛双,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲取授權(quán)牛哺、并注明作者與出處
?????? 上篇文章介紹了算法的本質(zhì)和基本概念达传,這次我們用實(shí)際的問題來做算法實(shí)戰(zhàn)篙耗。
?????? 假設(shè)如下場(chǎng)景: 公司新年晚會(huì)進(jìn)行奪寶游戲,獎(jiǎng)品是最新款的智能手機(jī)宪赶、VR游戲機(jī)宗弯、便攜電腦三件套。游戲規(guī)則如下: 當(dāng)主持人宣布游戲開始的時(shí)候搂妻,每位員工的手機(jī)上會(huì)同時(shí)收到兩組數(shù)字(數(shù)組中的每個(gè)數(shù)字都是正整數(shù)且兩兩不等)和一個(gè)目標(biāo)正整數(shù)蒙保。員工需要在兩組數(shù)字中分別取兩個(gè)數(shù)字相加,使得相加的結(jié)果與目標(biāo)正整數(shù)最接近欲主。哪位員工先做出結(jié)果,那么獎(jiǎng)品就歸誰(shuí)扁瓢。為了使贏率最高详恼,請(qǐng)問應(yīng)該采用什么樣的策略或者方法引几?
?????? 顯然敞掘,這是在對(duì)一個(gè)特定問題找方法玖雁。那么根據(jù)上篇文章所講到的茄菊,這就是在求算法疯潭。 那么如何算法求解呢?答案就在上篇文章提到的“樸素而廣泛的方法論”中面殖。這個(gè)方法論其實(shí)就是算法求解的套路竖哩。
套路第一步:重新定義問題,結(jié)構(gòu)化描述
?????? 原問題是生活場(chǎng)景脊僚,要轉(zhuǎn)換成結(jié)構(gòu)化問題描述相叁。結(jié)構(gòu)化描述分為如下兩步:數(shù)據(jù)與規(guī)則抽取、數(shù)據(jù)結(jié)構(gòu)選擇與轉(zhuǎn)化辽幌。
數(shù)據(jù)與規(guī)則抽取
?????? 數(shù)據(jù)的來源:數(shù)據(jù)一般在原問題描述中以名詞增淹、量詞形式出現(xiàn)
?????? 數(shù)據(jù)的摘取:并不是所有的名詞和量詞都是有效數(shù)據(jù)乌企。
?????? 很明顯虑润,只有和問題求解相關(guān)的名詞和量詞才有意義〖咏停“問題求解”是動(dòng)作拳喻,與動(dòng)詞相關(guān)。 那么是不是所有的動(dòng)詞都有效呢猪腕?也不是冗澈。只有和規(guī)則相關(guān)的動(dòng)詞才是有效的。
???????規(guī)則的發(fā)掘:規(guī)則就是抵達(dá)結(jié)果的條件陋葡。
?????? 根據(jù)上面的定義亚亲, 不難看出:
?????? 數(shù)據(jù)是:兩組數(shù)字(數(shù)組中的每個(gè)數(shù)字都是正整數(shù)且兩兩不等)、一個(gè)目標(biāo)整數(shù)
???? ?規(guī)則是:從兩組數(shù)字中分別取兩個(gè)數(shù)字相加腐缤,相加的結(jié)果必須與目標(biāo)正整數(shù)最接近
數(shù)據(jù)結(jié)構(gòu)選擇與轉(zhuǎn)化
?????? 上篇文章已經(jīng)講到了:算法的依托是數(shù)據(jù)結(jié)構(gòu)捌归。如果把算法看做設(shè)計(jì)域的話,那么數(shù)據(jù)結(jié)構(gòu)就是連接問題域到設(shè)計(jì)域的橋梁岭粤。那么如何選取合適的數(shù)據(jù)結(jié)構(gòu)呢陨溅?答案是:對(duì)上一步摘取的數(shù)據(jù)進(jìn)行類型聯(lián)想、關(guān)聯(lián)绍在。
?????? 上一步中门扇,我們已經(jīng)摘取了數(shù)據(jù)——兩組數(shù)和一個(gè)正整數(shù)。很明顯偿渡,這里涉及到兩個(gè)類型:數(shù)組和整數(shù)臼寄。而這兩個(gè)屬于基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)類型,至此數(shù)據(jù)結(jié)構(gòu)選擇問題解決了溜宽。接下來就是要對(duì)摘取的數(shù)據(jù)吉拳,基于選擇的數(shù)據(jù)結(jié)構(gòu)進(jìn)行轉(zhuǎn)化——“重整化”:
?????? ?兩組數(shù)字(數(shù)組中的每個(gè)數(shù)字都是正整數(shù)且兩兩不等)=> int A[]; int B[];
?????? ?目標(biāo)正整數(shù) => int c;
??????? 聰明的你,一定會(huì)問一個(gè)問題:數(shù)據(jù)結(jié)構(gòu)的選擇僅僅就在這一步?jīng)Q定嗎适揉? 答案是否定的留攒。數(shù)據(jù)結(jié)構(gòu)的選擇會(huì)貫穿整個(gè)算法設(shè)計(jì)煤惩,是一個(gè)不斷迭代的過程。后面部分會(huì)詳細(xì)闡述炼邀。
套路第二步:?jiǎn)栴}歸類
?????? 算法問題的基本類型:搜索魄揉、排序、規(guī)劃拭宁、計(jì)算洛退。
????? ?回到當(dāng)前問題,根據(jù)問題描述杰标,顯然屬于搜索類型兵怯。
套路第三步:經(jīng)驗(yàn)匹配
?????? 現(xiàn)在我們來翻看已有的搜索算法,看看有沒有能與當(dāng)前問題匹配的腔剂。
?????? 理論上有3種情況:
?????? 第1種情況媒区,100%匹配,此時(shí)直接“拿來主義”掸犬;
???????第2種情況袜漩,部分匹配,此時(shí)可在已有算法基礎(chǔ)上進(jìn)行調(diào)整登渣、組合或者改良噪服;
?????? 第3種情況毡泻,完全不匹配胜茧,此時(shí)需要我們根據(jù)已有知識(shí)(甚至是跨學(xué)科知識(shí),比方說數(shù)學(xué)仇味、生物等)呻顽,創(chuàng)新性地開發(fā)新算法。
?????? 針對(duì)搜索問題丹墨,我們有一個(gè)萬(wàn)能算法——“暴力搜索”廊遍,即遍歷每一種可能性,直到找到答案贩挣。但是這個(gè)算法要窮盡所有可能性喉前,所以帶來的時(shí)間和空間開銷通常都是巨大的,用上篇文章的術(shù)語(yǔ)來講王财,就是計(jì)算復(fù)雜度賊高卵迂。 為了給大家一個(gè)量化感覺,先用“暴力搜索”算法來解答這個(gè)題:
暴力搜索算法
?????? 對(duì)于數(shù)組A中的每一個(gè)元素進(jìn)行遍歷:
????????????? 設(shè)當(dāng)前元素為A[i]绒净,則:
????????????? 遍歷數(shù)組b中的每一個(gè)元素B[j]:
??????????? (i)計(jì)算A[i]+B[j]的值见咒,將所求的值記為t;
??????????????(ii) 計(jì)算t-c的絕對(duì)值|t-c|,記為k;
????????????? (iii) 如果當(dāng)前的k比歷史的k泄医(k的初值可以設(shè)成一個(gè)極大值)改览,那么:
????????????????????????? 將 {A[i], B[j]}取代之前的候選結(jié)果下翎,作為新的候選結(jié)果
??????? 待所有的遍歷結(jié)束,最終的候選結(jié)果就是所要求的解宝当。
???????上面的算法有兩重循環(huán)视事,所以暴力搜索時(shí)間復(fù)雜度為O(La x Lb)。 其中La表示數(shù)組a中元素的個(gè)數(shù)今妄,Lb表示數(shù)組b中元素的個(gè)數(shù)郑口。 隨著La和Lb的增大,復(fù)雜度以兩者乘積速度上升盾鳞。
???????那么如何對(duì)暴力算法進(jìn)行優(yōu)化呢犬性?
?????? 關(guān)于復(fù)雜度的計(jì)算,我會(huì)在下篇文章中詳細(xì)介紹腾仅。
套路第四步:算法優(yōu)化三步走
步驟1:找到算法性能瓶頸源頭
???????????? 稍微分析一下乒裆,就明白:上述暴力搜索算法的開銷在于窮盡了所有元素。
步驟2:對(duì)源頭進(jìn)行改造
???????????? 那么是否可以避免窮盡所有元素而得到結(jié)果呢推励?
???????????? 換言之鹤耍,是否可以只比較部分元素、其他元素就自然被排除了呢验辞?
???????????? 要得到這樣的效果稿黄,顯然我們需要一種性質(zhì)——這種性質(zhì)必須是容易獲得的:
??????????? ?要么可以直接從當(dāng)前數(shù)據(jù)中獲取,要么可以通過已有方法(算法)獲取跌造。
??????????? 最容易想到的就是有序性杆怕,這種性質(zhì)可以通過排序算法獲取。
?????????? ?我們可以用快速排序算法對(duì)A數(shù)組和B組數(shù)進(jìn)行排序壳贪,將排序后的元素按照下圖放置: (為了方便表示陵珍,我們假設(shè)A數(shù)組是10個(gè)元素,B數(shù)組是11個(gè)元素)
?????????? 上圖中的每個(gè)方格就是用來存放相加結(jié)果的违施。很顯然互纯,暴力搜索就是對(duì)上圖中的每個(gè)方格都做了計(jì)算。
?????????? 現(xiàn)在我們要做的磕蒲,就是利用有序性留潦,避開盡可能多的方格。
?????????? 我們從右上角方格[A10, B1]開始遍歷:
?????????? 記s[A10, B1] = A10 + B1辣往,則:
??????????? (i) 如果s[A10, B1] == 目標(biāo)正整數(shù)c兔院,那么元素對(duì){A10, B1}即為所求解
??????????? (ii) 如果s[A10, B1] < 目標(biāo)正整數(shù)c排吴, 那么所有與[A10秆乳,B1]在同一排的方格都不用計(jì)算了
?????????????????原因如下:因?yàn)锳1<=A2<=...<=A9<=A10,所以s[A1, B1] <= s[A2, B1] <= ... <= s[A10, B1],
?????????????????從而這些s距離c都比s[10, B1]遠(yuǎn)屹堰,都不是所求解肛冶。
????????????(iii) 類似地,如果s[A10, B1] > 目標(biāo)正整數(shù)c扯键,那么所有與A[10, B1]在同一列的方格都不用計(jì)算了
?????????? 顯然睦袖,按照對(duì)角線方向來遍歷,每遍歷一個(gè)方格荣刑,就可以避開一排或者一列的方格馅笙,感覺就像在玩掃雷游戲:)
步驟3:驗(yàn)證
??????? 現(xiàn)在我們來驗(yàn)證一下優(yōu)化后的算法的復(fù)雜度:
??????? 整個(gè)算法分成兩部分:
??????? 第1部分是快速排序±骺鳎快速排序算法的時(shí)間復(fù)雜度是O(nlogn)董习,所以這部分的時(shí)間復(fù)雜度是O(MAX(LalogLa, LblogLb))
???????? 第2部分是掃雷遍歷。這部分最壞的情況就是走完整個(gè)對(duì)角線爱只。此時(shí)共遍歷La+Lb-1個(gè)方格皿淋,時(shí)間復(fù)雜度是O(La+Lb)
???????? 兩者相加得到最壞情況下的整體時(shí)間復(fù)雜度為:O(MAX(LalogLa, LblogLb)+La+Lb)
本原創(chuàng)系列同步在以下自媒體上更新,敬請(qǐng)關(guān)注: