摘要:動態規劃法用表示最大子數組的結束下標為的情形,則對于,有這樣就有了一個子結構,對于初始情形,遍歷就能得到這個數組,其最大者即可最大子數組的和。動態規劃法想法巧妙,運行效率也高,但是沒有普遍的適用性。
問題簡介
??本文將介紹計算機算法中的經典問題——最大子數組問題(maximum subarray problem)。所謂的最大子數組問題,指的是:給定一個數組A,尋找A的和最大的非空連續子數組。比如,數組 A = [-2, -3, 4, -1, -2, 1, 5, -3], 最大子數組應為[4, -1, -2, 1, 5],其和為7。
??首先,如果A中的元素全部為正(或非負數),則最大子數組就是它本身;如果A中的元素全部為負,則最大子數組就是第一個元素組成的數組。以上兩種情形是平凡的,那么,如果A中的元素既有正數,又有負數,則該如何求解呢?本文將介紹該問題的四種算法,并給出后面三種算法的Python語言實現,解決該問題的算法如下:
暴力求解
分治法
Kadane算法
動態規劃法
??下面就這四種算法做詳細介紹。
暴力求解??假設數組的長度為n,暴力求解方法的思路是很簡單的,就是將子數組的開始坐標和結束坐標都遍歷一下,這樣共有$C_{n}^{2}$中組合方式,再考慮這所有組合方式中和最大的情形即可。
??該算法的運行時間為$O(n^{2})$,效率是很低的。那么,還有其它高效的算法嗎?
??分治法的基本思想是將問題劃分為一些子問題,子問題的形式與原問題一樣,只是規模更小,遞歸地求解出子問題,如果子問題的規模足夠小,則停止遞歸,直接求解,最后將子問題的解組合成原問題的解。
??對于最大子數組,我們要尋求子數組A[low...high]的最大子數組。令mid為該子數組的中央位置,我們考慮求解兩個子數組A[low...mid]和A[mid+1...high]。A[low...high]的任何連續子數組A[i...j]所處的位置必然是以下三種情況之一:
完全位于子數組A[low...mid]中,因此$lowleq ileq j leq mid.$
完全位于子數組A[mid+1...high]中,因此$mid< ileq j leq high.$
跨越了中點,因此$low leq i leq mid < j leq high.$
因此,最大子數組必定為上述3種情況中的最大者。對于情形1和情形2,可以遞歸地求解,剩下的就是尋找跨越中點的最大子數組。 ??有了FIND-MAX-CROSSING-SUBARRAY我們可以找到跨越中點的最大子數組,于是,我們也可以設計求解最大子數組問題的分治算法了,其偽代碼如下: ??顯然這樣的分治算法對于初學者來說,有點難度,但是熟能生巧, 多學多練也就不難了。該分治算法的運行時間為$O(n*logn).$ ??Kadane算法的偽代碼如下: ??Kadane算法的簡單想法就是尋找所有連續的正的子數組(max_ending_here就是用來干這事的),同時,記錄所有這些連續的正的子數組中的和最大的連續數組。每一次我們得到一個正數,就將它與max_so_far比較,如果它的值比max_so_far大,則更新max_so_far的值。 ?? 用MS[i]表示最大子數組的結束下標為i的情形,則對于i-1,有: ?? 可以看到以上四種算法,每種都有各自的優缺點。對于暴力求解方法,想法最簡單,但是算法效率不高。Kanade算法簡單高效,但是不易想到。分治算法運行效率高,但其分支過程的設計較為麻煩。動態規劃法想法巧妙,運行效率也高,但是沒有普遍的適用性。 ?? 下面將給出分治算法,Kanade算法和動態規劃法來求解最大子數組問題的Python程序, 代碼如下: 輸出結果如下: 算法導論(第三版) 機械工業出版社 https://www.geeksforgeeks.org... https://algorithms.tutorialho... 注意:本人現已開通兩個微信公眾號: 用Python做數學(微信號為:python_math)以及輕松學會Python爬蟲(微信號為:easy_web_scrape), 歡迎大家關注哦~~
??任何跨越中點的子數組都是由兩個子數組A[i...mid]和A[mid+1...j]組成,其中$low leq i leq mid$且$midFIND-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-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)
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
$$MS[i] = max{MS[i-1], A[i]}.$$
這樣就有了一個子結構,對于初始情形,$MS[1]=A[1].$遍歷i, 就能得到MS這個數組,其最大者即可最大子數組的和。# -*- 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), "
")
print("using Kanade Algorithm...")
print("Maximum contiguous sum is", maxSubArraySum(A, len(A)), "
")
print("using dynamic programming...")
MS = DP_maximum_subarray(A)
print("Maximum contiguous sum is", max(MS), "
")
main()
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
參考文獻
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/41824.html
摘要:最新更新請見原題鏈接動態規劃復雜度時間空間思路這是一道非常典型的動態規劃題,為了求整個字符串最大的子序列和,我們將先求較小的字符串的最大子序列和。而最大子序列和的算法和上個解法還是一樣的。 Maximum Subarray 最新更新請見:https://yanjia.me/zh/2019/02/... Find the contiguous subarray within an ar...
摘要:題目要求從一個整數數組中找到一個子數組,該子數組中的所有元素的乘積最大。比如數組的最大乘積子數組為思路與代碼這題目考察了動態編程的思想。至于為什么還要比較,是因為如果是一個負數的,那么之前的最小乘積在這里可能就成為了最大的乘積了。 題目要求 Find the contiguous subarray within an array (containing at least one num...
摘要:我們可以分別得出這三種情況下的最大子數列和,并比較得出最大的那個。我們只需要考慮左子列的最大和以及跨越了左右的中子列的最大值。 題目要求 Find the contiguous subarray within an array (containing at least one number) which has the largest sum. For example, given ...
摘要:如果單開元素加和更大判斷前面的子數組和是不是小于。此元素就成為了子數組的第一個元素。每次操作都要判斷,當前是否是最大值,更新值。 題目詳情 Find the contiguous subarray within an array (containing at least one number) which has the largest sum.For example, given t...
摘要:問題本質本質動態規劃問題。局部最優,全局最優。乘法問題,存在的情況是負數或者正數,或者從當前數開始新的連續元素相乘可能發生的情況在某個節點,繼續之前的增大減小,從此節點轉折。所以只要在局部動態中,保持最大最小當前,進行判斷保留即可。 Given an integer array nums, find the contiguous subarray within an array (co...
閱讀 2870·2021-11-16 11:55
閱讀 2614·2021-09-29 09:34
閱讀 3426·2021-09-01 14:21
閱讀 3771·2019-08-29 12:36
閱讀 702·2019-08-26 10:55
閱讀 3977·2019-08-26 10:20
閱讀 1031·2019-08-23 18:19
閱讀 1199·2019-08-23 17:56