動(dòng)態(tài)規(guī)劃法(八)最大子數(shù)組問(wèn)題(maximum subarray problem)

問(wèn)題簡(jiǎn)介

??本文將介紹計(jì)算機(jī)算法中的經(jīng)典問(wèn)題——最大子數(shù)組問(wèn)題(maximum subarray problem)。所謂的最大子數(shù)組問(wèn)題甲抖,指的是:給定一個(gè)數(shù)組A,尋找A的和最大的非空連續(xù)子數(shù)組。比如笔诵,數(shù)組 A = [-2, -3, 4, -1, -2, 1, 5, -3], 最大子數(shù)組應(yīng)為[4, -1, -2, 1, 5],其和為7姑子。
??首先乎婿,如果A中的元素全部為正(或非負(fù)數(shù)),則最大子數(shù)組就是它本身街佑;如果A中的元素全部為負(fù)谢翎,則最大子數(shù)組就是第一個(gè)元素組成的數(shù)組。以上兩種情形是平凡的沐旨,那么森逮,如果A中的元素既有正數(shù),又有負(fù)數(shù)磁携,則該如何求解呢褒侧?本文將介紹該問(wèn)題的四種算法,并給出后面三種算法的Python語(yǔ)言實(shí)現(xiàn),解決該問(wèn)題的算法如下:

  • 暴力求解
  • 分治法
  • Kadane算法
  • 動(dòng)態(tài)規(guī)劃法

??下面就這四種算法做詳細(xì)介紹闷供。

暴力求解

??假設(shè)數(shù)組的長(zhǎng)度為n烟央,暴力求解方法的思路是很簡(jiǎn)單的,就是將子數(shù)組的開(kāi)始坐標(biāo)和結(jié)束坐標(biāo)都遍歷一下这吻,這樣共有n(n-1)/2中組合方式吊档,再考慮這所有組合方式中和最大的情形即可。
??該算法的運(yùn)行時(shí)間為O(n^2),效率是很低的唾糯。那么怠硼,還有其它高效的算法嗎?

分治法

??分治法的基本思想是將問(wèn)題劃分為一些子問(wèn)題移怯,子問(wèn)題的形式與原問(wèn)題一樣香璃,只是規(guī)模更小,遞歸地求解出子問(wèn)題舟误,如果子問(wèn)題的規(guī)模足夠小葡秒,則停止遞歸,直接求解嵌溢,最后將子問(wèn)題的解組合成原問(wèn)題的解眯牧。
??對(duì)于最大子數(shù)組,我們要尋求子數(shù)組A[low...high]的最大子數(shù)組赖草。令mid為該子數(shù)組的中央位置学少,我們考慮求解兩個(gè)子數(shù)組A[low...mid]和A[mid+1...high]。A[low...high]的任何連續(xù)子數(shù)組A[i...j]所處的位置必然是以下三種情況之一:

  1. 完全位于子數(shù)組A[low...mid]中,因此 low <= i <= j <= mid.
  2. 完全位于子數(shù)組A[mid+1...high]中,因此mid< i <= j <= high.
  3. 跨越了中點(diǎn)秧骑,因此low <= i <= mid < j <= high.

因此版确,最大子數(shù)組必定為上述3種情況中的最大者。對(duì)于情形1和情形2乎折,可以遞歸地求解绒疗,剩下的就是尋找跨越中點(diǎn)的最大子數(shù)組。
??任何跨越中點(diǎn)的子數(shù)組都是由兩個(gè)子數(shù)組A[i...mid]和A[mid+1...j]組成骂澄,其中l(wèi)ow <= i <= mid且mid < j <= high.因此吓蘑,我們只需要找出形如A[i...mid]和A[mid+1...j]的最大子數(shù)組,然后將其合并即可坟冲,這可以在線性時(shí)間內(nèi)完成士修。過(guò)程FIND-MAX-CROSSING-SUBARRAY接收數(shù)組A和下標(biāo)low、mid和high作為輸入樱衷,返回一個(gè)下標(biāo)元組劃定跨越中點(diǎn)的最大子數(shù)組的邊界,并返回最大子數(shù)組中值的和酒唉。其偽代碼如下:

FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high):
left-sum = -inf
sum = 0
for i = mid downto low
    sum = sum + A[i]
    if sum > left-sum
        left-sum = sum
        max-left = i
        
right-sum = -inf
sum = 0
for j = mid+1 to high
    sum = sum + A[j]
    if sum > right-sum
        right-sum = sum
        max-right = i
        
return (max-left, max-right, left-sum+right+sum)

??有了FIND-MAX-CROSSING-SUBARRAY我們可以找到跨越中點(diǎn)的最大子數(shù)組矩桂,于是,我們也可以設(shè)計(jì)求解最大子數(shù)組問(wèn)題的分治算法了,其偽代碼如下:

FIND-MAXMIMUM-SUBARRAY(A, low, high):
if high = low
    return (low, high, A[low])
else 
    mid = floor((low+high)/2)
    (left-low, left-high, left-sum) = FIND-MAXMIMUM-SUBARRAY(A, low, mid)
    (right-low, right-high, right-sum) = FIND-MAXMIMUM-SUBARRAY(A, mid+1, high)
    (cross-low, cross-high, cross-sum) = FIND-MAXMIMUM-SUBARRAY(A, low, mid, high)
    
    if left-sum >= right-sum >= cross-sum
        return (left-low, left-high, left-sum)
    else right-sum >= left-sum >= cross-sum
        return (right-low, right-high, right-sum)
    else
        return (cross-low, cross-high, cross-sum)

??顯然這樣的分治算法對(duì)于初學(xué)者來(lái)說(shuō)侄榴,有點(diǎn)難度非洲,但是熟能生巧, 多學(xué)多練也就不難了辅辩。該分治算法的運(yùn)行時(shí)間為O(n*logn).

Kadane算法

??Kadane算法的偽代碼如下:

Initialize:
    max_so_far = 0
    max_ending_here = 0

Loop for each element of the array
  (a) max_ending_here = max_ending_here + a[i]
  (b) if(max_ending_here < 0)
            max_ending_here = 0
  (c) if(max_so_far < max_ending_here)
            max_so_far = max_ending_here
return max_so_far

??Kadane算法的簡(jiǎn)單想法就是尋找所有連續(xù)的正的子數(shù)組(max_ending_here就是用來(lái)干這事的),同時(shí),記錄所有這些連續(xù)的正的子數(shù)組中的和最大的連續(xù)數(shù)組申尼。每一次我們得到一個(gè)正數(shù),就將它與max_so_far比較螺戳,如果它的值比max_so_far大踏堡,則更新max_so_far的值。

動(dòng)態(tài)規(guī)劃法

?? 用MS[i]表示最大子數(shù)組的結(jié)束下標(biāo)為i的情形恒水,則對(duì)于i-1会放,有:
????????????MS[i] = max {MS[i-1], A[i]}.
這樣就有了一個(gè)子結(jié)構(gòu),對(duì)于初始情形钉凌,MS[1]=A[1].遍歷i, 就能得到MS這個(gè)數(shù)組咧最,其最大者即可最大子數(shù)組的和。

總結(jié)

?? 可以看到以上四種算法御雕,每種都有各自的優(yōu)缺點(diǎn)矢沿。對(duì)于暴力求解方法,想法最簡(jiǎn)單酸纲,但是算法效率不高捣鲸。Kanade算法簡(jiǎn)單高效,但是不易想到福青。分治算法運(yùn)行效率高摄狱,但其分支過(guò)程的設(shè)計(jì)較為麻煩。動(dòng)態(tài)規(guī)劃法想法巧妙无午,運(yùn)行效率也高媒役,但是沒(méi)有普遍的適用性。

Python程序

?? 下面將給出分治算法宪迟,Kanade算法和動(dòng)態(tài)規(guī)劃法來(lái)求解最大子數(shù)組問(wèn)題的Python程序酣衷, 代碼如下:

# -*- coding: utf-8 -*-
__author__ = 'Jclian'

import math

# find max crossing subarray in linear time
def find_max_crossing_subarray(A, low, mid, high):
    max_left, max_right = -1, -1

    # left part of the subarray
    left_sum = float("-Inf")
    sum = 0
    for i in range(mid, low - 1, -1):
        sum += A[i]
        if (sum > left_sum):
            left_sum = sum
            max_left = i

    # right part of the subarray
    right_sum = float("-Inf")
    sum = 0
    for j in range(mid + 1, high + 1):
        sum += A[j]
        if (sum > right_sum):
            right_sum = sum
            max_right = j

    return max_left, max_right, left_sum + right_sum

# using divide and conquer to solve maximum subarray problem
# time complexity: n*logn
def find_maximum_subarray(A, low, high):
    if (high == low):
        return low, high, A[low]
    else:
        mid = math.floor((low + high) / 2)
        left_low, left_high, left_sum = find_maximum_subarray(A, low, mid)
        right_low, right_high, right_sum = find_maximum_subarray(A, mid + 1, high)
        cross_low, cross_high, cross_sum = find_max_crossing_subarray(A, low, mid, high)
        if (left_sum >= right_sum and left_sum >= cross_sum):
            return left_low, left_high, left_sum
        elif (right_sum >= left_sum and right_sum >= cross_sum):
            return right_low, right_high, right_sum
        else:
            return cross_low, cross_high, cross_sum

# Python program to find maximum contiguous subarray
# Kadane’s Algorithm
def maxSubArraySum(a, size):
    max_so_far = float("-inf")
    max_ending_here = 0

    for i in range(size):
        max_ending_here = max_ending_here + a[i]
        if (max_so_far < max_ending_here):
            max_so_far = max_ending_here

        if max_ending_here < 0:
            max_ending_here = 0

    return max_so_far

# using dynamic programming to slove maximum subarray problem
def DP_maximum_subarray(arr):
    t = len(arr)
    MS = [0]*t
    MS[0] = arr[0]

    for i in range(1, t):
        MS[i] = max(MS[i-1]+arr[i], arr[i])

    return MS

def main():
    # example of array A
    A = [13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7]
    # A = [-2, 2, -3, 4, -1, 2, 1, -5, 3]
    # A = [0,-2, 3, 5, -1, 2]
    # A = [-9, -2, -3, -5, -3]
    # A = [1, 2, 3, 4, 5]
    # A = [-2, -3, 4, -1, -2, 1, 5, -3]

    print('using divide and conquer...')
    print("Maximum contiguous sum is",find_maximum_subarray(A, 0, len(A) - 1), '\n')

    print('using Kanade Algorithm...')
    print("Maximum contiguous sum is", maxSubArraySum(A, len(A)), '\n')

    print('using dynamic programming...')
    MS = DP_maximum_subarray(A)
    print("Maximum contiguous sum is", max(MS), '\n')

main()

輸出結(jié)果如下:

using divide and conquer...
Maximum contiguous sum is (7, 10, 43) 

using Kanade Algorithm...
Maximum contiguous sum is 43 

using dynamic programming...
Maximum contiguous sum is 43 

參考文獻(xiàn)

  1. 算法導(dǎo)論(第三版) 機(jī)械工業(yè)出版社
  2. https://www.geeksforgeeks.org/largest-sum-contiguous-subarray/
  3. https://algorithms.tutorialhorizon.com/dynamic-programming-maximum-subarray-problem/

注意:本人現(xiàn)已開(kāi)通兩個(gè)微信公眾號(hào): 用Python做數(shù)學(xué)(微信號(hào)為:python_math)以及輕松學(xué)會(huì)Python爬蟲(chóng)(微信號(hào)為:easy_web_scrape), 歡迎大家關(guān)注哦~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末次泽,一起剝皮案震驚了整個(gè)濱河市穿仪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌意荤,老刑警劉巖啊片,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異玖像,居然都是意外死亡紫谷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)笤昨,“玉大人祖驱,你說(shuō)我怎么就攤上這事÷髦希” “怎么了捺僻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)崇裁。 經(jīng)常有香客問(wèn)我匕坯,道長(zhǎng),這世上最難降的妖魔是什么寇壳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任醒颖,我火速辦了婚禮,結(jié)果婚禮上壳炎,老公的妹妹穿的比我還像新娘泞歉。我一直安慰自己,他們只是感情好匿辩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布腰耙。 她就那樣靜靜地躺著,像睡著了一般铲球。 火紅的嫁衣襯著肌膚如雪挺庞。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天稼病,我揣著相機(jī)與錄音选侨,去河邊找鬼。 笑死然走,一個(gè)胖子當(dāng)著我的面吹牛援制,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芍瑞,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼晨仑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拆檬?” 一聲冷哼從身側(cè)響起洪己,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎竟贯,沒(méi)想到半個(gè)月后答捕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屑那,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年拱镐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晌缘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痢站,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出选酗,到底是詐尸還是另有隱情阵难,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布芒填,位于F島的核電站呜叫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏殿衰。R本人自食惡果不足惜朱庆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闷祥。 院中可真熱鬧娱颊,春花似錦、人聲如沸凯砍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)悟衩。三九已至剧罩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間座泳,已是汗流浹背惠昔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挑势,地道東北人镇防。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像薛耻,于是被迫代替她去往敵國(guó)和親营罢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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

  • 分治策略 本文包括分治的基本概念二分查找快速排序歸并排序找出偽幣棋盤(pán)覆蓋最大子數(shù)組 源碼鏈接:https://gi...
    廖少少閱讀 1,850評(píng)論 0 7
  • Maximum Subarray 由于簡(jiǎn)書(shū)不支持latex語(yǔ)法饼齿,所以可以到下面的作業(yè)部落去看饲漾。https://ww...
    別時(shí)茫茫閱讀 2,315評(píng)論 0 2
  • 回溯算法 回溯法:也稱為試探法,它并不考慮問(wèn)題規(guī)模的大小缕溉,而是從問(wèn)題的最明顯的最小規(guī)模開(kāi)始逐步求解出可能的答案考传,并...
    fredal閱讀 13,657評(píng)論 0 89
  • “川師大殺人案”,這幾天也是熱搜詞匯证鸥,事件是指2016年3月27日晚上11點(diǎn)50分僚楞,四川師范大學(xué)舞蹈學(xué)院發(fā)生殺人事...
    NowInLife閱讀 695評(píng)論 0 0
  • 10月假期歸來(lái)勤晚,閑來(lái)無(wú)事在超聲室和軍軍哥、翟翟聊天泉褐,突然覺(jué)得做個(gè)超聲吧赐写,看看月經(jīng)期快到了嗎?那么努力的要寶寶但一直...
    不貪睡的牛閱讀 228評(píng)論 0 0