一个特殊的问题,给定一个列表列表(这里最多嵌套一层):
[['a', 'b'], 'c', ['d', 'e'], ['f', 'g'], 'h']
..找到所有长度与给定列表相同并包含子列表中元素的所有可能组合的列表,其中给定子列表的恰好1个元素与原始子列表位于同一位置(甚至很难用文字表达)。也就是说,找到这个:
['a', 'c', 'd', 'f', 'h']
['a', 'c', 'd', 'g', 'h']
['a', 'c', 'e', 'f', 'h']
['a', 'c', 'e', 'g', 'h']
['b', 'c', 'd', 'f', 'h']
['b', 'c', 'd', 'g', 'h']
['b', 'c', 'e', 'f', 'h']
['b', 'c', 'e', 'g', 'h']
现在,我找到了解决方案,但对我来说并不满意:
def all_paths(s, acc=None, result=None):
# not using usual "acc = acc or []" trick, because on the next recursive call "[] or []" would be
# evaluated left to right and acc would point to SECOND [], which creates separate accumulator
# for each call stack frame
if acc is None:
acc = []
if result is None:
result = []
head, tail = s[0], s[1:]
acc_copy = acc[:]
for el in head:
acc = acc_copy[:]
acc.append(el)
if tail:
all_paths(tail, acc=acc, result=result)
else:
result.append(acc)
return result
如您所见,它涉及复制累加器列表 TWICE,原因很明显,如果 .append() 或 .extend() 方法在递归堆栈中被调用,累加器将被修改,因为它是通过标签传递的(以官方术语共享?)。
我试图制定解决方案,pop()s 和 append()s 相关数量的项目从累加器中取出,但无法正确处理:
def all_p(s, acc=None, result=None, calldepth=0, seqlen=0):
if acc is None:
acc = []
if result is None:
seqlen = len(s)
result = []
head, tail = s[0], s[1:]
for el in head:
acc.append(el)
if tail:
all_p(tail, acc=acc, result=result, calldepth=calldepth+1, seqlen=seqlen)
else:
result.append(acc[:])
print acc
for i in xrange(1+seqlen-calldepth):
acc.pop()
return result
结果:
['a', 'c', 'd', 'f', 'h']
['a', 'c', 'd', 'g', 'h']
['a', 'c', 'd', 'e', 'f', 'h']
['a', 'c', 'd', 'e', 'g', 'h']
['a', 'c', 'd', 'e', 'b', 'c', 'd', 'f', 'h']
['a', 'c', 'd', 'e', 'b', 'c', 'd', 'g', 'h']
['a', 'c', 'd', 'e', 'b', 'c', 'd', 'e', 'f', 'h']
['a', 'c', 'd', 'e', 'b', 'c', 'd', 'e', 'g', 'h']
['a', 'c', 'd', 'f', 'h']
['a', 'c', 'd', 'g', 'h']
['a', 'c', 'd', 'e', 'f', 'h']
['a', 'c', 'd', 'e', 'g', 'h']
['a', 'c', 'd', 'e', 'b', 'c', 'd', 'f', 'h']
['a', 'c', 'd', 'e', 'b', 'c', 'd', 'g', 'h']
['a', 'c', 'd', 'e', 'b', 'c', 'd', 'e', 'f', 'h']
['a', 'c', 'd', 'e', 'b', 'c', 'd', 'e', 'g', 'h']
显然,这是因为这里的深度优先递归在调用链上上下跳跃,我无法获得正确的 pop() 数量来微调累加器列表。
我确实意识到这没有什么实际好处,因为复制列表是 O(n),而从列表中弹出 k 个项目是 O(k),所以这里没有太大的区别,但我很好奇这是否可以实现。
(背景:我正在重做电话代码基准,http ://page.mi.fu-berlin.de/prechelt/phonecode/ ,这是找到所有单词的部分,但电话号码的每个部分都可以映射到几个字,像这样:
... '4824': ['fort', 'Torf'], '4021': ['fern'], '562': ['mir', 'Mix'] ...
所以我需要通过选定的匹配单词和/或数字列表找到所有可能的“路径”,对应于给定的电话号码)
问题、要求:
不复制累加器的版本可以修复吗?
有没有使用 itertools 模块的解决方案?
任何其他更好的方法来解决这个特定问题?像非递归解决方案,更快的解决方案,更少的内存密集型?
是的,我知道这是一大堆问题,但如果有人解决了其中的一个非空子集,我将不胜感激。:-)