Rilasciato · in miglioramento
Guida a Backtracking · 6/6
Per ora questo capitolo è disponibile solo in inglese.
Backtracking is more than an interview technique. It runs inside tools you use every day: regular expression engines, SAT solvers, constraint programming libraries, and logic programming languages. This chapter covers where it shows up, the pitfalls you meet in production code, and where to read more.
re, Java java.util.regex, JavaScript, and PCRE try each alternative of a pattern (|, *, +) in turn and back up on failure. That is what makes features like backreferences possible, and also why some inputs take exponential time.A pattern like (a+)+$ can split a run of a characters in exponentially many ways, so on input whose ending does not match, the engine tries every split. Applying such a pattern to user-supplied text opens the door to a denial-of-service attack (ReDoS).
import re
import time
pattern = re.compile(r"(a+)+$")
for n in (16, 18, 20, 22):
text = "a" * n + "!"
start = time.perf_counter()
pattern.match(text)
print(n, f"{time.perf_counter() - start:.3f}s")
# every extra character roughly doubles the timeFixes include removing the nested repetition (a+$ matches the same strings here), limiting input length, or switching to an engine with linear-time guarantees such as RE2.
Python's default recursion limit is around 1000, so searches thousands of levels deep raise RecursionError. You can raise the limit with sys.setrecursionlimit, but that risks overflowing the C stack; for deep searches an explicit stack is safer.
def subsets_iterative(nums):
result = []
stack = [(0, [])] # (next index, partial solution)
while stack:
i, path = stack.pop()
if i == len(nums):
result.append(path)
continue
stack.append((i + 1, path)) # branch that skips nums[i]
stack.append((i + 1, path + [nums[i]])) # branch that takes it
return result
print(subsets_iterative([1, 2])) # [[1, 2], [1], [2], []]An explicit stack copies state instead of undoing it, so it uses more memory. When the depth is modest, the recursive version is simpler and faster.
For plain permutations, combinations, or Cartesian products, the standard library is faster and already tested. Write your own backtracking when you need to prune along the way.
from itertools import combinations, permutations, product
print(list(combinations("ABC", 2))) # [('A', 'B'), ('A', 'C'), ('B', 'C')]
print(len(list(permutations(range(4))))) # 24
print(list(product([0, 1], repeat=2))) # [(0, 0), (0, 1), (1, 0), (1, 1)]
0 commenti
Accedi · Accedi per lasciare un commento.
Scrivi tu il primo commento.