I am a Python novice who took one course on the subject last semester, which really sparked my interest. Right now I am trying to figure out some more "advanced" concepts on my own, the first one being recursion. I have been trying an exercise from a handbook I'm following, which basically boils down to: "recursively find a n-digit code consisting out of unique digits between 0 and 9."
This builds upon the previous exercise, which does not have the requirement of unique digits. I solved that one as shown below.
#Ask user for a code
code = input("Insert code as integer:\n")
#Usable digits in the code
digits = ['0','1','2','3','4','5','6','7','8','9']
#Initialization
result = ['' for i in range(len(code))]
counter = 0
def crack_code(digits, pos, code, result):
global counter
#Fill code
#For loop tries all digits sequentially
#Recursion: all positions will be filled
#0000 , 0001 ... 9998 , 9999
if pos < len(code):
for digit in digits:
result[pos] = digit
crack_code(digits, pos + 1, code, result)
#Code filled completely
else:
counter += 1
#Print matching result
if ''.join(result) == code:
print(f"\nCracked code: {''.join(result)}\nCounter: {counter}")
return
import time
t1 = time.time()
crack_code(digits, 0, code, result)
t2 = time.time()
t_tot = t2 - t1
print(f"Computation time: {t_tot:.3f} seconds")
However, upon visualizing this code on PythonTutor (a website you guys are likely familiar with) it turns out that this code is not efficient at all. Upon asking to find eg "29" it continues all the way to 99 before quitting. It will always exhausts all possibilities, even after the correct one has been found.
Question 1: [SOLVED by joao] how can I incorporate a stop once the result has been found? I though I did that by including a return
in the #Print matching result block
...
Question 2: how could I include the constraint of unique digits? I tried:
if pos < len(code):
for digit in digits:
if digit not in result:
result[pos] = digit
crack_code(digits, pos + 1, code, result)
Which worked as long as the digits are sorted, ie 1234, 2345 but not 7654, 9172... I feel like I'm somewhat on the right track but not quite there. This version also does not appear any quicker than the unconstrained one (likely because all options are tried anyway as mentioned before).
Question 3: when inputting 6789 as the code, I get a very strange output, which I can not explain. However, this only happens in the constrained version.
Insert code as integer:
6789
Cracked code:6789
Counter:6790
Cracked code:6789
Counter:6800
Cracked code:6789
Counter:6890
Cracked code:6789
Counter:6900
Cracked code:6789
Counter:6990
Cracked code:6789
Counter:7000
Cracked code:6789
Counter:7790
Cracked code:6789
Counter:7800
Cracked code:6789
Counter:7890
Cracked code:6789
Counter:7900
Cracked code:6789
Counter:7990
Cracked code:6789
Counter:8000
Cracked code:6789
Counter:8790
Cracked code:6789
Counter:8800
Cracked code:6789
Counter:8890
Cracked code:6789
Counter:8900
Cracked code:6789
Counter:8990
Cracked code:6789
Counter:9000
Cracked code:6789
Counter:9790
Cracked code:6789
Counter:9800
Cracked code:6789
Counter:9890
Cracked code:6789
Counter:9900
Cracked code:6789
Counter:9990
Cracked code:6789
Counter:10000
Computation time: 0.018 seconds
Any suggestions would be greatly appreciated!
Thanks for your time!
Ben
Answering Question1 : this is an ideal use case for exceptions .
The problem with 'return' is that your recursion has piled up many calls to crack_code, and return only brings you back one level, to where you last called crack_code. Since that's inside a 'for' loop, it'll continue looping.
To break out of all the crack_code calls, define a new exception class:
class MyException(Exception):
pass
(name it whatever you want). When you have found the result, instead of return use this:
raise MyException()
This will propagate up the entire call stack. If you leave it like this, your program will end (which is what you want) with an ugly error message and stack trace (that's messy), so to finish gracefully you need to catch the exception:
import time
t1 = time.time()
try:
crack_code(digits, 0, code, result)
except MyException:
# We go through here only if we cracked the code
print('found!')
# We execute this in every case
t2 = time.time()
t_tot = t2 - t1
print(f"Computation time: {t_tot:.3f} seconds")
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.