如何从列表中分解另一个数字?

时间:2011-09-22 17:56:00

标签: python algorithm

我对如何解决这个问题感到有些困惑。我知道我想做什么,但无法解决如何从逻辑上解决这个问题。 说我有一个清单:

numlist = [10,4]

我在另一个列表中有以下值:

datalist = [10,5,4,2,1]

如何仅使用numlist中的数字来细分datalist中的数字?

答案的一个例子是:

10, 4
10, 2,2
10, 2,1,1
10, 1,1,1,1
5,5, 4
5,5, 2,2

......等等。

我理解如何模糊地做到这一点。为列表中的每个条目创建一个for循环,并比较它是否可以被datalist分割,如果是,则打印结果。我想我需要递归,这是我无法理解的地方。

这是我到目前为止的代码(我有一些用于故障排除的打印语句):

def variableRecursion(self, solutionList):
    #solution list contrains ['red', 2, 'green', 1] which means 2 reds(value 4) and 1 green(value 2)

    #adding fake lookup list for now, in real code, I can use real data because I am reversing the order
    list = [('red', 4), ('green', 2), ('blue', 1) ]
    for x1, x2 in zip(solutionList[::2], solutionList[1::2]):
        for x in list:
            for y1, y2 in zip(x[::2], x[1::2]):
                #print x1, x2
                keyName = x1
                keyShares = x2
                keyValue = lookup.get_value(x1)
                if ((keyValue%y2) == 0) and (keyValue != y2):
                    tempList = []
                    #print 'You can break ', keyName, keyValue, ' with ', y1, y2, ' exactly ', keyValue/x2, ' times.'
                    #newKeyShares = keyShares - 1
                    for a1, a2 in zip(solutionList[::2], solutionList[1::2]):
                        #print a1, a2
                        print 'You can break ', keyName, keyValue, ' with ', y1, y2, ' exactly ', keyValue/y2, ' times.'
                        newKeyShares = keyShares - 1
                        print 'there is a match', a1, a2, ' but we will change the shares to ', newKeyShares
                        print a1
                        if (a1 == keyName):
                            print 'a'
                            tempList.append([(keyName), (newKeyShares)])
                        elif (a1 == y1):
                            print 'b'
                            tempList.append([(y1), (a2+keyValue/y2)])
                        else:
                            print 'c'
                            try:
                                tempList.append([(y1), (a2+keyValue/y2)])
                            except e:    
                                tempList.append([(a1), (a2)])
                    print tempList
                    appendList.appendList(tempList)
                    tempList = []
                    #exit()
                    #print solutionList

1 个答案:

答案 0 :(得分:3)

这个问题与欧拉项目的Problem 31非常相似:“使用任意数量的硬币可以用多少种不同的方式进行2英镑?”。仅在您的示例中,您要求枚举所有可以添加数字以获得10和4的方法。

解决问题的最佳方法是首先尝试分解一个数字。让我们看看五个可能的分手,使用数字[5,4,2,1]:

[5]
[4,1]
[2,2,1]
[2,1,1,1]
[1,1,1,1,1]

以下python代码将为您提供这些组合的列表:

def possibleSplits(value,validIncrements):
    ret = []
    for increment in validIncrements:
        if increment > value:
            continue
        if increment == value:
            ret.append([increment])
            continue
        if increment < value:
            remainder = value - increment
            toAdd = possibleSplits(remainder, validIncrements)
            for a in toAdd:
                ret.append([increment] + a)
    return ret

此代码假定其他相同答案的不同排序应视为不同。例如,当您拆分时,[4,1]和[1,4]都将显示为解决方案。如果您愿意,可以将其约束为仅具有数字排序的答案(因此[1,4]出现但不是[4,1])

def orderedPossibleSplits(value, validIncrements):
    ret = []
    splits = possibleSplits(value, validIncrements)
    for value in splits:
        value.sort()
        if value not in ret:
            ret.append(value)
    return ret

现在你可以使用它来查找10的可能拆分和4的可能拆分,并将它们组合起来:

increments = [10, 5, 4, 2, 1]
tenSplits = orderedPossibleSplits(10, increments)
fourSplits = orderedPossibleSplits(4, increments)
results = []
for tenSplit in tenSplits:
    for fourSplit in fourSplits:
        results.append(tenSplit + fourSplit)

编辑:如评论中所述,调用值为100的possibleSplits非常慢 - 超过十分钟并计数。发生这种情况的原因是因为possibleSplits(100)将递归调用possibleSplits(99),possibleSplits(98)和possibleSplits(96),每个都调用自己的三个可能的Splits,依此类推。我们可以用datalist [1,2,4]和大N来估计可能的Splits(N)的处理时间

processingTime(N)= C + processingTime(N-1)+ processingTime(N-2)+ processingTime(N-4)

持续一段时间C.

所以可能的Splits的相对时间是

N     1 | 2 | 3 | 4 | 5 ... 20    ... 98                         | 99                         | 100 
Time  1 | 1 | 1 | 4 | 7 ... 69748 ... 30633138046209681029984497 | 56343125079040471808818753 | 103631163705253975385349220

假设可能的Splits(5)需要7 ns,可能的Splits(100)需要大约3 * 10 ^ 9年。对于大多数实用程序来说,这可能是一个不合适的长时间。但是,我们可以通过利用记忆来减少这段时间。如果我们保存先前计算的调用的结果,我们可以获得线性时间复杂度,将可能的Splits(100)减少到100 ns。

评论还指出,orderedPossibleSplits(100)的预期价值约有700个元素。因此,possibleSplits(100)将具有更多的元素,因此即使使用memoization它也是不切实际的。相反,我们将丢弃它并重写orderedPossibleSplits以使用memoization,并且不依赖于possibleSplits。

#sorts each element of seq and returns it
def orderedInnerLists(seq):
    return map(sorted, seq)

#returns a copy of seq with duplicates removed
def removeDuplicates(seq):
    ret = []
    for value in seq:
        if value not in ret:
            ret.append(value)
    return ret

memoizedResults = {}
def orderedPossibleSplits(value,validIncrements):
    memoizeKey = (value, tuple(validIncrements))
    if memoizeKey in memoizedResults:
        return memoizedResults[memoizeKey]
    ret = []
    for increment in validIncrements:
        if increment > value:
            continue
        if increment == value:
            ret.append([increment])
            continue
        if increment < value:
            remainder = value - increment
            toAdd = orderedPossibleSplits(remainder, validIncrements)
            for a in toAdd:
                ret.append([increment] + a)
    memoizeValue = removeDuplicates(orderedInnerLists(ret))
    memoizedResults[memoizeKey] = memoizeValue
    return memoizeValue

在我的机器上,orderedPossibleSplits(100,[1,2,4])大约需要10秒钟 - 比我们最初的30亿年运行时间大大改善。