从列表中返回最大的子集或数字值,等于x

时间:2017-11-08 16:49:25

标签: python-3.x recursion dynamic-programming memoization subset-sum

我试图返回构成给定值总和的最大数字,如果有一个数字从列表中构成该值则返回

numberlist = [0,1,2,3,4,5,8,16,32,64,128]

def getSubsetOrSingle(x,listofvalues):
    if x in listofvalues:
        return x
    else:
        return list()# list of the biggest values available that make up x

我能够匹配列表中x的值,例如,如果您要传递

getSubsetOrSingle(128, numberlist)

它会返回128但是我想传递208并从列表中获取相当于它的最大可能值:

>>> print(getSubsetOrSingle(208, numberlist))
>>> [16,64,128]
>>> print(getSubsetOrSingle(128, numberlist))
>>> 128
>>> print(getSubsetOrSingle(33, numberlist))
>>> [1,32]
>>> print(getSubsetOrSingle(136, numberlist))
>>> [8,128]

1 个答案:

答案 0 :(得分:2)

我将这个问题分成两个案例来解决这个问题。检查目标编号是否与输入列表中的任何元素完全相等的情况可能很容易实现。你正在看的另一个案例可以通过一个很好的动态编程/记忆方法来解决。

让我们从一个缓慢的递归算法开始,然后我们可以将其转换为DP风格的重复。想象一下,您想要解决以下问题:

  

只使用列表中的前k个数字,您可以使用多少个数来计算某个目标数T?

作为基本情况,如果k = 0(也就是说,你可以使用零数字),那么你可以使用零数字使T = 0,并且不可能使T> 0。 0在任何方面。我们将通过说你需要 - 和无穷大来表示这一点;数字来表示这是不可能的。

现在,假设你有k> 0个号码可以使用。在这种情况下,您有两种选择。一种选择是将第k个数字作为总数的一部分。如果该数字是m,那么你想要使用尽可能多的数字使用第一个k-1数字来形成T-m。 (如果m≤T,你只能这样做。)另一种选择是不包括第k个数字,在这种情况下你想要使用尽可能多的硬币从第一个k - 1个硬币中形成T.

这是一些粗略的伪代码。我假设在负无穷大处添加任何东西都会给出负无穷大:

function mostNumbersFor(numberList, k, T) {
    /* Base case: if T < 0, we can’t make T. */
    if (T < 0) return -infinity;

    /* Base case: if k = 0, we can only make T = 0. */
    if (k == 0) {
        return T == 0? 0 : -infinity;
    }

    /* Otherwise, we either include the current number, or we don’t.
     * We take the better of the two options.
     */
    return max(mostNumbersFor(numberList, k - 1, T),
               mostNumbersFor(numberList, k - 1, T - numberList[k - 1]) + 1);
}

如果你再打电话

mostNumbersFor(numberList, numberList.length + 1, T);

您将获得恰好与T相加的最大数量。

现在,这种方法确实很慢,因为它会进行各种重复的递归调用。但是,如果添加memoization或动态编程以消除这些冗余调用,则速度会快得多。具体地,对于k(其中n是输入列表的长度)仅存在O(n)个可能值,并且对于T参数仅存在O(W)个可能值,其中W是输入目标值。这使得整体运行时间为O(nW),这在W中是伪多项式的,并且在实践中对于小的W值可能非常快。