Veröffentlicht · wird verbessert
Suchen-Anleitung · 6/6
Dieses Kapitel ist vorerst nur auf Englisch verfügbar.
Binary search is far more than an interview trick. It sits inside databases, version control, networking and load balancing. This chapter covers where searching shows up in real systems and the pitfalls that come up most often in production code.
WHERE price BETWEEN 100 AND 200 are fast precisely because the keys are kept in order.bisect, C++ std::lower_bound and std::equal_range, Java Arrays.binarySearch and Collections.binarySearch, and Rust slice::binary_search and partition_point all apply the same idea.git bisect binary searches the history between a known good and a known bad commit to find the commit that introduced a bug.random.choices uses bisect internally for this.Even with 1,000 commits in between, about 10 checks are enough to find the first bad commit, and the checks can be scripted.
git bisect start
git bisect bad # the current commit is broken
git bisect good v1.4.0 # this tag was known to work
# test each commit Git checks out, then mark it
git bisect good # or: git bisect bad
# automate with a test command: exit 0 = good, 1-127 except 125 = bad
git bisect run python -m pytest tests/test_parser.py -x -q
git bisect resetFinding which of many non-overlapping address ranges contains an IP is the problem "find the last range whose start is at most this address". Even with hundreds of thousands of ranges, each lookup is O(log n).
from bisect import bisect_right
from ipaddress import ip_address
ranges = [ # (start, end, label), sorted by start, non-overlapping
(int(ip_address("10.0.0.0")), int(ip_address("10.255.255.255")), "internal"),
(int(ip_address("192.168.0.0")), int(ip_address("192.168.255.255")), "lab"),
(int(ip_address("203.0.113.0")), int(ip_address("203.0.113.255")), "docs"),
]
starts = [start for start, _, _ in ranges]
def lookup(ip):
n = int(ip_address(ip))
i = bisect_right(starts, n) - 1
if i >= 0 and n <= ranges[i][1]:
return ranges[i][2]
return None
print(lookup("10.1.2.3"), lookup("192.168.5.5"), lookup("8.8.8.8"))
# internal lab Noneimport random
from bisect import bisect_right
from itertools import accumulate
servers = ["a", "b", "c"]
cumulative = list(accumulate([5, 3, 2])) # [5, 8, 10]
def pick():
r = random.random() * cumulative[-1] # 0 <= r < 10
return servers[bisect_right(cumulative, r)]
counts = {s: 0 for s in servers}
for _ in range(10_000):
counts[pick()] += 1
print(counts) # roughly a 5000, b 3000, c 2000When r is in [0, 5) you get a, in [5, 8) you get b, and otherwise c. At exactly 5 the pick must be b, which is why the code uses bisect_right rather than bisect_left.
bisect accepts a key argument.lo = mid that may not shrink the range.lo + (hi - lo) / 2.lo < hi.x in some_list inside a loop makes the whole thing O(n^2). Switch to a set when you only need exact matches.git bisect and weighted selection are all binary search over sorted order.bisect_right(...) - 1.
0 Kommentare
Anmelden · Melde dich an, um einen Kommentar zu schreiben.
Schreib den ersten Kommentar.