Python的最大子数组总和

时间:2019-12-07 19:12:23

标签: python python-3.x

在代码战中有一项任务要求执行以下操作: 最大和子数组的问题在于找到数组或整数列表中连续子序列的最大和。如果列表仅由负数组成,则返回0。 所以我尝试解决这个问题,我以为我成功了,但显然不是这样。这是我想出的代码:

def maxSequence(arr):
    sums = []
    for e in arr:
        for i in range(arr.index(e)+1, len(arr)): 
            e += arr[i]
            sums.append(e)
    if all(i<0 for i in arr) or not sums:
        return 0
    else:
        max_sum = max(sums)
        return max_sum

测试表明,大约90%的时间是好的,但并非每次都有效,因此不能清除此任务。有些不起作用:64应该等于78或134应该等于144-因此,当它不起作用时,结果要少10-15左右,所以我猜一个数字。可悲的是,您看不到这些列表的示例来查看它在那里是否不起作用,这只是最终结果是错误的。我不了解我的代码未涵盖的情况。有人可以解释我应该改变为100%率吗?

4 个答案:

答案 0 :(得分:0)

您的代码有两个问题:

  • 您不考虑长度为1的子序列。您应该在e列表中添加sums
  • 您的嵌套循环从观察到e的第一次出现的索引开始,而不是从e的实际索引开始。如果出现重复,这是一个问题。

因此,以下应该起作用:

def maxSequence_fixed(arr):
    sums = []
    for j, e in enumerate(arr):
        sums.append(e)  # <-- HERE
        for i in range(j + 1, len(arr)): 
            e += arr[i]
            sums.append(e)
    if all(i<0 for i in arr) or not sums:
        return 0
    else:
        max_sum = max(sums)
        return max_sum

请注意,有比这更有效/更清洁的方法,例如:

def max_sum_subseq(items):
    iter_items = iter(items)
    try:
        temp_sum = next(iter_items)
    except StopIteration:
        temp_sum = 0
    max_sum = temp_sum
    for item in iter_items:
        temp_sum = max(temp_sum + item, item)
        max_sum = max(temp_sum, max_sum)
    return max(max_sum, 0)

时间为O(N),内存为O(1)

请注意,全负输入的情况由max(max_sum, 0)之前的return处理。如果不需要,仅返回max_sum就足够了。

一种替代方法是使用max(next(iter_items), 0)而不是仅将next(iter_items)用作第一个temp_sum值。

更快的方法可以避免相对昂贵的max()调用:

def max_sum_subseq_fast(items):
    iter_items = iter(items)
    try:
        temp_sum = next(iter_items)
    except StopIteration:
        temp_sum = 0
    max_sum = temp_sum
    for item in iter_items:
        temp_sum += item
        if item > temp_sum:
            temp_sum = item
        if temp_sum > max_sum:
            max_sum = temp_sum
    return max_sum if max_sum > 0 else 0

请注意,max_sum_subseq()max_sum_subseq_fast()都可以在任何Iterable上运行(并且与其他解决方案相反,不需要Sequence)。


另一种效率较低(但更贴近您的想法)的解决方案是:

def max_sum_subseq_slow(items):
    max_sum = 0
    for i in range(len(items)):
        for j in range(i, len(items)):
            temp_sum = sum(items[i:j + 1])
            if temp_sum > max_sum:
                max_sum = temp_sum
    return max_sum

以下一些健全性检查:

seqs = [
    [],
    [-1],
    [1, -1],
    [1, -1, 1],
    [1, -1, 1, 1],
    [1, -1, 1, 1, 1],
    [1, -1, -1, 1, 1],
]

funcs = maxSequence_OP, maxSequence_fixed, max_sum_subseq, max_sum_subseq_fast, max_sum_subseq_slow

for seq in seqs:
    print(seq)
    for func in funcs:
        print(func(seq))

# []
# 0
# 0
# 0
# 0
# 0

# [-1]
# 0
# 0
# 0
# 0
# 0

# [1, -1]
# 0
# 1
# 1
# 1
# 1

# [1, -1, 1]
# 1
# 1
# 1
# 1
# 1

# [1, -1, 1, 1]
# 2
# 2
# 2
# 2
# 2

# [1, -1, 1, 1, 1]
# 3
# 3
# 3
# 3
# 3

# [1, -1, -1, 1, 1]
# 1
# 2
# 2
# 2
# 2

可以使用以下方法生成更多测试用例:

import random


seqs = [[random.randint(-10, 10) for _ in range(20)] for __ in range(100)]

一些时间:

funcs = maxSequence_OP, maxSequence_fixed, max_sum_subseq, max_sum_subseq_fast, max_sum_subseq_slow, maxSequence_Alexander

seq = [random.randint(-10, 10) for _ in range(1000)]
for func in funcs:
    print()
    print(func.__name__)
    %timeit func(seq)

# maxSequence_OP
# 10 loops, best of 3: 168 ms per loop

# maxSequence_fixed
# 10 loops, best of 3: 78 ms per loop

# max_sum_subseq
# 1000 loops, best of 3: 292 µs per loop

# max_sum_subseq_fast
# 10000 loops, best of 3: 66.3 µs per loop

# max_sum_subseq_slow
# 1 loop, best of 3: 1.21 s per loop

# maxSequence_Alexander
# 10000 loops, best of 3: 183 µs per loop

已编辑,可解决一些小问题,包括更快的解决方案和更新的基准测试,包括与@Alexander's solution的比较)

答案 1 :(得分:0)

这是一个相当著名的问题,需要确保其自己的维基百科文章:max-subarray

python代码实际上非常优雅:

def max_sub_array(A):
  curr_max_sum = float('-inf')
  global_max = float('-inf')
  for i in A:
    curr_max_sum = max(curr_max_sum + i, i)
    global_max = max(curr_max_sum, global_max)
  return global_max

我认为您在测试用例中失败了,因为代码的时间复杂性。解决方案应具有O(N) 时间复杂度O(1) 空间复杂度

答案 2 :(得分:0)

跟踪本地和全局最大值如果局部最大值降至零以下,请开始新的整数,因为新的运行总和将始终大于先前的负总和加上新的数字。返回全局最大值。

def maxSequence(arr):
    if not arr or all(val <= 0 for val in arr):
        return 0
    local_max = global_max = arr[0]
    for val in arr[1:]:
        local_max += val
        local_max = max(0, local_max)  # Restart `local_max` if it becomes negative.
        if local_max > global_max:
            global_max = local_max
    return global_max

您的代码的时间复杂度为O(n^2)(相对于上面的O(n)),这可能就是为什么它无法通过某些测试的原因。

>>> maxSequence([0, -1, -2])
0

>>> maxSequence([1, 2, 3 -7, 10, 15])
25  # 10 + 15

>>> maxSequence([1, 2, 3 -7, 10])
10  # Final number 10

>>> maxSequence([6, 2, 3 -7, 10])
14  # 6 + 2 + 3 - 7 + 10

答案 3 :(得分:0)

from collections import namedtuple

def find_maximum_subarray(array):   
    ms = namedtuple('ms', ['low', 'high', 'sum'])
    ms.low = 0
    ms.high = 0
    ms.sum = array[0]
    next_subarray_sum = ms.sum
    i = 0
    for j in range(1, len(array)):
        next_subarray_sum = next_subarray_sum + array[j]
        if next_subarray_sum >= ms.sum:
            ms.sum = next_subarray_sum
            ms.low = i
            ms.high = j
        else:
            i = i + 1
            next_subarray_sum = next_subarray_sum - array[i-1] 
    return ms