如何在不使用递归的情况下确定一组整数的总和

7
这是我在Stack Overflow上的第一篇文章,希望它是好的。
这是一个我自己想出来的问题,现在我有点尴尬地说,但它正在打败我。请注意,这不是一个作业练习,童子军的荣誉。
基本上,该程序将一个由0到9的整数组成的字符串作为输入。
strInput = '2415043'

那么你需要将那一串数字拆分成更小的数字组,直到最终这些组的总和等于预定义的总数。 在上述字符串的情况下,目标是289。

iTarget = 289

对于这个例子,有两个正确答案(但最有可能只显示一个,因为程序一旦达到目标就会停止):

Answer 1 = 241, 5, 043    (241 + 5 + 043    = 289)  

答案2 = 241、5、0、43(241 + 5 + 0 + 43 = 289)。
请注意,整数的位置不会改变。它们仍然按照原始字符串中的顺序排列。
现在,我知道如何使用递归解决这个问题。但令人沮丧的是,我不被允许使用递归
这需要使用只有while和for循环来解决。当然,也可以使用列表和函数。
以下是我目前的一些代码:我的代码:
                                         #Pre-defined input values, for the sake of simplicity
lstInput = ['2','4','1','5','0','4','3'] #This is the kind of list the user will input
sJoinedList = "".join(lstInput)          #sJoinedList = '2415043'
lstWorkingList = []                      #All further calculuations are performed on lstWorkingList
lstWorkingList.append(sJoinedList)       #lstWorkingList = ['2415043']
iTarget = 289                            #Target is pre-defined

-

def SumAll(_lst):          #Adds up all the elements in a list
   iAnswer = 0             #E.g. lstEg = [2,41,82]
     for r in _lst:        #     SumAll(lstEg) = 125
       iAnswer += int(r)
   return(iAnswer) 

-

def AddComma(_lst):
                                  #Adds 1 more comma to a list and resets all commas to start of list
                                  #E.g. lstEg = [5,1001,300]  (Note only 3 groups / 2 commas)
                                  #     AddComma(lstEg)
                                  #     [5,1,0,001300] (Now 4 groups / 3 commas)
    iNoOfCommas = len(_lst) - 1   #Current number of commas in list
    sResetString = "".join(_lst)  #Make a string with all the elements in the list
    lstTemporaryList = []
    sTemp = ""
    i = 0
    while i < iNoOfCommas +1:
        sTemp += sResetString[i]+','    #Add a comma after every element
        i += 1
    sTemp += sResetString[i:]       
    lstTemporaryList = sTemp.split(',') #Split sTemp into a list, using ',' as a separator
                                        #Returns list in format ['2', '415043'] or ['2', '4', '15043']
    return(lstTemporaryList)
    return(iAnswer)

基本上,伪代码会像这样:
伪代码:
while SumAll(lstWorkingList) != iTarget:      #While Sum != 289
    if(len(lstWorkingList[0]) == iMaxLength): #If max possible length of first element is reached
        AddComma(lstWorkingList)              #then add a new comma / group and
        Reset(lstWorkingList)                 #reset all the commas to the beginning of the list to start again
    else:
        ShiftGroups()                         #Keep shifting the comma's until all possible combinations
                                              #for this number of comma's have been tried
                                              #Otherwise, Add another comma and repeat the whole process

哎呀,这真是够啰嗦的。

我已经在纸上详细写下了程序所要遵循的流程,因此以下是预期的输出结果:

输出:

[2415043]  #Element 0 has reached maximum size, so add another group 
#AddComma()
#Reset()
[2, 415043] #ShiftGroups()
[24, 15043] #ShiftGroups()
[241, 5043] #ShiftGroups()
#...etc...etc...
[241504, 3] #Element 0 has reached maximum size, so add another group
#AddComma()
#Reset()
[2, 4, 15043] #ShiftGroups()
[2, 41, 5043] #ShiftGroups()
#etc...etc...

[2, 41504, 3] #Tricky part

现在来到了棘手的部分。 在下一步中,第一个元素必须变成24,而其他两个必须重置。

#Increase Element 0
#All other elements Reset() 
[24, 1, 5043] #ShiftGroups()
[24, 15, 043] #ShiftGroups()
#...etc...etc

[24, 1504, 3]
#Increase Element 0
#All other elements Reset()
[241, 5, 043] #BINGO!!!!

好的,这是程序逻辑的基本流程。现在我需要解决的唯一问题就是如何在没有递归的情况下使其工作。
对于那些一直阅读到这里的人,我真诚地感谢你们,并希望你们还有精力来帮助我解决这个问题。如果有任何不清楚的地方,请问我,我会详细解释的。
再次感谢!
编辑:2011年9月1日
非常感谢大家的回复和答案。它们都非常好,肯定比我走过的路线更加优雅。然而,我的学生从未使用“import”或任何比列表更高级的数据结构。但是,他们确实知道很多列表函数。
我还应该指出,这些学生在数学上非常有天赋,其中许多人曾参加并获得了国际数学奥林匹克竞赛的奖项。因此,这项任务并不超出他们的智力范围,也许只是超出了他们的Python知识范围。
昨晚我有了一个顿悟时刻。我还没有实现它,但会在周末期间完成并在这里发布我的结果。它可能有点粗糙,但我认为它能完成任务。
很抱歉让我这么久才回复,我的网络流量已经用完了,我不得不等到1日才能重置。顺便提一下,对于在南半球的人来说,春天快乐。
再次感谢你们的贡献。周末后我会选择最佳答案。
问候!

3
递归会创建一个“隐式”堆栈——如何将其变为“显式”? - user166390
2
为什么如果你自己想到了递归,就不能使用它?你是不是已经承诺在某个课程项目中要使用它了? - Tom Zych
2
作业?如果“不能使用递归”,那肯定是作业。 - Ken White
2
伪匈牙利命名法?lstWorkingList?呸! - johnsyweb
你在自己解决问题之前就分配了这个任务?好吧,我猜你不会再犯同样的错误了。我认为以下任何一个可行的解决方案对于初学者来说都是难以掌握的,除非他们被引导完成整个过程。 - agf
显示剩余5条评论
3个回答

3

一个可以找到所有解决方案的程序可以用优雅的函数式风格表达。

划分

首先,编写一个函数,将您的字符串划分为所有可能的方式。(以下实现基于http://code.activestate.com/recipes/576795/。) 示例:

def partitions(iterable):
    'Returns a list of all partitions of the parameter.'
    from itertools import chain, combinations
    s = iterable if hasattr(iterable, '__getslice__') else tuple(iterable)
    n = len(s)
    first, middle, last = [0], range(1, n), [n]
    return [map(s.__getslice__, chain(first, div), chain(div, last))
            for i in range(n) for div in combinations(middle, i)]

谓词

现在,您需要过滤列表以找到那些总和为期望值的分区。因此,编写一个小函数来测试分区是否满足此条件:

def pred(target):
   'Returns a function that returns True iff the numbers in the partition sum to iTarget.'
   return lambda partition: target == sum(map(int, partition))

主程序

最后,编写您的主程序:

strInput = '2415043'
iTarget = 289

# Run through the list of partitions and find partitions that satisfy pred
print filter(pred(iTarget), partitions(strInput))

请注意,这个结果是在一行代码中计算出来的。
结果:[['241','5','043'],['241','5','0','43']]

我使用的同一方法的更整洁版本。 - agf

3

递归并不是最好的工具,itertools.product 才是。

以下是我搜索的方法:

将搜索空间想象为长度为l(l为你的字符串长度减一)的所有二进制字符串。

选择其中一个二进制字符串。

将二进制字符串中的数字插入到搜索字符串中的数字之间。

2 4 1 5 0 4 3
 1 0 1 0 1 0

把1替换成逗号,把0替换成空格。
2,4 1,5 0,4 3

把所有东西加起来。
2,4 1,5 0,4 3 = 136

这个数字不是289,使用另一个二进制字符串再试一次。

2 4 1 5 0 4 3
 1 0 1 0 1 1

你有一个想法。

接下来是代码!

import itertools

strInput = '2415043'
intInput = map(int,strInput)
correctOutput = 289

# Somewhat inelegant, but what the heck
JOIN = 0
COMMA = 1

for combo in itertools.product((JOIN, COMMA), repeat = len(strInput) - 1):
    solution = []
    # The first element is ALWAYS a new one.
    for command, character in zip((COMMA,) + combo, intInput):
        if command == JOIN:
            # Append the new digit to the end of the most recent entry
            newValue = (solution[-1] * 10) + character
            solution[-1] = newValue
        elif command == COMMA:
            # Create a new entry
            solution.append(character)
        else:
            # Should never happen
            raise Exception("Invalid command code: " + command)
    if sum(solution) == correctOutput:
        print solution

编辑: agf发布了另一个版本的代码。它将字符串连接在一起,而不是像我有点粗暴的方法那样乘以10再加。此外,它使用true和false代替我的JOIN和COMMA常量。我认为这两种方法同样好,但当然我偏见。 :)

import itertools
strInput = '2415043'
correctOutput = 289
for combo in itertools.product((True, False), repeat = len(strInput) - 1):
    solution = []
    for command, character in zip((False,) + combo, strInput):
        if command:
            solution[-1] += character
        else:
            solution.append(character)
            solution = [int(x) for x in solution]
        if sum(solution) == correctOutput:
            print solution

我喜欢这个方法;我认为它是最清楚的。你可以进一步简化它(分号用于显示换行):`import itertools;strInput = '2415043'; correctOutput = 289;for combo in itertools.product((True, False), repeat = len(strInput) - 1): solution = []; for command, character in zip((False,) + combo, strInput): if command: solution[-1] += character; else: solution.append(character); solution = [int(x) for x in solution]; if sum(solution) == correctOutput: print solution;` - agf

1
进一步解释pst的提示,你可以创建一个显式栈并使用它来实现递归算法,而不必实际上进行任何递归调用,这与仅使用调用堆栈的递归不同。具体细节留给学生自己思考。 ;)

这难道不比实际使用递归更复杂吗? - Nick ODell
@Nick ODell:当然。现在,既然OP告诉我们这个限制的原因,我们可以看出这不是一个合适的方法。另一方面,如果OP是一个学生,需要应对任意强加的限制(“找到一种不使用递归的方法”),那么这将是一个有效的解决方案。 - Tom Zych

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接