maximum subarray problem

来源:
维基百科对该问题的描述
在计算机科学中,求最大子序列和的问题,目的在于从一维数组a[1,2,…n]中找到连续的子序列,并且和最大。
这里写图片描述
序列通常包含正负数以及0.。本问题有以下性质:
1.序列元素均为非正数,所求为所有数相加后的最小值。
2.序列元素均为非负数,所求为所有元素之和。
3.序列非空
4.允许多个不同的子序列,有相同的最大子序列之和。
以下将讨论解决该问题的不同算法,并分析其时间复杂度。

最大子序列之和用于多个领域的数据分析,如基因序列分析,计算机视觉,数据挖掘。

Genomic sequence analysis employs maximum subarray algorithms to identify important biological segments of protein sequences and the information for the purpose of predicting outcomes[clarify]. As an example, specific information of a protein sequence can be organized into a linear function which can be used to understand the structure and function of a protein.[clarify] Biologists find this approach to be efficient and easy to analyze their data.The score-based technique of finding the segment with the highest total sum is used in many problems similar to the MSP. In genomic sequence analysis, these problems include conserved segments, GC-rich regions, tandem repeats, low-complexity filter, DNA binding domains, and regions of high charge。
For computer vision , maximum subarray algorithms are used in bitmap images to detect the highest score subsequence which represents the brightest area in an image. The image is a two dimensional array of positive values that corresponds to the brightness of a pixel. The algorithm is evaluated after normalizing every value in the array by subtracting them from the mean brightness.
Data mining is an application of maximum subarray algorithms with numerical attributes. To understand the role of the maximum subarray problem in data mining it is important to be familiar with the association rule and its parts. The association rule is an if/then statement that creates relationships between unrelated pieces of data. The two parts of the association rule are the antecedent (if statement) and the consequent (then statement). In business applications of data mining, association rules are important in examining and foreseeing customer’s actions/behavior. Data mining is used for companies to anticipate common trends, by representing problems with maximum subarray problem into an sub-array to be normalized and solved. The highest result[clarify] of the array will give companies information of what customers are responding well to and will influence the companies’ actions as a result.

问题: 给定一个数列,例如【−2, 1, −3, 4, −1, 2, 1, −5, 4】, 求一个连续的数列使得数列内的元素和最大, 示例中最大子数列应该是【4, −1, 2, 1】, 求和值为6。

这个问题是可以衍生到一些变种问题, 如寻找数列中最大乘积序列,且要求序列中,相邻元素间隔不超过限定值等, 常出现在笔试面试编程题中。

该问题最早于1977年提出,但是直到1984年才被Jay Kadane 发现了线性时间的最优解法,所以算法虽然长度很短,但其实并不容易理解。

算法1. Brute Force暴力解决
需要两个for循环,列举出所有的数组A中所有成对的i,j。通过计算A[i..j-1]+A[j]得到数组A[i,…j]的和。

 1 int maxSumSoFar=0;
 2 int maxISoFar=0;
 3 int maxJSoFar=-1;
 4 for(int i=0; i<n; i++){
 5     int sum=0;
 6     for(int j=i; j<n; j++){
 7         sum+=A[j]; //sum is that of A[i..j]
 8         if(sum>maxSumSoFar){
 9             maxSumSoFar=sum;
10             maxISoFar=i;
11             maxJSoFar=j;
12         }
13     }
14 }

时间复杂度:O(n^2)

算法2:Divide and conquer分治法
字面上的解释是“分而治之”,通常是递归算法,就是 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

import numpy as np
 2 def divide_and_conquer(A):
 3     # Array of cumulative sums allows for the computation of overlapping solution in constant time.
 4     sums = np.insert(np.cumsum(A), 0, 0)
 5     def loop(l,r):
 6         if l == r: return (A[l], sums[l], sums[l+1])
 7         else: 
 8             # If I have more than one element, split the array and loop over left and right part.
 9             c = (l + r - 1) // 2
10             resLeft, minPrefixLeft, maxPrefixLeft = loop(l, c)
11             resRight, minPrefixRight, maxPrefixRight = loop(c + 1, r)
12             minPrefix = min(minPrefixLeft, minPrefixRight)
13             maxPrefix = max(maxPrefixLeft, maxPrefixRight)
14             # Solution overlapping both the left and the right part can be computed as follows.
15             resCenter = maxPrefixRight - minPrefixLeft
16             # Overall solution is maximum of solutions on the left, on the right and overlapping.
17             res = max(resLeft, resCenter, resRight)
18             return (res, minPrefix, maxPrefix)
19     res, _, _ = loop(0, len(A)-1)
20     return res

若要使用分治法,则需满足以下条件:
该问题的规模缩小到一定的程度就可以容易地解决; 该问题可以分解为若干个规模较小的相同问题;
分解出的子问题的解可以合并为原问题的解; 分解出的各个子问题是相互独立的。
T(n)= 2T(n/2) + O(1), T(1) = O(1)
Where 2T(n/2) represent the two recursion calls and the division of the list, and O(1) represents the time for calculating mCenter at each recursion. Then as a result T(n)= O(n).
Thus the total running time of this algorithm is O(n).

算法3:Kadane算法
算法描述:

遍历该数组, 在遍历过程中, 将遍历到的元素依次累加起来, 当累加结果小于或等于0时, 从下一个元素开始,重新开始累加。
累加过程中, 要用一个变量(max_so_far)记录所获得过的最大值
一次遍历之后, 变量 max_so_far 中存储的即为最大子片段的和值。

def max_subarray(A):
    max_ending_here = max_so_far = 0
    for x in A:
        max_ending_here = max(0, max_ending_here + x)
        max_so_far = max(max_so_far, max_ending_here)
    return max_so_far

首先, 题目中有一个隐含的设定, 最大子片段是可以为空的, 空片段的和值是0。 这一点必须要明确, 不然会无法理解算法的正确性, 所以当给定的数列中,求和为负数的时候,例如【-2,1, -3】, 算法会返回最大求和值0, 因为默认该数组的最大子片段是一个空序列。

理解此算法的关键在于:

最大子片段中不可能包含求和值为负的前缀。 例如 【-2, 1,4】 必然不能是最大子数列, 因为去掉值为负的前缀后【-2,1】, 可以得到一个更大的子数列 【4】、
所以在遍历过程中,每当累加结果成为一个非正值时, 就应当将下一个元素作为潜在最大子数列的起始元素, 重新开始累加。
由于在累加过程中, 出现过的最大值都会被记录, 且每一个可能成为 最大子数列起始元素 的位置, 都会导致新一轮的累加, 这样就保证了答案搜索过程的完备性和正确性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值