Python中的递归用于日常编码挑战

Python中的递归用于日常编码挑战,python,recursion,Python,Recursion,我试图解决一些我在网上发现的编码难题。然而,我被下面的问题阻止了。我试图用递归来解决这个问题,但我觉得我缺少了递归中一个非常重要的概念。我的代码适用于以下所有示例,但最后一个示例除外 有人能指出我在递归代码中犯的错误吗?或者指导我解决这个问题 我知道为什么我的代码会中断,但我不知道如何绕过Python中的“通过对象引用传递”,我认为这会给我带来更大的问题 编码问题是: 在一个神秘的岛屿上,有一种叫做古克斯的生物,有三种颜色:红色、绿色和蓝色。Qux的一种力量是,如果它们中的两个站在一起,它们可以

我试图解决一些我在网上发现的编码难题。然而,我被下面的问题阻止了。我试图用递归来解决这个问题,但我觉得我缺少了递归中一个非常重要的概念。我的代码适用于以下所有示例,但最后一个示例除外

有人能指出我在递归代码中犯的错误吗?或者指导我解决这个问题

我知道为什么我的代码会中断,但我不知道如何绕过Python中的“通过对象引用传递”,我认为这会给我带来更大的问题

编码问题是:

在一个神秘的岛屿上,有一种叫做古克斯的生物,有三种颜色:红色、绿色和蓝色。Qux的一种力量是,如果它们中的两个站在一起,它们可以变成一个单一的第三种颜色的生物

假设N个qux排成一行,确定在任何可能的转换序列之后剩余的最小qux数。 例如,给定输入
['R','G','B','G','B']
,可以通过以下步骤以单个Qux结束:

        Arrangement       |   Change
----------------------------------------
['R', 'G', 'B', 'G', 'B'] | (R, G) -> B
['B', 'B', 'G', 'B']      | (B, G) -> R
['B', 'R', 'B']           | (R, B) -> G
['B', 'G']                | (B, G) -> R
['R']                     |
________________________________________
我的代码是:

class fusionCreatures(object):
    """Regular Numbers Gen.
    """

    def __init__(self , value=[]):
        self.value = value
        self.ans = len(self.value)
        

    def fusion(self, fus_arr, i):
        color = ['R','G','B']
        color.remove(fus_arr[i])
        color.remove(fus_arr[i+1])
        fus_arr.pop(i)
        fus_arr.pop(i)
        fus_arr.insert(i, color[0])
        return fus_arr
        
        
    def fusionCreatures1(self, arr=None):
        # this method is to find the smallest number of creature in a row after fusion 
        if arr == None:
            arr = self.value
        for i in range (0,len(arr)-1):
            #print(arr)
            if len(arr) == 2 and i >= 1 or len(arr)<2:
                break
            if arr[i] != arr[i+ 1]:
                arr1 = self.fusion(arr, i)
                testlen = self.fusionCreatures1(arr)
                if len(arr) < self.ans:
                    self.ans = len(arr)
        return self.ans

我首先要提到的是,有一种演绎方法在O(n)中有效,并在本文中详细介绍。它归结为检查列表中三种类型元素的计数的奇偶性,以确定几个固定结果中的哪一个发生

您提到您更喜欢使用递归方法,即O(n!)。这是一个很好的开始,因为它可以作为一个工具来帮助获得O(n)解决方案,并且是一种常见的递归模式

因为我们不知道两个qux之间的给定融合是否最终会导致一个最优的全局解决方案,所以我们不得不尝试各种可能性。我们通过浏览列表并寻找潜在的融合来实现这一点。当我们找到一个时,在一个新列表中执行转换,并在其上调用
fuse\u qxes
。在这一过程中,我们将跟踪所达到的最小长度

这里有一种方法:

def fuse_quxes(quxes, choices="RGB"):
    fusion = {x[:-1]: [x[-1]] for x in permutations(choices)}

    def walk(quxes):
        best = len(quxes)

        for i in range(1, len(quxes)):
            if quxes[i-1] != quxes[i]:
                sub = quxes[:i-1] + fusion[quxes[i-1], quxes[i]] + quxes[i+1:]
                best = min(walk(sub), best)

        return best

    return walk(quxes)
这几乎是您提供的代码的发展方向,但实现似乎不清楚。不幸的是,我没有看到任何单一或快速的解决办法。以下是一些一般性问题:

  • fusioncreates1
    函数放入一个类中,允许它改变外部状态,即
    self.value
    self.ans
    self.value
    的名称特别糟糕,难以跟踪。其目的似乎是将其用作参考副本,以将
    arr
    重置为其默认值,但
    arr=self.value
    意味着当
    fus\u arr
    fusion()
    中发生变异时,
    self.value
    也会发生变化。所有内容几乎都是对一个基本列表的引用

    向这些副本添加切片至少可以使程序更容易推理,例如,
    fusion()
    函数中的
    arr=self.value[:]
    fus\u arr=fus\u arr[:]
    。简而言之,试着去写

    self.ans
    也不清楚,不必要;最好将结果值降级为递归函数中的局部变量

    似乎没有必要将无状态函数放入类中,除非它是纯静态方法,并且该类充当名称空间

  • 认知过载的另一个原因是分支语句,如
    if
    break
    。我们希望尽量减少这些的频率和嵌套。以下是伪代码中的
    fusioncreates1
    ,带有突变和复杂相互作用的注释:

    def fusionCreatures1():
        if ...
            read mutated global state
        for i in len(arr):
            if complex length and index checks:
                break
            if arr[i] != arr[i+ 1]:
                impure_func_that_changes_arr_length(arr)
                recurse()
                if new best compared to global state:
                    mutate global state
    
    您可能会同意,在运行此函数时,很难在心理上逐步完成

  • fusionCreatures1()
    中,有两个变量未使用:

            arr1 = self.fusion(arr, i)
            testlen = self.fusionCreatures1(arr)
    
    赋值
    arr1=self.fusion(arr,i)
    (连同
    返回fus\u arr
    )似乎表明人们不了解
    self.fusion
    实际上是一个变异参数数组的函数。所以调用它意味着
    arr1是arr
    ,我们还有另一个别名变量需要解释

    除此之外,程序中既没有使用
    arr1
    也没有使用
    testlen
    ,因此意图不明确

    一个好的工具会发现这些未使用的变量,并识别我提到的大多数其他复杂性问题

  • 在循环列表时对其进行变异通常是灾难性的
    self.fusion(arr,i)
    在一个循环内变异
    arr
    ,这使得很难对其长度进行推理,并且当
    范围(len(arr))
    不再匹配函数体中的实际
    len(arr)
    时,会导致索引错误(或者至少需要一个体内先决条件)。如上所述,使用切片将
    self.fusion(arr,i)
    变得纯粹,可以修复此问题,但揭示了不存在递归,从而导致堆栈溢出错误
  • 避免使用变量名,如
    arr
    arr1
    value
    ,除非上下文明显。同样,这些混淆了意图,使程序难以理解
一些小的风格建议:

  • 根据使用
    snake\u案例
    。类名应
    标题化
    ,以区别于函数。不需要从
    对象继承
    ——这是隐式的
  • 在函数和运算符周围使用一致的间距:
    range(0,len(arr)-1):
    更清晰,例如
    range(len(arr)-1):
    。在块周围使用垂直空格
  • 使用列表,而不是键入
    t1
    t2
    <代码>t7
  • 函数名应该是动词,而不是名词。类似于
    fusioncreates
    的类以及名为
    fusioncreates1
    的方法尚不清楚。类似于
    QuxesSolver.minimize(生物)
    make t
            arr1 = self.fusion(arr, i)
            testlen = self.fusionCreatures1(arr)
    
    from collections import defaultdict
    from itertools import permutations
    from random import choice, randint
    
    def fuse_quxes_linear(quxes, choices="RGB"):
        counts = defaultdict(int)
    
        for e in quxes:
            counts[e] += 1
    
        if not quxes or any(x == len(quxes) for x in counts.values()):
            return len(quxes)
        elif len(set(counts[x] % 2 for x in choices)) == 1:
            return 2
    
        return 1
    
    
    def fuse_quxes(quxes, choices="RGB"):
        fusion = {x[:-1]: [x[-1]] for x in permutations(choices)}
    
        def walk(quxes):
            best = len(quxes)
    
            for i in range(1, len(quxes)):
                if quxes[i-1] != quxes[i]:
                    sub = quxes[:i-1] + fusion[quxes[i-1], quxes[i]] + quxes[i+1:]
                    best = min(walk(sub), best)
    
            return best
    
        return walk(quxes)
    
    if __name__ == "__main__":
        tests = [
            ['R','G','B','G','B'],
            ['R','G','B','R','G','B'],
            ['R','R','G','B','G','B'],
            ['G','R','B','R','G'],
            ['G','R','B','R','G','R','G'],
            ['R','R','R','R','R'],
            ['R', 'R', 'R', 'G', 'G', 'G', 'B', 'B', 'B']
        ]
    
        for test in tests:
            print(test, "=>", fuse_quxes(test))
            assert fuse_quxes_linear(test) == fuse_quxes(test)
    
        for i in range(100):
            test = [choice("RGB") for x in range(randint(0, 10))]
            assert fuse_quxes_linear(test) == fuse_quxes(test)
    
    def fuse_quxes(l):
        n = len(l)
        for i in range(n - 1):
            if l[i] == l[i + 1]:
                continue
            else:
                newn = fuse_quxes(l[:i] + [3 - l[i] - l[i + 1]] + l[i+2:])
                if newn < n:
                    n = newn
        return n
    
    IN[5]: fuse_quxes([0, 0, 0, 1, 1, 1, 2, 2, 2])
    Out[5]: 2
    
    ['R', 'G', 'B', 'G', 'B']
    ['R', 'R', 'G', 'B']
    ['R', 'B', 'B']
    ['G', 'B']
    ['R']
    Result 1
    _______________________
    ['R', 'G', 'B', 'R', 'G', 'B']
    ['R', 'G', 'B', 'R', 'R']
    ['R', 'G', 'G', 'R']
    ['B', 'G', 'R']
    ['B', 'B']
    Result 2
    _______________________
    ['R', 'R', 'G', 'B', 'G', 'B']
    ['R', 'B', 'B', 'G', 'B']
    ['G', 'B', 'G', 'B']
    ['R', 'G', 'B']
    ['R', 'R']
    Result 2
    _______________________
    ['G', 'R', 'B', 'R', 'G']
    ['G', 'G', 'R', 'G']
    ['G', 'B', 'G']
    ['R', 'G']
    ['B']
    Result 1
    _______________________
    ['G', 'R', 'B', 'R', 'G', 'R', 'G']
    ['G', 'G', 'R', 'G', 'R', 'G']
    ['G', 'B', 'G', 'R', 'G']
    ['R', 'G', 'R', 'G']
    ['B', 'R', 'G']
    ['B', 'B']
    Result 2
    _______________________
    ['R', 'R', 'R', 'R', 'R']
    Result 5
    _______________________
    ['R', 'R', 'R', 'G', 'G', 'G', 'B', 'B', 'B']
    ['R', 'R', 'B', 'G', 'G', 'B', 'B', 'B']
    ['R', 'G', 'G', 'G', 'B', 'B', 'B']
    ['B', 'G', 'G', 'B', 'B', 'B']
    ['R', 'G', 'B', 'B', 'B']
    ['R', 'R', 'B', 'B']
    ['R', 'G', 'B']
    ['R', 'R']
    Result 2
    _______________________
    [1, 2, 2, 1, 2, 5, 2]