Reputation: 55
totalcount = 0
res = []
def permute(nums):
backtrack(nums, [])
def backtrack(nums, path):
global totalcount
totalcount += 1
if not nums:
res.append(path)
for x in range(len(nums)):
#res.append(x)
backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
permute([1,2,3])
This is the code to return the permutations of a list of numbers, in my case [1, 2, 3]
. I'm trying to trace the recursive calls to view the logic flow, but I just can't wrap my mind around it. When I commented in res.append(x)
to figure out why I see this:
[0, 0, 0, [1, 2, 3], 1, 0, [1, 3, 2],
1, 0, 0, [2, 1, 3], 1, 0, [2, 3, 1],
2, 0, 0, [3, 1, 2], 1, 0, [3, 2, 1]]
Also totalcount
is 16
here, and I'm not sure why this function ran a total of that many times.
My questions are :
x=0
. but of course it calls backtrack again, so in those calls, x
goes to zero again. Following nums[:x]+nums[x+1:], path+[nums[x]]
in this logic, I can see where [1,2,3]
comes from (and before [1,2,3]
the x
values are 0,0,0
as I expected). But then all those inner calls should finish and then X should be two right? so doesn't that mean the next item in res should be [2,1,3]
? I would expect this to just output res = [1,2,3],[2,1,3],[3,1,2]
, but it is indeed correctly giving the remaining permutations where 1 is first and 2 is first and 3 is first. How?Upvotes: 1
Views: 137
Reputation: 4455
Rather than answer your questions, I hope I can give you a technique that will let you find the answers for yourself. Consider adding a breakpoint()
call to your code. It's a built-in that will put you in the interactive debugger.
totalcount = 0
res = []
def permute(nums):
backtrack(nums, [])
def backtrack(nums, path):
global totalcount
totalcount += 1
if not nums:
res.append(path)
for x in range(len(nums)):
#res.append(x)
backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
breakpoint()
permute([1,2,3])
Once you are in the interactive debugger, you can use the h
command to list the available commands or h <command>
to get help on a command. Basically, you need to just use the s
command to step through your code.
The debugger can definitely help you with that. Here are more commands that could help you:
With that information, you can display the local variables in the calling function to give you an idea of where you were in the process:
$ python back
> /Users/mrosen609/fun/back(15)<module>()
-> permute([1,2,3])
(Pdb) b 13
Breakpoint 1 at /Users/mrosen609/fun/back:13
(Pdb) c
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) w
/Users/mrosen609/fun/back(15)<module>()
-> permute([1,2,3])
/Users/mrosen609/fun/back(5)permute()
-> backtrack(nums, [])
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) c
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) w
/Users/mrosen609/fun/back(15)<module>()
-> permute([1,2,3])
/Users/mrosen609/fun/back(5)permute()
-> backtrack(nums, [])
/Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) nums
[2, 3]
(Pdb) nums, path
([2, 3], [1])
(Pdb) up
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) nums, path
([1, 2, 3], [])
(Pdb) x
0
(Pdb) down
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) nums, path
([2, 3], [1])
(Pdb) x
0
(Pdb) c
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) w
/Users/mrosen609/fun/back(15)<module>()
-> permute([1,2,3])
/Users/mrosen609/fun/back(5)permute()
-> backtrack(nums, [])
/Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
/Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) x
0
(Pdb) nums, path
([3], [1, 2])
(Pdb) up
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) nums, path
([2, 3], [1])
(Pdb) up
> /Users/mrosen609/fun/back(13)backtrack()
-> backtrack(nums[:x]+nums[x+1:], path+[nums[x]])
(Pdb) nums, path
([1, 2, 3], [])
To be honest, I did introduce two additional commands: (b) breakpoint and (c) continue. I wanted let the program run until it hit line 13 and not have to press (s) step so much. And so, ideally you can see that (c) continue takes you to the next breakpoint.
Hopefully this example helps, but you may want to review the interwebs for additional information about pdb
.
Upvotes: 1
Reputation: 27609
I logged the calls:
backtrack([1, 2, 3], [])
backtrack([2, 3], [1])
backtrack([3], [1, 2])
backtrack([], [1, 2, 3])
backtrack([2], [1, 3])
backtrack([], [1, 3, 2])
backtrack([1, 3], [2])
backtrack([3], [2, 1])
backtrack([], [2, 1, 3])
backtrack([1], [2, 3])
backtrack([], [2, 3, 1])
backtrack([1, 2], [3])
backtrack([2], [3, 1])
backtrack([], [3, 1, 2])
backtrack([1], [3, 2])
backtrack([], [3, 2, 1])
Obtained with this decorator:
def log(func):
depth = 0
def logging_func(*args):
nonlocal depth
print(' ' * depth +
func.__name__ + '('+
repr(args)[1:-1] + ')')
depth += 1
result = func(*args)
depth -= 1
return result
return logging_func
Just copy that into your code and apply it to your backtrack
:
@log
def backtrack(nums, path):
...
Upvotes: 2