簡體   English   中英

最大總和子列表?

[英]Maximum sum sublist?

我對它的問題感到困惑。

寫函數mssl() (最小和子子列表),它將整數列表作為輸入。 然后,它計算並返回輸入列表的最大總和子列表的總和。 最大總和子列表是輸入列表的子列表(切片),其條目總和最大。 空子列表定義為總和0.例如,列表[4, -2, -8, 5, -2, 7, 7, 2, -6, 5] -2,7,7,2,-6,5]的最大總和子列表是[5, -2, 7, 7, 2]及其條目之和為19

如果我要使用這個函數,它應該返回類似的東西

>>> l = [4, -2, -8, 5, -2, 7, 7, 2, -6, 5]
>>> mssl(l)
19
>>> mssl([3,4,5])
12
>>> mssl([-2,-3,-5])
0

我該怎么做?

這是我目前的嘗試,但它不會產生預期的結果:

def mssl(x):
    ' list ==> int '
    res = 0
    for a in x:
        if a >= 0:
            res = sum(x)
        return res
    else:
        return 0

實際上,使用動態編程實現了非常優雅,非常有效的解決方案 它需要O(1)空間O(n)時間 - 這不能被擊敗!

A定義為輸入數組(零索引),將B[i]為所有子列表的最大總和,但不包括位置i (即所有子列表A[j:i] )。 因此, B[0] = 0B[1] = max(B[0]+A[0], 0)B[2] = max(B[1]+A[1], 0)B[3] = max(B[2]+A[2], 0) ,依此類推。 然后,顯然,解決方案簡單地由max(B[0], ..., B[n])

由於每個B值僅取決於前一個B ,我們可以避免存儲整個B數組,從而為我們提供O(1)空間保證。

通過這種方法, mssl為一個非常簡單的循環:

def mssl(l):
    best = cur = 0
    for i in l:
        cur = max(cur + i, 0)
        best = max(best, cur)
    return best

示范:

>>> mssl([3,4,5])
12
>>> mssl([4, -2, -8, 5, -2, 7, 7, 2, -6, 5])
19
>>> mssl([-2,-3,-5])
0

如果你想要開始和結束切片索引,你需要跟蹤更多的信息(注意這仍然是O(1)空間和O(n)時間,它只是有點毛茸茸):

def mssl(l):
    best = cur = 0
    curi = starti = besti = 0
    for ind, i in enumerate(l):
        if cur+i > 0:
            cur += i
        else: # reset start position
            cur, curi = 0, ind+1

        if cur > best:
            starti, besti, best = curi, ind+1, cur
    return starti, besti, best

這將返回一個元組(a, b, c) ,使得sum(l[a:b]) == cc為maximal:

>>> mssl([4, -2, -8, 5, -2, 7, 7, 2, -6, 5])
(3, 8, 19)
>>> sum([4, -2, -8, 5, -2, 7, 7, 2, -6, 5][3:8])
19

這是最大的子陣列問題 Kadane的算法可以在O(n)時間和O(1)空間中解決它,它如下:

def mssl(x):
    max_ending_here = max_so_far = 0
    for a in x:
        max_ending_here = max(0, max_ending_here + a)
        max_so_far = max(max_so_far, max_ending_here)
    return max_so_far

根據問題,萬一,如果列表中的所有元素都是負數,它應該返回最大總和為'ZERO'

相反,如果您希望輸出為子陣列的最大值(負數),則以下代碼將有助於:

In [21]: def mssl(l):
...:     best = cur = l[0]
...:     for i in range(len(l)):
...:         cur = max(cur + l[i], l[i])
...:         best = max(best, cur)
...:     return best

例子:

In [23]: mssl([-6, -44, -5, -4, -9, -11, -3, -99])
Out[23]: -3
In [24]: mssl([-51, -23, -8, -2, -6])
Out[24]: -2

正數和負數

In [30]: mssl([4, -2, -8, 5, -2, 7, 7, 2, -6, 5])
Out[30]: 19

因此,如果您了解子列表是什么(或切片,可以假設為相同的事物),則切片由起始索引和結束索引定義。

所以也許您可以嘗試迭代所有可能的開始和結束索引並計算相應的總和,然后返回最大值。

提示:起始索引可以在0到len(given_list)-1 結束索引可以從start_indexlen(given_list)-1 您可以使用嵌套for循環來檢查所有可能的組合。

這是Java中的一個實現,使用Kadane的算法,它打印最大總和的索引。 實現需要O(n)時間和O(1)空間。

public static void maxSumIndexes(int[] a) {

    int size = a.length;
    if(size == 0) return;

    int maxAtIndex = a[0], max = a[0];
    int bAtIndex = 0;
    int b = 0, e = 0;

    for(int i = 1; i < size; i++) {
        maxAtIndex = Math.max(a[i], a[i] + maxAtIndex);
        if(maxAtIndex == a[i])
            bAtIndex = i;

        max = Math.max(max, maxAtIndex);
        if(max == maxAtIndex) {
            e = i;
            b = (b != bAtIndex)? bAtIndex : b;
        }
    }

    System.out.println(b);
    System.out.println(e);
}

簡單的解決方案是迭代列表,只是嘗試添加切片,直到找到最佳切片。 這里我還包括返回實際子列表的選項,默認情況下為False。 我為此目的使用defaultdict,因為它比查找更簡單。

from collections import defaultdict

def mssl(lst, return_sublist=False):
    d = defaultdict(list)
    for i in range(len(lst)+1):
        for j in range(len(lst)+1):
            d[sum(lst[i:j])].append(lst[i:j])
    key = max(d.keys())
    if return_sublist:
        return (key, d[key])
    return key

print mssl([4, -2, -8, 5, -2, 7, 7, 2, -6, 5])
19
print mssl([4, -2, -8, 5, -2, 7, 7, 2, -6, 5], True)
(19, [[5, -2, 7, 7, 2]])

獎勵:列表理解方法:

def _mssl(lst):
    return max( sum( lst[i:j] ) for i in xrange(len(lst)+1) for j in xrange(i, len(lst)+1) )

它要求您選擇列表的較小子部分,以使較小子部分的總和最大。

如果列表都是正數[1 2 3]那么當然,具有最大總和的子部分只是整個列表[1 2 3]的總和,即6。

如果列表全部為負[-1 -2 -3]則具有最大總和的子部分為[] ,其總和為0。

然而,如果列表有一些積極的和一些消極的,那么決定就更難了

[1 2 3 -100 3 4 5]你應該看到[3 4 5]並返回12

[1 2 3 -2 3 4 5]你應該全部使用並返回16

我假設這是在數組中找到產生最大總和的子序列的問題。 我在搜索最大總和SUBSET問題時遇到了這個問題。

這個問題的java實現:

public static int maximumSumSubSequence(int[] array) {

    if (null == array) {
        return -1;
    }

    int maxSum = Integer.MIN_VALUE;
    int startIndexFinal = 0;
    int endIndexFinal = 0;
    int currentSum = 0;
    int startIndexCurrent = 0;

    for (int i = 0; i < array.length; i++) {
        currentSum += array[i];

        if (currentSum > maxSum) {
            maxSum = currentSum;
            endIndexFinal = i;
            startIndexFinal = startIndexCurrent;
        }
        if (currentSum <= 0) {
            currentSum = 0;
            startIndexCurrent = i + 1;
        }
    }
    System.out.println("startIndex: " + startIndexFinal + " endIndex: " + endIndexFinal);
    return maxSum;
}

這種區別可能對OP來說並不重要,OP似乎只是試圖理解如何解決問題,但我認為值得一提:

這里的其他解決方案涉及重復總結列表的所有子部分。 我們可以通過使用動態編程來避免這些重復的總和,因為當然如果我們已經知道從ij的總和,我們不需要再次添加它們來獲得從ij+1的總和!

也就是說,制作部分和的2d數組,使得partsum[i, j] == sum(lst[i:j]) 像(使用字典因為它更容易索引;一個numpy數組同樣容易和更有效):

import operator

def mssl(lst, return_sublist=False):
    partsum = { (0, 0): 0 }  # to correctly get empty list if all are negative
    for i in xrange(len(lst) - 1):  # or range() in python 3
        last = partsum[i, i+1] = lst[i]
        for j in xrange(i+1, len(lst)):
            last = partsum[i, j+1] = last + lst[j]

    if return_sublist:
        (i, j), sum = max(partsum.iteritems(), key=operator.itemgetter(1))
        return sum, lst[i:j]

    return max(partsum.itervalues())  # or viewvalues() in 2.7 / values() in 3.x

這需要O(n ^ 2)時間和內存,而不是O(n ^ 3)時間和O(1)內存用於Lev / Inbar的方法(如果沒有像Inbar的第一個代碼示例那樣狡猾地實現)。

這篇文章介紹了三種查找數組最大子數組的方法。

  • 蠻力(O(n * n))
  • 分而治之(O(nlgn))
  • 卡丹的算法(O(n))

其中,最快的是Kadane算法,其時間復雜度為O(n)。

如果有人正在尋找更長版本的代碼,這里是:

def mesl(lst):
    sub_sum = list()
    row_sum = list()
    for i in range(len(lst)):
        sub_sum = list()
        sub_sum.append(lst[i])
        k = 1
        for j in range(i+1,len(lst)):
            sub_sum.append(sub_sum[k-1] + lst[j])
            k+=1
        row_sum.append(max(sub_sum))      
    sum = max(row_sum)
    if  sum < 0:
        sum = 0
    return sum

我提出了一種基於動態編程的方法。 主要思想是,當我們遍歷數組時,向我們當前的sum-value添加一個新元素應該增加sum的值,或者,我們繼續使用當前元素並忘記舊的sum-value。

為了適應具有負值的數組,我們使用數組的第一個元素實例化變量。

def maxSumSubArr(arr):
    cur_sum = best_sum = arr[0]
    for i in range(1, len(arr)):
        cur_sum = max(arr[i], cur_sum+arr[i])
        best_sum = max(best_sum, cur_sum)
    return best_sum

該方法的運行時間為O(n),空間復雜度為O(1)。

如果您希望在沒有元素為正的情況下輸出為零,則將cur_sum和best_sum變量實例化為0並從第一個元素而不是第二個元素迭代。

這是javascript中最短和最好的解決方案,以解決最大的子陣列問題:

var maxSubArray = function(nums) {
    for (let i = 1; i < nums.length; i++){
        nums[i] = Math.max(nums[i], nums[i] + nums[i - 1]);
    }
    return Math.max(...nums);
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM