【问题标题】:Recursively generating all the possible calculations between set elements in Python在 Python 中递归生成集合元素之间的所有可能计算
【发布时间】:2016-10-12 22:11:48
【问题描述】:

我很难理解为什么下面的递归代码(函数 count())给出了错误的计算计数,但基于手动编写的嵌套 for 循环(函数 count2())给出了正确的计数,即嗯! * 4 ^ (n-1)?

(此时不要在意输出变量。我稍后会使用它,如果我能先解决这个难题。)

我希望创建一个递归函数,可以为任意长度的列表创建计算,这就是为什么仅仅嵌套 for 循环是不够的。

import itertools
import operator
# http://stackoverflow.com/questions/2983139/assign-operator-to-variable-in-python
ops = {
    0: operator.add,
    1: operator.sub,
    2: operator.mul,
    3: operator.truediv
}
comb = [4, 1, 2, 3]
perms = list()
# itertools.permutations is not subscriptable, so this is a mandatory step.
# See e.g. http://stackoverflow.com/questions/216972/in-python-what-does-it-mean-if-an-object-is-subscriptable-or-not
# for details.
for i in itertools.permutations(comb):
    perms.append(i)
output = list()
output2 = list()

# In theory, there are n! * 4 ^ (n-1) possibilities for each set.
# In practice however some of these are redundant, because multiplication and
# addition are indifferent to calculation order. That's not tested here;
# nor is the possibility of division by zero.

# Variable debug is there just to enable checking the calculation count;
# it serves no other purpose.
debug = list()
debug2 = list()

def count(i):
    for j in range(len(i)):
        for op in ops:
            if j+1 < len(i):
                res = ops[op](i[j], i[j+1])
                if j+2 < len(i):
                    ls = list(i[j+1:])
                    ls[0] = res
                    count(ls)
                else:
                    debug.append([len(i), i[j], ops[op], i[j+1], res])
                    if res == 10: output.append(res)

def count2(i):
    for j in range(len(i)):
        for op in ops:
            if j+1 < len(i):
                res = ops[op](i[j], i[j+1])
                for op2 in ops:
                    if j+2 < len(i):
                        res2 = ops[op2](res, i[j+2])
                        for op3 in ops:
                            if j+3 < len(i):
                                res3 = ops[op3](res2, i[j+3])
                                debug2.append(res3)
                                if res3 == 10: output2.append(res3)

for i in perms:
    count(i)
    count2(i)
print(len(debug)) # The result is 2400, which is wrong.
print(len(debug2)) # The result is 1536, which is correct.

【问题讨论】:

    标签: python recursion permutation


    【解决方案1】:

    您在递归函数中附加了太多的结果。让我用你的例子来详细说明。让我们考虑计算原始排列。来电

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

    应该会导致以下递归调用:

    count([5, 2, 3])  # +
    count([3, 2, 3])  # -
    count([4, 2, 3])  # *
    count([4, 2, 3])  # /
    

    而且它不应该附加任何结果,对吧!但是,由于您在顶级调用中循环遍历索引,因此还会导致以下所有递归调用:

    count([3, 3])   # these calls are premature
    count([-1, 3])  # since they reduce 1, 2
    count([2, 3])   # and do not consider
    count([0.5, 3]) # the first element 4
    

    并附加所有[2, 3] 计算的结果,这同样为时过早!

    您真正想要在递归函数中做的是仅减少 第一对 元素(不是每一对相邻元素!),然后递归计数结果列表的计算。因此,您的功能可以简化为:

    def count(lst):
        # no mo' looping through the list. That's why we do recursion!
        for op in ops.values():
            if len(lst) > 1:
                res = op(*lst[:2])  # reduce first pair
                if len(lst) > 2: 
                    ls_short = [res] + list(lst[2:])
                    count(ls_short)
                else:
                    debug.append(res)
    
    ...
    > print(len(debug)) 
    1536
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-11
      • 1970-01-01
      • 2018-10-08
      • 2011-05-05
      • 2017-12-11
      • 2019-08-06
      • 1970-01-01
      相关资源
      最近更新 更多