是否可以使用递归技术编写组合函数?

时间:2015-07-06 10:07:17

标签: python algorithm recursion

昨天,我遇到了一个问题,需要在范围为5的迭代中计算组合。

我尝试创建自己的原始函数,而不是使用itertools.combination。它看起来像:

def combine_5(elements):
    """Find all combinations in elements with range 5."""
    temp_list = []
    for i in elements:
        cur_index = elements.index(i)
        for j in elements[cur_index+1 : ]:
            cur_index = elements.index(j)
            for k in elements[cur_index+1 : ]:
                cur_index = elements.index(k)
                for n in elements[cur_index+1 : ]:
                    cur_index = elements.index(n)
                    for m in elements[cur_index+1 : ]:
                        temp_list.append((i,j,k,n,m))

    return temp_list

然后我想也许我可以抽象一点,制作一个combine_n函数。以下是我最初的蓝图:

# Unfinished version of combine_n
def combine_n(elements, r, cur_index=-1):
    """Find all combinations in elements with range n"""
    r -= 1 
    target_list = elements[cur_index+1 : ]
    for i in target_list:
        cur_index = elements.index(i)
        if r > 0:
            combine_n(elements, r, cur_index)
            pass
        else:
            pass

然后我被困在那里一整天,主要的问题是我无法在递归函数中正确传达一个值。我添加了一些修复一个问题的代码。但是因为它适用于每个递归循环,所以出现了新的问题。更多修复导致更多错误,恶性循环。

然后我去寻找itertools.combination源代码的帮助。事实证明它没有使用递归技术。

您是否认为可以使用递归技术将此combine_5函数抽象为combine_n函数?你对它的实现有任何想法吗?

失败样本1:

def combine_n(elements, r, cur_index=-1):
    """Find all combinations in elements with range n"""
    r -= 1 
    target_list = elements[cur_index+1 : ]
    for i in target_list:
        cur_index = elements.index(i)
        if r > 0:
            combine_n(elements, r, cur_index)
            print i
        else:
            print i
  

这是我最近在一系列过于复杂的实验后的尝试   核心思想是:如果我能正确打印出来,我可以稍后将它们收集到一个容器中   但问题是,在嵌套for循环中,当较低的for循环命中一个空列表时   temp_list.append((i,j,k,n,m))的{​​{1}}条款不起作用。
  但是在combine_5中,它仍然会打印上层for循环的内容   像combine_n([0,1],2)将打印FAILURE SAMPLE 1   我需要找到一种方法将这个空信息传达给上级for循环   到目前为止我还没弄明白。

1 个答案:

答案 0 :(得分:1)

是的,通过递归可以做到这一点。你可以让combine_n返回一个元组列表,其中包含从索引cur_index开始的所有组合,并从cur_combo的部分组合开始,这是你在递归时构建的:

def combine_n(elements, r, cur_index=0, cur_combo=()):
    r-=1
    temp_list = []
    for elem_index in range(cur_index, len(elements)-r):
        i = elements[elem_index]
        if r > 0:
            temp_list = temp_list + combine_n(elements, r, elem_index+1, cur_combo+(i,))
        else:
            temp_list.append(cur_combo+(i,))
    return temp_list

elements = list(range(1,6))
print = combine_n(elements, 3)

输出:

[(1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5), (2, 3, 4), (2, 3, 5), (2, 4, 5), (3, 4, 5)]

for循环仅上升到len(元素)-r,因为如果你走得更远,那么剩下的元素就没有足够的元素来填充元组中的剩余位置。元组只会在最后一级递归时添加到列表中,然后通过返回temp_lists并在每个级别连接回顶部来传递回调用堆栈。