Search code examples
pythonpython-3.xalgorithmrecursionbacktracking

Why this two backtracking algorithm have same outputs?


I have made two implementations of a backtracking algorithm to solve this challenge on Codewars ( https://www.codewars.com/kata/5a6b24d4e626c59d5b000066 ) . My problem is that I grasp why the first implementation works but not why the second one . I think that my problem is in the return statement in the last line with the hash signs . I don't get why when the backtracking is terminating , since a solution was found, the second implementation returns the right solution instead of an empty list . Is it because when the solution is passed to the previous call of the function his value in the previous call ( which was with an element less ) is overwritten with the complete solution ?

First :

def square_sums_row(n):
    a , sol = [ x for x in range(1,n+1)] , []
    def recurse(a,sol):
        if a == [] : 
            return sol 
        for i,x in enumerate(a) :
            if is_valid(x,sol) :
                sol.append(x)
                iterate = recurse(a[:i]+a[i+1:],sol) ###########
                if iterate : ###########
                    return iterate #########
                sol.pop(-1)
        return False
    def is_valid(x,sol):
        if len(sol) == 0 :
            return True
        if (( sol[-1] + x )**0.5) % 1 == 0:
            return True
        return False
    return recurse(a,sol)

Second :

def square_sums_row(n):
    s , sol = [ x for x in range(1,n+1)] , []
    def recurse(s,sol):
        if s == [] :  
            return sol
        for i,x in enumerate(s) :
            if is_valid(x,sol) :
                sol.append(x)
                if recurse(s[:i]+s[i+1:],sol) : ###########
                    return sol ##############
                sol.pop(-1)
        return False
    def is_valid(x,sol):
        if len(sol) == 0 :
            return True
        if (( sol[-1] + x )**0.5) % 1 == 0:
            return True
        return False
    return recurse(s,sol)

Solution

  • To expand on the other answer, there is only one list object sol that is being passed around and returned. It's never copied. Here's another solution to emphasise that point. Here sol is not an argument anywhere, it's just taken from the closure, and only booleans are returned by the inner functions.

    def square_sums_row(n):
        sol = []
    
        def recurse(s):
            if s == []:
                return True
            for i, x in enumerate(s):
                if is_valid(x):
                    sol.append(x)
                    iterate = recurse(s[:i] + s[i + 1:])  ###########
                    if iterate:  ###########
                        return True
                    sol.pop(-1)
            return False
    
        def is_valid(x):
            if len(sol) == 0:
                return True
            if ((sol[-1] + x) ** 0.5) % 1 == 0:
                return True
            return False
    
        if recurse([x for x in range(1, n + 1)]):
            return sol
        else:
            return False