Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/361.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
递归的;“所有路径”;通过列表列表-Python_Python_Recursion - Fatal编程技术网

递归的;“所有路径”;通过列表列表-Python

递归的;“所有路径”;通过列表列表-Python,python,recursion,Python,Recursion,一个特殊的问题,给定一个列表列表(此处最多嵌套一个级别): ..找到所有长度与给定列表相同的列表,并包含子列表中所有可能的元素组合,给定子列表中正好有一个元素与原始子列表位于同一位置(很难用文字表达)。也就是说,找到这个: ['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

一个特殊的问题,给定一个列表列表(此处最多嵌套一个级别):

..找到所有长度与给定列表相同的列表,并包含子列表中所有可能的元素组合,给定子列表中正好有一个元素与原始子列表位于同一位置(很难用文字表达)。也就是说,找到这个:

['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
如您所见,它需要复制两次累加器列表,原因很明显,如果.append()或.extend()方法在递归堆栈中被调用,累加器将被修改,因为它是通过标签传递的(用官方术语共享?)

我试图设计出一个解决方案,使pop()和append()从累加器中删除相关数量的项,但无法正确执行:

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),所以这里没有太大的区别,但我很好奇这是否可以实现

(背景:我正在重做phonecode基准测试,这是查找所有单词的部分,但电话号码的每一部分都可以映射到几个单词,如下所示:

... '4824': ['fort', 'Torf'], '4021': ['fern'], '562': ['mir', 'Mix'] ...
因此,我需要通过与给定电话号码对应的匹配单词和/或数字的选定列表找到所有可能的“路径”)

问题、请求:

  • 不复制累加器的版本可以修复吗

  • 是否有一种使用itertools模块的解决方案

  • 还有其他更好的方法来解决这个问题吗?像非递归解决方案一样,更快的解决方案,更少的内存密集型解决方案

  • 是的,我知道这是一大堆问题,但如果有人能解决其中一个非空的子集,我将不胜感激。:-)

    是否有一种使用itertools模块的解决方案

    是的,这很简单。这对于您的特定示例来说已经足够了

    >>> import itertools
    >>> l = [['a', 'b'], 'c', ['d', 'e'], ['f', 'g'], 'h']
    >>> for i in itertools.product(*l): print list(i)
    ['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']
    
    …但正如DSM在评论中指出的那样,它之所以有效,是因为您的示例使用了一个字符串,而字符串是长度为一的序列对象。如果总是这样,你可以这样表达这个列表

    ['ab', 'c', 'de', 'fg', 'h']
    
    >>> l = [None, int, 0, 'abc', [1, 2, 3], ('a', 'b')]
    >>> for i in itertools.product(*[i if isinstance(i, (list, tuple)) else [i] for i in l]): print list(i)
    [None, <type 'int'>, 0, 'abc', 1, 'a']
    [None, <type 'int'>, 0, 'abc', 1, 'b']
    [None, <type 'int'>, 0, 'abc', 2, 'a']
    [None, <type 'int'>, 0, 'abc', 2, 'b']
    [None, <type 'int'>, 0, 'abc', 3, 'a']
    [None, <type 'int'>, 0, 'abc', 3, 'b']
    
    但是,在一般情况下,您可能希望确保所有列表项都是这样的序列

    ['ab', 'c', 'de', 'fg', 'h']
    
    >>> l = [None, int, 0, 'abc', [1, 2, 3], ('a', 'b')]
    >>> for i in itertools.product(*[i if isinstance(i, (list, tuple)) else [i] for i in l]): print list(i)
    [None, <type 'int'>, 0, 'abc', 1, 'a']
    [None, <type 'int'>, 0, 'abc', 1, 'b']
    [None, <type 'int'>, 0, 'abc', 2, 'a']
    [None, <type 'int'>, 0, 'abc', 2, 'b']
    [None, <type 'int'>, 0, 'abc', 3, 'a']
    [None, <type 'int'>, 0, 'abc', 3, 'b']
    
    >l=[None,int,0,'abc',[1,2,3],('a','b')]
    >>>对于itertools.product中的i(*[i if is instance(i,(list,tuple))else[i]对于l中的i]):打印列表(i)
    [无,0,'abc',1,'a']
    [无,0,'abc',1,'b']
    [无,0,'abc',2,'a']
    [无,0,'abc',2,'b']
    [无,0,'abc',3,'a']
    [无,0,'abc',3,'b']
    
    还有其他更好的方法来解决这个问题吗?喜欢 非递归解决方案,更快的解决方案,更少的内存密集型解决方案


    任何解决方案都可能必须以某种方式使用递归,如果不是在堆栈上,那么是在堆上。

    这是一个不太Pythonesque的版本,但可能是可展开的版本:

    def all_paths(l):
            if 1 == len(l):
                    return [ l ]
            down = all_paths(l[1:])
    
            # We iterate only lists, not tuples, not strings
            if type(l[0]) in [ list ]:
                    return [ [ ll ] + k for k in down for ll in l[0] ]
            return [ [ l[0] ] + k for k in down ]
    
    l = [['a', 'b'], 'c', ['d', 'e'], ['f', 'g'], 'h']
    
    for i in all_paths(l):
            print i
    
    在这种形式下,它在所有方面都不如
    itertools
    解决方案:时间是前者的两倍,内存影响是后者的四倍以上:

    HEAP SUMMARY (itertools)
    in use at exit: 1,867,521 bytes in 537 blocks
    total heap usage: 12,904 allocs, 12,367 frees, 8,444,917 bytes allocated
    
    HEAP SUMMARY (lists)
    in use at exit: 1,853,779 bytes in 498 blocks
    total heap usage: 57,653 allocs, 57,155 frees, 9,272,129 bytes allocated
    
    此外,展开函数(至少以一种相当简单的方式)不如
    itertools
    (速度是前者的三倍),并且内存分配量是后者的两倍:

    HEAP SUMMARY:
    in use at exit: 1,853,779 bytes in 498 blocks
    total heap usage: 26,707 allocs, 26,209 frees, 8,812,153 bytes allocated
    
    
    def all_paths(l):
            m   = len(l)-1
            val = [ i for i in range(0,m) ]
            ndx = [ 0 for i in l ]
            top = [ len(i) if type(i) in [list] else 0 for i in l ]
            while(True):
                    path = [ l[i] if top[i] == 0 else l[i][ndx[i]] for i in val ]
                    #print path
                    n   = m
                    ndx[n] = ndx[n] + 1
                    while (ndx[n] >= top[n]):
                            ndx[n] = 0
                            n = n - 1
                            if (-1 == n):
                                    return
                            ndx[n] = ndx[n] + 1
    

    (使用
    enumerate
    type(l[i])
    会导致更差的性能)。

    这是一个很好的问题,但我觉得它更适合于codereview.stackexchange.com,而不是这里-您有一个可行的解决方案,这里没有需要解决的问题。@g.d.d.c:在某种程度上,你是对的,尽管我用了错误的措辞——我真正想知道的是,itertools是否可以用来解决这个问题。我怀疑这可能,结果证明Aya能够产生这样的解决方案。请注意,如果说“c”是“反例”,或者“c”是0(在这种情况下它甚至不会运行),这可能不会给出预期的结果。@DSM很好。对更新后的答案有什么评论吗?我想这是因为我没有学习itertools。还有一个理由这么做。