人工智能(6)Dynamic Programming

上次提到了猫妙,回溯(Backtrack)干像,深度和廣度搜索哺徊,一些演化的算法图贸,比如規(guī)定了最大深度的深度搜索蹂季,或者叫做DFS with iterative deepening (DFS-ID),每一個(gè)的action都有一個(gè)固定的cost疏日,假設(shè)最優(yōu)方案的深度為d的話偿洁,空間和時(shí)間的復(fù)雜度:O(d)和O(b^d)。

總結(jié)一下之前講過的這幾種算法, 不難看出時(shí)間復(fù)雜度都是指數(shù)級(jí)別的

那么有沒有一些方式能把時(shí)間復(fù)雜度降低一些呢沟优?我們先來看一下動(dòng)態(tài)規(guī)劃涕滋。

上次提到了,對于一個(gè)搜索的問題挠阁,包含幾個(gè)要素:

當(dāng)前的狀態(tài)S, 到達(dá)下一個(gè)狀態(tài)的動(dòng)作a,這一動(dòng)作的代價(jià)c, 下一個(gè)狀態(tài)(Succ (s,a), successor of S)宾肺,最終的狀態(tài)。

動(dòng)態(tài)規(guī)劃實(shí)際上就是這樣的一個(gè)過程:

在當(dāng)前狀態(tài)S下侵俗,判斷出來達(dá)到下一個(gè)狀態(tài)S‘的動(dòng)作的代價(jià)爱榕,然后通過某種方式計(jì)算一下這個(gè)狀態(tài)到達(dá)最終狀態(tài)的代價(jià)。要做的就是找出來代價(jià)和最小的下一個(gè)狀態(tài)坡慌。

用這樣的一個(gè)例子一起來看一下動(dòng)態(tài)規(guī)劃的一個(gè)過程:

假設(shè)現(xiàn)在有1黔酥,2,5,10塊四種面值的紙幣跪者,現(xiàn)在問一下湊齊68塊錢最少需要多少張紙幣棵帽?

直覺地:先用10塊的6張,湊不夠10塊的渣玲,用5塊的一張逗概,再用2塊的和1塊的各一張,剛好68塊忘衍,毫無疑問這樣的方法是正確的逾苫,這其實(shí)用到了貪心的思想(Greedy Method)。

試想一下枚钓,如果現(xiàn)在的錢幣面值設(shè)置不是這樣的铅搓,而是1,5搀捷,8星掰,10 塊的面值,要湊64塊錢嫩舟,貪心的方法會(huì)選6張10塊氢烘,再選4張一塊,但這不是用的最少的家厌,8張8塊的是最少的播玖。遇到這樣的一類問題,方案就是動(dòng)態(tài)規(guī)劃的方法饭于。

假設(shè)現(xiàn)在要兌換N塊的紙幣黎棠,那么最優(yōu)的方案需要的張數(shù)n是關(guān)于N的一個(gè)這樣的遞歸函數(shù):

n(N) = min {1+n(N-1);1+n(N-5); 1+n(N-8); 1+n(N-10)}

很明顯有幾個(gè)隱含的條件:如果N = 1,5,8,10, n(N) = 1, 一張就夠了镰绎。選擇的紙幣的面值肯定不能超過要湊的紙幣總數(shù)脓斩,比如要湊6塊錢,8和10塊肯定用不到了畴栖。代碼實(shí)現(xiàn)也很容易:


# i_STEM,Dynamic programming

# 用1随静,5,8吗讶,10 塊的紙幣湊N塊的問題

# 遞歸

import numpy as np

value = [1,5,8,10]

N = 40

def exchange(value,N, count = [0]):

    count[0]+= 1

    mn = N

    if N in value:

        print ("Recursion times:",count[0])

        return 1

    for i in [int(v) for v in value if int (v) <=N]:

        n = 1 + exchange(value,N-i,mem)

        if n < mn:

            mn = n

    return mn

exchange(value,N) #

但上面這段短短的代碼燎猛,卻要花很多時(shí)間去遞歸,舉個(gè)例子照皆,要算一下湊40塊重绷,要調(diào)用20多萬次遞歸,膜毁,普通電腦可能花一分鐘左右才跑完昭卓。愤钾。那么問題出現(xiàn)在哪兒呢?不難想象候醒,很多重復(fù)的數(shù)據(jù)出現(xiàn)能颁,重新進(jìn)行計(jì)算,浪費(fèi)了大量的時(shí)間倒淫。比如伙菊,湊68塊,可能會(huì)用到前67塊各自的需要的最少的數(shù)量的紙幣敌土,才能得出最后的最優(yōu)的結(jié)果镜硕。如果把這些結(jié)果保存下來,就省去了重復(fù)計(jì)算的時(shí)間返干,這其實(shí)也是“學(xué)習(xí)”的一個(gè)過程兴枯,或者說“記憶”。很簡單的犬金,加幾行代碼就可以了。首先六剥,聲明存儲(chǔ)的變量晚顷,函數(shù)調(diào)用的時(shí)候作為參數(shù)。接著疗疟,遞歸出口多了一個(gè)该默,除了面值等于1,5策彤,8栓袖,10的之外,其他存儲(chǔ)過的最少的紙幣的張數(shù)也保存起來店诗,到時(shí)候直接調(diào)用裹刮。這樣顯示,遞歸了80多次庞瘸,調(diào)用了70多次捧弃,不到1秒鐘就跑完程序。


# i_STEM,遞歸動(dòng)態(tài)規(guī)劃

import numpy as np

value = [1,5,8,10]

N = 40

mem = np.zeros([N+1]) # 存儲(chǔ)所有N-1塊的需要的紙幣的最少的張數(shù)

def exchange(value,N, mem,count = [0,0]):

    count[0]+= 1

    mn = N

    if N in value:

        print ("Recursion times:",count[0])

        return 1

    elif mem[N] > 0:

        count[1]+= 1

        print ("Retrieval times:",count[1])

        return mem[N]

    for i in [int(v) for v in value if int (v) <=N]:

        n = 1 + exchange(value,N-i,mem)

        if n < mn:

            mn = n

            mem[N] = mn

    return mn

exchange(value,N, mem) #

print (mem)

那么有一個(gè)問題擦囊,程序跑完之后违霞,所有1-39塊的最少的張數(shù)都會(huì)保存起來嗎?

答案是不一定瞬场,因?yàn)樵谇?0塊的張數(shù)的時(shí)候买鸽,是一個(gè)自頂向下的過程,并且贯被,40塊不一定需要的張數(shù)比20多眼五,30多塊的需要的多妆艘,自頂向下分解的時(shí)候,不一定會(huì)分解到每一個(gè)值弹砚。那么問題來了双仍,能不能找到一種方法,把1-39塊的每一種方案都找出來桌吃?

答案是肯定的朱沃,自底向上的過程可以確保頂層的問題肯定能從下面找到解答。同樣的公式:

n(N) = min {1+n(N-1)茅诱;1+n(N-5); 1+n(N-8); 1+n(N-10)}

在第一種方案中逗物,n(N)代表遞歸的解決方案,這里n(N)表示的是存儲(chǔ)的最優(yōu)方案的值瑟俭,也即不是去遞歸調(diào)用翎卓,而是直接從存儲(chǔ)里面去取值,也就是說我們不需要使用遞歸就可以計(jì)算出來摆寄,無非是在原有的基礎(chǔ)上試探所有四種紙幣的可行性和解決方案是否最優(yōu)失暴。在程序?qū)崿F(xiàn)的時(shí)候,每個(gè)n(N)的初始值都是N,也即微饥,最壞的方案是都用1塊去湊逗扒,然后每求一個(gè)n(N),依據(jù)N的大小欠橘,考慮這四種紙幣拼湊的情況矩肩,在n(N-1), n(N-5), n(N-8), n(N-10)的基礎(chǔ)上加1取值即可。這樣的話時(shí)間復(fù)雜度是O(4*N)肃续,空間復(fù)雜度O(N)黍檩。來看一下代碼的實(shí)現(xiàn):


# i_STEM,自底向上動(dòng)態(tài)規(guī)劃

import numpy as np

value = [1,5,8,10]

N = 40

# 存儲(chǔ)最優(yōu)的方案,N+1個(gè)0的list,這樣保證下標(biāo)是0-40始锚,41個(gè)數(shù)刽酱,雖然0下標(biāo)的位置用不到,

# cost = [0]*(N + 1)

cost = np.zeros([N+1])

def exchange(value, N, cost):

    # N重循環(huán)

    for i in range(1,N+1):

        # 初始化為最差的方案,全部用1塊

        cost[i] = i

        # 最多四重循環(huán)

        for j in [k for k in value if k <= i]:

            # 讀取i-j塊需要的張數(shù)瞧捌,再加一張即可肛跌,j一定是不大于i,需要注意的是cost[0]=0保持不變

            cost_temp = cost[i-j] + 1

            if cost_temp <= cost[i]:

                cost[i] = cost_temp

    return cost

exchange(value, N, cost)

如果要想存儲(chǔ)解決的方案察郁,也即哪些紙幣被用來去湊N塊衍慎,聲明另外一個(gè)變量,cost皮钠,求N塊的方案稳捆,存儲(chǔ)最后一個(gè)紙幣的數(shù)額m,也即coin[N]=m麦轰,再找出N-m的最后一張紙幣的數(shù)額乔夯,這樣就可以把整個(gè)方案的找出來砖织。來看一下代碼實(shí)現(xiàn):


# i_STEM,自底向上動(dòng)態(tài)規(guī)劃,存儲(chǔ)方案

import numpy as np

value = [1,5,8,10]

N = 40

# 存儲(chǔ)最優(yōu)的方案末荐,N+1個(gè)0的list,這樣保證下標(biāo)是0-40侧纯,41個(gè)數(shù),雖然0下標(biāo)的位置用不到,

# cost = [0]*(N + 1)

cost = np.zeros([N+1])

# 求N塊的方案甲脏,存儲(chǔ)最后一個(gè)紙幣的數(shù)額m眶熬,也即coin[N]=m,再找出N-m的最后一張紙幣的數(shù)額块请,這樣就可以把整個(gè)方案的找出來娜氏,

coin = np.zeros([N+1])

def exchange(value, N, cost):

    # N重循環(huán)

    for i in range(1,N+1):

        # 初始化為最差的方案,全部用1塊

        cost[i] = i

        # 最多四重循環(huán)

        for j in [k for k in value if k <= i]:

            # 讀取i-j塊需要的張數(shù)墩新,再加一張即可贸弥,j一定是不大于i,需要注意的是cost[0]=0保持不變

            cost_temp = cost[i-j] + 1

            if cost_temp <= cost[i]:

                cost[i] = cost_temp

                coin[i] = j

    return cost,coin

exchange(value, N, cost)

簡單的總結(jié)海渊,上面的前三種方案中绵疲,第一種單純使用遞歸,而不使用存儲(chǔ)的話臣疑,花費(fèi)大量的時(shí)間盔憨;第二種方案,使用了一些存儲(chǔ)朝捆,同時(shí)也有遞歸調(diào)用般渡,節(jié)約了大量的時(shí)間懒豹;第三種方案芙盘,最大限度的使用存儲(chǔ),沒有使用遞歸脸秽,自底向上的解決了問題儒老,也節(jié)省了很多的時(shí)間。適當(dāng)?shù)氖褂么鎯?chǔ)记餐,來節(jié)約大量的時(shí)間成本驮樊,還是很合算的。

類似的拿少量存儲(chǔ)去節(jié)省大量時(shí)間的例子還有很多片酝,在python中囚衔,遞歸調(diào)用的層數(shù)不能超過1000層,再考慮到時(shí)間成本雕沿,所以大量的運(yùn)算的時(shí)候练湿,合適的使用存儲(chǔ)很有必要。除此之外审轮,動(dòng)態(tài)規(guī)劃在很多領(lǐng)域中都可以見到肥哎,比如在生物信息學(xué)中辽俗,做DNA序列的alignment的時(shí)候,動(dòng)態(tài)規(guī)劃是非常常見的例子篡诽,比如Needleman–Wunsch 算法

再回到搜索中來崖飘,搜索中的動(dòng)態(tài)規(guī)劃的幾個(gè)要素上面提到了,當(dāng)前狀態(tài)S杈女,動(dòng)作a朱浴,動(dòng)作的代價(jià)Cost(S,a)和下一個(gè)狀態(tài)Succ(s,a),以及終止?fàn)顟B(tài)的判斷碧信。與上面所說的不太一樣的地方是赊琳,上面的問題每一個(gè)動(dòng)作的代價(jià)是一樣的,增加一張紙幣砰碴,只是這張紙幣選擇的上一個(gè)狀態(tài)不盡相同躏筏,導(dǎo)致整個(gè)方案的代價(jià)也不同,取其中最優(yōu)的呈枉。

在搜索中趁尼,

FutureCost (S) = 0, 如果是終止?fàn)顟B(tài)猖辫;

FutureCost (S) = min [Cost(S,a) + FutureCost (Succ(s,a))], 不是終止?fàn)顟B(tài)酥泞。

好,動(dòng)態(tài)規(guī)劃是非常重要的一種思想啃憎,我們從時(shí)間和存儲(chǔ)成本的角度簡單的分析了一下這樣的應(yīng)用芝囤,在不同的行業(yè)領(lǐng)域,都會(huì)不同的實(shí)際應(yīng)用的方案辛萍,但抽象化的中心思想是一致的悯姊。后面遇到更經(jīng)典的動(dòng)態(tài)規(guī)劃案例,再補(bǔ)充分享贩毕。下次來看一下搜索中 uniform cost search的實(shí)現(xiàn)悯许。

Reference:

Problem Solving with Algorithms and Data Structures

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辉阶,隨后出現(xiàn)的幾起案子先壕,更是在濱河造成了極大的恐慌,老刑警劉巖谆甜,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垃僚,死亡現(xiàn)場離奇詭異,居然都是意外死亡规辱,警方通過查閱死者的電腦和手機(jī)谆棺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來按摘,“玉大人包券,你說我怎么就攤上這事纫谅。” “怎么了溅固?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵付秕,是天一觀的道長。 經(jīng)常有香客問我侍郭,道長询吴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任亮元,我火速辦了婚禮猛计,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爆捞。我一直安慰自己奉瘤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布煮甥。 她就那樣靜靜地躺著盗温,像睡著了一般。 火紅的嫁衣襯著肌膚如雪成肘。 梳的紋絲不亂的頭發(fā)上卖局,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機(jī)與錄音双霍,去河邊找鬼砚偶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛洒闸,可吹牛的內(nèi)容都是我干的染坯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼顷蟀,長吁一口氣:“原來是場噩夢啊……” “哼酒请!你這毒婦竟也來了骡技?” 一聲冷哼從身側(cè)響起鸣个,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎布朦,沒想到半個(gè)月后囤萤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡是趴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年涛舍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唆途。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡富雅,死狀恐怖掸驱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情没佑,我是刑警寧澤毕贼,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蛤奢,受9級(jí)特大地震影響鬼癣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜啤贩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一待秃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痹屹,春花似錦章郁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至足画,卻和暖如春雄驹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淹辞。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工医舆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人象缀。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓蔬将,卻偏偏與公主長得像,于是被迫代替她去往敵國和親央星。 傳聞我的和親對象是個(gè)殘疾皇子霞怀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容