search_suggest/tests/test_1_trie.py

220 lines
6.6 KiB
Python

"""
TEST & GIẢI THÍCH: Module Trie
================================
File này test và giải thích chi tiết cách Trie hoạt động
"""
import sys
sys.path.append('src')
from search.trie import Trie, TrieNode
def explain_trie():
"""Giải thích chi tiết Trie"""
print("=" * 80)
print("GIẢI THÍCH: TRIE (PREFIX TREE)")
print("=" * 80)
print("""
TRIE LÀ GÌ?
-----------
- Cấu trúc dữ liệu dạng CÂY
- Mỗi NODE = 1 ký tự
- Dùng để tìm PREFIX (từ bắt đầu bằng...)
VÍ DỤ TRỰC QUAN:
----------------
Lưu 3 từ: "làm", "lau", "lên"
(root)
|
l
/|\\
/ | \\
à a ê
| | |
m u n
- Duyệt từ root → l → à → m = "làm"
- Duyệt từ root → l → a → u = "lau"
- Duyệt từ root → l → ê → n = "lên"
TẠI SAO NHANH?
--------------
- Tìm "la" chỉ cần: root → l → a → thu thập nhánh con
- Độ phức tạp: O(m) với m = độ dài query
- KHÔNG cần duyệt tất cả documents!
""")
input("\nNhấn Enter để xem demo...")
def demo_step_by_step():
"""Demo từng bước"""
print("\n" + "=" * 80)
print("DEMO TỪNG BƯỚC")
print("=" * 80)
# Tạo Trie
trie = Trie()
# Dữ liệu test
documents = [
"làm sạch nhà vệ sinh", # ID: 0
"làm sạch văn phòng", # ID: 1
"làm wc", # ID: 2
"lau sàn nhà", # ID: 3
"lau kính cửa sổ", # ID: 4
"vệ sinh toilet", # ID: 5
]
print("\n📥 BƯỚC 1: THÊM DOCUMENTS VÀO TRIE")
print("-" * 80)
print("Mỗi document được lưu theo từng ký tự trong cây\n")
for doc_id, doc in enumerate(documents):
trie.insert(doc.lower(), doc_id)
print(f"✓ Đã thêm [{doc_id}]: {doc}")
# Giải thích cụ thể cho 2 documents đầu
if doc_id == 0:
print(" → Tạo đường: root → l → à → m → → s → ạ → c → h...")
elif doc_id == 1:
print(" → Tạo đường: root → l → à → m → → s → ạ → c → h... (chia sẻ prefix với doc 0)")
elif doc_id == 2:
print(" → Tạo đường: root → l → à → m → → w → c (chia sẻ 'làm ' với doc 0,1)")
input("\nNhấn Enter để test tìm kiếm...")
print("\n" + "=" * 80)
print("🔍 BƯỚC 2: TEST TÌM KIẾM")
print("=" * 80)
# Test case 1
print("\n📌 TEST 1: Tìm 'làm'")
print("-" * 80)
query = "làm"
results = trie.search_prefix(query)
print(f"Query: '{query}'")
print(f"Cách hoạt động:")
print(f" 1. Duyệt: root → l → à → m")
print(f" 2. Đến node 'm', thu thập TẤT CẢ documents từ đây xuống")
print(f" 3. Tìm thấy {len(results)} documents:")
for doc_id in results:
print(f" [{doc_id}] {documents[doc_id]}")
input("\nNhấn Enter tiếp...")
# Test case 2
print("\n📌 TEST 2: Tìm 'làm s'")
print("-" * 80)
query = "làm s"
results = trie.search_prefix(query)
print(f"Query: '{query}'")
print(f"Cách hoạt động:")
print(f" 1. Duyệt: root → l → à → m → → s")
print(f" 2. Đến node 's', thu thập documents")
print(f" 3. KẾT QUẢ chính xác hơn (loại bỏ 'làm wc'):")
print(f" 4. Tìm thấy {len(results)} documents:")
for doc_id in results:
print(f" [{doc_id}] {documents[doc_id]}")
input("\nNhấn Enter tiếp...")
# Test case 3
print("\n📌 TEST 3: Tìm 'lau'")
print("-" * 80)
query = "lau"
results = trie.search_prefix(query)
print(f"Query: '{query}'")
print(f"Cách hoạt động:")
print(f" 1. Duyệt: root → l → a → u")
print(f" 2. Thu thập documents từ node 'u'")
print(f" 3. Tìm thấy {len(results)} documents:")
for doc_id in results:
print(f" [{doc_id}] {documents[doc_id]}")
input("\nNhấn Enter tiếp...")
# Test case 4 - KHÔNG TÌM THẤY
print("\n📌 TEST 4: Tìm 'wc'")
print("-" * 80)
query = "wc"
results = trie.search_prefix(query)
print(f"Query: '{query}'")
print(f"Cách hoạt động:")
print(f" 1. Duyệt: root → w")
print(f" 2. ❌ KHÔNG tìm thấy node 'w' ở root")
print(f" 3. Return []")
print(f"")
print(f"❓ TẠI SAO KHÔNG TÌM THẤY?")
print(f" - 'wc' NẰM Ở GIỮA câu: 'làm wc'")
print(f" - Trie chỉ tìm được từ Ở ĐẦU câu")
print(f" - Trong cây, 'w' nằm sau 'làm ' chứ không ở root")
print(f"")
print(f"💡 GIẢI PHÁP: Dùng INVERTED INDEX (module tiếp theo)")
input("\nNhấn Enter để xem kết luận...")
def conclusion():
"""Kết luận"""
print("\n" + "=" * 80)
print("📝 KẾT LUẬN")
print("=" * 80)
print("""
✅ TRIE TÌM ĐƯỢC:
-----------------
"làm" → tất cả bắt đầu bằng "làm"
"làm s" → tất cả bắt đầu bằng "làm s" (chính xác hơn)
"lau" → tất cả bắt đầu bằng "lau"
"vệ" → tất cả bắt đầu bằng "vệ"
❌ TRIE KHÔNG TÌM ĐƯỢC:
-----------------------
"wc" → vì "wc" nằm Ở GIỮA câu "làm wc"
"sạch" → vì "sạch" nằm sau "làm " (không phải prefix)
"toilet" → vì "toilet" nằm sau "vệ sinh "
📊 ĐỘ PHỨC TẠP:
---------------
- Thêm document: O(m) với m = độ dài text
- Tìm kiếm: O(m + n) với m = độ dài query, n = số kết quả
- Memory: O(tổng số ký tự của tất cả documents)
🎯 KHI NÀO DÙNG TRIE?
---------------------
✓ Autocomplete (gợi ý khi gõ)
✓ Prefix search (tìm từ bắt đầu bằng...)
✓ Spell checker
✓ IP routing
⏭️ MODULE TIẾP THEO:
---------------------
→ INVERTED INDEX
→ Tìm keyword ở BẤT KỲ ĐÂU trong text
→ Giải quyết vấn đề của Trie
""")
print("=" * 80)
if __name__ == "__main__":
explain_trie()
demo_step_by_step()
conclusion()
print("\n✅ Hoàn thành! Bạn đã hiểu Trie chưa?")
print("📂 Code thực tế: src/search/trie.py")
print("⏭️ Tiếp theo: tests/test_2_inverted_index.py\n")