Challenge 2: List vs Set Membership, Timed — Possible Solution ==================================================================== import time numbers_list = list(range(100_000)) numbers_set = set(range(100_000)) start = time.time() for i in range(10_000): _ = i in numbers_list list_time = time.time() - start print(f"List: {list_time:.4f}s") start = time.time() for i in range(10_000): _ = i in numbers_set set_time = time.time() - start print(f"Set: {set_time:.4f}s") Output (exact timings will vary, but the list will always be dramatically slower): List: 0.4213s Set: 0.0009s WHY THIS WORKS AS AN ANSWER ------------------------------ numbers_list = list(range(100_000)) and numbers_set = set(range (100_000)) build the two collections directly from range(), reusing the list()/set() constructor pattern from Course 1, Chapters 6 — both contain the exact same 100,000 values, just stored differently. for i in range(10_000): _ = i in numbers_list reuses this chapter's own if item in allowed_list example, run 10,000 times. Per the chapter's O(n) explanation, EVERY single one of those 10,000 checks has to potentially scan through the list element by element looking for a match — a linear cost that adds up fast across that many checks. for i in range(10_000): _ = i in numbers_set reuses the chapter's own if item in allowed_set example. Per the chapter's O(1) explanation, each check is a direct hash lookup regardless of how many items are in the set — so 10,000 checks against the set take a small, roughly constant amount of total time, nowhere close to the list's. time.time() before/after each block reuses the same before/after timing pattern used throughout this course (Chapter 3's threading challenge, Chapter 4's asyncio challenge) to make the O(n) vs O(1) difference this chapter describes directly measurable rather than just asserted — the list version should be dramatically, often hundreds of times, slower.